2002-06-16 12:57:31 +02:00
|
|
|
/*
|
2002-12-22 11:33:08 +01:00
|
|
|
* recorder.c: The actual DVB recorder
|
2002-06-16 12:57:31 +02:00
|
|
|
*
|
|
|
|
* See the main source file 'vdr.c' for copyright information and
|
|
|
|
* how to reach the author.
|
|
|
|
*
|
2007-01-07 14:46:14 +01:00
|
|
|
* $Id: recorder.c 1.18 2007/01/07 14:43:09 kls Exp $
|
2002-06-16 12:57:31 +02:00
|
|
|
*/
|
|
|
|
|
2006-01-08 11:03:44 +01:00
|
|
|
#include "recorder.h"
|
2002-06-16 12:57:31 +02:00
|
|
|
#include <stdarg.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
2004-10-16 09:36:28 +02:00
|
|
|
#define RECORDERBUFSIZE MEGABYTE(5)
|
2002-06-16 12:57:31 +02:00
|
|
|
|
2003-05-16 13:36:06 +02:00
|
|
|
// The maximum time we wait before assuming that a recorded video data stream
|
|
|
|
// is broken:
|
|
|
|
#define MAXBROKENTIMEOUT 30 // seconds
|
|
|
|
|
2002-06-16 12:57:31 +02:00
|
|
|
#define MINFREEDISKSPACE (512) // MB
|
|
|
|
#define DISKCHECKINTERVAL 100 // seconds
|
|
|
|
|
2007-01-07 14:46:14 +01:00
|
|
|
// --- cFileWriter -----------------------------------------------------------
|
|
|
|
|
2004-10-16 09:36:28 +02:00
|
|
|
class cFileWriter : public cThread {
|
|
|
|
private:
|
|
|
|
cRemux *remux;
|
|
|
|
cFileName *fileName;
|
|
|
|
cIndexFile *index;
|
|
|
|
uchar pictureType;
|
|
|
|
int fileSize;
|
2005-10-31 13:14:26 +01:00
|
|
|
cUnbufferedFile *recordFile;
|
2004-10-16 09:36:28 +02:00
|
|
|
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")
|
2002-06-16 12:57:31 +02:00
|
|
|
{
|
|
|
|
fileName = NULL;
|
2004-10-16 09:36:28 +02:00
|
|
|
remux = Remux;
|
2002-06-16 12:57:31 +02:00
|
|
|
index = NULL;
|
|
|
|
pictureType = NO_PICTURE;
|
|
|
|
fileSize = 0;
|
|
|
|
lastDiskSpaceCheck = time(NULL);
|
|
|
|
fileName = new cFileName(FileName, true);
|
|
|
|
recordFile = fileName->Open();
|
2005-10-31 13:14:26 +01:00
|
|
|
if (!recordFile)
|
2002-06-16 12:57:31 +02:00
|
|
|
return;
|
|
|
|
// Create the index file:
|
|
|
|
index = new cIndexFile(FileName, true);
|
|
|
|
if (!index)
|
|
|
|
esyslog("ERROR: can't allocate index");
|
|
|
|
// let's continue without index, so we'll at least have the recording
|
|
|
|
}
|
|
|
|
|
2004-10-16 09:36:28 +02:00
|
|
|
cFileWriter::~cFileWriter()
|
2002-06-16 12:57:31 +02:00
|
|
|
{
|
2004-10-16 09:36:28 +02:00
|
|
|
Cancel(3);
|
2002-06-16 12:57:31 +02:00
|
|
|
delete index;
|
|
|
|
delete fileName;
|
|
|
|
}
|
|
|
|
|
2004-10-16 09:36:28 +02:00
|
|
|
bool cFileWriter::RunningLowOnDiskSpace(void)
|
2002-06-16 12:57:31 +02:00
|
|
|
{
|
|
|
|
if (time(NULL) > lastDiskSpaceCheck + DISKCHECKINTERVAL) {
|
|
|
|
int Free = FreeDiskSpaceMB(fileName->Name());
|
|
|
|
lastDiskSpaceCheck = time(NULL);
|
|
|
|
if (Free < MINFREEDISKSPACE) {
|
|
|
|
dsyslog("low disk space (%d MB, limit is %d MB)", Free, MINFREEDISKSPACE);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2004-10-16 09:36:28 +02:00
|
|
|
bool cFileWriter::NextFile(void)
|
2002-06-16 12:57:31 +02:00
|
|
|
{
|
2005-10-31 13:14:26 +01:00
|
|
|
if (recordFile && pictureType == I_FRAME) { // every file shall start with an I_FRAME
|
2002-06-16 12:57:31 +02:00
|
|
|
if (fileSize > MEGABYTE(Setup.MaxVideoFileSize) || RunningLowOnDiskSpace()) {
|
|
|
|
recordFile = fileName->NextFile();
|
|
|
|
fileSize = 0;
|
|
|
|
}
|
|
|
|
}
|
2005-10-31 13:14:26 +01:00
|
|
|
return recordFile != NULL;
|
2002-06-16 12:57:31 +02:00
|
|
|
}
|
|
|
|
|
2004-10-16 09:36:28 +02:00
|
|
|
void cFileWriter::Action(void)
|
2002-06-16 12:57:31 +02:00
|
|
|
{
|
2003-05-16 13:36:06 +02:00
|
|
|
time_t t = time(NULL);
|
2005-08-14 11:24:57 +02:00
|
|
|
while (Running()) {
|
2004-10-16 09:36:28 +02:00
|
|
|
int Count;
|
|
|
|
uchar *p = remux->Get(Count, &pictureType);
|
|
|
|
if (p) {
|
2005-08-14 11:24:57 +02:00
|
|
|
if (!Running() && pictureType == I_FRAME) // finish the recording before the next 'I' frame
|
2004-10-16 09:36:28 +02:00
|
|
|
break;
|
|
|
|
if (NextFile()) {
|
|
|
|
if (index && pictureType != NO_PICTURE)
|
|
|
|
index->Write(pictureType, fileName->Number(), fileSize);
|
2005-10-31 13:14:26 +01:00
|
|
|
if (recordFile->Write(p, Count) < 0) {
|
2004-10-16 09:36:28 +02:00
|
|
|
LOG_ERROR_STR(fileName->Name());
|
2002-06-16 12:57:31 +02:00
|
|
|
break;
|
|
|
|
}
|
2004-10-16 09:36:28 +02:00
|
|
|
fileSize += Count;
|
|
|
|
remux->Del(Count);
|
2002-06-16 12:57:31 +02:00
|
|
|
}
|
2004-10-16 09:36:28 +02:00
|
|
|
else
|
|
|
|
break;
|
2003-05-16 13:36:06 +02:00
|
|
|
t = time(NULL);
|
|
|
|
}
|
|
|
|
else if (time(NULL) - t > MAXBROKENTIMEOUT) {
|
|
|
|
esyslog("ERROR: video data stream broken");
|
|
|
|
cThread::EmergencyExit(true);
|
|
|
|
t = time(NULL);
|
2002-06-16 12:57:31 +02:00
|
|
|
}
|
2004-10-16 09:36:28 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-01-07 14:46:14 +01:00
|
|
|
// --- 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)
|
2004-10-16 09:36:28 +02:00
|
|
|
,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);
|
2005-01-16 14:40:47 +01:00
|
|
|
remux = new cRemux(VPid, APids, Setup.UseDolbyDigital ? DPids : NULL, SPids, true);
|
2004-10-16 09:36:28 +02:00
|
|
|
writer = new cFileWriter(FileName, remux);
|
|
|
|
}
|
|
|
|
|
|
|
|
cRecorder::~cRecorder()
|
|
|
|
{
|
|
|
|
Detach();
|
|
|
|
delete writer;
|
|
|
|
delete remux;
|
|
|
|
delete ringBuffer;
|
|
|
|
}
|
|
|
|
|
|
|
|
void cRecorder::Activate(bool On)
|
|
|
|
{
|
|
|
|
if (On) {
|
|
|
|
writer->Start();
|
|
|
|
Start();
|
|
|
|
}
|
2005-08-13 13:17:24 +02:00
|
|
|
else
|
2004-10-16 09:36:28 +02:00
|
|
|
Cancel(3);
|
|
|
|
}
|
|
|
|
|
|
|
|
void cRecorder::Receive(uchar *Data, int Length)
|
|
|
|
{
|
2005-08-14 11:24:57 +02:00
|
|
|
if (Running()) {
|
2004-10-16 09:36:28 +02:00
|
|
|
int p = ringBuffer->Put(Data, Length);
|
2005-08-14 11:24:57 +02:00
|
|
|
if (p != Length && Running())
|
2004-10-16 09:36:28 +02:00
|
|
|
ringBuffer->ReportOverflow(Length - p);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void cRecorder::Action(void)
|
|
|
|
{
|
2005-08-14 11:24:57 +02:00
|
|
|
while (Running()) {
|
2004-10-16 09:36:28 +02:00
|
|
|
int r;
|
|
|
|
uchar *b = ringBuffer->Get(r);
|
|
|
|
if (b) {
|
|
|
|
int Count = remux->Put(b, r);
|
|
|
|
if (Count)
|
|
|
|
ringBuffer->Del(Count);
|
2006-01-08 11:03:44 +01:00
|
|
|
else
|
|
|
|
cCondWait::SleepMs(100); // avoid busy loop when resultBuffer is full in cRemux::Put()
|
2004-10-16 09:36:28 +02:00
|
|
|
}
|
2002-06-16 12:57:31 +02:00
|
|
|
}
|
|
|
|
}
|