mirror of
https://github.com/vdr-projects/vdr.git
synced 2025-03-01 10:50:46 +00:00
The recording format is now Transport Stream
This commit is contained in:
172
recorder.c
172
recorder.c
@@ -4,13 +4,10 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* how to reach the author.
|
||||
*
|
||||
* $Id: recorder.c 1.19 2007/02/24 16:36:24 kls Exp $
|
||||
* $Id: recorder.c 2.1 2009/01/06 12:38:01 kls Exp $
|
||||
*/
|
||||
|
||||
#include "recorder.h"
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include "shutdown.h"
|
||||
|
||||
#define RECORDERBUFSIZE MEGABYTE(5)
|
||||
@@ -22,33 +19,34 @@
|
||||
#define MINFREEDISKSPACE (512) // MB
|
||||
#define DISKCHECKINTERVAL 100 // seconds
|
||||
|
||||
// --- cFileWriter -----------------------------------------------------------
|
||||
// --- cRecorder -------------------------------------------------------------
|
||||
|
||||
class cFileWriter : public cThread {
|
||||
private:
|
||||
cRemux *remux;
|
||||
cFileName *fileName;
|
||||
cIndexFile *index;
|
||||
uchar pictureType;
|
||||
int fileSize;
|
||||
cUnbufferedFile *recordFile;
|
||||
time_t lastDiskSpaceCheck;
|
||||
bool RunningLowOnDiskSpace(void);
|
||||
bool NextFile(void);
|
||||
protected:
|
||||
virtual void Action(void);
|
||||
public:
|
||||
cFileWriter(const char *FileName, cRemux *Remux);
|
||||
virtual ~cFileWriter();
|
||||
};
|
||||
|
||||
cFileWriter::cFileWriter(const char *FileName, cRemux *Remux)
|
||||
:cThread("file writer")
|
||||
cRecorder::cRecorder(const char *FileName, tChannelID ChannelID, int Priority, int VPid, const int *APids, const int *DPids, const int *SPids)
|
||||
:cReceiver(ChannelID, Priority, VPid, APids, Setup.UseDolbyDigital ? DPids : NULL, SPids)
|
||||
,cThread("recording")
|
||||
,recordingInfo(FileName)
|
||||
{
|
||||
// Make sure the disk is up and running:
|
||||
|
||||
SpinUpDisk(FileName);
|
||||
|
||||
ringBuffer = new cRingBufferLinear(RECORDERBUFSIZE, TS_SIZE * 2, true, "Recorder");
|
||||
ringBuffer->SetTimeouts(0, 100);
|
||||
cChannel *Channel = Channels.GetByChannelID(ChannelID);
|
||||
int Pid = VPid;
|
||||
int Type = Channel ? Channel->Vtype() : 0;
|
||||
if (!Pid && APids) {
|
||||
Pid = APids[0];
|
||||
Type = 0x04;
|
||||
}
|
||||
if (!Pid && DPids) {
|
||||
Pid = DPids[0];
|
||||
Type = 0x06;
|
||||
}
|
||||
frameDetector = new cFrameDetector(Pid, Type);
|
||||
patPmtGenerator.GeneratePmt(ChannelID);
|
||||
fileName = NULL;
|
||||
remux = Remux;
|
||||
index = NULL;
|
||||
pictureType = NO_PICTURE;
|
||||
fileSize = 0;
|
||||
lastDiskSpaceCheck = time(NULL);
|
||||
fileName = new cFileName(FileName, true);
|
||||
@@ -62,14 +60,16 @@ cFileWriter::cFileWriter(const char *FileName, cRemux *Remux)
|
||||
// let's continue without index, so we'll at least have the recording
|
||||
}
|
||||
|
||||
cFileWriter::~cFileWriter()
|
||||
cRecorder::~cRecorder()
|
||||
{
|
||||
Cancel(3);
|
||||
Detach();
|
||||
delete index;
|
||||
delete fileName;
|
||||
delete frameDetector;
|
||||
delete ringBuffer;
|
||||
}
|
||||
|
||||
bool cFileWriter::RunningLowOnDiskSpace(void)
|
||||
bool cRecorder::RunningLowOnDiskSpace(void)
|
||||
{
|
||||
if (time(NULL) > lastDiskSpaceCheck + DISKCHECKINTERVAL) {
|
||||
int Free = FreeDiskSpaceMB(fileName->Name());
|
||||
@@ -82,10 +82,10 @@ bool cFileWriter::RunningLowOnDiskSpace(void)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool cFileWriter::NextFile(void)
|
||||
bool cRecorder::NextFile(void)
|
||||
{
|
||||
if (recordFile && pictureType == I_FRAME) { // every file shall start with an I_FRAME
|
||||
if (fileSize > MEGABYTE(Setup.MaxVideoFileSize) || RunningLowOnDiskSpace()) {
|
||||
if (recordFile && frameDetector->IndependentFrame()) { // every file shall start with an independent frame
|
||||
if (fileSize > MEGABYTE(off_t(Setup.MaxVideoFileSize)) || RunningLowOnDiskSpace()) {
|
||||
recordFile = fileName->NextFile();
|
||||
fileSize = 0;
|
||||
}
|
||||
@@ -93,67 +93,10 @@ bool cFileWriter::NextFile(void)
|
||||
return recordFile != NULL;
|
||||
}
|
||||
|
||||
void cFileWriter::Action(void)
|
||||
{
|
||||
time_t t = time(NULL);
|
||||
while (Running()) {
|
||||
int Count;
|
||||
uchar *p = remux->Get(Count, &pictureType);
|
||||
if (p) {
|
||||
if (!Running() && pictureType == I_FRAME) // finish the recording before the next 'I' frame
|
||||
break;
|
||||
if (NextFile()) {
|
||||
if (index && pictureType != NO_PICTURE)
|
||||
index->Write(pictureType, fileName->Number(), fileSize);
|
||||
if (recordFile->Write(p, Count) < 0) {
|
||||
LOG_ERROR_STR(fileName->Name());
|
||||
break;
|
||||
}
|
||||
fileSize += Count;
|
||||
remux->Del(Count);
|
||||
}
|
||||
else
|
||||
break;
|
||||
t = time(NULL);
|
||||
}
|
||||
else if (time(NULL) - t > MAXBROKENTIMEOUT) {
|
||||
esyslog("ERROR: video data stream broken");
|
||||
ShutdownHandler.RequestEmergencyExit();
|
||||
t = time(NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- cRecorder -------------------------------------------------------------
|
||||
|
||||
cRecorder::cRecorder(const char *FileName, tChannelID ChannelID, int Priority, int VPid, const int *APids, const int *DPids, const int *SPids)
|
||||
:cReceiver(ChannelID, Priority, VPid, APids, Setup.UseDolbyDigital ? DPids : NULL, SPids)
|
||||
,cThread("recording")
|
||||
{
|
||||
// Make sure the disk is up and running:
|
||||
|
||||
SpinUpDisk(FileName);
|
||||
|
||||
ringBuffer = new cRingBufferLinear(RECORDERBUFSIZE, TS_SIZE * 2, true, "Recorder");
|
||||
ringBuffer->SetTimeouts(0, 100);
|
||||
remux = new cRemux(VPid, APids, Setup.UseDolbyDigital ? DPids : NULL, SPids, true);
|
||||
writer = new cFileWriter(FileName, remux);
|
||||
}
|
||||
|
||||
cRecorder::~cRecorder()
|
||||
{
|
||||
Detach();
|
||||
delete writer;
|
||||
delete remux;
|
||||
delete ringBuffer;
|
||||
}
|
||||
|
||||
void cRecorder::Activate(bool On)
|
||||
{
|
||||
if (On) {
|
||||
writer->Start();
|
||||
if (On)
|
||||
Start();
|
||||
}
|
||||
else
|
||||
Cancel(3);
|
||||
}
|
||||
@@ -169,15 +112,54 @@ void cRecorder::Receive(uchar *Data, int Length)
|
||||
|
||||
void cRecorder::Action(void)
|
||||
{
|
||||
time_t t = time(NULL);
|
||||
bool Synced = false;
|
||||
bool InfoWritten = false;
|
||||
while (Running()) {
|
||||
int r;
|
||||
uchar *b = ringBuffer->Get(r);
|
||||
if (b) {
|
||||
int Count = remux->Put(b, r);
|
||||
if (Count)
|
||||
int Count = frameDetector->Analyze(b, r);
|
||||
if (Count) {
|
||||
if (!Running() && frameDetector->IndependentFrame()) // finish the recording before the next independent frame
|
||||
break;
|
||||
if (Synced |= frameDetector->IndependentFrame()) { // start with first independent frame
|
||||
if (!InfoWritten) {
|
||||
if (recordingInfo.Read()) {
|
||||
if (frameDetector->FramesPerSecond() > 0 && recordingInfo.FramesPerSecond() != frameDetector->FramesPerSecond()) {
|
||||
recordingInfo.SetFramesPerSecond(frameDetector->FramesPerSecond());
|
||||
recordingInfo.Write();
|
||||
}
|
||||
}
|
||||
InfoWritten = true;
|
||||
}
|
||||
if (!NextFile())
|
||||
break;
|
||||
if (index && frameDetector->NewFrame())
|
||||
index->Write(frameDetector->IndependentFrame(), fileName->Number(), fileSize);
|
||||
if (frameDetector->IndependentFrame()) {
|
||||
recordFile->Write(patPmtGenerator.GetPat(), TS_SIZE);
|
||||
fileSize += TS_SIZE;
|
||||
int Index = 0;
|
||||
while (uchar *pmt = patPmtGenerator.GetPmt(Index)) {
|
||||
recordFile->Write(pmt, TS_SIZE);
|
||||
fileSize += TS_SIZE;
|
||||
}
|
||||
}
|
||||
if (recordFile->Write(b, Count) < 0) {
|
||||
LOG_ERROR_STR(fileName->Name());
|
||||
break;
|
||||
}
|
||||
fileSize += Count;
|
||||
t = time(NULL);
|
||||
}
|
||||
ringBuffer->Del(Count);
|
||||
else
|
||||
cCondWait::SleepMs(100); // avoid busy loop when resultBuffer is full in cRemux::Put()
|
||||
}
|
||||
}
|
||||
if (time(NULL) - t > MAXBROKENTIMEOUT) {
|
||||
esyslog("ERROR: video data stream broken");
|
||||
ShutdownHandler.RequestEmergencyExit();
|
||||
t = time(NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user