source: extensions/gsdl-video/trunk/perllib/plugins/VideoConverter.pm@ 19785

Last change on this file since 19785 was 19785, checked in by davidb, 15 years ago

General improvements to processing audio and for TIMEDHTMLPLugin to process files that have been edited by OpenOffice

File size: 19.1 KB
RevLine 
[18556]1###########################################################################
2#
3# VideoConverter - helper plugin that does video (and audio) conversion using ffmpeg
4#
5# A component of the Greenstone digital library software
6# from the New Zealand Digital Library Project at the
7# University of Waikato, New Zealand.
8#
9# Copyright (C) 2008 New Zealand Digital Library Project
10#
11# This program is free software; you can redistribute it and/or modify
12# it under the terms of the GNU General Public License as published by
13# the Free Software Foundation; either version 2 of the License, or
14# (at your option) any later version.
15#
16# This program is distributed in the hope that it will be useful,
17# but WITHOUT ANY WARRANTY; without even the implied warranty of
18# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19# GNU General Public License for more details.
20#
21# You should have received a copy of the GNU General Public License
22# along with this program; if not, write to the Free Software
23# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24#
25###########################################################################
26package VideoConverter;
27
28use MultimediaConverter;
29
30
31use strict;
32no strict 'refs'; # allow filehandles to be variables and viceversa
33
34use gsprintf 'gsprintf';
35
36BEGIN {
37 @VideoConverter::ISA = ('MultimediaConverter');
38}
39
40
41my @thumb_arguments = (
42 { 'name' => "create_thumbnail",
43 'desc' => "{ImageConverter.create_thumbnail}",
44 'type' => "enum",
45 'list' => [{'name' => "true", 'desc' => "{common.true}"},
46 {'name' => "false", 'desc' => "{common.false}"}],
47 'deft' => "true",
48 'reqd' => "no" },
49 { 'name' => "thumbnailsize",
50 'desc' => "{ImageConverter.thumbnailsize}",
51 'type' => "int",
52 'deft' => "100",
53 'range' => "1,",
54 'reqd' => "no" },
55 { 'name' => "thumbnailtype",
56 'desc' => "{ImageConverter.thumbnailtype}",
57 'type' => "string",
58 'deft' => "jpg",
59 'reqd' => "no" },
60 { 'name' => "noscaleup",
61 'desc' => "{ImageConverter.noscaleup}",
62 'type' => "flag",
63 'reqd' => "no" } );
64
65
66my @screenview_arguments = (
67 { 'name' => "create_screenview",
68 'desc' => "{ImageConverter.create_screenview}",
69 'type' => "enum",
70 'list' => [{'name' => "true", 'desc' => "{common.true}"},
71 {'name' => "false", 'desc' => "{common.false}"}],
72 'deft' => "true",
73 'reqd' => "no" },
74 { 'name' => "screenviewsize",
75 'desc' => "{ImageConverter.screenviewsize}",
76 'type' => "int",
77 'deft' => "352",
78 'range' => "1,",
79 'reqd' => "no" },
80 { 'name' => "screenviewtype",
81 'desc' => "{ImageConverter.screenviewtype}",
82 'type' => "string",
83 'deft' => "jpg",
84 'reqd' => "no" } );
85
86my $arguments = [
87 @thumb_arguments,
88 @screenview_arguments,
89
90 { 'name' => "converttotype",
91 'desc' => "{ImageConverter.converttotype}",
92 'type' => "string",
93 'deft' => "",
94 'reqd' => "no" },
95
96
97 { 'name' => "converttosize",
98 'desc' => "{VideoPlugin.converttosize}",
99 'type' => "int",
100 'deft' => "",
101## 'deft' => "352",
102 'reqd' => "no" },
103 { 'name' => "converttobitrate",
104 'desc' => "{VideoPlugin.converttobitrate}",
105 'type' => "string",
106 'deft' => "200k",
107 'reqd' => "no" },
108 { 'name' => "extractkeyframes",
109 'desc' => "{VideoPlugin.extractkeyframes}",
110 'type' => "flag",
111 'deft' => "0",
112 'reqd' => "no" },
113 { 'name' => "streamingsize",
114 'desc' => "{VideoPlugin.streamingsize}",
115 'type' => "int",
116 'deft' => "352",
117 'reqd' => "no" },
118 { 'name' => "streamingbitrate",
119 'desc' => "{VideoPlugin.streamingbitrate}",
120 'type' => "string",
121 'deft' => "200k",
122 'reqd' => "no" },
123
124 { 'name' => "minimumsize",
125 'desc' => "{ImageConverter.minimumsize}",
126 'type' => "int",
127 'deft' => "100",
128 'range' => "1,",
129 'reqd' => "no" },
130
131 ];
132
133my $options = { 'name' => "VideoConverter",
134 'desc' => "{VideoConverter.desc}",
135 'abstract' => "yes",
136 'inherits' => "yes",
137 'args' => $arguments };
138
139sub new {
140 my ($class) = shift (@_);
141 my ($pluginlist,$inputargs,$hashArgOptLists) = @_;
142 push(@$pluginlist, $class);
143
144 push(@{$hashArgOptLists->{"ArgList"}},@{$arguments});
145 push(@{$hashArgOptLists->{"OptList"}},$options);
146
147 my $self = new MultimediaConverter($pluginlist, $inputargs, $hashArgOptLists, 1);
148
149
150 return bless $self, $class;
151
152}
153
154
155
156
157# Discover the characteristics of a video file.
158
159# Equivalent step to that in ImagePlugin that uses ImageMagicks's
160# 'indentify' utility
161
162# Here we use 'ffmpeg' for video but for consistency keep the Perl
163# method name the same as before
164
165
166sub identify {
167 my ($video, $outhandle, $verbosity) = @_;
168
169 # Use the ffmpeg command to get the file specs
170 my $command = "ffmpeg -i \"$video\"";
171
172 print $outhandle " $command\n" if ($verbosity > 2);
173 my $result = '';
174 $result = `$command 2>&1`;
175 print $outhandle " $result\n" if ($verbosity > 4);
176
177 # Read the type, width, and height etc.
178 my $vtype = 'unknown';
179 my $vcodec = 'unknown';
180 my $width = 'unknown';
181 my $height = 'unknown';
182 my $fps = 'unknown';
183
184 my $atype = 'unknown';
185 my $afreq = 'unknown';
186 my $achan = 'unknown';
187 my $arate = 'unknown';
188
189 my $video_safe = quotemeta $video;
190
191 # strip off everything up to filename
192 $result =~ s/^.*\'$video_safe\'://s;
193
[18982]194## if ($result =~ m/Video: (.*?) fps/m) {
195 if ($result =~ m/^\s+Stream.*?Video: (.*?)$/m) {
[18556]196 my $video_info = $1;
[18982]197 $video_info =~ s/\s*\[.*?\]//g;
198
199 my @video_fields = split(/,\s*/,$video_info);
200
201 $vtype = shift @video_fields;
202
203 if ($video_fields[0] !~ m/(\d+)x(\d+)/) {
204 $vcodec = shift @video_fields;
[18556]205 }
[18982]206 my $video_dim = shift @video_fields;
207
208 ($width,$height) = ($video_dim =~ m/(\d+)x(\d+)/);
209
210# if ($video_info =~ m/([^,]+),(?: ([^,]+),)? (\d+)x(\d+),.*?(\d+\.\d+)/)
211# {
212# $vtype = $1;
213# $vcodec = $2 if defined $2;
214# $width = $3;
215# $height = $4;
216# $fps = $5;
217# }
[18556]218 }
219
220 if ($result =~ m/Audio: (\w+), (\d+) Hz, (\w+)(?:, (\d+.*))?/m) {
221 $atype = $1;
222 $afreq = $2;
223 $achan = $3;
224 $arate = $4 if (defined $4);
225 }
226
227 # Read the duration
228 my $duration = "unknown";
229 if ($result =~ m/Duration: (\d+:\d+:\d+\.\d+)/m) {
230 $duration = $1;
231 }
232 print $outhandle " file: $video:\t $vtype, $width, $height, $duration\n"
233 if ($verbosity > 2);
234
235 if ($verbosity >3) {
236 print $outhandle "\t video codec=$vcodec, fps = $fps\n";
237 print $outhandle "\t audio codec=$atype, freq = $afreq Hz, $achan, $arate\n";
238 }
239
240 # Return the specs
241 return ($vtype, $width, $height, $duration, -s $video,
242 $vcodec,$fps,$atype,$afreq,$achan,$arate);
243}
244
245
246sub vob_durations
247{
248 my ($media_base_dir,$title_num,$outhandle) = @_;
249
250 my $filter_re = sprintf("^VTS_%02d_[1-9]\\.VOB\$",$title_num);
251
252 my $duration_info = {};
253
254 if (opendir(VTSIN,$media_base_dir)) {
255 my @vts_title_vobs = grep { $_ =~ m/$filter_re/ } readdir(VTSIN);
256 closedir(VTSIN);
257
258 foreach my $v (@vts_title_vobs) {
259 my $full_v = &util::filename_cat($media_base_dir,$v);
260
261 my ($vtype, $width, $height, $duration, $vsize,
262 $vcodec,$fps,$atype,$afreq,$achan,$arate) = identify($full_v,$outhandle,0);
263
264 my ($vob_num) = ($v =~ m/^VTS_\d\d_(\d)\.VOB$/);
265
266 $duration_info->{$vob_num} = $duration;
267 print STDERR "**** $title_num: $title_num, storing {$vob_num} => $duration\n";
268
269 }
270
271 }
272 else {
273 print $outhandle "Warning: unable to read files in directory $media_base_dir\n";
274 }
275
276 return $duration_info;
277
278}
279
280
281
282sub init_cache_for_file {
283 my $self = shift(@_);
284
285 my ($video_filename) = @_;
286
287 $self->SUPER::init_cache_for_file($video_filename);
288
289
290 my @flvtool2_monitor = ( 'monitor_init' ,"convertutil::monitor_init_unbuffered",
291 'monitor_line' , "VideoConverter::flvtool2_monitor_line",
292 'monitor_deinit' , "convertutil::monitor_deinit_unbuffered" );
293
294 $self->{'flvtool2_monitor'} = \@flvtool2_monitor;
295
296
297}
298
299
300
301sub optional_frame_scale
302{
303 my $self = shift (@_);
304 my ($orig_size,$video_width,$video_height) = @_;
305
306 my $s_opt = "";
[19785]307
[18556]308 if ($video_width > $video_height) {
309 if ($video_width > $orig_size) {
310 my $scale_factor = $orig_size/$video_width;
311 my $scaled_width = int($video_width * $scale_factor);
312 my $scaled_height = int($video_height * $scale_factor);
313
314 # round to be ensure multiple of 2 (needed by some codecs)
315 $scaled_width = int($scaled_width/2)*2;
316 $scaled_height = int($scaled_height/2)*2;
317
318 $s_opt = "-s ${scaled_width}x${scaled_height}";
319 }
320 # else, video is smaller than requested size, don't scale up
321 }
322 else {
323 if ($video_height > $orig_size) {
324 my $scale_factor = $orig_size/$video_height;
325 my $scaled_width = int($video_width * $scale_factor);
326 my $scaled_height = int($video_height * $scale_factor);
327
328 # round to be ensure multiple of 2 (needed by some codecs)
329 $scaled_width = int($scaled_width/2)*2;
330 $scaled_height = int($scaled_height/2)*2;
331
332 $s_opt = "-s ${scaled_width}x${scaled_height}";
333 }
334 # else, video is smaller than requested size, don't scale up
335
336 }
337
338 return $s_opt;
339}
340
341
342sub keyframe_cmd
343{
344 my $self = shift (@_);
345 my ($ivideo_filename) = @_;
346
347 my $video_ext_dir = &util::filename_cat($ENV{'GSDLHOME'},"ext","video");
348
349 my $output_dir = $self->{'cached_dir'};
350 my $ivideo_root = $self->{'cached_file_root'};
351
352 my $oshot_filename = &util::filename_cat($output_dir,"shots.xml");
353
354 my $exp_duration = $self->{'exp_duration'};
355 my $t_opt = (defined $exp_duration) ? "-t $exp_duration" : "";
356
357 my $main_opts = "-y $t_opt";
358
359 my $hive = &util::filename_cat($video_ext_dir,"lib","vhook","hive.so");
360
361 my $oflash_filename = &util::filename_cat($output_dir,"$ivideo_root\_keyframe.flv");
362
363 my $vhook_opts = "$hive -o $oshot_filename -k $output_dir $ivideo_filename";
364
365 my $ivideo_filename_gsdlenv = $self->gsdlhome_independent($ivideo_filename);
366 my $oflash_filename_gsdlenv = $self->gsdlhome_independent($oflash_filename);
367
368 my $ffmpeg_cmd = "ffkeyframe $main_opts -vhook \"$vhook_opts\" -i \"$ivideo_filename_gsdlenv\" -an -y \"$oflash_filename_gsdlenv\"";
369
370
371 return ($ffmpeg_cmd,$oflash_filename);
372}
373
374
375sub stream_cmd
376{
377 my $self = shift (@_);
378 my ($ivideo_filename,$video_width,$video_height,
379 $streaming_quality,
380 $streaming_bitrate,$streaming_size,
381 $opt_streaming_achan, $opt_streaming_arate) = @_;
382
383 my $streaming_achan
384 = (defined $opt_streaming_achan) ? $opt_streaming_achan : 2;
385
386 my $streaming_arate
387 = (defined $opt_streaming_arate) ? $opt_streaming_arate : 22050;
388
389 my $output_dir = $self->{'cached_dir'};
390 my $ivideo_root = $self->{'cached_file_root'};
391
392 my $oflash_file = "${ivideo_root}_stream.flv";
393 my $oflash_filename = &util::filename_cat($output_dir,$oflash_file);
394
[19785]395 my $s_opt = "";
396 if ($self->{'media_type'} ne "audio") {
397 $s_opt = $self->optional_frame_scale($streaming_size,$video_width,$video_height);
398 }
[18556]399
[19785]400
[18556]401 my $exp_duration = $self->{'exp_duration'};
402 my $t_opt = (defined $exp_duration) ? "-t $exp_duration" : "";
403
404 my $main_opts = "-y $t_opt";
405
406 my $bitrate_opt = "-b $streaming_bitrate";
407 ### my $stream_opts = "-r 25 $s_opt";
408 my $stream_opts .= " $s_opt -ac $streaming_achan -ar $streaming_arate";
409
410 # -flags +ilme+ildct' and maybe '-flags +alt' for interlaced material, and try '-top 0/1'
411
412 my $all_opts = "$main_opts $stream_opts";
413
414 my $ffmpeg_cmd;
415
416 my $ivideo_filename_gsdlenv = $self->gsdlhome_independent($ivideo_filename);
417 my $oflash_filename_gsdlenv = $self->gsdlhome_independent($oflash_filename);
418
419 if ($streaming_quality eq "high") {
420
421 my $pass_log_file = &util::filename_cat($output_dir,"$ivideo_root-logpass.txt");
422 if (-e $pass_log_file) {
423 &util::rm($pass_log_file);
424 }
425
426 my $pass_log_file_gsdlenv = $self->gsdlhome_independent($pass_log_file);
427
428 $all_opts .= " -passlogfile \"$pass_log_file_gsdlenv\"";
429
430 my $ffmpeg_cmd_pass1 = "ffmpeg -pass 1 -i \"$ivideo_filename_gsdlenv\" $all_opts -y \"$oflash_filename_gsdlenv\"";
431
432 my $ffmpeg_cmd_pass2 = "ffmpeg -pass 2 -i \"$ivideo_filename_gsdlenv\" $all_opts $bitrate_opt -y \"$oflash_filename_gsdlenv\"";
[18995]433 $ffmpeg_cmd = "( $ffmpeg_cmd_pass1 && $ffmpeg_cmd_pass2 )";
[18556]434 }
435 else {
436 # single pass
437
438 $ffmpeg_cmd = "ffmpeg -i \"$ivideo_filename_gsdlenv\" $all_opts -y \"$oflash_filename_gsdlenv\"";
439 }
440
441 return ($ffmpeg_cmd,$oflash_filename,$oflash_file);
442}
443
444
445
446sub audio_excerpt_cmd
447{
448 my $self = shift (@_);
449 my ($ivoa_filename,$hh,$mm,$ss,$opt_excerpt_len) = @_;
450
451 # ivoa = input video or audio
452
453 my $time_encoded = "$hh:$mm:$ss";
454 my $time_encoded_file = "$hh$mm$ss";
455
456
457 my $output_dir = $self->{'cached_dir'};
458 my $ivoa_root = $self->{'cached_file_root'};
459
460 my $omp3_file = "${ivoa_root}_$time_encoded_file.mp3";
461 my $omp3_filename = &util::filename_cat($output_dir,$omp3_file);
462
463 my $all_opts = "-y -acodec mp3 -ss $time_encoded ";
464
465 if (defined $opt_excerpt_len) {
466 $all_opts .= "-t $opt_excerpt_len ";
467 }
468
469
470 my $ivoa_filename_gsdlenv = $self->gsdlhome_independent($ivoa_filename);
471 my $omp3_filename_gsdlenv = $self->gsdlhome_independent($omp3_filename);
472
473
474 my $ffmpeg_cmd = "ffmpeg -i \"$ivoa_filename_gsdlenv\" $all_opts \"$omp3_filename_gsdlenv\"";
475
476 return ($ffmpeg_cmd,$omp3_filename,$omp3_file);
477}
478
479
480
481sub streamseekable_cmd
482{
483 my $self = shift (@_);
484 my ($oflash_filename) = @_;
485
486 my $output_dir = $self->{'cached_dir'};
487 my $ivideo_root = $self->{'cached_file_root'};
488
489 my $cue_filename = &util::filename_cat($output_dir,"on_cue.xml");
490
491 my $flvtool_cmd = "flvtool2 -vUP \"$oflash_filename\"";
492
493 return ($flvtool_cmd,$oflash_filename);
494}
495
496
497sub streamkeyframes_cmd
498{
499 my $self = shift (@_);
500 my ($oflash_filename,$doc_obj,$section) = @_;
501
502 my $assocfilepath
503 = $doc_obj->get_metadata_element($section,"assocfilepath");
504
505 my $output_dir = $self->{'cached_dir'};
506
507 my $cue_filename = &util::filename_cat($output_dir,"on_cue.xml");
508
509 my $video_server = $ENV{'GEXT_VIDEO_SERVER'};
510 my $video_prefix = $ENV{'GEXT_VIDEO_PREFIX'};
511
512 my $collect = $ENV{'GSDLCOLLECTION'};
513
[18982]514 print STDERR "**** Warning: need to remove dependency of GEXT_VIDEO_SERVER and _PREFIX\n";
515
[18556]516 my $flvtool_cmd = "flvtool2 -vAUtP \"$cue_filename\" -thumbLocation:$video_server$video_prefix/collect/$collect/index/assoc/$assocfilepath \"$oflash_filename\"";
517
518
519 return ($flvtool_cmd,$oflash_filename);
520}
521
522
523sub streamcuepts_cmd
524{
525 my $self = shift (@_);
526 my ($oflash_filename) = @_;
527
528 my $output_dir = $self->{'cached_dir'};
529
530 my $cue_filename = &util::filename_cat($output_dir,"on_cue.xml");
531
532 my $video_server = $ENV{'GEXT_VIDEO_SERVER'};
533 my $video_prefix = $ENV{'GEXT_VIDEO_PREFIX'};
534
535 my $collect = $ENV{'GSDLCOLLECTION'};
536
[19785]537 ## my $thumbloc = "$video_server$video_prefix/collect/$collect";
[18556]538
[19785]539
[18556]540# my $flvtool_cmd = "flvtool2 -vUAtP \"$cue_filename\" -thumbLocation:$thumbloc \"$oflash_filename\"";
541
542# my $flvtool_cmd = "flvtool2 -vUAt \"$cue_filename\" \"$oflash_filename\"";
543
544
545
546# my $flvtool_cmd = "flvtool2 -vUAt \"$cue_filename\" \"$oflash_filename\"";
547
548
549## my $flvtool_cmd = "flvtool2 -vAt \"$cue_filename\" -UP \"$oflash_filename\" \"$output_dir/updated.flv\"";
550
551## my $flvtool_cmd = "flvtool2 -vAtU \"$cue_filename\" \"$oflash_filename\" \"$output_dir/updated.flv\"";
552
553 my $flvtool_cmd = "flvtool2 -vAtUP \"$cue_filename\" \"$oflash_filename\"";
554
555 return ($flvtool_cmd,$oflash_filename);
556}
557
558
559sub keyframe_thumbnail_cmd
560{
561 my $self = shift (@_);
562 my ($ivideo_filename,$thumbnailfile,$thumbnailwidth,$thumbnailheight) = @_;
563
564 my $output_dir = $self->{'cached_dir'};
565 my $ivideo_root = $self->{'cached_file_root'};
566
567 my $key_filename_prefix = &util::filename_cat($output_dir,$ivideo_root);
568
569
570 # Try for 4th keyframe, but fall back to 1st if doesn't exist
571 my $key_filename = "${key_filename_prefix}_0003.jpg";
572 $key_filename = "${key_filename_prefix}_0000.jpg" if (!-e $key_filename);
573
574 my $key_filename_gsdlenv = $self->gsdlhome_independent($key_filename);
575 my $thumbnailfile_gsdlenv = $self->gsdlhome_independent($thumbnailfile);
576
577 my $command;
578
579 if (-e $key_filename) {
580 $command = "convert -interlace plane -verbose -geometry $thumbnailwidth"
581 . "x$thumbnailheight \"$key_filename_gsdlenv\" \"$thumbnailfile_gsdlenv\"";
582 }
583 else {
584 # extractkeyframe has either not been switched on, or else had
585 # a problem when running
586 # => extract a from
587 # my $frame_rate = 1.0 / 60.0;
588
589 my $ivideo_filename_gsdlenv = $self->gsdlhome_independent($ivideo_filename);
590
591
592
593 $command = "ffmpeg -i \"$ivideo_filename_gsdlenv\" -ss 12.5 -vframes 1 -f image2 -s ${thumbnailwidth}x${thumbnailheight} -y \"$thumbnailfile_gsdlenv\"";
594
595 # fmpeg -i input.dv -r 1 -f image2 -s 120x96 images%05d.png
596 }
597
598 return ($command,$thumbnailfile);
599}
600
601
602sub keyframe_montage_cmd
603{
604 my $self = shift (@_);
605 my ($ivideo_filename,$montagefile) = @_;
606
607 my $output_dir = $self->{'cached_dir'};
608 my $ivideo_root = $self->{'cached_file_root'};
609
610 my $key_filename_prefix = &util::filename_cat($output_dir,$ivideo_root);
611
612 my $options = "-tile 10 -geometry 75x62+2+2";
613
614 my $command = "montage $options ${key_filename_prefix}_*.jpg \"$montagefile\"";
615
616 return ($command,$montagefile);
617}
618
619
620
621sub parse_shot_xml
622{
623 my ($self) = shift(@_);
624
625 my $outhandle = $self->{'outhandle'};
626 my $output_dir = $self->{'cached_dir'};
627
628 my $shots_filename = &util::filename_cat($output_dir,"shots.xml");
629
630 eval {
631 $self->{'parser'}->parsefile($shots_filename);
632 };
633
634 if ($@) {
635 print $outhandle "VideoConverter: skipping $shots_filename as not conformant to Hive shot syntax\n" if ($self->{'verbosity'} > 1);
636 print $outhandle "\n Perl Error:\n $@\n" if ($self->{'verbosity'}>2);
637 return 0;
638 }
639
640}
641
642sub associate_keyframes_old
643{
644 my ($self) = shift(@_);
645
646 my ($doc_obj,$section) = @_;
647
648 my $output_dir = $self->{'cached_dir'};
649
650 my $count = 1;
651 foreach my $kframe_file (@{$self->{'keyframe_fnames'}}) {
652
653 my $kframe_filename = &util::filename_cat($output_dir,$kframe_file);
654 $doc_obj->associate_file($kframe_filename,"keyframe$count.jpg","image/jpeg",
655 $section);
656 $count++;
657 }
658
659 $doc_obj->add_utf8_metadata($section,"NumKeyframes",scalar(@{$self->{'keyframe_fnames'}}));
660
661
662 # *****
663 # $doc_obj->add_metadata ($section, "thumblist", $self->{'flowplayer_thumblist'});
664
665}
666
667sub associate_keyframes
668{
669 my ($self) = shift(@_);
670
671 my ($doc_obj,$section) = @_;
672
673 my $output_dir = $self->{'cached_dir'};
674 my $timeline = $self->{'keyframe_timeline'};
675
676 my $count = 1;
677
678 foreach my $t (sort { $timeline->{$a}->{'keyframeindex'} <=> $timeline->{$b}->{'keyframeindex'} } keys %$timeline)
679 {
680 my $kframe_file = $timeline->{$t}->{'thumb'};
681 my $timestamp = $timeline->{$t}->{'timestamp'};
682
683 my $kframe_filename = &util::filename_cat($output_dir,$kframe_file);
684 $doc_obj->associate_file($kframe_filename,"keyframe$count.jpg","image/jpeg",
685 $section);
686 $doc_obj->add_utf8_metadata($section,"KeyframeTimestamp",$timestamp);
687
688 $count++;
689 }
690
691 $doc_obj->add_utf8_metadata($section,"NumKeyframes",scalar(@{$self->{'keyframe_fnames'}}));
692
693
694 # *****
695 # $doc_obj->add_metadata ($section, "thumblist", $self->{'flowplayer_thumblist'});
696}
697
698
699
700sub flvtool2_monitor_line
701{
702 my ($line) = @_;
703
704 my $had_error = 0;
705 my $generate_dot = 1;
706
707 if ($line =~ m/\s+\- /) {
708 # ignore tabulated output printed at end of command
709 $generate_dot = 0;
710 }
711
712 if ($line =~ m/^Error:/i) {
713 $had_error = 1;
714 }
715
716 return ($had_error,$generate_dot);
717}
718
719
720
721
722
7231;
Note: See TracBrowser for help on using the repository browser.