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

Last change on this file since 28369 was 28369, checked in by davidb, 11 years ago

Changes to work with newer version of ffmpeg

File size: 11.9 KB
RevLine 
[18556]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
[26532]42my $enable_streaming_list =
43 [{'name' => "disabled", 'desc' => "Do not create any audio file optimised for streaming over the Internet."},
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
[18556]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",
[26532]73 'reqd' => "no" },
[18556]74
[26532]75 { 'name' => "enable_streaming",
76 'desc' => "{MultimediaPlug.enable_streaming}",
77 'type' => "enum",
78 'list' => $enable_streaming_list,
79 'deft' => "disabled",
80 'reqd' => "no" }
81
82];
83
[18556]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 $self->SUPER::init(@_);
121 $self->AudioConverter::init(@_);
122}
123
124
125sub get_default_process_exp {
126 my $self = shift (@_);
127
128 return q^(?i)\.(mp3|au|aiff?|aifc|wav|ogg|flac|shn)$^;
129}
130
131
132
133# Create the keyframes, thumbnail and screenview images, and discover
134# the Video's size, width, and height using the ffmpeg utility.
135
136sub run_convert {
137 my $self = shift (@_);
138 my $base_dir = shift (@_);
139 my $filename = shift (@_); # filename with full path
140 my $file = shift (@_); # filename without path
141 my $doc_obj = shift (@_);
142
143 my $section = $doc_obj->get_top_section();
144
145 my $verbosity = $self->{'verbosity'};
146 my $outhandle = $self->{'outhandle'};
147
148 # check the filename is okay
149 return 0 if ($file eq "" || $filename eq "");
150
151 my $minimumsize = $self->{'minimumsize'};
152 if (defined $minimumsize && (-s $filename < $minimumsize)) {
153 print $outhandle "AudioPlugin: \"$filename\" too small, skipping\n"
154 if ($verbosity > 1);
155 }
156
157 my ($aduration,$asize,$atype,$afreq,$achan,$arate)
158 = &AudioConverter::identify($filename, $outhandle, $verbosity);
159
160 if ($aduration eq "N/A") {
161 print $outhandle "Unable to determine duration of $file\n";
162 $aduration = undef;
163 }
164
165 my ($dur_hour,$dur_min,$dur_sec)
166 = ($aduration =~ m/(\d+):(\d+):(\d+\.\d+)/);
167
168 my $total_dur_secs = undef;
169
170 if (defined $dur_hour && defined $dur_min && defined *dur_sec) {
171 $total_dur_secs = $dur_hour*3600 + $dur_min*60 + $dur_sec;
172 }
173
174
[20003]175 # Convert the audio to a new type (if required).
[18556]176 my $converttotype = $self->{'converttotype'};
177 my $converttosize = $self->{'converttosize'};
178
179 # shorten duration prcessed for experimentation purposes
180 my $exp_duration = undef;
181
182 my $excerpt_duration = $self->{'excerpt_duration'};
183
184 if ((defined $excerpt_duration) && ($excerpt_duration ne "")) {
185 $exp_duration = $excerpt_duration;
186 my ($hh,$mm,$ss,$ms) = ($exp_duration =~ m/^(\d\d):(\d\d):(\d\d)\.?(\d\d)?/);
187 my $excerpt_dur_in_secs = $hh * 3600 + $mm * 60 + $ss;
188
189 if (defined $total_dur_secs) {
190 if ($excerpt_dur_in_secs > $total_dur_secs) {
191 # clip is already shorter than requested video excerpt duration
192 # set exp_duration back to undefined
193 $exp_duration = undef;
194 }
195 }
196 else {
197 $exp_duration = undef;
198 }
199 }
200
201
202 if (defined $exp_duration)
203 {
204 print $outhandle "Only encoding first $exp_duration of video.\n";
205 $self->{'exp_duration'} = $exp_duration;
206 }
207
208 my $ascii_only_filenames = $self->{'use_ascii_only_filenames'};
209
210 $self->init_cache_for_file($filename);
211
212 my $originalfilename = undef;
213 my $type = "unknown";
214
215 my $output_dir = $self->{'cached_dir'};
216 my $iaudio_root = $self->{'cached_file_root'};
217
218 my $convertto_regenerated = 0;
219 if (($converttotype ne "" && $filename !~ m/$converttotype$/i)) {
220
221 $originalfilename = $filename;
222
[26532]223 my ($convertto_command,$ofilename,$ofile) = $self->audio_convertto_cmd($filename,$converttotype);
[18556]224
225 my $convertto_result;
226 my $convertto_error;
227
228 my $convertto_options = { @{$self->{'ffmpeg_monitor'}},
229 'message_prefix' => "Convert to",
230 'message' => "Converting audio to $converttotype" };
231
232 ($convertto_regenerated,$convertto_result,$convertto_error)
[26532]233 = $self->run_cached_general_cmd($convertto_command,$filename,$ofilename,$convertto_options);
[18556]234
235 $type = $converttotype;
[28369]236 $filename = $ofilename;
237 $file = $ofile;
238
239 ($aduration,$asize,$atype,$afreq,$achan,$arate)
240 = &AudioConverter::identify($ofilename, $outhandle, $verbosity);
241
242 if ($aduration eq "N/A") {
243 print $outhandle "Unable to determine duration of converted $ofile\n";
244 $aduration = undef;
245 }
[18556]246 }
247
248
249 # Add the audio metadata
250
251 my $file_unicode = pack("U0C*", map { ord($_) } split(//,$file)); # force explicitly to unicode
252
253 $file_unicode =~ s/\x{2018}|\x{2019}|\x{201C}|\x{201D}//g; # remove smart quotes as cause problem in URL for video server
254 $file_unicode =~ s/\x{2013}/\-/g; # change en-dash to '-' as again causes problems for video server
255
256## print STDERR "**** file metadata = ", &gsprintf::debug_unicode_string($file_unicode), "\n";
257
258
259 # split filename on char if specified
260 if ($file_unicode =~ m/\-/) {
261
262 my @file_split = split(/\s*\-\s*/,$file_unicode);
263 my $creator = shift @file_split;
264 my $title = shift @file_split;
265
266 $title =~ s/\..*?$//;
267 $title =~ s/^(.)/\u$1/;
268
269 $doc_obj->add_utf8_metadata($section,"Title",$title);
270
271 my @creator_split = split(/\s+and\s+/,$creator);
272 foreach my $c (@creator_split) {
273 $doc_obj->add_utf8_metadata($section,"Creator",$c);
274 }
275 }
276
277 $file = $file_unicode;
[25346]278# my $filemeta = $self->filename_to_utf8_metadata($file);
279# my $filemeta_url_safe = $self->url_safe($filemeta);
[18556]280
[25346]281 my $filemeta_url_safe = &unicode::filename_to_url($file);
282
[18556]283 $doc_obj->add_utf8_metadata ($section, "Audio", $filemeta_url_safe);
284
285 # Also want to set filename as 'Source' metadata to be
286 # consistent with other plugins
[25346]287# $doc_obj->add_utf8_metadata ($section, "Source", $filemeta_url_safe);
[18556]288
289
290 if ($atype ne " ") {
291 $type = $atype;
292 }
293
294 $doc_obj->add_metadata ($section, "FileFormat", $type);
295 $doc_obj->add_metadata ($section, "FileSize", $asize);
296
297 $doc_obj->add_metadata ($section, "AudioType", $atype);
298 $doc_obj->add_metadata ($section, "AudioDuration", $aduration);
299
300 $doc_obj->add_metadata ($section, "AudioFreq", $afreq);
301 $doc_obj->add_metadata ($section, "AudioChannels", $achan);
302 $doc_obj->add_metadata ($section, "AudioRate", $arate);
303
304 $doc_obj->add_utf8_metadata ($section, "srclink",
305 "<a href=\"_httpprefix_/collect/[collection]/index/assoc/[assocfilepath]/[Audio]\">");
306 $doc_obj->add_utf8_metadata ($section, "/srclink", "</a>");
307 $doc_obj->add_metadata ($section, "srcicon", "[AudioType]");
308
309 # Add the image as an associated file
310 $doc_obj->associate_file($filename,$file,"audio/$type",$section);
311
312
313
314
315 my $streamable_regenerated = 0;
316 my $optionally_run_general_cmd
317 = ($convertto_regenerated) ? "regenerate_general_cmd" : "run_cached_general_cmd";
318
319 if ($self->{'enable_streaming'}) {
320
[25346]321 my $enable_streaming = $self->{'enable_streaming'};
322
[18556]323 # Even if the original file or 'converttotype' is set to MP3
324 # we'll still go ahead and generate this MP3 as it might very
325 # well have different settings (such as a lower sample rate)
326
327 my $streaming_bitrate = $self->{'streamingbitrate'};
328 my $streaming_size = $self->{'streamingsize'};
329
[25346]330 my $ifilename = $originalfilename || $filename;
[18556]331
[25346]332 my ($stream_cmd,$mp_filename,$mp_file);
333 if ($enable_streaming eq "mp4") {
334 ($stream_cmd,$mp_filename,$mp_file)
335 = $self->audio_mp4_stream_cmd($ifilename, $streaming_size,
336 $streaming_bitrate);
337 }
338 else {
339 ($stream_cmd,$mp_filename,$mp_file)
340 = $self->audio_stream_ffmpeg_cmd($ifilename, $enable_streaming,
341 $streaming_bitrate, $streaming_size,);
342
343 }
[18556]344
345
346 my $streamable_options = { @{$self->{'ffmpeg_monitor'}},
347 'message_prefix' => "Stream",
[25346]348 'message' => "Generating streamable audio: $mp_file" };
[18556]349
350 my $streamable_result;
351 my $streamable_had_error;
352 ($streamable_regenerated,$streamable_result,$streamable_had_error)
[25346]353 = $self->$optionally_run_general_cmd($stream_cmd,$ifilename,$mp_filename,$streamable_options);
[18556]354
355 if (!$streamable_had_error) {
356
[25346]357 my $streamable_url = $mp_file;
[18556]358 my $streamable_url_safe = $self->url_safe($streamable_url);
359
360 $doc_obj->add_utf8_metadata ($section, "streamableaudio", $streamable_url_safe);
[25346]361 $doc_obj->associate_file($mp_filename,$mp_file,"audio/mpeg",
[18556]362 $section);
363 }
364
365 # The following aren't currently used
366
[25346]367 $self->{'mp_file'} = $mp_file;
368 $self->{'mp_filename'} = $mp_filename;
[18556]369 }
370
371
372 return $type;
373}
374
375
376
377
[20003]378sub read_into_doc_obj {
379 my $self = shift (@_);
380 my ($pluginfo, $base_dir, $file, $block_hash, $metadata, $processor, $maxdocs, $total_count, $gli) = @_;
[18556]381
[20003]382 $self->{'media_type'} = "audio";
[18556]383
[20003]384 my ($rv,$doc_obj) = $self->SUPER::read_into_doc_obj(@_);
385
386 if ($rv != 1) {
387 return ($rv,$doc_obj);
388 }
389
390 $self->{'media_type'} = undef;
391
392 return ($rv,$doc_obj);
393}
394
395
396
[18556]3971;
398
399
400
401
402
403
404
405
406
407
408
Note: See TracBrowser for help on using the repository browser.