Changeset 18556 for extensions

Show
Ignore:
Timestamp:
20.02.2009 11:40:23 (11 years ago)
Author:
davidb
Message:

Restructing of VideoPlugin? to be like ImagePlugin? (with its supporting ImageConverter? plugin). The pattern was then repeated for Audio, so we now have an AudioPlugin? and AudioConverter?. Where possible code is shared in a Multimedia base class

Location:
extensions/gsdl-video/trunk/perllib/plugins
Files:
4 added
2 modified

Legend:

Unmodified
Added
Removed
  • extensions/gsdl-video/trunk/perllib/plugins/MultimediaConverter.pm

    r18543 r18556  
    1 ########################################################################## 
    2 # 
    3 # videoconvert.pm -- utility to help convert video file 
    4 # 
    5 # Copyright (C) 1999 DigiLib Systems Limited, NZ 
     1########################################################################### 
     2# 
     3# MultimediaConverter - helper plugin that does audio and video 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 
    610# 
    711# This program is free software; you can redistribute it and/or modify 
     
    2024# 
    2125########################################################################### 
    22  
    23  
    24 package videoconvert; 
     26package MultimediaConverter; 
     27 
     28use BaseMediaConverter; 
     29 
    2530 
    2631use strict; 
    2732no strict 'refs'; # allow filehandles to be variables and viceversa 
    2833 
    29 use baseconvert; 
    30  
    31 sub BEGIN { 
    32     @videoconvert::ISA = ('baseconvert'); 
    33 } 
    34  
    35  
    36 # Discover the characteristics of a video file. 
    37 #  Equivalent step to that in ImagePlugin that uses ImageMagicks's 'indentify' utility 
    38 #  Here we use 'ffmpeg' for video but for consistency keep the Perl method name the same  
    39 #  as before 
     34use gsprintf 'gsprintf'; 
     35 
     36BEGIN { 
     37    @MultimediaConverter::ISA = ('BaseMediaConverter'); 
     38} 
     39 
     40my $arguments = [ 
     41      { 'name' => "converttotype", 
     42    'desc' => "{ImageConverter.converttotype}", 
     43    'type' => "string", 
     44    'deft' => "", 
     45    'reqd' => "no" }, 
     46      { 'name' => "minimumsize", 
     47    'desc' => "{ImageConverter.minimumsize}", 
     48    'type' => "int", 
     49    'deft' => "100", 
     50    'range' => "1,", 
     51    'reqd' => "no" }, 
     52         ]; 
     53 
     54my $options = { 'name' => "MultimediaConverter", 
     55        'desc' => "{MultimediaConverter.desc}", 
     56        'abstract' => "yes", 
     57        'inherits' => "yes", 
     58        'args' => $arguments }; 
     59 
     60sub new { 
     61    my ($class) = shift (@_); 
     62    my ($pluginlist,$inputargs,$hashArgOptLists) = @_; 
     63    push(@$pluginlist, $class); 
     64 
     65    push(@{$hashArgOptLists->{"ArgList"}},@{$arguments}); 
     66    push(@{$hashArgOptLists->{"OptList"}},$options); 
     67 
     68    my $self = new BaseMediaConverter($pluginlist, $inputargs, $hashArgOptLists, 1); 
     69 
     70 
     71    return bless $self, $class; 
     72 
     73} 
     74 
     75 
     76 
     77# needs to be called after BasePlugin init, so that outhandle is set up. 
     78sub init { 
     79    my $self = shift(@_); 
     80 
     81    my $outhandle = $self->{'outhandle'}; 
     82 
     83    $self->{'tmp_file_paths'} = (); 
     84 
     85    # Check that ffmpeg is installed and available on the path  
     86    my $multimedia_conversion_available = 1; 
     87    my $no_multimedia_conversion_reason = ""; 
     88 
     89    # None of this works very well on Windows 95/98... 
     90    if ($ENV{'GSDLOS'} eq "windows" && !Win32::IsWinNT()) { 
     91    $multimedia_conversion_available = 0; 
     92    $no_multimedia_conversion_reason = "win95notsupported"; 
     93    } else { 
     94 
     95    my $result = `ffmpeg -h 2>&1`; 
     96 
     97 
     98    if (!defined $result || $result !~ m/^FFmpeg version/) { 
     99        $self->{'ffmpeg_installed'} = 0; 
     100        print $outhandle $result; 
     101        $multimedia_conversion_available = 0; 
     102        $no_multimedia_conversion_reason = "ffmpegnotinstalled"; 
     103    } 
     104    else { 
     105        $self->{'ffmpeg_installed'} = 1; 
     106    } 
     107    } 
     108 
     109    $self->{'multimedia_conversion_available'} = $multimedia_conversion_available; 
     110    $self->{'no_multimedia_conversion_reason'} = $no_multimedia_conversion_reason; 
     111 
     112    if ($self->{'multimedia_conversion_available'} == 0) { 
     113    &gsprintf($outhandle, "MultimediaConverter: {MultimediaConverter.noconversionavailable} ({MultimediaConverter.".$self->{'no_multimedia_conversion_reason'}."})\n"); 
     114    } 
     115        
     116} 
     117     
     118 
    40119 
    41120 
    42121sub identify {  
    43     my ($video, $outhandle, $verbosity) = @_; 
    44  
    45     # Use the ffmpeg command to get the file specs 
    46     my $command = "ffmpeg -i \"$video\""; 
    47  
    48     print $outhandle "  $command\n" if ($verbosity > 2); 
    49     my $result = ''; 
    50     $result = `$command 2>&1`; 
    51     print $outhandle "  $result\n" if ($verbosity > 4); 
    52  
    53     # Read the type, width, and height etc. 
    54     my $vtype =  'unknown'; 
    55     my $vcodec = 'unknown'; 
    56     my $width =  'unknown'; 
    57     my $height = 'unknown'; 
    58     my $fps    = 'unknown'; 
    59  
    60     my $atype =  'unknown'; 
    61     my $afreq =  'unknown'; 
    62     my $achan =  'unknown'; 
    63     my $arate =  'unknown'; 
    64  
    65     my $video_safe = quotemeta $video; 
    66  
    67     # strip off everything up to filename 
    68     $result =~ s/^.*\'$video_safe\'://s; 
    69  
    70     if ($result =~ m/Video: (.*?) fps/m) { 
    71     my $video_info = $1; 
    72     if ($video_info =~ m/([^,]+),(?: ([^,]+),)? (\d+)x(\d+),.*?(\d+\.\d+)/) 
    73     { 
    74         $vtype = $1; 
    75         $vcodec = $2 if defined $2; 
    76         $width = $3; 
    77         $height = $4; 
    78         $fps = $5; 
    79     } 
    80     } 
    81  
    82 ##    if ($result =~ m/Video: (\w+), (\w+), (\d+)x(\d+),.*?(\d+\.\d+) fps/m) { 
    83 #    if ($result =~ m/Video: ([^,]+),(?: ([^,]+),)? (\d+)x(\d+),.*?(\d+\.\d+) fps/m) { 
    84 #   $vtype = $1; 
    85 #   $vcodec = $2; 
    86 #   $width = $3; 
    87 #   $height = $4; 
    88 #   $fps = $5; 
    89 #    } 
    90  
    91     if ($result =~ m/Audio: (\w+), (\d+) Hz, (\w+)(?:, (\d+.*))?/m) { 
    92     $atype = $1; 
    93     $afreq = $2; 
    94     $achan = $3; 
    95     $arate = $4 if (defined $4); 
    96     } 
    97  
    98     # Read the duration 
    99     my $duration = "unknown"; 
    100     if ($result =~ m/Duration: (\d+:\d+:\d+\.\d+)/m) { 
    101     $duration = $1; 
    102     } 
    103     print $outhandle "  file: $video:\t $vtype, $width, $height, $duration\n"  
    104     if ($verbosity > 2); 
    105  
    106     if ($verbosity >3) { 
    107     print $outhandle "\t video codec=$vcodec, fps = $fps\n"; 
    108     print $outhandle "\t audio codec=$atype, freq = $afreq Hz, $achan, $arate\n"; 
    109     } 
    110  
    111     # Return the specs 
    112     return ($vtype, $width, $height, $duration, -s $video, 
    113         $vcodec,$fps,$atype,$afreq,$achan,$arate); 
    114 } 
    115  
    116  
    117 sub vob_durations 
    118 { 
    119     my ($media_base_dir,$title_num,$outhandle) = @_; 
    120  
    121     my $filter_re = sprintf("^VTS_%02d_[1-9]\\.VOB\$",$title_num); 
    122  
    123     my $duration_info = {}; 
    124  
    125     if (opendir(VTSIN,$media_base_dir)) { 
    126     my @vts_title_vobs = grep { $_ =~ m/$filter_re/ } readdir(VTSIN); 
    127     closedir(VTSIN); 
    128  
    129     foreach my $v (@vts_title_vobs) { 
    130         my $full_v = &util::filename_cat($media_base_dir,$v); 
    131  
    132         my ($vtype, $width, $height, $duration, $vsize, 
    133         $vcodec,$fps,$atype,$afreq,$achan,$arate) = identify($full_v,$outhandle,0); 
    134  
    135         my ($vob_num) = ($v =~ m/^VTS_\d\d_(\d)\.VOB$/); 
    136  
    137         $duration_info->{$vob_num} = $duration; 
    138         print STDERR "**** $title_num: $title_num, storing {$vob_num} => $duration\n"; 
    139  
    140     } 
    141  
    142     } 
    143     else { 
    144     print $outhandle "Warning: unable to read files in directory $media_base_dir\n"; 
    145     } 
    146      
    147     return $duration_info; 
    148  
    149 } 
    150  
    151  
    152  
    153 sub new { 
    154     my ($class) = shift @_; 
    155  
    156     my ($base_dir,$video_filename, $verbosity,$outhandle, 
    157     $exp_duration,$ascii_only_filenames) = @_; 
    158  
    159     my $self = new baseconvert($base_dir,$video_filename,$verbosity,$outhandle); 
    160  
    161     $self->{'exp_duration'} = $exp_duration; 
     122    my ($filename, $outhandle, $verbosity) = @_; 
     123 
     124    die "MultimediaConvert::identify() needs to be defined by inheriting plugin"; 
     125} 
     126 
     127 
     128 
     129sub init_cache_for_file { 
     130    my $self = shift(@_); 
     131 
     132    my ($media_filename) = @_; 
     133 
     134    $self->SUPER::init_cache_for_file($media_filename); 
     135 
     136 
     137    # This should probably be replaced with Anu's work that replaced 
     138    # non-ASCII chars with URL encodings 
     139 
     140    my $ascii_only_filenames = $self->{'use_ascii_only_filenams'}; 
    162141 
    163142    if (defined $ascii_only_filenames && ($ascii_only_filenames)) { 
    164     my $file_root = $self->{'file_root'}; 
    165     $self->{'file_root'} = ascii_only_filename($file_root); 
     143    my $file_root = $self->{'cached_file_root'}; 
     144    $self->{'cached_file_root'} = ascii_only_filename($file_root); 
    166145    } 
    167146                   
    168     my @ffmpeg_monitor   = ( 'monitor_init'   , "videoconvert::ffmpeg_monitor_init", 
    169                  'monitor_line'   , "videoconvert::ffmpeg_monitor_line", 
    170                  'monitor_deinit' , "videoconvert::ffmpeg_monitor_deinit" ); 
     147    my @ffmpeg_monitor   = ( 'monitor_init'   , "MultimediaConverter::ffmpeg_monitor_init", 
     148                 'monitor_line'   , "MultimediaConverter::ffmpeg_monitor_line", 
     149                 'monitor_deinit' , "MultimediaConverter::ffmpeg_monitor_deinit" ); 
    171150                     
    172     my @flvtool2_monitor = ( 'monitor_init'   ,"monitor_init_unbuffered", 
    173                  'monitor_line'   , "videoconvert::flvtool2_monitor_line", 
    174                  'monitor_deinit' , "monitor_deinit_unbuffered" ); 
    175151                     
    176152    $self->{'ffmpeg_monitor'} = \@ffmpeg_monitor; 
    177     $self->{'flvtool2_monitor'} = \@flvtool2_monitor; 
    178  
    179  
    180     return bless $self, $class; 
    181 } 
     153 
     154 
     155} 
     156 
     157 
    182158 
    183159 
     
    216192 
    217193    my $url = $file_unicode; 
    218     $url =~ s/\x{2018}|\x{2019}|\x{201C}|\x{201D}//g; # remove smart quotes as cause problem in URL for video server 
    219     $url =~ s/\x{2013}/\-/g; # change en-dash to '-' as again causes problems for video server 
     194    $url =~ s/\x{2018}|\x{2019}|\x{201C}|\x{201D}//g; # remove smart quotes as cause problem in URL for streaming web server 
     195    $url =~ s/\x{2013}/\-/g; # change en-dash to '-' as again causes problems for streaming web server 
    220196     
    221197    return $url; 
     
    255231 
    256232 
    257  
    258 sub optional_frame_scale 
    259 { 
    260     my $self = shift (@_); 
    261     my ($orig_size,$video_width,$video_height) = @_; 
    262  
    263     my $s_opt = ""; 
    264     if ($video_width > $video_height) { 
    265     if ($video_width > $orig_size) { 
    266         my $scale_factor = $orig_size/$video_width; 
    267         my $scaled_width = int($video_width * $scale_factor); 
    268         my $scaled_height = int($video_height * $scale_factor); 
    269  
    270         # round to be ensure multiple of 2 (needed by some codecs) 
    271         $scaled_width  = int($scaled_width/2)*2; 
    272         $scaled_height = int($scaled_height/2)*2; 
    273  
    274         $s_opt = "-s ${scaled_width}x${scaled_height}"; 
    275     } 
    276     # else, video is smaller than requested size, don't scale up 
    277     } 
    278     else { 
    279     if ($video_height > $orig_size) { 
    280         my $scale_factor = $orig_size/$video_height; 
    281         my $scaled_width = int($video_width * $scale_factor); 
    282         my $scaled_height = int($video_height * $scale_factor); 
    283  
    284         # round to be ensure multiple of 2 (needed by some codecs) 
    285         $scaled_width  = int($scaled_width/2)*2; 
    286         $scaled_height = int($scaled_height/2)*2; 
    287  
    288         $s_opt = "-s ${scaled_width}x${scaled_height}"; 
    289     } 
    290     # else, video is smaller than requested size, don't scale up 
    291      
    292     } 
    293      
    294     return $s_opt; 
    295 } 
    296  
    297  
    298 sub keyframe_cmd 
    299 { 
    300     my $self = shift (@_); 
    301     my ($ivideo_filename) = @_; 
    302  
    303     my $video_ext_dir = &util::filename_cat($ENV{'GSDLHOME'},"ext","video"); 
    304  
    305     my $output_dir = $self->{'cached_dir'}; 
    306     my $ivideo_root = $self->{'file_root'}; 
    307  
    308     my $oshot_filename = &util::filename_cat($output_dir,"shots.xml"); 
    309  
    310     my $exp_duration = $self->{'exp_duration'}; 
    311     my $t_opt = (defined $exp_duration) ? "-t $exp_duration" : ""; 
    312  
    313     my $main_opts = "-y $t_opt"; 
    314  
    315     my $hive = &util::filename_cat($video_ext_dir,"lib","vhook","hive.so"); 
    316  
    317     my $oflash_filename = &util::filename_cat($output_dir,"$ivideo_root\_keyframe.flv"); 
    318  
    319     my $vhook_opts = "$hive  -o $oshot_filename -k $output_dir $ivideo_filename"; 
    320  
    321     my $ivideo_filename_gsdlenv = $self->gsdlhome_independent($ivideo_filename); 
    322     my $oflash_filename_gsdlenv = $self->gsdlhome_independent($oflash_filename); 
    323  
    324     my $ffmpeg_cmd = "ffkeyframe  $main_opts -vhook \"$vhook_opts\"   -i \"$ivideo_filename_gsdlenv\" -an -y \"$oflash_filename_gsdlenv\""; 
    325  
    326  
    327     return ($ffmpeg_cmd,$oflash_filename); 
    328 } 
    329  
    330  
    331 sub stream_cmd 
    332 { 
    333     my $self = shift (@_); 
    334     my ($ivideo_filename,$video_width,$video_height, 
    335     $streaming_quality, 
    336     $streaming_bitrate,$streaming_size,  
    337     $opt_streaming_achan, $opt_streaming_arate) = @_; 
    338  
    339     my $streaming_achan  
    340     = (defined $opt_streaming_achan) ? $opt_streaming_achan : 2; 
    341  
    342     my $streaming_arate  
    343     = (defined $opt_streaming_arate) ? $opt_streaming_arate : 22050; 
    344  
    345     my $output_dir = $self->{'cached_dir'}; 
    346     my $ivideo_root = $self->{'file_root'}; 
    347  
    348     my $oflash_file = "${ivideo_root}_stream.flv"; 
    349     my $oflash_filename = &util::filename_cat($output_dir,$oflash_file); 
    350  
    351     my $s_opt = $self->optional_frame_scale($streaming_size,$video_width,$video_height); 
    352  
    353     my $exp_duration = $self->{'exp_duration'}; 
    354     my $t_opt = (defined $exp_duration) ? "-t $exp_duration" : ""; 
    355  
    356     my $main_opts = "-y $t_opt"; 
    357  
    358     my $bitrate_opt = "-b $streaming_bitrate"; 
    359     ### my $stream_opts = "-r 25 $s_opt"; 
    360     my $stream_opts .= " $s_opt -ac $streaming_achan -ar $streaming_arate"; 
    361  
    362     # -flags +ilme+ildct' and maybe '-flags +alt' for interlaced material, and try '-top 0/1' 
    363  
    364     my $all_opts = "$main_opts $stream_opts";  
    365  
    366     my $ffmpeg_cmd; 
    367  
    368     my $ivideo_filename_gsdlenv = $self->gsdlhome_independent($ivideo_filename); 
    369     my $oflash_filename_gsdlenv = $self->gsdlhome_independent($oflash_filename); 
    370  
    371     if ($streaming_quality eq "high") { 
    372  
    373     my $pass_log_file = &util::filename_cat($output_dir,"$ivideo_root-logpass.txt"); 
    374     if (-e $pass_log_file) { 
    375         &util::rm($pass_log_file); 
    376     } 
    377  
    378     my $pass_log_file_gsdlenv = $self->gsdlhome_independent($pass_log_file); 
    379  
    380     $all_opts .= " -passlogfile \"$pass_log_file_gsdlenv\""; 
    381  
    382     my $ffmpeg_cmd_pass1 = "ffmpeg -pass 1 -i \"$ivideo_filename_gsdlenv\" $all_opts -y \"$oflash_filename_gsdlenv\""; 
    383  
    384     my $ffmpeg_cmd_pass2 = "ffmpeg -pass 2 -i \"$ivideo_filename_gsdlenv\" $all_opts $bitrate_opt -y \"$oflash_filename_gsdlenv\""; 
    385     $ffmpeg_cmd = "( $ffmpeg_cmd_pass1 ; $ffmpeg_cmd_pass2 )"; 
    386     } 
    387     else { 
    388     # single pass 
    389  
    390     $ffmpeg_cmd = "ffmpeg -i \"$ivideo_filename_gsdlenv\" $all_opts -y \"$oflash_filename_gsdlenv\""; 
    391     } 
    392  
    393     return ($ffmpeg_cmd,$oflash_filename,$oflash_file); 
    394 } 
    395  
    396  
    397  
    398 sub audio_excerpt_cmd 
    399 { 
    400     my $self = shift (@_); 
    401     my ($ivoa_filename,$hh,$mm,$ss,$opt_excerpt_len) = @_; 
    402  
    403     # ivoa = input video or audio 
    404  
    405     my $time_encoded = "$hh:$mm:$ss"; 
    406     my $time_encoded_file = "$hh$mm$ss"; 
    407      
    408      
    409     my $output_dir = $self->{'cached_dir'}; 
    410     my $ivoa_root = $self->{'file_root'}; 
    411  
    412     my $omp3_file = "${ivoa_root}_$time_encoded_file.mp3"; 
    413     my $omp3_filename = &util::filename_cat($output_dir,$omp3_file); 
    414  
    415     my $all_opts = "-y -acodec mp3 -ss $time_encoded "; 
    416  
    417     if (defined $opt_excerpt_len) { 
    418     $all_opts .= "-t $opt_excerpt_len "; 
    419     } 
    420  
    421  
    422     my $ivoa_filename_gsdlenv = $self->gsdlhome_independent($ivoa_filename); 
    423     my $omp3_filename_gsdlenv = $self->gsdlhome_independent($omp3_filename); 
    424  
    425  
    426     my $ffmpeg_cmd = "ffmpeg -i \"$ivoa_filename_gsdlenv\" $all_opts  \"$omp3_filename_gsdlenv\""; 
    427  
    428     return ($ffmpeg_cmd,$omp3_filename,$omp3_file); 
    429 } 
    430  
    431  
    432  
    433 sub streamseekable_cmd 
    434 { 
    435     my $self = shift (@_); 
    436     my ($oflash_filename) = @_; 
    437  
    438     my $output_dir = $self->{'cached_dir'}; 
    439     my $ivideo_root = $self->{'file_root'}; 
    440  
    441     my $cue_filename = &util::filename_cat($output_dir,"on_cue.xml"); 
    442  
    443     my $flvtool_cmd = "flvtool2 -vUP \"$oflash_filename\""; 
    444  
    445     return ($flvtool_cmd,$oflash_filename); 
    446 } 
    447  
    448  
    449 sub streamkeyframes_cmd 
    450 { 
    451     my $self = shift (@_); 
    452     my ($oflash_filename,$doc_obj,$section) = @_; 
    453  
    454     my $assocfilepath  
    455     = $doc_obj->get_metadata_element($section,"assocfilepath"); 
    456  
    457     my $output_dir = $self->{'cached_dir'}; 
    458  
    459     my $cue_filename = &util::filename_cat($output_dir,"on_cue.xml"); 
    460  
    461     my $video_server = $ENV{'GEXT_VIDEO_SERVER'};  
    462     my $video_prefix = $ENV{'GEXT_VIDEO_PREFIX'}; 
    463  
    464     my $collect = $ENV{'GSDLCOLLECTION'}; 
    465   
    466     my $flvtool_cmd = "flvtool2 -vAUtP \"$cue_filename\" -thumbLocation:$video_server$video_prefix/collect/$collect/index/assoc/$assocfilepath \"$oflash_filename\""; 
    467  
    468  
    469     return ($flvtool_cmd,$oflash_filename); 
    470 } 
    471  
    472  
    473 sub streamcuepts_cmd 
    474 { 
    475     my $self = shift (@_); 
    476     my ($oflash_filename) = @_; 
    477  
    478     my $output_dir = $self->{'cached_dir'}; 
    479  
    480     my $cue_filename = &util::filename_cat($output_dir,"on_cue.xml"); 
    481  
    482     my $video_server = $ENV{'GEXT_VIDEO_SERVER'};  
    483     my $video_prefix = $ENV{'GEXT_VIDEO_PREFIX'}; 
    484  
    485     my $collect = $ENV{'GSDLCOLLECTION'}; 
    486     my $thumbloc = "$video_server$video_prefix/collect/$collect"; 
    487  
    488  
    489 #    my $flvtool_cmd = "flvtool2 -vUAtP \"$cue_filename\" -thumbLocation:$thumbloc \"$oflash_filename\""; 
    490  
    491 #    my $flvtool_cmd = "flvtool2 -vUAt \"$cue_filename\" \"$oflash_filename\""; 
    492  
    493  
    494  
    495 #    my $flvtool_cmd = "flvtool2 -vUAt \"$cue_filename\" \"$oflash_filename\""; 
    496  
    497  
    498 ##    my $flvtool_cmd = "flvtool2 -vAt \"$cue_filename\" -UP \"$oflash_filename\" \"$output_dir/updated.flv\""; 
    499  
    500 ##    my $flvtool_cmd = "flvtool2 -vAtU \"$cue_filename\" \"$oflash_filename\" \"$output_dir/updated.flv\""; 
    501  
    502     my $flvtool_cmd = "flvtool2 -vAtUP \"$cue_filename\" \"$oflash_filename\""; 
    503  
    504     return ($flvtool_cmd,$oflash_filename); 
    505 } 
    506  
    507  
    508 sub keyframe_thumbnail_cmd 
    509 { 
    510     my $self = shift (@_); 
    511     my ($ivideo_filename,$thumbnailfile,$thumbnailwidth,$thumbnailheight) = @_; 
    512  
    513     my $output_dir = $self->{'cached_dir'}; 
    514     my $ivideo_root = $self->{'file_root'}; 
    515  
    516     my $key_filename_prefix = &util::filename_cat($output_dir,$ivideo_root); 
    517  
    518  
    519     # Try for 4th keyframe, but fall back to 1st if doesn't exist 
    520     my $key_filename = "${key_filename_prefix}_0003.jpg"; 
    521     $key_filename = "${key_filename_prefix}_0000.jpg" if (!-e $key_filename); 
    522  
    523     my $key_filename_gsdlenv = $self->gsdlhome_independent($key_filename); 
    524     my $thumbnailfile_gsdlenv = $self->gsdlhome_independent($thumbnailfile); 
    525  
    526     my $command; 
    527  
    528     if (-e $key_filename) { 
    529     $command = "convert -interlace plane -verbose -geometry $thumbnailwidth"  
    530         . "x$thumbnailheight \"$key_filename_gsdlenv\" \"$thumbnailfile_gsdlenv\""; 
    531     } 
    532     else { 
    533     # extractkeyframe has either not been switched on, or else had 
    534     # a problem when running 
    535     # => extract a from  
    536     # my $frame_rate = 1.0 / 60.0; 
    537      
    538     my $ivideo_filename_gsdlenv = $self->gsdlhome_independent($ivideo_filename); 
    539  
    540  
    541  
    542     $command = "ffmpeg -i \"$ivideo_filename_gsdlenv\"  -ss 12.5 -vframes 1 -f image2 -s ${thumbnailwidth}x${thumbnailheight} -y \"$thumbnailfile_gsdlenv\""; 
    543  
    544     # fmpeg -i input.dv -r 1 -f image2 -s 120x96 images%05d.png 
    545     } 
    546  
    547     return ($command,$thumbnailfile); 
    548 } 
    549  
    550  
    551 sub keyframe_montage_cmd 
    552 { 
    553     my $self = shift (@_); 
    554     my ($ivideo_filename,$montagefile) = @_; 
    555  
    556     my $output_dir = $self->{'cached_dir'}; 
    557     my $ivideo_root = $self->{'file_root'}; 
    558  
    559     my $key_filename_prefix = &util::filename_cat($output_dir,$ivideo_root); 
    560  
    561     my $options = "-tile 10  -geometry 75x62+2+2"; 
    562  
    563     my $command = "montage $options ${key_filename_prefix}_*.jpg \"$montagefile\""; 
    564  
    565     return ($command,$montagefile); 
    566 } 
    567  
    568  
    569  
    570 sub parse_shot_xml 
    571 { 
    572     my ($self) = shift(@_); 
    573  
    574     my ($plugin) = @_; 
    575  
    576     my $outhandle = $self->{'outhandle'}; 
    577     my $output_dir = $self->{'cached_dir'}; 
    578  
    579     my $shots_filename = &util::filename_cat($output_dir,"shots.xml"); 
    580  
    581     eval { 
    582     $plugin->{'parser'}->parsefile($shots_filename); 
    583     }; 
    584      
    585     if ($@) { 
    586     print $outhandle "videoconvert.pm: skipping $shots_filename as not conformant to Hive shot syntax\n" if ($self->{'verbosity'} > 1); 
    587     print $outhandle "\n Perl Error:\n $@\n" if ($self->{'verbosity'}>2); 
    588     return 0; 
    589     } 
    590  
    591 } 
    592  
    593 sub associate_keyframes_old 
    594 { 
    595     my ($self) = shift(@_); 
    596  
    597     my ($doc_obj,$section,$plugin) = @_; 
    598  
    599     my $output_dir = $self->{'cached_dir'}; 
    600  
    601     my $count = 1; 
    602     foreach my $kframe_file (@{$plugin->{'keyframe_fnames'}}) { 
    603  
    604     my $kframe_filename = &util::filename_cat($output_dir,$kframe_file); 
    605     $doc_obj->associate_file($kframe_filename,"keyframe$count.jpg","image/jpeg", 
    606                  $section); 
    607     $count++; 
    608     } 
    609  
    610     $doc_obj->add_utf8_metadata($section,"NumKeyframes",scalar(@{$plugin->{'keyframe_fnames'}})); 
    611  
    612  
    613     # ***** 
    614     # $doc_obj->add_metadata ($section, "thumblist", $plugin->{'flowplayer_thumblist'}); 
    615  
    616 } 
    617  
    618 sub associate_keyframes 
    619 { 
    620     my ($self) = shift(@_); 
    621  
    622     my ($doc_obj,$section,$plugin) = @_; 
    623  
    624     my $output_dir = $self->{'cached_dir'}; 
    625     my $timeline = $plugin->{'keyframe_timeline'}; 
    626      
    627     my $count = 1; 
    628  
    629     foreach my $t (sort { $timeline->{$a}->{'keyframeindex'} <=> $timeline->{$b}->{'keyframeindex'} } keys %$timeline) 
    630     { 
    631     my $kframe_file = $timeline->{$t}->{'thumb'}; 
    632     my $timestamp = $timeline->{$t}->{'timestamp'}; 
    633  
    634     my $kframe_filename = &util::filename_cat($output_dir,$kframe_file); 
    635     $doc_obj->associate_file($kframe_filename,"keyframe$count.jpg","image/jpeg", 
    636                  $section); 
    637     $doc_obj->add_utf8_metadata($section,"KeyframeTimestamp",$timestamp); 
    638  
    639     $count++; 
    640     } 
    641  
    642     $doc_obj->add_utf8_metadata($section,"NumKeyframes",scalar(@{$plugin->{'keyframe_fnames'}})); 
    643  
    644  
    645     # ***** 
    646     # $doc_obj->add_metadata ($section, "thumblist", $plugin->{'flowplayer_thumblist'}); 
    647 } 
    648  
    649  
    650  
    651233sub ffmpeg_monitor_init 
    652234{ 
     
    693275 
    694276 
    695 sub flvtool2_monitor_line 
    696 { 
    697     my ($line) = @_; 
    698  
    699     my $had_error = 0; 
    700     my $generate_dot = 1; 
    701  
    702     if ($line =~ m/\s+\- /) { 
    703     # ignore tabulated output printed at end of command 
    704     $generate_dot = 0; 
    705     } 
    706      
    707     if ($line =~ m/^Error:/i) { 
    708     $had_error = 1; 
    709     } 
    710  
    711     return ($had_error,$generate_dot); 
    712 } 
    713  
    714  
    715  
    716277 
    717278 
  • extensions/gsdl-video/trunk/perllib/plugins/VideoPlugin.pm

    r18490 r18556  
    11###################################################################### 
    22# 
    3 # VideoPlugin.pm -- plugin for processing video largely based on ImagePlug 
     3# VideoPlugin.pm -- plugin for processing video files  
    44# A component of the Greenstone digital library software 
    55# from the New Zealand Digital Library Project at the  
     
    2424########################################################################### 
    2525 
     26# -- Largely modeled on how ImagePlugin works  
     27# -- Can convert to audio as well as video 
     28 
    2629package VideoPlugin; 
    2730 
     
    3033no strict 'subs'; 
    3134 
    32 use videoconvert; 
    3335use XMLParser; 
    3436use gsprintf; 
    3537 
    36 use BasePlugin; 
     38use MultimediaPlugin; 
     39use VideoConverter; 
    3740 
    3841sub BEGIN { 
    39     @VideoPlugin::ISA = ('BasePlugin'); 
    40  
    41     if (!defined $ENV{'GEXTVIDEO'}) { 
    42     print STDERR "Warning: Greenstone Video extension not detected.\n"; 
    43     } 
    44  
    45 } 
    46  
    47  
    48 # Customized from BasePlugin. Make 'incremental' the default  
    49 # to avoid Greenstone hashing on the file (which in the case of video 
    50 # is HUGE) to generate OID. Also supress 'hash' as option so the user 
    51 # can't choose it. 
    52  
    53 our $oidtype_list =  
    54     [ { 'name' => "auto", 
    55     'desc' => "{BasePlugin.OIDtype.auto}" }, 
    56       { 'name' => "assigned", 
    57         'desc' => "{import.OIDtype.assigned}" }, 
    58       { 'name' => "incremental", 
    59         'desc' => "{import.OIDtype.incremental}" }, 
    60       { 'name' => "dirname", 
    61         'desc' => "{import.OIDtype.dirname}" } ]; 
     42    @VideoPlugin::ISA = ('MultimediaPlugin', 'VideoConverter'); 
     43} 
    6244 
    6345 
     
    6749    'type' => "regexp", 
    6850    'deft' => &get_default_process_exp(), 
    69     'reqd' => "no" }, 
    70       { 'name' => "OIDtype", 
    71     'desc' => "{import.OIDtype}", 
    72     'type' => "enum", 
    73     'list' => $oidtype_list, 
    74     'deft' => "incremental", 
    75     'reqd' => "no", 
    76     'modegli' => "2" }, 
    77  
    78       { 'name' => "noscaleup", 
    79     'desc' => "{VideoPlug.noscaleup}", 
    80     'type' => "flag", 
    81     'reqd' => "no" }, 
    82       { 'name' => "use_ascii_only_filenames", 
    83     'desc' => "{VideoPlug.use_ascii_only_filenames}", 
    84     'type' => "flag", 
    85     'reqd' => "no" }, 
    86       { 'name' => "video_excerpt_duration", 
    87     'desc' => "{VideoPlug.video_excerpt_duration}", 
    88     'type' => "string", 
    89     'deft' => "", 
    90     'reqd' => "no" }, 
    91       { 'name' => "extractthumbnail", 
    92     'desc' => "{VideoPlug.extractthumbnail}", 
    93     'type' => "flag", 
    94     'deft' => "0", 
    95     'reqd' => "no" }, 
    96       { 'name' => "thumbnailsize", 
    97     'desc' => "{VideoPlug.thumbnailsize}", 
    98     'type' => "int", 
    99     'deft' => "100", 
    100     'range' => "1,", 
    101     'reqd' => "no" }, 
    102       { 'name' => "thumbnailtype", 
    103     'desc' => "{VideoPlug.thumbnailtype}", 
    104     'type' => "string", 
    105     'deft' => "jpeg", 
    106     'reqd' => "no" }, 
    107       { 'name' => "extractscreenview", 
    108     'desc' => "{VideoPlug.extractscreenview}", 
    109     'type' => "flag", 
    110     'deft' => "0", 
    111     'reqd' => "no" }, 
    112       { 'name' => "screenviewsize", 
    113     'desc' => "{VideoPlug.screenviewsize}", 
    114     'type' => "int", 
    115     'deft' => "0", 
    116     'range' => "1,", 
    117     'reqd' => "no" }, 
    118       { 'name' => "screenviewtype", 
    119     'desc' => "{VideoPlug.screenviewtype}", 
    120     'type' => "string", 
    121     'deft' => "jpg", 
    122     'reqd' => "no" }, 
    123       { 'name' => "converttotype", 
    124     'desc' => "{VideoPlug.converttotype}", 
    125     'type' => "string", 
    126     'deft' => "", 
    127     'reqd' => "no" }, 
    128       { 'name' => "converttosize", 
    129     'desc' => "{VideoPlug.converttosize}", 
    130     'type' => "int", 
    131     'deft' => "", 
    132 ##  'deft' => "352", 
    133     'reqd' => "no" }, 
    134       { 'name' => "converttobitrate", 
    135     'desc' => "{VideoPlug.converttobitrate}", 
    136     'type' => "string", 
    137     'deft' => "200k", 
    138     'reqd' => "no" }, 
    139       { 'name' => "extractkeyframes", 
    140     'desc' => "{VideoPlug.extractkeyframes}", 
    141     'type' => "flag", 
    142     'deft' => "0", 
    143     'reqd' => "no" }, 
    144       { 'name' => "enablestreaming", 
    145     'desc' => "{VideoPlug.enablestreaming}", 
    146     'type' => "flag", 
    147     'deft' => "1", 
    148     'reqd' => "no" }, 
    149       { 'name' => "streamingsize", 
    150     'desc' => "{VideoPlug.streamingsize}", 
    151     'type' => "int", 
    152     'deft' => "352", 
    153     'reqd' => "no" }, 
    154       { 'name' => "streamingbitrate", 
    155     'desc' => "{VideoPlug.streamingbitrate}", 
    156     'type' => "string", 
    157     'deft' => "200k", 
    158     'reqd' => "no" }, 
    159       { 'name' => "minimumsize", 
    160     'desc' => "{VideoPlug.minimumsize}", 
    161     'type' => "int", 
    162     'deft' => "100", 
    163     'range' => "1,", 
    16451    'reqd' => "no" } ]; 
    16552 
     53 
    16654my $options = { 'name'     => "VideoPlugin", 
    167         'desc'     => "{VideoPlug.desc}", 
     55        'desc'     => "{VideoPlugin.desc}", 
    16856        'abstract' => "no", 
    16957        'inherits' => "yes", 
    17058        'args'     => $arguments }; 
    171  
    172  
    173 my ($self); 
    17459 
    17560sub new { 
     
    18166    push(@{$hashArgOptLists->{"OptList"}},$options); 
    18267 
    183     $self = new BasePlugin($pluginlist, $inputargs, $hashArgOptLists); 
    184  
    185     # Check that ffmpeg is installed and available on the path (except for Windows 95/98) 
    186     # This test is "inherited" from ImagePlugin where ImageMagick support 
    187     # for building cannot be used on these two versions of Windows as they 
    188     # do not have enough flexablity in the use of stdout and stderr to 
    189     # support how our code works.  It seems reasonable to assume the 
    190     # same is true for VideoPlugin work using ffmpeg. 
    191  
    192     if (($ENV{'GSDLOS'} ne "windows" || Win32::IsWinNT())) { 
    193  
    194     my $result = `ffmpeg -h 2>&1`; 
    195  
    196  
    197     if (!defined $result || $result !~ m/^FFmpeg version/) { 
    198         $self->{'ffmpeg_installed'} = 0; 
    199         print STDERR $result; 
    200     } 
    201     else { 
    202         $self->{'ffmpeg_installed'} = 1; 
    203     } 
    204  
    205     } 
    206  
    207  
    208     # create XML::Parser object for parsing metadata.xml files 
    209     my $parser; 
    210     if ($]<5.008) { 
    211     # Perl 5.6 
    212     $parser = new XML::Parser('Style' => 'Stream', 
    213                   'Handlers' => {'Char' => \&Char, 
    214                          'Doctype' => \&Doctype 
    215                          }); 
    216     } 
    217     else { 
    218     # Perl 5.8 
    219     $parser = new XML::Parser('Style' => 'Stream', 
    220                   'ProtocolEncoding' => 'ISO-8859-1', 
    221                   'Handlers' => {'Char' => \&Char, 
    222                          'Doctype' => \&Doctype 
    223                          }); 
    224     } 
     68    new VideoConverter($pluginlist, $inputargs, $hashArgOptLists); 
     69    my $self = new MultimediaPlugin($pluginlist, $inputargs, $hashArgOptLists); 
     70 
     71    if ($self->{'info_only'}) { 
     72    # don't worry about creating the XML parser as all we want is the  
     73    # list of plugin options 
     74    return bless $self, $class; 
     75    } 
     76 
     77 
     78    # create XML::Parser object for parsing keyframe files (produced by hive) 
     79    my $parser = new XML::Parser('Style' => 'Stream', 
     80                                 'Pkg' => 'VideoPlugin', 
     81                                 'PluginObj' => $self, 
     82                 'Namespaces' => 1, # strip out namespaces 
     83                 'Handlers' => {'Char' => \&Char, 
     84                        'XMLDecl' => \&XMLDecl, 
     85                        'Entity'  => \&Entity, 
     86                        'Doctype' => \&Doctype, 
     87                        'Default' => \&Default 
     88                                 }); 
    22589 
    22690    $self->{'parser'} = $parser; 
     
    22892 
    22993    return bless $self, $class; 
     94} 
     95 
     96 
     97 
     98sub begin { 
     99    my $self = shift (@_); 
     100    my ($pluginfo, $base_dir, $processor, $maxdocs) = @_; 
     101 
     102    $self->SUPER::begin(@_); 
     103    $self->VideoConverter::begin(@_);  
    230104} 
    231105 
     
    236110 
    237111    $self->SUPER::init(@_); 
    238     ## $self->ImageConverter::init(); # ****** VideoConverter::init ?? 
     112    $self->VideoConverter::init(@_); 
    239113} 
    240114 
     
    246120} 
    247121 
    248 # this makes no sense for image 
    249 sub block_cover_image 
     122 
     123sub extract_keyframes 
    250124{ 
    251     my $self =shift (@_); 
    252     my ($filename) = @_; 
    253  
    254     return; 
    255 } 
    256  
    257  
     125    my $self = shift (@_); 
     126    my ($doc_obj,$originalfilename,$filename) = @_; 
     127 
     128    my $section = $doc_obj->get_top_section(); 
     129 
     130    my $output_dir   = $self->{'cached_dir'}; 
     131    my $ivideo_root  = $self->{'cached_file_root'}; 
     132 
     133    # Generate the keyframes with ffmpeg and hive 
     134    my ($keyframe_cmd,$okeyframe_filename) = $self->keyframe_cmd($originalfilename || $filename); 
     135     
     136    my $keyframe_options = { @{$self->{'ffmpeg_monitor'}}, 
     137                 'message_prefix' => "Keyframe", 
     138                 'message' => "Extracting keyframes" };  
     139     
     140    $self->autorun_cached_general_cmd($keyframe_cmd,$okeyframe_filename,$keyframe_options); 
     141    $self->parse_shot_xml(); 
     142    $self->associate_keyframes($doc_obj,$section); 
     143} 
     144 
     145 
     146sub enable_streaming 
     147{ 
     148    my $self = shift (@_); 
     149    my ($doc_obj,$originalfilename,$filename,$convertto_regenerated, 
     150    $video_width,$video_height) = @_; 
     151 
     152    my $section = $doc_obj->get_top_section(); 
     153 
     154    my $output_dir   = $self->{'cached_dir'}; 
     155    my $ivideo_root  = $self->{'cached_file_root'}; 
     156     
     157    # Generate the Flash FLV format for streaming purposes 
     158    my $streamable_regenerated = 0; 
     159 
     160    my $optionally_run_general_cmd = "run_uncached_general_cmd"; 
     161    if ($self->{'enable_cache'}) { 
     162    $optionally_run_general_cmd 
     163        = ($convertto_regenerated) ? "regenerate_general_cmd" : "run_cached_general_cmd"; 
     164    } 
     165 
     166    my $streaming_bitrate = $self->{'streamingbitrate'}; 
     167    my $streaming_size    = $self->{'streamingsize'}; 
     168     
     169    my $streaming_quality = "high"; 
     170     
     171    my ($stream_cmd,$oflash_filename,$oflash_file)  
     172    = $self->stream_cmd($originalfilename || $filename, 
     173                $video_width,$video_height, 
     174                $streaming_quality, 
     175                $streaming_bitrate, $streaming_size); 
     176     
     177     
     178    my $streamable_options = { @{$self->{'ffmpeg_monitor'}}, 
     179                   'message_prefix' => "Stream", 
     180                   'message' => "Generating streamable video: $oflash_file" };  
     181     
     182    my $streamable_result; 
     183    my $streamable_had_error; 
     184    ($streamable_regenerated,$streamable_result,$streamable_had_error) 
     185    = $self->$optionally_run_general_cmd($stream_cmd,$oflash_filename,$streamable_options); 
     186     
     187    if (!$streamable_had_error) { 
     188    my ($streamseekable_cmd,$ostreamseekable_filename) = $self->streamseekable_cmd($oflash_filename); 
     189     
     190    my $streamseekable_options = { @{$self->{'flvtool2_monitor'}}, 
     191                       'message_prefix' => "Stream Seekable", 
     192                       'message' => "Reprocessing video stream to be seekable by timeline: $oflash_file" };  
     193     
     194    if ($streamable_regenerated) { 
     195        $self->run_general_cmd($streamseekable_cmd,$streamseekable_options); 
     196    } 
     197     
     198    my $streamable_url = $oflash_file; 
     199    ## $streamable_url =~ s/ /%20/g; 
     200    my $streamable_url_safe = $self->url_safe($streamable_url); 
     201     
     202    $doc_obj->add_utf8_metadata ($section, "streamablevideo", $streamable_url_safe); 
     203    $doc_obj->associate_file($oflash_filename,$oflash_file,"video/flash", 
     204                 $section); 
     205    } 
     206 
     207##    my $video_width = $doc_obj->get_metadata_element($section,"VideoWidth"); 
     208##    my $video_height = $doc_obj->get_metadata_element($section,"VideoHeight"); 
     209     
     210    # 
     211    # FlowPlayer.swf       height+22 pixels 
     212    # FlowPlayerBlack.swf  height+16 pixels 
     213    # FlowPlayerThermo.swf height+16 pixels 
     214    # FlowPlayerWhite.swf  height+26 pixels 
     215    my $flashwidth = $video_width; 
     216    my $flashheight = $video_height + 22; 
     217    if ($self->{'extractkeyframes'}) { 
     218    $flashheight += 100; 
     219    } 
     220    $doc_obj->add_metadata ($section, "flashwidth",    $flashwidth); 
     221    $doc_obj->add_metadata ($section, "flashheight",   $flashheight); 
     222     
     223    my $video_server = $ENV{'GEXT_VIDEO_SERVER'}; 
     224    my $video_prefix = $ENV{'GEXT_VIDEO_PREFIX'}; 
     225    my $base_url = "$video_server$video_prefix/collect/[collection]/index/assoc/[assocfilepath]/"; 
     226    my $base_url_safe = $self->url_safe($base_url); 
     227    $doc_obj->add_utf8_metadata ($section, "baseurl",$base_url_safe); 
     228     
     229    $self->{'oflash_file'} = $oflash_file; 
     230    $self->{'oflash_filename'} = $oflash_filename; 
     231 
     232    return $streamable_regenerated; 
     233} 
     234 
     235sub extract_thumbnail 
     236{ 
     237    my $self = shift (@_); 
     238    my ($doc_obj,$filename,$convertto_regenerated,$thumbnailtype, 
     239    $thumbnail_width, $thumbnail_height) = @_; 
     240 
     241    my $section = $doc_obj->get_top_section(); 
     242 
     243    my $output_dir   = $self->{'cached_dir'}; 
     244    my $ivideo_root  = $self->{'cached_file_root'}; 
     245 
     246    my $verbosity = $self->{'verbosity'}; 
     247    my $outhandle = $self->{'outhandle'}; 
     248 
     249 
     250    # Generate the thumbnail with convert, a la ImagePlug 
     251 
     252    my $thumbnailfile = &util::filename_cat($output_dir,"$ivideo_root.$thumbnailtype"); 
     253     
     254     
     255    my $optionally_run_general_cmd = "run_uncached_general_cmd"; 
     256    if ($self->{'enable_cache'}) {   
     257    $optionally_run_general_cmd  
     258    = ($convertto_regenerated) ? "regenerate_general_cmd" : "run_cached_general_cmd"; 
     259    } 
     260     
     261    my ($thumb_cmd ,$othumb_filename) 
     262    = $self->keyframe_thumbnail_cmd($filename,$thumbnailfile,$thumbnail_width,$thumbnail_height); 
     263     
     264    my $thumb_options = { 'verbosity' => $verbosity, 
     265              'outhandle' => $outhandle, 
     266              'message_prefix' => "Thumbnail", 
     267              'message' => "Generating thumbnail" };  
     268     
     269    my ($thumb_regenerated,$thumb_result,$thumb_had_error) 
     270    = $self->$optionally_run_general_cmd($thumb_cmd,$thumbnailfile,$thumb_options); 
     271     
     272    # Add the thumbnail as an associated file ... 
     273    if (-e "$thumbnailfile") {  
     274    $doc_obj->associate_file("$thumbnailfile", "thumbnail.$thumbnailtype",  
     275                 "image/$thumbnailtype",$section); 
     276    $doc_obj->add_metadata ($section, "ThumbType", $thumbnailtype); 
     277    $doc_obj->add_metadata ($section, "Thumb", "thumbnail.$thumbnailtype"); 
     278     
     279    $doc_obj->add_utf8_metadata ($section, "thumbicon", "<img src=\"_httpprefix_/collect/[collection]/index/assoc/[assocfilepath]/[Thumb]\" width=[ThumbWidth] height=[ThumbHeight]>"); 
     280###     $doc_obj->add_utf8_metadata ($section, "thumbicon", "<img src=\"_httpprefix_/collect/[collection]/index/assoc/[assocfilepath]/[Thumb]\">"); 
     281    } 
     282     
     283    # Extract Thumnail metadata from convert output 
     284    if ($thumb_result =~ m/[0-9]+x[0-9]+=>([0-9]+)x([0-9]+)/) { 
     285    $doc_obj->add_metadata ($section, "ThumbWidth", $1); 
     286    $doc_obj->add_metadata ($section, "ThumbHeight", $2); 
     287    } 
     288    else { 
     289    # Two reasons for getting to here: 
     290    #   1.thumbnail was generated by ffmpeg, not imagemagick convert 
     291    #   2.thumbnail was cached, so imagemagick convert was not run 
     292    # Either way, the solution is the same: 
     293    # => run "identify $thumbnailfile" and parse result 
     294     
     295    $thumb_result = `identify \"$thumbnailfile\"`; 
     296     
     297    if ($thumb_result =~ m/([0-9]+)x([0-9]+)/) { 
     298        $doc_obj->add_metadata ($section, "ThumbWidth", $1); 
     299        $doc_obj->add_metadata ($section, "ThumbHeight", $2); 
     300    } 
     301    } 
     302} 
     303 
     304 
     305sub extract_keyframes_montage 
     306{ 
     307    my $self = shift (@_); 
     308    my ($doc_obj,$filename,$thumbnailtype) = @_; 
     309 
     310    my $section = $doc_obj->get_top_section(); 
     311 
     312    my $output_dir   = $self->{'cached_dir'}; 
     313    my $ivideo_root  = $self->{'cached_file_root'}; 
     314 
     315     
     316    # Generate the mosaic with 'montage' 
     317    my $montagefile = &util::filename_cat($output_dir,"$ivideo_root\_montage.$thumbnailtype"); 
     318     
     319    my ($montage_cmd,$omontage_filename) 
     320    = $self->keyframe_montage_cmd($filename,$montagefile); 
     321     
     322    my $montage_options = { 'message_prefix' => "Montage", 
     323                'message' => "Generating montage" };  
     324     
     325    my ($montage_result,$montage_had_error) 
     326    = $self->run_general_cmd($montage_cmd,$montage_options); 
     327     
     328    # Add the montage as an associated file ... 
     329    if (-e "$montagefile") {  
     330    $doc_obj->associate_file("$montagefile", "montage.$thumbnailtype",  
     331                 "image/$thumbnailtype",$section); 
     332    $doc_obj->add_metadata ($section, "MontageType", $thumbnailtype); 
     333    $doc_obj->add_metadata ($section, "Montage", "montage.$thumbnailtype"); 
     334     
     335    $doc_obj->add_utf8_metadata ($section, "montageicon", "<img src=\"_httpprefix_/collect/[collection]/index/assoc/[assocfilepath]/[Montage]\" >"); 
     336    } 
     337} 
     338 
     339 
     340 
     341sub extract_screenview 
     342{ 
     343    my $self = shift (@_); 
     344    my ($doc_obj,$filename,$screenviewtype, $screenview_width,$screenview_height) = @_; 
     345 
     346    my $section = $doc_obj->get_top_section(); 
     347 
     348    my $output_dir   = $self->{'cached_dir'}; 
     349    my $ivideo_root  = $self->{'cached_file_root'}; 
     350     
     351     
     352    # make the screenview image 
     353 
     354    my $screenviewfilename = &util::filename_cat($output_dir,"$ivideo_root.$screenviewtype"); 
     355 
     356     
     357    my ($screenview_cmd,$oscreenview_filename) 
     358    = $self->keyframe_thumbnail_cmd($filename,$screenviewfilename,$screenview_width,$screenview_height); 
     359 
     360    my $screenview_options = { 'message_prefix' => "Screenview", 
     361                   'message' => "Generating screenview image" };  
     362 
     363    my ($result,$had_error) 
     364    = $self->run_general_cmd($screenview_cmd,$screenview_options); 
     365 
     366    # get screenview dimensions, size and type 
     367    if ($result =~ m/[0-9]+x[0-9]+=>([0-9]+)x([0-9]+)/) { 
     368    $doc_obj->add_metadata ($section, "ScreenWidth", $1); 
     369    $doc_obj->add_metadata ($section, "ScreenHeight", $2); 
     370    } 
     371    else { 
     372    $doc_obj->add_metadata ($section, "ScreenWidth", $screenview_width); 
     373    $doc_obj->add_metadata ($section, "ScreenHeight", $screenview_height); 
     374    } 
     375     
     376    #add the screenview as an associated file ... 
     377    if (-e "$screenviewfilename") {  
     378    $doc_obj->associate_file("$screenviewfilename", "screenview.$screenviewtype", 
     379                 "image/$screenviewtype",$section); 
     380    $doc_obj->add_metadata ($section, "ScreenType", $screenviewtype); 
     381    $doc_obj->add_metadata ($section, "Screen", "screenview.$screenviewtype"); 
     382     
     383    $doc_obj->add_utf8_metadata ($section, "screenicon", "<img src=\"_httpprefix_/collect/[collection]/index/assoc/[assocfilepath]/[Screen]\" width=[ScreenWidth] height=[ScreenHeight]>"); 
     384    } else { 
     385    my $outhandle = $self->{'outhandle'}; 
     386    print $outhandle "VideoPlugin: couldn't find \"$screenviewfilename\"\n"; 
     387    } 
     388} 
    258389 
    259390 
     
    284415    my ($video_type, $video_width, $video_height, $video_duration, $video_size, 
    285416    $vcodec,$vfps,$atype,$afreq,$achan,$arate)  
    286     = &videoconvert::identify($filename, $outhandle, $verbosity); 
     417    = &VideoConverter::identify($filename, $outhandle, $verbosity); 
    287418 
    288419    if ($vfps eq "unknown") { 
     
    305436    my $exp_duration = undef; 
    306437     
    307     my $video_excerpt_duration = $self->{'video_excerpt_duration'}; 
    308  
    309     if ((defined $video_excerpt_duration) && ($video_excerpt_duration ne "")) { 
    310     $exp_duration = $video_excerpt_duration; 
     438    my $excerpt_duration = $self->{'excerpt_duration'}; 
     439 
     440    if ((defined $excerpt_duration) && ($excerpt_duration ne "")) { 
     441    $exp_duration = $excerpt_duration; 
    311442    my ($hh,$mm,$ss,$ms) = ($exp_duration =~ m/^(\d\d):(\d\d):(\d\d)\.?(\d\d)?/); 
    312443    my $excerpt_dur_in_secs = $hh * 3600 + $mm * 60 + $ss; 
     
    330461 
    331462    my $ascii_only_filenames = $self->{'use_ascii_only_filenames'}; 
    332     my $videoconvert  
    333     = new videoconvert($base_dir,$filename,$verbosity,$outhandle,$exp_duration,$ascii_only_filenames); 
    334     $self->{'videoconvert'} = $videoconvert; 
     463    $self->init_cache_for_file($filename); 
    335464 
    336465    my $originalfilename = undef; 
    337466    my $type = "unknown"; 
    338467 
    339     my $output_dir = $videoconvert->{'cached_dir'}; 
    340     my $ivideo_root = $videoconvert->{'file_root'}; 
     468    my $output_dir = $self->{'cached_dir'}; 
     469    my $ivideo_root = $self->{'cached_file_root'}; 
    341470 
    342471    my $convertto_regenerated = 0; 
     
    355484    $filename = &util::filename_cat($output_dir,$file); 
    356485  
    357     my $s_opt = $videoconvert->optional_frame_scale($converttosize,$video_width,$video_height); 
     486    my $s_opt = $self->optional_frame_scale($converttosize,$video_width,$video_height); 
    358487    my $exp_duration = $self->{'exp_duration'}; 
    359488    my $t_opt = (defined $exp_duration) ? "-t $exp_duration" : ""; 
     
    373502    my $convertto_error; 
    374503     
    375     my $convertto_options = { @{$videoconvert->{'ffmpeg_monitor'}}, 
     504    my $convertto_options = { @{$self->{'ffmpeg_monitor'}}, 
    376505                  'message_prefix' => "Convert to", 
    377506                  'message' => "Converting video to $converttotype" };  
    378507 
    379508    ($convertto_regenerated,$convertto_result,$convertto_error) 
    380         = $videoconvert->run_cached_general_cmd($convertto_command,$filename,$convertto_options); 
     509        = $self->autorun_cached_general_cmd($convertto_command,$filename,$convertto_options); 
    381510                             
    382511    $type = $converttotype; 
     
    414543    $file = $file_unicode; 
    415544    my $filemeta = $self->filename_to_utf8_metadata($file); 
    416     my $filemeta_url_safe = $videoconvert->url_safe($filemeta); 
     545    my $filemeta_url_safe = $self->url_safe($filemeta); 
    417546 
    418547    $doc_obj->add_utf8_metadata ($section, "Video", $filemeta_url_safe); 
     
    454583 
    455584    if ($self->{'extractkeyframes'}) { 
    456     # Generate the keyframes with ffmpeg and hive 
    457     my ($keyframe_cmd,$okeyframe_filename) = $videoconvert->keyframe_cmd($originalfilename || $filename); 
    458  
    459     my $keyframe_options = { @{$videoconvert->{'ffmpeg_monitor'}}, 
    460                  'message_prefix' => "Keyframe", 
    461                  'message' => "Extracting keyframes" };  
    462  
    463     $videoconvert->run_cached_general_cmd($keyframe_cmd,$okeyframe_filename,$keyframe_options); 
    464     $videoconvert->parse_shot_xml($self); 
    465     $videoconvert->associate_keyframes($doc_obj,$section,$self); 
     585    $self->extract_keyframes($doc_obj,$originalfilename,$filename); 
    466586    } 
    467587 
    468588    my $streamable_regenerated = 0; 
    469     my $optionally_run_general_cmd  
    470     = ($convertto_regenerated) ? "regenerate_general_cmd" : "run_cached_general_cmd"; 
    471  
    472     if ($self->{'enablestreaming'}) { 
    473     # Generate the Flash FLV format for streaming purposes 
    474  
    475     my $streaming_bitrate = $self->{'streamingbitrate'}; 
    476     my $streaming_size    = $self->{'streamingsize'}; 
    477  
    478     my $streaming_quality = "high"; 
    479  
    480     my ($stream_cmd,$oflash_filename,$oflash_file)  
    481         = $videoconvert->stream_cmd($originalfilename || $filename, 
    482                     $video_width,$video_height, 
    483                     $streaming_quality, 
    484                     $streaming_bitrate, $streaming_size); 
    485  
    486  
    487     my $streamable_options = { @{$videoconvert->{'ffmpeg_monitor'}}, 
    488                    'message_prefix' => "Stream", 
    489                    'message' => "Generating streamable video: $oflash_file" };  
    490  
    491     my $streamable_result; 
    492     my $streamable_had_error; 
    493     ($streamable_regenerated,$streamable_result,$streamable_had_error) 
    494         = $videoconvert->$optionally_run_general_cmd($stream_cmd,$oflash_filename,$streamable_options); 
    495  
    496     if (!$streamable_had_error) { 
    497         my ($streamseekable_cmd,$ostreamseekable_filename) = $videoconvert->streamseekable_cmd($oflash_filename); 
    498  
    499         my $streamseekable_options = { @{$videoconvert->{'flvtool2_monitor'}}, 
    500                        'message_prefix' => "Stream Seekable", 
    501                        'message' => "Reprocessing video stream to be seekable by timeline: $oflash_file" };  
    502  
    503         if ($streamable_regenerated) { 
    504         $videoconvert->run_general_cmd($streamseekable_cmd,$streamseekable_options); 
    505         } 
    506  
    507         my $streamable_url = $oflash_file; 
    508         ## $streamable_url =~ s/ /%20/g; 
    509         my $streamable_url_safe = $videoconvert->url_safe($streamable_url); 
    510  
    511         $doc_obj->add_utf8_metadata ($section, "streamablevideo", $streamable_url_safe); 
    512         $doc_obj->associate_file($oflash_filename,$oflash_file,"video/flash", 
    513                      $section); 
    514     } 
    515  
    516     my $video_width = $doc_obj->get_metadata_element($section,"VideoWidth"); 
    517     my $video_height = $doc_obj->get_metadata_element($section,"VideoHeight"); 
    518      
    519     # 
    520     # FlowPlayer.swf       height+22 pixels 
    521     # FlowPlayerBlack.swf  height+16 pixels 
    522     # FlowPlayerThermo.swf height+16 pixels 
    523     # FlowPlayerWhite.swf  height+26 pixels 
    524     my $flashwidth = $video_width; 
    525     my $flashheight = $video_height + 22; 
    526     if ($self->{'extractkeyframes'}) { 
    527         $flashheight += 100; 
    528     } 
    529     $doc_obj->add_metadata ($section, "flashwidth",    $flashwidth); 
    530     $doc_obj->add_metadata ($section, "flashheight",   $flashheight); 
    531      
    532     my $video_server = $ENV{'GEXT_VIDEO_SERVER'}; 
    533     my $video_prefix = $ENV{'GEXT_VIDEO_PREFIX'}; 
    534     my $base_url = "$video_server$video_prefix/collect/[collection]/index/assoc/[assocfilepath]/"; 
    535     my $base_url_safe = $videoconvert->url_safe($base_url); 
    536     $doc_obj->add_utf8_metadata ($section, "baseurl",$base_url_safe); 
    537      
    538     $self->{'oflash_file'} = $oflash_file; 
    539     $self->{'oflash_filename'} = $oflash_filename; 
    540     } 
    541  
    542  
    543     # Make the thumbnail image 
     589 
     590    if ($self->{'enable_streaming'}) {   
     591    $streamable_regenerated  
     592        = $self->enable_streaming($doc_obj,$originalfilename,$filename, 
     593                      $convertto_regenerated, 
     594                      $video_width,$video_height); 
     595    } 
     596 
     597 
    544598    my $thumbnailsize = $self->{'thumbnailsize'} || 100; 
    545599    my $thumbnailtype = $self->{'thumbnailtype'} || 'jpg'; 
    546600 
    547     my $thumbnailwidth;  
    548     my $thumbnailheight; 
    549  
    550     if ($video_width>$video_height) { 
    551     my $scale_ratio = $video_height / $video_width; 
    552     $thumbnailwidth = $thumbnailsize; 
    553     $thumbnailheight = int($thumbnailsize * $scale_ratio); 
    554     } 
    555     else { 
    556     my $scale_ratio = $video_width / $video_height; 
    557     $thumbnailwidth = int($thumbnailsize * $scale_ratio); 
    558     $thumbnailheight = $thumbnailsize; 
    559     } 
    560  
    561  
    562     my $thumbnailfile = &util::filename_cat($output_dir,"$ivideo_root.$thumbnailtype"); 
    563  
    564  
    565     if ($self->{'extractthumbnail'}) { 
    566     # Generate the thumbnail with convert, a la ImagePlug 
    567     my ($thumb_cmd ,$othumb_filename) 
    568         = $videoconvert->keyframe_thumbnail_cmd($filename,$thumbnailfile,$thumbnailwidth,$thumbnailheight); 
    569  
    570     my $thumb_options = { 'verbosity' => $verbosity, 
    571                   'outhandle' => $outhandle, 
    572                   'message_prefix' => "Thumbnail", 
    573                   'message' => "Generating thumbnail" };  
    574  
    575     my ($thumb_regenerated,$thumb_result,$thumb_had_error) 
    576         = $videoconvert->$optionally_run_general_cmd($thumb_cmd,$thumbnailfile,$thumb_options); 
    577  
    578     # Add the thumbnail as an associated file ... 
    579     if (-e "$thumbnailfile") {  
    580         $doc_obj->associate_file("$thumbnailfile", "thumbnail.$thumbnailtype",  
    581                      "image/$thumbnailtype",$section); 
    582         $doc_obj->add_metadata ($section, "ThumbType", $thumbnailtype); 
    583         $doc_obj->add_metadata ($section, "Thumb", "thumbnail.$thumbnailtype"); 
    584  
    585         $doc_obj->add_utf8_metadata ($section, "thumbicon", "<img src=\"_httpprefix_/collect/[collection]/index/assoc/[assocfilepath]/[Thumb]\" width=[ThumbWidth] height=[ThumbHeight]>"); 
    586 ###     $doc_obj->add_utf8_metadata ($section, "thumbicon", "<img src=\"_httpprefix_/collect/[collection]/index/assoc/[assocfilepath]/[Thumb]\">"); 
    587     } 
    588  
    589     # Extract Thumnail metadata from convert output 
    590     if ($thumb_result =~ m/[0-9]+x[0-9]+=>([0-9]+)x([0-9]+)/) { 
    591         $doc_obj->add_metadata ($section, "ThumbWidth", $1); 
    592         $doc_obj->add_metadata ($section, "ThumbHeight", $2); 
     601 
     602    if ($self->{'create_thumbnail'} eq "true") { 
     603 
     604    my $thumbnail_width;  
     605    my $thumbnail_height; 
     606     
     607    if ($video_width>$video_height) { 
     608        my $scale_ratio = $video_height / $video_width; 
     609        $thumbnail_width = $thumbnailsize; 
     610        $thumbnail_height = int($thumbnailsize * $scale_ratio); 
    593611    } 
    594612    else { 
    595         # Two reasons for getting to here: 
    596         #   1.thumbnail was generated by ffmpeg, not imagemagick convert 
    597         #   2.thumbnail was cached, so imagemagick convert was not run 
    598         # Either way, the solution is the same: 
    599         # => run "identify $thumbnailfile" and parse result 
    600  
    601         $thumb_result = `identify \"$thumbnailfile\"`; 
    602          
    603         if ($thumb_result =~ m/([0-9]+)x([0-9]+)/) { 
    604         $doc_obj->add_metadata ($section, "ThumbWidth", $1); 
    605         $doc_obj->add_metadata ($section, "ThumbHeight", $2); 
    606         } 
    607     } 
     613        my $scale_ratio = $video_width / $video_height; 
     614        $thumbnail_width = int($thumbnailsize * $scale_ratio); 
     615        $thumbnail_height = $thumbnailsize; 
     616    } 
     617     
     618 
     619    $self->extract_thumbnail($doc_obj,$filename,$convertto_regenerated, 
     620                 $thumbnailtype,  
     621                 $thumbnail_width,$thumbnail_height); 
    608622    } 
    609623 
    610624 
    611625    if ($self->{'extractkeyframes'}) { 
    612  
    613     # Generate the mosaic with 'montage' 
    614     my $montagefile = &util::filename_cat($output_dir,"$ivideo_root\_montage.$thumbnailtype"); 
    615  
    616     my ($montage_cmd,$omontage_filename) 
    617         = $videoconvert->keyframe_montage_cmd($filename,$montagefile); 
    618  
    619     my $montage_options = { 'message_prefix' => "Montage", 
    620                 'message' => "Generating montage" };  
    621  
    622     my ($montage_result,$montage_had_error) 
    623         = $videoconvert->run_general_cmd($montage_cmd,$montage_options); 
    624  
    625     # Add the montage as an associated file ... 
    626     if (-e "$montagefile") {  
    627         $doc_obj->associate_file("$montagefile", "montage.$thumbnailtype",  
    628                      "image/$thumbnailtype",$section); 
    629         $doc_obj->add_metadata ($section, "MontageType", $thumbnailtype); 
    630         $doc_obj->add_metadata ($section, "Montage", "montage.$thumbnailtype"); 
    631  
    632         $doc_obj->add_utf8_metadata ($section, "montageicon", "<img src=\"_httpprefix_/collect/[collection]/index/assoc/[assocfilepath]/[Montage]\" >"); 
    633     } 
    634     } 
     626    $self->extract_keyframes_montage($doc_obj,$filename,$thumbnailtype); 
     627    } 
     628 
     629    my $screenviewsize = $self->{'screenviewsize'}; 
     630    my $screenviewtype = $self->{'screenviewtype'} || 'jpeg'; 
    635631 
    636632    # Make a screen-sized version of the picture if requested 
    637     if ($self->{'extractscreenview'}) { 
     633    if ($self->{'create_screenview'} eq "true") { 
    638634 
    639635    # To do: if the actual image smaller than the screenview size, 
    640636    # we should use the original ! 
    641637 
    642     my $screenviewsize = $self->{'screenviewsize'}; 
    643     my $screenviewtype = $self->{'screenviewtype'} || 'jpeg'; 
    644     my $screenviewfilename = &util::filename_cat($output_dir,"$ivideo_root.$screenviewtype"); 
    645  
    646     my $screenviewwidth;  
    647     my $screenviewheight; 
     638    my $screenview_width;  
     639    my $screenview_height; 
    648640     
    649641    if ($video_width>$video_height) { 
    650642        my $scale_ratio = $video_height / $video_width; 
    651         $screenviewwidth = $screenviewsize; 
    652         $screenviewheight = int($screenviewsize * $scale_ratio); 
     643        $screenview_width = $screenviewsize; 
     644        $screenview_height = int($screenviewsize * $scale_ratio); 
    653645    } 
    654646    else { 
    655647        my $scale_ratio = $video_width / $video_height; 
    656         $screenviewwidth = int($screenviewsize * $scale_ratio); 
    657         $screenviewheight = $screenviewsize; 
    658     } 
    659  
    660  
    661     # make the screenview image 
    662  
    663     my ($screenview_cmd,$oscreenview_filename) 
    664         = $videoconvert->keyframe_thumbnail_cmd($filename,$screenviewfilename,$screenviewwidth,$screenviewheight); 
    665  
    666     my $screenview_options = { 'message_prefix' => "Screenview", 
    667                    'message' => "Generating screenview image" };  
    668  
    669     my ($result,$had_error) 
    670         = $videoconvert->run_general_cmd($screenview_cmd,$screenview_options); 
    671  
    672     # get screenview dimensions, size and type 
    673         if ($result =~ m/[0-9]+x[0-9]+=>([0-9]+)x([0-9]+)/) { 
    674         $doc_obj->add_metadata ($section, "ScreenWidth", $1); 
    675         $doc_obj->add_metadata ($section, "ScreenHeight", $2); 
    676     } 
    677     else { 
    678         $doc_obj->add_metadata ($section, "ScreenWidth", $video_width); 
    679         $doc_obj->add_metadata ($section, "ScreenHeight", $video_height); 
    680     } 
    681  
    682     #add the screenview as an associated file ... 
    683     if (-e "$screenviewfilename") {  
    684         $doc_obj->associate_file("$screenviewfilename", "screenview.$screenviewtype", 
    685                      "image/$screenviewtype",$section); 
    686         $doc_obj->add_metadata ($section, "ScreenType", $screenviewtype); 
    687         $doc_obj->add_metadata ($section, "Screen", "screenview.$screenviewtype"); 
    688  
    689         $doc_obj->add_utf8_metadata ($section, "screenicon", "<img src=\"_httpprefix_/collect/[collection]/index/assoc/[assocfilepath]/[Screen]\" width=[ScreenWidth] height=[ScreenHeight]>"); 
    690     } else { 
    691         print $outhandle "VideoPlugin: couldn't find \"$screenviewfilename\"\n"; 
    692     } 
     648        $screenview_width = int($screenviewsize * $scale_ratio); 
     649        $screenview_height = $screenviewsize; 
     650    } 
     651 
     652 
     653    $self->extract_screenview($doc_obj,$filename, 
     654                  $screenviewtype, 
     655                  $screenview_width,$screenview_height); 
    693656    } 
    694657 
     
    703666    my ($pluginfo, $base_dir, $file, $block_hash, $metadata, $processor, $maxdocs, $total_count, $gli) = @_; 
    704667 
    705     my $outhandle = $self->{'outhandle'}; 
    706  
    707     # should we move this to read? What about secondary plugins? 
    708     print STDERR "<Processing n='$file' p='$self->{'plugin_type'}'>\n" if ($gli); 
    709     print $outhandle "$self->{'plugin_type'} processing $file\n" 
    710         if $self->{'verbosity'} > 1; 
    711  
    712     my ($filename_full_path, $filename_no_path) = &util::get_full_filenames($base_dir, $file); 
    713  
    714     # create a new document 
    715     my $doc_obj = new doc ($filename_full_path, "indexed_doc", $self->{'file_rename_method'}); 
    716  
    717  
    718     my $top_section = $doc_obj->get_top_section(); 
    719  
    720     ### <Video Specific> ### 
    721     $doc_obj->set_OIDtype ($processor->{'OIDtype'}, $processor->{'OIDmetadata'});     
    722     ### </Video Specific> ### 
    723  
    724  
    725     $doc_obj->add_utf8_metadata($top_section, "Plugin", "$self->{'plugin_type'}"); 
    726     $doc_obj->add_utf8_metadata($top_section, "FileSize", (-s $filename_full_path)); 
    727   
    728     # sets the UTF8 filename (Source) for display and sets the url ref to URL encoded version 
    729     # of the UTF8 filename (SourceFile) for generated files 
    730     $self->set_Source_metadata($doc_obj, $filename_no_path); 
    731  
    732  
    733     # plugin specific stuff - what args do we need here?? 
    734     unless (defined ($self->process($pluginfo, $base_dir, $file, $metadata, $doc_obj, $gli))) { 
    735     print STDERR "<ProcessingError n='$file'>\n" if ($gli); 
    736     return (-1,undef); 
    737     } 
    738      
    739     # include any metadata passed in from previous plugins  
    740     # note that this metadata is associated with the top level section 
    741     my $section = $doc_obj->get_top_section(); 
    742     # can we merge these two methods?? 
    743     $self->add_associated_files($doc_obj, $filename_full_path); 
    744     $self->extra_metadata ($doc_obj, $section, $metadata); 
    745     $self->auto_extract_metadata($doc_obj); 
    746  
    747     # if we haven't found any Title so far, assign one 
    748     # this was shifted to here from inside read() 
    749     $self->title_fallback($doc_obj,$section,$filename_no_path); 
    750      
    751     $self->add_OID($doc_obj); 
    752  
    753  
    754     ### <Video Specific> ### 
     668    my ($rv,$doc_obj) = $self->SUPER::read_into_doc_obj(@_); 
     669 
     670    if ($rv != 1) { 
     671    return ($rv,$doc_obj); 
     672    } 
     673 
    755674 
    756675    if (($self->{'enablestreaming'}) && ($self->{'extractkeyframes'})) { 
     676    my $section = $doc_obj->get_top_section(); 
    757677    my $oflash_filename = $self->{'oflash_filename'}; 
    758     my $videoconvert = $self->{'videoconvert'}; 
    759678    my ($streamkeyframes_cmd,$ostreamkeyframes_filename)  
    760         = $videoconvert->streamkeyframes_cmd($oflash_filename,$doc_obj,$section); 
     679        = $self->streamkeyframes_cmd($oflash_filename,$doc_obj,$section); 
    761680 
    762681    my $verbosity = $self->{'verbosity'}; 
    763682 
    764     my $streamkeyframes_options = { @{$videoconvert->{'flvtool2_monitor'}}, 
     683    my $streamkeyframes_options = { @{$self->{'flvtool2_monitor'}}, 
    765684                    'message_prefix' => "Stream Keyframes", 
    766685                    'message' => "Reprocessing video stream to add keyframes on timeline" };  
    767 #### this used to be commented out!!! 
    768     $videoconvert->run_general_cmd($streamkeyframes_cmd,$streamkeyframes_options); 
    769     } 
    770     ### </Video Specific> ### 
    771      
    772     return (1,$doc_obj); 
    773 } 
    774  
    775 sub add_dummy_text { 
    776     my $self = shift(@_); 
    777     my ($doc_obj, $section) = @_; 
    778  
    779     # add NoText metadata so we can hide this dummy text in format statements 
    780     $doc_obj->add_metadata($section, "NoText", "1"); 
    781     $doc_obj->add_text($section, &gsprintf::lookup_string("{BasePlugin.dummy_text}",1)); 
    782      
    783 } 
    784  
    785  
    786  
    787  
    788 # do plugin specific processing of doc_obj 
    789 sub process { 
    790     my $self = shift (@_); 
    791     # options?? 
    792     my ($pluginfo, $base_dir, $file, $metadata, $doc_obj, $gli) = @_; 
    793  
    794     my $outhandle = $self->{'outhandle'}; 
    795  
    796     my ($filename_full_path, $filename_no_path) = &util::get_full_filenames($base_dir, $file); 
    797  
    798  
    799     if ($self->{'ffmpeg_installed'}) { 
    800     my $utf8_filename_no_path = $self->filepath_to_utf8($filename_no_path); 
    801     my $url_encoded_filename = &util::rename_file($utf8_filename_no_path, $self->{'file_rename_method'}); 
    802  
    803  
    804     #run convert to get the thumbnail and extract size and type info 
    805     my $result = $self->run_convert($base_dir, $filename_full_path,  
    806                     $url_encoded_filename, $doc_obj); 
    807      
    808     if (!defined $result) { 
    809         if ($gli) { 
    810         print STDERR "<ProcessingError n='$file'>\n"; 
    811         } 
    812         print $outhandle "VideoPlugin: couldn't process \"$filename_full_path\"\n"; 
    813         return (-1,undef); # error during processing 
    814     } 
    815     }  
    816     else { 
    817     if ($gli) { 
    818         &gsprintf(STDERR, "<Warning p='VideoPlugin' r='{VideoConverter.noconversionavailable}: {VideoConverter.".$self->{'no_image_conversion_reason'}."}'>"); 
    819     } 
    820     # all we do is add the original video file as an associated file, and set up srclink etc 
    821     my $assoc_file = $doc_obj->get_assocfile_from_sourcefile(); 
    822     my $section = $doc_obj->get_top_section(); 
    823  
    824     $doc_obj->associate_file($filename_full_path, $assoc_file, "", $section); 
    825  
    826     $doc_obj->add_metadata ($section, "srclink", "<a href=\"_httpprefix_/collect/[collection]/index/assoc/[assocfilepath]/[SourceFile]\">"); 
    827     $doc_obj->add_metadata ($section, "/srclink", "</a>"); 
    828     $doc_obj->add_metadata ($section, "srcicon", "<img src=\"_httpprefix_/collect/[collection]/index/assoc/[assocfilepath]/[SourceFile]\" width=\"100\">"); 
    829  
    830     } 
    831     #we have no text - adds dummy text and NoText metadata 
    832     $self->add_dummy_text($doc_obj, $doc_obj->get_top_section()); 
    833  
    834     return 1; 
    835  
     686 
     687    $self->run_general_cmd($streamkeyframes_cmd,$streamkeyframes_options); 
     688    } 
     689     
     690    return ($rv,$doc_obj); 
    836691} 
    837692 
     
    908763 
    909764 
    910  
    911 sub Doctype { 
     765sub StartDocument {$_[0]->{'PluginObj'}->xml_start_document(@_);} 
     766sub XMLDecl {$_[0]->{'PluginObj'}->xml_xmldecl(@_);} 
     767sub Entity {$_[0]->{'PluginObj'}->xml_entity(@_);} 
     768sub Doctype {$_[0]->{'PluginObj'}->xml_doctype(@_);} 
     769sub StartTag {$_[0]->{'PluginObj'}->xml_start_tag(@_);} 
     770sub EndTag {$_[0]->{'PluginObj'}->xml_end_tag(@_);} 
     771sub Text {$_[0]->{'PluginObj'}->xml_text(@_);} 
     772sub PI {$_[0]->{'PluginObj'}->xml_pi(@_);} 
     773sub EndDocument {$_[0]->{'PluginObj'}->xml_end_document(@_);} 
     774sub Default {$_[0]->{'PluginObj'}->xml_default(@_);} 
     775 
     776 
     777# This Char function overrides the one in XML::Parser::Stream to overcome a 
     778# problem where $expat->{Text} is treated as the return value, slowing 
     779# things down significantly in some cases. 
     780sub Char { 
     781    use bytes;  # Necessary to prevent encoding issues with XML::Parser 2.31+ 
     782    $_[0]->{'Text'} .= $_[1]; 
     783    return undef; 
     784} 
     785 
     786sub xml_start_document { 
     787    my $self = shift(@_); 
    912788    my ($expat, $name, $sysid, $pubid, $internal) = @_; 
    913789 
    914     # my $root_tag = $self->{'root_tag'}; 
     790} 
     791 
     792# Called for XML declarations 
     793sub xml_xmldecl { 
     794    my $self = shift(@_); 
     795    my ($expat, $version, $encoding, $standalone) = @_; 
     796} 
     797 
     798# Called for XML entities 
     799sub xml_entity { 
     800  my $self = shift(@_); 
     801  my ($expat, $name, $val, $sysid, $pubid, $ndata) = @_; 
     802} 
     803 
     804 
     805# Called for DOCTYPE declarations - use die to bail out if this doctype 
     806# is not meant for this plugin 
     807sub xml_doctype { 
     808    my $self = shift(@_); 
     809    my ($expat, $name, $sysid, $pubid, $internal) = @_; 
     810 
     811    # This test used to be done in xml_start_document 
     812    # Moved to here as seems more logical 
    915813 
    916814    if ($name !~ "seg") {    
    917     die "Root tag $name does not match expected <seg>"; 
    918     } 
    919 } 
    920  
    921 sub StartTag { 
     815    die "VideoPlugin: Root tag $name does not match expected <seg>"; 
     816    } 
     817} 
     818 
     819 
     820sub xml_start_tag { 
     821    my $self = shift(@_); 
    922822    my ($expat, $element) = @_; 
    923823 
     
    931831    #$self->{'flowplayer_thumblist'} = "thumbs: \\["; 
    932832 
    933     my $output_dir = $self->{'videoconvert'}->{'cached_dir'}; 
     833    my $output_dir = $self->{'cached_dir'}; 
    934834    my $cue_filename = &util::filename_cat($output_dir,"on_cue.xml"); 
    935835 
     
    945845    my $avg_frame_num = int(($pre_frame_num+$post_frame_num)/2.0)+1; 
    946846 
    947     my $output_dir = $self->{'videoconvert'}->{'cached_dir'}; 
    948     my $ivideo_root = $self->{'videoconvert'}->{'file_root'}; 
     847    my $output_dir = $self->{'cached_dir'}; 
     848    my $ivideo_root = $self->{'cached_file_root'}; 
    949849 
    950850    my $keyframe_index = $self->{'keyframe_index'}; 
     
    1016916} 
    1017917 
    1018 sub EndTag { 
     918sub xml_end_tag { 
     919    my $self = shift(@_); 
    1019920    my ($expat, $element) = @_; 
    1020921 
     
    1032933 
    1033934 
    1034 sub Text { 
    1035     my $text = $_; 
    1036 } 
    1037  
    1038 # This Char function overrides the one in XML::Parser::Stream to overcome a 
    1039 # problem where $expat->{Text} is treated as the return value, slowing 
    1040 # things down significantly in some cases. 
    1041 sub Char { 
    1042   $_[0]->{'Text'} .= $_[1]; 
    1043   return undef; 
    1044 } 
     935 
     936 
     937 
     938# Called just before start or end tags with accumulated non-markup text in 
     939# the $_ variable. 
     940sub xml_text { 
     941    my $self = shift(@_); 
     942    my ($expat) = @_; 
     943} 
     944 
     945# Called for processing instructions. The $_ variable will contain a copy 
     946# of the pi. 
     947sub xml_pi { 
     948    my $self = shift(@_); 
     949    my ($expat, $target, $data) = @_; 
     950} 
     951 
     952# Called at the end of the XML document. 
     953sub xml_end_document { 
     954    my $self = shift(@_); 
     955    my ($expat) = @_; 
     956 
     957    $self->close_document(); 
     958} 
     959 
     960 
     961# Called for any characters not handled by the above functions. 
     962sub xml_default { 
     963    my $self = shift(@_); 
     964    my ($expat, $text) = @_; 
     965} 
     966 
    1045967 
    10469681;