| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package Linux::DVB::DVBT::Ffmpeg ; |
|
2
|
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
=head1 NAME |
|
4
|
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
Linux::DVB::DVBT::Ffmpeg - Helper module for transcoding recorded streams |
|
6
|
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
=head1 SYNOPSIS |
|
8
|
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
use Linux::DVB::DVBT::Ffmpeg ; |
|
10
|
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
13
|
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
Module provides a set of useful routines used for transcoding the recorded .ts transport stream file |
|
15
|
|
|
|
|
|
|
into mpeg/mp4 etc. Use of these routines is entirely optional and does not form part of the base |
|
16
|
|
|
|
|
|
|
DVBT functionality. |
|
17
|
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
Currently supported file formats are: |
|
19
|
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
=over 4 |
|
21
|
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
=item B<.mpeg> - mpeg2 video / audio / subtitles |
|
23
|
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
=item B<.mp4> - mpeg4 video / audio |
|
25
|
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
The mp4 settings are configured to ensure that the file is compatible with playback on the PS3 |
|
27
|
|
|
|
|
|
|
(server by Twonky media server). |
|
28
|
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
Note: if you use this then be prepared for a long wait! On my system, a 2 hour film can take 13 hours to transcode. |
|
30
|
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
=item B<.m2v> - mpeg2 video |
|
32
|
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
=item B<.mp2> - mpeg2 audio |
|
34
|
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
=item B<.mp3> - mpeg3 audio |
|
36
|
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
You may notice ffmpeg reports the error: |
|
38
|
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
lame: output buffer too small |
|
40
|
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
Apparently (accoriding to the ffmpeg developers) this is perfectly fine. For further details see (http://howto-pages.org/ffmpeg/#basicaudio). |
|
42
|
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
=back |
|
44
|
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
Obviously, ffmpeg must be installed on your machine to run these functions. |
|
46
|
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
=head1 SUPPORT |
|
48
|
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
I don't intend to support every possible option in ffmpeg! These routines are provided as being helpful, but you |
|
50
|
|
|
|
|
|
|
can ignore them if you don't like them. |
|
51
|
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
Helpful suggestions/requests may result in my adding functionality, but I don't promise anything! |
|
53
|
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
=cut |
|
55
|
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
#============================================================================================ |
|
58
|
|
|
|
|
|
|
# USES |
|
59
|
|
|
|
|
|
|
#============================================================================================ |
|
60
|
10
|
|
|
10
|
|
37
|
use strict ; |
|
|
10
|
|
|
|
|
12
|
|
|
|
10
|
|
|
|
|
248
|
|
|
61
|
10
|
|
|
10
|
|
36
|
use File::Basename ; |
|
|
10
|
|
|
|
|
12
|
|
|
|
10
|
|
|
|
|
626
|
|
|
62
|
10
|
|
|
10
|
|
37
|
use Data::Dumper ; |
|
|
10
|
|
|
|
|
13
|
|
|
|
10
|
|
|
|
|
3637
|
|
|
63
|
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
#============================================================================================ |
|
65
|
|
|
|
|
|
|
# GLOBALS |
|
66
|
|
|
|
|
|
|
#============================================================================================ |
|
67
|
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
our $VERSION = '2.10' ; |
|
69
|
|
|
|
|
|
|
our $DEBUG = 0 ; |
|
70
|
|
|
|
|
|
|
our $DEBUG_FFMPEG = 0 ; |
|
71
|
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
# margin on video length checks (in seconds) - allow for 3 minutes of padding |
|
73
|
|
|
|
|
|
|
our $DURATION_MARGIN = 180 ; |
|
74
|
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
# Niceness level |
|
76
|
|
|
|
|
|
|
our $NICE = 19 ; |
|
77
|
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
## mpeg4 |
|
79
|
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
# const |
|
81
|
|
|
|
|
|
|
our $vidbitrate='1050kb' ; |
|
82
|
|
|
|
|
|
|
our $bittolerance='200kb' ; |
|
83
|
|
|
|
|
|
|
our $audbitrate='128kb' ; |
|
84
|
|
|
|
|
|
|
our $audchannels=2 ; |
|
85
|
|
|
|
|
|
|
our $threads=2 ; |
|
86
|
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
my $me_method_opt="-me_method" ; |
|
88
|
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
my $common_start = "-vcodec libx264 ". |
|
90
|
|
|
|
|
|
|
"-b $vidbitrate ". |
|
91
|
|
|
|
|
|
|
"-flags +loop ". |
|
92
|
|
|
|
|
|
|
"-cmp +chroma ". |
|
93
|
|
|
|
|
|
|
"-partitions +parti4x4+partp8x8+partb8x8" ; |
|
94
|
|
|
|
|
|
|
my $common_end = "-bf 3 ". |
|
95
|
|
|
|
|
|
|
"-b_strategy 1 ". |
|
96
|
|
|
|
|
|
|
"-threads $threads ". |
|
97
|
|
|
|
|
|
|
"-level 31 ". |
|
98
|
|
|
|
|
|
|
"-coder 1 ". |
|
99
|
|
|
|
|
|
|
"-me_range 16 ". |
|
100
|
|
|
|
|
|
|
"-g 250 ". |
|
101
|
|
|
|
|
|
|
"-keyint_min 25 ". |
|
102
|
|
|
|
|
|
|
"-sc_threshold 40 ". |
|
103
|
|
|
|
|
|
|
"-i_qfactor 0.71 ". |
|
104
|
|
|
|
|
|
|
"-bt $bittolerance ". |
|
105
|
|
|
|
|
|
|
"-rc_eq 'blurCplx^(1-qComp)' ". |
|
106
|
|
|
|
|
|
|
"-qcomp 0.6 ". |
|
107
|
|
|
|
|
|
|
"-qmin 10 ". |
|
108
|
|
|
|
|
|
|
"-qmax 51 ". |
|
109
|
|
|
|
|
|
|
"-qdiff 4 ". |
|
110
|
|
|
|
|
|
|
"-aspect 16:9 ". |
|
111
|
|
|
|
|
|
|
"-y " ; |
|
112
|
|
|
|
|
|
|
my $pass1_codec = "-an" ; |
|
113
|
|
|
|
|
|
|
my $pass2_codec = "-acodec libfaac ". |
|
114
|
|
|
|
|
|
|
"-ac $audchannels ". |
|
115
|
|
|
|
|
|
|
"-ab $audbitrate ". |
|
116
|
|
|
|
|
|
|
"-async 1 ". |
|
117
|
|
|
|
|
|
|
"-f mp4 " ; |
|
118
|
|
|
|
|
|
|
my $pass1_options = "$me_method_opt epzs ". |
|
119
|
|
|
|
|
|
|
"-subq 1 ". |
|
120
|
|
|
|
|
|
|
"-trellis 0 ". |
|
121
|
|
|
|
|
|
|
"-refs 1" ; |
|
122
|
|
|
|
|
|
|
my $pass2_options = "$me_method_opt umh ". |
|
123
|
|
|
|
|
|
|
"-subq 5 ". |
|
124
|
|
|
|
|
|
|
"-trellis 1 ". |
|
125
|
|
|
|
|
|
|
"-refs 5 " . |
|
126
|
|
|
|
|
|
|
"-scodec copy "; |
|
127
|
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
## This is how to transcode the source into the various supported formats |
|
131
|
|
|
|
|
|
|
our %COMMANDS = ( |
|
132
|
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
'ts' => |
|
134
|
|
|
|
|
|
|
'ffmpeg -i "$src" -vcodec copy -acodec copy -scodec copy -async 1 -y "$dest.$ext"', |
|
135
|
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
'mpeg' => [ |
|
137
|
|
|
|
|
|
|
# First try |
|
138
|
|
|
|
|
|
|
'ffmpeg -i "$src" -vcodec copy -acodec copy -scodec copy -async 1 -y "$dest.$ext"', |
|
139
|
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
# Alternate |
|
141
|
|
|
|
|
|
|
'ffmpeg -i "$src" -vcodec mpeg2video -sameq -acodec copy -scodec copy -async 1 -y "$dest.$ext"', |
|
142
|
|
|
|
|
|
|
], |
|
143
|
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
'm2v' => [ |
|
145
|
|
|
|
|
|
|
# First try |
|
146
|
|
|
|
|
|
|
'ffmpeg -i "$src" -vcodec copy -f mpeg2video -y "$dest.$ext"', |
|
147
|
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
# Alternate |
|
149
|
|
|
|
|
|
|
'ffmpeg -i "$src" -vcodec mpeg2video -sameq -f mpeg2video -y "$dest.$ext"', |
|
150
|
|
|
|
|
|
|
], |
|
151
|
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
'mp2' => |
|
153
|
|
|
|
|
|
|
'ffmpeg -i "$src" -acodec copy -f mp2 -y "$dest.$ext"', |
|
154
|
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
'mp3' => |
|
156
|
|
|
|
|
|
|
'ffmpeg -i "$src" -f mp3 -y "$dest.$ext"', |
|
157
|
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
'mp4' => [ |
|
159
|
|
|
|
|
|
|
[ |
|
160
|
|
|
|
|
|
|
# 1st pass |
|
161
|
|
|
|
|
|
|
'ffmpeg -i "$src" ' . "$pass1_codec -pass 1 $common_start $pass1_options $common_end" . ' -y "$temp.$ext"', |
|
162
|
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
# 2nd pass |
|
164
|
|
|
|
|
|
|
'ffmpeg -i "$src" ' . "$pass2_codec -pass 2 $common_start $pass2_options $common_end" . '$title_opt -y "$dest.$ext"', |
|
165
|
|
|
|
|
|
|
], |
|
166
|
|
|
|
|
|
|
], |
|
167
|
|
|
|
|
|
|
) ; |
|
168
|
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
## Order in which to process streams |
|
171
|
|
|
|
|
|
|
my @STREAM_ORDER = qw/video audio subtitle/ ; |
|
172
|
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
## Map the requested audio/video/subtitle streams into a file format |
|
174
|
|
|
|
|
|
|
# |
|
175
|
|
|
|
|
|
|
# file format (extension), output spec regexp, supported audio channels (0, 1, 2+) |
|
176
|
|
|
|
|
|
|
# |
|
177
|
|
|
|
|
|
|
# List is ordered so that preferred file formats are earlier in the list |
|
178
|
|
|
|
|
|
|
# End of lis contain least preferred options. |
|
179
|
|
|
|
|
|
|
# |
|
180
|
|
|
|
|
|
|
our @FORMATS ; |
|
181
|
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
# map file extension into format |
|
183
|
|
|
|
|
|
|
my %FORMATS ; |
|
184
|
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
# Aliases |
|
186
|
|
|
|
|
|
|
my %ALIASES ; |
|
187
|
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
BEGIN { |
|
189
|
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
## Map the requested audio/video/subtitle streams into a file format |
|
193
|
|
|
|
|
|
|
# |
|
194
|
|
|
|
|
|
|
# file format (extension), output spec regexp, supported audio channels (0, 1, 2+) |
|
195
|
|
|
|
|
|
|
# |
|
196
|
|
|
|
|
|
|
# List is ordered so that preferred file formats are earlier in the list |
|
197
|
|
|
|
|
|
|
# End of lis contain least preferred options. |
|
198
|
|
|
|
|
|
|
# |
|
199
|
10
|
|
|
10
|
|
63
|
@FORMATS = ( |
|
200
|
|
|
|
|
|
|
# default |
|
201
|
|
|
|
|
|
|
['mpeg', 'va+s*'], # normal video |
|
202
|
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
['m2v', 'v'], |
|
204
|
|
|
|
|
|
|
['mp2', 'a'], |
|
205
|
|
|
|
|
|
|
['mp3', 'a'], |
|
206
|
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
# catch-alls: |
|
208
|
|
|
|
|
|
|
['mpeg', 'a+s*'], # mpeg can also be a container for just multiple audio etc |
|
209
|
|
|
|
|
|
|
['mpeg', 'vs*'], |
|
210
|
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
['mp4', 'va+s*'], |
|
212
|
|
|
|
|
|
|
['mp4', 'a+s*'], |
|
213
|
|
|
|
|
|
|
['mp4', 'vs*'], |
|
214
|
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
['ts', 'va+s*'], # normal video |
|
216
|
|
|
|
|
|
|
['ts', 'a+s*'], # mpeg can also be a container for just multiple audio etc |
|
217
|
|
|
|
|
|
|
['ts', 'vs*'], |
|
218
|
|
|
|
|
|
|
['ts', '.+'], # must contain at least 1 stream |
|
219
|
|
|
|
|
|
|
) ; |
|
220
|
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
# make a HASH keyed on the file format (extension), where the value is an array of the possible |
|
222
|
|
|
|
|
|
|
# output regexps |
|
223
|
10
|
|
|
|
|
23
|
foreach (@FORMATS) |
|
224
|
|
|
|
|
|
|
{ |
|
225
|
130
|
|
|
|
|
126
|
my ($ext, $regexp) = @$_ ; |
|
226
|
130
|
|
100
|
|
|
297
|
$FORMATS{$ext} ||= [] ; |
|
227
|
130
|
|
|
|
|
85
|
push @{$FORMATS{$ext}}, $regexp ; |
|
|
130
|
|
|
|
|
172
|
|
|
228
|
|
|
|
|
|
|
} |
|
229
|
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
# Create alias list |
|
231
|
|
|
|
|
|
|
%ALIASES = ( |
|
232
|
10
|
|
|
|
|
38
|
'mpg' => 'mpeg', |
|
233
|
|
|
|
|
|
|
'mpeg2' => 'mpeg', |
|
234
|
|
|
|
|
|
|
'mpeg4' => 'mp4', |
|
235
|
|
|
|
|
|
|
'TS' => 'ts', |
|
236
|
|
|
|
|
|
|
) ; |
|
237
|
10
|
|
|
|
|
20
|
foreach (@FORMATS) |
|
238
|
|
|
|
|
|
|
{ |
|
239
|
130
|
|
|
|
|
103
|
my ($ext, $regexp) = @$_ ; |
|
240
|
130
|
|
|
|
|
25815
|
$ALIASES{$ext} = $ext ; |
|
241
|
|
|
|
|
|
|
} |
|
242
|
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
} |
|
244
|
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
#============================================================================================ |
|
247
|
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
=head2 Functions |
|
249
|
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
=over 4 |
|
251
|
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
=cut |
|
253
|
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
#----------------------------------------------------------------------------- |
|
257
|
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
=item B |
|
259
|
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
Transcode the recorded transport stream file into the required format, as specified by $multiplex_info_href. |
|
261
|
|
|
|
|
|
|
|
|
262
|
|
|
|
|
|
|
(Called by L). |
|
263
|
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
Multiplex info HASH ref is of the form: |
|
265
|
|
|
|
|
|
|
|
|
266
|
|
|
|
|
|
|
{ |
|
267
|
|
|
|
|
|
|
'pids' => [ |
|
268
|
|
|
|
|
|
|
{ |
|
269
|
|
|
|
|
|
|
'pid' => Stream PID |
|
270
|
|
|
|
|
|
|
'pidtype' => pid type (video, audio, subtitle) |
|
271
|
|
|
|
|
|
|
}, |
|
272
|
|
|
|
|
|
|
... |
|
273
|
|
|
|
|
|
|
], |
|
274
|
|
|
|
|
|
|
'errors' => [], |
|
275
|
|
|
|
|
|
|
'warnings' => [], |
|
276
|
|
|
|
|
|
|
'lines' => [], |
|
277
|
|
|
|
|
|
|
|
|
278
|
|
|
|
|
|
|
'destfile' => final written file name (set by this function) |
|
279
|
|
|
|
|
|
|
} |
|
280
|
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
$written_files_href is an optional HASH that may be provided by the calling routine to track which files have been written. |
|
282
|
|
|
|
|
|
|
Since the file type (extension) can be adjusted by the routine to match it's supported formats/codecs, there is the chance that |
|
283
|
|
|
|
|
|
|
a file may be accidently over written. The tracking HASH ensures that, if a file was previously written with the same filename, |
|
284
|
|
|
|
|
|
|
then the new file is written with a unique filename. |
|
285
|
|
|
|
|
|
|
|
|
286
|
|
|
|
|
|
|
The 'errors' and 'warnings' ARRAY refs are filled with any error/warning messages as the routine is run. Also, the 'lines' |
|
287
|
|
|
|
|
|
|
ARRAY ref is filled with the ffmpeg output. |
|
288
|
|
|
|
|
|
|
|
|
289
|
|
|
|
|
|
|
If $destfile is undefined, then the routine just checks that $srcfile duration is as expected; setting the errors array if not. |
|
290
|
|
|
|
|
|
|
|
|
291
|
|
|
|
|
|
|
When $destfile is defined, it's file type is checked to ensure that the number of streams (pids) and the stream types are supported |
|
292
|
|
|
|
|
|
|
for this file. If not, then the next preferred file type is used and the destination file adjusted accordingly. The final written |
|
293
|
|
|
|
|
|
|
destination filename is written into the HASH as 'destfile'. |
|
294
|
|
|
|
|
|
|
|
|
295
|
|
|
|
|
|
|
The routine returns 0 on success; non-zero for any error. |
|
296
|
|
|
|
|
|
|
|
|
297
|
|
|
|
|
|
|
=cut |
|
298
|
|
|
|
|
|
|
|
|
299
|
|
|
|
|
|
|
sub ts_transcode |
|
300
|
|
|
|
|
|
|
{ |
|
301
|
0
|
|
|
0
|
1
|
0
|
my ($src, $destfile, $multiplex_info_href, $written_files_href) = @_ ; |
|
302
|
0
|
|
|
|
|
0
|
my $error = 0 ; |
|
303
|
|
|
|
|
|
|
|
|
304
|
0
|
0
|
|
|
|
0
|
print STDERR "ts_transcode($src, $destfile)\n" if $DEBUG ; |
|
305
|
|
|
|
|
|
|
|
|
306
|
|
|
|
|
|
|
## errors, warnings, and output lines are stored in the HASH |
|
307
|
0
|
|
0
|
|
|
0
|
my $errors_aref = ($multiplex_info_href->{'errors'} ||= []) ; |
|
308
|
0
|
|
0
|
|
|
0
|
my $warnings_aref = ($multiplex_info_href->{'warnings'} ||= []) ; |
|
309
|
0
|
|
0
|
|
|
0
|
my $lines_aref = ($multiplex_info_href->{'lines'} ||= []) ; |
|
310
|
|
|
|
|
|
|
|
|
311
|
|
|
|
|
|
|
## if src not specified, then destination is a ts file - just check it's length |
|
312
|
0
|
0
|
|
|
|
0
|
if (! $src) |
|
313
|
|
|
|
|
|
|
{ |
|
314
|
|
|
|
|
|
|
#### Check the destination file duration |
|
315
|
0
|
0
|
|
|
|
0
|
if (! -s "$destfile") |
|
316
|
|
|
|
|
|
|
{ |
|
317
|
0
|
|
|
|
|
0
|
$error = "final file \"$destfile\" zero length" ; |
|
318
|
0
|
|
|
|
|
0
|
push @$errors_aref, $error ; |
|
319
|
0
|
|
|
|
|
0
|
return $error ; |
|
320
|
|
|
|
|
|
|
} |
|
321
|
0
|
|
|
|
|
0
|
my $file_duration = video_duration("$destfile") ; |
|
322
|
0
|
0
|
|
|
|
0
|
if ($file_duration < $multiplex_info_href->{'duration'} - $DURATION_MARGIN) |
|
323
|
|
|
|
|
|
|
{ |
|
324
|
0
|
|
|
|
|
0
|
$error = "Duration of final \"$destfile\" ($file_duration secs) not as expected ($multiplex_info_href->{'duration'} secs)" ; |
|
325
|
0
|
|
|
|
|
0
|
push @$errors_aref, $error ; |
|
326
|
0
|
|
|
|
|
0
|
return $error ; |
|
327
|
|
|
|
|
|
|
} |
|
328
|
|
|
|
|
|
|
} |
|
329
|
|
|
|
|
|
|
else |
|
330
|
|
|
|
|
|
|
{ |
|
331
|
|
|
|
|
|
|
#### Check the source file duration |
|
332
|
0
|
0
|
|
|
|
0
|
if (! -s "$src") |
|
333
|
|
|
|
|
|
|
{ |
|
334
|
0
|
|
|
|
|
0
|
$error = "source file \"$src\" zero length" ; |
|
335
|
0
|
|
|
|
|
0
|
push @$errors_aref, $error ; |
|
336
|
0
|
|
|
|
|
0
|
return $error ; |
|
337
|
|
|
|
|
|
|
} |
|
338
|
0
|
|
|
|
|
0
|
my $file_duration = video_duration("$src") ; |
|
339
|
0
|
0
|
|
|
|
0
|
if ($file_duration < $multiplex_info_href->{'duration'} - $DURATION_MARGIN) |
|
340
|
|
|
|
|
|
|
{ |
|
341
|
0
|
|
|
|
|
0
|
my $warn = "Duration of source \"$src\" ($file_duration secs) not as expected ($multiplex_info_href->{'duration'} secs)" ; |
|
342
|
0
|
|
|
|
|
0
|
push @$warnings_aref, $warn ; |
|
343
|
|
|
|
|
|
|
} |
|
344
|
|
|
|
|
|
|
} |
|
345
|
|
|
|
|
|
|
|
|
346
|
|
|
|
|
|
|
## Save source filename |
|
347
|
0
|
|
|
|
|
0
|
$multiplex_info_href->{'srcfile'} = $src ; |
|
348
|
|
|
|
|
|
|
|
|
349
|
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
|
|
351
|
|
|
|
|
|
|
#### Select the dest file format |
|
352
|
0
|
0
|
|
|
|
0
|
if ($src) |
|
353
|
|
|
|
|
|
|
{ |
|
354
|
|
|
|
|
|
|
# turn the pid types into a valid output spec |
|
355
|
|
|
|
|
|
|
# e.g. vaa for 2 audio + 1 video |
|
356
|
0
|
|
|
|
|
0
|
my $out_spec = _pids_out_spec(@{$multiplex_info_href->{'pids'}}) ; |
|
|
0
|
|
|
|
|
0
|
|
|
357
|
|
|
|
|
|
|
|
|
358
|
|
|
|
|
|
|
# Ensure the file format is correct |
|
359
|
0
|
|
|
|
|
0
|
$error = _sanitise_options(\$destfile, \$out_spec, $errors_aref, $warnings_aref) ; |
|
360
|
0
|
0
|
|
|
|
0
|
return $error if $error ; |
|
361
|
|
|
|
|
|
|
|
|
362
|
|
|
|
|
|
|
|
|
363
|
|
|
|
|
|
|
# check specified filename |
|
364
|
0
|
|
|
|
|
0
|
my ($name, $path, $ext) = fileparse($destfile, '\..*') ; |
|
365
|
0
|
0
|
|
|
|
0
|
$ext = substr $ext, 1 if $ext ; |
|
366
|
0
|
|
|
|
|
0
|
my $dest = "$path$name" ; |
|
367
|
|
|
|
|
|
|
|
|
368
|
|
|
|
|
|
|
# check to see if we've already written this filename |
|
369
|
0
|
0
|
|
|
|
0
|
if (exists($written_files_href->{"$dest.$ext"})) |
|
370
|
|
|
|
|
|
|
{ |
|
371
|
|
|
|
|
|
|
# have to amend the filename so we don't overwrite |
|
372
|
0
|
|
|
|
|
0
|
my $num=1 ; |
|
373
|
0
|
|
|
|
|
0
|
while (exists($written_files_href->{"$dest$num.$ext"})) |
|
374
|
|
|
|
|
|
|
{ |
|
375
|
0
|
|
|
|
|
0
|
++$num ; |
|
376
|
|
|
|
|
|
|
} |
|
377
|
|
|
|
|
|
|
|
|
378
|
|
|
|
|
|
|
# report the change |
|
379
|
0
|
|
|
|
|
0
|
push @$warnings_aref, "Filename \"$dest.$ext\" was modified to \"$dest$num.$ext\" because a previously written file has the same name" ; |
|
380
|
|
|
|
|
|
|
|
|
381
|
|
|
|
|
|
|
# change |
|
382
|
0
|
|
|
|
|
0
|
$dest .= $num ; |
|
383
|
|
|
|
|
|
|
} |
|
384
|
|
|
|
|
|
|
|
|
385
|
|
|
|
|
|
|
# track filenames |
|
386
|
0
|
|
|
|
|
0
|
$written_files_href->{"$dest.$ext"} = 1 ; |
|
387
|
|
|
|
|
|
|
|
|
388
|
|
|
|
|
|
|
# return written filename & extension |
|
389
|
0
|
|
|
|
|
0
|
$multiplex_info_href->{'destfile'} = "$dest.$ext" ; |
|
390
|
0
|
|
|
|
|
0
|
$multiplex_info_href->{'destext'} = ".$ext" ; |
|
391
|
|
|
|
|
|
|
|
|
392
|
|
|
|
|
|
|
|
|
393
|
|
|
|
|
|
|
# make sure the extension is in a form we understand |
|
394
|
0
|
|
|
|
|
0
|
my $aliased_ext = $ALIASES{$ext} ; |
|
395
|
|
|
|
|
|
|
|
|
396
|
0
|
0
|
|
|
|
0
|
print STDERR " + dest=$dest ext=$ext\n" if $DEBUG ; |
|
397
|
|
|
|
|
|
|
|
|
398
|
0
|
0
|
|
|
|
0
|
print STDERR "COMMANDS list for $aliased_ext =" . Data::Dumper->Dump([$COMMANDS{$aliased_ext}]) if $DEBUG >= 5 ; |
|
399
|
|
|
|
|
|
|
|
|
400
|
|
|
|
|
|
|
## Run ffmpeg |
|
401
|
0
|
|
|
|
|
0
|
my $cmds_ref = $COMMANDS{$aliased_ext} ; |
|
402
|
0
|
0
|
|
|
|
0
|
my @cmds = ref($cmds_ref) ? @$cmds_ref : ($cmds_ref) ; |
|
403
|
|
|
|
|
|
|
|
|
404
|
|
|
|
|
|
|
# create extra variables for variable replacement |
|
405
|
0
|
|
|
|
|
0
|
my $temp = "${path}temp$$.$name" ; # used for mp4 |
|
406
|
0
|
|
|
|
|
0
|
my $title_opt = "" ; # used for mp4 |
|
407
|
0
|
0
|
|
|
|
0
|
if ($multiplex_info_href->{'title'}) |
|
408
|
|
|
|
|
|
|
{ |
|
409
|
0
|
|
|
|
|
0
|
$title_opt = "-metadata title=\"$multiplex_info_href->{'title'}\" " ; |
|
410
|
|
|
|
|
|
|
} |
|
411
|
|
|
|
|
|
|
|
|
412
|
|
|
|
|
|
|
# run through alternatives |
|
413
|
0
|
|
|
|
|
0
|
for (my $idx=0; $idx < scalar(@cmds); ++$idx) |
|
414
|
|
|
|
|
|
|
{ |
|
415
|
0
|
|
|
|
|
0
|
my $cmd = $cmds[$idx] ; |
|
416
|
0
|
|
|
|
|
0
|
my $rc=0 ; |
|
417
|
0
|
|
|
|
|
0
|
my $pass_str ; |
|
418
|
|
|
|
|
|
|
|
|
419
|
|
|
|
|
|
|
# if this is an array, then it's a multi-pass algorithm |
|
420
|
0
|
0
|
|
|
|
0
|
my @passes = ref($cmd) ? @$cmd : ($cmd) ; |
|
421
|
0
|
|
|
|
|
0
|
foreach my $pass (@passes) |
|
422
|
|
|
|
|
|
|
{ |
|
423
|
|
|
|
|
|
|
|
|
424
|
0
|
0
|
|
|
|
0
|
print STDERR "PASS: $pass\n" if $DEBUG ; |
|
425
|
0
|
|
|
|
|
0
|
($pass_str = $pass) =~ s/\\/\\\\/g ; |
|
426
|
0
|
|
|
|
|
0
|
$pass_str =~ s/\"/\\\"/g ; |
|
427
|
|
|
|
|
|
|
|
|
428
|
0
|
0
|
|
|
|
0
|
print STDERR "PASS STR: $pass_str\n" if $DEBUG ; |
|
429
|
|
|
|
|
|
|
|
|
430
|
|
|
|
|
|
|
# expand all variables |
|
431
|
0
|
|
|
|
|
0
|
my $args ; |
|
432
|
0
|
|
|
|
|
0
|
eval "\$args = \"$pass_str\";" ; |
|
433
|
|
|
|
|
|
|
|
|
434
|
0
|
0
|
|
|
|
0
|
print STDERR "ARGS: $args\n" if $DEBUG ; |
|
435
|
|
|
|
|
|
|
|
|
436
|
|
|
|
|
|
|
# run the ffmpeg command |
|
437
|
0
|
|
|
|
|
0
|
my @lines ; |
|
438
|
0
|
|
|
|
|
0
|
$rc = run_transcoder($args, \@lines) ; |
|
439
|
|
|
|
|
|
|
|
|
440
|
|
|
|
|
|
|
# save results |
|
441
|
0
|
|
|
|
|
0
|
push @$lines_aref, @lines ; |
|
442
|
|
|
|
|
|
|
|
|
443
|
0
|
0
|
|
|
|
0
|
print STDERR "RC = $rc\n" if $DEBUG ; |
|
444
|
|
|
|
|
|
|
|
|
445
|
|
|
|
|
|
|
# stop if failed (non-zero exit status) |
|
446
|
0
|
0
|
|
|
|
0
|
last if $rc ; |
|
447
|
|
|
|
|
|
|
} |
|
448
|
|
|
|
|
|
|
|
|
449
|
|
|
|
|
|
|
# failed? |
|
450
|
0
|
|
|
|
|
0
|
my $pass_failed ; |
|
451
|
0
|
0
|
|
|
|
0
|
if ($rc) |
|
452
|
|
|
|
|
|
|
{ |
|
453
|
0
|
|
|
|
|
0
|
$pass_failed = "ffmpeg command failed (status = $rc)" ; |
|
454
|
|
|
|
|
|
|
} |
|
455
|
|
|
|
|
|
|
else |
|
456
|
|
|
|
|
|
|
{ |
|
457
|
|
|
|
|
|
|
# check video duration |
|
458
|
0
|
0
|
|
|
|
0
|
if (! -s "$dest.$ext") |
|
459
|
|
|
|
|
|
|
{ |
|
460
|
0
|
|
|
|
|
0
|
$pass_failed = "destination file \"$dest.$ext\" zero length" ; |
|
461
|
|
|
|
|
|
|
} |
|
462
|
|
|
|
|
|
|
else |
|
463
|
|
|
|
|
|
|
{ |
|
464
|
0
|
|
|
|
|
0
|
my $file_duration = video_duration("$dest.$ext") ; |
|
465
|
0
|
0
|
|
|
|
0
|
if ($file_duration < $multiplex_info_href->{'duration'} - $DURATION_MARGIN) |
|
466
|
|
|
|
|
|
|
{ |
|
467
|
0
|
|
|
|
|
0
|
$pass_failed = "Duration of \"$dest.$ext\" ($file_duration secs) not as expected ($multiplex_info_href->{'duration'} secs)" ; |
|
468
|
|
|
|
|
|
|
} |
|
469
|
|
|
|
|
|
|
else |
|
470
|
|
|
|
|
|
|
{ |
|
471
|
|
|
|
|
|
|
# all's well so stop |
|
472
|
0
|
|
|
|
|
0
|
last ; |
|
473
|
|
|
|
|
|
|
} |
|
474
|
|
|
|
|
|
|
} |
|
475
|
|
|
|
|
|
|
} |
|
476
|
|
|
|
|
|
|
|
|
477
|
0
|
0
|
|
|
|
0
|
if ($pass_failed) |
|
478
|
|
|
|
|
|
|
{ |
|
479
|
|
|
|
|
|
|
# can we try again |
|
480
|
0
|
0
|
|
|
|
0
|
if ( ($idx+1) < scalar(@cmds)) |
|
481
|
|
|
|
|
|
|
{ |
|
482
|
0
|
|
|
|
|
0
|
push @$warnings_aref, "$pass_failed, trying alternate command" ; |
|
483
|
|
|
|
|
|
|
} |
|
484
|
|
|
|
|
|
|
else |
|
485
|
|
|
|
|
|
|
{ |
|
486
|
0
|
|
|
|
|
0
|
$error = $pass_failed ; |
|
487
|
0
|
|
|
|
|
0
|
push @$errors_aref, $error ; |
|
488
|
0
|
|
|
|
|
0
|
return $error ; |
|
489
|
|
|
|
|
|
|
} |
|
490
|
|
|
|
|
|
|
} |
|
491
|
|
|
|
|
|
|
} |
|
492
|
|
|
|
|
|
|
|
|
493
|
|
|
|
|
|
|
# delete any temp files |
|
494
|
0
|
|
|
|
|
0
|
for my $tempfile (glob("${path}temp*")) |
|
495
|
|
|
|
|
|
|
{ |
|
496
|
0
|
|
|
|
|
0
|
unlink $tempfile ; |
|
497
|
|
|
|
|
|
|
} |
|
498
|
|
|
|
|
|
|
} |
|
499
|
|
|
|
|
|
|
|
|
500
|
0
|
|
|
|
|
0
|
return $error ; |
|
501
|
|
|
|
|
|
|
} |
|
502
|
|
|
|
|
|
|
|
|
503
|
|
|
|
|
|
|
|
|
504
|
|
|
|
|
|
|
|
|
505
|
|
|
|
|
|
|
|
|
506
|
|
|
|
|
|
|
#----------------------------------------------------------------------------- |
|
507
|
|
|
|
|
|
|
|
|
508
|
|
|
|
|
|
|
=item B |
|
509
|
|
|
|
|
|
|
|
|
510
|
|
|
|
|
|
|
Processes the destination file, the requested output spec (if any), and the language spec (if any) |
|
511
|
|
|
|
|
|
|
to ensure they are set to a valid combination of settings which will result in a supported file format |
|
512
|
|
|
|
|
|
|
being written. Adjusts/sets the values accordingly. |
|
513
|
|
|
|
|
|
|
|
|
514
|
|
|
|
|
|
|
If defined, the output spec (and language) always has precedence over the specified file format and the file format |
|
515
|
|
|
|
|
|
|
(i.e. extension) will be adjusted to match the required output. |
|
516
|
|
|
|
|
|
|
|
|
517
|
|
|
|
|
|
|
This is used when scheduling recording of multiple channels in the multiplex. This routine ensures that the correct |
|
518
|
|
|
|
|
|
|
streams are added to the recording. |
|
519
|
|
|
|
|
|
|
|
|
520
|
|
|
|
|
|
|
=cut |
|
521
|
|
|
|
|
|
|
|
|
522
|
|
|
|
|
|
|
# Want Input Out |
|
523
|
|
|
|
|
|
|
# ext lang out record out ext |
|
524
|
|
|
|
|
|
|
# av '' '' av av .mpeg |
|
525
|
|
|
|
|
|
|
# av .mpeg '' '' av av .mpeg (=ext) |
|
526
|
|
|
|
|
|
|
# a+v .mpeg 'a a' '' a+v a+v .mpeg (=ext) |
|
527
|
|
|
|
|
|
|
# avs .mpeg '' 'avs' avs =out .mpeg (=ext) |
|
528
|
|
|
|
|
|
|
# a+vs .mpeg 'a a' 'avs' a+vs =out .mpeg (=ext) |
|
529
|
|
|
|
|
|
|
|
|
530
|
|
|
|
|
|
|
# a '' 'a' a =out .mp2 |
|
531
|
|
|
|
|
|
|
# a 'a' 'a' a =out .mp2 |
|
532
|
|
|
|
|
|
|
# a .mp2 '' '' a a .mp2 (=ext) |
|
533
|
|
|
|
|
|
|
# a .mp3 '' '' a a .mp3 (=ext) |
|
534
|
|
|
|
|
|
|
# a .mp3 'a' '' a a .mp3 (=ext) |
|
535
|
|
|
|
|
|
|
|
|
536
|
|
|
|
|
|
|
# v .m2v '' '' v v .m2v (=ext) |
|
537
|
|
|
|
|
|
|
# v '' 'v' v =out .m2v (=ext) |
|
538
|
|
|
|
|
|
|
|
|
539
|
|
|
|
|
|
|
sub sanitise_options |
|
540
|
|
|
|
|
|
|
{ |
|
541
|
53
|
|
|
53
|
1
|
107202
|
my ($destfile_ref, $out_ref, $lang_ref, $errors_aref, $warnings_aref) = @_ ; |
|
542
|
53
|
|
|
|
|
91
|
my $error = 0 ; |
|
543
|
|
|
|
|
|
|
|
|
544
|
|
|
|
|
|
|
# language spec is optional |
|
545
|
53
|
|
|
|
|
100
|
my $lang = "" ; |
|
546
|
53
|
|
50
|
|
|
135
|
$lang_ref ||= \$lang ; |
|
547
|
|
|
|
|
|
|
|
|
548
|
53
|
|
50
|
|
|
87
|
$$destfile_ref ||= "" ; |
|
549
|
53
|
|
100
|
|
|
135
|
$$lang_ref ||= "" ; |
|
550
|
53
|
|
|
|
|
49
|
my $orig_lang = $$lang_ref ; |
|
551
|
|
|
|
|
|
|
|
|
552
|
|
|
|
|
|
|
# merge together the output spec & language spec and ensure output spec is in the correct form |
|
553
|
53
|
|
|
|
|
119
|
($$out_ref, $$lang_ref) = _normalise_output_spec($$out_ref, $$lang_ref) ; |
|
554
|
53
|
50
|
|
|
|
87
|
print STDERR "sanitise_options($$destfile_ref, $$out_ref, $$lang_ref)\n" if $DEBUG ; |
|
555
|
|
|
|
|
|
|
|
|
556
|
|
|
|
|
|
|
# check specified filename |
|
557
|
53
|
|
|
|
|
1168
|
my ($name, $path, $ext) = fileparse($$destfile_ref, '\..*') ; |
|
558
|
53
|
100
|
|
|
|
129
|
$ext = substr $ext, 1 if $ext ; |
|
559
|
53
|
|
|
|
|
92
|
my $dest = "$path$name" ; |
|
560
|
|
|
|
|
|
|
|
|
561
|
53
|
50
|
|
|
|
81
|
print STDERR " + dest=$dest ext=$ext\n" if $DEBUG ; |
|
562
|
|
|
|
|
|
|
|
|
563
|
|
|
|
|
|
|
# check output spec |
|
564
|
53
|
100
|
|
|
|
96
|
if (!$$out_ref) |
|
565
|
|
|
|
|
|
|
{ |
|
566
|
|
|
|
|
|
|
## no output spec defined |
|
567
|
|
|
|
|
|
|
|
|
568
|
|
|
|
|
|
|
# get default output spec |
|
569
|
19
|
|
|
|
|
31
|
my $default_ext = $FORMATS[0][0] ; |
|
570
|
19
|
50
|
|
|
|
26
|
print STDERR "format regexp 2 out - Default...\n" if $DEBUG ; |
|
571
|
19
|
|
|
|
|
38
|
my $default_out = _format_regexp2out($FORMATS[0][1], $$lang_ref) ; |
|
572
|
|
|
|
|
|
|
|
|
573
|
19
|
50
|
|
|
|
25
|
print STDERR "No out specified: default out=$default_out default ext=$default_ext\n" if $DEBUG ; |
|
574
|
|
|
|
|
|
|
|
|
575
|
|
|
|
|
|
|
# check file format |
|
576
|
19
|
100
|
|
|
|
28
|
if (!$ext) |
|
577
|
|
|
|
|
|
|
{ |
|
578
|
2
|
50
|
|
|
|
3
|
print STDERR " + No ext specified: using default out=$default_out default ext=$default_ext\n" if $DEBUG ; |
|
579
|
|
|
|
|
|
|
# no file format, set it based on default output spec |
|
580
|
2
|
|
|
|
|
3
|
$$out_ref = $default_out ; |
|
581
|
2
|
|
|
|
|
4
|
$ext = $default_ext ; |
|
582
|
|
|
|
|
|
|
} |
|
583
|
|
|
|
|
|
|
else |
|
584
|
|
|
|
|
|
|
{ |
|
585
|
|
|
|
|
|
|
# file format specified, see if we support it |
|
586
|
17
|
100
|
|
|
|
30
|
if (exists($ALIASES{$ext})) |
|
587
|
|
|
|
|
|
|
{ |
|
588
|
|
|
|
|
|
|
# make sure the extension is in a form we understand |
|
589
|
16
|
|
|
|
|
20
|
my $aliased_ext = $ALIASES{$ext} ; |
|
590
|
|
|
|
|
|
|
|
|
591
|
16
|
50
|
|
|
|
22
|
print STDERR "format regexp 2 out - preferred for $ext ...\n" if $DEBUG ; |
|
592
|
|
|
|
|
|
|
# convert the first (preferred) regexp into output spec |
|
593
|
16
|
|
|
|
|
32
|
($$out_ref, $$lang_ref) = _format_regexp2out($FORMATS{$aliased_ext}[0], $$lang_ref) ; |
|
594
|
|
|
|
|
|
|
|
|
595
|
16
|
50
|
|
|
|
35
|
print STDERR " + ext specified ($ext): using regexp $FORMATS{$aliased_ext}[0] out=$$out_ref ext=$aliased_ext\n" if $DEBUG ; |
|
596
|
|
|
|
|
|
|
} |
|
597
|
|
|
|
|
|
|
else |
|
598
|
|
|
|
|
|
|
{ |
|
599
|
1
|
50
|
|
|
|
3
|
print STDERR " + non-supported ext specified: using default out=$default_out default ext=$default_ext\n" if $DEBUG ; |
|
600
|
|
|
|
|
|
|
|
|
601
|
|
|
|
|
|
|
# report warning |
|
602
|
1
|
|
|
|
|
4
|
push @$warnings_aref, "File type \"$ext\" is not supported, changing to \"$default_ext\"" ; |
|
603
|
|
|
|
|
|
|
|
|
604
|
|
|
|
|
|
|
# non-supoported file format, set to defaults |
|
605
|
1
|
|
|
|
|
2
|
$$out_ref = $default_out ; |
|
606
|
1
|
|
|
|
|
1
|
$ext = $default_ext ; |
|
607
|
|
|
|
|
|
|
} |
|
608
|
|
|
|
|
|
|
} |
|
609
|
|
|
|
|
|
|
} |
|
610
|
|
|
|
|
|
|
|
|
611
|
|
|
|
|
|
|
# check for language spec being dropped due to output spec |
|
612
|
53
|
100
|
100
|
|
|
158
|
if (($orig_lang) && ($$lang_ref ne $orig_lang)) |
|
613
|
|
|
|
|
|
|
{ |
|
614
|
|
|
|
|
|
|
# report warning |
|
615
|
6
|
|
|
|
|
22
|
push @$warnings_aref, "Language spec \"$orig_lang\" is being ignored because of the specified required output (no audio)" ; |
|
616
|
|
|
|
|
|
|
} |
|
617
|
|
|
|
|
|
|
|
|
618
|
|
|
|
|
|
|
## Do the rest of the processing now that we've handled the language spec |
|
619
|
53
|
|
|
|
|
97
|
$error = _sanitise_options($destfile_ref, $out_ref, $errors_aref, $warnings_aref) ; |
|
620
|
|
|
|
|
|
|
|
|
621
|
53
|
|
|
|
|
114
|
return $error ; |
|
622
|
|
|
|
|
|
|
} |
|
623
|
|
|
|
|
|
|
|
|
624
|
|
|
|
|
|
|
|
|
625
|
|
|
|
|
|
|
#----------------------------------------------------------------------------- |
|
626
|
|
|
|
|
|
|
|
|
627
|
|
|
|
|
|
|
=item B |
|
628
|
|
|
|
|
|
|
|
|
629
|
|
|
|
|
|
|
Uses ffmpeg to determine the video file duration. Returns the duration in seconds. |
|
630
|
|
|
|
|
|
|
|
|
631
|
|
|
|
|
|
|
=cut |
|
632
|
|
|
|
|
|
|
|
|
633
|
|
|
|
|
|
|
sub video_duration |
|
634
|
|
|
|
|
|
|
{ |
|
635
|
0
|
|
|
0
|
1
|
0
|
my ($file) = @_ ; |
|
636
|
|
|
|
|
|
|
|
|
637
|
0
|
|
|
|
|
0
|
my %info = video_info($file) ; |
|
638
|
0
|
|
|
|
|
0
|
return $info{'duration'} ; |
|
639
|
|
|
|
|
|
|
} |
|
640
|
|
|
|
|
|
|
|
|
641
|
|
|
|
|
|
|
#----------------------------------------------------------------------------- |
|
642
|
|
|
|
|
|
|
|
|
643
|
|
|
|
|
|
|
=item B |
|
644
|
|
|
|
|
|
|
|
|
645
|
|
|
|
|
|
|
Uses ffmpeg to determine the video file contents. Returns a HASH containing: |
|
646
|
|
|
|
|
|
|
|
|
647
|
|
|
|
|
|
|
'input' => Input number |
|
648
|
|
|
|
|
|
|
'duration' => video duration in seconds |
|
649
|
|
|
|
|
|
|
'pids' => { |
|
650
|
|
|
|
|
|
|
$pid => { |
|
651
|
|
|
|
|
|
|
'input' => input number that this pid is part of |
|
652
|
|
|
|
|
|
|
'stream' => stream number |
|
653
|
|
|
|
|
|
|
'lang' => audio language |
|
654
|
|
|
|
|
|
|
'pidtype' => pid type (video, audio, subtitle) |
|
655
|
|
|
|
|
|
|
} |
|
656
|
|
|
|
|
|
|
} |
|
657
|
|
|
|
|
|
|
|
|
658
|
|
|
|
|
|
|
=cut |
|
659
|
|
|
|
|
|
|
|
|
660
|
|
|
|
|
|
|
sub video_info |
|
661
|
|
|
|
|
|
|
{ |
|
662
|
0
|
|
|
0
|
1
|
0
|
my ($file) = @_ ; |
|
663
|
|
|
|
|
|
|
|
|
664
|
0
|
|
|
|
|
0
|
my @lines ; |
|
665
|
0
|
|
|
|
|
0
|
run_transcoder("ffmpeg -i '$file'", \@lines) ; |
|
666
|
|
|
|
|
|
|
|
|
667
|
0
|
|
|
|
|
0
|
my %info = ( |
|
668
|
|
|
|
|
|
|
'input' => undef, |
|
669
|
|
|
|
|
|
|
'duration' => 0, |
|
670
|
|
|
|
|
|
|
'pids' => {}, |
|
671
|
|
|
|
|
|
|
) ; |
|
672
|
0
|
|
|
|
|
0
|
foreach my $line (reverse @lines) |
|
673
|
|
|
|
|
|
|
{ |
|
674
|
|
|
|
|
|
|
# Input #0, mpegts, from 'bbc1-bbc2.ts': |
|
675
|
|
|
|
|
|
|
# Duration: 00:00:27.18, start: 15213.487800, bitrate: 4049 kb/s |
|
676
|
|
|
|
|
|
|
# Stream #0.0[0x259]: Audio: mp2, 48000 Hz, 2 channels, s16, 256 kb/s |
|
677
|
|
|
|
|
|
|
# Stream #0.1[0x258]: Video: mpeg2video, yuv420p, 720x576 [PAR 64:45 DAR 16:9], 15000 kb/s, 25 fps, 25 tbr, 90k tbn, 50 tbc |
|
678
|
|
|
|
|
|
|
# Stream #0.2[0x262]: Video: mpeg2video, yuv420p, 720x576 [PAR 64:45 DAR 16:9], 15000 kb/s, 25 fps, 25 tbr, 90k tbn, 50 tbc |
|
679
|
|
|
|
|
|
|
# Stream #0.3[0x263]: Audio: mp2, 48000 Hz, 2 channels, s16, 256 kb/s |
|
680
|
|
|
|
|
|
|
# At least one output file must be specified |
|
681
|
|
|
|
|
|
|
|
|
682
|
|
|
|
|
|
|
# Stream #0.0[0x258]: Video: mpeg2video, yuv420p, 720x576 [PAR 64:45 DAR 16:9], 15000 kb/s, 25 fps, 25 tbr, 90k tbn, 50 tbc |
|
683
|
|
|
|
|
|
|
# Stream #0.1[0x259](eng): Audio: mp2, 48000 Hz, 2 channels, s16, 256 kb/s |
|
684
|
|
|
|
|
|
|
# Stream #0.2[0x25a](eng): Audio: mp2, 48000 Hz, 1 channels, s16, 64 kb/s |
|
685
|
|
|
|
|
|
|
# Stream #0.3[0x25d](eng): Subtitle: dvbsub |
|
686
|
|
|
|
|
|
|
|
|
687
|
|
|
|
|
|
|
# Stream #0.1[0x259](eng): Audio: mp2, 48000 Hz, 2 channels, s16, 256 kb/s |
|
688
|
0
|
0
|
|
|
|
0
|
if ($line =~ /Stream #(\d+)\.(\d+)\[(0x[\da-f]+)\]\((\S+)\): (\S+): /i) |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
689
|
|
|
|
|
|
|
{ |
|
690
|
0
|
|
|
|
|
0
|
my ($input, $stream, $lang, $pid, $type) = ($1, $2, $3, hex($4), lc $5) ; |
|
691
|
0
|
|
|
|
|
0
|
$info{'pids'}{$pid} = { |
|
692
|
|
|
|
|
|
|
'input' => $input, |
|
693
|
|
|
|
|
|
|
'stream' => $stream, |
|
694
|
|
|
|
|
|
|
'lang' => $lang, |
|
695
|
|
|
|
|
|
|
'pidtype' => $type, |
|
696
|
|
|
|
|
|
|
} ; |
|
697
|
|
|
|
|
|
|
} |
|
698
|
|
|
|
|
|
|
# Stream #0.0[0x258]: Video: mpeg2video, yuv420p, 720x576 [PAR 64:45 DAR 16:9], 15000 kb/s, 25 fps, 25 tbr, 90k tbn, 50 tbc |
|
699
|
|
|
|
|
|
|
elsif ($line =~ /Stream #(\d+)\.(\d+)\[(0x[\da-f]+)\]: (\S+): /i) |
|
700
|
|
|
|
|
|
|
{ |
|
701
|
0
|
|
|
|
|
0
|
my ($input, $stream, $pid, $type) = ($1, $2, hex($3), lc $4) ; |
|
702
|
0
|
|
|
|
|
0
|
$info{'pids'}{$pid} = { |
|
703
|
|
|
|
|
|
|
'input' => $input, |
|
704
|
|
|
|
|
|
|
'stream' => $stream, |
|
705
|
|
|
|
|
|
|
'pidtype' => $type, |
|
706
|
|
|
|
|
|
|
} ; |
|
707
|
|
|
|
|
|
|
} |
|
708
|
|
|
|
|
|
|
# Duration: 00:00:27.18, start: 15213.487800, bitrate: 4049 kb/s |
|
709
|
|
|
|
|
|
|
elsif ($line =~ /Duration: (\d+):(\d+):(\d+).(\d+)/i) |
|
710
|
|
|
|
|
|
|
{ |
|
711
|
0
|
|
|
|
|
0
|
my ($hour, $min, $sec, $ms) = ($1, $2, $3, $4) ; |
|
712
|
0
|
|
|
|
|
0
|
$sec += $min*60 + $hour*60*60; |
|
713
|
0
|
|
|
|
|
0
|
$info{'duration'} = $sec ; |
|
714
|
|
|
|
|
|
|
} |
|
715
|
|
|
|
|
|
|
# Input #0, mpegts, from 'bbc1-bbc2.ts': |
|
716
|
|
|
|
|
|
|
elsif ($line =~ /Input #(\d+),/i) |
|
717
|
|
|
|
|
|
|
{ |
|
718
|
0
|
|
|
|
|
0
|
$info{'input'} = $1 ; |
|
719
|
0
|
|
|
|
|
0
|
last ; |
|
720
|
|
|
|
|
|
|
} |
|
721
|
|
|
|
|
|
|
} |
|
722
|
|
|
|
|
|
|
|
|
723
|
0
|
|
|
|
|
0
|
return %info ; |
|
724
|
|
|
|
|
|
|
} |
|
725
|
|
|
|
|
|
|
|
|
726
|
|
|
|
|
|
|
#----------------------------------------------------------------------------- |
|
727
|
|
|
|
|
|
|
|
|
728
|
|
|
|
|
|
|
=item B |
|
729
|
|
|
|
|
|
|
|
|
730
|
|
|
|
|
|
|
Run the transcoder command with the provided arguments. If the $lines_aref ARRAY ref is supplied, |
|
731
|
|
|
|
|
|
|
then the output lines from ffmpeg are returned in that array (one entry per line). |
|
732
|
|
|
|
|
|
|
|
|
733
|
|
|
|
|
|
|
Returns the exit status from ffmpeg. |
|
734
|
|
|
|
|
|
|
|
|
735
|
|
|
|
|
|
|
=cut |
|
736
|
|
|
|
|
|
|
|
|
737
|
|
|
|
|
|
|
sub run_transcoder |
|
738
|
|
|
|
|
|
|
{ |
|
739
|
0
|
|
|
0
|
1
|
0
|
my ($args, $lines_aref) = @_ ; |
|
740
|
|
|
|
|
|
|
|
|
741
|
0
|
|
0
|
|
|
0
|
$lines_aref ||= [] ; |
|
742
|
|
|
|
|
|
|
|
|
743
|
|
|
|
|
|
|
# get command name |
|
744
|
0
|
|
|
|
|
0
|
my $transcoder = "" ; |
|
745
|
0
|
|
|
|
|
0
|
($transcoder = $args) =~ s/^\s*(\S+).*/$1/ ; |
|
746
|
|
|
|
|
|
|
|
|
747
|
|
|
|
|
|
|
# set niceness |
|
748
|
0
|
|
|
|
|
0
|
my $nice = "" ; |
|
749
|
0
|
0
|
|
|
|
0
|
if ($NICE) |
|
750
|
|
|
|
|
|
|
{ |
|
751
|
0
|
|
|
|
|
0
|
$nice = "nice -n $NICE" ; |
|
752
|
|
|
|
|
|
|
} |
|
753
|
|
|
|
|
|
|
# run ffmpeg |
|
754
|
|
|
|
|
|
|
# my $cmd = "$nice ffmpeg $args" ; |
|
755
|
0
|
|
|
|
|
0
|
my $cmd = "$nice $args" ; |
|
756
|
0
|
|
|
|
|
0
|
@$lines_aref = `$cmd 2>&1 ; echo RC=$?` ; |
|
757
|
|
|
|
|
|
|
|
|
758
|
|
|
|
|
|
|
# strip newlines |
|
759
|
0
|
|
|
|
|
0
|
foreach (@$lines_aref) |
|
760
|
|
|
|
|
|
|
{ |
|
761
|
0
|
|
|
|
|
0
|
chomp $_ ; |
|
762
|
|
|
|
|
|
|
|
|
763
|
|
|
|
|
|
|
# Strip out the intermediate processing output |
|
764
|
0
|
|
|
|
|
0
|
$_ =~ s/^.*\r//g ; |
|
765
|
|
|
|
|
|
|
|
|
766
|
|
|
|
|
|
|
# prepend with command name |
|
767
|
0
|
|
|
|
|
0
|
$_ = "[$transcoder] $_" ; |
|
768
|
|
|
|
|
|
|
} |
|
769
|
|
|
|
|
|
|
|
|
770
|
|
|
|
|
|
|
# Add command to start |
|
771
|
0
|
|
|
|
|
0
|
unshift @$lines_aref , $cmd ; |
|
772
|
|
|
|
|
|
|
|
|
773
|
|
|
|
|
|
|
# get status |
|
774
|
0
|
|
|
|
|
0
|
my $rc=-1 ; |
|
775
|
0
|
0
|
|
|
|
0
|
if ($lines_aref->[-1] =~ m/RC=(\d+)/) |
|
776
|
|
|
|
|
|
|
{ |
|
777
|
0
|
|
|
|
|
0
|
$rc = $1 ; |
|
778
|
|
|
|
|
|
|
} |
|
779
|
|
|
|
|
|
|
|
|
780
|
0
|
0
|
|
|
|
0
|
if ($DEBUG_FFMPEG) |
|
781
|
|
|
|
|
|
|
{ |
|
782
|
0
|
|
|
|
|
0
|
print STDERR "-------------------------------------------\n" ; |
|
783
|
0
|
|
|
|
|
0
|
foreach (@$lines_aref) |
|
784
|
|
|
|
|
|
|
{ |
|
785
|
0
|
|
|
|
|
0
|
print STDERR "$_\n" ; |
|
786
|
|
|
|
|
|
|
} |
|
787
|
0
|
|
|
|
|
0
|
print STDERR "STATUS = $rc\n" ; |
|
788
|
0
|
|
|
|
|
0
|
print STDERR "-------------------------------------------\n" ; |
|
789
|
|
|
|
|
|
|
} |
|
790
|
|
|
|
|
|
|
|
|
791
|
0
|
|
|
|
|
0
|
return $rc ; |
|
792
|
|
|
|
|
|
|
} |
|
793
|
|
|
|
|
|
|
|
|
794
|
|
|
|
|
|
|
# ============================================================================================ |
|
795
|
|
|
|
|
|
|
# PRIVATE |
|
796
|
|
|
|
|
|
|
# ============================================================================================ |
|
797
|
|
|
|
|
|
|
|
|
798
|
|
|
|
|
|
|
# -------------------------------------------------------------------------------------------- |
|
799
|
|
|
|
|
|
|
# Internal routine that expects the output spec to already have been created/normalised into |
|
800
|
|
|
|
|
|
|
# a valid spec (including any language options) |
|
801
|
|
|
|
|
|
|
sub _sanitise_options |
|
802
|
|
|
|
|
|
|
{ |
|
803
|
53
|
|
|
53
|
|
59
|
my ($destfile_ref, $output_spec_ref, $errors_aref, $warnings_aref) = @_ ; |
|
804
|
53
|
|
|
|
|
44
|
my $error = 0 ; |
|
805
|
|
|
|
|
|
|
|
|
806
|
53
|
|
50
|
|
|
85
|
$$destfile_ref ||= "" ; |
|
807
|
53
|
|
50
|
|
|
91
|
$errors_aref ||= [] ; |
|
808
|
53
|
|
50
|
|
|
66
|
$warnings_aref ||= [] ; |
|
809
|
|
|
|
|
|
|
|
|
810
|
53
|
50
|
|
|
|
75
|
print STDERR "_sanitise_options($$destfile_ref, $$output_spec_ref)\n" if $DEBUG ; |
|
811
|
|
|
|
|
|
|
|
|
812
|
|
|
|
|
|
|
# check specified filename |
|
813
|
53
|
|
|
|
|
804
|
my ($name, $path, $ext) = fileparse($$destfile_ref, '\..*') ; |
|
814
|
53
|
100
|
|
|
|
106
|
$ext = substr $ext, 1 if $ext ; |
|
815
|
53
|
|
|
|
|
50
|
my $dest = "$path$name" ; |
|
816
|
|
|
|
|
|
|
|
|
817
|
53
|
50
|
|
|
|
74
|
print STDERR " + dest=$dest ext=$ext\n" if $DEBUG ; |
|
818
|
|
|
|
|
|
|
|
|
819
|
|
|
|
|
|
|
# check output spec |
|
820
|
53
|
50
|
|
|
|
83
|
if ($$output_spec_ref) |
|
821
|
|
|
|
|
|
|
{ |
|
822
|
|
|
|
|
|
|
## output spec defined |
|
823
|
|
|
|
|
|
|
|
|
824
|
|
|
|
|
|
|
# use it to check the file format |
|
825
|
53
|
|
|
|
|
82
|
my @supported_types = _output_formats($$output_spec_ref) ; |
|
826
|
|
|
|
|
|
|
|
|
827
|
53
|
50
|
|
|
|
85
|
print STDERR "out specified: supported types=" . Data::Dumper->Dump([\@supported_types]) if $DEBUG ; |
|
828
|
|
|
|
|
|
|
|
|
829
|
|
|
|
|
|
|
# check file format |
|
830
|
53
|
100
|
|
|
|
75
|
if (!$ext) |
|
831
|
|
|
|
|
|
|
{ |
|
832
|
|
|
|
|
|
|
# no file format, set it based on output spec |
|
833
|
7
|
50
|
|
|
|
15
|
if (@supported_types) |
|
834
|
|
|
|
|
|
|
{ |
|
835
|
|
|
|
|
|
|
# use first supported type |
|
836
|
7
|
|
|
|
|
11
|
$ext = $supported_types[0] ; |
|
837
|
|
|
|
|
|
|
} |
|
838
|
|
|
|
|
|
|
} |
|
839
|
|
|
|
|
|
|
else |
|
840
|
|
|
|
|
|
|
{ |
|
841
|
|
|
|
|
|
|
# file format specified, check it matches supported types |
|
842
|
46
|
|
|
|
|
62
|
my %valid_types = map { $_ => 1} @supported_types ; |
|
|
207
|
|
|
|
|
350
|
|
|
843
|
46
|
100
|
100
|
|
|
250
|
if (exists($ALIASES{$ext}) && exists($valid_types{ $ALIASES{$ext} })) |
|
844
|
|
|
|
|
|
|
{ |
|
845
|
|
|
|
|
|
|
# ok to use |
|
846
|
|
|
|
|
|
|
} |
|
847
|
|
|
|
|
|
|
else |
|
848
|
|
|
|
|
|
|
{ |
|
849
|
|
|
|
|
|
|
# file format does not match requested output, use default for the requested output |
|
850
|
14
|
|
|
|
|
21
|
my $old_ext = $ext ; |
|
851
|
14
|
|
|
|
|
14
|
$ext = undef ; |
|
852
|
14
|
50
|
|
|
|
32
|
if (@supported_types) |
|
853
|
|
|
|
|
|
|
{ |
|
854
|
|
|
|
|
|
|
# use first supported type |
|
855
|
14
|
|
|
|
|
19
|
my $new_ext = $supported_types[0] ; |
|
856
|
|
|
|
|
|
|
|
|
857
|
|
|
|
|
|
|
# report warning |
|
858
|
14
|
|
|
|
|
39
|
push @$warnings_aref, "File type \"$old_ext\" does not match requested output, changing to \"$new_ext\"" ; |
|
859
|
|
|
|
|
|
|
|
|
860
|
|
|
|
|
|
|
# change |
|
861
|
14
|
|
|
|
|
40
|
$ext = $new_ext ; |
|
862
|
|
|
|
|
|
|
} |
|
863
|
|
|
|
|
|
|
} |
|
864
|
|
|
|
|
|
|
} |
|
865
|
|
|
|
|
|
|
} |
|
866
|
|
|
|
|
|
|
|
|
867
|
|
|
|
|
|
|
|
|
868
|
|
|
|
|
|
|
## If we get here and either the output spec or the file format are not defined, then there's been an error |
|
869
|
53
|
50
|
|
|
|
96
|
if (!$$output_spec_ref) |
|
|
|
50
|
|
|
|
|
|
|
870
|
|
|
|
|
|
|
{ |
|
871
|
0
|
|
|
|
|
0
|
$error = "Unable to determine the correct recording type for the specified output file format" ; |
|
872
|
|
|
|
|
|
|
} |
|
873
|
|
|
|
|
|
|
elsif (!$ext) |
|
874
|
|
|
|
|
|
|
{ |
|
875
|
0
|
|
|
|
|
0
|
$error = "Unable to determine the correct recording type for the specified output file format" ; |
|
876
|
|
|
|
|
|
|
} |
|
877
|
|
|
|
|
|
|
else |
|
878
|
|
|
|
|
|
|
{ |
|
879
|
|
|
|
|
|
|
## ok to finish off the output file |
|
880
|
53
|
|
|
|
|
85
|
$$destfile_ref = "$path$name.$ext" ; |
|
881
|
|
|
|
|
|
|
} |
|
882
|
|
|
|
|
|
|
|
|
883
|
53
|
50
|
|
|
|
71
|
if ($error) |
|
884
|
|
|
|
|
|
|
{ |
|
885
|
0
|
|
|
|
|
0
|
push @$errors_aref, $error ; |
|
886
|
|
|
|
|
|
|
} |
|
887
|
|
|
|
|
|
|
|
|
888
|
53
|
|
|
|
|
73
|
return $error ; |
|
889
|
|
|
|
|
|
|
} |
|
890
|
|
|
|
|
|
|
|
|
891
|
|
|
|
|
|
|
|
|
892
|
|
|
|
|
|
|
# -------------------------------------------------------------------------------------------- |
|
893
|
|
|
|
|
|
|
# Ensure the output spec is in the correct form & has the stream types in the correct order |
|
894
|
|
|
|
|
|
|
sub _normalise_output_spec |
|
895
|
|
|
|
|
|
|
{ |
|
896
|
88
|
|
|
88
|
|
104
|
my ($out, $lang) = @_ ; |
|
897
|
|
|
|
|
|
|
|
|
898
|
88
|
|
100
|
|
|
157
|
$out ||= "" ; |
|
899
|
88
|
|
100
|
|
|
167
|
$lang ||= "" ; |
|
900
|
|
|
|
|
|
|
|
|
901
|
88
|
|
|
|
|
77
|
my $out_spec = "" ; |
|
902
|
|
|
|
|
|
|
|
|
903
|
88
|
50
|
|
|
|
139
|
print STDERR "_normalise_output_spec(out=\"$out\", lang=\"$lang\")\n" if $DEBUG ; |
|
904
|
|
|
|
|
|
|
|
|
905
|
88
|
100
|
|
|
|
131
|
if ($out) |
|
906
|
|
|
|
|
|
|
{ |
|
907
|
|
|
|
|
|
|
# number of audio channels (-1 because, if audio is specified in the output, that already adds 1) |
|
908
|
69
|
|
|
|
|
106
|
my $num_audio = _audio_chan_count($lang) ; |
|
909
|
69
|
100
|
|
|
|
122
|
$num_audio-- if $num_audio >= 1 ; |
|
910
|
|
|
|
|
|
|
|
|
911
|
69
|
50
|
|
|
|
87
|
print STDERR " + num_audio=$num_audio\n" if $DEBUG ; |
|
912
|
|
|
|
|
|
|
|
|
913
|
|
|
|
|
|
|
# look at each stream type in the correct order |
|
914
|
69
|
|
|
|
|
94
|
foreach my $type (@STREAM_ORDER) |
|
915
|
|
|
|
|
|
|
{ |
|
916
|
|
|
|
|
|
|
# add stream type code (a=audio, v=video etc) to spec if this type |
|
917
|
|
|
|
|
|
|
# is in the specified spec |
|
918
|
207
|
|
|
|
|
233
|
my $type_ch = substr $type, 0, 1 ; |
|
919
|
207
|
100
|
|
|
|
1529
|
if ($out =~ /$type_ch/) |
|
920
|
|
|
|
|
|
|
{ |
|
921
|
120
|
|
|
|
|
104
|
$out_spec .= $type_ch ; |
|
922
|
|
|
|
|
|
|
|
|
923
|
120
|
50
|
|
|
|
164
|
print STDERR " + + add $type : $type_ch\n" if $DEBUG ; |
|
924
|
|
|
|
|
|
|
|
|
925
|
120
|
100
|
|
|
|
222
|
if ($type eq 'audio') |
|
926
|
|
|
|
|
|
|
{ |
|
927
|
|
|
|
|
|
|
## add in the language audio channels (-1 ) |
|
928
|
58
|
|
|
|
|
75
|
$out_spec .= 'a'x$num_audio ; |
|
929
|
|
|
|
|
|
|
|
|
930
|
58
|
50
|
|
|
|
132
|
print STDERR " + + add lang : ".'a'x$num_audio."\n" if $DEBUG ; |
|
931
|
|
|
|
|
|
|
} |
|
932
|
|
|
|
|
|
|
} |
|
933
|
|
|
|
|
|
|
else |
|
934
|
|
|
|
|
|
|
{ |
|
935
|
|
|
|
|
|
|
# this type is not in the output spec |
|
936
|
87
|
100
|
|
|
|
201
|
if ($type eq 'audio') |
|
937
|
|
|
|
|
|
|
{ |
|
938
|
|
|
|
|
|
|
## no audio specified, so kill off the language spec |
|
939
|
11
|
|
|
|
|
19
|
$lang = "" ; |
|
940
|
|
|
|
|
|
|
|
|
941
|
11
|
50
|
|
|
|
27
|
print STDERR " + + no audio : removing any language spec\n" if $DEBUG ; |
|
942
|
|
|
|
|
|
|
} |
|
943
|
|
|
|
|
|
|
} |
|
944
|
|
|
|
|
|
|
} |
|
945
|
|
|
|
|
|
|
} |
|
946
|
|
|
|
|
|
|
|
|
947
|
88
|
50
|
|
|
|
425
|
print STDERR "final out_spec=\"$out_spec\"\n" if $DEBUG ; |
|
948
|
|
|
|
|
|
|
|
|
949
|
88
|
|
|
|
|
212
|
return ($out_spec, $lang) ; |
|
950
|
|
|
|
|
|
|
} |
|
951
|
|
|
|
|
|
|
|
|
952
|
|
|
|
|
|
|
|
|
953
|
|
|
|
|
|
|
# -------------------------------------------------------------------------------------------- |
|
954
|
|
|
|
|
|
|
# Convert the output spec into a list of supported types |
|
955
|
|
|
|
|
|
|
sub _output_formats |
|
956
|
|
|
|
|
|
|
{ |
|
957
|
53
|
|
|
53
|
|
57
|
my ($out) = @_ ; |
|
958
|
|
|
|
|
|
|
|
|
959
|
|
|
|
|
|
|
## Get list of valid file types |
|
960
|
53
|
50
|
|
|
|
74
|
print STDERR "checking out $out...\n" if $DEBUG ; |
|
961
|
53
|
|
|
|
|
38
|
my @valid_types ; |
|
962
|
53
|
|
|
|
|
67
|
foreach my $fmt_aref (@FORMATS) |
|
963
|
|
|
|
|
|
|
{ |
|
964
|
689
|
|
|
|
|
821
|
my ($valid_fmt, $regexp) = @$fmt_aref ; |
|
965
|
|
|
|
|
|
|
|
|
966
|
689
|
50
|
|
|
|
913
|
print STDERR " + check $out : $valid_fmt => $regexp\n" if $DEBUG ; |
|
967
|
689
|
100
|
|
|
|
5280
|
if ($out =~ m/^$regexp$/) |
|
968
|
|
|
|
|
|
|
{ |
|
969
|
241
|
50
|
|
|
|
317
|
print STDERR " + + match\n" if $DEBUG ; |
|
970
|
|
|
|
|
|
|
# save format with the command information |
|
971
|
|
|
|
|
|
|
# $valid_types{$valid_fmt} = $COMMANDS{$valid_fmt} ; |
|
972
|
241
|
|
|
|
|
420
|
push @valid_types, $valid_fmt ; |
|
973
|
|
|
|
|
|
|
} |
|
974
|
|
|
|
|
|
|
} |
|
975
|
53
|
|
|
|
|
144
|
return @valid_types ; |
|
976
|
|
|
|
|
|
|
} |
|
977
|
|
|
|
|
|
|
|
|
978
|
|
|
|
|
|
|
# -------------------------------------------------------------------------------------------- |
|
979
|
|
|
|
|
|
|
# Convert the regular expression string into an output spec string. For example, converts: |
|
980
|
|
|
|
|
|
|
# v+a+s* |
|
981
|
|
|
|
|
|
|
# into |
|
982
|
|
|
|
|
|
|
# va |
|
983
|
|
|
|
|
|
|
# |
|
984
|
|
|
|
|
|
|
# Also amends the spec with any language specifier |
|
985
|
|
|
|
|
|
|
# |
|
986
|
|
|
|
|
|
|
sub _format_regexp2out |
|
987
|
|
|
|
|
|
|
{ |
|
988
|
35
|
|
|
35
|
|
41
|
my ($regexp, $lang) = @_ ; |
|
989
|
|
|
|
|
|
|
|
|
990
|
35
|
|
|
|
|
34
|
my $out = $regexp ; |
|
991
|
|
|
|
|
|
|
|
|
992
|
35
|
50
|
|
|
|
49
|
print STDERR "_format_regexp2out($regexp)\n" if $DEBUG ; |
|
993
|
|
|
|
|
|
|
|
|
994
|
35
|
|
|
|
|
67
|
$out =~ s/\+//g ; |
|
995
|
35
|
50
|
|
|
|
50
|
print STDERR " + remove + : $out\n" if $DEBUG ; |
|
996
|
35
|
|
|
|
|
64
|
$out =~ s/(.)\*//g ; |
|
997
|
35
|
50
|
|
|
|
49
|
print STDERR " + remove X* : $out\n" if $DEBUG ; |
|
998
|
35
|
|
|
|
|
32
|
$out =~ s/\*//g ; |
|
999
|
35
|
50
|
|
|
|
44
|
print STDERR " + remove * : $out\n" if $DEBUG ; |
|
1000
|
|
|
|
|
|
|
|
|
1001
|
|
|
|
|
|
|
# normalise |
|
1002
|
35
|
|
|
|
|
45
|
($out, $lang) = _normalise_output_spec($out, $lang) ; |
|
1003
|
|
|
|
|
|
|
|
|
1004
|
35
|
50
|
|
|
|
53
|
print STDERR " + final out : $out\n" if $DEBUG ; |
|
1005
|
|
|
|
|
|
|
|
|
1006
|
35
|
100
|
|
|
|
85
|
return wantarray ? ($out, $lang) : $out ; |
|
1007
|
|
|
|
|
|
|
} |
|
1008
|
|
|
|
|
|
|
|
|
1009
|
|
|
|
|
|
|
# -------------------------------------------------------------------------------------------- |
|
1010
|
|
|
|
|
|
|
# Return the number of audio channels requested by the language spec |
|
1011
|
|
|
|
|
|
|
# |
|
1012
|
|
|
|
|
|
|
sub _audio_chan_count |
|
1013
|
|
|
|
|
|
|
{ |
|
1014
|
69
|
|
|
69
|
|
68
|
my ($language_spec) = @_ ; |
|
1015
|
69
|
|
|
|
|
63
|
my $num_audio = 0 ; |
|
1016
|
|
|
|
|
|
|
|
|
1017
|
|
|
|
|
|
|
# process language spec |
|
1018
|
69
|
|
100
|
|
|
133
|
$language_spec ||= "" ; |
|
1019
|
69
|
100
|
|
|
|
89
|
if ($language_spec) |
|
1020
|
|
|
|
|
|
|
{ |
|
1021
|
|
|
|
|
|
|
# appends to default audio chan |
|
1022
|
38
|
100
|
|
|
|
126
|
if ($language_spec =~ s/\+//g) |
|
1023
|
|
|
|
|
|
|
{ |
|
1024
|
17
|
|
|
|
|
20
|
++$num_audio ; |
|
1025
|
|
|
|
|
|
|
} |
|
1026
|
|
|
|
|
|
|
|
|
1027
|
|
|
|
|
|
|
# work through the language spec |
|
1028
|
38
|
|
|
|
|
151
|
my @lang = split /[\s,]+/, $language_spec ; |
|
1029
|
38
|
|
|
|
|
56
|
$num_audio += scalar(@lang) ; |
|
1030
|
|
|
|
|
|
|
} |
|
1031
|
|
|
|
|
|
|
|
|
1032
|
69
|
|
|
|
|
88
|
return $num_audio ; |
|
1033
|
|
|
|
|
|
|
} |
|
1034
|
|
|
|
|
|
|
|
|
1035
|
|
|
|
|
|
|
# -------------------------------------------------------------------------------------------- |
|
1036
|
|
|
|
|
|
|
# Convert the recorded pids into a valid output spec |
|
1037
|
|
|
|
|
|
|
# |
|
1038
|
|
|
|
|
|
|
sub _pids_out_spec |
|
1039
|
|
|
|
|
|
|
{ |
|
1040
|
0
|
|
|
0
|
|
|
my (@pids) = @_ ; |
|
1041
|
|
|
|
|
|
|
|
|
1042
|
0
|
0
|
|
|
|
|
print STDERR "_pids_out_spec()\n" if $DEBUG>=10 ; |
|
1043
|
|
|
|
|
|
|
|
|
1044
|
|
|
|
|
|
|
## turn the pid types into a format string of the form: |
|
1045
|
|
|
|
|
|
|
## e.g. aav for 2 audio + 1 video |
|
1046
|
0
|
|
|
|
|
|
my $out_spec = "" ; |
|
1047
|
0
|
|
|
|
|
|
foreach my $type (@STREAM_ORDER) |
|
1048
|
|
|
|
|
|
|
{ |
|
1049
|
0
|
|
|
|
|
|
foreach my $pid_href (@pids) |
|
1050
|
|
|
|
|
|
|
{ |
|
1051
|
0
|
0
|
|
|
|
|
print STDERR " + check pid $pid_href->{'pid'} type=$pid_href->{'type'} against $type..\n" if $DEBUG>=10 ; |
|
1052
|
0
|
0
|
|
|
|
|
if ($pid_href->{'pidtype'} eq $type) |
|
1053
|
|
|
|
|
|
|
{ |
|
1054
|
0
|
|
|
|
|
|
$out_spec .= substr($type, 0, 1) ; |
|
1055
|
0
|
0
|
|
|
|
|
print STDERR " + + added outspec=\"$out_spec\"\n" if $DEBUG>=10 ; |
|
1056
|
|
|
|
|
|
|
} |
|
1057
|
|
|
|
|
|
|
} |
|
1058
|
|
|
|
|
|
|
} |
|
1059
|
0
|
|
|
|
|
|
return $out_spec ; |
|
1060
|
|
|
|
|
|
|
} |
|
1061
|
|
|
|
|
|
|
|
|
1062
|
|
|
|
|
|
|
|
|
1063
|
|
|
|
|
|
|
# ============================================================================================ |
|
1064
|
|
|
|
|
|
|
# END OF PACKAGE |
|
1065
|
|
|
|
|
|
|
|
|
1066
|
|
|
|
|
|
|
=back |
|
1067
|
|
|
|
|
|
|
|
|
1068
|
|
|
|
|
|
|
=cut |
|
1069
|
|
|
|
|
|
|
|
|
1070
|
|
|
|
|
|
|
1; |
|
1071
|
|
|
|
|
|
|
|