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

Last change on this file since 24188 was 24188, checked in by max, 13 years ago

Use MediaInfo by default,
can now successfully seek into the video very fast in order to extract a frame,
for now grab a frame at 35% of the video for thumbnail and screenview

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