root/gs2-extensions/video-and-audio/trunk/src/perllib/plugins/VideoConverter.pm @ 25890

Revision 25890, 49.6 KB (checked in by jmt12, 8 years ago)

Moving -ss option before input video in FFMPEG commands as it is significantly faster

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