vdr/recorder.c
Klaus Schmidinger e36fe18c48 Version 1.3.19
- Making sure at least the default skin is available at program start in case a
  plugin needs to issue an error message (thanks to Achim Tuffentshammer for
  reporting a crash in such a case). Also checking if there is a current skin
  in cSkins::Message().
- Completed the Finnish OSD texts and fixed internationalization of the text
  for "Setup/DVB/Audio language(s)" (thanks to Rolf Ahrenberg).
- Completed the Estonian OSD texts and switched to iso8859-13 character set
  (thanks to Arthur Konovalov).
- Made cCondWait::SleepMs() sleep at least 3ms to avoid a possible busy wait.
- Fixed canceling the LIRC thread (thanks to Marco Schlüßler for pointing out
  this one).
- The "Green" button in the "Main" menu is now always "Audio", since the audio
  channel might be changed even if there is only one actual audio PID.
- Fixed handling the '-E' option which was broken in version 1.3.18 (thanks to
  Christian Jacobsen for reporting this one).
- Added 'channels.conf.terr' entries for Mainz (thanks to Michael Heyse).
- Implemented cDolbyRepacker for better handling of Dolby Digital PES packets
  (thanks to Reinhard Nissl).
- Fixed playing files with PES packets longer than 2048 byte through the full
  featured DVB card (thanks to Marco Kremer for reporting this one and providing
  a test sample).
- Recording and Transfer Mode now handle more than 2 audio PIDs. For this the
  interfaces of the following functions have been changed:
  cTransferControl::cTransferControl()
  cTransfer::cTransfer()
  cRecorder::cRecorder()
  cReceiver::cReceiver()
  cRemux::cRemux()
- Fixed a possible race condition in cDevice::Action() and cTSBuffer::Action()
  (thanks to Stefan Huelswitt).
- Extended some buffer sizes to allow handling HDTV streams (thanks to Reinhard
  Nissl).
- Added 'channels.conf.terr' entries for Düsseldorf and Köln (thanks to Walter Koch).
- Falling back to 'stereo' when switching channels in case the user had switched
  to 'left' or 'right' (suggested by Rolf Groppe).
- Completed the Danish OSD texts (thanks to Mogens Elneff).
- Recording and Transfer Mode can now handle up to 8 Dolby Digital tracks (thanks
  to Marco Schlüßler for a patch that implements substream handling into
  cDevice::PlayPesPacket(), and Reinhard Nissl for adding substream handling to
  cDolbyRepacker).
- Added PlayPes(NULL, 0) to cTransfer::Action() when clearing the transfer buffer
  to avoid overflows (thanks to Marco Schlüßler for pointing this out).
2005-01-23 18:00:00 +01:00

188 lines
4.5 KiB
C

/*
* recorder.c: The actual DVB recorder
*
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: recorder.c 1.13 2005/01/16 12:53:17 kls Exp $
*/
#include <stdarg.h>
#include <stdio.h>
#include <unistd.h>
#include "recorder.h"
#define RECORDERBUFSIZE MEGABYTE(5)
// The maximum time we wait before assuming that a recorded video data stream
// is broken:
#define MAXBROKENTIMEOUT 30 // seconds
#define MINFREEDISKSPACE (512) // MB
#define DISKCHECKINTERVAL 100 // seconds
class cFileWriter : public cThread {
private:
cRemux *remux;
cFileName *fileName;
cIndexFile *index;
uchar pictureType;
int fileSize;
int recordFile;
bool active;
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")
{
active = false;
fileName = NULL;
remux = Remux;
index = NULL;
pictureType = NO_PICTURE;
fileSize = 0;
lastDiskSpaceCheck = time(NULL);
fileName = new cFileName(FileName, true);
recordFile = fileName->Open();
if (recordFile < 0)
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
}
cFileWriter::~cFileWriter()
{
active = false;
Cancel(3);
delete index;
delete fileName;
}
bool cFileWriter::RunningLowOnDiskSpace(void)
{
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;
}
bool cFileWriter::NextFile(void)
{
if (recordFile >= 0 && pictureType == I_FRAME) { // every file shall start with an I_FRAME
if (fileSize > MEGABYTE(Setup.MaxVideoFileSize) || RunningLowOnDiskSpace()) {
recordFile = fileName->NextFile();
fileSize = 0;
}
}
return recordFile >= 0;
}
void cFileWriter::Action(void)
{
time_t t = time(NULL);
active = true;
while (active) {
int Count;
uchar *p = remux->Get(Count, &pictureType);
if (p) {
//XXX+ active??? see old version (Busy)
if (!active && 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 (safe_write(recordFile, 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");
cThread::EmergencyExit(true);
t = time(NULL);
}
}
active = false;
}
cRecorder::cRecorder(const char *FileName, int Ca, int Priority, int VPid, const int *APids, const int *DPids, const int *SPids)
:cReceiver(Ca, Priority, VPid, APids, Setup.UseDolbyDigital ? DPids : NULL, SPids)
,cThread("recording")
{
active = false;
// 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();
Start();
}
else if (active) {
active = false;
Cancel(3);
}
}
void cRecorder::Receive(uchar *Data, int Length)
{
if (active) {
int p = ringBuffer->Put(Data, Length);
if (p != Length && active)
ringBuffer->ReportOverflow(Length - p);
}
}
void cRecorder::Action(void)
{
active = true;
while (active) {
int r;
uchar *b = ringBuffer->Get(r);
if (b) {
int Count = remux->Put(b, r);
if (Count)
ringBuffer->Del(Count);
}
}
}