root/gs2-extensions/video/trunk/perllib/plugins/VideoConverter.pm @ 22432

Revision 22432, 36.8 KB (checked in by davidb, 9 years ago)

Adjustment of calling BaseMediaConverter? to pass in additional input file parameter

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' => "576",
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      { 'name' => "extract_keyframes",
116    'desc' => "{VideoPlugin.extractkeyframes}",
117    'type' => "flag",
118    'deft' => "0",
119    'reqd' => "no" },
120      { 'name' => "keep_keyframes",
121    'desc' => "{VideoPlugin.keep_keyframes}",
122    'type' => "string",
123    'deft' => "all",
124    'reqd' => "no" },
125      { 'name' => "streamingsize",
126    'desc' => "{VideoPlugin.streamingsize}",
127    'type' => "int",
128    'deft' => "352",
129    'reqd' => "no" },
130      { 'name' => "streamingbitrate",
131    'desc' => "{VideoPlugin.streamingbitrate}",
132    'type' => "string",
133    'deft' => "200k",
134    'reqd' => "no" },
135    { 'name' => "streamingHQsize",
136    'desc' => "{VideoPlugin.streamingsize}",
137    'type' => "int",
138    'deft' => "576",
139    'reqd' => "no" },
140    { 'name' => "streamingHQVideoBitrate",
141    'desc' => "{VideoPlugin.streamingbitrate}",
142    'type' => "int",
143    'deft' => "432",
144    'reqd' => "no" },
145    { 'name' => "streamingHQAudioBitrate",
146    'desc' => "{VideoPlugin.streamingbitrate}",
147    'type' => "int",
148    'deft' => "80",
149    'reqd' => "no" },
150
151      { 'name' => "minimumsize",
152    'desc' => "{ImageConverter.minimumsize}",
153    'type' => "int",
154    'deft' => "100",
155    'range' => "1,",
156    'reqd' => "no" },
157
158         ];
159
160my $options = { 'name' => "VideoConverter",
161        'desc' => "{VideoConverter.desc}",
162        'abstract' => "yes",
163        'inherits' => "yes",
164        'args' => $arguments };
165
166sub new {
167    my ($class) = shift (@_);
168    my ($pluginlist,$inputargs,$hashArgOptLists) = @_;
169    push(@$pluginlist, $class);
170
171    push(@{$hashArgOptLists->{"ArgList"}},@{$arguments});
172    push(@{$hashArgOptLists->{"OptList"}},$options);
173
174    my $self = new MultimediaConverter($pluginlist, $inputargs, $hashArgOptLists, 1);
175
176
177    return bless $self, $class;
178
179}
180
181
182
183
184# Discover the characteristics of a video file.
185
186#  Equivalent step to that in ImagePlugin that uses ImageMagicks's
187#  'indentify' utility
188
189#  Here we use 'MediaInfo' for video but for consistency keep the Perl
190#  method name the same as before
191
192sub mediaInfo_Inform_Cmd {
193    my ($video, $parameter, $outhandle, $verbosity) = @_;
194   
195    # Use MediaInfo CLI to get the file spec according to the requested parameter
196    my $command = "mediainfo --Inform=\"$parameter\" \"$video\"";
197
198    print $outhandle "  $command\n" if ($verbosity > 2);
199    my $result = '';
200    $result = `$command 2>&1`;
201    chomp $result;
202    print $outhandle "  $result\n" if ($verbosity > 4);
203   
204    # Return the asked spec
205    return ($result);
206}
207
208# Here we use mediaInfo to get all the possible metadata in XML form that could then be parsed
209#the result will vary depending on the file type and how it was encoded
210sub mediaInfo_XML_Cmd {
211    my ($video, $outhandle, $verbosity) = @_;
212   
213    # Use MediaInfo CLI to get the file spec according to the requested parameter
214    my $command = "mediainfo --Output=XML \"$video\"";
215   
216    print $outhandle "  $command\n" if ($verbosity > 2);
217    my $result = '';
218    $result = `$command 2>&1`;
219    print $outhandle "  $result\n" if ($verbosity > 4);
220   
221    # Return the asked spec
222    return ($result);
223}
224
225#Here we parse the video specs contained in the XML text in order to create a metadata entry for each field
226sub mediaInfo_parse_XML {
227    my ($xmlTxt, $outhandle, $verbosity) = @_;
228   
229    my @parts = split(/<track\s+type=\"(.*?)\">/is,$xmlTxt);
230   
231    shift @parts; # Skip preamble
232    my $metadata_table = {};
233   
234    while (@parts) {
235    my $type = shift(@parts);
236    my $vals = shift(@parts);
237    my @fieldlist=();
238    while ($vals =~ s/<(.*?)>(.*?)<\/\1>//) {
239        my $metaname = $1;
240        my $metaval  = $2;
241        my $fullmetaname = "mi.$type\^$metaname";
242        $metadata_table->{$fullmetaname}= $metaval;
243        push(@fieldlist,$fullmetaname);
244    }
245   
246    $metadata_table->{"mi.${type}Fields"}= join(",",@fieldlist);
247   
248    }
249   
250    return $metadata_table;
251
252}
253
254sub identifyMI {
255    my ($video, $outhandle, $verbosity) = @_;
256
257    # Use the ffmpeg command to get the file specs
258    my $command = "ffmpeg -i \"$video\"";
259
260    print $outhandle "  $command\n" if ($verbosity > 2);
261    my $result = '';
262    $result = `$command 2>&1`;
263    print $outhandle "  $result\n" if ($verbosity > 4);
264
265    # Read the type, width, and height etc.
266    # This could be done in a more efficient way by only calling the application mediainfo once and
267    # giving all the parameters then parsing the results, however on most operating system
268    # once the application is called once it is then cached for subsequent calls.
269    my $vtype =  mediaInfo_Inform_Cmd($video,"General;%Format%",$outhandle,0);
270    my $duration = mediaInfo_Inform_Cmd($video,"General;%Duration%",$outhandle,0);
271    my $durationDisplay = mediaInfo_Inform_Cmd($video,"General;%Duration/String1%",$outhandle,0);
272    my $filesize = mediaInfo_Inform_Cmd($video,"General;%FileSize/String%",$outhandle,0);
273    my $vcodec = mediaInfo_Inform_Cmd($video,"Video;%Format%",$outhandle,0);
274    my $vrate = mediaInfo_Inform_Cmd($video,"Video;%BitRate%",$outhandle,0);
275    my $width =  mediaInfo_Inform_Cmd($video,"Video;%Width%",$outhandle,0);
276    my $height = mediaInfo_Inform_Cmd($video,"Video;%Height%",$outhandle,0);
277    my $fps    = mediaInfo_Inform_Cmd($video,"Video;%FrameRate%",$outhandle,0);
278    my $atype =  mediaInfo_Inform_Cmd($video,"Audio;%Format%",$outhandle,0);
279    my $afreq =  mediaInfo_Inform_Cmd($video,"Audio;%SamplingRate/String%",$outhandle,0);
280    my $achan =  mediaInfo_Inform_Cmd($video,"Audio;%Channel(s)%",$outhandle,0);
281    my $arate =  mediaInfo_Inform_Cmd($video,"Audio;%BitRate%",$outhandle,0);
282
283    my $xmlTxt =  mediaInfo_XML_Cmd ($video,$outhandle,0);
284   
285    my $metadata_table = mediaInfo_parse_XML($xmlTxt,$outhandle,0);
286   
287    # Return the specs
288    return ($vtype,$width,$height,$duration,$durationDisplay,$filesize,
289        $vcodec,$vrate,$fps,$atype,$afreq,$achan,$arate,$metadata_table);
290}
291
292
293sub identify {
294    my ($video, $outhandle, $verbosity) = @_;
295
296    # Use the ffmpeg command to get the file specs
297    my $command = "ffmpeg -i \"$video\"";
298
299    print $outhandle "  $command\n" if ($verbosity > 2);
300    my $result = '';
301    $result = `$command 2>&1`;
302    print $outhandle "  $result\n" if ($verbosity > 4);
303
304    # Read the type, width, and height etc.
305    my $vtype =  'unknown';
306    my $vcodec = 'unknown';
307    my $width =  'unknown';
308    my $height = 'unknown';
309    my $fps    = 'unknown';
310
311    my $atype =  'unknown';
312    my $afreq =  'unknown';
313    my $achan =  'unknown';
314    my $arate =  'unknown';
315
316    my $video_safe = quotemeta $video;
317
318    # strip off everything up to filename
319    $result =~ s/^.*\'$video_safe\'://s;
320
321##    if ($result =~ m/Video: (.*?) fps/m) {
322    if ($result =~ m/^\s+Stream.*?Video: (.*?)$/m) {
323    my $video_info = $1;
324    $video_info =~ s/\s*\[.*?\]//g;
325
326    my @video_fields = split(/,\s*/,$video_info);
327   
328    $vtype = shift @video_fields;
329
330    if ($video_fields[0] !~ m/(\d+)x(\d+)/) {
331        $vcodec = shift @video_fields;
332    }
333    my $video_dim = shift @video_fields;
334
335    ($width,$height) = ($video_dim =~ m/(\d+)x(\d+)/);
336   
337    if ($video_fields[0] =~ m/(\d+)\s+tbr$/) {
338        $fps = $1;
339    }
340
341#   if ($video_info =~ m/([^,]+),(?: ([^,]+),)? (\d+)x(\d+),.*?(\d+\.\d+)/)
342#   {
343#       $vtype = $1;
344#       $vcodec = $2 if defined $2;
345#       $width = $3;
346#       $height = $4;
347#       $fps = $5;
348#   }
349    }
350
351    if ($result =~ m/Audio: (\w+), (\d+) Hz, (\w+)(?:, (\d+.*))?/m) {
352    $atype = $1;
353    $afreq = $2;
354    $achan = $3;
355    $arate = $4 if (defined $4);
356    }
357
358    # Read the duration
359    my $duration = "unknown";
360    if ($result =~ m/Duration: (\d+:\d+:\d+\.\d+)/m) {
361    $duration = $1;
362    }
363    print $outhandle "  file: $video:\t $vtype, $width, $height, $duration\n"
364    if ($verbosity > 2);
365
366    if ($verbosity >3) {
367    print $outhandle "\t video codec=$vcodec, fps = $fps\n";
368    print $outhandle "\t audio codec=$atype, freq = $afreq Hz, $achan, $arate\n";
369    }
370
371    # Return the specs
372    return ($vtype, $width, $height, $duration, -s $video,
373        $vcodec,$fps,$atype,$afreq,$achan,$arate);
374}
375
376
377sub vob_durations
378{
379    my ($media_base_dir,$title_num,$outhandle) = @_;
380
381    my $filter_re = sprintf("^VTS_%02d_[1-9]\\.VOB\$",$title_num);
382
383    my $duration_info = {};
384
385    if (opendir(VTSIN,$media_base_dir)) {
386    my @vts_title_vobs = grep { $_ =~ m/$filter_re/ } readdir(VTSIN);
387    closedir(VTSIN);
388
389    foreach my $v (@vts_title_vobs) {
390        my $full_v = &util::filename_cat($media_base_dir,$v);
391
392        my ($vtype, $width, $height, $duration, $durationDisplay, $vsize,
393        $vcodec,$vrate,$fps,$atype,$afreq,$achan,$arate) = identify($full_v,$outhandle,0);
394
395        my ($vob_num) = ($v =~ m/^VTS_\d\d_(\d)\.VOB$/);
396
397        $duration_info->{$vob_num} = $duration;
398        print STDERR "**** $title_num: $title_num, storing {$vob_num} => $duration\n";
399
400    }
401
402    }
403    else {
404    print $outhandle "Warning: unable to read files in directory $media_base_dir\n";
405    }
406   
407    return $duration_info;
408
409}
410
411
412
413sub init_cache_for_file {
414    my $self = shift(@_);
415
416    my ($video_filename) = @_;
417
418    $self->SUPER::init_cache_for_file($video_filename);
419
420                   
421    my @flvtool2_monitor = ( 'monitor_init'   ,"convertutil::monitor_init_unbuffered",
422                 'monitor_line'   , "VideoConverter::flvtool2_monitor_line",
423                 'monitor_deinit' , "convertutil::monitor_deinit_unbuffered" );
424                   
425    $self->{'flvtool2_monitor'} = \@flvtool2_monitor;
426
427
428}
429
430
431
432sub optional_frame_scale
433{
434    my $self = shift (@_);
435    my ($orig_size,$video_width,$video_height) = @_;
436
437    my $s_opt = "";
438
439    if ($video_width > $video_height) {
440    if ($video_width > $orig_size) {
441        my $scale_factor = $orig_size/$video_width;
442        my $scaled_width = int($video_width * $scale_factor);
443        my $scaled_height = int($video_height * $scale_factor);
444
445        # round to be ensure multiple of 2 (needed by some codecs)
446        $scaled_width  = int($scaled_width/2)*2;
447        $scaled_height = int($scaled_height/2)*2;
448
449        $s_opt = "-s ${scaled_width}x${scaled_height}";
450    }
451    # else, video is smaller than requested size, don't scale up
452    }
453    else {
454    if ($video_height > $orig_size) {
455        my $scale_factor = $orig_size/$video_height;
456        my $scaled_width = int($video_width * $scale_factor);
457        my $scaled_height = int($video_height * $scale_factor);
458
459        # round to be ensure multiple of 2 (needed by some codecs)
460        $scaled_width  = int($scaled_width/2)*2;
461        $scaled_height = int($scaled_height/2)*2;
462
463        $s_opt = "-s ${scaled_width}x${scaled_height}";
464    }
465    # else, video is smaller than requested size, don't scale up
466   
467    }
468   
469    return $s_opt;
470}
471
472
473sub keyframe_cmd
474{
475    my $self = shift (@_);
476    my ($ivideo_filename) = @_;
477
478    my $video_ext_dir = &util::filename_cat($ENV{'GSDLHOME'},"ext","video");
479
480    my $output_dir = $self->{'cached_dir'};
481    my $ivideo_root = $self->{'cached_file_root'};
482
483    my $oshot_filename = &util::filename_cat($output_dir,"shots.xml");
484
485    my $exp_duration = $self->{'exp_duration'};
486    my $t_opt = (defined $exp_duration) ? "-t $exp_duration" : "";
487
488    my $main_opts = "-y $t_opt";
489
490    my $hive = &util::filename_cat($video_ext_dir,"lib","vhook","hive.so");
491
492    my $oflash_filename = &util::filename_cat($output_dir,"$ivideo_root\_keyframe.flv");
493
494    my $vhook_opts = "$hive  -o $oshot_filename -k $output_dir $ivideo_filename";
495
496    my $ivideo_filename_gsdlenv = $self->gsdlhome_independent($ivideo_filename);
497    my $oflash_filename_gsdlenv = $self->gsdlhome_independent($oflash_filename);
498
499    my $ffmpeg_cmd = "ffkeyframe  $main_opts -vhook \"$vhook_opts\"   -i \"$ivideo_filename_gsdlenv\" -an -y \"$oflash_filename_gsdlenv\"";
500
501
502    return ($ffmpeg_cmd,$oflash_filename);
503}
504
505sub get_ovideo_file
506{
507    my $self = shift (@_);
508    my ($stream_type) = @_;
509   
510    my $ivideo_root = $self->{'cached_file_root'};
511    my $ofile;
512   
513    if ($stream_type eq "flv")
514    {
515        $ofile = "${ivideo_root}_vstream.flv";
516    }
517    else
518    {
519        $ofile = "${ivideo_root}_vHQstream.mp4";
520    }
521   
522    return $ofile;
523}
524
525sub get_ovideo_filename
526{
527    my $self = shift (@_);
528    my ($stream_type) = @_;
529   
530    my $output_dir = $self->{'cached_dir'};
531
532    my $ofile = $self->get_ovideo_file($stream_type);
533       
534    my $ofilename = &util::filename_cat($output_dir,$ofile);
535   
536    return $ofilename;
537}
538
539sub stream_mp4_video_cmd
540{
541    my $self = shift (@_);
542    my ($ivideo_filename,$streaming_HQ_size, $streaming_HQ_VideoBitrate,
543     $streaming_HQ_AudioBitrate) = @_;
544
545
546    my $output_dir = $self->{'cached_dir'};
547    my $ivideo_root = $self->{'cached_file_root'};
548
549    my $omp4_file = "${ivideo_root}_vHQstream.mp4";
550    my $omp4_filename = &util::filename_cat($output_dir,$omp4_file);
551
552
553    #my $exp_duration = $self->{'exp_duration'};
554    #my $t_opt = (defined $exp_duration) ? "-t $exp_duration" : "";
555
556
557   
558    my $size;   
559    # Looks for empty string as well as fullsize string
560    if(!$streaming_HQ_size || $streaming_HQ_size eq "fullsize") {
561        $size = "--strict-anamorphic";
562    } else {
563        $size = "-w $streaming_HQ_size --loose-anamorphic";
564    }
565   
566
567    my $ivideo_filename_gsdlenv = $self->gsdlhome_independent($ivideo_filename);
568    my $omp4_filename_gsdlenv = $self->gsdlhome_independent($omp4_filename);
569   
570    #my $pre_opts = "--decomb -t 1 -c 1";
571    #my $post_opts = "-f mp4 $size -O -e x264 -b $streaming_HQ_VideoBitrate -a 1 -E faac -6 dpl2 -B $streaming_HQ_AudioBitrate -D 0.0 -x ref=2:bframes=2:me-umh -v 1";
572   
573    my $pre_opts = "-t 1 -c 1";
574    my $post_opts = "-f mp4 -O $size --decomb -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";
575
576    my $handbrake_cmd = "HandbrakeCLI.exe -i \"$ivideo_filename_gsdlenv\" $pre_opts -o \"$omp4_filename_gsdlenv\" $post_opts";
577
578
579    return ($handbrake_cmd,$omp4_filename,$omp4_file);
580}
581
582
583
584sub stream_flv_video_cmd
585{
586    my $self = shift (@_);
587    my ($ivideo_filename,$video_width,$video_height,
588    $streaming_quality,
589    $streaming_bitrate,$streaming_size,
590    $opt_streaming_achan, $opt_streaming_arate) = @_;
591
592    my $streaming_achan
593    = (defined $opt_streaming_achan) ? $opt_streaming_achan : 2;
594
595    my $streaming_arate
596    = (defined $opt_streaming_arate) ? $opt_streaming_arate : 22050;
597
598    my $output_dir = $self->{'cached_dir'};
599    my $ivideo_root = $self->{'cached_file_root'};
600
601    my $oflash_file = "${ivideo_root}_vstream.flv";
602    my $oflash_filename = &util::filename_cat($output_dir,$oflash_file);
603
604    my $s_opt = "";
605    my $media_type = $self->{'media_type'};
606    if ($media_type ne "audio") {
607    $s_opt = $self->optional_frame_scale($streaming_size,$video_width,$video_height);
608    }
609
610
611    my $exp_duration = $self->{'exp_duration'};
612    my $t_opt = (defined $exp_duration) ? "-t $exp_duration" : "";
613
614    my $main_opts = "-y $t_opt";
615
616    my $bitrate_opt = "-b $streaming_bitrate";
617    ### my $stream_opts = "-r 25 $s_opt";
618    my $stream_opts .= " $s_opt -ac $streaming_achan -ar $streaming_arate";
619
620    # -flags +ilme+ildct' and maybe '-flags +alt' for interlaced material, and try '-top 0/1'
621
622    my $all_opts = "$main_opts $stream_opts";
623
624    my $ffmpeg_cmd;
625
626    my $ivideo_filename_gsdlenv = $self->gsdlhome_independent($ivideo_filename);
627    my $oflash_filename_gsdlenv = $self->gsdlhome_independent($oflash_filename);
628
629    if ($streaming_quality eq "high") {
630
631    my $pass_log_file = &util::filename_cat($output_dir,"$ivideo_root-logpass.txt");
632    if (-e $pass_log_file) {
633        &util::rm($pass_log_file);
634    }
635
636    my $pass_log_file_gsdlenv = $self->gsdlhome_independent($pass_log_file);
637
638    $all_opts .= " -passlogfile \"$pass_log_file_gsdlenv\"";
639
640    my $ffmpeg_cmd_pass1 = "ffmpeg -pass 1 -i \"$ivideo_filename_gsdlenv\" $all_opts -y \"$oflash_filename_gsdlenv\"";
641
642    my $ffmpeg_cmd_pass2 = "ffmpeg -pass 2 -i \"$ivideo_filename_gsdlenv\" $all_opts $bitrate_opt -y \"$oflash_filename_gsdlenv\"";
643    $ffmpeg_cmd = "( $ffmpeg_cmd_pass1 && $ffmpeg_cmd_pass2 )";
644    }
645    else {
646    # single pass
647
648    $ffmpeg_cmd = "ffmpeg -i \"$ivideo_filename_gsdlenv\" $all_opts -y \"$oflash_filename_gsdlenv\"";
649    }
650
651    return ($ffmpeg_cmd,$oflash_filename,$oflash_file);
652}
653
654
655
656
657sub stream_flv_audio_cmd
658{
659    # AudioConverter also has a routine for doing this
660    # => merge into one!
661    # **************
662
663    my $self = shift (@_);
664    my ($ivideo_filename, $opt_streaming_achan, $opt_streaming_arate) = @_;
665
666    # Convert either audio or video to streamable audio format
667
668    my $streaming_achan
669    = (defined $opt_streaming_achan) ? $opt_streaming_achan : 2;
670
671    my $streaming_arate
672    = (defined $opt_streaming_arate) ? $opt_streaming_arate : 22050;
673
674    my $output_dir = $self->{'cached_dir'};
675    my $ivideo_root = $self->{'cached_file_root'};
676
677    my $ofla_file = "${ivideo_root}_astream.flv";
678    my $ofla_filename = &util::filename_cat($output_dir,$ofla_file);
679
680    my $exp_duration = $self->{'exp_duration'};
681    my $t_opt = (defined $exp_duration) ? "-t $exp_duration" : "";
682
683    my $main_opts = "-vn -y $t_opt";
684
685    my $stream_opts .= " -ac $streaming_achan -ar $streaming_arate";
686
687    my $all_opts = "$main_opts $stream_opts";
688
689    my $ffmpeg_cmd;
690
691    my $ivideo_filename_gsdlenv = $self->gsdlhome_independent($ivideo_filename);
692    my $ofla_filename_gsdlenv = $self->gsdlhome_independent($ofla_filename);
693    $ffmpeg_cmd = "ffmpeg -i \"$ivideo_filename_gsdlenv\" $all_opts -y \"$ofla_filename_gsdlenv\"";
694
695    return ($ffmpeg_cmd,$ofla_filename,$ofla_file);
696}
697
698
699
700
701sub audio_excerpt_cmd
702{
703    my $self = shift (@_);
704    my ($ivoa_filename,$hh,$mm,$ss,$opt_excerpt_len) = @_;
705
706    # ivoa = input video or audio
707
708    my $time_encoded = "$hh:$mm:$ss";
709    my $time_encoded_file = "$hh$mm$ss";
710   
711   
712    my $output_dir = $self->{'cached_dir'};
713    my $ivoa_root = $self->{'cached_file_root'};
714
715    my $omp3_file = "${ivoa_root}_$time_encoded_file.mp3";
716    my $omp3_filename = &util::filename_cat($output_dir,$omp3_file);
717
718    my $all_opts = "-y -acodec mp3 -ss $time_encoded ";
719
720    if (defined $opt_excerpt_len) {
721    $all_opts .= "-t $opt_excerpt_len ";
722    }
723
724
725    my $ivoa_filename_gsdlenv = $self->gsdlhome_independent($ivoa_filename);
726    my $omp3_filename_gsdlenv = $self->gsdlhome_independent($omp3_filename);
727
728
729    my $ffmpeg_cmd = "ffmpeg -i \"$ivoa_filename_gsdlenv\" $all_opts  \"$omp3_filename_gsdlenv\"";
730
731    return ($ffmpeg_cmd,$omp3_filename,$omp3_file);
732}
733
734
735
736sub streamseekable_cmd
737{
738    my $self = shift (@_);
739    my ($oflash_filename) = @_;
740
741    my $output_dir = $self->{'cached_dir'};
742    my $ivideo_root = $self->{'cached_file_root'};
743
744    ## my $cue_filename = &util::filename_cat($output_dir,"on_cue.xml");
745
746    my $flvtool_cmd = "flvtool2 -vUP \"$oflash_filename\"";
747
748    return ($flvtool_cmd,$oflash_filename);
749}
750
751
752sub streamkeyframes_cmd
753{
754    my $self = shift (@_);
755    my ($oflash_filename,$doc_obj,$section) = @_;
756
757    my $assocfilepath
758    = $doc_obj->get_metadata_element($section,"assocfilepath");
759
760    my $output_dir = $self->{'cached_dir'};
761
762    my $cue_filename = &util::filename_cat($output_dir,"on_cue.xml");
763 
764    my $flvtool_cmd = "flvtool2 -vAUtP \"$cue_filename\"  \"$oflash_filename\"";
765
766    return ($flvtool_cmd,$oflash_filename);
767}
768
769
770sub streamkeyframes_cmd_old
771{
772    my $self = shift (@_);
773    my ($oflash_filename,$doc_obj,$section) = @_;
774
775    my $assocfilepath
776    = $doc_obj->get_metadata_element($section,"assocfilepath");
777
778    my $output_dir = $self->{'cached_dir'};
779
780    my $cue_filename = &util::filename_cat($output_dir,"on_cue.xml");
781
782    my $video_server = $ENV{'GEXT_VIDEO_SERVER'};
783    my $video_prefix = $ENV{'GEXT_VIDEO_PREFIX'};
784
785    my $collect = $ENV{'GSDLCOLLECTION'};
786 
787    print STDERR "**** Warning: need to remove dependency of GEXT_VIDEO_SERVER and _PREFIX\n";
788
789    my $flvtool_cmd = "flvtool2 -vAUtP \"$cue_filename\" -thumbLocation:$video_server$video_prefix/collect/$collect/index/assoc/$assocfilepath \"$oflash_filename\"";
790
791
792    return ($flvtool_cmd,$oflash_filename);
793}
794
795
796sub streamcuepts_cmd
797{
798    my $self = shift (@_);
799    my ($oflash_filename) = @_;
800
801    my $output_dir = $self->{'cached_dir'};
802
803    my $cue_filename = &util::filename_cat($output_dir,"on_cue.xml");
804
805    ##my $video_server = $ENV{'GEXT_VIDEO_SERVER'};
806    ##my $video_prefix = $ENV{'GEXT_VIDEO_PREFIX'};
807
808    ## my $collect = $ENV{'GSDLCOLLECTION'};
809
810    ## my $thumbloc = "$video_server$video_prefix/collect/$collect";
811
812
813#    my $flvtool_cmd = "flvtool2 -vUAtP \"$cue_filename\" -thumbLocation:$thumbloc \"$oflash_filename\"";
814
815#    my $flvtool_cmd = "flvtool2 -vUAt \"$cue_filename\" \"$oflash_filename\"";
816
817
818
819#    my $flvtool_cmd = "flvtool2 -vUAt \"$cue_filename\" \"$oflash_filename\"";
820
821
822##    my $flvtool_cmd = "flvtool2 -vAt \"$cue_filename\" -UP \"$oflash_filename\" \"$output_dir/updated.flv\"";
823
824##    my $flvtool_cmd = "flvtool2 -vAtU \"$cue_filename\" \"$oflash_filename\" \"$output_dir/updated.flv\"";
825
826    my $flvtool_cmd = "flvtool2 -vAtUP \"$cue_filename\" \"$oflash_filename\"";
827
828    return ($flvtool_cmd,$oflash_filename);
829}
830
831
832sub keyframe_thumbnail_cmd
833{
834    my $self = shift (@_);
835    my ($ivideo_filename,$thumbnailfile,$thumbnailwidth,$thumbnailheight) = @_;
836
837    my $output_dir = $self->{'cached_dir'};
838    my $ivideo_root = $self->{'cached_file_root'};
839
840    my $key_filename_prefix = &util::filename_cat($output_dir,$ivideo_root);
841
842
843    # Try for 4th keyframe, but fall back to 1st if doesn't exist
844    my $key_filename = "${key_filename_prefix}_0003.jpg";
845    $key_filename = "${key_filename_prefix}_0000.jpg" if (!-e $key_filename);
846
847    my $key_filename_gsdlenv = $self->gsdlhome_independent($key_filename);
848    my $thumbnailfile_gsdlenv = $self->gsdlhome_independent($thumbnailfile);
849
850    my $command;
851
852    if (-e $key_filename) {
853    $command = "convert -interlace plane -verbose -geometry $thumbnailwidth"
854        . "x$thumbnailheight \"$key_filename_gsdlenv\" \"$thumbnailfile_gsdlenv\"";
855    }
856    else {
857    # extract_keyframe has either not been switched on, or else had
858    # a problem when running
859    # => extract a from
860    # my $frame_rate = 1.0 / 60.0;
861   
862    my $ivideo_filename_gsdlenv = $self->gsdlhome_independent($ivideo_filename);
863
864    $command = "ffmpeg -i \"$ivideo_filename_gsdlenv\"  -ss 3.5 -vframes 1 -f image2 -s ${thumbnailwidth}x${thumbnailheight} -y \"$thumbnailfile_gsdlenv\"";
865
866    # fmpeg -i input.dv -r 1 -f image2 -s 120x96 images%05d.png
867    }
868
869    return ($command,$thumbnailfile);
870}
871
872
873sub keyframe_montage_cmd
874{
875    my $self = shift (@_);
876    my ($ivideo_filename,$montagefile) = @_;
877
878    my $output_dir = $self->{'cached_dir'};
879    my $ivideo_root = $self->{'cached_file_root'};
880
881    my $key_filename_prefix = &util::filename_cat($output_dir,$ivideo_root);
882
883    my $options = "-tile 10  -geometry 75x62+2+2";
884
885    my $command = "montage $options ${key_filename_prefix}_*.jpg \"$montagefile\"";
886
887    return ($command,$montagefile);
888}
889
890
891
892sub parse_shot_xml
893{
894    my ($self) = shift(@_);
895
896    my $outhandle = $self->{'outhandle'};
897    my $output_dir = $self->{'cached_dir'};
898
899    my $shots_filename = &util::filename_cat($output_dir,"shots.xml");
900
901    eval {
902    $self->{'parser'}->parsefile($shots_filename);
903    };
904   
905    if ($@) {
906    print $outhandle "VideoConverter: skipping $shots_filename as not conformant to Hive shot syntax\n" if ($self->{'verbosity'} > 1);
907    print $outhandle "\n Perl Error:\n $@\n" if ($self->{'verbosity'}>2);
908    return 0;
909    }
910
911}
912
913sub associate_keyframes_old
914{
915    my ($self) = shift(@_);
916
917    my ($doc_obj,$section) = @_;
918
919    my $output_dir = $self->{'cached_dir'};
920
921    my $count = 1;
922    foreach my $kframe_file (@{$self->{'keyframe_fnames'}}) {
923
924    my $kframe_filename = &util::filename_cat($output_dir,$kframe_file);
925    $doc_obj->associate_file($kframe_filename,"keyframe$count.jpg","image/jpeg",
926                 $section);
927    $count++;
928    }
929
930    $doc_obj->add_utf8_metadata($section,"NumKeyframes",scalar(@{$self->{'keyframe_fnames'}}));
931
932
933    # *****
934    # $doc_obj->add_metadata ($section, "thumblist", $self->{'flowplayer_thumblist'});
935
936}
937
938sub associate_keyframes
939{
940    my ($self) = shift(@_);
941
942    my ($doc_obj,$topsection,$timeline) = @_;
943
944    my $output_dir = $self->{'cached_dir'};
945   
946    my $count = 1;
947
948    foreach my $t (sort { $timeline->{$a}->{'keyframeindex'} <=> $timeline->{$b}->{'keyframeindex'} } keys %$timeline)
949    {
950    my $kframe_file = $timeline->{$t}->{'thumb'};
951    my $timestamp = $timeline->{$t}->{'timestamp'};
952
953    # create next sub-section to video "document"
954    my $endchild = $doc_obj->insert_section($doc_obj->get_end_child($topsection));
955    $doc_obj->add_utf8_metadata($endchild,"Title","Timestamp $timestamp");
956    $doc_obj->add_utf8_metadata($endchild,"FrameNum",$t);
957
958    my $kframe_filename = &util::filename_cat($output_dir,$kframe_file);
959    $doc_obj->associate_file($kframe_filename,"keyframe$count.jpg",
960                 "image/jpeg",
961                 $endchild);
962
963    $doc_obj->add_utf8_metadata($endchild,"assockeyframe","keyframe$count.jpg");
964
965    $doc_obj->add_utf8_metadata($topsection,"KeyframeTimestamp",$timestamp);
966    $doc_obj->add_utf8_metadata($topsection,"KeyframeFrameNum",$t);
967
968
969
970    $count++;
971    }
972
973    #### $doc_obj->add_utf8_metadata($topsection,"NumKeyframes",scalar(@{$self->{'keyframe_fnames'}}));
974
975    $doc_obj->add_utf8_metadata($topsection,"NumKeyframes",scalar(keys %$timeline));
976
977
978    # *****
979    # $doc_obj->add_metadata ($topsection, "thumblist", $self->{'flowplayer_thumblist'});
980}
981
982
983
984sub enable_audio_streaming
985{
986    my $self = shift (@_);
987    my ($doc_obj,$originalfilename,$filename,$convertto_regenerated) = @_;
988
989    my $section = $doc_obj->get_top_section();
990
991    my $output_dir   = $self->{'cached_dir'};
992    my $ivideo_root  = $self->{'cached_file_root'};
993   
994    # Generate FLV audio-only format for streaming purposes
995
996    my $optionally_run_general_cmd = "run_uncached_general_cmd";
997    if ($self->{'enable_cache'}) {
998    $optionally_run_general_cmd
999        = ($convertto_regenerated) ? "regenerate_general_cmd" : "run_cached_general_cmd";
1000    }
1001
1002   
1003    my $ifilename = $originalfilename || $filename;
1004
1005    my ($stream_cmd,$ofla_filename,$ofla_file)
1006    = $self->stream_flv_audio_cmd($ifilename);
1007   
1008   
1009    my $streamable_options = { @{$self->{'ffmpeg_monitor'}},
1010                   'message_prefix' => "Stream",
1011                   'message' => "Generating streamable audio: $ofla_file" };
1012   
1013
1014    my ($streamable_regenerated,$streamable_result,$streamable_had_error)
1015    = $self->$optionally_run_general_cmd($stream_cmd,
1016                         $ifilename,$ofla_filename,
1017                         $streamable_options);
1018
1019
1020    if (!$streamable_had_error) {
1021    my ($streamseekable_cmd,$ostreamseekable_filename) = $self->streamseekable_cmd($ofla_filename);
1022   
1023    my $streamseekable_options = { @{$self->{'flvtool2_monitor'}},
1024                       'message_prefix' => "Stream Seekable",
1025                       'message' => "Reprocessing audio stream to be seekable by timeline: $ofla_file" };
1026   
1027    if ($streamable_regenerated) {
1028        $self->run_general_cmd($streamseekable_cmd,$streamseekable_options);
1029    }
1030   
1031    my $streamable_url = $ofla_file;
1032    my $streamable_url_safe = $self->url_safe($streamable_url);
1033
1034    $doc_obj->add_utf8_metadata ($section, "streamableaudio", $streamable_url_safe);
1035    $doc_obj->associate_file($ofla_filename,$ofla_file,"audio/flash",
1036                 $section);
1037    }
1038
1039    $doc_obj->add_metadata ($section, "audioflashwidth",    400);
1040    $doc_obj->add_metadata ($section, "audioflashheight",   22 + 100);
1041
1042    $self->{'ofla_file'} = $ofla_file;
1043    $self->{'ofla_filename'} = $ofla_filename;
1044
1045    return $streamable_regenerated;
1046}
1047
1048
1049
1050
1051
1052sub enable_video_streaming
1053{
1054    my $self = shift (@_);
1055    my ($doc_obj,$originalfilename,$filename,$convertto_regenerated,
1056    $video_width,$video_height) = @_;
1057
1058    my $section = $doc_obj->get_top_section();
1059
1060    my $output_dir   = $self->{'cached_dir'};
1061    my $ivideo_root  = $self->{'cached_file_root'};
1062   
1063    # Generate the Flash FLV format for streaming purposes
1064
1065    my $optionally_run_general_cmd = "run_uncached_general_cmd";
1066    if ($self->{'enable_cache'}) {
1067    $optionally_run_general_cmd
1068        = ($convertto_regenerated) ? "regenerate_general_cmd" : "run_cached_general_cmd";
1069    }
1070
1071
1072    my $streaming_bitrate = $self->{'streamingbitrate'};
1073    my $streaming_size    = $self->{'streamingsize'};
1074   
1075    my $streaming_quality = "high";
1076   
1077    my $ifilename = $originalfilename || $filename;
1078
1079    my ($stream_cmd,$oflash_filename,$oflash_file)
1080    = $self->stream_flv_video_cmd($ifilename,
1081                     $video_width,$video_height,
1082                     $streaming_quality,
1083                     $streaming_bitrate, $streaming_size);
1084   
1085   
1086    my $streamable_options = { @{$self->{'ffmpeg_monitor'}},
1087                   'message_prefix' => "Stream",
1088                   'message' => "Generating streamable video: $oflash_file" };
1089   
1090
1091    my ($streamable_regenerated,$streamable_result,$streamable_had_error)
1092    = $self->$optionally_run_general_cmd($stream_cmd,
1093                         $ifilename,$oflash_filename,
1094                         $streamable_options);
1095   
1096    if (!$streamable_had_error) {
1097    my ($streamseekable_cmd,$ostreamseekable_filename) = $self->streamseekable_cmd($oflash_filename);
1098   
1099    my $streamseekable_options = { @{$self->{'flvtool2_monitor'}},
1100                       'message_prefix' => "Stream Seekable",
1101                       'message' => "Reprocessing video stream to be seekable by timeline: $oflash_file" };
1102   
1103    if ($streamable_regenerated) {
1104        $self->run_general_cmd($streamseekable_cmd,$streamseekable_options);
1105    }
1106   
1107    my $streamable_url = $oflash_file;
1108    my $streamable_url_safe = $self->url_safe($streamable_url);
1109   
1110    $doc_obj->add_utf8_metadata ($section, "streamablevideo", $streamable_url_safe);
1111    $doc_obj->associate_file($oflash_filename,$oflash_file,"video/flash",
1112                 $section);
1113    }
1114
1115
1116    #
1117    # FlowPlayer.swf       height+22 pixels
1118    # FlowPlayerBlack.swf  height+16 pixels
1119    # FlowPlayerThermo.swf height+16 pixels
1120    # FlowPlayerWhite.swf  height+26 pixels
1121
1122    my $flashwidth = $video_width;
1123    my $flashheight = $video_height + 22;
1124
1125    if ($self->{'extract_keyframes'}) {
1126    $flashheight += 100;
1127    }
1128
1129    $doc_obj->add_metadata ($section, "flashwidth",    $flashwidth);
1130    $doc_obj->add_metadata ($section, "flashheight",   $flashheight);
1131     
1132    $self->{'oflash_file'} = $oflash_file;
1133    $self->{'oflash_filename'} = $oflash_filename;
1134
1135    return $streamable_regenerated;
1136}
1137
1138sub enable_h264_streaming
1139{
1140    my $self = shift (@_);
1141    my ($doc_obj,$originalfilename,$filename,$convertto_regenerated,
1142    $video_width,$video_height) = @_;
1143   
1144
1145    my $section = $doc_obj->get_top_section();
1146
1147    my $output_dir   = $self->{'cached_dir'};
1148    my $ivideo_root  = $self->{'cached_file_root'};
1149   
1150    # Generate the H264 video file format for streaming purposes using the cache feature if used
1151
1152    my $optionally_run_general_cmd = "run_uncached_general_cmd";
1153    if ($self->{'enable_cache'}) {
1154    $optionally_run_general_cmd
1155        = ($convertto_regenerated) ? "regenerate_general_cmd" : "run_cached_general_cmd";
1156    }
1157
1158    my $streaming_HQ_size    = $self->{'streamingHQsize'};
1159    my $streaming_HQ_VideoBitrate    = $self->{'streamingHQVideoBitrate'};
1160    my $streaming_HQ_AudioBitrate    = $self->{'streamingHQAudioBitrate'};
1161
1162    my $ifilename = $originalfilename || $filename;
1163    my ($stream_cmd,$oflash_filename,$oflash_file)
1164    = $self->stream_mp4_video_cmd($ifilename,
1165                     $streaming_HQ_size,
1166                     $streaming_HQ_VideoBitrate,
1167                     $streaming_HQ_AudioBitrate);
1168   
1169   
1170    my $streamable_options = { @{$self->{'handbrake_monitor'}},
1171                   'message_prefix' => "MP4 Stream",
1172                   'message' => "Generating streamable MP4 video: $oflash_file" };
1173   
1174
1175    my ($streamable_regenerated,$streamable_result,$streamable_had_error)
1176    = $self->$optionally_run_general_cmd($stream_cmd,
1177                         $ifilename,$oflash_filename,
1178                         $streamable_options);
1179   
1180    if (!$streamable_had_error) {
1181
1182   
1183    my $streamable_url = $oflash_file;
1184    my $streamable_url_safe = $self->url_safe($streamable_url);
1185   
1186    my $outhandle = $self->{'outhandle'};
1187
1188    my ($vtype, $width, $height, $duration, $durationDisplay, $vsize,
1189        $vcodec,$vrate,$fps,$atype,$afreq,$achan,$arate,$metadata_table) = identify($oflash_filename,$outhandle,0);
1190       
1191    $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingVideoFormat", $vtype);
1192    $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingVideoFileSize", $vsize);
1193    $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingWidth", $width);
1194    $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingHeight", $height);
1195    $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingDuration", $durationDisplay);
1196    $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingFPS", $fps);
1197    $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingVideoCodec", $vcodec);
1198    $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingVideoBitrate", $vrate);
1199    $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingAudioCodec", $atype);
1200    $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingAudioBitrate", $arate);
1201    $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingFileBitrate", $vrate + $arate);
1202   
1203    foreach my $metaname(keys %$metadata_table)
1204    {
1205        my $metaval = $metadata_table->{$metaname};
1206       
1207        $doc_obj->add_utf8_metadata ($section, $metaname, $metaval);
1208    }
1209   
1210    #add the metadata for the resulting file that should be open inside the flash video player
1211    $doc_obj->add_utf8_metadata ($section, "streamablevideo", $streamable_url_safe);
1212    $doc_obj->associate_file($oflash_filename,$oflash_file,"video/mp4",
1213                 $section);
1214    }
1215
1216
1217
1218
1219    my $flashwidth = $video_width;
1220    my $flashheight = $video_height + 22;
1221
1222    if ($self->{'extract_keyframes'}) {
1223    $flashheight += 100;
1224    }
1225
1226    $doc_obj->add_metadata ($section, "flashwidth",    $flashwidth);
1227    $doc_obj->add_metadata ($section, "flashheight",   $flashheight);
1228     
1229    $self->{'oflash_file'} = $oflash_file;
1230    $self->{'oflash_filename'} = $oflash_filename;
1231
1232    return $streamable_regenerated;
1233   
1234   
1235}
1236
1237sub enable_full_streaming
1238{
1239    my $self = shift (@_);
1240    my ($doc_obj,$originalfilename,$filename,$convertto_regenerated,
1241    $video_width,$video_height) = @_;
1242
1243    my $video_streamable_regenerated
1244    = $self->enable_video_streaming($doc_obj,$originalfilename,$filename,
1245                    $convertto_regenerated,
1246                    $video_width,$video_height);
1247
1248    my $audio_streamable_regenerated
1249    = $self->enable_audio_streaming($doc_obj,$originalfilename,$filename,
1250                    $convertto_regenerated);
1251
1252    return ($video_streamable_regenerated || $audio_streamable_regenerated);
1253}
1254
1255
1256
1257sub flvtool2_monitor_line
1258{
1259    my ($line) = @_;
1260
1261    my $had_error = 0;
1262    my $generate_dot = 1;
1263
1264    if ($line =~ m/\s+\- /) {
1265    # ignore tabulated output printed at end of command
1266    $generate_dot = 0;
1267    }
1268   
1269    if ($line =~ m/^Error:/i) {
1270    $had_error = 1;
1271    }
1272
1273    return ($had_error,$generate_dot);
1274}
1275
1276
1277
1278
1279
12801;
Note: See TracBrowser for help on using the browser.