vdr/recorder.c
Klaus Schmidinger 1aadb31fb3 Version 1.7.5
- Fixed a hangup when replaying a TS recording with subtitles activated (reported
  by Timo Helkio).
- Fixed handling the 'new' indicator in the recordings menu for TS recordings
  (thanks to Derek Kelly).
- Added cap_sys_nice to the capabilities that are not dropped (thanks to Rolf
  Ahrenberg).
- Updated the Italian OSD texts (thanks to Diego Pierotto).
- Added cRecordingInfo::GetEvent() (thanks to Marcel Unbehaun).
- Improved synchronizing the progress display, trick modes and subtitle display
  to the actual audio/video. This now works independent of any buffer sizes the
  output device might use.
  + The cBackTrace class has been replaced with cPtsIndex, which keeps track
    of the PTS timestamps of recently played frames.
  + cDevice::GetSTC() is now required to deliver the STC even in trick modes.
    It is sufficient if it returns the PTS of the most recently presented
    audio/video frame.
  + The full-featured DVB cards need an improved firmware in order to return
    proper STC values in trick modes (thanks to Oliver Endriss for enhancing the
    av7110 firmware).
- Adapted cFrameDetector::Analyze() to HD NTSC broadcasts that split frames over
  several payload units (thanks to Derek Kelly for reporting this and helping in
  testing).
- Modified cFrameDetector::Analyze() to make it process whole frames at once, so
  that file I/O overhead is minimized during recording (reported by Günter
  Niedermeier).
- Added command line help for the '-i' option.
- Fixed cDvbPlayer::NextFile() to handle files larger than 2GB (thanks to Jose
  Alberto Reguero).
- Improved replay at the begin and end of a recording. The very first and very last
  frame is now sent to the output device repeatedly until GetSTC() reports that it
  has been played. cDvbPlayer::Action() no longer calls DeviceFlush() (thanks to
  Reinhard Nissl for making sure vdr-xine no longer needs this).
- Added missing '[]' to the delete operator in cMenuEditStrItem::~cMenuEditStrItem().
- Added missing virtual destructor to cPalette.
- Now freeing configDirectory before setting it to a new value in
  cPlugin::SetConfigDirectory().
- Fixed a crash when jumping to an editing mark in an audio recording.
- Fixed the 'VideoOnly' condition in the PlayPes() and PlayTs() calls in
  cDvbPlayer::Action() (thanks to Reinhard Nissl).
- cDevice::PlayTs() now plays as many TS packets as possible in one call.
- Making sure any floating point numbers written use a decimal point (thanks to
  Oliver Endriss for pointing out a problem with the F record in the info file of
  a recording).
- Fixed detecting the frame rate for radio recordings.
- Added missing AUDIO_PAUSE/AUDIO_CONTINUE calls to cDvbDevice (thanks to Oliver
  Endriss).
- No longer writing the video type into channels.conf if VPID is 0 (thanks to
  Oliver Endriss for reporting this).
- Improved efficiency of cEIT::cEIT() (thanks to Tobias Bratfisch).
2009-04-12 11:39:00 +02:00

165 lines
4.9 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 2.3 2009/03/20 15:49:02 kls Exp $
*/
#include "recorder.h"
#include "shutdown.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
// --- 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")
,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.SetChannel(Channel);
fileName = NULL;
index = NULL;
fileSize = 0;
lastDiskSpaceCheck = time(NULL);
fileName = new cFileName(FileName, true);
recordFile = fileName->Open();
if (!recordFile)
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
}
cRecorder::~cRecorder()
{
Detach();
delete index;
delete fileName;
delete frameDetector;
delete ringBuffer;
}
bool cRecorder::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 cRecorder::NextFile(void)
{
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;
}
}
return recordFile != NULL;
}
void cRecorder::Activate(bool On)
{
if (On)
Start();
else
Cancel(3);
}
void cRecorder::Receive(uchar *Data, int Length)
{
if (Running()) {
int p = ringBuffer->Put(Data, Length);
if (p != Length && Running())
ringBuffer->ReportOverflow(Length - p);
}
}
void cRecorder::Action(void)
{
time_t t = time(NULL);
bool InfoWritten = false;
while (Running()) {
int r;
uchar *b = ringBuffer->Get(r);
if (b) {
int Count = frameDetector->Analyze(b, r);
if (Count) {
if (!Running() && frameDetector->IndependentFrame()) // finish the recording before the next independent frame
break;
if (frameDetector->Synced()) {
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);
}
}
if (time(NULL) - t > MAXBROKENTIMEOUT) {
esyslog("ERROR: video data stream broken");
ShutdownHandler.RequestEmergencyExit();
t = time(NULL);
}
}
}