source: gs2-extensions/video-and-audio/trunk/src/perllib/plugins/AudioPlugin.pm@ 29018

Last change on this file since 29018 was 29018, checked in by jmt12, 10 years ago

Fixing bug where plugin tried to generating streaming video of filetype 'disabled' is streaming was set to disabled

File size: 12.4 KB
Line 
1######################################################################
2#
3# AudioPlugin.pm -- plugin for processing video largely based on ImagePlug
4# A component of the Greenstone digital library software
5# from the New Zealand Digital Library Project at the
6# University of Waikato, New Zealand.
7#
8# Copyright (C) 1999 New Zealand Digital Library Project
9#
10# This program is free software; you can redistribute it and/or modify
11# it under the terms of the GNU General Public License as published by
12# the Free Software Foundation; either version 2 of the License, or
13# (at your option) any later version.
14#
15# This program is distributed in the hope that it will be useful,
16# but WITHOUT ANY WARRANTY; without even the implied warranty of
17# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18# GNU General Public License for more details.
19#
20# You should have received a copy of the GNU General Public License
21# along with this program; if not, write to the Free Software
22# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23#
24###########################################################################
25
26package AudioPlugin;
27
28use strict;
29no strict 'refs'; # allow filehandles to be variables and viceversa
30no strict 'subs';
31
32use gsprintf;
33
34use MultimediaPlugin;
35use AudioConverter;
36
37sub BEGIN {
38 @AudioPlugin::ISA = ('MultimediaPlugin', 'AudioConverter');
39}
40
41
42my $enable_streaming_list =
43 [{'name' => "disabled", 'desc' => "Do not create any audio file optimised for streaming over the Internet."}, # Don't move this entry - it's used as a sentinel value below
44 {'name' => "flv", 'desc' => "Uses the FLV format for streaming media. Better to target old computers."},
45 {'name' => "mp4", 'desc' => "Uses the MP4 container with H264 and AAC codecs. Better quality at very low bitrates but more ressources intensive."},
46 {'name' => "mp3", 'desc' => "(audio only) Uses MP3 for psuedo streaming."}
47 ];
48
49my $streaming_mime_types = { "flv" => "video/flv",
50 "mp4" => "video/mp4",
51 "mp3" => "audio/mpeg" };
52
53my $arguments =
54 [ { 'name' => "process_exp",
55 'desc' => "{BasePlugin.process_exp}",
56 'type' => "regexp",
57 'deft' => &get_default_process_exp(),
58 'reqd' => "no" },
59 { 'name' => "converttotype",
60 'desc' => "{AudioPlugin.converttotype}",
61 'type' => "string",
62 'deft' => "",
63 'reqd' => "no" },
64 { 'name' => "converttobitrate",
65 'desc' => "{AudioPlugin.converttobitrate}",
66 'type' => "string",
67 'deft' => "128k",
68 'reqd' => "no" },
69 { 'name' => "streamingbitrate",
70 'desc' => "{AudioPlugin.streamingbitrate}",
71 'type' => "string",
72 'deft' => "200k",
73 'reqd' => "no" },
74
75 { 'name' => "enable_streaming",
76 'desc' => "{MultimediaPlug.enable_streaming}",
77 'type' => "enum",
78 'list' => $enable_streaming_list,
79 'deft' => @{$enable_streaming_list}[0]->{'name'},
80 'reqd' => "no" }
81
82];
83
84my $options = { 'name' => "AudioPlugin",
85 'desc' => "{AudioPlugin.desc}",
86 'abstract' => "no",
87 'inherits' => "yes",
88 'args' => $arguments };
89
90
91sub new {
92 my ($class) = shift (@_);
93 my ($pluginlist,$inputargs,$hashArgOptLists) = @_;
94 push(@$pluginlist, $class);
95
96 push(@{$hashArgOptLists->{"ArgList"}},@{$arguments});
97 push(@{$hashArgOptLists->{"OptList"}},$options);
98
99 new AudioConverter($pluginlist, $inputargs, $hashArgOptLists);
100 my $self = new MultimediaPlugin($pluginlist, $inputargs, $hashArgOptLists);
101
102
103 return bless $self, $class;
104}
105
106
107sub begin {
108 my $self = shift (@_);
109 my ($pluginfo, $base_dir, $processor, $maxdocs) = @_;
110
111 $self->SUPER::begin(@_);
112 $self->AudioConverter::begin(@_);
113}
114
115
116sub init {
117 my $self = shift (@_);
118 my ($verbosity, $outhandle, $failhandle) = @_;
119
120 if ($self->{'enable_streaming'} eq @{$enable_streaming_list}[0]->{'name'})
121 {
122 print $outhandle "!Warning! AudioPlugin will generate no streamable audio as format set to \'disabled\'\n";
123 }
124
125 $self->SUPER::init(@_);
126 $self->AudioConverter::init(@_);
127}
128
129
130sub get_default_process_exp {
131 my $self = shift (@_);
132
133 return q^(?i)\.(mp3|au|aiff?|aifc|wav|ogg|flac|shn)$^;
134}
135
136
137
138# Create the keyframes, thumbnail and screenview images, and discover
139# the Video's size, width, and height using the ffmpeg utility.
140
141sub run_convert {
142 my $self = shift (@_);
143 my $base_dir = shift (@_);
144 my $filename = shift (@_); # filename with full path
145 my $file = shift (@_); # filename without path
146 my $doc_obj = shift (@_);
147
148 my $section = $doc_obj->get_top_section();
149
150 my $verbosity = $self->{'verbosity'};
151 my $outhandle = $self->{'outhandle'};
152
153 # check the filename is okay
154 return 0 if ($file eq "" || $filename eq "");
155
156 my $minimumsize = $self->{'minimumsize'};
157 if (defined $minimumsize && (-s $filename < $minimumsize)) {
158 print $outhandle "AudioPlugin: \"$filename\" too small, skipping\n"
159 if ($verbosity > 1);
160 }
161
162 my ($aduration,$asize,$atype,$afreq,$achan,$arate)
163 = &AudioConverter::identify($filename, $outhandle, $verbosity);
164
165 if ($aduration eq "N/A") {
166 print $outhandle "Unable to determine duration of $file\n";
167 $aduration = undef;
168 }
169
170 my ($dur_hour,$dur_min,$dur_sec)
171 = ($aduration =~ m/(\d+):(\d+):(\d+\.\d+)/);
172
173 my $total_dur_secs = undef;
174
175 if (defined $dur_hour && defined $dur_min && defined *dur_sec) {
176 $total_dur_secs = $dur_hour*3600 + $dur_min*60 + $dur_sec;
177 }
178
179
180 # Convert the audio to a new type (if required).
181 my $converttotype = $self->{'converttotype'};
182 my $converttosize = $self->{'converttosize'};
183
184 # shorten duration prcessed for experimentation purposes
185 my $exp_duration = undef;
186
187 my $excerpt_duration = $self->{'excerpt_duration'};
188
189 if ((defined $excerpt_duration) && ($excerpt_duration ne "")) {
190 $exp_duration = $excerpt_duration;
191 my ($hh,$mm,$ss,$ms) = ($exp_duration =~ m/^(\d\d):(\d\d):(\d\d)\.?(\d\d)?/);
192 my $excerpt_dur_in_secs = $hh * 3600 + $mm * 60 + $ss;
193
194 if (defined $total_dur_secs) {
195 if ($excerpt_dur_in_secs > $total_dur_secs) {
196 # clip is already shorter than requested video excerpt duration
197 # set exp_duration back to undefined
198 $exp_duration = undef;
199 }
200 }
201 else {
202 $exp_duration = undef;
203 }
204 }
205
206
207 if (defined $exp_duration)
208 {
209 print $outhandle "Only encoding first $exp_duration of video.\n";
210 $self->{'exp_duration'} = $exp_duration;
211 }
212
213 my $ascii_only_filenames = $self->{'use_ascii_only_filenames'};
214
215 $self->init_cache_for_file($filename);
216
217 my $originalfilename = undef;
218 my $type = "unknown";
219
220 my $output_dir = $self->{'cached_dir'};
221 my $iaudio_root = $self->{'cached_file_root'};
222
223 my $convertto_regenerated = 0;
224 if (($converttotype ne "" && $filename !~ m/$converttotype$/i)) {
225
226 $originalfilename = $filename;
227
228 my ($convertto_command,$ofilename,$ofile) = $self->audio_convertto_cmd($filename,$converttotype);
229
230 my $convertto_result;
231 my $convertto_error;
232
233 my $convertto_options = { @{$self->{'ffmpeg_monitor'}},
234 'message_prefix' => "Convert to",
235 'message' => "Converting audio to $converttotype" };
236
237 ($convertto_regenerated,$convertto_result,$convertto_error)
238 = $self->run_cached_general_cmd($convertto_command,$filename,$ofilename,$convertto_options);
239
240 $type = $converttotype;
241 $filename = $ofilename;
242 $file = $ofile;
243
244 ($aduration,$asize,$atype,$afreq,$achan,$arate)
245 = &AudioConverter::identify($ofilename, $outhandle, $verbosity);
246
247 if ($aduration eq "N/A") {
248 print $outhandle "Unable to determine duration of converted $ofile\n";
249 $aduration = undef;
250 }
251 }
252
253
254 # Add the audio metadata
255
256 my $file_unicode = pack("U0C*", map { ord($_) } split(//,$file)); # force explicitly to unicode
257
258 $file_unicode =~ s/\x{2018}|\x{2019}|\x{201C}|\x{201D}//g; # remove smart quotes as cause problem in URL for video server
259 $file_unicode =~ s/\x{2013}/\-/g; # change en-dash to '-' as again causes problems for video server
260
261## print STDERR "**** file metadata = ", &gsprintf::debug_unicode_string($file_unicode), "\n";
262
263
264 # split filename on char if specified
265 if ($file_unicode =~ m/\-/) {
266
267 my @file_split = split(/\s*\-\s*/,$file_unicode);
268 my $creator = shift @file_split;
269 my $title = shift @file_split;
270
271 $title =~ s/\..*?$//;
272 $title =~ s/^(.)/\u$1/;
273
274 $doc_obj->add_utf8_metadata($section,"Title",$title);
275
276 my @creator_split = split(/\s+and\s+/,$creator);
277 foreach my $c (@creator_split) {
278 $doc_obj->add_utf8_metadata($section,"Creator",$c);
279 }
280 }
281
282 $file = $file_unicode;
283# my $filemeta = $self->filename_to_utf8_metadata($file);
284# my $filemeta_url_safe = $self->url_safe($filemeta);
285
286 my $filemeta_url_safe = &unicode::filename_to_url($file);
287
288 $doc_obj->add_utf8_metadata ($section, "Audio", $filemeta_url_safe);
289
290 # Also want to set filename as 'Source' metadata to be
291 # consistent with other plugins
292# $doc_obj->add_utf8_metadata ($section, "Source", $filemeta_url_safe);
293
294
295 if ($atype ne " ") {
296 $type = $atype;
297 }
298
299 $doc_obj->add_metadata ($section, "FileFormat", $type);
300 $doc_obj->add_metadata ($section, "FileSize", $asize);
301
302 $doc_obj->add_metadata ($section, "AudioType", $atype);
303 $doc_obj->add_metadata ($section, "AudioDuration", $aduration);
304
305 $doc_obj->add_metadata ($section, "AudioFreq", $afreq);
306 $doc_obj->add_metadata ($section, "AudioChannels", $achan);
307 $doc_obj->add_metadata ($section, "AudioRate", $arate);
308
309 $doc_obj->add_utf8_metadata ($section, "srclink",
310 "<a href=\"_httpprefix_/collect/[collection]/index/assoc/[assocfilepath]/[Audio]\">");
311 $doc_obj->add_utf8_metadata ($section, "/srclink", "</a>");
312 $doc_obj->add_metadata ($section, "srcicon", "[AudioType]");
313
314 # Add the image as an associated file
315 $doc_obj->associate_file($filename,$file,"audio/$type",$section);
316
317
318
319
320 my $streamable_regenerated = 0;
321 my $optionally_run_general_cmd
322 = ($convertto_regenerated) ? "regenerate_general_cmd" : "run_cached_general_cmd";
323
324 if ($self->{'enable_streaming'})
325 {
326 # The first entry in the "enabled_streaming_list" array should be "disabled"
327 if ($self->{'enable_streaming'} ne @{$enable_streaming_list}[0]->{'name'})
328 {
329 my $enable_streaming = $self->{'enable_streaming'};
330
331 # Even if the original file or 'converttotype' is set to MP3
332 # we'll still go ahead and generate this MP3 as it might very
333 # well have different settings (such as a lower sample rate)
334
335 my $streaming_bitrate = $self->{'streamingbitrate'};
336 my $streaming_size = $self->{'streamingsize'};
337
338 my $ifilename = $originalfilename || $filename;
339
340 my ($stream_cmd,$mp_filename,$mp_file);
341 if ($enable_streaming eq "mp4") {
342 ($stream_cmd,$mp_filename,$mp_file)
343 = $self->audio_mp4_stream_cmd($ifilename, $streaming_size,
344 $streaming_bitrate);
345 }
346 else {
347 ($stream_cmd,$mp_filename,$mp_file)
348 = $self->audio_stream_ffmpeg_cmd($ifilename, $enable_streaming,
349 $streaming_bitrate, $streaming_size,);
350
351 }
352
353
354 my $streamable_options = { @{$self->{'ffmpeg_monitor'}},
355 'message_prefix' => "Stream",
356 'message' => "Generating streamable audio: $mp_file" };
357
358 my $streamable_result;
359 my $streamable_had_error;
360 ($streamable_regenerated,$streamable_result,$streamable_had_error)
361 = $self->$optionally_run_general_cmd($stream_cmd,$ifilename,$mp_filename,$streamable_options);
362
363 if (!$streamable_had_error)
364 {
365 my $streamable_url = $mp_file;
366 my $streamable_url_safe = $self->url_safe($streamable_url);
367
368 $doc_obj->add_utf8_metadata ($section, "streamableaudio", $streamable_url_safe);
369 $doc_obj->associate_file($mp_filename,$mp_file,"audio/mpeg", $section);
370 }
371
372 # The following aren't currently used
373 $self->{'mp_file'} = $mp_file;
374 $self->{'mp_filename'} = $mp_filename;
375 }
376 }
377 return $type;
378}
379
380
381
382
383sub read_into_doc_obj {
384 my $self = shift (@_);
385 my ($pluginfo, $base_dir, $file, $block_hash, $metadata, $processor, $maxdocs, $total_count, $gli) = @_;
386
387 $self->{'media_type'} = "audio";
388
389 my ($rv,$doc_obj) = $self->SUPER::read_into_doc_obj(@_);
390
391 if ($rv != 1) {
392 return ($rv,$doc_obj);
393 }
394
395 $self->{'media_type'} = undef;
396
397 return ($rv,$doc_obj);
398}
399
400
401
4021;
403
404
405
406
407
408
409
410
411
412
413
Note: See TracBrowser for help on using the repository browser.