source: gs2-extensions/video-and-audio/trunk/src/perllib/plugins/VideoConverter.pm@ 25346

Last change on this file since 25346 was 25346, checked in by davidb, 12 years ago

Updates to code that take account of changes in the central PM modules

File size: 49.2 KB
Line 
1###########################################################################
2#
3# VideoConverter - helper plugin that does video (and audio) conversion using ffmpeg
4#
5# A component of the Greenstone digital library software
6# from the New Zealand Digital Library Project at the
7# University of Waikato, New Zealand.
8#
9# Copyright (C) 2008 New Zealand Digital Library Project
10#
11# This program is free software; you can redistribute it and/or modify
12# it under the terms of the GNU General Public License as published by
13# the Free Software Foundation; either version 2 of the License, or
14# (at your option) any later version.
15#
16# This program is distributed in the hope that it will be useful,
17# but WITHOUT ANY WARRANTY; without even the implied warranty of
18# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19# GNU General Public License for more details.
20#
21# You should have received a copy of the GNU General Public License
22# along with this program; if not, write to the Free Software
23# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24#
25###########################################################################
26package VideoConverter;
27
28use MultimediaConverter;
29
30
31use strict;
32no strict 'refs'; # allow filehandles to be variables and viceversa
33
34use gsprintf 'gsprintf';
35
36BEGIN {
37 @VideoConverter::ISA = ('MultimediaConverter');
38}
39
40
41my @thumb_arguments = (
42 { 'name' => "create_thumbnail",
43 'desc' => "{ImageConverter.create_thumbnail}",
44 'type' => "enum",
45 'list' => [{'name' => "true", 'desc' => "{common.true}"},
46 {'name' => "false", 'desc' => "{common.false}"}],
47 'deft' => "true",
48 'reqd' => "no" },
49 { 'name' => "keep_original_video",
50 'desc' => "Copy the original video file in the collection as an associated file.",
51 'type' => "enum",
52 'list' => [{'name' => "true", 'desc' => "Include the original video file in the collection"},
53 {'name' => "false", 'desc' => "Do not copy the original video file in the collection"}],
54 'deft' => "false",
55 'reqd' => "no" },
56 { 'name' => "thumbnailsize",
57 'desc' => "{ImageConverter.thumbnailsize}",
58 'type' => "int",
59 'deft' => "100",
60 'range' => "1,",
61 'reqd' => "no" },
62 { 'name' => "thumbnailtype",
63 'desc' => "{ImageConverter.thumbnailtype}",
64 'type' => "string",
65 'deft' => "jpg",
66 'reqd' => "no" },
67 { 'name' => "noscaleup",
68 'desc' => "{ImageConverter.noscaleup}",
69 'type' => "flag",
70 'reqd' => "no" } );
71
72
73my @screenview_arguments = (
74 { 'name' => "create_screenview",
75 'desc' => "{ImageConverter.create_screenview}",
76 'type' => "enum",
77 'list' => [{'name' => "true", 'desc' => "{common.true}"},
78 {'name' => "false", 'desc' => "{common.false}"}],
79 'deft' => "true",
80 'reqd' => "no" },
81 { 'name' => "screenviewsize",
82 'desc' => "{ImageConverter.screenviewsize}",
83 'type' => "int",
84 'deft' => "720",
85 'range' => "1,",
86 'reqd' => "no" },
87 { 'name' => "screenviewtype",
88 'desc' => "{ImageConverter.screenviewtype}",
89 'type' => "string",
90 'deft' => "jpg",
91 'reqd' => "no" } );
92
93my $arguments = [
94 @thumb_arguments,
95 @screenview_arguments,
96
97 { 'name' => "converttotype",
98 'desc' => "{ImageConverter.converttotype}",
99 'type' => "string",
100 'deft' => "",
101 'reqd' => "no" },
102
103
104 { 'name' => "converttosize",
105 'desc' => "{VideoPlugin.converttosize}",
106 'type' => "int",
107 'deft' => "",
108## 'deft' => "352",
109 'reqd' => "no" },
110 { 'name' => "converttobitrate",
111 'desc' => "{VideoPlugin.converttobitrate}",
112 'type' => "string",
113 'deft' => "200k",
114 'reqd' => "no" },
115
116 { 'name' => "create_keyframes",
117 'desc' => "{VideoPlugin.extractkeyframes}",
118 'type' => "enum",
119 'list' => [{'name' => "true", 'desc' => "{common.true}"},
120 {'name' => "false", 'desc' => "{common.false}"}],
121 'deft' => "false",
122 'reqd' => "no" },
123 { 'name' => "keyframes_algorithm",
124 'desc' => "{VideoPlugin.keyframesAlgorithm}",
125 'type' => "enum",
126 'list' => [{'name' => "mtn", 'desc' => "{keyframesAlgorithm.mtn}"},
127 {'name' => "ffkeyframe", 'desc' => "{keyframesAlgorithm.ffkeyframe}"}],
128 'deft' => "mtn",
129 'reqd' => "no" },
130
131 { 'name' => "ffkeyframe_num_shots",
132 'desc' => "{VideoPlugin.keep_keyframes}",
133 'type' => "string",
134 'deft' => "all",
135 'reqd' => "no" },
136
137 { 'name' => "mtn_skip_intro",
138 'desc' => "{VideoPlugin.mtnSkipIntro}",
139 'type' => "int",
140 'deft' => "0",
141 'reqd' => "no" },
142 { 'name' => "mtn_skip_end",
143 'desc' => "{VideoPlugin.mtnSkipEnd}",
144 'type' => "int",
145 'deft' => "0",
146 'reqd' => "no" },
147 { 'name' => "mtn_timestep",
148 'desc' => "{VideoPlugin.mtnTimestep}",
149 'type' => "int",
150 'deft' => "120",
151 'reqd' => "no" },
152 { 'name' => "mtn_detect_blanks",
153 'desc' => "{VideoPlugin.mtnDetectBlanks}",
154 'type' => "int",
155 'deft' => "80",
156 'range' => "0,100",
157 'reqd' => "no" },
158# { 'name' => "mtn_detect_sharpness",
159# 'desc' => "{VideoPlugin.mtnDetectSharpness}",
160# 'type' => "enum",
161# [{'name' => "off", 'desc' => "{mtnDetectSharpness.off}"},
162# {'name' => "low", 'desc' => "{mtnDetectSharpness.low}"},
163# {'name' => "medium", 'desc' => "{mtnDetectSharpness.medium}"},
164# {'name' => "high", 'desc' => "{mtnDetectSharpness.high}"},
165# {'name' => "highest", 'desc' => "{mtnDetectSharpness.highest}"}],
166# 'deft' => "highest",
167# 'reqd' => "no" },
168
169 { 'name' => "create_montage",
170 'desc' => "{VideoPlugin.createMontage}",
171 'type' => "enum",
172 'list' => [{'name' => "true", 'desc' => "{common.true}"},
173 {'name' => "false", 'desc' => "{common.false}"}],
174 'deft' => "false",
175 'reqd' => "no" },
176
177 { 'name' => "streamingsize",
178 'desc' => "{VideoPlugin.streamingsize}",
179 'type' => "int",
180 'deft' => "352",
181 'reqd' => "no" },
182 { 'name' => "streamingbitrate",
183 'desc' => "{VideoPlugin.streamingbitrate}",
184 'type' => "string",
185 'deft' => "200k",
186 'reqd' => "no" },
187 { 'name' => "streamingHQsize",
188 'desc' => "{VideoPlugin.streamingsize}",
189 'type' => "int",
190 'deft' => "720",
191 'reqd' => "no" },
192 { 'name' => "streamingHQVideoBitrate",
193 'desc' => "{VideoPlugin.streamingbitrate}",
194 'type' => "int",
195 'deft' => "496",
196 'reqd' => "no" },
197 { 'name' => "streamingHQAudioBitrate",
198 'desc' => "{VideoPlugin.streamingbitrate}",
199 'type' => "int",
200 'deft' => "80",
201 'reqd' => "no" },
202 { 'name' => "videoDeinterlacingFilter",
203 'desc' => "Activate a deinterlacing filter to increase the quality of TV footage",
204 'type' => "enum",
205 'list' => [{'name' => "true", 'desc' => "{common.true}"},
206 {'name' => "false", 'desc' => "{common.false}"}],
207 'deft' => "false",
208 'reqd' => "no" },
209 { 'name' => "getMaximumMetadata",
210 'desc' => "Extract the maximum technical information of each video",
211 'type' => "enum",
212 'list' => [{'name' => "true", 'desc' => "{common.true}"},
213 {'name' => "false", 'desc' => "{common.false}"}],
214 'deft' => "false",
215 'reqd' => "no" },
216
217 { 'name' => "minimumsize",
218 'desc' => "{ImageConverter.minimumsize}",
219 'type' => "int",
220 'deft' => "100",
221 'range' => "1,",
222 'reqd' => "no" },
223
224 ];
225
226my $options = { 'name' => "VideoConverter",
227 'desc' => "{VideoConverter.desc}",
228 'abstract' => "yes",
229 'inherits' => "yes",
230 'args' => $arguments };
231
232sub new {
233 my ($class) = shift (@_);
234 my ($pluginlist,$inputargs,$hashArgOptLists) = @_;
235 push(@$pluginlist, $class);
236
237 push(@{$hashArgOptLists->{"ArgList"}},@{$arguments});
238 push(@{$hashArgOptLists->{"OptList"}},$options);
239
240 my $self = new MultimediaConverter($pluginlist, $inputargs, $hashArgOptLists, 1);
241
242
243 return bless $self, $class;
244
245}
246
247
248# Here we use 'MediaInfo' for video and ask for a specific specification
249sub mediaInfo_Inform_Cmd {
250 my ($video, $parameter, $outhandle, $verbosity) = @_;
251
252 # Use MediaInfo CLI to get the file spec according to the requested parameter
253 my $command = "mediainfo --Inform=\"$parameter\" \"$video\"";
254
255 print $outhandle " $command\n" if ($verbosity > 2);
256 my $result = '';
257 $result = `$command 2>&1`;
258 chomp $result;
259 print $outhandle " $result\n" if ($verbosity > 4);
260
261 # Return the asked spec
262 return ($result);
263}
264
265# Here we use mediaInfo to get all the possible metadata in XML form that could then be parsed
266# the result will vary depending on the file type and how it was encoded
267sub mediaInfo_XML_Cmd {
268 my ($video, $outhandle, $verbosity) = @_;
269
270 # Use MediaInfo CLI to get the file spec according to the requested parameter
271 my $command = "mediainfo --Output=XML \"$video\"";
272
273 print $outhandle " $command\n" if ($verbosity > 2);
274 my $result = '';
275 $result = `$command 2>&1`;
276 print $outhandle " $result\n" if ($verbosity > 4);
277
278 # Return the asked spec
279 return ($result);
280}
281
282# Here we parse the video specs contained in the XML text in order to create a metadata entry for each field
283# that mediainfo is giving back, this can vary from media file to media file.
284sub mediaInfo_parse_XML {
285 my ($xmlTxt, $outhandle, $verbosity) = @_;
286
287 my @parts = split(/<track\s+type=\"(.*?)\">/is,$xmlTxt);
288
289 shift @parts; # Skip preamble
290 my $metadata_table = {};
291
292 while (@parts) {
293 my $type = shift(@parts);
294 my $vals = shift(@parts);
295 my @fieldlist=();
296 while ($vals =~ s/<(.*?)>(.*?)<\/\1>//) {
297 my $metaname = $1;
298 my $metaval = $2;
299 my $fullmetaname = "mi.$type\^$metaname";
300 $metadata_table->{$fullmetaname}= $metaval;
301 push(@fieldlist,$fullmetaname);
302 }
303
304 $metadata_table->{"mi.${type}Fields"}= join(",",@fieldlist);
305 }
306
307 return $metadata_table;
308
309 while (@parts) {
310 my $type = shift(@parts);
311 my $vals = shift(@parts);
312 my @fieldlist=();
313 while ($vals =~ s/<(.*?)>(.*?)<\/\1>//) {
314 my $metaname = $1;
315 my $metaval = $2;
316 my $fullmetaname = "mi.$type\^$metaname";
317 if($metaname ne "Complete_name") #ignore this field as it is pointing to the cache directory
318 {
319 $metadata_table->{$fullmetaname}= $metaval;
320 push(@fieldlist,$fullmetaname);
321 }
322 }
323 $metadata_table->{"mi.${type}Fields"}= join(",",@fieldlist);
324 }
325 return $metadata_table;
326
327}
328
329sub identifyMI {
330 my ($video, $outhandle, $verbosity) = @_;
331
332 # Use the MediaInfo command to get the file specs
333 my $command = "MediaInfo \"$video\"";
334
335 print $outhandle " $command\n" if ($verbosity > 2);
336 my $result = '';
337 $result = `$command 2>&1`;
338 print $outhandle " $result\n" if ($verbosity > 4);
339
340 # Read the type, width, and height etc. from media file
341 # This could be done in a more efficient way by only calling the application MediaInfo once and
342 # giving all the parameters inside a special macro file then parsing the results, however
343 # on most operating system when the application is called once it is then cached for subsequent calls.
344 # General info
345 my $vtype = mediaInfo_Inform_Cmd($video,"General;%Format%",$outhandle,0); #Container of the video e.g. MPEG-TS
346 my $duration = mediaInfo_Inform_Cmd($video,"General;%Duration%",$outhandle,0); #Duration of the video in ms e.g. 5545841
347 my $durationDisplay = mediaInfo_Inform_Cmd($video,"General;%Duration/String%",$outhandle,0); #Duration of the video in minutes and secs or hours and minutes e.g. 25m 12s or 2h 26m
348 my $filesize = mediaInfo_Inform_Cmd($video,"General;%FileSize%",$outhandle,0); #File size in bytes e.g. 1024
349 my $filesizeDisplay = mediaInfo_Inform_Cmd($video,"General;%FileSize/String%",$outhandle,0); #File size with measure e.g. 25.6 MiB
350 my $orateDisplay = mediaInfo_Inform_Cmd($video,"General;%OverallBitRate/String%",$outhandle,0); #General file bitrate with measure e.g. 7 538 Kbps
351 # Video info
352 my $vcodec = mediaInfo_Inform_Cmd($video,"Video;%Format%",$outhandle,0); #Codec used for the video compression e.g. AVC
353 my $vrate = mediaInfo_Inform_Cmd($video,"Video;%BitRate%",$outhandle,0); #Bitrate used for the video stream in bps e.g. 546165
354 my $width = mediaInfo_Inform_Cmd($video,"Video;%Width%",$outhandle,0); #Width of the video in pixels e.g. 1920
355 my $height = mediaInfo_Inform_Cmd($video,"Video;%Height%",$outhandle,0); #Height of the video in pixels e.g. 1080
356 my $fps = mediaInfo_Inform_Cmd($video,"Video;%FrameRate%",$outhandle,0); #Video frames per second e.g. 30
357 # Audio info
358 my $acodec = mediaInfo_Inform_Cmd($video,"Audio;%Format%",$outhandle,0); # Audio codec used for the compression e.g. AAC
359 my $afreq = mediaInfo_Inform_Cmd($video,"Audio;%SamplingRate/String%",$outhandle,0); #Sampling rate used for audio encoding e.g. 48000
360 my $achan = mediaInfo_Inform_Cmd($video,"Audio;%Channel(s)%",$outhandle,0); #Number of channels e.g. 2
361 my $arate = mediaInfo_Inform_Cmd($video,"Audio;%BitRate%",$outhandle,0); #Audio bitrate for the audio stream in bps e.g. 65436
362 my $atracks = mediaInfo_Inform_Cmd($video,"Audio;%StreamCount%",$outhandle,0); #number of audio tracks in this video file e.g. 1
363 # Subtitles info
364 my $stype = mediaInfo_Inform_Cmd($video,"Text;%Format%",$outhandle,0); # Format used for the subtitles e.g. DVB Subtitle
365 my $slang = mediaInfo_Inform_Cmd($video,"Text;%Language/String%",$outhandle,0); # Language used for the subtitles e.g. English
366
367
368 my $xmlTxt = mediaInfo_XML_Cmd ($video,$outhandle,0);
369 my $metadata_table = mediaInfo_parse_XML($xmlTxt,$outhandle,0);
370
371 # Return the specs
372 my $identify_vals = { "vtype" => $vtype, "width" => $width, "height" => $height, "duration" => $duration, "filesize" => $filesize, "vcodec" => $vcodec,
373 "fps" => $fps, "acodec" => $acodec, "afreq" => $afreq, "achan" => $achan, "arate" => $arate, "metadata_table" => $metadata_table, "durationDisplay" => $durationDisplay,
374 "filesizeDisplay" => $filesizeDisplay, "orateDisplay" => $orateDisplay, "vrate" => $vrate, "stype" => $stype, "slang" => $slang, "atracks" => $atracks };
375
376 return $identify_vals;
377}
378
379# Here we use 'ffmpeg' for video and ask for a specific specification
380# This is now deprecated this will be replaced by identifyMI in the future
381sub identify {
382 my ($video, $outhandle, $verbosity) = @_;
383
384 # Use the ffmpeg command to get the file specs
385 my $command = "ffmpeg -i \"$video\"";
386
387 print $outhandle " $command\n" if ($verbosity > 2);
388 my $result = '';
389 $result = `$command 2>&1`;
390 print $outhandle " $result\n" if ($verbosity > 4);
391
392 # Read the type, width, and height etc.
393 my $vtype = 'unknown';
394 my $vcodec = 'unknown';
395 my $width = 'unknown';
396 my $height = 'unknown';
397 my $fps = 'unknown';
398
399 my $atype = 'unknown';
400 my $afreq = 'unknown';
401 my $achan = 'unknown';
402 my $arate = 'unknown';
403
404 my $video_safe = quotemeta $video;
405
406 # strip off everything up to filename
407 $result =~ s/^.*\'$video_safe\'://s;
408
409## if ($result =~ m/Video: (.*?) fps/m) {
410 if ($result =~ m/^\s+Stream.*?Video: (.*?)$/m) {
411 my $video_info = $1;
412 $video_info =~ s/\s*\[.*?\]//g;
413
414 my @video_fields = split(/,\s*/,$video_info);
415
416 $vtype = shift @video_fields;
417
418 if ($video_fields[0] !~ m/(\d+)x(\d+)/) {
419 $vcodec = shift @video_fields;
420 }
421 my $video_dim = shift @video_fields;
422
423 ($width,$height) = ($video_dim =~ m/(\d+)x(\d+)/);
424
425 if ($video_fields[0] =~ m/(\d+)\s+tbr$/) {
426 $fps = $1;
427 }
428
429# if ($video_info =~ m/([^,]+),(?: ([^,]+),)? (\d+)x(\d+),.*?(\d+\.\d+)/)
430# {
431# $vtype = $1;
432# $vcodec = $2 if defined $2;
433# $width = $3;
434# $height = $4;
435# $fps = $5;
436# }
437 }
438
439 if ($result =~ m/Audio: (\w+), (\d+) Hz, (\w+)(?:, (\d+.*))?/m) {
440 $atype = $1;
441 $afreq = $2;
442 $achan = $3;
443 $arate = $4 if (defined $4);
444 }
445
446 # Read the duration
447 my $duration = "unknown";
448 if ($result =~ m/Duration: (\d+:\d+:\d+\.\d+)/m) {
449 $duration = $1;
450 }
451 print $outhandle " file: $video:\t $vtype, $width, $height, $duration\n"
452 if ($verbosity > 2);
453
454 if ($verbosity >3) {
455 print $outhandle "\t video codec=$vcodec, fps = $fps\n";
456 print $outhandle "\t audio codec=$atype, freq = $afreq Hz, $achan, $arate\n";
457 }
458
459
460 my $identify_vals = { "vtype" => $vtype, "width" => $width, "height" => $height, "duration" => $duration, "filesize" => -s $video, "vcodec" => $vcodec,
461 "fps" => $fps, "acodec" => $atype, "afreq" => $afreq, "achan" => $achan, "arate" => $arate };
462
463 return $identify_vals;
464
465 # Return the specs
466 return ($vtype, $width, $height, $duration, -s $video,
467 $vcodec,$fps,$atype,$afreq,$achan,$arate);
468}
469
470
471sub vob_durations
472{
473 my ($media_base_dir,$title_num,$outhandle) = @_;
474
475 my $filter_re = sprintf("^VTS_%02d_[1-9]\\.VOB\$",$title_num);
476
477 my $duration_info = {};
478
479 if (opendir(VTSIN,$media_base_dir)) {
480 my @vts_title_vobs = grep { $_ =~ m/$filter_re/ } readdir(VTSIN);
481 closedir(VTSIN);
482
483 foreach my $v (@vts_title_vobs) {
484 my $full_v = &util::filename_cat($media_base_dir,$v);
485
486 my $identify_hash = identifyMI($full_v,$outhandle,0);
487
488 my $duration = $identify_hash->{'duration'};
489
490 my ($vob_num) = ($v =~ m/^VTS_\d\d_(\d)\.VOB$/);
491
492 $duration_info->{$vob_num} = $duration;
493 print STDERR "**** $title_num: $title_num, storing {$vob_num} => $duration\n";
494
495 }
496
497 }
498 else {
499 print $outhandle "Warning: unable to read files in directory $media_base_dir\n";
500 }
501
502 return $duration_info;
503
504}
505
506
507
508sub init_cache_for_file {
509 my $self = shift(@_);
510
511 my ($video_filename) = @_;
512
513 $self->SUPER::init_cache_for_file($video_filename);
514
515
516 my @flvtool2_monitor = ( 'monitor_init' ,"convertutil::monitor_init_unbuffered",
517 'monitor_line' , "VideoConverter::flvtool2_monitor_line",
518 'monitor_deinit' , "convertutil::monitor_deinit_unbuffered" );
519
520 $self->{'flvtool2_monitor'} = \@flvtool2_monitor;
521
522
523}
524
525
526
527sub optional_frame_scale
528{
529 my $self = shift (@_);
530 my ($orig_size,$video_width,$video_height) = @_;
531
532 my $s_opt = "";
533
534 if ($video_width > $video_height) {
535 if ($video_width > $orig_size) {
536 my $scale_factor = $orig_size/$video_width;
537 my $scaled_width = int($video_width * $scale_factor);
538 my $scaled_height = int($video_height * $scale_factor);
539
540 # round to be ensure multiple of 2 (needed by some codecs)
541 $scaled_width = int($scaled_width/2)*2;
542 $scaled_height = int($scaled_height/2)*2;
543
544 $s_opt = "-s ${scaled_width}x${scaled_height}";
545 }
546 # else, video is smaller than requested size, don't scale up
547 }
548 else {
549 if ($video_height > $orig_size) {
550 my $scale_factor = $orig_size/$video_height;
551 my $scaled_width = int($video_width * $scale_factor);
552 my $scaled_height = int($video_height * $scale_factor);
553
554 # round to be ensure multiple of 2 (needed by some codecs)
555 $scaled_width = int($scaled_width/2)*2;
556 $scaled_height = int($scaled_height/2)*2;
557
558 $s_opt = "-s ${scaled_width}x${scaled_height}";
559 }
560 # else, video is smaller than requested size, don't scale up
561
562 }
563
564 return $s_opt;
565}
566
567
568sub ffKeyframe_cmd
569{
570 my $self = shift (@_);
571 my ($ivideo_filename) = @_;
572
573 my $video_ext_dir = &util::filename_cat($ENV{'GSDLHOME'},"ext","video");
574
575 my $output_dir = $self->{'cached_dir'};
576 my $ivideo_root = $self->{'cached_file_root'};
577
578 my $oshot_filename = &util::filename_cat($output_dir,"shots.xml");
579
580 my $exp_duration = $self->{'exp_duration'};
581 my $t_opt = (defined $exp_duration) ? "-t $exp_duration" : "";
582
583 my $main_opts = "-y $t_opt";
584
585 my $hive = &util::filename_cat($video_ext_dir,"lib","vhook","hive.so");
586
587 my $oflash_filename = &util::filename_cat($output_dir,"$ivideo_root\_keyframe.flv");
588
589 my $vhook_opts = "$hive -o $oshot_filename -k $output_dir $ivideo_filename";
590
591 my $ivideo_filename_gsdlenv = $self->gsdlhome_independent($ivideo_filename);
592 my $oflash_filename_gsdlenv = $self->gsdlhome_independent($oflash_filename);
593
594 my $ffmpeg_cmd = "ffkeyframe $main_opts -vhook \"$vhook_opts\" -i \"$ivideo_filename_gsdlenv\" -an -y \"$oflash_filename_gsdlenv\"";
595
596
597 return ($ffmpeg_cmd,$oflash_filename);
598}
599
600
601sub mtnKeyframe_cmd
602{
603 my $self = shift (@_);
604 my ($ivideo_filename) = @_;
605
606 my $video_ext_dir = &util::filename_cat($ENV{'GSDLHOME'},"ext","video");
607
608 my $output_dir = $self->{'cached_dir'};
609 my $ivideo_root = $self->{'cached_file_root'};
610
611 my $frames_output_dir = &util::filename_cat($output_dir,"frames");
612
613 my $main_opts = "-z -P -I -O \"$frames_output_dir\"";
614
615 my $mtn_skip_intro_secs = $self->{'mtn_skip_intro'};
616 my $mtn_skip_end_secs = $self->{'mtn_skip_end'};
617 my $mtn_timestep_secs = $self->{'mtn_timestep'};
618 my $mtn_detect_blank_perc = $self->{'mtn_detect_blanks'};
619
620 my $mtn_detect_blank = $mtn_detect_blank_perc/100.0;
621
622 my $keyframe_height;
623 if ($self->{'create_screenview'} eq "true") {
624 $keyframe_height = $self->{'screenviewsize'};
625 }
626 else {
627 # assume thumbnail size
628 $keyframe_height = $self->{'thumbnailsize'};
629 }
630
631 $main_opts .= " -B $mtn_skip_intro_secs";
632 $main_opts .= " -E $mtn_skip_end_secs";
633 $main_opts .= " -s $mtn_timestep_secs";
634 $main_opts .= " -b $mtn_detect_blank";
635 $main_opts .= " -h $keyframe_height";
636
637 my $ivideo_filename_gsdlenv = $self->gsdlhome_independent($ivideo_filename);
638
639 my $mtn_cmd = "mtn $main_opts \"$ivideo_filename_gsdlenv\"";
640
641 my $output_s_filename = &util::filename_cat($frames_output_dir,$ivideo_root."_s.jpg");
642
643 return ($mtn_cmd,$output_s_filename);
644}
645
646
647sub get_ovideo_file
648{
649 my $self = shift (@_);
650 my ($stream_type) = @_;
651
652 my $ivideo_root = $self->{'cached_file_root'};
653 my $ofile;
654
655 if ($stream_type eq "flv")
656 {
657 $ofile = "${ivideo_root}_vstream.flv";
658 }
659 else
660 {
661 $ofile = "${ivideo_root}_vHQstream.mp4";
662 }
663
664 return $ofile;
665}
666
667sub get_ovideo_filename
668{
669 my $self = shift (@_);
670 my ($stream_type) = @_;
671
672 my $output_dir = $self->{'cached_dir'};
673
674 my $ofile = $self->get_ovideo_file($stream_type);
675
676 my $ofilename = &util::filename_cat($output_dir,$ofile);
677
678 return $ofilename;
679}
680
681# Generate the command to use with Handbrake to recompress a video using h264/aac compatible with Flash Player 10
682sub stream_mp4_video_cmd
683{
684 my $self = shift (@_);
685 my ($ivideo_filename,$streaming_HQ_size, $streaming_HQ_VideoBitrate,
686 $streaming_HQ_AudioBitrate) = @_;
687
688 my $output_dir = $self->{'cached_dir'};
689 my $ivideo_root = $self->{'cached_file_root'};
690
691 my $omp4_file = "${ivideo_root}_vHQstream.mp4";
692 my $omp4_filename = &util::filename_cat($output_dir,$omp4_file);
693
694
695 #my $exp_duration = $self->{'exp_duration'};
696 #my $t_opt = (defined $exp_duration) ? "-t $exp_duration" : "";
697
698 my $deinterlace = $self->{'videoDeinterlacingFilter'};
699 my $video_processing_parameters;
700 # Use specific resizing algorythms depending if we need to downsize the video resolution or not
701 if(!$streaming_HQ_size || $streaming_HQ_size eq "fullsize") {
702 $video_processing_parameters = "--strict-anamorphic";
703 } else {
704 $video_processing_parameters = "-w $streaming_HQ_size --loose-anamorphic";
705 }
706 # Use the deinterlace video filter if enabled in the plugin
707 if ($deinterlace eq "true")
708 {
709 $video_processing_parameters .= " --decomb";
710 }
711
712 my $ivideo_filename_gsdlenv = $self->gsdlhome_independent($ivideo_filename);
713 my $omp4_filename_gsdlenv = $self->gsdlhome_independent($omp4_filename);
714
715 # Generate the pre and post video parameters for Handbrake v0.9.4
716 my $pre_opts = "-t 1 -c 1";
717 my $post_opts = "-f mp4 -O $video_processing_parameters -e x264 -b $streaming_HQ_VideoBitrate -a 1 -E faac -6 dpl2 -R Auto -B $streaming_HQ_AudioBitrate -D 0.0 -x ref=2:bframes=2:subq=6:mixed-refs=0:weightb=0:8x8dct=0:trellis=0";
718
719 my $handbrake_cmd = "HandBrakeCLI -i \"$ivideo_filename_gsdlenv\" $pre_opts -o \"$omp4_filename_gsdlenv\" $post_opts";
720
721 #print STDERR "****\nHandbrake command: $handbrake_cmd\n****\n";
722
723 return ($handbrake_cmd,$omp4_filename,$omp4_file);
724}
725
726
727
728sub stream_flv_video_cmd
729{
730 my $self = shift (@_);
731 my ($ivideo_filename,$video_width,$video_height,
732 $streaming_quality,
733 $streaming_bitrate,$streaming_size,
734 $opt_streaming_achan, $opt_streaming_arate) = @_;
735
736 my $streaming_achan
737 = (defined $opt_streaming_achan) ? $opt_streaming_achan : 2;
738
739 my $streaming_arate
740 = (defined $opt_streaming_arate) ? $opt_streaming_arate : 22050;
741
742 my $output_dir = $self->{'cached_dir'};
743 my $ivideo_root = $self->{'cached_file_root'};
744
745 my $oflash_file = "${ivideo_root}_vstream.flv";
746 my $oflash_filename = &util::filename_cat($output_dir,$oflash_file);
747
748 my $s_opt = "";
749 my $media_type = $self->{'media_type'};
750 if ($media_type ne "audio") {
751 $s_opt = $self->optional_frame_scale($streaming_size,$video_width,$video_height);
752 }
753
754
755 my $exp_duration = $self->{'exp_duration'};
756 my $t_opt = (defined $exp_duration) ? "-t $exp_duration" : "";
757
758 my $main_opts = "-y $t_opt";
759
760 my $bitrate_opt = "-b $streaming_bitrate";
761 ### my $stream_opts = "-r 25 $s_opt";
762 my $stream_opts .= " $s_opt -ac $streaming_achan -ar $streaming_arate";
763
764 # -flags +ilme+ildct' and maybe '-flags +alt' for interlaced material, and try '-top 0/1'
765
766 my $all_opts = "$main_opts $stream_opts";
767
768 my $ffmpeg_cmd;
769
770 my $ivideo_filename_gsdlenv = $self->gsdlhome_independent($ivideo_filename);
771 my $oflash_filename_gsdlenv = $self->gsdlhome_independent($oflash_filename);
772
773 if ($streaming_quality eq "high") {
774
775 my $pass_log_file = &util::filename_cat($output_dir,"$ivideo_root-logpass.txt");
776 if (-e $pass_log_file) {
777 &util::rm($pass_log_file);
778 }
779
780 my $pass_log_file_gsdlenv = $self->gsdlhome_independent($pass_log_file);
781
782 $all_opts .= " -passlogfile \"$pass_log_file_gsdlenv\"";
783
784 my $ffmpeg_cmd_pass1 = "ffmpeg -pass 1 -i \"$ivideo_filename_gsdlenv\" $all_opts -y \"$oflash_filename_gsdlenv\"";
785
786 my $ffmpeg_cmd_pass2 = "ffmpeg -pass 2 -i \"$ivideo_filename_gsdlenv\" $all_opts $bitrate_opt -y \"$oflash_filename_gsdlenv\"";
787 $ffmpeg_cmd = "( $ffmpeg_cmd_pass1 && $ffmpeg_cmd_pass2 )";
788 }
789 else {
790 # single pass
791
792 $ffmpeg_cmd = "ffmpeg -i \"$ivideo_filename_gsdlenv\" $all_opts -y \"$oflash_filename_gsdlenv\"";
793 }
794
795 return ($ffmpeg_cmd,$oflash_filename,$oflash_file);
796}
797
798
799
800
801sub stream_flv_audio_cmd
802{
803 # AudioConverter also has a routine for doing this
804 # => merge into one!
805 # **************
806
807 my $self = shift (@_);
808 my ($ivideo_filename, $opt_streaming_achan, $opt_streaming_arate) = @_;
809
810 # Convert either audio or video to streamable audio format
811
812 my $streaming_achan
813 = (defined $opt_streaming_achan) ? $opt_streaming_achan : 2;
814
815 my $streaming_arate
816 = (defined $opt_streaming_arate) ? $opt_streaming_arate : 22050;
817
818 my $output_dir = $self->{'cached_dir'};
819 my $ivideo_root = $self->{'cached_file_root'};
820
821 my $ofla_file = "${ivideo_root}_astream.flv";
822 my $ofla_filename = &util::filename_cat($output_dir,$ofla_file);
823
824 my $exp_duration = $self->{'exp_duration'};
825 my $t_opt = (defined $exp_duration) ? "-t $exp_duration" : "";
826
827 my $main_opts = "-vn -y $t_opt";
828
829 my $stream_opts .= " -ac $streaming_achan -ar $streaming_arate";
830
831 my $all_opts = "$main_opts $stream_opts";
832
833 my $ffmpeg_cmd;
834
835 my $ivideo_filename_gsdlenv = $self->gsdlhome_independent($ivideo_filename);
836 my $ofla_filename_gsdlenv = $self->gsdlhome_independent($ofla_filename);
837 $ffmpeg_cmd = "ffmpeg -i \"$ivideo_filename_gsdlenv\" $all_opts -y \"$ofla_filename_gsdlenv\"";
838
839 return ($ffmpeg_cmd,$ofla_filename,$ofla_file);
840}
841
842
843
844
845sub audio_excerpt_cmd
846{
847 my $self = shift (@_);
848 my ($ivoa_filename,$hh,$mm,$ss,$opt_excerpt_len) = @_;
849
850 # ivoa = input video or audio
851
852 my $time_encoded = "$hh:$mm:$ss";
853 my $time_encoded_file = "$hh$mm$ss";
854
855
856 my $output_dir = $self->{'cached_dir'};
857 my $ivoa_root = $self->{'cached_file_root'};
858
859 my $omp3_file = "${ivoa_root}_$time_encoded_file.mp3";
860 my $omp3_filename = &util::filename_cat($output_dir,$omp3_file);
861
862 my $all_opts = "-y -acodec mp3 -ss $time_encoded ";
863
864 if (defined $opt_excerpt_len) {
865 $all_opts .= "-t $opt_excerpt_len ";
866 }
867
868
869 my $ivoa_filename_gsdlenv = $self->gsdlhome_independent($ivoa_filename);
870 my $omp3_filename_gsdlenv = $self->gsdlhome_independent($omp3_filename);
871
872
873 my $ffmpeg_cmd = "ffmpeg -i \"$ivoa_filename_gsdlenv\" $all_opts \"$omp3_filename_gsdlenv\"";
874
875 return ($ffmpeg_cmd,$omp3_filename,$omp3_file);
876}
877
878
879
880sub streamseekable_cmd
881{
882 my $self = shift (@_);
883 my ($oflash_filename) = @_;
884
885 my $output_dir = $self->{'cached_dir'};
886 my $ivideo_root = $self->{'cached_file_root'};
887
888 ## my $cue_filename = &util::filename_cat($output_dir,"on_cue.xml");
889
890 my $flvtool_cmd = "flvtool2 -vUP \"$oflash_filename\"";
891
892 return ($flvtool_cmd,$oflash_filename);
893}
894
895
896sub streamkeyframes_cmd
897{
898 my $self = shift (@_);
899 my ($oflash_filename,$doc_obj,$section) = @_;
900
901 my $assocfilepath
902 = $doc_obj->get_metadata_element($section,"assocfilepath");
903
904 my $output_dir = $self->{'cached_dir'};
905
906 my $cue_filename = &util::filename_cat($output_dir,"on_cue.xml");
907
908 my $flvtool_cmd = "flvtool2 -vAUtP \"$cue_filename\" \"$oflash_filename\"";
909
910 return ($flvtool_cmd,$oflash_filename);
911}
912
913
914sub streamkeyframes_cmd_old
915{
916 my $self = shift (@_);
917 my ($oflash_filename,$doc_obj,$section) = @_;
918
919 my $assocfilepath
920 = $doc_obj->get_metadata_element($section,"assocfilepath");
921
922 my $output_dir = $self->{'cached_dir'};
923
924 my $cue_filename = &util::filename_cat($output_dir,"on_cue.xml");
925
926 my $video_server = $ENV{'GEXT_VIDEO_SERVER'};
927 my $video_prefix = $ENV{'GEXT_VIDEO_PREFIX'};
928
929 my $collect = $ENV{'GSDLCOLLECTION'};
930
931 print STDERR "**** Warning: need to remove dependency of GEXT_VIDEO_SERVER and _PREFIX\n";
932
933 my $flvtool_cmd = "flvtool2 -vAUtP \"$cue_filename\" -thumbLocation:$video_server$video_prefix/collect/$collect/index/assoc/$assocfilepath \"$oflash_filename\"";
934
935
936 return ($flvtool_cmd,$oflash_filename);
937}
938
939
940sub streamcuepts_cmd
941{
942 my $self = shift (@_);
943 my ($oflash_filename) = @_;
944
945 my $output_dir = $self->{'cached_dir'};
946
947 my $cue_filename = &util::filename_cat($output_dir,"on_cue.xml");
948
949 ##my $video_server = $ENV{'GEXT_VIDEO_SERVER'};
950 ##my $video_prefix = $ENV{'GEXT_VIDEO_PREFIX'};
951
952 ## my $collect = $ENV{'GSDLCOLLECTION'};
953
954 ## my $thumbloc = "$video_server$video_prefix/collect/$collect";
955
956
957# my $flvtool_cmd = "flvtool2 -vUAtP \"$cue_filename\" -thumbLocation:$thumbloc \"$oflash_filename\"";
958
959# my $flvtool_cmd = "flvtool2 -vUAt \"$cue_filename\" \"$oflash_filename\"";
960
961
962
963# my $flvtool_cmd = "flvtool2 -vUAt \"$cue_filename\" \"$oflash_filename\"";
964
965
966## my $flvtool_cmd = "flvtool2 -vAt \"$cue_filename\" -UP \"$oflash_filename\" \"$output_dir/updated.flv\"";
967
968## my $flvtool_cmd = "flvtool2 -vAtU \"$cue_filename\" \"$oflash_filename\" \"$output_dir/updated.flv\"";
969
970 my $flvtool_cmd = "flvtool2 -vAtUP \"$cue_filename\" \"$oflash_filename\"";
971
972 return ($flvtool_cmd,$oflash_filename);
973}
974
975
976sub keyframe_thumbnail_cmd
977{
978 my $self = shift (@_);
979 my ($ivideo_filename,$thumbnailfile,$thumbnailwidth,$thumbnailheight,$video_duration) = @_;
980
981 my $output_dir = $self->{'cached_dir'};
982 my $ivideo_root = $self->{'cached_file_root'};
983
984 my $key_filename_prefix = &util::filename_cat($output_dir,$ivideo_root);
985
986
987 # Try for 4th keyframe, but fall back to 1st if doesn't exist
988 my $key_filename = "${key_filename_prefix}_0003.jpg";
989 $key_filename = "${key_filename_prefix}_0000.jpg" if (!-e $key_filename);
990
991 my $key_filename_gsdlenv = $self->gsdlhome_independent($key_filename);
992 my $thumbnailfile_gsdlenv = $self->gsdlhome_independent($thumbnailfile);
993
994 my $command;
995
996 if (-e $key_filename) {
997 $command = "convert -interlace plane -verbose -geometry $thumbnailwidth"
998 . "x$thumbnailheight \"$key_filename_gsdlenv\" \"$thumbnailfile_gsdlenv\"";
999 }
1000 else {
1001 # create_keyframe is either false, or else had a problem when running
1002 # => extract a from
1003 # my $frame_rate = 1.0 / 60.0;
1004
1005 my $ivideo_filename_gsdlenv = $self->gsdlhome_independent($ivideo_filename);
1006
1007 # If ever reused, then needs to change to newer hashmap method
1008 #my ($vtype, $width, $height, $duration, $vsize,
1009 # $vcodec ,$fps ,$atype ,$afreq ,$achan ,$arate) = identify($ivideo_filename_gsdlenv,$outhandle,0);
1010
1011 #print STDERR "***** grabbing keyframe as 50% of $video_duration\n";
1012 my $ss_opt = ($video_duration / 1000.0) * 0.35;
1013 #print STDERR "*** ssopt = $ss_opt\n";
1014
1015 $command = "ffmpeg -ss $ss_opt -i \"$ivideo_filename_gsdlenv\" -ss 4.5 -an -vframes 1 -f image2 -s ${thumbnailwidth}x${thumbnailheight} -deinterlace -y \"$thumbnailfile_gsdlenv\"";
1016 #$command = "ffmpeg -ss 4.5 -i \"$ivideo_filename_gsdlenv\" -ss 4.5 -an -vframes 1 -f image2 -s ${thumbnailwidth}x${thumbnailheight} -deinterlace -y \"$thumbnailfile_gsdlenv\"";
1017
1018## $command = "ffmpeg -i \"$ivideo_filename_gsdlenv\" -ss 10.5 -vframes 1 -f image2 -s ${thumbnailwidth}x${thumbnailheight} -deinterlace -y \"$thumbnailfile_gsdlenv\"";
1019
1020 $command = "ffmpeg -i \"$ivideo_filename_gsdlenv\" -ss 16.5 -vframes 1 -f image2 -s ${thumbnailwidth}x${thumbnailheight} -deinterlace -y \"$thumbnailfile_gsdlenv\"";
1021
1022
1023 # fmpeg -i input.dv -r 1 -f image2 -s 120x96 images%05d.png
1024 }
1025
1026 return ($command,$thumbnailfile);
1027}
1028
1029
1030
1031
1032sub keyframe_montage_cmd
1033{
1034 my $self = shift (@_);
1035 my ($ivideo_filename,$montagefile) = @_;
1036
1037 my $output_dir = $self->{'cached_dir'};
1038 my $ivideo_root = $self->{'cached_file_root'};
1039
1040 my $key_filename_prefix = &util::filename_cat($output_dir,$ivideo_root);
1041
1042 my $options = "-tile 10 -geometry 75x62+2+2";
1043
1044 my $command = "montage $options ${key_filename_prefix}_*.jpg \"$montagefile\"";
1045
1046 return ($command,$montagefile);
1047}
1048
1049
1050
1051sub parse_shot_xml
1052{
1053 my ($self) = shift(@_);
1054
1055 my $outhandle = $self->{'outhandle'};
1056 my $output_dir = $self->{'cached_dir'};
1057
1058 my $shots_filename = &util::filename_cat($output_dir,"shots.xml");
1059
1060 eval {
1061 $self->{'parser'}->parsefile($shots_filename);
1062 };
1063
1064 if ($@) {
1065 print $outhandle "VideoConverter: skipping $shots_filename as not conformant to Hive shot syntax\n" if ($self->{'verbosity'} > 1);
1066 print $outhandle "\n Perl Error:\n $@\n" if ($self->{'verbosity'}>2);
1067 return 0;
1068 }
1069
1070}
1071
1072
1073sub parse_shot_dir
1074{
1075 my ($self) = shift(@_);
1076
1077 my $outhandle = $self->{'outhandle'};
1078 my $output_dir = $self->{'cached_dir'};
1079
1080 my $ivideo_root = $self->{'cached_file_root'};
1081
1082 my $frames_output_dir = &util::filename_cat($output_dir,"frames");
1083
1084 my @jpeg_frames = ();
1085
1086 if (opendir(FDIRIN, $frames_output_dir)) {
1087 @jpeg_frames = grep { $_ =~ /_\d+\.jpg$/ } readdir(FDIRIN);
1088 closedir(FDIRIN);
1089 }
1090 else {
1091 print STDERR "VideoConverter:: Failed to open directory $frames_output_dir:\n$!\n";
1092 return 0;
1093 }
1094
1095 $self->{'keyframe_timeline'} = {};
1096
1097 my $vfps = $self->{'video-fps'};
1098
1099 my $keyframe_index = 1;
1100
1101 foreach my $f (@jpeg_frames) {
1102 my ($hh,$mm,$ss) = ($f =~ m/_(\d{2})_(\d{2})_(\d{2})_\d+\.jpg$/);
1103 my $frame_num = (3600*$hh + 60*$mm + $ss) * $vfps;
1104
1105 my $time_msec = (3600*$hh + 60*$mm + $ss) * 1000;
1106
1107 my $timeline_rec = { 'name'=> "Keyframe $keyframe_index",
1108 'keyframeindex' => $keyframe_index,
1109 'timestamp' => $time_msec,
1110 'thumb' => $f,
1111 'keyframenum' => $keyframe_index };
1112
1113 $self->{'keyframe_timeline'}->{$keyframe_index}=$timeline_rec;
1114
1115 $keyframe_index++;
1116 }
1117
1118 my $cue_filename = &util::filename_cat($output_dir,"on_cue.xml");
1119
1120 $self->open_on_cue($cue_filename);
1121
1122 $self->output_distributed_keyframes($self->{'keyframe_timeline'},$self->{'ffkeyframe_num_shots'});
1123
1124 $self->close_on_cue();
1125
1126 return 1;
1127}
1128
1129sub associate_keyframes_old
1130{
1131 my ($self) = shift(@_);
1132
1133 my ($doc_obj,$section) = @_;
1134
1135 my $output_dir = $self->{'cached_dir'};
1136
1137 my $count = 1;
1138 foreach my $kframe_file (@{$self->{'keyframe_fnames'}}) {
1139
1140 my $kframe_filename = &util::filename_cat($output_dir,$kframe_file);
1141 $doc_obj->associate_file($kframe_filename,"keyframe$count.jpg","image/jpeg",
1142 $section);
1143 $count++;
1144 }
1145
1146 $doc_obj->add_utf8_metadata($section,"NumKeyframes",scalar(@{$self->{'keyframe_fnames'}}));
1147
1148
1149 # *****
1150 # $doc_obj->add_metadata ($section, "thumblist", $self->{'flowplayer_thumblist'});
1151
1152}
1153
1154sub associate_keyframes
1155{
1156 my ($self) = shift(@_);
1157
1158 my ($doc_obj,$topsection,$timeline) = @_;
1159
1160 my $output_dir = $self->{'cached_dir'};
1161
1162 my $count = 1;
1163
1164 foreach my $t (sort { $timeline->{$a}->{'keyframeindex'} <=> $timeline->{$b}->{'keyframeindex'} } keys %$timeline)
1165 {
1166 my $kframe_file = $timeline->{$t}->{'thumb'};
1167 my $timestamp = $timeline->{$t}->{'timestamp'};
1168
1169 # create next sub-section to video "document"
1170 my $endchild = $doc_obj->insert_section($doc_obj->get_end_child($topsection));
1171 $doc_obj->add_utf8_metadata($endchild,"Title","Timestamp $timestamp");
1172 $doc_obj->add_utf8_metadata($endchild,"FrameNum",$t);
1173
1174 my $kframe_filename = &util::filename_cat($output_dir,$kframe_file);
1175 $doc_obj->associate_file($kframe_filename,"keyframe$count.jpg",
1176 "image/jpeg",
1177 $endchild);
1178
1179 $doc_obj->add_utf8_metadata($endchild,"assockeyframe","keyframe$count.jpg");
1180
1181 $doc_obj->add_utf8_metadata($topsection,"KeyframeTimestamp",$timestamp);
1182 $doc_obj->add_utf8_metadata($topsection,"KeyframeFrameNum",$t);
1183
1184
1185
1186 $count++;
1187 }
1188
1189 #### $doc_obj->add_utf8_metadata($topsection,"NumKeyframes",scalar(@{$self->{'keyframe_fnames'}}));
1190
1191 $doc_obj->add_utf8_metadata($topsection,"NumKeyframes",scalar(keys %$timeline));
1192
1193
1194 # *****
1195 # $doc_obj->add_metadata ($topsection, "thumblist", $self->{'flowplayer_thumblist'});
1196}
1197
1198
1199
1200sub enable_audio_streaming
1201{
1202 my $self = shift (@_);
1203 my ($doc_obj,$originalfilename,$filename,$convertto_regenerated) = @_;
1204
1205 my $section = $doc_obj->get_top_section();
1206
1207 my $output_dir = $self->{'cached_dir'};
1208 my $ivideo_root = $self->{'cached_file_root'};
1209
1210 # Generate FLV audio-only format for streaming purposes
1211
1212 my $optionally_run_general_cmd = "run_uncached_general_cmd";
1213 if ($self->{'enable_cache'}) {
1214 $optionally_run_general_cmd
1215 = ($convertto_regenerated) ? "regenerate_general_cmd" : "run_cached_general_cmd";
1216 }
1217
1218
1219 my $ifilename = $originalfilename || $filename;
1220
1221 my ($stream_cmd,$ofla_filename,$ofla_file)
1222 = $self->stream_flv_audio_cmd($ifilename);
1223
1224
1225 my $streamable_options = { @{$self->{'ffmpeg_monitor'}},
1226 'message_prefix' => "Stream",
1227 'message' => "Generating streamable audio: $ofla_file" };
1228
1229
1230 my ($streamable_regenerated,$streamable_result,$streamable_had_error)
1231 = $self->$optionally_run_general_cmd($stream_cmd,
1232 $ifilename,$ofla_filename,
1233 $streamable_options);
1234
1235
1236 if (!$streamable_had_error) {
1237 my ($streamseekable_cmd,$ostreamseekable_filename) = $self->streamseekable_cmd($ofla_filename);
1238
1239 my $streamseekable_options = { @{$self->{'flvtool2_monitor'}},
1240 'message_prefix' => "Stream Seekable",
1241 'message' => "Reprocessing audio stream to be seekable by timeline: $ofla_file" };
1242
1243 if ($streamable_regenerated) {
1244 $self->run_general_cmd($streamseekable_cmd,$streamseekable_options);
1245 }
1246
1247 my $streamable_url = $ofla_file;
1248 my $streamable_url_safe = $self->url_safe($streamable_url);
1249
1250 $doc_obj->add_utf8_metadata ($section, "streamableaudio", $streamable_url_safe);
1251 $doc_obj->associate_file($ofla_filename,$ofla_file,"audio/flash",
1252 $section);
1253 }
1254
1255 $doc_obj->add_metadata ($section, "audioflashwidth", 400);
1256 $doc_obj->add_metadata ($section, "audioflashheight", 22 + 100);
1257
1258 $self->{'ofla_file'} = $ofla_file;
1259 $self->{'ofla_filename'} = $ofla_filename;
1260
1261 return $streamable_regenerated;
1262}
1263
1264
1265
1266
1267
1268sub enable_video_streaming
1269{
1270 my $self = shift (@_);
1271 my ($doc_obj,$originalfilename,$filename,$convertto_regenerated,
1272 $video_width,$video_height) = @_;
1273
1274 my $section = $doc_obj->get_top_section();
1275
1276 my $output_dir = $self->{'cached_dir'};
1277 my $ivideo_root = $self->{'cached_file_root'};
1278
1279 # Generate the Flash FLV format for streaming purposes
1280
1281 my $optionally_run_general_cmd = "run_uncached_general_cmd";
1282 if ($self->{'enable_cache'}) {
1283 $optionally_run_general_cmd
1284 = ($convertto_regenerated) ? "regenerate_general_cmd" : "run_cached_general_cmd";
1285 }
1286
1287
1288 my $streaming_bitrate = $self->{'streamingbitrate'};
1289 my $streaming_size = $self->{'streamingsize'};
1290
1291 my $streaming_quality = "high";
1292
1293 my $ifilename = $originalfilename || $filename;
1294
1295 my ($stream_cmd,$oflash_filename,$oflash_file)
1296 = $self->stream_flv_video_cmd($ifilename,
1297 $video_width,$video_height,
1298 $streaming_quality,
1299 $streaming_bitrate, $streaming_size);
1300
1301
1302 my $streamable_options = { @{$self->{'ffmpeg_monitor'}},
1303 'message_prefix' => "Stream",
1304 'message' => "Generating streamable video: $oflash_file" };
1305
1306
1307 my ($streamable_regenerated,$streamable_result,$streamable_had_error)
1308 = $self->$optionally_run_general_cmd($stream_cmd,
1309 $ifilename,$oflash_filename,
1310 $streamable_options);
1311
1312 if (!$streamable_had_error) {
1313 my ($streamseekable_cmd,$ostreamseekable_filename) = $self->streamseekable_cmd($oflash_filename);
1314
1315 my $streamseekable_options = { @{$self->{'flvtool2_monitor'}},
1316 'message_prefix' => "Stream Seekable",
1317 'message' => "Reprocessing video stream to be seekable by timeline: $oflash_file" };
1318
1319 if ($streamable_regenerated) {
1320 $self->run_general_cmd($streamseekable_cmd,$streamseekable_options);
1321 }
1322
1323 my $streamable_url = $oflash_file;
1324 my $streamable_url_safe = $self->url_safe($streamable_url);
1325
1326 $doc_obj->add_utf8_metadata ($section, "streamablevideo", $streamable_url_safe);
1327 $doc_obj->associate_file($oflash_filename,$oflash_file,"video/flash",
1328 $section);
1329 }
1330
1331
1332 #
1333 # FlowPlayer.swf height+22 pixels
1334 # FlowPlayerBlack.swf height+16 pixels
1335 # FlowPlayerThermo.swf height+16 pixels
1336 # FlowPlayerWhite.swf height+26 pixels
1337
1338 my $flashwidth = $video_width;
1339 my $flashheight = $video_height + 22;
1340
1341 if ($self->{'create_keyframes'} eq "true") {
1342 $flashheight += 100;
1343 }
1344
1345 $doc_obj->add_metadata ($section, "flashwidth", $flashwidth);
1346 $doc_obj->add_metadata ($section, "flashheight", $flashheight);
1347
1348 $self->{'oflash_file'} = $oflash_file;
1349 $self->{'oflash_filename'} = $oflash_filename;
1350
1351 return $streamable_regenerated;
1352}
1353
1354sub enable_direct_streaming
1355{
1356 my $self = shift (@_);
1357 my ($doc_obj,$originalfilename,$filename,$file,
1358 $video_width,$video_height) = @_;
1359
1360 my $section = $doc_obj->get_top_section();
1361
1362 my $ifilename = $originalfilename || $filename;
1363 #my $ifile = $self->gsdlhome_independent($ifilename);
1364 my $ifile = $file;
1365
1366 print STDERR "Using the original video file for direct streaming: $ifile\n";
1367
1368 my $streamable_url = $ifile;
1369 my $streamable_url_safe = $self->url_safe($streamable_url);
1370
1371 my $outhandle = $self->{'outhandle'};
1372
1373 my $identify_vals = identifyMI($ifilename,$outhandle,0);
1374
1375 #Add the most common metadata extracted by MediaInfo from the streamable video file
1376 $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingVideoFormat", $identify_vals->{'vtype'});
1377 $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingVideoFileSize", $identify_vals->{'filesizeDisplay'});
1378 $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingVideoFileSizeBytes", $identify_vals->{'filesize'});
1379 $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingWidth", $identify_vals->{'width'});
1380 $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingHeight", $identify_vals->{'height'});
1381 $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingDuration", $identify_vals->{'durationDisplay'});
1382 $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingDurationMs", $identify_vals->{'duration'});
1383 $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingFPS", $identify_vals->{'fps'});
1384 $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingVideoCodec", $identify_vals->{'vcodec'});
1385 $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingVideoBitrate", $identify_vals->{'vrate'});
1386 $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingAudioCodec", $identify_vals->{'acodec'});
1387 $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingAudioBitrate", $identify_vals->{'arate'});
1388 $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingAudioTracks", $identify_vals->{'atracks'});
1389 $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingOverallBitrate", $identify_vals->{'orateDisplay'});
1390 $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingSubtitleType", $identify_vals->{'stype'});
1391 $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingSubtitleLanguage", $identify_vals->{'slang'});
1392
1393 my $get_max_metadata = $self->{'getMaximumMetadata'};
1394 if ($get_max_metadata eq "true"){
1395 my $metadata_table = $identify_vals->{'metadata_table'};
1396 foreach my $metaname(keys %$metadata_table)
1397 {
1398 my $metaval = $identify_vals->{'metadata_table'}->{$metaname};
1399 $doc_obj->add_utf8_metadata ($section, $metaname, $metaval);
1400 }
1401 }
1402
1403 #add the metadata for the resulting file that should be open inside the replayme video player
1404 $doc_obj->add_utf8_metadata ($section, "streamablevideo", $streamable_url_safe);
1405 $doc_obj->associate_file($ifilename,$ifile,"application/octet-stream",$section);
1406
1407 my $flashwidth = $video_width;
1408 my $flashheight = $video_height + 22;
1409
1410 if ($self->{'create_keyframes'} eq "true") {
1411 $flashheight += 100;
1412 }
1413
1414 $doc_obj->add_metadata ($section, "flashwidth", $flashwidth);
1415 $doc_obj->add_metadata ($section, "flashheight", $flashheight);
1416
1417 $self->{'oflash_file'} = $ifile;
1418 $self->{'oflash_filename'} = $ifilename;
1419
1420}
1421
1422
1423sub enable_h264_streaming
1424{
1425 my $self = shift (@_);
1426 my ($doc_obj,$originalfilename,$filename,$convertto_regenerated,
1427 $video_width,$video_height) = @_;
1428
1429
1430 my $section = $doc_obj->get_top_section();
1431
1432 my $output_dir = $self->{'cached_dir'};
1433 my $ivideo_root = $self->{'cached_file_root'};
1434
1435 # Generate the H264 video file format for streaming purposes using the cache feature if used
1436
1437 my $optionally_run_general_cmd = "run_uncached_general_cmd";
1438 if ($self->{'enable_cache'}) {
1439 $optionally_run_general_cmd
1440 = ($convertto_regenerated) ? "regenerate_general_cmd" : "run_cached_general_cmd";
1441 }
1442
1443 my $streaming_HQ_size = $self->{'streamingHQsize'};
1444 my $streaming_HQ_VideoBitrate = $self->{'streamingHQVideoBitrate'};
1445 my $streaming_HQ_AudioBitrate = $self->{'streamingHQAudioBitrate'};
1446
1447 my $ifilename = $originalfilename || $filename;
1448 my ($stream_cmd,$oflash_filename,$oflash_file)
1449 = $self->stream_mp4_video_cmd($ifilename,
1450 $streaming_HQ_size,
1451 $streaming_HQ_VideoBitrate,
1452 $streaming_HQ_AudioBitrate);
1453
1454
1455 my $streamable_options = { @{$self->{'handbrake_monitor'}},
1456 'message_prefix' => "MP4 Stream",
1457 'message' => "Generating streamable MP4 video: $oflash_file" };
1458
1459
1460 my ($streamable_regenerated,$streamable_result,$streamable_had_error)
1461 = $self->$optionally_run_general_cmd($stream_cmd,
1462 $ifilename,$oflash_filename,
1463 $streamable_options);
1464
1465 if (!$streamable_had_error) {
1466
1467
1468 my $streamable_url = $oflash_file;
1469 my $streamable_url_safe = $self->url_safe($streamable_url);
1470
1471 my $outhandle = $self->{'outhandle'};
1472
1473 my $identify_vals = identifyMI($oflash_filename,$outhandle,0);
1474
1475 #Add the most common metadata extracted by MediaInfo from the streamable video file
1476 $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingVideoFormat", $identify_vals->{'vtype'});
1477 $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingVideoFileSize", $identify_vals->{'filesizeDisplay'});
1478 $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingWidth", $identify_vals->{'width'});
1479 $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingHeight", $identify_vals->{'height'});
1480 $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingDuration", $identify_vals->{'durationDisplay'});
1481 $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingFPS", $identify_vals->{'fps'});
1482 $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingVideoCodec", $identify_vals->{'vcodec'});
1483 $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingVideoBitrate", $identify_vals->{'vrate'});
1484 $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingAudioCodec", $identify_vals->{'acodec'});
1485 $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingAudioBitrate", $identify_vals->{'arate'});
1486 $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingOverallBitrate", $identify_vals->{'orateDisplay'});
1487 $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingSubtitleType", $identify_vals->{'stype'});
1488 $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingSubtitleLangue", $identify_vals->{'slang'});
1489
1490 my $get_max_metadata = $self->{'getMaximumMetadata'};
1491 if ($get_max_metadata eq "true"){
1492 my $metadata_table = $identify_vals->{'metadata_table'};
1493 foreach my $metaname(keys %$metadata_table)
1494 {
1495 my $metaval = $identify_vals->{'metadata_table'}->{$metaname};
1496 $doc_obj->add_utf8_metadata ($section, $metaname, $metaval);
1497 }
1498 }
1499
1500 #add the metadata for the resulting file that should be open inside the flash video player
1501 $doc_obj->add_utf8_metadata ($section, "streamablevideo", $streamable_url_safe);
1502 $doc_obj->associate_file($oflash_filename,$oflash_file,"video/mp4",
1503 $section);
1504 }
1505
1506 my $flashwidth = $video_width;
1507 my $flashheight = $video_height + 22;
1508
1509 if ($self->{'create_keyframes'} eq "true") {
1510 $flashheight += 100;
1511 }
1512
1513 $doc_obj->add_metadata ($section, "flashwidth", $flashwidth);
1514 $doc_obj->add_metadata ($section, "flashheight", $flashheight);
1515
1516 $self->{'oflash_file'} = $oflash_file;
1517 $self->{'oflash_filename'} = $oflash_filename;
1518
1519 return $streamable_regenerated;
1520
1521
1522}
1523
1524sub enable_full_streaming
1525{
1526 my $self = shift (@_);
1527 my ($doc_obj,$originalfilename,$filename,$convertto_regenerated,
1528 $video_width,$video_height) = @_;
1529
1530 my $video_streamable_regenerated
1531 = $self->enable_video_streaming($doc_obj,$originalfilename,$filename,
1532 $convertto_regenerated,
1533 $video_width,$video_height);
1534
1535 my $audio_streamable_regenerated
1536 = $self->enable_audio_streaming($doc_obj,$originalfilename,$filename,
1537 $convertto_regenerated);
1538
1539 return ($video_streamable_regenerated || $audio_streamable_regenerated);
1540}
1541
1542
1543
1544sub flvtool2_monitor_line
1545{
1546 my ($line) = @_;
1547
1548 my $had_error = 0;
1549 my $generate_dot = 1;
1550
1551 if ($line =~ m/\s+\- /) {
1552 # ignore tabulated output printed at end of command
1553 $generate_dot = 0;
1554 }
1555
1556 if ($line =~ m/^Error:/i) {
1557 $had_error = 1;
1558 }
1559
1560 return ($had_error,$generate_dot);
1561}
1562
1563
1564
1565
1566
15671;
Note: See TracBrowser for help on using the repository browser.