vdr  2.4.1
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 }
cIndexFile
Definition: recording.h:451
TS_SIZE
#define TS_SIZE
Definition: remux.h:34
cNaluStreamProcessor
Definition: remux.h:602
cNaluStreamProcessor::SetPid
void SetPid(int VPid)
Definition: remux.h:618
cPatPmtGenerator::GetPmt
uchar * GetPmt(int &Index)
Returns a pointer to the Index'th TS packet of the PMT section.
Definition: remux.c:636
cRecordingInfo::SetFramesPerSecond
void SetFramesPerSecond(double FramesPerSecond)
Definition: recording.c:447
recorder.h
cRingBuffer::SetTimeouts
void SetTimeouts(int PutTimeout, int GetTimeout)
Definition: ringbuffer.c:89
cReceiver
Definition: receiver.h:17
cRecorder::fileSize
off_t fileSize
Definition: recorder.h:29
cRingBufferLinear::Put
int Put(const uchar *Data, int Count)
Puts at most Count bytes of Data into the ring buffer.
Definition: ringbuffer.c:306
cChannel::Dpid
int Dpid(int i) const
Definition: channels.h:161
cFrameDetector::Synced
bool Synced(void)
Returns true if the frame detector has synced on the data stream.
Definition: remux.h:544
cRecorder::Action
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
cSetup::MaxVideoFileSize
int MaxVideoFileSize
Definition: config.h:337
cDevice::Priority
int Priority(void) const
Returns the priority of the current receiving session (-MAXPRIORITY..MAXPRIORITY),...
Definition: device.c:1632
cRecorder::naluStreamProcessor
cNaluStreamProcessor * naluStreamProcessor
Definition: recorder.h:24
cFileName::NextFile
cUnbufferedFile * NextFile(void)
Definition: recording.c:3040
cFileName::Open
cUnbufferedFile * Open(void)
Definition: recording.c:2964
cFrameDetector::Analyze
int Analyze(const uchar *Data, int Length)
Analyzes the TS packets pointed to by Data.
Definition: remux.c:1690
MIN_TS_PACKETS_FOR_FRAME_DETECTOR
#define MIN_TS_PACKETS_FOR_FRAME_DETECTOR
Definition: remux.h:509
cRecorder::patPmtGenerator
cPatPmtGenerator patPmtGenerator
Definition: recorder.h:23
cRecordingInfo::FramesPerSecond
double FramesPerSecond(void) const
Definition: recording.h:90
cRecorder::Receive
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
cThread::Cancel
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
FreeDiskSpaceMB
int FreeDiskSpaceMB(const char *Directory, int *UsedMB)
Definition: tools.c:446
cFileName::GetLastPatPmtVersions
bool GetLastPatPmtVersions(int &PatVersion, int &PmtVersion)
Definition: recording.c:2913
Setup
cSetup Setup
Definition: config.c:372
ShutdownHandler
cShutdownHandler ShutdownHandler
Definition: shutdown.c:27
cTimeMs::Set
void Set(int Ms=0)
Definition: tools.c:774
shutdown.h
cRecordingInfo
Definition: recording.h:63
MAXBROKENTIMEOUT
#define MAXBROKENTIMEOUT
Definition: recorder.c:17
cFileName::Number
uint16_t Number(void)
Definition: recording.h:501
RUC_STARTRECORDING
#define RUC_STARTRECORDING
Definition: recording.h:421
cRecorder::recordingName
char * recordingName
Definition: recorder.h:28
DoubleEqual
bool DoubleEqual(double a, double b)
Definition: tools.h:95
cRecordingUserCommand::InvokeCommand
static void InvokeCommand(const char *State, const char *RecordingFileName, const char *SourceFileName=NULL)
Definition: recording.c:2306
cThread::Running
bool Running(void)
Returns false if a derived cThread object shall leave its Action() function.
Definition: thread.h:101
cRecorder::frameDetector
cFrameDetector * frameDetector
Definition: recorder.h:22
cFileName
Definition: recording.h:489
cRingBufferLinear::Del
void Del(int Count)
Deletes at most Count bytes from the ring buffer.
Definition: ringbuffer.c:371
uchar
unsigned char uchar
Definition: tools.h:31
cNaluStreamProcessor::GetTotalPackets
long long int GetTotalPackets()
Definition: remux.h:628
cRecorder::cRecorder
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
cRecorder::fileName
cFileName * fileName
Definition: recorder.h:25
cSetup::DumpNaluFill
int DumpNaluFill
Definition: config.h:340
cNaluStreamProcessor::GetDroppedPackets
long long int GetDroppedPackets()
Definition: remux.h:629
cRecorder::ringBuffer
cRingBufferLinear * ringBuffer
Definition: recorder.h:21
cPatPmtGenerator::GetPat
uchar * GetPat(void)
Returns a pointer to the PAT section, which consists of exactly one TS packet.
Definition: remux.c:630
cRecorder::index
cIndexFile * index
Definition: recorder.h:26
cTimeMs
Definition: tools.h:369
cRingBuffer::SetIoThrottle
void SetIoThrottle(void)
Definition: ringbuffer.c:95
DEFAULTFRAMESPERSECOND
#define DEFAULTFRAMESPERSECOND
Definition: recording.h:351
cShutdownHandler::RequestEmergencyExit
void RequestEmergencyExit(void)
Requests an emergency exit of the VDR main loop.
Definition: shutdown.c:93
cRecorder::~cRecorder
virtual ~cRecorder()
Definition: recorder.c:75
cPatPmtGenerator::SetVersions
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
cChannel::Vpid
int Vpid(void) const
Definition: channels.h:154
cRingBufferLinear::Get
uchar * Get(int &Count)
Gets data from the ring buffer.
Definition: ringbuffer.c:346
dsyslog
#define dsyslog(a...)
Definition: tools.h:37
cRecorder::Activate
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
cChannel::Vtype
int Vtype(void) const
Definition: channels.h:156
cChannel
Definition: channels.h:89
cReceiver::Detach
void Detach(void)
Definition: receiver.c:125
cChannel::Apid
int Apid(int i) const
Definition: channels.h:160
cNaluStreamProcessor::GetBuffer
uchar * GetBuffer(int &OutLength)
Definition: remux.c:2013
cFileName::Name
const char * Name(void)
Definition: recording.h:500
cRecorder::recordFile
cUnbufferedFile * recordFile
Definition: recorder.h:27
SpinUpDisk
bool SpinUpDisk(const char *FileName)
Definition: tools.c:667
MINFREEDISKSPACE
#define MINFREEDISKSPACE
Definition: recorder.c:19
cFrameDetector::IndependentFrame
bool IndependentFrame(void)
Returns true if a new frame was detected and this is an independent frame (i.e.
Definition: remux.h:549
cPatPmtGenerator::SetChannel
void SetChannel(const cChannel *Channel)
Sets the Channel for which the PAT/PMT shall be generated.
Definition: remux.c:621
cThread
Definition: thread.h:79
cIndexFile::Write
bool Write(bool Independent, uint16_t FileNumber, off_t FileOffset)
Definition: recording.c:2704
cUnbufferedFile::Write
ssize_t Write(const void *Data, size_t Size)
Definition: tools.c:1915
cRingBufferLinear
Definition: ringbuffer.h:48
RECORDERBUFSIZE
#define RECORDERBUFSIZE
Definition: recorder.c:13
cTimeMs::TimedOut
bool TimedOut(void) const
Definition: tools.c:779
MEGABYTE
#define MEGABYTE(n)
Definition: tools.h:45
cRecordingInfo::Write
bool Write(FILE *f, const char *Prefix="") const
Definition: recording.c:518
DISKCHECKINTERVAL
#define DISKCHECKINTERVAL
Definition: recorder.c:20
cRecordingInfo::Read
bool Read(FILE *f)
Definition: recording.c:459
cRecorder::NextFile
bool NextFile(void)
Definition: recorder.c:104
cRecorder::RunningLowOnDiskSpace
bool RunningLowOnDiskSpace(void)
Definition: recorder.c:91
cThread::Start
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
cFrameDetector::FramesPerSecond
double FramesPerSecond(void)
Returns the number of frames per second, or 0 if this information is not available.
Definition: remux.h:553
cFrameDetector
Definition: remux.h:513
isyslog
#define isyslog(a...)
Definition: tools.h:36
cFrameDetector::NewFrame
bool NewFrame(void)
Returns true if the data given to the last call to Analyze() started a new frame.
Definition: remux.h:546
cRingBuffer::ReportOverflow
void ReportOverflow(int Bytes)
Definition: ringbuffer.c:101
esyslog
#define esyslog(a...)
Definition: tools.h:35
cRecorder::lastDiskSpaceCheck
time_t lastDiskSpaceCheck
Definition: recorder.h:30
LOG_ERROR_STR
#define LOG_ERROR_STR(s)
Definition: tools.h:40
LOCK_RECORDINGS_WRITE
#define LOCK_RECORDINGS_WRITE
Definition: recording.h:307
cNaluStreamProcessor::PutBuffer
void PutBuffer(uchar *Data, int Length)
Definition: remux.c:2004