vdr
1.7.27
|
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 }