Ignore:
Timestamp:
2009-02-20T11:40:23+13:00 (15 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

File:
1 edited

Legend:

Unmodified
Added
Removed
  • 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;
Note: See TracChangeset for help on using the changeset viewer.