###################################################################### # # MultimediaPlugin.pm -- internal plugin to support processing audio and video # A component of the Greenstone digital library software # from the New Zealand Digital Library Project at the # University of Waikato, New Zealand. # # Copyright (C) 1999 New Zealand Digital Library Project # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # ########################################################################### # -- Largely modeled on how ImagePlugin works package MultimediaPlugin; use strict; no strict 'refs'; # allow filehandles to be variables and viceversa no strict 'subs'; use gsprintf; use BasePlugin; sub BEGIN { @MultimediaPlugin::ISA = ('BasePlugin'); if (!defined $ENV{'GEXTVIDEO'}) { print STDERR "Warning: Greenstone Video extension not detected.\n"; } } # do later the description my $arguments = [ { 'name' => "use_ascii_only_filenames", 'desc' => "{VideoPlug.use_ascii_only_filenames}", 'type' => "flag", 'reqd' => "no" }, { 'name' => "excerpt_duration", 'desc' => "{MultimediaPlug.excerpt_duration}", 'type' => "string", 'deft' => "", 'reqd' => "no" }, { 'name' => "converttotype", 'desc' => "{MultimediaPlug.converttotype}", 'type' => "string", 'deft' => "", 'reqd' => "no" }, { 'name' => "enable_streaming", 'desc' => "{MultimediaPlug.enable_streaming}", 'type' => "enum", 'list' => [{'name' => "disabled", 'desc' => "Do not create any video file optimised for streaming over the Internet."}, {'name' => "flv", 'desc' => "Uses the FLV format for streaming media. Better to target old computers."}, {'name' => "mp4", 'desc' => "Uses the MP4 container with H264 and AAC codecs. Better quality at very low bitrates but more ressources intensive."}, {'name' => "direct", 'desc' => "Uses the original video file for direct streaming. The video player should be compatible with the format used!"}], 'deft' => "disabled", 'reqd' => "no" }]; my $options = { 'name' => "MultimediaPlugin", 'desc' => "{MultimediaPlug.desc}", 'abstract' => "yes", 'inherits' => "yes", 'args' => $arguments }; sub new { my ($class) = shift (@_); my ($pluginlist,$inputargs,$hashArgOptLists) = @_; push(@$pluginlist, $class); push(@{$hashArgOptLists->{"ArgList"}},@{$arguments}); push(@{$hashArgOptLists->{"OptList"}},$options); my $self = new BasePlugin($pluginlist, $inputargs, $hashArgOptLists); # Check that ffmpeg is installed and available on the path (except for Windows 95/98) # This test is "inherited" from ImagePlugin where ImageMagick support # for building cannot be used on these two versions of Windows as they # do not have enough flexablity in the use of stdout and stderr to # support how our code works. It seems reasonable to assume the # same is true for MultimediaPlugin work using ffmpeg. if (($ENV{'GSDLOS'} ne "windows" || Win32::IsWinNT())) { my $result = `ffmpeg -h 2>&1`; if (!defined $result || $result !~ m/^FFmpeg version/m) { $self->{'ffmpeg_installed'} = 0; if (defined $result) { print STDERR $result; } else { print STDERR "Unable to find ffmpeg\n"; } } else { $self->{'ffmpeg_installed'} = 1; } } return bless $self, $class; } sub begin { my $self = shift (@_); my ($pluginfo, $base_dir, $processor, $maxdocs) = @_; $self->SUPER::begin(@_); $self->{'base_dir'} = $base_dir; } sub init { my $self = shift (@_); my ($verbosity, $outhandle, $failhandle) = @_; $self->SUPER::init(@_); } # Want to hash on doc.xml rather than original file which in the case of # audio and video can be HUGE sub get_oid_hash_type { my $self = shift (@_); return "hash_on_ga_xml"; } # Create the keyframes, thumbnail and screenview images, and discover # the Video's size, width, and height using the ffmpeg utility. sub run_convert { my $self = shift (@_); die "$self The inheriting class must implement run_convert method"; } sub read_into_doc_obj { my $self = shift (@_); my ($pluginfo, $base_dir, $file, $block_hash, $metadata, $processor, $maxdocs, $total_count, $gli) = @_; my $outhandle = $self->{'outhandle'}; # should we move this to read? What about secondary plugins? print STDERR "\n" if ($gli); print $outhandle "$self->{'plugin_type'} processing $file\n" if $self->{'verbosity'} > 1; my ($filename_full_path, $filename_no_path) = &util::get_full_filenames($base_dir, $file); # create a new document my $doc_obj = new doc ($filename_full_path, "indexed_doc", $self->{'file_rename_method'}); my $top_section = $doc_obj->get_top_section(); $doc_obj->add_utf8_metadata($top_section, "Plugin", "$self->{'plugin_type'}"); $doc_obj->add_utf8_metadata($top_section, "FileSize", (-s $filename_full_path)); # sets the UTF8 filename (Source) for display and sets the url ref to URL encoded version # of the UTF8 filename (SourceFile) for generated files $self->set_Source_metadata($doc_obj, $filename_no_path); # plugin specific stuff - what args do we need here?? unless (defined ($self->process($pluginfo, $base_dir, $file, $metadata, $doc_obj, $gli))) { print STDERR "\n" if ($gli); return (-1,undef); } # include any metadata passed in from previous plugins # note that this metadata is associated with the top level section my $section = $doc_obj->get_top_section(); # can we merge these two methods?? $self->add_associated_files($doc_obj, $filename_full_path); $self->extra_metadata ($doc_obj, $section, $metadata); $self->auto_extract_metadata($doc_obj); # if we haven't found any Title so far, assign one # this was shifted to here from inside read() $self->title_fallback($doc_obj,$section,$filename_no_path); $self->add_OID($doc_obj); $self->post_process_doc_obj($pluginfo, $base_dir, $file, $metadata, $doc_obj, $gli); return (1,$doc_obj); } sub add_dummy_text { my $self = shift(@_); my ($doc_obj, $section) = @_; # add NoText metadata so we can hide this dummy text in format statements $doc_obj->add_metadata($section, "NoText", "1"); $doc_obj->add_text($section, &gsprintf::lookup_string("{BasePlugin.dummy_text}",1)); } # do plugin specific processing of doc_obj sub process { my $self = shift (@_); # options?? my ($pluginfo, $base_dir, $file, $metadata, $doc_obj, $gli) = @_; my $outhandle = $self->{'outhandle'}; my ($filename_full_path, $filename_no_path) = &util::get_full_filenames($base_dir, $file); if ($self->{'ffmpeg_installed'}) { my $utf8_filename_no_path = $self->filepath_to_utf8($filename_no_path); my $url_encoded_filename = &util::rename_file($utf8_filename_no_path, $self->{'file_rename_method'}); #run convert to get the thumbnail and extract size and type info my $result = $self->run_convert($base_dir, $filename_full_path, $url_encoded_filename, $doc_obj); if (!defined $result) { if ($gli) { print STDERR "\n"; } print $outhandle "MultimediaPlugin: couldn't process \"$filename_full_path\"\n"; return (-1,undef); # error during processing } } else { if ($gli) { &gsprintf(STDERR, ""); } # all we do is add the original video file as an associated file, and set up srclink etc my $assoc_file = $doc_obj->get_assocfile_from_sourcefile(); my $section = $doc_obj->get_top_section(); $doc_obj->associate_file($filename_full_path, $assoc_file, "", $section); $doc_obj->add_metadata ($section, "srclink", ""); $doc_obj->add_metadata ($section, "/srclink", ""); $doc_obj->add_metadata ($section, "srcicon", ""); } #we have no text - adds dummy text and NoText metadata $self->add_dummy_text($doc_obj, $doc_obj->get_top_section()); return 1; } 1;