diff options
Diffstat (limited to 'src/plugins/previewopus_extractor.c')
-rw-r--r-- | src/plugins/previewopus_extractor.c | 1221 |
1 files changed, 0 insertions, 1221 deletions
diff --git a/src/plugins/previewopus_extractor.c b/src/plugins/previewopus_extractor.c deleted file mode 100644 index 45ea420..0000000 --- a/src/plugins/previewopus_extractor.c +++ /dev/null | |||
@@ -1,1221 +0,0 @@ | |||
1 | /* | ||
2 | This file is part of libextractor. | ||
3 | Copyright Copyright (C) 2008, 2013 Bruno Cabral and Christian Grothoff | ||
4 | |||
5 | libextractor is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published | ||
7 | by the Free Software Foundation; either version 3, or (at your | ||
8 | option) any later version. | ||
9 | |||
10 | libextractor is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with libextractor; see the file COPYING. If not, write to the | ||
17 | Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | ||
18 | Boston, MA 02110-1301, USA. | ||
19 | */ | ||
20 | /** | ||
21 | * @file previewopus_extractor.c | ||
22 | * @author Bruno Cabral | ||
23 | * @author Christian Grothoff | ||
24 | * @brief this extractor produces a binary encoded | ||
25 | * audio snippet of music/video files using ffmpeg libs. | ||
26 | * | ||
27 | * Based on ffmpeg samples. | ||
28 | * | ||
29 | * Note that ffmpeg has a few issues: | ||
30 | * (1) there are no recent official releases of the ffmpeg libs | ||
31 | * (2) ffmpeg has a history of having security issues (parser is not robust) | ||
32 | * | ||
33 | * So this plugin cannot be recommended for system with high security | ||
34 | *requirements. | ||
35 | */ | ||
36 | #include "platform.h" | ||
37 | #include "extractor.h" | ||
38 | #include <magic.h> | ||
39 | |||
40 | #include <libavutil/avutil.h> | ||
41 | #include <libavutil/audio_fifo.h> | ||
42 | #include <libavutil/opt.h> | ||
43 | #include <libavutil/mathematics.h> | ||
44 | #include <libavformat/avformat.h> | ||
45 | #include <libavcodec/avcodec.h> | ||
46 | #include <libswscale/swscale.h> | ||
47 | #include <libavresample/avresample.h> | ||
48 | |||
49 | |||
50 | /** | ||
51 | * Set to 1 to enable debug output. | ||
52 | */ | ||
53 | #define DEBUG 0 | ||
54 | |||
55 | /** | ||
56 | * Set to 1 to enable a output file for testing. | ||
57 | */ | ||
58 | #define OUTPUT_FILE 0 | ||
59 | |||
60 | |||
61 | /** | ||
62 | * Maximum size in bytes for the preview. | ||
63 | */ | ||
64 | #define MAX_SIZE (28 * 1024) | ||
65 | |||
66 | /** | ||
67 | * HardLimit for file | ||
68 | */ | ||
69 | #define HARD_LIMIT_SIZE (50 * 1024) | ||
70 | |||
71 | |||
72 | /** The output bit rate in kbit/s */ | ||
73 | #define OUTPUT_BIT_RATE 28000 | ||
74 | /** The number of output channels */ | ||
75 | #define OUTPUT_CHANNELS 2 | ||
76 | /** The audio sample output format */ | ||
77 | #define OUTPUT_SAMPLE_FORMAT AV_SAMPLE_FMT_S16 | ||
78 | |||
79 | |||
80 | /** Our output buffer*/ | ||
81 | static unsigned char *buffer; | ||
82 | |||
83 | /** Actual output buffer size */ | ||
84 | static int totalSize; | ||
85 | |||
86 | /** | ||
87 | * Convert an error code into a text message. | ||
88 | * @param error Error code to be converted | ||
89 | * @return Corresponding error text (not thread-safe) | ||
90 | */ | ||
91 | static char *const | ||
92 | get_error_text (const int error) | ||
93 | { | ||
94 | static char error_buffer[255]; | ||
95 | av_strerror (error, error_buffer, sizeof(error_buffer)); | ||
96 | return error_buffer; | ||
97 | } | ||
98 | |||
99 | |||
100 | /** | ||
101 | * Read callback. | ||
102 | * | ||
103 | * @param opaque the 'struct EXTRACTOR_ExtractContext' | ||
104 | * @param buf where to write data | ||
105 | * @param buf_size how many bytes to read | ||
106 | * @return -1 on error (or for unknown file size) | ||
107 | */ | ||
108 | static int | ||
109 | read_cb (void *opaque, | ||
110 | uint8_t *buf, | ||
111 | int buf_size) | ||
112 | { | ||
113 | struct EXTRACTOR_ExtractContext *ec = opaque; | ||
114 | void *data; | ||
115 | ssize_t ret; | ||
116 | |||
117 | ret = ec->read (ec->cls, &data, buf_size); | ||
118 | if (ret <= 0) | ||
119 | return ret; | ||
120 | memcpy (buf, data, ret); | ||
121 | return ret; | ||
122 | } | ||
123 | |||
124 | |||
125 | /** | ||
126 | * Seek callback. | ||
127 | * | ||
128 | * @param opaque the 'struct EXTRACTOR_ExtractContext' | ||
129 | * @param offset where to seek | ||
130 | * @param whence how to seek; AVSEEK_SIZE to return file size without seeking | ||
131 | * @return -1 on error (or for unknown file size) | ||
132 | */ | ||
133 | static int64_t | ||
134 | seek_cb (void *opaque, | ||
135 | int64_t offset, | ||
136 | int whence) | ||
137 | { | ||
138 | struct EXTRACTOR_ExtractContext *ec = opaque; | ||
139 | |||
140 | if (AVSEEK_SIZE == whence) | ||
141 | return ec->get_size (ec->cls); | ||
142 | return ec->seek (ec->cls, offset, whence); | ||
143 | } | ||
144 | |||
145 | |||
146 | /** | ||
147 | * write callback. | ||
148 | * | ||
149 | * @param opaque NULL | ||
150 | * @param pBuffer to write | ||
151 | * @param pBufferSize , amount to write | ||
152 | * @return 0 on error | ||
153 | */ | ||
154 | static int | ||
155 | writePacket (void *opaque, | ||
156 | unsigned char *pBuffer, | ||
157 | int pBufferSize) | ||
158 | { | ||
159 | int sizeToCopy = pBufferSize; | ||
160 | |||
161 | if ( (totalSize + pBufferSize) > HARD_LIMIT_SIZE) | ||
162 | sizeToCopy = HARD_LIMIT_SIZE - totalSize; | ||
163 | |||
164 | memcpy (buffer + totalSize, pBuffer, sizeToCopy); | ||
165 | totalSize += sizeToCopy; | ||
166 | return sizeToCopy; | ||
167 | } | ||
168 | |||
169 | |||
170 | /** | ||
171 | * Open an output file and the required encoder. | ||
172 | * Also set some basic encoder parameters. | ||
173 | * Some of these parameters are based on the input file's parameters. | ||
174 | */ | ||
175 | static int | ||
176 | open_output_file ( | ||
177 | AVCodecContext *input_codec_context, | ||
178 | AVFormatContext **output_format_context, | ||
179 | AVCodecContext **output_codec_context) | ||
180 | { | ||
181 | AVStream *stream = NULL; | ||
182 | AVCodec *output_codec = NULL; | ||
183 | AVIOContext *io_ctx; | ||
184 | int error; | ||
185 | unsigned char *iob; | ||
186 | |||
187 | if (NULL == (iob = av_malloc (16 * 1024))) | ||
188 | return AVERROR_EXIT; | ||
189 | if (NULL == (io_ctx = avio_alloc_context (iob, 16 * 1024, | ||
190 | AVIO_FLAG_WRITE, NULL, | ||
191 | NULL, | ||
192 | &writePacket /* no writing */, | ||
193 | NULL))) | ||
194 | { | ||
195 | av_free (iob); | ||
196 | return AVERROR_EXIT; | ||
197 | } | ||
198 | if (NULL == ((*output_format_context) = avformat_alloc_context ())) | ||
199 | { | ||
200 | av_free (io_ctx); | ||
201 | return AVERROR_EXIT; | ||
202 | } | ||
203 | (*output_format_context)->pb = io_ctx; | ||
204 | |||
205 | /** Guess the desired container format based on the file extension. */ | ||
206 | if (! ((*output_format_context)->oformat = av_guess_format (NULL, | ||
207 | "file.ogg", | ||
208 | NULL))) | ||
209 | { | ||
210 | #if DEBUG | ||
211 | fprintf (stderr, "Could not find output file format\n"); | ||
212 | #endif | ||
213 | error = AVERROR (ENOSYS); | ||
214 | goto cleanup; | ||
215 | } | ||
216 | |||
217 | /** Find the encoder to be used by its name. */ | ||
218 | if (! (output_codec = avcodec_find_encoder (AV_CODEC_ID_OPUS))) | ||
219 | { | ||
220 | #if DEBUG | ||
221 | fprintf (stderr, "Could not find an OPUS encoder.\n"); | ||
222 | #endif | ||
223 | error = AVERROR (ENOSYS); | ||
224 | goto cleanup; | ||
225 | } | ||
226 | |||
227 | /** Create a new audio stream in the output file container. */ | ||
228 | if (! (stream = avformat_new_stream (*output_format_context, output_codec))) | ||
229 | { | ||
230 | #if DEBUG | ||
231 | fprintf (stderr, "Could not create new stream\n"); | ||
232 | #endif | ||
233 | error = AVERROR (ENOMEM); | ||
234 | goto cleanup; | ||
235 | } | ||
236 | |||
237 | /** Save the encoder context for easiert access later. */ | ||
238 | *output_codec_context = stream->codec; | ||
239 | |||
240 | /** | ||
241 | * Set the basic encoder parameters. | ||
242 | * The input file's sample rate is used to avoid a sample rate conversion. | ||
243 | */ | ||
244 | (*output_codec_context)->channels = OUTPUT_CHANNELS; | ||
245 | (*output_codec_context)->channel_layout = av_get_default_channel_layout ( | ||
246 | OUTPUT_CHANNELS); | ||
247 | (*output_codec_context)->sample_rate = 48000; // Opus need 48000 | ||
248 | (*output_codec_context)->sample_fmt = AV_SAMPLE_FMT_S16; | ||
249 | (*output_codec_context)->bit_rate = OUTPUT_BIT_RATE; | ||
250 | |||
251 | /** Open the encoder for the audio stream to use it later. */ | ||
252 | if ((error = avcodec_open2 (*output_codec_context, output_codec, NULL)) < 0) | ||
253 | { | ||
254 | #if DEBUG | ||
255 | fprintf (stderr, "Could not open output codec (error '%s')\n", | ||
256 | get_error_text (error)); | ||
257 | #endif | ||
258 | goto cleanup; | ||
259 | } | ||
260 | return 0; | ||
261 | |||
262 | cleanup: | ||
263 | av_free (io_ctx); | ||
264 | return error < 0 ? error : AVERROR_EXIT; | ||
265 | } | ||
266 | |||
267 | |||
268 | /** Initialize one data packet for reading or writing. */ | ||
269 | static void | ||
270 | init_packet (AVPacket *packet) | ||
271 | { | ||
272 | av_init_packet (packet); | ||
273 | /** Set the packet data and size so that it is recognized as being empty. */ | ||
274 | packet->data = NULL; | ||
275 | packet->size = 0; | ||
276 | } | ||
277 | |||
278 | |||
279 | /** Initialize one audio frame for reading from the input file */ | ||
280 | static int | ||
281 | init_input_frame (AVFrame **frame) | ||
282 | { | ||
283 | *frame = av_frame_alloc (); | ||
284 | if (NULL == *frame) | ||
285 | { | ||
286 | #if DEBUG | ||
287 | fprintf (stderr, "Could not allocate input frame\n"); | ||
288 | #endif | ||
289 | return AVERROR (ENOMEM); | ||
290 | } | ||
291 | return 0; | ||
292 | } | ||
293 | |||
294 | |||
295 | /** | ||
296 | * Initialize the audio resampler based on the input and output codec settings. | ||
297 | * If the input and output sample formats differ, a conversion is required | ||
298 | * libavresample takes care of this, but requires initialization. | ||
299 | */ | ||
300 | static int | ||
301 | init_resampler (AVCodecContext *input_codec_context, | ||
302 | AVCodecContext *output_codec_context, | ||
303 | AVAudioResampleContext **resample_context) | ||
304 | { | ||
305 | /** | ||
306 | * Only initialize the resampler if it is necessary, i.e., | ||
307 | * if and only if the sample formats differ. | ||
308 | */ | ||
309 | if ((input_codec_context->sample_fmt != output_codec_context->sample_fmt) || | ||
310 | (input_codec_context->channels != output_codec_context->channels) ) | ||
311 | { | ||
312 | int error; | ||
313 | |||
314 | /** Create a resampler context for the conversion. */ | ||
315 | if (! (*resample_context = avresample_alloc_context ())) | ||
316 | { | ||
317 | #if DEBUG | ||
318 | fprintf (stderr, "Could not allocate resample context\n"); | ||
319 | #endif | ||
320 | return AVERROR (ENOMEM); | ||
321 | } | ||
322 | |||
323 | |||
324 | /** | ||
325 | * Set the conversion parameters. | ||
326 | * Default channel layouts based on the number of channels | ||
327 | * are assumed for simplicity (they are sometimes not detected | ||
328 | * properly by the demuxer and/or decoder). | ||
329 | */av_opt_set_int (*resample_context, "in_channel_layout", | ||
330 | av_get_default_channel_layout ( | ||
331 | input_codec_context->channels), 0); | ||
332 | av_opt_set_int (*resample_context, "out_channel_layout", | ||
333 | av_get_default_channel_layout ( | ||
334 | output_codec_context->channels), 0); | ||
335 | av_opt_set_int (*resample_context, "in_sample_rate", | ||
336 | input_codec_context->sample_rate, 0); | ||
337 | av_opt_set_int (*resample_context, "out_sample_rate", | ||
338 | output_codec_context->sample_rate, 0); | ||
339 | av_opt_set_int (*resample_context, "in_sample_fmt", | ||
340 | input_codec_context->sample_fmt, 0); | ||
341 | av_opt_set_int (*resample_context, "out_sample_fmt", | ||
342 | output_codec_context->sample_fmt, 0); | ||
343 | |||
344 | /** Open the resampler with the specified parameters. */ | ||
345 | if ((error = avresample_open (*resample_context)) < 0) | ||
346 | { | ||
347 | #if DEBUG | ||
348 | fprintf (stderr, "Could not open resample context\n"); | ||
349 | #endif | ||
350 | avresample_free (resample_context); | ||
351 | return error; | ||
352 | } | ||
353 | } | ||
354 | return 0; | ||
355 | } | ||
356 | |||
357 | |||
358 | /** Initialize a FIFO buffer for the audio samples to be encoded. */ | ||
359 | static int | ||
360 | init_fifo (AVAudioFifo **fifo) | ||
361 | { | ||
362 | /** Create the FIFO buffer based on the specified output sample format. */ | ||
363 | if (! (*fifo = av_audio_fifo_alloc (OUTPUT_SAMPLE_FORMAT, OUTPUT_CHANNELS, | ||
364 | 1))) | ||
365 | { | ||
366 | #if DEBUG | ||
367 | fprintf (stderr, "Could not allocate FIFO\n"); | ||
368 | #endif | ||
369 | return AVERROR (ENOMEM); | ||
370 | } | ||
371 | return 0; | ||
372 | } | ||
373 | |||
374 | |||
375 | /** Write the header of the output file container. */ | ||
376 | static int | ||
377 | write_output_file_header (AVFormatContext *output_format_context) | ||
378 | { | ||
379 | int error; | ||
380 | if ((error = avformat_write_header (output_format_context, NULL)) < 0) | ||
381 | { | ||
382 | #if DEBUG | ||
383 | fprintf (stderr, "Could not write output file header (error '%s')\n", | ||
384 | get_error_text (error)); | ||
385 | #endif | ||
386 | return error; | ||
387 | } | ||
388 | return 0; | ||
389 | } | ||
390 | |||
391 | |||
392 | /** Decode one audio frame from the input file. */ | ||
393 | static int | ||
394 | decode_audio_frame (AVFrame *frame, | ||
395 | AVFormatContext *input_format_context, | ||
396 | AVCodecContext *input_codec_context, int audio_stream_index, | ||
397 | int *data_present, int *finished) | ||
398 | { | ||
399 | /** Packet used for temporary storage. */ | ||
400 | AVPacket input_packet; | ||
401 | int error; | ||
402 | init_packet (&input_packet); | ||
403 | |||
404 | /** Read one audio frame from the input file into a temporary packet. */ | ||
405 | while (1) | ||
406 | { | ||
407 | if ((error = av_read_frame (input_format_context, &input_packet)) < 0) | ||
408 | { | ||
409 | /** If we are the the end of the file, flush the decoder below. */ | ||
410 | if (error == AVERROR_EOF) | ||
411 | { | ||
412 | #if DEBUG | ||
413 | fprintf (stderr, "EOF in decode_audio\n"); | ||
414 | #endif | ||
415 | *finished = 1; | ||
416 | } | ||
417 | else | ||
418 | { | ||
419 | #if DEBUG | ||
420 | fprintf (stderr, "Could not read frame (error '%s')\n", | ||
421 | get_error_text (error)); | ||
422 | #endif | ||
423 | return error; | ||
424 | } | ||
425 | } | ||
426 | |||
427 | if (input_packet.stream_index == audio_stream_index) | ||
428 | break; | ||
429 | } | ||
430 | |||
431 | /** | ||
432 | * Decode the audio frame stored in the temporary packet. | ||
433 | * The input audio stream decoder is used to do this. | ||
434 | * If we are at the end of the file, pass an empty packet to the decoder | ||
435 | * to flush it. | ||
436 | */if ((error = avcodec_decode_audio4 (input_codec_context, frame, | ||
437 | data_present, &input_packet)) < 0) | ||
438 | { | ||
439 | #if DEBUG | ||
440 | fprintf (stderr, "Could not decode frame (error '%s')\n", | ||
441 | get_error_text (error)); | ||
442 | #endif | ||
443 | av_packet_unref (&input_packet); | ||
444 | return error; | ||
445 | } | ||
446 | |||
447 | /** | ||
448 | * If the decoder has not been flushed completely, we are not finished, | ||
449 | * so that this function has to be called again. | ||
450 | */ | ||
451 | if (*finished && *data_present) | ||
452 | *finished = 0; | ||
453 | av_packet_unref (&input_packet); | ||
454 | return 0; | ||
455 | } | ||
456 | |||
457 | |||
458 | /** | ||
459 | * Initialize a temporary storage for the specified number of audio samples. | ||
460 | * The conversion requires temporary storage due to the different format. | ||
461 | * The number of audio samples to be allocated is specified in frame_size. | ||
462 | */ | ||
463 | static int | ||
464 | init_converted_samples (uint8_t ***converted_input_samples, int*out_linesize, | ||
465 | AVCodecContext *output_codec_context, | ||
466 | int frame_size) | ||
467 | { | ||
468 | int error; | ||
469 | |||
470 | /** | ||
471 | * Allocate as many pointers as there are audio channels. | ||
472 | * Each pointer will later point to the audio samples of the corresponding | ||
473 | * channels (although it may be NULL for interleaved formats). | ||
474 | */if (! (*converted_input_samples = calloc (output_codec_context->channels, | ||
475 | sizeof(**converted_input_samples)))) | ||
476 | { | ||
477 | #if DEBUG | ||
478 | fprintf (stderr, "Could not allocate converted input sample pointers\n"); | ||
479 | #endif | ||
480 | return AVERROR (ENOMEM); | ||
481 | } | ||
482 | |||
483 | /** | ||
484 | * Allocate memory for the samples of all channels in one consecutive | ||
485 | * block for convenience. | ||
486 | */ | ||
487 | if ((error = av_samples_alloc (*converted_input_samples, out_linesize, | ||
488 | output_codec_context->channels, | ||
489 | frame_size, | ||
490 | output_codec_context->sample_fmt, 0)) < 0) | ||
491 | { | ||
492 | #if DEBUG | ||
493 | fprintf (stderr, | ||
494 | "Could not allocate converted input samples (error '%s')\n", | ||
495 | get_error_text (error)); | ||
496 | #endif | ||
497 | av_freep (&(*converted_input_samples)[0]); | ||
498 | free (*converted_input_samples); | ||
499 | return error; | ||
500 | } | ||
501 | return 0; | ||
502 | } | ||
503 | |||
504 | |||
505 | /** | ||
506 | * Convert the input audio samples into the output sample format. | ||
507 | * The conversion happens on a per-frame basis, the size of which is specified | ||
508 | * by frame_size. | ||
509 | */ | ||
510 | static int | ||
511 | convert_samples (uint8_t **input_data, | ||
512 | uint8_t **converted_data, const int in_sample, const int | ||
513 | out_sample, const int out_linesize, | ||
514 | AVAudioResampleContext *resample_context) | ||
515 | { | ||
516 | int error; | ||
517 | |||
518 | /** Convert the samples using the resampler. */ | ||
519 | if ((error = avresample_convert (resample_context, converted_data, | ||
520 | out_linesize, | ||
521 | out_sample, input_data, 0, in_sample)) < 0) | ||
522 | { | ||
523 | #if DEBUG | ||
524 | fprintf (stderr, "Could not convert input samples (error '%s')\n", | ||
525 | get_error_text (error)); | ||
526 | #endif | ||
527 | return error; | ||
528 | } | ||
529 | |||
530 | |||
531 | /** | ||
532 | * Perform a sanity check so that the number of converted samples is | ||
533 | * not greater than the number of samples to be converted. | ||
534 | * If the sample rates differ, this case has to be handled differently | ||
535 | */if (avresample_available (resample_context)) | ||
536 | { | ||
537 | #if DEBUG | ||
538 | fprintf (stderr, "%i Converted samples left over\n",avresample_available ( | ||
539 | resample_context)); | ||
540 | #endif | ||
541 | } | ||
542 | |||
543 | |||
544 | return 0; | ||
545 | } | ||
546 | |||
547 | |||
548 | /** Add converted input audio samples to the FIFO buffer for later processing. */ | ||
549 | static int | ||
550 | add_samples_to_fifo (AVAudioFifo *fifo, | ||
551 | uint8_t **converted_input_samples, | ||
552 | const int frame_size) | ||
553 | { | ||
554 | int error; | ||
555 | |||
556 | /** | ||
557 | * Make the FIFO as large as it needs to be to hold both, | ||
558 | * the old and the new samples. | ||
559 | */ | ||
560 | if ((error = av_audio_fifo_realloc (fifo, av_audio_fifo_size (fifo) | ||
561 | + frame_size)) < 0) | ||
562 | { | ||
563 | #if DEBUG | ||
564 | fprintf (stderr, "Could not reallocate FIFO\n"); | ||
565 | #endif | ||
566 | return error; | ||
567 | } | ||
568 | |||
569 | /** Store the new samples in the FIFO buffer. */ | ||
570 | if (av_audio_fifo_write (fifo, (void **) converted_input_samples, | ||
571 | frame_size) < frame_size) | ||
572 | { | ||
573 | #if DEBUG | ||
574 | fprintf (stderr, "Could not write data to FIFO\n"); | ||
575 | #endif | ||
576 | return AVERROR_EXIT; | ||
577 | } | ||
578 | return 0; | ||
579 | } | ||
580 | |||
581 | |||
582 | /** | ||
583 | * Read one audio frame from the input file, decodes, converts and stores | ||
584 | * it in the FIFO buffer. | ||
585 | */ | ||
586 | static int | ||
587 | read_decode_convert_and_store (AVAudioFifo *fifo, | ||
588 | AVFormatContext *input_format_context, | ||
589 | AVCodecContext *input_codec_context, | ||
590 | AVCodecContext *output_codec_context, | ||
591 | AVAudioResampleContext *resampler_context, int | ||
592 | audio_stream_index, | ||
593 | int *finished) | ||
594 | { | ||
595 | /** Temporary storage of the input samples of the frame read from the file. */ | ||
596 | AVFrame *input_frame = NULL; | ||
597 | /** Temporary storage for the converted input samples. */ | ||
598 | uint8_t **converted_input_samples = NULL; | ||
599 | int data_present; | ||
600 | int ret = AVERROR_EXIT; | ||
601 | |||
602 | /** Initialize temporary storage for one input frame. */ | ||
603 | if (init_input_frame (&input_frame)) | ||
604 | { | ||
605 | #if DEBUG | ||
606 | fprintf (stderr, "Failed at init frame\n"); | ||
607 | #endif | ||
608 | goto cleanup; | ||
609 | |||
610 | } | ||
611 | /** Decode one frame worth of audio samples. */ | ||
612 | if (decode_audio_frame (input_frame, input_format_context, | ||
613 | input_codec_context, audio_stream_index, | ||
614 | &data_present, finished)) | ||
615 | { | ||
616 | #if DEBUG | ||
617 | fprintf (stderr, "Failed at decode audio\n"); | ||
618 | #endif | ||
619 | |||
620 | goto cleanup; | ||
621 | |||
622 | } | ||
623 | /** | ||
624 | * If we are at the end of the file and there are no more samples | ||
625 | * in the decoder which are delayed, we are actually finished. | ||
626 | * This must not be treated as an error. | ||
627 | */if (*finished && ! data_present) | ||
628 | { | ||
629 | ret = 0; | ||
630 | #if DEBUG | ||
631 | fprintf (stderr, "Failed at finished or no data\n"); | ||
632 | #endif | ||
633 | goto cleanup; | ||
634 | } | ||
635 | /** If there is decoded data, convert and store it */ | ||
636 | if (data_present) | ||
637 | { | ||
638 | int out_linesize; | ||
639 | // FIX ME: I'm losing samples, but can't get it to work. | ||
640 | int out_samples = avresample_available (resampler_context) | ||
641 | + avresample_get_delay (resampler_context) | ||
642 | + input_frame->nb_samples; | ||
643 | |||
644 | |||
645 | // fprintf(stderr, "Input nbsamples %i out_samples: %i \n",input_frame->nb_samples,out_samples); | ||
646 | |||
647 | /** Initialize the temporary storage for the converted input samples. */ | ||
648 | if (init_converted_samples (&converted_input_samples, &out_linesize, | ||
649 | output_codec_context, | ||
650 | out_samples)) | ||
651 | { | ||
652 | #if DEBUG | ||
653 | fprintf (stderr, "Failed at init_converted_samples\n"); | ||
654 | #endif | ||
655 | goto cleanup; | ||
656 | } | ||
657 | |||
658 | /** | ||
659 | * Convert the input samples to the desired output sample format. | ||
660 | * This requires a temporary storage provided by converted_input_samples. | ||
661 | */ | ||
662 | if (convert_samples (input_frame->extended_data, converted_input_samples, | ||
663 | input_frame->nb_samples, out_samples, out_linesize, | ||
664 | resampler_context)) | ||
665 | { | ||
666 | |||
667 | |||
668 | #if DEBUG | ||
669 | fprintf (stderr, "Failed at convert_samples, input frame %i \n", | ||
670 | input_frame->nb_samples); | ||
671 | #endif | ||
672 | goto cleanup; | ||
673 | } | ||
674 | /** Add the converted input samples to the FIFO buffer for later processing. */ | ||
675 | if (add_samples_to_fifo (fifo, converted_input_samples, | ||
676 | out_samples)) | ||
677 | { | ||
678 | #if DEBUG | ||
679 | fprintf (stderr, "Failed at add_samples_to_fifo\n"); | ||
680 | #endif | ||
681 | goto cleanup; | ||
682 | } | ||
683 | ret = 0; | ||
684 | } | ||
685 | ret = 0; | ||
686 | |||
687 | cleanup: | ||
688 | if (converted_input_samples) | ||
689 | { | ||
690 | av_freep (&converted_input_samples[0]); | ||
691 | free (converted_input_samples); | ||
692 | } | ||
693 | av_frame_free (&input_frame); | ||
694 | return ret; | ||
695 | } | ||
696 | |||
697 | |||
698 | /** | ||
699 | * Initialize one input frame for writing to the output file. | ||
700 | * The frame will be exactly frame_size samples large. | ||
701 | */ | ||
702 | static int | ||
703 | init_output_frame (AVFrame **frame, | ||
704 | AVCodecContext *output_codec_context, | ||
705 | int frame_size) | ||
706 | { | ||
707 | int error; | ||
708 | |||
709 | /** Create a new frame to store the audio samples. */ | ||
710 | *frame = av_frame_alloc (); | ||
711 | if (NULL == *frame) | ||
712 | { | ||
713 | #if DEBUG | ||
714 | fprintf (stderr, "Could not allocate output frame\n"); | ||
715 | #endif | ||
716 | return AVERROR_EXIT; | ||
717 | } | ||
718 | |||
719 | /** | ||
720 | * Set the frame's parameters, especially its size and format. | ||
721 | * av_frame_get_buffer needs this to allocate memory for the | ||
722 | * audio samples of the frame. | ||
723 | * Default channel layouts based on the number of channels | ||
724 | * are assumed for simplicity. | ||
725 | */(*frame)->nb_samples = frame_size; | ||
726 | (*frame)->channel_layout = output_codec_context->channel_layout; | ||
727 | (*frame)->format = output_codec_context->sample_fmt; | ||
728 | (*frame)->sample_rate = output_codec_context->sample_rate; | ||
729 | |||
730 | |||
731 | // fprintf(stderr, "%i %i \n",frame_size , (*frame)->format,(*frame)->sample_rate); | ||
732 | |||
733 | /** | ||
734 | * Allocate the samples of the created frame. This call will make | ||
735 | * sure that the audio frame can hold as many samples as specified. | ||
736 | */ | ||
737 | if ((error = av_frame_get_buffer (*frame, 0)) < 0) | ||
738 | { | ||
739 | #if DEBUG | ||
740 | fprintf (stderr, "Could allocate output frame samples (error '%s')\n", | ||
741 | get_error_text (error)); | ||
742 | #endif | ||
743 | av_frame_free (frame); | ||
744 | return error; | ||
745 | } | ||
746 | |||
747 | return 0; | ||
748 | } | ||
749 | |||
750 | |||
751 | /** Encode one frame worth of audio to the output file. */ | ||
752 | static int | ||
753 | encode_audio_frame (AVFrame *frame, | ||
754 | AVFormatContext *output_format_context, | ||
755 | AVCodecContext *output_codec_context, | ||
756 | int *data_present) | ||
757 | { | ||
758 | /** Packet used for temporary storage. */ | ||
759 | AVPacket output_packet; | ||
760 | int error; | ||
761 | init_packet (&output_packet); | ||
762 | |||
763 | /** | ||
764 | * Encode the audio frame and store it in the temporary packet. | ||
765 | * The output audio stream encoder is used to do this. | ||
766 | */ | ||
767 | if ((error = avcodec_encode_audio2 (output_codec_context, &output_packet, | ||
768 | frame, data_present)) < 0) | ||
769 | { | ||
770 | #if DEBUG | ||
771 | fprintf (stderr, "Could not encode frame (error '%s')\n", | ||
772 | get_error_text (error)); | ||
773 | #endif | ||
774 | av_packet_unref (&output_packet); | ||
775 | return error; | ||
776 | } | ||
777 | |||
778 | /** Write one audio frame from the temporary packet to the output file. */ | ||
779 | if (*data_present) | ||
780 | { | ||
781 | if ((error = av_write_frame (output_format_context, &output_packet)) < 0) | ||
782 | { | ||
783 | #if DEBUG | ||
784 | fprintf (stderr, "Could not write frame (error '%s')\n", | ||
785 | get_error_text (error)); | ||
786 | #endif | ||
787 | |||
788 | av_packet_unref (&output_packet); | ||
789 | return error; | ||
790 | } | ||
791 | |||
792 | av_packet_unref (&output_packet); | ||
793 | } | ||
794 | |||
795 | return 0; | ||
796 | } | ||
797 | |||
798 | |||
799 | /** | ||
800 | * Load one audio frame from the FIFO buffer, encode and write it to the | ||
801 | * output file. | ||
802 | */ | ||
803 | static int | ||
804 | load_encode_and_write (AVAudioFifo *fifo, | ||
805 | AVFormatContext *output_format_context, | ||
806 | AVCodecContext *output_codec_context) | ||
807 | { | ||
808 | /** Temporary storage of the output samples of the frame written to the file. */ | ||
809 | AVFrame *output_frame; | ||
810 | /** | ||
811 | * Use the maximum number of possible samples per frame. | ||
812 | * If there is less than the maximum possible frame size in the FIFO | ||
813 | * buffer use this number. Otherwise, use the maximum possible frame size | ||
814 | */const int frame_size = FFMIN (av_audio_fifo_size (fifo), | ||
815 | output_codec_context->frame_size); | ||
816 | int data_written; | ||
817 | |||
818 | /** Initialize temporary storage for one output frame. */ | ||
819 | if (init_output_frame (&output_frame, output_codec_context, frame_size)) | ||
820 | return AVERROR_EXIT; | ||
821 | |||
822 | /** | ||
823 | * Read as many samples from the FIFO buffer as required to fill the frame. | ||
824 | * The samples are stored in the frame temporarily. | ||
825 | */ | ||
826 | if (av_audio_fifo_read (fifo, (void **) output_frame->data, frame_size) < | ||
827 | frame_size) | ||
828 | { | ||
829 | #if DEBUG | ||
830 | fprintf (stderr, "Could not read data from FIFO\n"); | ||
831 | #endif | ||
832 | av_frame_free (&output_frame); | ||
833 | return AVERROR_EXIT; | ||
834 | } | ||
835 | |||
836 | /** Encode one frame worth of audio samples. */ | ||
837 | if (encode_audio_frame (output_frame, output_format_context, | ||
838 | output_codec_context, &data_written)) | ||
839 | { | ||
840 | av_frame_free (&output_frame); | ||
841 | return AVERROR_EXIT; | ||
842 | } | ||
843 | av_frame_free (&output_frame); | ||
844 | return 0; | ||
845 | } | ||
846 | |||
847 | |||
848 | /** Write the trailer of the output file container. */ | ||
849 | static int | ||
850 | write_output_file_trailer (AVFormatContext *output_format_context) | ||
851 | { | ||
852 | int error; | ||
853 | if ((error = av_write_trailer (output_format_context)) < 0) | ||
854 | { | ||
855 | #if DEBUG | ||
856 | fprintf (stderr, "Could not write output file trailer (error '%s')\n", | ||
857 | get_error_text (error)); | ||
858 | #endif | ||
859 | return error; | ||
860 | } | ||
861 | return 0; | ||
862 | } | ||
863 | |||
864 | |||
865 | #define ENUM_CODEC_ID enum AVCodecID | ||
866 | |||
867 | |||
868 | /** | ||
869 | * Perform the audio snippet extraction | ||
870 | * | ||
871 | * @param ec extraction context to use | ||
872 | */ | ||
873 | static void | ||
874 | extract_audio (struct EXTRACTOR_ExtractContext *ec) | ||
875 | { | ||
876 | AVIOContext *io_ctx; | ||
877 | struct AVFormatContext *format_ctx; | ||
878 | AVCodecContext *codec_ctx; | ||
879 | AVFormatContext *output_format_context = NULL; | ||
880 | AVCodec *codec; | ||
881 | AVDictionary *options; | ||
882 | AVFrame *frame; | ||
883 | AVCodecContext*output_codec_context = NULL; | ||
884 | AVAudioResampleContext *resample_context = NULL; | ||
885 | AVAudioFifo *fifo = NULL; | ||
886 | |||
887 | int audio_stream_index; | ||
888 | int i; | ||
889 | int err; | ||
890 | int duration; | ||
891 | unsigned char *iob; | ||
892 | |||
893 | |||
894 | totalSize = 0; | ||
895 | if (NULL == (iob = av_malloc (16 * 1024))) | ||
896 | return; | ||
897 | if (NULL == (io_ctx = avio_alloc_context (iob, | ||
898 | 16 * 1024, | ||
899 | 0, ec, | ||
900 | &read_cb, | ||
901 | NULL /* no writing */, | ||
902 | &seek_cb))) | ||
903 | { | ||
904 | av_free (iob); | ||
905 | return; | ||
906 | } | ||
907 | if (NULL == (format_ctx = avformat_alloc_context ())) | ||
908 | { | ||
909 | av_free (io_ctx); | ||
910 | return; | ||
911 | } | ||
912 | format_ctx->pb = io_ctx; | ||
913 | options = NULL; | ||
914 | if (0 != avformat_open_input (&format_ctx, "<no file>", NULL, &options)) | ||
915 | { | ||
916 | av_free (io_ctx); | ||
917 | return; | ||
918 | } | ||
919 | av_dict_free (&options); | ||
920 | if (0 > avformat_find_stream_info (format_ctx, NULL)) | ||
921 | { | ||
922 | #if DEBUG | ||
923 | fprintf (stderr, | ||
924 | "Failed to read stream info\n"); | ||
925 | #endif | ||
926 | avformat_close_input (&format_ctx); | ||
927 | av_free (io_ctx); | ||
928 | return; | ||
929 | } | ||
930 | codec = NULL; | ||
931 | codec_ctx = NULL; | ||
932 | audio_stream_index = -1; | ||
933 | for (i = 0; i<format_ctx->nb_streams; i++) | ||
934 | { | ||
935 | codec_ctx = format_ctx->streams[i]->codec; | ||
936 | if (AVMEDIA_TYPE_AUDIO != codec_ctx->codec_type) | ||
937 | continue; | ||
938 | if (NULL == (codec = avcodec_find_decoder (codec_ctx->codec_id))) | ||
939 | continue; | ||
940 | options = NULL; | ||
941 | if (0 != (err = avcodec_open2 (codec_ctx, codec, &options))) | ||
942 | { | ||
943 | codec = NULL; | ||
944 | continue; | ||
945 | } | ||
946 | av_dict_free (&options); | ||
947 | audio_stream_index = i; | ||
948 | break; | ||
949 | } | ||
950 | if ( (-1 == audio_stream_index) || | ||
951 | (0 == codec_ctx->channels) ) | ||
952 | { | ||
953 | #if DEBUG | ||
954 | fprintf (stderr, | ||
955 | "No audio streams or no suitable codec found\n"); | ||
956 | #endif | ||
957 | if (NULL != codec) | ||
958 | avcodec_close (codec_ctx); | ||
959 | avformat_close_input (&format_ctx); | ||
960 | av_free (io_ctx); | ||
961 | return; | ||
962 | } | ||
963 | |||
964 | frame = av_frame_alloc (); | ||
965 | if (NULL == frame) | ||
966 | { | ||
967 | #if DEBUG | ||
968 | fprintf (stderr, | ||
969 | "Failed to allocate frame\n"); | ||
970 | #endif | ||
971 | avcodec_close (codec_ctx); | ||
972 | avformat_close_input (&format_ctx); | ||
973 | av_free (io_ctx); | ||
974 | return; | ||
975 | } | ||
976 | |||
977 | |||
978 | if (! (buffer = malloc (HARD_LIMIT_SIZE))) | ||
979 | goto cleanup; | ||
980 | |||
981 | |||
982 | /** Open the output file for writing. */ | ||
983 | if (open_output_file (codec_ctx, | ||
984 | &output_format_context, | ||
985 | &output_codec_context)) | ||
986 | goto cleanup; | ||
987 | /** Initialize the resampler to be able to convert audio sample formats. */ | ||
988 | if (init_resampler (codec_ctx, | ||
989 | output_codec_context, | ||
990 | &resample_context)) | ||
991 | goto cleanup; | ||
992 | /** Initialize the FIFO buffer to store audio samples to be encoded. */ | ||
993 | if (init_fifo (&fifo)) | ||
994 | goto cleanup; | ||
995 | |||
996 | /** Write the header of the output file container. */ | ||
997 | if (write_output_file_header (output_format_context)) | ||
998 | goto cleanup; | ||
999 | |||
1000 | |||
1001 | if (format_ctx->duration == AV_NOPTS_VALUE) | ||
1002 | { | ||
1003 | duration = -1; | ||
1004 | #if DEBUG | ||
1005 | fprintf (stderr, | ||
1006 | "Duration unknown\n"); | ||
1007 | #endif | ||
1008 | } | ||
1009 | else | ||
1010 | { | ||
1011 | duration = format_ctx->duration; | ||
1012 | #if DEBUG | ||
1013 | fprintf (stderr, | ||
1014 | "Duration: %lld\n", | ||
1015 | format_ctx->duration); | ||
1016 | #endif | ||
1017 | } | ||
1018 | |||
1019 | /* if duration is known, seek to first tried, | ||
1020 | * else use 10 sec into stream */ | ||
1021 | |||
1022 | if (-1 != duration) | ||
1023 | err = av_seek_frame (format_ctx, -1, (duration / 3), 0); | ||
1024 | else | ||
1025 | err = av_seek_frame (format_ctx, -1, 10 * AV_TIME_BASE, 0); | ||
1026 | |||
1027 | |||
1028 | if (err >= 0) | ||
1029 | avcodec_flush_buffers (codec_ctx); | ||
1030 | |||
1031 | |||
1032 | /** | ||
1033 | * Loop as long as we have input samples to read or output samples | ||
1034 | * to write; abort as soon as we have neither. | ||
1035 | */ | ||
1036 | while (1) | ||
1037 | { | ||
1038 | /** Use the encoder's desired frame size for processing. */ | ||
1039 | const int output_frame_size = output_codec_context->frame_size; | ||
1040 | int finished = 0; | ||
1041 | |||
1042 | /** | ||
1043 | * Make sure that there is one frame worth of samples in the FIFO | ||
1044 | * buffer so that the encoder can do its work. | ||
1045 | * Since the decoder's and the encoder's frame size may differ, we | ||
1046 | * need to FIFO buffer to store as many frames worth of input samples | ||
1047 | * that they make up at least one frame worth of output samples. | ||
1048 | */while ((av_audio_fifo_size (fifo) < output_frame_size)) | ||
1049 | { | ||
1050 | /** | ||
1051 | * Decode one frame worth of audio samples, convert it to the | ||
1052 | * output sample format and put it into the FIFO buffer. | ||
1053 | */ | ||
1054 | if (read_decode_convert_and_store (fifo, | ||
1055 | format_ctx, | ||
1056 | codec_ctx, | ||
1057 | output_codec_context, | ||
1058 | resample_context, | ||
1059 | audio_stream_index, | ||
1060 | &finished)) | ||
1061 | { | ||
1062 | goto cleanup; | ||
1063 | } | ||
1064 | |||
1065 | /** | ||
1066 | * If we are at the end of the input file, we continue | ||
1067 | * encoding the remaining audio samples to the output file. | ||
1068 | */ | ||
1069 | if (finished) | ||
1070 | break; | ||
1071 | } | ||
1072 | |||
1073 | /* Already over our limit*/ | ||
1074 | if (totalSize >= MAX_SIZE) | ||
1075 | finished = 1; | ||
1076 | |||
1077 | /** | ||
1078 | * If we have enough samples for the encoder, we encode them. | ||
1079 | * At the end of the file, we pass the remaining samples to | ||
1080 | * the encoder. | ||
1081 | */// | ||
1082 | while (av_audio_fifo_size (fifo) >= output_frame_size || | ||
1083 | (finished && av_audio_fifo_size (fifo) > 0)) | ||
1084 | { | ||
1085 | /** | ||
1086 | * Take one frame worth of audio samples from the FIFO buffer, | ||
1087 | * encode it and write it to the output file. | ||
1088 | */ | ||
1089 | if (load_encode_and_write (fifo, | ||
1090 | output_format_context, | ||
1091 | output_codec_context)) | ||
1092 | goto cleanup; | ||
1093 | } | ||
1094 | /** | ||
1095 | * If we are at the end of the input file and have encoded | ||
1096 | * all remaining samples, we can exit this loop and finish. | ||
1097 | */ | ||
1098 | if (finished) | ||
1099 | { | ||
1100 | int data_written; | ||
1101 | /** Flush the encoder as it may have delayed frames. */ | ||
1102 | do { | ||
1103 | encode_audio_frame (NULL, | ||
1104 | output_format_context, | ||
1105 | output_codec_context, | ||
1106 | &data_written); | ||
1107 | } while (data_written); | ||
1108 | break; | ||
1109 | } | ||
1110 | } | ||
1111 | |||
1112 | /** Write the trailer of the output file container. */ | ||
1113 | if (write_output_file_trailer (output_format_context)) | ||
1114 | goto cleanup; | ||
1115 | ec->proc (ec->cls, | ||
1116 | "previewopus", | ||
1117 | EXTRACTOR_METATYPE_AUDIO_PREVIEW, | ||
1118 | EXTRACTOR_METAFORMAT_BINARY, | ||
1119 | "audio/opus", | ||
1120 | buffer, | ||
1121 | totalSize); | ||
1122 | |||
1123 | #if OUTPUT_FILE | ||
1124 | { | ||
1125 | FILE *f; | ||
1126 | |||
1127 | f = fopen ("example.opus", "wb"); | ||
1128 | if (! f) | ||
1129 | { | ||
1130 | fprintf (stderr, "Could not open %s\n", "file"); | ||
1131 | exit (1); | ||
1132 | } | ||
1133 | fwrite (buffer, 1, totalSize, f); | ||
1134 | fclose (f); | ||
1135 | } | ||
1136 | #endif | ||
1137 | |||
1138 | cleanup: | ||
1139 | av_free (frame); | ||
1140 | free (buffer); | ||
1141 | |||
1142 | if (fifo) | ||
1143 | av_audio_fifo_free (fifo); | ||
1144 | if (resample_context) | ||
1145 | { | ||
1146 | avresample_close (resample_context); | ||
1147 | avresample_free (&resample_context); | ||
1148 | } | ||
1149 | if (output_codec_context) | ||
1150 | avcodec_close (output_codec_context); | ||
1151 | |||
1152 | avcodec_close (codec_ctx); | ||
1153 | avformat_close_input (&format_ctx); | ||
1154 | av_free (io_ctx); | ||
1155 | } | ||
1156 | |||
1157 | |||
1158 | /** | ||
1159 | * Main method for the opus-preview plugin. | ||
1160 | * | ||
1161 | * @param ec extraction context | ||
1162 | */ | ||
1163 | void | ||
1164 | EXTRACTOR_previewopus_extract_method (struct EXTRACTOR_ExtractContext *ec) | ||
1165 | { | ||
1166 | ssize_t iret; | ||
1167 | void *data; | ||
1168 | |||
1169 | if (-1 == (iret = ec->read (ec->cls, | ||
1170 | &data, | ||
1171 | 16 * 1024))) | ||
1172 | return; | ||
1173 | |||
1174 | if (0 != ec->seek (ec->cls, 0, SEEK_SET)) | ||
1175 | return; | ||
1176 | |||
1177 | extract_audio (ec); | ||
1178 | } | ||
1179 | |||
1180 | |||
1181 | /** | ||
1182 | * Log callback. Does nothing. | ||
1183 | * | ||
1184 | * @param ptr NULL | ||
1185 | * @param level log level | ||
1186 | * @param format format string | ||
1187 | * @param ap arguments for format | ||
1188 | */ | ||
1189 | static void | ||
1190 | previewopus_av_log_callback (void*ptr, | ||
1191 | int level, | ||
1192 | const char *format, | ||
1193 | va_list ap) | ||
1194 | { | ||
1195 | #if DEBUG | ||
1196 | vfprintf (stderr, format, ap); | ||
1197 | #endif | ||
1198 | } | ||
1199 | |||
1200 | |||
1201 | /** | ||
1202 | * Initialize av-libs | ||
1203 | */ | ||
1204 | void __attribute__ ((constructor)) | ||
1205 | previewopus_lib_init (void) | ||
1206 | { | ||
1207 | av_log_set_callback (&previewopus_av_log_callback); | ||
1208 | } | ||
1209 | |||
1210 | |||
1211 | /** | ||
1212 | * Destructor for the library, cleans up. | ||
1213 | */ | ||
1214 | void __attribute__ ((destructor)) | ||
1215 | previewopus_ltdl_fini () | ||
1216 | { | ||
1217 | |||
1218 | } | ||
1219 | |||
1220 | |||
1221 | /* end of previewopus_extractor.c */ | ||