vdr  2.0.2
recorder.c
Go to the documentation of this file.
1 /*
2  * recorder.c: The actual DVB recorder
3  *
4  * See the main source file 'vdr.c' for copyright information and
5  * how to reach the author.
6  *
7  * $Id: recorder.c 2.17 2012/09/22 11:53:57 kls Exp $
8  */
9 
10 #include "recorder.h"
11 #include "shutdown.h"
12 
13 #define RECORDERBUFSIZE (MEGABYTE(20) / TS_SIZE * TS_SIZE) // multiple of TS_SIZE
14 
15 // The maximum time we wait before assuming that a recorded video data stream
16 // is broken:
17 #define MAXBROKENTIMEOUT 30 // seconds
18 
19 #define MINFREEDISKSPACE (512) // MB
20 #define DISKCHECKINTERVAL 100 // seconds
21 
22 // --- cRecorder -------------------------------------------------------------
23 
24 cRecorder::cRecorder(const char *FileName, const cChannel *Channel, int Priority)
25 :cReceiver(Channel, Priority)
26 ,cThread("recording")
27 {
28  recordingName = strdup(FileName);
29 
30  // Make sure the disk is up and running:
31 
32  SpinUpDisk(FileName);
33 
35  ringBuffer->SetTimeouts(0, 100);
37 
38  int Pid = Channel->Vpid();
39  int Type = Channel->Vtype();
40  if (!Pid && Channel->Apid(0)) {
41  Pid = Channel->Apid(0);
42  Type = 0x04;
43  }
44  if (!Pid && Channel->Dpid(0)) {
45  Pid = Channel->Dpid(0);
46  Type = 0x06;
47  }
48  frameDetector = new cFrameDetector(Pid, Type);
49  if ( Type == 0x1B // MPEG4 video
50  && (Setup.DumpNaluFill ? (strstr(FileName, "NALUKEEP") == NULL) : (strstr(FileName, "NALUDUMP") != NULL))) { // MPEG4
51  isyslog("Starting NALU fill dumper");
54  }
55  else
56  naluStreamProcessor = NULL;
57  index = NULL;
58  fileSize = 0;
59  lastDiskSpaceCheck = time(NULL);
60  fileName = new cFileName(FileName, true);
61  int PatVersion, PmtVersion;
62  if (fileName->GetLastPatPmtVersions(PatVersion, PmtVersion))
63  patPmtGenerator.SetVersions(PatVersion + 1, PmtVersion + 1);
64  patPmtGenerator.SetChannel(Channel);
66  if (!recordFile)
67  return;
68  // Create the index file:
69  index = new cIndexFile(FileName, true);
70  if (!index)
71  esyslog("ERROR: can't allocate index");
72  // let's continue without index, so we'll at least have the recording
73 }
74 
76 {
77  Detach();
78  if (naluStreamProcessor) {
79  long long int TotalPackets = naluStreamProcessor->GetTotalPackets();
80  long long int DroppedPackets = naluStreamProcessor->GetDroppedPackets();
81  isyslog("NALU fill dumper: %lld of %lld packets dropped, %lli%%", DroppedPackets, TotalPackets, TotalPackets ? DroppedPackets*100/TotalPackets : 0);
82  delete naluStreamProcessor;
83  }
84  delete index;
85  delete fileName;
86  delete frameDetector;
87  delete ringBuffer;
88  free(recordingName);
89 }
90 
92 {
93  if (time(NULL) > lastDiskSpaceCheck + DISKCHECKINTERVAL) {
94  int Free = FreeDiskSpaceMB(fileName->Name());
95  lastDiskSpaceCheck = time(NULL);
96  if (Free < MINFREEDISKSPACE) {
97  dsyslog("low disk space (%d MB, limit is %d MB)", Free, MINFREEDISKSPACE);
98  return true;
99  }
100  }
101  return false;
102 }
103 
105 {
106  if (recordFile && frameDetector->IndependentFrame()) { // every file shall start with an independent frame
109  fileSize = 0;
110  }
111  }
112  return recordFile != NULL;
113 }
114 
115 void cRecorder::Activate(bool On)
116 {
117  if (On)
118  Start();
119  else
120  Cancel(3);
121 }
122 
123 void cRecorder::Receive(uchar *Data, int Length)
124 {
125  if (Running()) {
126  int p = ringBuffer->Put(Data, Length);
127  if (p != Length && Running())
128  ringBuffer->ReportOverflow(Length - p);
129  }
130 }
131 
133 {
134  time_t t = time(NULL);
135  bool InfoWritten = false;
136  bool FirstIframeSeen = false;
137  while (Running()) {
138  int r;
139  uchar *b = ringBuffer->Get(r);
140  if (b) {
141  int Count = frameDetector->Analyze(b, r);
142  if (Count) {
143  if (!Running() && frameDetector->IndependentFrame()) // finish the recording before the next independent frame
144  break;
145  if (frameDetector->Synced()) {
146  if (!InfoWritten) {
147  cRecordingInfo RecordingInfo(recordingName);
148  if (RecordingInfo.Read()) {
151  RecordingInfo.Write();
153  }
154  }
155  InfoWritten = true;
156  }
157  if (FirstIframeSeen || frameDetector->IndependentFrame()) {
158  FirstIframeSeen = true; // start recording with the first I-frame
159  if (!NextFile())
160  break;
161  if (index && frameDetector->NewFrame())
165  fileSize += TS_SIZE;
166  int Index = 0;
167  while (uchar *pmt = patPmtGenerator.GetPmt(Index)) {
168  recordFile->Write(pmt, TS_SIZE);
169  fileSize += TS_SIZE;
170  }
171  }
172  if (naluStreamProcessor) {
173  naluStreamProcessor->PutBuffer(b, Count);
174  bool Fail = false;
175  while (true) {
176  int OutLength = 0;
177  uchar *OutData = naluStreamProcessor->GetBuffer(OutLength);
178  if (!OutData || OutLength <= 0)
179  break;
180  if (recordFile->Write(OutData, OutLength) < 0) {
182  Fail = true;
183  break;
184  }
185  fileSize += OutLength;
186  }
187  if (Fail)
188  break;
189  }
190  else {
191  if (recordFile->Write(b, Count) < 0) {
193  break;
194  }
195  fileSize += Count;
196  }
197 
198  t = time(NULL);
199  }
200  }
201  ringBuffer->Del(Count);
202  }
203  }
204  if (time(NULL) - t > MAXBROKENTIMEOUT) {
205  esyslog("ERROR: video data stream broken");
207  t = time(NULL);
208  }
209  }
210 }
211