vdr
2.0.2
Main Page
Namespaces
Classes
Files
File List
File Members
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
34
ringBuffer
=
new
cRingBufferLinear
(
RECORDERBUFSIZE
,
MIN_TS_PACKETS_FOR_FRAME_DETECTOR
*
TS_SIZE
,
true
,
"Recorder"
);
35
ringBuffer
->
SetTimeouts
(0, 100);
36
ringBuffer
->
SetIoThrottle
();
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"
);
52
naluStreamProcessor
=
new
cNaluStreamProcessor
();
53
naluStreamProcessor
->
SetPid
(Pid);
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);
65
recordFile
=
fileName
->
Open
();
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
75
cRecorder::~cRecorder
()
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
91
bool
cRecorder::RunningLowOnDiskSpace
(
void
)
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
104
bool
cRecorder::NextFile
(
void
)
105
{
106
if
(
recordFile
&&
frameDetector
->
IndependentFrame
()) {
// every file shall start with an independent frame
107
if
(
fileSize
>
MEGABYTE
(off_t(
Setup
.
MaxVideoFileSize
)) ||
RunningLowOnDiskSpace
()) {
108
recordFile
=
fileName
->
NextFile
();
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
132
void
cRecorder::Action
(
void
)
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
()) {
149
if
(
frameDetector
->
FramesPerSecond
() > 0 &&
DoubleEqual
(RecordingInfo.
FramesPerSecond
(),
DEFAULTFRAMESPERSECOND
) && !
DoubleEqual
(RecordingInfo.
FramesPerSecond
(),
frameDetector
->
FramesPerSecond
())) {
150
RecordingInfo.
SetFramesPerSecond
(
frameDetector
->
FramesPerSecond
());
151
RecordingInfo.
Write
();
152
Recordings
.
UpdateByName
(
recordingName
);
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
())
162
index
->
Write
(
frameDetector
->
IndependentFrame
(),
fileName
->
Number
(),
fileSize
);
163
if
(
frameDetector
->
IndependentFrame
()) {
164
recordFile
->
Write
(
patPmtGenerator
.
GetPat
(),
TS_SIZE
);
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) {
181
LOG_ERROR_STR
(
fileName
->
Name
());
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) {
192
LOG_ERROR_STR
(
fileName
->
Name
());
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"
);
206
ShutdownHandler
.
RequestEmergencyExit
();
207
t = time(NULL);
208
}
209
}
210
}
211
Generated by
1.8.3.1