vdr  2.4.7
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 4.4 2015/09/12 14:56:15 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 30000 // milliseconds
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(const uchar *Data, int Length)
124 {
125  if (Running()) {
126  static const uchar aff[TS_SIZE - 4] = { 0xB7, 0x00,
127  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
128  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
129  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
130  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
131  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
132  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
133  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
134  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
135  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
136  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
137  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
138  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
139  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
140  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
141  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
142  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
143  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
144  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
145  0xFF, 0xFF}; // Length is always TS_SIZE!
146  if ((Data[3] & 0b00110000) == 0b00100000 && !memcmp(Data + 4, aff, sizeof(aff)))
147  return; // Adaptation Field Filler found, skipping
148  int p = ringBuffer->Put(Data, Length);
149  if (p != Length && Running())
150  ringBuffer->ReportOverflow(Length - p);
151  }
152 }
153 
155 {
157  bool InfoWritten = false;
158  bool FirstIframeSeen = false;
159  while (Running()) {
160  int r;
161  uchar *b = ringBuffer->Get(r);
162  if (b) {
163  int Count = frameDetector->Analyze(b, r);
164  if (Count) {
165  if (!Running() && frameDetector->IndependentFrame()) // finish the recording before the next independent frame
166  break;
167  if (frameDetector->Synced()) {
168  if (!InfoWritten) {
169  cRecordingInfo RecordingInfo(recordingName);
170  if (RecordingInfo.Read()) {
173  RecordingInfo.Write();
175  Recordings->UpdateByName(recordingName);
176  }
177  }
178  InfoWritten = true;
180  }
181  if (FirstIframeSeen || frameDetector->IndependentFrame()) {
182  FirstIframeSeen = true; // start recording with the first I-frame
183  if (!NextFile())
184  break;
185  if (index && frameDetector->NewFrame())
189  fileSize += TS_SIZE;
190  int Index = 0;
191  while (uchar *pmt = patPmtGenerator.GetPmt(Index)) {
192  recordFile->Write(pmt, TS_SIZE);
193  fileSize += TS_SIZE;
194  }
196  }
197  if (naluStreamProcessor) {
198  naluStreamProcessor->PutBuffer(b, Count);
199  bool Fail = false;
200  while (true) {
201  int OutLength = 0;
202  uchar *OutData = naluStreamProcessor->GetBuffer(OutLength);
203  if (!OutData || OutLength <= 0)
204  break;
205  if (recordFile->Write(OutData, OutLength) < 0) {
207  Fail = true;
208  break;
209  }
210  fileSize += OutLength;
211  }
212  if (Fail)
213  break;
214  }
215  else {
216  if (recordFile->Write(b, Count) < 0) {
218  break;
219  }
220  fileSize += Count;
221  }
222 
223  }
224  }
225  ringBuffer->Del(Count);
226  }
227  }
228  if (t.TimedOut()) {
229  esyslog("ERROR: video data stream broken");
232  }
233  }
234 }
int Vpid(void) const
Definition: channels.h:154
int Dpid(int i) const
Definition: channels.h:161
int Vtype(void) const
Definition: channels.h:156
int Apid(int i) const
Definition: channels.h:160
cUnbufferedFile * NextFile(void)
Definition: recording.c:3048
uint16_t Number(void)
Definition: recording.h:504
cUnbufferedFile * Open(void)
Definition: recording.c:2972
bool GetLastPatPmtVersions(int &PatVersion, int &PmtVersion)
Definition: recording.c:2921
const char * Name(void)
Definition: recording.h:503
bool Synced(void)
Returns true if the frame detector has synced on the data stream.
Definition: remux.h:544
bool IndependentFrame(void)
Returns true if a new frame was detected and this is an independent frame (i.e.
Definition: remux.h:549
double FramesPerSecond(void)
Returns the number of frames per second, or 0 if this information is not available.
Definition: remux.h:553
int Analyze(const uchar *Data, int Length)
Analyzes the TS packets pointed to by Data.
Definition: remux.c:1690
bool NewFrame(void)
Returns true if the data given to the last call to Analyze() started a new frame.
Definition: remux.h:546
bool Write(bool Independent, uint16_t FileNumber, off_t FileOffset)
Definition: recording.c:2712
void SetPid(int VPid)
Definition: remux.h:618
long long int GetTotalPackets()
Definition: remux.h:628
void PutBuffer(uchar *Data, int Length)
Definition: remux.c:2004
uchar * GetBuffer(int &OutLength)
Definition: remux.c:2013
long long int GetDroppedPackets()
Definition: remux.h:629
uchar * GetPmt(int &Index)
Returns a pointer to the Index'th TS packet of the PMT section.
Definition: remux.c:636
void SetChannel(const cChannel *Channel)
Sets the Channel for which the PAT/PMT shall be generated.
Definition: remux.c:621
void SetVersions(int PatVersion, int PmtVersion)
Sets the version numbers for the generated PAT and PMT, in case this generator is used to,...
Definition: remux.c:615
uchar * GetPat(void)
Returns a pointer to the PAT section, which consists of exactly one TS packet.
Definition: remux.c:630
void Detach(void)
Definition: receiver.c:125
cRecorder(const char *FileName, const cChannel *Channel, int Priority)
Creates a new recorder for the given Channel and the given Priority that will record into the file Fi...
Definition: recorder.c:24
bool NextFile(void)
Definition: recorder.c:104
cFileName * fileName
Definition: recorder.h:25
cIndexFile * index
Definition: recorder.h:26
virtual void Receive(const uchar *Data, int Length)
This function is called from the cDevice we are attached to, and delivers one TS packet from the set ...
Definition: recorder.c:123
off_t fileSize
Definition: recorder.h:29
virtual void Action(void)
A derived cThread class must implement the code it wants to execute as a separate thread in this func...
Definition: recorder.c:154
cFrameDetector * frameDetector
Definition: recorder.h:22
time_t lastDiskSpaceCheck
Definition: recorder.h:30
cUnbufferedFile * recordFile
Definition: recorder.h:27
cRingBufferLinear * ringBuffer
Definition: recorder.h:21
char * recordingName
Definition: recorder.h:28
bool RunningLowOnDiskSpace(void)
Definition: recorder.c:91
virtual ~cRecorder()
Definition: recorder.c:75
virtual void Activate(bool On)
If you override Activate() you need to call Detach() (which is a member of the cReceiver class) from ...
Definition: recorder.c:115
cNaluStreamProcessor * naluStreamProcessor
Definition: recorder.h:24
cPatPmtGenerator patPmtGenerator
Definition: recorder.h:23
void SetFramesPerSecond(double FramesPerSecond)
Definition: recording.c:447
bool Write(FILE *f, const char *Prefix="") const
Definition: recording.c:518
bool Read(FILE *f)
Definition: recording.c:459
double FramesPerSecond(void) const
Definition: recording.h:93
static void InvokeCommand(const char *State, const char *RecordingFileName, const char *SourceFileName=NULL)
Definition: recording.c:2314
void Del(int Count)
Deletes at most Count bytes from the ring buffer.
Definition: ringbuffer.c:371
int Put(const uchar *Data, int Count)
Puts at most Count bytes of Data into the ring buffer.
Definition: ringbuffer.c:306
uchar * Get(int &Count)
Gets data from the ring buffer.
Definition: ringbuffer.c:346
void SetTimeouts(int PutTimeout, int GetTimeout)
Definition: ringbuffer.c:89
void SetIoThrottle(void)
Definition: ringbuffer.c:95
void ReportOverflow(int Bytes)
Definition: ringbuffer.c:101
int DumpNaluFill
Definition: config.h:340
int MaxVideoFileSize
Definition: config.h:337
void RequestEmergencyExit(void)
Requests an emergency exit of the VDR main loop.
Definition: shutdown.c:93
Definition: thread.h:79
void bool Start(void)
Sets the description of this thread, which will be used when logging starting or stopping of the thre...
Definition: thread.c:304
bool Running(void)
Returns false if a derived cThread object shall leave its Action() function.
Definition: thread.h:101
void Cancel(int WaitSeconds=0)
Cancels the thread by first setting 'running' to false, so that the Action() loop can finish in an or...
Definition: thread.c:354
Definition: tools.h:367
void Set(int Ms=0)
Sets the timer.
Definition: tools.c:776
bool TimedOut(void) const
Definition: tools.c:781
ssize_t Write(const void *Data, size_t Size)
Definition: tools.c:1921
cSetup Setup
Definition: config.c:372
#define MAXBROKENTIMEOUT
Definition: recorder.c:17
#define DISKCHECKINTERVAL
Definition: recorder.c:20
#define MINFREEDISKSPACE
Definition: recorder.c:19
#define RECORDERBUFSIZE
Definition: recorder.c:13
#define DEFAULTFRAMESPERSECOND
Definition: recording.h:354
#define LOCK_RECORDINGS_WRITE
Definition: recording.h:310
#define RUC_STARTRECORDING
Definition: recording.h:424
#define TS_SIZE
Definition: remux.h:34
#define MIN_TS_PACKETS_FOR_FRAME_DETECTOR
Definition: remux.h:509
cShutdownHandler ShutdownHandler
Definition: shutdown.c:27
int FreeDiskSpaceMB(const char *Directory, int *UsedMB)
Definition: tools.c:448
bool SpinUpDisk(const char *FileName)
Definition: tools.c:669
#define MEGABYTE(n)
Definition: tools.h:45
#define LOG_ERROR_STR(s)
Definition: tools.h:40
unsigned char uchar
Definition: tools.h:31
#define dsyslog(a...)
Definition: tools.h:37
bool DoubleEqual(double a, double b)
Definition: tools.h:93
#define esyslog(a...)
Definition: tools.h:35
#define isyslog(a...)
Definition: tools.h:36