]*>.*?.*?<\\/table>";
### print STDERR "*** filtered table = $filtered_table\n";
$$textref =~ s/$metatable_re/$filtered_table/si;
}
return $metatable_store;
}
my $only_num = 0;
my $only_limit = 100;
sub hms_to_secs
{
my $self = shift @_;
my ($hh,$mm,$ss) = @_;
return $hh*3600 + $mm*60 + $ss;
}
sub timed_event
{
my $self = shift (@_);
my ($front, $tape, $hh, $mm, $ss, $back) = @_;
my $time_encoded = "$hh:$mm:$ss";
my $time_encoded_file = "$hh$mm$ss";
print STDERR "***** time endocde = $time_encoded\n";
my $time_pos = $self->{'time_pos'};
my $time_num = scalar(@{$self->{'time_seq'}});
#### my $videoconvert = $self->{'videoconvert'};
# excerpt from tim_pos -> time_pos + 2;
my $adv_time_pos = $time_pos+2;
$adv_time_pos = $time_num-1 if ($adv_time_pos>=$time_num);
my $adv_time_elem = $self->{'time_seq'}->[$adv_time_pos];
# print STDERR "*** ", substr($adv_time_elem,0,800), "\n";
my ($atape,$ah,$am,$as) = ($adv_time_elem =~ m/(T\d+:) ?(\d\d):(\d\d):(\d\d)(?:\:\d\d)?/);
my $excerpt_len = undef;
my $time_in_secs = $self->hms_to_secs($hh,$mm,$ss);
if (!defined $tape) {
if (defined $self->{'ptape'}) {
$tape = $self->{'ptape'};
}
else {
print STDERR "Warning: First time offset does not define tape side\n";
print STDERR " Defaulting to 1\n";
$tape = "T1:";
}
}
if (!defined $atape) {
$atape = "T1";
}
# print STDERR "**** atape = $atape\n";
# print STDERR "*** tape = $tape\n";
if ($atape eq $tape) {
my $adv_time_in_secs = $self->hms_to_secs($ah,$am,$as);
$excerpt_len = $adv_time_in_secs - $time_in_secs;
}
my $collect_dir = $ENV{'GSDLCOLLECTDIR'};
my $media_type = $self->{'media_type'};
my $media_dir = $self->{'media_dir'};
my $media_root = $self->{'media_root'};
my $media_base_dir = $self->{'media_base_dir'};
my $mp3_html;
my $avi_html;
my $tape_num = "1";
if (defined $media_type) {
my $src_file;
my $src_filename;
if ($media_type eq "video") {
$src_file = "$media_root\_01_$tape_num.VOB";
$src_filename
= &util::filename_cat($media_base_dir, $src_file);
}
elsif ($media_type =~ m/^audio$/) {
$src_file = "$media_root\_Tape$tape_num.mp3";
$src_filename
= &util::filename_cat($media_base_dir, $src_file);
}
else {
print STDERR "Warning: Unrecognised media type: $media_type\n";
}
# Embed link to allow video to stream from marked time event
if ($media_type eq "video") {
# video excerpt
$avi_html .= "";
# save 'time-stamp' as metadata???
}
# Do similar for audio.
# Obviously, pure audio needs to be handled here, but also
# video (and link in to its audio track)
if ($media_type =~ m/^(video|audio)$/) {
my ($voa_root) = ($src_file =~ m/^(.*)\..*?$/);
my $mp3_url = "javascript:audioplayerseek($hh,$mm,$ss)";
$mp3_html = "";
# my $baseexcerpt_url = "_httpcollection_/index/assoc/{If}{[assocfilepath],[assocfilepath],[parent(Top):assocfilepath]}";
}
}
else {
print STDERR "Warning: no media type defined.\n";
}
$only_num++;
$self->{'time_pos'}++;
my $table = "
";
$table .= "$avi_html | " if (defined $avi_html);
$table .= "$mp3_html | ";
$table .= "$time_encoded | ";
$table .= "
";
# my $line = "$front$avi_html $mp3_html $time_encoded$back";
my $line = "$front$table$back";
# Remember tape prefix for next call
$self->{'ptape'} = $tape;
return $line;
}
sub hyperlink_timing_info
{
my ($self, $textref) = @_;
my $media_type = $self->{'media_type'};
my $media_dir = $self->{'media_dir'};
my $media_root = $self->{'media_root'};
##my $front_re = "(?:)?";
my $front_re = "]+class=\"?Timing(?:-.*?)?\"?[^>]*>(?:)?";
my $tape_re = "T\\d+:";
my $tt_re = "\\d\\d";
my $back_re = ".*?(?:<\\/span>\\s*)?(?:<\\/p>)";
my $time_match_re = "$front_re$tt_re:$tt_re:$tt_re$back_re(?:\:$tt_re)?";
# my $time_sub_re = "($front_re)($tape_re)? ?($tt_re):($tt_re):($tt_re)(?:\:$tt_re)?($back_re)";
my $tape_match_re = "$front_re($tape_re)? ?$tt_re:$tt_re:$tt_re(?:\:$tt_re)?$back_re";
my $tape_sub_re = "($front_re)($tt_re):($tt_re):($tt_re)(?:\:$tt_re)?($back_re)";
my @time_seq = ($$textref =~ m/$time_match_re/sgi);
print STDERR "***** got ", scalar(@time_seq), " matches\n";
# artifically add tape information into all time indexes
my $extrapolate_tape = "";
my $num_seq = scalar(@time_seq);
for (my $i=0; $i<$num_seq; $i++) {
my ($curr_tape) = ($time_seq[$i] =~ m/$tape_match_re/sgi);
if (!defined $curr_tape) {
$time_seq[$i] =~ s/$tape_sub_re/$1$extrapolate_tape$2:$3:$4$5/sgi;
}
else {
if ($curr_tape ne $extrapolate_tape) {
$extrapolate_tape = $curr_tape;
}
}
}
$self->{'time_seq'} = \@time_seq;
$self->{'time_pos'} = 0;
# my $front_re = "]+class=\"?Timing(?:-.*?)?\"?[^>]*>(?:)?";
$$textref =~ s/($front_re)(T\d+:)? ?(\d\d):(\d\d):(\d\d)(?:\:\d\d)?.*?((?:<\/span>)?\s*<\/p>)/$self->timed_event($1,$2,$3,$4,$5,$6)/sgie;
##$$textref =~ s/()(T\d+:)? ?(\d\d):(\d\d):(\d\d)(?:\:\d\d)?.*?(<\/span>)/$self->timed_event($1,$2,$3,$4,$5,$6)/sgie;
# knock out any div tags as they interfere with document broken up into sections
$$textref =~ s/<\/?div.*?>//gi;
# Embedded word style infomation is lost by HTMLPlug => make Question
# style use HTML italics
$$textref =~ s/(.*?)<\/p>/
$1<\/i><\/p>/gsi;
}
sub store_block_files
{
my $self =shift (@_);
my ($filename_full_path, $block_hash) = @_;
# Would be better if this were tied to particular HTML docs that
# this plugin has processed.
# For now block all files native to DVD format
if (($filename_full_path =~ m/\.(IFO|BUP)$/) # block info and backup-info data
|| ($filename_full_path =~ m/VIDEO_TS\..*?$/) # block top-level files
|| ($filename_full_path =~ m/VTS_\d\d_\d\.VOB$/) #
) {
$block_hash->{'file_blocks'}->{$filename_full_path} = 1;
}
}
sub read_block {
my $self = shift (@_);
my ($pluginfo, $base_dir, $file, $metadata, $processor, $maxdocs, $total_count, $gli) = @_;
print STDERR "***** TimedHTMLPlugin read_block\n";
my $filename = $file;
$filename = &util::filename_cat ($base_dir, $file) if $base_dir =~ /\w/;
if (($filename =~ m/\.(IFO|BUP)$/) # block info and backup-info data
|| ($filename =~ m/VIDEO_TS\..*?$/) # block top-level files
|| ($filename =~ m/VTS_\d\d_\d\.VOB$/) #
) {
$self->{'num_blocked'} ++;
return (0,undef); # blocked
}
##### || ($filename =~ m/VTS_\d\d_0\.VOB$/) # block menu item for this track
if (defined $self->{'file_blocks'}->{$filename} && $self->{'file_blocks'}->{$filename} == 1){
$self->{'num_blocked'} ++;
return (0,undef); # blocked
}
if (defined $self->{'re_file_block'}) {
my $re_file_blocks = $self->{'re_file_block'};
foreach my $re_file_block (@$re_file_blocks) {
### print STDERR "**** re file block: $filename =~ m $re_file_block\n";
if ($filename =~ m/$re_file_block/) {
### print STDERR "*** blocking $filename\n";
$self->{'num_blocked'} ++;
return (0,undef); # blocked
}
}
}
return $self->SUPER::read_block(@_);
}
sub read_file {
my $self = shift @_;
my ($filename, $encoding, $language, $textref) = @_;
$only_num = 0;
$self->SUPER::read_file(@_);
my $metatable_store = $self->extract_metadata_table($textref);
$self->{'metatable_store'} = $metatable_store;
my ($root, $dirname, $suffix)
= &File::Basename::fileparse($filename, "\\.[^\\.]+\$");
# media defaults
my $media_type = undef;
my $media_dir = $root;
## my $media_root = "VTS_%02d_%02d";
my $media_root = "VTS";
if (defined $metatable_store->{'GS.Media'}) {
$media_type = $metatable_store->{'GS.Media'};
$media_type = lc($media_type);
}
if (defined $metatable_store->{'GS.MediaDirectory'}) {
$media_dir = $metatable_store->{'GS.MediaDirectory'};
}
if (defined $metatable_store->{'GS.MediaRoot'}) {
$media_root = $metatable_store->{'GS.MediaRoot'};
}
$self->{'media_type'} = $media_type;
$self->{'media_dir'} = $media_dir;
$self->{'media_root'} = $media_root;
my $media_base_dir = &util::filename_cat($dirname,$media_dir);
$self->{'media_base_dir'} = $media_base_dir;
$self->{'vob_duration_info'} = &VideoConverter::vob_durations($media_base_dir,"1",$self->{'outhandle'});
## $self->hyperlink_timing_info($textref);
# Convert entities to their UTF8 equivalents
$$textref =~ s/&(lt|gt|amp|quot|nbsp);/&z$1;/go;
$$textref =~ s/&([^;]+);/&ghtml::getcharequiv($1,1)/gseo;
$$textref =~ s/&z(lt|gt|amp|quot|nbsp);/&$1;/go;
}
sub streamable_video
{
my $self = shift (@_);
my ($base_dir,$filename,$doc_obj,$section) = @_;
my $outhandle = $self->{'outhandle'};
my $verbosity = $self->{'verbosity'};
my $media_base_dir = $self->{'media_base_dir'};
#---
# Determine size of input video
my $collect_dir = $ENV{'GSDLCOLLECTDIR'};
my $media_type = $self->{'media_type'};
my $media_dir = $self->{'media_dir'};
my $media_root = $self->{'media_root'};
my $tape_num = "1";
my $src_file;
my $src_filename;
if ($media_type eq "video") {
$src_file = "$media_root\_01_$tape_num.VOB";
$src_filename
= &util::filename_cat($media_base_dir, $src_file);
}
elsif ($media_type =~ m/^audio$/) {
$src_file = "$media_root\_Tape$tape_num.mp3";
$src_filename
= &util::filename_cat($media_base_dir, $src_file);
}
else {
print STDERR "Warning: Unrecognised media type: $media_type\n";
}
if (-e $src_filename) {
$self->{'file_blocks'}->{$src_filename} = 1;
my $re_file_block = $src_filename;
$re_file_block =~ s/_01_$tape_num.VOB$/_\\d+_\\d+.VOB\$/;
push(@{$self->{'re_file_block'}},$re_file_block);
}
my ($video_type, $video_width, $video_height, $video_duration, $video_size,
$vcodec,$vfps,$atype,$afreq,$achan,$arate)
= &VideoConverter::identify($src_filename, $outhandle, $verbosity);
if ($vfps eq "unknown") {
print $outhandle "Unknown framerate, defaulting to 25 frames per second.\n";
$vfps = 25;
}
### print STDERR "*** video duration = $video_duration\n";
my ($dur_hour,$dur_min,$dur_sec)
= ($video_duration =~ m/(\d+):(\d+):(\d+\.\d+)/);
my $total_dur_secs = $dur_hour*3600 + $dur_min*60 + $dur_sec;
$self->{'video-fps'} = $vfps;
$self->{'num-total-frames'} = $total_dur_secs * $vfps;
# shorten duration prcessed for experimentation purposes
my $exp_duration = undef;
my $video_excerpt_duration = $self->{'video_excerpt_duration'};
print STDERR "**** video/audio duration (secs) = $total_dur_secs\n";
if ((defined $video_excerpt_duration) && ($video_excerpt_duration ne "")) {
$exp_duration = $video_excerpt_duration;
my ($hh,$mm,$ss,$ms) = ($exp_duration =~ m/^(\d\d):(\d\d):(\d\d)\.?(\d\d)?/);
my $excerpt_dur_in_secs = $hh * 3600 + $mm * 60 + $ss;
if ($excerpt_dur_in_secs < $total_dur_secs) {
$self->{'num-total-frames'} = $excerpt_dur_in_secs * $vfps; # override calculation for full length duration
}
else {
# clip is already shorter than requested video excerpt duration
# set exp_duration back to undefined
$exp_duration = undef;
}
}
print $outhandle "TimedHTMLPlug: Preparing video files associated with $filename for HTMLPlug\n";
if (defined $exp_duration)
{
print $outhandle "Only encoding first $exp_duration of video.\n";
$self->{'exp_duration'} = $exp_duration;
}
### my $videoconvert
### = new videoconvert($base_dir,$src_filename,$verbosity,$outhandle,$exp_duration);
### $self->{'videoconvert'} = $videoconvert;
# ******
$self->init_cache_for_file($src_filename);
#---
# Generate the Flash FLV format for streaming purposes
my $streaming_bitrate = "256k"; # used to be 192k
my $streaming_size = "360"; # used to be 352 (CIF?)
my $streaming_achan = "1";
my $streaming_arate = "44100";
my $streaming_quality = "high";
#---
# Convert video to flash
#---
my ($stream_cmd,$oflash_filename,$oflash_file)
= $self->stream_cmd($src_filename,
$video_width,$video_height,
$streaming_quality, $streaming_bitrate, $streaming_size,
$streaming_achan, $streaming_arate);
my $streamable_options = { @{$self->{'ffmpeg_monitor'}},
'message_prefix' => "Stream",
'message' => "Generating streamable video: $oflash_file" };
my ($streamable_regenerated,$streamable_result,$streamable_had_error)
= $self->run_cached_general_cmd($stream_cmd,$oflash_filename,$streamable_options);
$self->{'streamable_regenerated'} = $streamable_regenerated;
if (!$streamable_had_error) {
#---
# Make video seekable
#---
my ($streamseekable_cmd,$ostreamseekable_filename) = $self->streamseekable_cmd($oflash_filename);
my $streamseekable_options = { @{$self->{'flvtool2_monitor'}},
'message_prefix' => "Stream Seekable",
'message' => "Reprocessing video stream to be seekable by timeline: $oflash_file" };
if ($streamable_regenerated) {
$self->run_general_cmd($streamseekable_cmd,$streamseekable_options);
}
my $streamable_url = $oflash_file;
## $streamable_url =~ s/ /%20/g;
$doc_obj->add_metadata ($section, "streamablevideo", $streamable_url);
$doc_obj->associate_file($oflash_filename,$oflash_file,"video/flash",
$section);
}
#
# FlowPlayer.swf height+22 pixels
# FlowPlayerBlack.swf height+16 pixels
# FlowPlayerThermo.swf height+16 pixels
# FlowPlayerWhite.swf height+26 pixels
if ($media_type eq "audio") {
$doc_obj->add_metadata ($section, "flashwidth", $video_width);
$doc_obj->add_metadata ($section, "flashheight", 22 + 100);
}
else {
$doc_obj->add_metadata ($section, "flashwidth", $video_width);
$doc_obj->add_metadata ($section, "flashheight", $video_height + 22 + 100);
}
my $base_url = "_httpstreamserverprefix_/collect/[collection]/index/assoc/{If}{[assocfilepath],[assocfilepath],[parent(Top):assocfilepath]}/";
$doc_obj->add_metadata ($section, "baseurl",$base_url);
$self->{'oflash_file'} = $oflash_file;
$self->{'oflash_filename'} = $oflash_filename;
}
sub add_cuepoints
{
my ($self) = shift @_;
my ($doc_obj) = @_;
my $section = $doc_obj->get_top_section();
my $chapters = $doc_obj->get_children($section);
# open file
my $output_dir = $self->{'cached_dir'};
my $cue_filename = &util::filename_cat($output_dir,"on_cue.xml");
open(CUEOUT,">$cue_filename")
|| die "Unable to open $cue_filename: $!\n";
print CUEOUT "\n";
my $cc = 0;
foreach my $chap (@$chapters)
{
$cc++;
my $chap_text = $doc_obj->get_text($chap);
my @cuepoints
= ($chap_text =~ m/(?:T\d+:)? ?(\d\d:\d\d:\d\d)(?:\:\d\d)?/gsi);
my $count = 0;
foreach my $cuepoint (@cuepoints)
{
my ($hh,$mm,$ss) = ($cuepoint =~ m/(\d\d):(\d\d):(\d\d)/);
my $cuept_insecs = $self->hms_to_secs($hh,$mm,$ss);
if ($cuept_insecs < 2) {
$cuept_insecs += 2;
}
my $cuept_inmsecs = 1000 * $cuept_insecs;
$doc_obj->add_metadata($section,"cuepoint",$cuept_inmsecs);
my $chap_title = $doc_obj->get_metadata_element($chap,"Title");
# Navigation point (used in seeking)
# print CUEOUT " \n";
# print CUEOUT " $chap_title\n";
# print CUEOUT " $cuept_inmsecs\n";
# print CUEOUT " \n";
# print CUEOUT " dummy\n";
# print CUEOUT " $chap\n";
# print CUEOUT " $count\n";
# print CUEOUT " $cuept_inmsecs\n";
# print CUEOUT " \n";
# print CUEOUT " navigation\n";
# print CUEOUT " \n";
# print CUEOUT " \n";
# print CUEOUT " $chap_title\n";
# print CUEOUT " $cuept_inmsecs\n";
# print CUEOUT " \n";
# print CUEOUT " foo.jpg\n";
# print CUEOUT " \n";
# print CUEOUT " navigation\n";
# print CUEOUT " \n";
if ($count==0)
{
# Event (used to hook in to javascipt callback)
# Only do for the first in each chapter
print CUEOUT " \n";
## print CUEOUT " Test $cc\n";
print CUEOUT " $chap_title\n";
print CUEOUT " $cuept_inmsecs\n";
print CUEOUT " \n";
print CUEOUT " $cuept_inmsecs\n";
print CUEOUT " $chap\n";
print CUEOUT " \n";
print CUEOUT " event\n";
## print CUEOUT " actionscript\n";
print CUEOUT " \n";
}
$count++;
}
}
print CUEOUT "
\n";
close(CUEOUT);
#### my $videoconvert = $self->{'videoconvert'};
my $oflash_filename = $self->{'oflash_filename'};
my $oflash_file = $self->{'oflash_file'};
my ($streamcuepts_cmd,$ostreamcuepts_filename) = $self->streamcuepts_cmd($oflash_filename);
my $verbosity = $self->{'verbosity'};
my $outhandle = $self->{'outhandle'};
my $streamcuepts_options = { @{$self->{'flvtool2_monitor'}},
'message_prefix' => "Stream Cue Points",
'message' => "Reprocessing video stream to add cuepoints on timeline: $oflash_file" };
my $streamable_regenerated = $self->{'streamable_regenerated'};
if ($streamable_regenerated) {
$self->run_general_cmd($streamcuepts_cmd,$streamcuepts_options);
}
}
sub process {
my $self = shift (@_);
my ($textref, $pluginfo, $base_dir, $file, $metadata, $doc_obj, $gli) = @_;
my $filename = $file;
$filename = &util::filename_cat ($base_dir, $file) if $base_dir =~ /\w/;
my $top_section = $doc_obj->get_top_section();
$self->streamable_video($base_dir,$filename,$doc_obj,$top_section);
$self->hyperlink_timing_info($textref);
foreach my $mp3_excerpt (@{$self->{'mp3_excerpts'}}) {
my $omp3_file = $mp3_excerpt->{'omp3_file'};
my $omp3_filename = $mp3_excerpt->{'omp3_filename'};
$doc_obj->associate_file($omp3_filename,$omp3_file,"audio/mp3",
$top_section);
}
$self->{'mp3_excerpts'} = undef;
my $metatable_store = $self->{'metatable_store'};
foreach my $metaname (keys %$metatable_store) {
my $metadata = $metatable_store->{$metaname};
$doc_obj->add_metadata($top_section,$metaname,$metadata);
}
$self->{'metatable_store'} = undef;
my $ret_val = $self->SUPER::process(@_);
$self->add_cuepoints($doc_obj);
return $ret_val;
}
sub replace_images {
my $self = shift (@_);
my ($front, $link, $back, $base_dir,
$file, $doc_obj, $section) = @_;
if ($link =~ m/^"?_.*?_"?\//) {
# variety of greenstone macro, so let through unchanged
return $front . $link . $back ;
}
return $self->SUPER::replace_images(@_);
}
sub replace_href_links {
my $self = shift (@_);
my ($front, $link, $back, $base_dir, $file, $doc_obj, $section) = @_;
if ($link =~ m/^"?_.*?_"?/) {
# variety of greenstone macro, so let through unchanged
return $front . $link . $back ;
}
return $self->SUPER::replace_href_links(@_);
}
1;