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

Revision 21599, 31.2 KB (checked in by davidb, 9 years ago)

Section level support added in

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