The recording format is now Transport Stream

This commit is contained in:
Klaus Schmidinger
2009-01-06 14:41:11 +01:00
parent 7470253c60
commit 7de7ede26f
20 changed files with 832 additions and 2488 deletions

View File

@@ -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);
}
}
}