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
|
|
61
|
use strict ; |
|
10
|
|
|
|
|
19
|
|
|
10
|
|
|
|
|
429
|
|
61
|
10
|
|
|
10
|
|
63
|
use File::Basename ; |
|
10
|
|
|
|
|
22
|
|
|
10
|
|
|
|
|
908
|
|
62
|
10
|
|
|
10
|
|
59
|
use Data::Dumper ; |
|
10
|
|
|
|
|
20
|
|
|
10
|
|
|
|
|
6289
|
|
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
|
|
106
|
@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
|
|
|
|
|
39
|
foreach (@FORMATS) |
224
|
|
|
|
|
|
|
{ |
225
|
130
|
|
|
|
|
180
|
my ($ext, $regexp) = @$_ ; |
226
|
130
|
|
100
|
|
|
529
|
$FORMATS{$ext} ||= [] ; |
227
|
130
|
|
|
|
|
132
|
push @{$FORMATS{$ext}}, $regexp ; |
|
130
|
|
|
|
|
351
|
|
228
|
|
|
|
|
|
|
} |
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
# Create alias list |
231
|
|
|
|
|
|
|
%ALIASES = ( |
232
|
10
|
|
|
|
|
62
|
'mpg' => 'mpeg', |
233
|
|
|
|
|
|
|
'mpeg2' => 'mpeg', |
234
|
|
|
|
|
|
|
'mpeg4' => 'mp4', |
235
|
|
|
|
|
|
|
'TS' => 'ts', |
236
|
|
|
|
|
|
|
) ; |
237
|
10
|
|
|
|
|
29
|
foreach (@FORMATS) |
238
|
|
|
|
|
|
|
{ |
239
|
130
|
|
|
|
|
152
|
my ($ext, $regexp) = @$_ ; |
240
|
130
|
|
|
|
|
60253
|
$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
|
180209
|
my ($destfile_ref, $out_ref, $lang_ref, $errors_aref, $warnings_aref) = @_ ; |
542
|
53
|
|
|
|
|
99
|
my $error = 0 ; |
543
|
|
|
|
|
|
|
|
544
|
|
|
|
|
|
|
# language spec is optional |
545
|
53
|
|
|
|
|
125
|
my $lang = "" ; |
546
|
53
|
|
50
|
|
|
161
|
$lang_ref ||= \$lang ; |
547
|
|
|
|
|
|
|
|
548
|
53
|
|
50
|
|
|
153
|
$$destfile_ref ||= "" ; |
549
|
53
|
|
100
|
|
|
188
|
$$lang_ref ||= "" ; |
550
|
53
|
|
|
|
|
83
|
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
|
|
|
|
|
142
|
($$out_ref, $$lang_ref) = _normalise_output_spec($$out_ref, $$lang_ref) ; |
554
|
53
|
50
|
|
|
|
122
|
print STDERR "sanitise_options($$destfile_ref, $$out_ref, $$lang_ref)\n" if $DEBUG ; |
555
|
|
|
|
|
|
|
|
556
|
|
|
|
|
|
|
# check specified filename |
557
|
53
|
|
|
|
|
1550
|
my ($name, $path, $ext) = fileparse($$destfile_ref, '\..*') ; |
558
|
53
|
100
|
|
|
|
156
|
$ext = substr $ext, 1 if $ext ; |
559
|
53
|
|
|
|
|
82
|
my $dest = "$path$name" ; |
560
|
|
|
|
|
|
|
|
561
|
53
|
50
|
|
|
|
100
|
print STDERR " + dest=$dest ext=$ext\n" if $DEBUG ; |
562
|
|
|
|
|
|
|
|
563
|
|
|
|
|
|
|
# check output spec |
564
|
53
|
100
|
|
|
|
125
|
if (!$$out_ref) |
565
|
|
|
|
|
|
|
{ |
566
|
|
|
|
|
|
|
## no output spec defined |
567
|
|
|
|
|
|
|
|
568
|
|
|
|
|
|
|
# get default output spec |
569
|
19
|
|
|
|
|
54
|
my $default_ext = $FORMATS[0][0] ; |
570
|
19
|
50
|
|
|
|
39
|
print STDERR "format regexp 2 out - Default...\n" if $DEBUG ; |
571
|
19
|
|
|
|
|
63
|
my $default_out = _format_regexp2out($FORMATS[0][1], $$lang_ref) ; |
572
|
|
|
|
|
|
|
|
573
|
19
|
50
|
|
|
|
47
|
print STDERR "No out specified: default out=$default_out default ext=$default_ext\n" if $DEBUG ; |
574
|
|
|
|
|
|
|
|
575
|
|
|
|
|
|
|
# check file format |
576
|
19
|
100
|
|
|
|
35
|
if (!$ext) |
577
|
|
|
|
|
|
|
{ |
578
|
2
|
50
|
|
|
|
5
|
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
|
|
|
|
|
5
|
$ext = $default_ext ; |
582
|
|
|
|
|
|
|
} |
583
|
|
|
|
|
|
|
else |
584
|
|
|
|
|
|
|
{ |
585
|
|
|
|
|
|
|
# file format specified, see if we support it |
586
|
17
|
100
|
|
|
|
61
|
if (exists($ALIASES{$ext})) |
587
|
|
|
|
|
|
|
{ |
588
|
|
|
|
|
|
|
# make sure the extension is in a form we understand |
589
|
16
|
|
|
|
|
36
|
my $aliased_ext = $ALIASES{$ext} ; |
590
|
|
|
|
|
|
|
|
591
|
16
|
50
|
|
|
|
35
|
print STDERR "format regexp 2 out - preferred for $ext ...\n" if $DEBUG ; |
592
|
|
|
|
|
|
|
# convert the first (preferred) regexp into output spec |
593
|
16
|
|
|
|
|
66
|
($$out_ref, $$lang_ref) = _format_regexp2out($FORMATS{$aliased_ext}[0], $$lang_ref) ; |
594
|
|
|
|
|
|
|
|
595
|
16
|
50
|
|
|
|
55
|
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
|
|
|
|
|
3
|
$ext = $default_ext ; |
607
|
|
|
|
|
|
|
} |
608
|
|
|
|
|
|
|
} |
609
|
|
|
|
|
|
|
} |
610
|
|
|
|
|
|
|
|
611
|
|
|
|
|
|
|
# check for language spec being dropped due to output spec |
612
|
53
|
100
|
100
|
|
|
229
|
if (($orig_lang) && ($$lang_ref ne $orig_lang)) |
613
|
|
|
|
|
|
|
{ |
614
|
|
|
|
|
|
|
# report warning |
615
|
6
|
|
|
|
|
18
|
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
|
|
|
|
|
127
|
$error = _sanitise_options($destfile_ref, $out_ref, $errors_aref, $warnings_aref) ; |
620
|
|
|
|
|
|
|
|
621
|
53
|
|
|
|
|
154
|
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
|
|
82
|
my ($destfile_ref, $output_spec_ref, $errors_aref, $warnings_aref) = @_ ; |
804
|
53
|
|
|
|
|
61
|
my $error = 0 ; |
805
|
|
|
|
|
|
|
|
806
|
53
|
|
50
|
|
|
112
|
$$destfile_ref ||= "" ; |
807
|
53
|
|
50
|
|
|
101
|
$errors_aref ||= [] ; |
808
|
53
|
|
50
|
|
|
103
|
$warnings_aref ||= [] ; |
809
|
|
|
|
|
|
|
|
810
|
53
|
50
|
|
|
|
95
|
print STDERR "_sanitise_options($$destfile_ref, $$output_spec_ref)\n" if $DEBUG ; |
811
|
|
|
|
|
|
|
|
812
|
|
|
|
|
|
|
# check specified filename |
813
|
53
|
|
|
|
|
1164
|
my ($name, $path, $ext) = fileparse($$destfile_ref, '\..*') ; |
814
|
53
|
100
|
|
|
|
137
|
$ext = substr $ext, 1 if $ext ; |
815
|
53
|
|
|
|
|
76
|
my $dest = "$path$name" ; |
816
|
|
|
|
|
|
|
|
817
|
53
|
50
|
|
|
|
95
|
print STDERR " + dest=$dest ext=$ext\n" if $DEBUG ; |
818
|
|
|
|
|
|
|
|
819
|
|
|
|
|
|
|
# check output spec |
820
|
53
|
50
|
|
|
|
112
|
if ($$output_spec_ref) |
821
|
|
|
|
|
|
|
{ |
822
|
|
|
|
|
|
|
## output spec defined |
823
|
|
|
|
|
|
|
|
824
|
|
|
|
|
|
|
# use it to check the file format |
825
|
53
|
|
|
|
|
114
|
my @supported_types = _output_formats($$output_spec_ref) ; |
826
|
|
|
|
|
|
|
|
827
|
53
|
50
|
|
|
|
114
|
print STDERR "out specified: supported types=" . Data::Dumper->Dump([\@supported_types]) if $DEBUG ; |
828
|
|
|
|
|
|
|
|
829
|
|
|
|
|
|
|
# check file format |
830
|
53
|
100
|
|
|
|
95
|
if (!$ext) |
831
|
|
|
|
|
|
|
{ |
832
|
|
|
|
|
|
|
# no file format, set it based on output spec |
833
|
7
|
50
|
|
|
|
22
|
if (@supported_types) |
834
|
|
|
|
|
|
|
{ |
835
|
|
|
|
|
|
|
# use first supported type |
836
|
7
|
|
|
|
|
16
|
$ext = $supported_types[0] ; |
837
|
|
|
|
|
|
|
} |
838
|
|
|
|
|
|
|
} |
839
|
|
|
|
|
|
|
else |
840
|
|
|
|
|
|
|
{ |
841
|
|
|
|
|
|
|
# file format specified, check it matches supported types |
842
|
46
|
|
|
|
|
73
|
my %valid_types = map { $_ => 1} @supported_types ; |
|
207
|
|
|
|
|
476
|
|
843
|
46
|
100
|
100
|
|
|
315
|
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
|
|
|
|
|
22
|
my $old_ext = $ext ; |
851
|
14
|
|
|
|
|
17
|
$ext = undef ; |
852
|
14
|
50
|
|
|
|
28
|
if (@supported_types) |
853
|
|
|
|
|
|
|
{ |
854
|
|
|
|
|
|
|
# use first supported type |
855
|
14
|
|
|
|
|
16
|
my $new_ext = $supported_types[0] ; |
856
|
|
|
|
|
|
|
|
857
|
|
|
|
|
|
|
# report warning |
858
|
14
|
|
|
|
|
32
|
push @$warnings_aref, "File type \"$old_ext\" does not match requested output, changing to \"$new_ext\"" ; |
859
|
|
|
|
|
|
|
|
860
|
|
|
|
|
|
|
# change |
861
|
14
|
|
|
|
|
38
|
$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
|
|
|
|
136
|
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
|
|
|
|
|
114
|
$$destfile_ref = "$path$name.$ext" ; |
881
|
|
|
|
|
|
|
} |
882
|
|
|
|
|
|
|
|
883
|
53
|
50
|
|
|
|
107
|
if ($error) |
884
|
|
|
|
|
|
|
{ |
885
|
0
|
|
|
|
|
0
|
push @$errors_aref, $error ; |
886
|
|
|
|
|
|
|
} |
887
|
|
|
|
|
|
|
|
888
|
53
|
|
|
|
|
111
|
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
|
|
147
|
my ($out, $lang) = @_ ; |
897
|
|
|
|
|
|
|
|
898
|
88
|
|
100
|
|
|
223
|
$out ||= "" ; |
899
|
88
|
|
100
|
|
|
235
|
$lang ||= "" ; |
900
|
|
|
|
|
|
|
|
901
|
88
|
|
|
|
|
117
|
my $out_spec = "" ; |
902
|
|
|
|
|
|
|
|
903
|
88
|
50
|
|
|
|
222
|
print STDERR "_normalise_output_spec(out=\"$out\", lang=\"$lang\")\n" if $DEBUG ; |
904
|
|
|
|
|
|
|
|
905
|
88
|
100
|
|
|
|
170
|
if ($out) |
906
|
|
|
|
|
|
|
{ |
907
|
|
|
|
|
|
|
# number of audio channels (-1 because, if audio is specified in the output, that already adds 1) |
908
|
69
|
|
|
|
|
182
|
my $num_audio = _audio_chan_count($lang) ; |
909
|
69
|
100
|
|
|
|
161
|
$num_audio-- if $num_audio >= 1 ; |
910
|
|
|
|
|
|
|
|
911
|
69
|
50
|
|
|
|
127
|
print STDERR " + num_audio=$num_audio\n" if $DEBUG ; |
912
|
|
|
|
|
|
|
|
913
|
|
|
|
|
|
|
# look at each stream type in the correct order |
914
|
69
|
|
|
|
|
186
|
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
|
|
|
|
|
415
|
my $type_ch = substr $type, 0, 1 ; |
919
|
207
|
100
|
|
|
|
2305
|
if ($out =~ /$type_ch/) |
920
|
|
|
|
|
|
|
{ |
921
|
120
|
|
|
|
|
141
|
$out_spec .= $type_ch ; |
922
|
|
|
|
|
|
|
|
923
|
120
|
50
|
|
|
|
260
|
print STDERR " + + add $type : $type_ch\n" if $DEBUG ; |
924
|
|
|
|
|
|
|
|
925
|
120
|
100
|
|
|
|
325
|
if ($type eq 'audio') |
926
|
|
|
|
|
|
|
{ |
927
|
|
|
|
|
|
|
## add in the language audio channels (-1 ) |
928
|
58
|
|
|
|
|
106
|
$out_spec .= 'a'x$num_audio ; |
929
|
|
|
|
|
|
|
|
930
|
58
|
50
|
|
|
|
198
|
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
|
|
|
|
857
|
if ($type eq 'audio') |
937
|
|
|
|
|
|
|
{ |
938
|
|
|
|
|
|
|
## no audio specified, so kill off the language spec |
939
|
11
|
|
|
|
|
14
|
$lang = "" ; |
940
|
|
|
|
|
|
|
|
941
|
11
|
50
|
|
|
|
39
|
print STDERR " + + no audio : removing any language spec\n" if $DEBUG ; |
942
|
|
|
|
|
|
|
} |
943
|
|
|
|
|
|
|
} |
944
|
|
|
|
|
|
|
} |
945
|
|
|
|
|
|
|
} |
946
|
|
|
|
|
|
|
|
947
|
88
|
50
|
|
|
|
185
|
print STDERR "final out_spec=\"$out_spec\"\n" if $DEBUG ; |
948
|
|
|
|
|
|
|
|
949
|
88
|
|
|
|
|
301
|
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
|
|
79
|
my ($out) = @_ ; |
958
|
|
|
|
|
|
|
|
959
|
|
|
|
|
|
|
## Get list of valid file types |
960
|
53
|
50
|
|
|
|
110
|
print STDERR "checking out $out...\n" if $DEBUG ; |
961
|
53
|
|
|
|
|
70
|
my @valid_types ; |
962
|
53
|
|
|
|
|
117
|
foreach my $fmt_aref (@FORMATS) |
963
|
|
|
|
|
|
|
{ |
964
|
689
|
|
|
|
|
1030
|
my ($valid_fmt, $regexp) = @$fmt_aref ; |
965
|
|
|
|
|
|
|
|
966
|
689
|
50
|
|
|
|
1087
|
print STDERR " + check $out : $valid_fmt => $regexp\n" if $DEBUG ; |
967
|
689
|
100
|
|
|
|
7657
|
if ($out =~ m/^$regexp$/) |
968
|
|
|
|
|
|
|
{ |
969
|
241
|
50
|
|
|
|
411
|
print STDERR " + + match\n" if $DEBUG ; |
970
|
|
|
|
|
|
|
# save format with the command information |
971
|
|
|
|
|
|
|
# $valid_types{$valid_fmt} = $COMMANDS{$valid_fmt} ; |
972
|
241
|
|
|
|
|
671
|
push @valid_types, $valid_fmt ; |
973
|
|
|
|
|
|
|
} |
974
|
|
|
|
|
|
|
} |
975
|
53
|
|
|
|
|
196
|
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
|
|
64
|
my ($regexp, $lang) = @_ ; |
989
|
|
|
|
|
|
|
|
990
|
35
|
|
|
|
|
54
|
my $out = $regexp ; |
991
|
|
|
|
|
|
|
|
992
|
35
|
50
|
|
|
|
84
|
print STDERR "_format_regexp2out($regexp)\n" if $DEBUG ; |
993
|
|
|
|
|
|
|
|
994
|
35
|
|
|
|
|
112
|
$out =~ s/\+//g ; |
995
|
35
|
50
|
|
|
|
76
|
print STDERR " + remove + : $out\n" if $DEBUG ; |
996
|
35
|
|
|
|
|
106
|
$out =~ s/(.)\*//g ; |
997
|
35
|
50
|
|
|
|
125
|
print STDERR " + remove X* : $out\n" if $DEBUG ; |
998
|
35
|
|
|
|
|
59
|
$out =~ s/\*//g ; |
999
|
35
|
50
|
|
|
|
122
|
print STDERR " + remove * : $out\n" if $DEBUG ; |
1000
|
|
|
|
|
|
|
|
1001
|
|
|
|
|
|
|
# normalise |
1002
|
35
|
|
|
|
|
69
|
($out, $lang) = _normalise_output_spec($out, $lang) ; |
1003
|
|
|
|
|
|
|
|
1004
|
35
|
50
|
|
|
|
84
|
print STDERR " + final out : $out\n" if $DEBUG ; |
1005
|
|
|
|
|
|
|
|
1006
|
35
|
100
|
|
|
|
117
|
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
|
|
104
|
my ($language_spec) = @_ ; |
1015
|
69
|
|
|
|
|
93
|
my $num_audio = 0 ; |
1016
|
|
|
|
|
|
|
|
1017
|
|
|
|
|
|
|
# process language spec |
1018
|
69
|
|
100
|
|
|
227
|
$language_spec ||= "" ; |
1019
|
69
|
100
|
|
|
|
141
|
if ($language_spec) |
1020
|
|
|
|
|
|
|
{ |
1021
|
|
|
|
|
|
|
# appends to default audio chan |
1022
|
38
|
100
|
|
|
|
154
|
if ($language_spec =~ s/\+//g) |
1023
|
|
|
|
|
|
|
{ |
1024
|
17
|
|
|
|
|
26
|
++$num_audio ; |
1025
|
|
|
|
|
|
|
} |
1026
|
|
|
|
|
|
|
|
1027
|
|
|
|
|
|
|
# work through the language spec |
1028
|
38
|
|
|
|
|
190
|
my @lang = split /[\s,]+/, $language_spec ; |
1029
|
38
|
|
|
|
|
87
|
$num_audio += scalar(@lang) ; |
1030
|
|
|
|
|
|
|
} |
1031
|
|
|
|
|
|
|
|
1032
|
69
|
|
|
|
|
124
|
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
|
|
|
|
|
|
|
|