vdr  1.7.27
recorder.c
Go to the documentation of this file.
00001 /*
00002  * recorder.c: The actual DVB recorder
00003  *
00004  * See the main source file 'vdr.c' for copyright information and
00005  * how to reach the author.
00006  *
00007  * $Id: recorder.c 2.15 2011/09/04 09:26:44 kls Exp $
00008  */
00009 
00010 #include "recorder.h"
00011 #include "shutdown.h"
00012 
00013 #define RECORDERBUFSIZE  (MEGABYTE(5) / TS_SIZE * TS_SIZE) // multiple of TS_SIZE
00014 
00015 // The maximum time we wait before assuming that a recorded video data stream
00016 // is broken:
00017 #define MAXBROKENTIMEOUT 30 // seconds
00018 
00019 #define MINFREEDISKSPACE    (512) // MB
00020 #define DISKCHECKINTERVAL   100 // seconds
00021 
00022 // --- cRecorder -------------------------------------------------------------
00023 
00024 cRecorder::cRecorder(const char *FileName, const cChannel *Channel, int Priority)
00025 :cReceiver(Channel, Priority)
00026 ,cThread("recording")
00027 {
00028   recordingName = strdup(FileName);
00029 
00030   // Make sure the disk is up and running:
00031 
00032   SpinUpDisk(FileName);
00033 
00034   ringBuffer = new cRingBufferLinear(RECORDERBUFSIZE, MIN_TS_PACKETS_FOR_FRAME_DETECTOR * TS_SIZE, true, "Recorder");
00035   ringBuffer->SetTimeouts(0, 100);
00036 
00037   int Pid = Channel->Vpid();
00038   int Type = Channel->Vtype();
00039   if (!Pid && Channel->Apid(0)) {
00040      Pid = Channel->Apid(0);
00041      Type = 0x04;
00042      }
00043   if (!Pid && Channel->Dpid(0)) {
00044      Pid = Channel->Dpid(0);
00045      Type = 0x06;
00046      }
00047   frameDetector = new cFrameDetector(Pid, Type);
00048   index = NULL;
00049   fileSize = 0;
00050   lastDiskSpaceCheck = time(NULL);
00051   fileName = new cFileName(FileName, true);
00052   int PatVersion, PmtVersion;
00053   if (fileName->GetLastPatPmtVersions(PatVersion, PmtVersion))
00054      patPmtGenerator.SetVersions(PatVersion + 1, PmtVersion + 1);
00055   patPmtGenerator.SetChannel(Channel);
00056   recordFile = fileName->Open();
00057   if (!recordFile)
00058      return;
00059   // Create the index file:
00060   index = new cIndexFile(FileName, true);
00061   if (!index)
00062      esyslog("ERROR: can't allocate index");
00063      // let's continue without index, so we'll at least have the recording
00064 }
00065 
00066 cRecorder::~cRecorder()
00067 {
00068   Detach();
00069   delete index;
00070   delete fileName;
00071   delete frameDetector;
00072   delete ringBuffer;
00073   free(recordingName);
00074 }
00075 
00076 bool cRecorder::RunningLowOnDiskSpace(void)
00077 {
00078   if (time(NULL) > lastDiskSpaceCheck + DISKCHECKINTERVAL) {
00079      int Free = FreeDiskSpaceMB(fileName->Name());
00080      lastDiskSpaceCheck = time(NULL);
00081      if (Free < MINFREEDISKSPACE) {
00082         dsyslog("low disk space (%d MB, limit is %d MB)", Free, MINFREEDISKSPACE);
00083         return true;
00084         }
00085      }
00086   return false;
00087 }
00088 
00089 bool cRecorder::NextFile(void)
00090 {
00091   if (recordFile && frameDetector->IndependentFrame()) { // every file shall start with an independent frame
00092      if (fileSize > fileName->MaxFileSize() || RunningLowOnDiskSpace()) {
00093         recordFile = fileName->NextFile();
00094         fileSize = 0;
00095         }
00096      }
00097   return recordFile != NULL;
00098 }
00099 
00100 void cRecorder::Activate(bool On)
00101 {
00102   if (On)
00103      Start();
00104   else
00105      Cancel(3);
00106 }
00107 
00108 void cRecorder::Receive(uchar *Data, int Length)
00109 {
00110   if (Running()) {
00111      int p = ringBuffer->Put(Data, Length);
00112      if (p != Length && Running())
00113         ringBuffer->ReportOverflow(Length - p);
00114      }
00115 }
00116 
00117 void cRecorder::Action(void)
00118 {
00119   time_t t = time(NULL);
00120   bool InfoWritten = false;
00121   bool FirstIframeSeen = false;
00122   while (Running()) {
00123         int r;
00124         uchar *b = ringBuffer->Get(r);
00125         if (b) {
00126            int Count = frameDetector->Analyze(b, r);
00127            if (Count) {
00128               if (!Running() && frameDetector->IndependentFrame()) // finish the recording before the next independent frame
00129                  break;
00130               if (frameDetector->Synced()) {
00131                  if (!InfoWritten) {
00132                     cRecordingInfo RecordingInfo(recordingName);
00133                     if (RecordingInfo.Read()) {
00134                        if (frameDetector->FramesPerSecond() > 0 && DoubleEqual(RecordingInfo.FramesPerSecond(), DEFAULTFRAMESPERSECOND) && !DoubleEqual(RecordingInfo.FramesPerSecond(), frameDetector->FramesPerSecond())) {
00135                           RecordingInfo.SetFramesPerSecond(frameDetector->FramesPerSecond());
00136                           RecordingInfo.Write();
00137                           Recordings.UpdateByName(recordingName);
00138                           }
00139                        }
00140                     InfoWritten = true;
00141                     }
00142                  if (FirstIframeSeen || frameDetector->IndependentFrame()) {
00143                     FirstIframeSeen = true; // start recording with the first I-frame
00144                     if (!NextFile())
00145                        break;
00146                     if (index && frameDetector->NewFrame())
00147                        index->Write(frameDetector->IndependentFrame(), fileName->Number(), fileSize);
00148                     if (frameDetector->IndependentFrame()) {
00149                        recordFile->Write(patPmtGenerator.GetPat(), TS_SIZE);
00150                        fileSize += TS_SIZE;
00151                        int Index = 0;
00152                        while (uchar *pmt = patPmtGenerator.GetPmt(Index)) {
00153                              recordFile->Write(pmt, TS_SIZE);
00154                              fileSize += TS_SIZE;
00155                              }
00156                        }
00157                     if (recordFile->Write(b, Count) < 0) {
00158                        LOG_ERROR_STR(fileName->Name());
00159                        break;
00160                        }
00161                     fileSize += Count;
00162                     t = time(NULL);
00163                     }
00164                  }
00165               ringBuffer->Del(Count);
00166               }
00167            }
00168         if (time(NULL) - t > MAXBROKENTIMEOUT) {
00169            esyslog("ERROR: video data stream broken");
00170            ShutdownHandler.RequestEmergencyExit();
00171            t = time(NULL);
00172            }
00173         }
00174 }