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

Revision 25518, 49.5 KB (checked in by davidb, 8 years ago)

Tweaks to code based on building the nzonair4 test collection

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    $command = "ffmpeg -i \"$ivideo_filename_gsdlenv\"  -ss 16.5 -vframes 1 -f image2 -s ${thumbnailwidth}x${thumbnailheight} -deinterlace -y \"$thumbnailfile_gsdlenv\"";
1032
1033
1034    # fmpeg -i input.dv -r 1 -f image2 -s 120x96 images%05d.png
1035    }
1036
1037    return ($command,$thumbnailfile);
1038}
1039
1040
1041
1042
1043sub keyframe_montage_cmd
1044{
1045    my $self = shift (@_);
1046    my ($ivideo_filename,$montagefile) = @_;
1047
1048    my $output_dir = $self->{'cached_dir'};
1049    my $ivideo_root = $self->{'cached_file_root'};
1050
1051    my $key_filename_prefix = &util::filename_cat($output_dir,$ivideo_root);
1052
1053    my $options = "-tile 10  -geometry 75x62+2+2";
1054
1055    my $command = "montage $options ${key_filename_prefix}_*.jpg \"$montagefile\"";
1056
1057    return ($command,$montagefile);
1058}
1059
1060
1061
1062sub parse_shot_xml
1063{
1064    my ($self) = shift(@_);
1065
1066    my $outhandle = $self->{'outhandle'};
1067    my $output_dir = $self->{'cached_dir'};
1068
1069    my $shots_filename = &util::filename_cat($output_dir,"shots.xml");
1070
1071    eval {
1072    $self->{'parser'}->parsefile($shots_filename);
1073    };
1074   
1075    if ($@) {
1076    print $outhandle "VideoConverter: skipping $shots_filename as not conformant to Hive shot syntax\n" if ($self->{'verbosity'} > 1);
1077    print $outhandle "\n Perl Error:\n $@\n" if ($self->{'verbosity'}>2);
1078    return 0;
1079    }
1080
1081}
1082
1083
1084sub parse_shot_dir
1085{
1086    my ($self) = shift(@_);
1087   
1088    my $outhandle = $self->{'outhandle'};
1089    my $output_dir = $self->{'cached_dir'};
1090   
1091    my $ivideo_root  = $self->{'cached_file_root'};
1092   
1093    my $frames_output_dir = &util::filename_cat($output_dir,"frames");
1094   
1095    my @jpeg_frames = ();
1096   
1097    if (opendir(FDIRIN, $frames_output_dir)) {
1098    @jpeg_frames = grep { $_ =~ /_\d+\.jpg$/ } readdir(FDIRIN);
1099    closedir(FDIRIN);
1100    }
1101    else {
1102    print STDERR "VideoConverter:: Failed to open directory $frames_output_dir:\n$!\n";
1103    return 0;
1104    }
1105   
1106    $self->{'keyframe_timeline'} = {};
1107   
1108    my $vfps = $self->{'video-fps'};
1109   
1110    my $keyframe_index = 1;
1111   
1112    foreach my $f (@jpeg_frames) {
1113    my ($hh,$mm,$ss) = ($f =~ m/_(\d{2})_(\d{2})_(\d{2})_\d+\.jpg$/);
1114    my $frame_num = (3600*$hh + 60*$mm + $ss) * $vfps;
1115   
1116    my $time_msec = (3600*$hh + 60*$mm + $ss) * 1000;
1117   
1118    my $timeline_rec = { 'name'=> "Keyframe $keyframe_index",
1119                 'keyframeindex' => $keyframe_index,
1120                 'timestamp' => $time_msec,
1121                 'thumb' => &util::filename_cat("frames",$f),
1122                 'keyframenum' => $keyframe_index };
1123   
1124    $self->{'keyframe_timeline'}->{$keyframe_index}=$timeline_rec;
1125   
1126    $keyframe_index++;
1127    }
1128   
1129    my $cue_filename = &util::filename_cat($output_dir,"on_cue.xml");
1130   
1131    $self->open_on_cue($cue_filename);
1132
1133    $self->output_distributed_keyframes($self->{'keyframe_timeline'},$self->{'ffkeyframe_num_shots'});
1134
1135    $self->close_on_cue();
1136   
1137    return 1;
1138}
1139
1140sub associate_keyframes_old
1141{
1142    my ($self) = shift(@_);
1143
1144    my ($doc_obj,$section) = @_;
1145
1146    my $output_dir = $self->{'cached_dir'};
1147
1148    my $count = 1;
1149    foreach my $kframe_file (@{$self->{'keyframe_fnames'}}) {
1150
1151    my $kframe_filename = &util::filename_cat($output_dir,$kframe_file);
1152    $doc_obj->associate_file($kframe_filename,"keyframe$count.jpg","image/jpeg",
1153                 $section);
1154    $count++;
1155    }
1156
1157    $doc_obj->add_utf8_metadata($section,"NumKeyframes",scalar(@{$self->{'keyframe_fnames'}}));
1158
1159
1160    # *****
1161    # $doc_obj->add_metadata ($section, "thumblist", $self->{'flowplayer_thumblist'});
1162
1163}
1164
1165sub associate_keyframes
1166{
1167    my ($self) = shift(@_);
1168
1169    my ($doc_obj,$topsection,$timeline) = @_;
1170
1171    my $output_dir = $self->{'cached_dir'};
1172   
1173    my $count = 1;
1174
1175##    print STDERR "**** timeline keys = ", join("; ", keys %$timeline), "\n";
1176    foreach my $t (sort { $timeline->{$a}->{'keyframeindex'} <=> $timeline->{$b}->{'keyframeindex'} } keys %$timeline)
1177    {
1178    my $kframe_file = $timeline->{$t}->{'thumb'};
1179    my $timestamp = $timeline->{$t}->{'timestamp'};
1180
1181    # create next sub-section to video "document"
1182    my $endchild = $doc_obj->insert_section($doc_obj->get_end_child($topsection));
1183    $doc_obj->add_utf8_metadata($endchild,"Title","Timestamp $timestamp");
1184    $doc_obj->add_utf8_metadata($endchild,"FrameNum",$t);
1185
1186    my $kframe_filename = &util::filename_cat($output_dir,$kframe_file);
1187    $doc_obj->associate_file($kframe_filename,"keyframe$count.jpg",
1188                 "image/jpeg",
1189                 $endchild);
1190
1191    $doc_obj->add_utf8_metadata($endchild,"assockeyframe","keyframe$count.jpg");
1192
1193    $doc_obj->add_utf8_metadata($topsection,"KeyframeTimestamp",$timestamp);
1194    $doc_obj->add_utf8_metadata($topsection,"KeyframeFrameNum",$t);
1195
1196
1197
1198    $count++;
1199    }
1200
1201    #### $doc_obj->add_utf8_metadata($topsection,"NumKeyframes",scalar(@{$self->{'keyframe_fnames'}}));
1202
1203    $doc_obj->add_utf8_metadata($topsection,"NumKeyframes",scalar(keys %$timeline));
1204
1205
1206    # *****
1207    # $doc_obj->add_metadata ($topsection, "thumblist", $self->{'flowplayer_thumblist'});
1208}
1209
1210
1211
1212sub enable_audio_streaming
1213{
1214    my $self = shift (@_);
1215    my ($doc_obj,$originalfilename,$filename,$convertto_regenerated) = @_;
1216
1217    my $section = $doc_obj->get_top_section();
1218
1219    my $output_dir   = $self->{'cached_dir'};
1220    my $ivideo_root  = $self->{'cached_file_root'};
1221   
1222    # Generate FLV audio-only format for streaming purposes
1223
1224    my $optionally_run_general_cmd = "run_uncached_general_cmd";
1225    if ($self->{'enable_cache'}) {
1226    $optionally_run_general_cmd
1227        = ($convertto_regenerated) ? "regenerate_general_cmd" : "run_cached_general_cmd";
1228    }
1229
1230   
1231    my $ifilename = $originalfilename || $filename;
1232
1233    my ($stream_cmd,$ofla_filename,$ofla_file)
1234    = $self->stream_flv_audio_cmd($ifilename);
1235   
1236   
1237    my $streamable_options = { @{$self->{'ffmpeg_monitor'}},
1238                   'message_prefix' => "Stream",
1239                   'message' => "Generating streamable audio: $ofla_file" };
1240   
1241
1242    my ($streamable_regenerated,$streamable_result,$streamable_had_error)
1243    = $self->$optionally_run_general_cmd($stream_cmd,
1244                         $ifilename,$ofla_filename,
1245                         $streamable_options);
1246
1247
1248    if (!$streamable_had_error) {
1249    my ($streamseekable_cmd,$ostreamseekable_filename) = $self->streamseekable_cmd($ofla_filename);
1250   
1251    my $streamseekable_options = { @{$self->{'flvtool2_monitor'}},
1252                       'message_prefix' => "Stream Seekable",
1253                       'message' => "Reprocessing audio stream to be seekable by timeline: $ofla_file" };
1254   
1255    if ($streamable_regenerated) {
1256        $self->run_general_cmd($streamseekable_cmd,$streamseekable_options);
1257    }
1258   
1259    my $streamable_url = $ofla_file;
1260    my $streamable_url_safe = $self->url_safe($streamable_url);
1261
1262    $doc_obj->add_utf8_metadata ($section, "streamableaudio", $streamable_url_safe);
1263    $doc_obj->associate_file($ofla_filename,$ofla_file,"audio/flash",
1264                 $section);
1265    }
1266
1267    $doc_obj->add_metadata ($section, "audioflashwidth",    400);
1268    $doc_obj->add_metadata ($section, "audioflashheight",   22 + 100);
1269
1270    $self->{'ofla_file'} = $ofla_file;
1271    $self->{'ofla_filename'} = $ofla_filename;
1272
1273    return $streamable_regenerated;
1274}
1275
1276
1277
1278
1279
1280sub enable_video_streaming
1281{
1282    my $self = shift (@_);
1283    my ($doc_obj,$originalfilename,$filename,$convertto_regenerated,
1284    $video_width,$video_height) = @_;
1285
1286    my $section = $doc_obj->get_top_section();
1287
1288    my $output_dir   = $self->{'cached_dir'};
1289    my $ivideo_root  = $self->{'cached_file_root'};
1290   
1291    # Generate the Flash FLV format for streaming purposes
1292
1293    my $optionally_run_general_cmd = "run_uncached_general_cmd";
1294    if ($self->{'enable_cache'}) {
1295    $optionally_run_general_cmd
1296        = ($convertto_regenerated) ? "regenerate_general_cmd" : "run_cached_general_cmd";
1297    }
1298
1299
1300    my $streaming_bitrate = $self->{'streamingbitrate'};
1301    my $streaming_size    = $self->{'streamingsize'};
1302   
1303    my $streaming_quality = "high";
1304   
1305    my $ifilename = $originalfilename || $filename;
1306
1307    my ($stream_cmd,$oflash_filename,$oflash_file)
1308    = $self->stream_flv_video_cmd($ifilename,
1309                     $video_width,$video_height,
1310                     $streaming_quality,
1311                     $streaming_bitrate, $streaming_size);
1312   
1313   
1314    my $streamable_options = { @{$self->{'ffmpeg_monitor'}},
1315                   'message_prefix' => "Stream",
1316                   'message' => "Generating streamable video: $oflash_file" };
1317   
1318
1319    my ($streamable_regenerated,$streamable_result,$streamable_had_error)
1320    = $self->$optionally_run_general_cmd($stream_cmd,
1321                         $ifilename,$oflash_filename,
1322                         $streamable_options);
1323   
1324    if (!$streamable_had_error) {
1325    my ($streamseekable_cmd,$ostreamseekable_filename) = $self->streamseekable_cmd($oflash_filename);
1326   
1327    my $streamseekable_options = { @{$self->{'flvtool2_monitor'}},
1328                       'message_prefix' => "Stream Seekable",
1329                       'message' => "Reprocessing video stream to be seekable by timeline: $oflash_file" };
1330   
1331    if ($streamable_regenerated) {
1332        $self->run_general_cmd($streamseekable_cmd,$streamseekable_options);
1333    }
1334   
1335    my $streamable_url = $oflash_file;
1336    my $streamable_url_safe = $self->url_safe($streamable_url);
1337   
1338    $doc_obj->add_utf8_metadata ($section, "streamablevideo", $streamable_url_safe);
1339    $doc_obj->associate_file($oflash_filename,$oflash_file,"video/flash",
1340                 $section);
1341    }
1342
1343
1344    #
1345    # FlowPlayer.swf       height+22 pixels
1346    # FlowPlayerBlack.swf  height+16 pixels
1347    # FlowPlayerThermo.swf height+16 pixels
1348    # FlowPlayerWhite.swf  height+26 pixels
1349
1350    my $flashwidth = $video_width;
1351    my $flashheight = $video_height + 22;
1352
1353    if ($self->{'create_keyframes'} eq "true") {
1354    $flashheight += 100;
1355    }
1356
1357    $doc_obj->add_metadata ($section, "flashwidth",    $flashwidth);
1358    $doc_obj->add_metadata ($section, "flashheight",   $flashheight);
1359     
1360    $self->{'oflash_file'} = $oflash_file;
1361    $self->{'oflash_filename'} = $oflash_filename;
1362
1363    return $streamable_regenerated;
1364}
1365
1366sub enable_direct_streaming
1367{
1368    my $self = shift (@_);
1369    my ($doc_obj,$originalfilename,$filename,$file,
1370    $video_width,$video_height) = @_;
1371
1372    my $section = $doc_obj->get_top_section();
1373   
1374    my $ifilename = $originalfilename || $filename;
1375    #my $ifile = $self->gsdlhome_independent($ifilename);
1376    my $ifile = $file;
1377
1378    print STDERR "Using the original video file for direct streaming: $ifile\n";
1379
1380    my $streamable_url = $ifile;
1381    my $streamable_url_safe = $self->url_safe($streamable_url);
1382   
1383    my $outhandle = $self->{'outhandle'};
1384
1385    my $identify_vals = identifyMI($ifilename,$outhandle,0);
1386
1387    #Add the most common metadata extracted by MediaInfo from the streamable video file
1388    $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingVideoFormat", $identify_vals->{'vtype'});
1389    $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingVideoFileSize", $identify_vals->{'filesizeDisplay'});
1390    $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingVideoFileSizeBytes", $identify_vals->{'filesize'});
1391    $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingWidth", $identify_vals->{'width'});
1392    $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingHeight", $identify_vals->{'height'});
1393    $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingDuration", $identify_vals->{'durationDisplay'});
1394    $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingDurationMs", $identify_vals->{'duration'});
1395    $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingFPS", $identify_vals->{'fps'});
1396    $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingVideoCodec", $identify_vals->{'vcodec'});
1397    $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingVideoBitrate", $identify_vals->{'vrate'});
1398    $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingAudioCodec", $identify_vals->{'acodec'});
1399    $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingAudioBitrate", $identify_vals->{'arate'});
1400    $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingAudioTracks", $identify_vals->{'atracks'});
1401    $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingOverallBitrate", $identify_vals->{'orateDisplay'});
1402    $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingSubtitleType", $identify_vals->{'stype'});
1403    $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingSubtitleLanguage", $identify_vals->{'slang'});
1404   
1405    my $get_max_metadata = $self->{'getMaximumMetadata'};
1406    if ($get_max_metadata eq "true"){
1407        my $metadata_table = $identify_vals->{'metadata_table'};
1408        foreach my $metaname(keys %$metadata_table)
1409        {
1410            my $metaval = $identify_vals->{'metadata_table'}->{$metaname};
1411            $doc_obj->add_utf8_metadata ($section, $metaname, $metaval);
1412        }
1413    }
1414   
1415    #add the metadata for the resulting file that should be open inside the replayme video player
1416    $doc_obj->add_utf8_metadata ($section, "streamablevideo", $streamable_url_safe);
1417    $doc_obj->associate_file($ifilename,$ifile,"application/octet-stream",$section);
1418
1419    my $flashwidth = $video_width;
1420    my $flashheight = $video_height + 22;
1421
1422    if ($self->{'create_keyframes'} eq "true") {
1423    $flashheight += 100;
1424    }
1425
1426    $doc_obj->add_metadata ($section, "flashwidth",    $flashwidth);
1427    $doc_obj->add_metadata ($section, "flashheight",   $flashheight);
1428     
1429    $self->{'oflash_file'} = $ifile;
1430    $self->{'oflash_filename'} = $ifilename;
1431
1432}
1433
1434
1435sub enable_h264_streaming
1436{
1437    my $self = shift (@_);
1438    my ($doc_obj,$originalfilename,$filename,$convertto_regenerated,
1439    $video_width,$video_height) = @_;
1440   
1441
1442    my $section = $doc_obj->get_top_section();
1443
1444    my $output_dir   = $self->{'cached_dir'};
1445    my $ivideo_root  = $self->{'cached_file_root'};
1446   
1447    # Generate the H264 video file format for streaming purposes using the cache feature if used
1448
1449    my $optionally_run_general_cmd = "run_uncached_general_cmd";
1450    if ($self->{'enable_cache'}) {
1451    $optionally_run_general_cmd
1452        = ($convertto_regenerated) ? "regenerate_general_cmd" : "run_cached_general_cmd";
1453    }
1454
1455    my $streaming_HQ_size    = $self->{'streamingHQsize'};
1456    my $streaming_HQ_VideoBitrate    = $self->{'streamingHQVideoBitrate'};
1457    my $streaming_HQ_AudioBitrate    = $self->{'streamingHQAudioBitrate'};
1458
1459    my $ifilename = $originalfilename || $filename;
1460    my ($stream_cmd,$oflash_filename,$oflash_file)
1461    = $self->stream_mp4_video_cmd($ifilename,
1462                     $streaming_HQ_size,
1463                     $streaming_HQ_VideoBitrate,
1464                     $streaming_HQ_AudioBitrate);
1465   
1466   
1467    my $streamable_options = { @{$self->{'handbrake_monitor'}},
1468                   'message_prefix' => "MP4 Stream",
1469                   'message' => "Generating streamable MP4 video: $oflash_file" };
1470   
1471
1472    my ($streamable_regenerated,$streamable_result,$streamable_had_error)
1473    = $self->$optionally_run_general_cmd($stream_cmd,
1474                         $ifilename,$oflash_filename,
1475                         $streamable_options);
1476   
1477    if (!$streamable_had_error) {
1478
1479   
1480    my $streamable_url = $oflash_file;
1481    my $streamable_url_safe = $self->url_safe($streamable_url);
1482   
1483    my $outhandle = $self->{'outhandle'};
1484
1485    my $identify_vals = identifyMI($oflash_filename,$outhandle,0);
1486   
1487    #Add the most common metadata extracted by MediaInfo from the streamable video file
1488    $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingVideoFormat", $identify_vals->{'vtype'});
1489    $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingVideoFileSize", $identify_vals->{'filesizeDisplay'});
1490    $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingWidth", $identify_vals->{'width'});
1491    $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingHeight", $identify_vals->{'height'});
1492    $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingDuration", $identify_vals->{'durationDisplay'});
1493    $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingFPS", $identify_vals->{'fps'});
1494    $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingVideoCodec", $identify_vals->{'vcodec'});
1495    $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingVideoBitrate", $identify_vals->{'vrate'});
1496    $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingAudioCodec", $identify_vals->{'acodec'});
1497    $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingAudioBitrate", $identify_vals->{'arate'});
1498    $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingOverallBitrate", $identify_vals->{'orateDisplay'});
1499    $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingSubtitleType", $identify_vals->{'stype'});
1500    $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingSubtitleLangue", $identify_vals->{'slang'});
1501   
1502    my $get_max_metadata = $self->{'getMaximumMetadata'};
1503    if ($get_max_metadata eq "true"){
1504        my $metadata_table = $identify_vals->{'metadata_table'};
1505        foreach my $metaname(keys %$metadata_table)
1506        {
1507            my $metaval = $identify_vals->{'metadata_table'}->{$metaname};
1508            $doc_obj->add_utf8_metadata ($section, $metaname, $metaval);
1509        }
1510    }
1511   
1512    #add the metadata for the resulting file that should be open inside the flash video player
1513    $doc_obj->add_utf8_metadata ($section, "streamablevideo", $streamable_url_safe);
1514    $doc_obj->associate_file($oflash_filename,$oflash_file,"video/mp4",
1515                 $section);
1516    }
1517
1518    my $flashwidth = $video_width;
1519    my $flashheight = $video_height + 22;
1520
1521    if ($self->{'create_keyframes'} eq "true") {
1522    $flashheight += 100;
1523    }
1524
1525    $doc_obj->add_metadata ($section, "flashwidth",    $flashwidth);
1526    $doc_obj->add_metadata ($section, "flashheight",   $flashheight);
1527     
1528    $self->{'oflash_file'} = $oflash_file;
1529    $self->{'oflash_filename'} = $oflash_filename;
1530
1531    return $streamable_regenerated;
1532   
1533   
1534}
1535
1536sub enable_full_streaming
1537{
1538    my $self = shift (@_);
1539    my ($doc_obj,$originalfilename,$filename,$convertto_regenerated,
1540    $video_width,$video_height) = @_;
1541
1542    my $video_streamable_regenerated
1543    = $self->enable_video_streaming($doc_obj,$originalfilename,$filename,
1544                    $convertto_regenerated,
1545                    $video_width,$video_height);
1546
1547    my $audio_streamable_regenerated
1548    = $self->enable_audio_streaming($doc_obj,$originalfilename,$filename,
1549                    $convertto_regenerated);
1550
1551    return ($video_streamable_regenerated || $audio_streamable_regenerated);
1552}
1553
1554
1555
1556sub flvtool2_monitor_line
1557{
1558    my ($line) = @_;
1559
1560    my $had_error = 0;
1561    my $generate_dot = 1;
1562
1563    if ($line =~ m/\s+\- /) {
1564    # ignore tabulated output printed at end of command
1565    $generate_dot = 0;
1566    }
1567   
1568    if ($line =~ m/^Error:/i) {
1569    $had_error = 1;
1570    }
1571
1572    return ($had_error,$generate_dot);
1573}
1574
1575
1576
1577
1578
15791;
Note: See TracBrowser for help on using the browser.