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

Last change on this file since 25516 was 25516, checked in by davidb, 12 years ago

Code changes prompted by the development of ReplayMe

File size: 49.4 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
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 foreach my $t (sort { $timeline->{$a}->{'keyframeindex'} <=> $timeline->{$b}->{'keyframeindex'} } keys %$timeline)
1176 {
1177 my $kframe_file = $timeline->{$t}->{'thumb'};
1178 my $timestamp = $timeline->{$t}->{'timestamp'};
1179
1180 # create next sub-section to video "document"
1181 my $endchild = $doc_obj->insert_section($doc_obj->get_end_child($topsection));
1182 $doc_obj->add_utf8_metadata($endchild,"Title","Timestamp $timestamp");
1183 $doc_obj->add_utf8_metadata($endchild,"FrameNum",$t);
1184
1185 my $kframe_filename = &util::filename_cat($output_dir,$kframe_file);
1186 $doc_obj->associate_file($kframe_filename,"keyframe$count.jpg",
1187 "image/jpeg",
1188 $endchild);
1189
1190 $doc_obj->add_utf8_metadata($endchild,"assockeyframe","keyframe$count.jpg");
1191
1192 $doc_obj->add_utf8_metadata($topsection,"KeyframeTimestamp",$timestamp);
1193 $doc_obj->add_utf8_metadata($topsection,"KeyframeFrameNum",$t);
1194
1195
1196
1197 $count++;
1198 }
1199
1200 #### $doc_obj->add_utf8_metadata($topsection,"NumKeyframes",scalar(@{$self->{'keyframe_fnames'}}));
1201
1202 $doc_obj->add_utf8_metadata($topsection,"NumKeyframes",scalar(keys %$timeline));
1203
1204
1205 # *****
1206 # $doc_obj->add_metadata ($topsection, "thumblist", $self->{'flowplayer_thumblist'});
1207}
1208
1209
1210
1211sub enable_audio_streaming
1212{
1213 my $self = shift (@_);
1214 my ($doc_obj,$originalfilename,$filename,$convertto_regenerated) = @_;
1215
1216 my $section = $doc_obj->get_top_section();
1217
1218 my $output_dir = $self->{'cached_dir'};
1219 my $ivideo_root = $self->{'cached_file_root'};
1220
1221 # Generate FLV audio-only format for streaming purposes
1222
1223 my $optionally_run_general_cmd = "run_uncached_general_cmd";
1224 if ($self->{'enable_cache'}) {
1225 $optionally_run_general_cmd
1226 = ($convertto_regenerated) ? "regenerate_general_cmd" : "run_cached_general_cmd";
1227 }
1228
1229
1230 my $ifilename = $originalfilename || $filename;
1231
1232 my ($stream_cmd,$ofla_filename,$ofla_file)
1233 = $self->stream_flv_audio_cmd($ifilename);
1234
1235
1236 my $streamable_options = { @{$self->{'ffmpeg_monitor'}},
1237 'message_prefix' => "Stream",
1238 'message' => "Generating streamable audio: $ofla_file" };
1239
1240
1241 my ($streamable_regenerated,$streamable_result,$streamable_had_error)
1242 = $self->$optionally_run_general_cmd($stream_cmd,
1243 $ifilename,$ofla_filename,
1244 $streamable_options);
1245
1246
1247 if (!$streamable_had_error) {
1248 my ($streamseekable_cmd,$ostreamseekable_filename) = $self->streamseekable_cmd($ofla_filename);
1249
1250 my $streamseekable_options = { @{$self->{'flvtool2_monitor'}},
1251 'message_prefix' => "Stream Seekable",
1252 'message' => "Reprocessing audio stream to be seekable by timeline: $ofla_file" };
1253
1254 if ($streamable_regenerated) {
1255 $self->run_general_cmd($streamseekable_cmd,$streamseekable_options);
1256 }
1257
1258 my $streamable_url = $ofla_file;
1259 my $streamable_url_safe = $self->url_safe($streamable_url);
1260
1261 $doc_obj->add_utf8_metadata ($section, "streamableaudio", $streamable_url_safe);
1262 $doc_obj->associate_file($ofla_filename,$ofla_file,"audio/flash",
1263 $section);
1264 }
1265
1266 $doc_obj->add_metadata ($section, "audioflashwidth", 400);
1267 $doc_obj->add_metadata ($section, "audioflashheight", 22 + 100);
1268
1269 $self->{'ofla_file'} = $ofla_file;
1270 $self->{'ofla_filename'} = $ofla_filename;
1271
1272 return $streamable_regenerated;
1273}
1274
1275
1276
1277
1278
1279sub enable_video_streaming
1280{
1281 my $self = shift (@_);
1282 my ($doc_obj,$originalfilename,$filename,$convertto_regenerated,
1283 $video_width,$video_height) = @_;
1284
1285 my $section = $doc_obj->get_top_section();
1286
1287 my $output_dir = $self->{'cached_dir'};
1288 my $ivideo_root = $self->{'cached_file_root'};
1289
1290 # Generate the Flash FLV format for streaming purposes
1291
1292 my $optionally_run_general_cmd = "run_uncached_general_cmd";
1293 if ($self->{'enable_cache'}) {
1294 $optionally_run_general_cmd
1295 = ($convertto_regenerated) ? "regenerate_general_cmd" : "run_cached_general_cmd";
1296 }
1297
1298
1299 my $streaming_bitrate = $self->{'streamingbitrate'};
1300 my $streaming_size = $self->{'streamingsize'};
1301
1302 my $streaming_quality = "high";
1303
1304 my $ifilename = $originalfilename || $filename;
1305
1306 my ($stream_cmd,$oflash_filename,$oflash_file)
1307 = $self->stream_flv_video_cmd($ifilename,
1308 $video_width,$video_height,
1309 $streaming_quality,
1310 $streaming_bitrate, $streaming_size);
1311
1312
1313 my $streamable_options = { @{$self->{'ffmpeg_monitor'}},
1314 'message_prefix' => "Stream",
1315 'message' => "Generating streamable video: $oflash_file" };
1316
1317
1318 my ($streamable_regenerated,$streamable_result,$streamable_had_error)
1319 = $self->$optionally_run_general_cmd($stream_cmd,
1320 $ifilename,$oflash_filename,
1321 $streamable_options);
1322
1323 if (!$streamable_had_error) {
1324 my ($streamseekable_cmd,$ostreamseekable_filename) = $self->streamseekable_cmd($oflash_filename);
1325
1326 my $streamseekable_options = { @{$self->{'flvtool2_monitor'}},
1327 'message_prefix' => "Stream Seekable",
1328 'message' => "Reprocessing video stream to be seekable by timeline: $oflash_file" };
1329
1330 if ($streamable_regenerated) {
1331 $self->run_general_cmd($streamseekable_cmd,$streamseekable_options);
1332 }
1333
1334 my $streamable_url = $oflash_file;
1335 my $streamable_url_safe = $self->url_safe($streamable_url);
1336
1337 $doc_obj->add_utf8_metadata ($section, "streamablevideo", $streamable_url_safe);
1338 $doc_obj->associate_file($oflash_filename,$oflash_file,"video/flash",
1339 $section);
1340 }
1341
1342
1343 #
1344 # FlowPlayer.swf height+22 pixels
1345 # FlowPlayerBlack.swf height+16 pixels
1346 # FlowPlayerThermo.swf height+16 pixels
1347 # FlowPlayerWhite.swf height+26 pixels
1348
1349 my $flashwidth = $video_width;
1350 my $flashheight = $video_height + 22;
1351
1352 if ($self->{'create_keyframes'} eq "true") {
1353 $flashheight += 100;
1354 }
1355
1356 $doc_obj->add_metadata ($section, "flashwidth", $flashwidth);
1357 $doc_obj->add_metadata ($section, "flashheight", $flashheight);
1358
1359 $self->{'oflash_file'} = $oflash_file;
1360 $self->{'oflash_filename'} = $oflash_filename;
1361
1362 return $streamable_regenerated;
1363}
1364
1365sub enable_direct_streaming
1366{
1367 my $self = shift (@_);
1368 my ($doc_obj,$originalfilename,$filename,$file,
1369 $video_width,$video_height) = @_;
1370
1371 my $section = $doc_obj->get_top_section();
1372
1373 my $ifilename = $originalfilename || $filename;
1374 #my $ifile = $self->gsdlhome_independent($ifilename);
1375 my $ifile = $file;
1376
1377 print STDERR "Using the original video file for direct streaming: $ifile\n";
1378
1379 my $streamable_url = $ifile;
1380 my $streamable_url_safe = $self->url_safe($streamable_url);
1381
1382 my $outhandle = $self->{'outhandle'};
1383
1384 my $identify_vals = identifyMI($ifilename,$outhandle,0);
1385
1386 #Add the most common metadata extracted by MediaInfo from the streamable video file
1387 $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingVideoFormat", $identify_vals->{'vtype'});
1388 $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingVideoFileSize", $identify_vals->{'filesizeDisplay'});
1389 $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingVideoFileSizeBytes", $identify_vals->{'filesize'});
1390 $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingWidth", $identify_vals->{'width'});
1391 $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingHeight", $identify_vals->{'height'});
1392 $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingDuration", $identify_vals->{'durationDisplay'});
1393 $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingDurationMs", $identify_vals->{'duration'});
1394 $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingFPS", $identify_vals->{'fps'});
1395 $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingVideoCodec", $identify_vals->{'vcodec'});
1396 $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingVideoBitrate", $identify_vals->{'vrate'});
1397 $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingAudioCodec", $identify_vals->{'acodec'});
1398 $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingAudioBitrate", $identify_vals->{'arate'});
1399 $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingAudioTracks", $identify_vals->{'atracks'});
1400 $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingOverallBitrate", $identify_vals->{'orateDisplay'});
1401 $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingSubtitleType", $identify_vals->{'stype'});
1402 $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingSubtitleLanguage", $identify_vals->{'slang'});
1403
1404 my $get_max_metadata = $self->{'getMaximumMetadata'};
1405 if ($get_max_metadata eq "true"){
1406 my $metadata_table = $identify_vals->{'metadata_table'};
1407 foreach my $metaname(keys %$metadata_table)
1408 {
1409 my $metaval = $identify_vals->{'metadata_table'}->{$metaname};
1410 $doc_obj->add_utf8_metadata ($section, $metaname, $metaval);
1411 }
1412 }
1413
1414 #add the metadata for the resulting file that should be open inside the replayme video player
1415 $doc_obj->add_utf8_metadata ($section, "streamablevideo", $streamable_url_safe);
1416 $doc_obj->associate_file($ifilename,$ifile,"application/octet-stream",$section);
1417
1418 my $flashwidth = $video_width;
1419 my $flashheight = $video_height + 22;
1420
1421 if ($self->{'create_keyframes'} eq "true") {
1422 $flashheight += 100;
1423 }
1424
1425 $doc_obj->add_metadata ($section, "flashwidth", $flashwidth);
1426 $doc_obj->add_metadata ($section, "flashheight", $flashheight);
1427
1428 $self->{'oflash_file'} = $ifile;
1429 $self->{'oflash_filename'} = $ifilename;
1430
1431}
1432
1433
1434sub enable_h264_streaming
1435{
1436 my $self = shift (@_);
1437 my ($doc_obj,$originalfilename,$filename,$convertto_regenerated,
1438 $video_width,$video_height) = @_;
1439
1440
1441 my $section = $doc_obj->get_top_section();
1442
1443 my $output_dir = $self->{'cached_dir'};
1444 my $ivideo_root = $self->{'cached_file_root'};
1445
1446 # Generate the H264 video file format for streaming purposes using the cache feature if used
1447
1448 my $optionally_run_general_cmd = "run_uncached_general_cmd";
1449 if ($self->{'enable_cache'}) {
1450 $optionally_run_general_cmd
1451 = ($convertto_regenerated) ? "regenerate_general_cmd" : "run_cached_general_cmd";
1452 }
1453
1454 my $streaming_HQ_size = $self->{'streamingHQsize'};
1455 my $streaming_HQ_VideoBitrate = $self->{'streamingHQVideoBitrate'};
1456 my $streaming_HQ_AudioBitrate = $self->{'streamingHQAudioBitrate'};
1457
1458 my $ifilename = $originalfilename || $filename;
1459 my ($stream_cmd,$oflash_filename,$oflash_file)
1460 = $self->stream_mp4_video_cmd($ifilename,
1461 $streaming_HQ_size,
1462 $streaming_HQ_VideoBitrate,
1463 $streaming_HQ_AudioBitrate);
1464
1465
1466 my $streamable_options = { @{$self->{'handbrake_monitor'}},
1467 'message_prefix' => "MP4 Stream",
1468 'message' => "Generating streamable MP4 video: $oflash_file" };
1469
1470
1471 my ($streamable_regenerated,$streamable_result,$streamable_had_error)
1472 = $self->$optionally_run_general_cmd($stream_cmd,
1473 $ifilename,$oflash_filename,
1474 $streamable_options);
1475
1476 if (!$streamable_had_error) {
1477
1478
1479 my $streamable_url = $oflash_file;
1480 my $streamable_url_safe = $self->url_safe($streamable_url);
1481
1482 my $outhandle = $self->{'outhandle'};
1483
1484 my $identify_vals = identifyMI($oflash_filename,$outhandle,0);
1485
1486 #Add the most common metadata extracted by MediaInfo from the streamable video file
1487 $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingVideoFormat", $identify_vals->{'vtype'});
1488 $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingVideoFileSize", $identify_vals->{'filesizeDisplay'});
1489 $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingWidth", $identify_vals->{'width'});
1490 $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingHeight", $identify_vals->{'height'});
1491 $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingDuration", $identify_vals->{'durationDisplay'});
1492 $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingFPS", $identify_vals->{'fps'});
1493 $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingVideoCodec", $identify_vals->{'vcodec'});
1494 $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingVideoBitrate", $identify_vals->{'vrate'});
1495 $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingAudioCodec", $identify_vals->{'acodec'});
1496 $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingAudioBitrate", $identify_vals->{'arate'});
1497 $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingOverallBitrate", $identify_vals->{'orateDisplay'});
1498 $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingSubtitleType", $identify_vals->{'stype'});
1499 $doc_obj->add_utf8_metadata ($section, "tv.HQStreamingSubtitleLangue", $identify_vals->{'slang'});
1500
1501 my $get_max_metadata = $self->{'getMaximumMetadata'};
1502 if ($get_max_metadata eq "true"){
1503 my $metadata_table = $identify_vals->{'metadata_table'};
1504 foreach my $metaname(keys %$metadata_table)
1505 {
1506 my $metaval = $identify_vals->{'metadata_table'}->{$metaname};
1507 $doc_obj->add_utf8_metadata ($section, $metaname, $metaval);
1508 }
1509 }
1510
1511 #add the metadata for the resulting file that should be open inside the flash video player
1512 $doc_obj->add_utf8_metadata ($section, "streamablevideo", $streamable_url_safe);
1513 $doc_obj->associate_file($oflash_filename,$oflash_file,"video/mp4",
1514 $section);
1515 }
1516
1517 my $flashwidth = $video_width;
1518 my $flashheight = $video_height + 22;
1519
1520 if ($self->{'create_keyframes'} eq "true") {
1521 $flashheight += 100;
1522 }
1523
1524 $doc_obj->add_metadata ($section, "flashwidth", $flashwidth);
1525 $doc_obj->add_metadata ($section, "flashheight", $flashheight);
1526
1527 $self->{'oflash_file'} = $oflash_file;
1528 $self->{'oflash_filename'} = $oflash_filename;
1529
1530 return $streamable_regenerated;
1531
1532
1533}
1534
1535sub enable_full_streaming
1536{
1537 my $self = shift (@_);
1538 my ($doc_obj,$originalfilename,$filename,$convertto_regenerated,
1539 $video_width,$video_height) = @_;
1540
1541 my $video_streamable_regenerated
1542 = $self->enable_video_streaming($doc_obj,$originalfilename,$filename,
1543 $convertto_regenerated,
1544 $video_width,$video_height);
1545
1546 my $audio_streamable_regenerated
1547 = $self->enable_audio_streaming($doc_obj,$originalfilename,$filename,
1548 $convertto_regenerated);
1549
1550 return ($video_streamable_regenerated || $audio_streamable_regenerated);
1551}
1552
1553
1554
1555sub flvtool2_monitor_line
1556{
1557 my ($line) = @_;
1558
1559 my $had_error = 0;
1560 my $generate_dot = 1;
1561
1562 if ($line =~ m/\s+\- /) {
1563 # ignore tabulated output printed at end of command
1564 $generate_dot = 0;
1565 }
1566
1567 if ($line =~ m/^Error:/i) {
1568 $had_error = 1;
1569 }
1570
1571 return ($had_error,$generate_dot);
1572}
1573
1574
1575
1576
1577
15781;
Note: See TracBrowser for help on using the repository browser.