diff options
Diffstat (limited to 'src/plugins/previewopus_extractor.c')
-rw-r--r-- | src/plugins/previewopus_extractor.c | 1247 |
1 files changed, 0 insertions, 1247 deletions
diff --git a/src/plugins/previewopus_extractor.c b/src/plugins/previewopus_extractor.c deleted file mode 100644 index f137f38..0000000 --- a/src/plugins/previewopus_extractor.c +++ /dev/null | |||
@@ -1,1247 +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 <libswresample/swresample.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 | SwrContext **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 = swr_alloc ())) | ||
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 | */ | ||
330 | av_opt_set_int (*resample_context, | ||
331 | "in_channel_layout", | ||
332 | av_get_default_channel_layout ( | ||
333 | input_codec_context->channels), 0); | ||
334 | av_opt_set_int (*resample_context, | ||
335 | "out_channel_layout", | ||
336 | av_get_default_channel_layout ( | ||
337 | output_codec_context->channels), 0); | ||
338 | av_opt_set_int (*resample_context, | ||
339 | "in_sample_rate", | ||
340 | input_codec_context->sample_rate, 0); | ||
341 | av_opt_set_int (*resample_context, | ||
342 | "out_sample_rate", | ||
343 | output_codec_context->sample_rate, 0); | ||
344 | av_opt_set_int (*resample_context, | ||
345 | "in_sample_fmt", | ||
346 | input_codec_context->sample_fmt, 0); | ||
347 | av_opt_set_int (*resample_context, | ||
348 | "out_sample_fmt", | ||
349 | output_codec_context->sample_fmt, 0); | ||
350 | |||
351 | /** Open the resampler with the specified parameters. */ | ||
352 | if ((error = swr_init (*resample_context)) < 0) | ||
353 | { | ||
354 | #if DEBUG | ||
355 | fprintf (stderr, "Could not open resample context\n"); | ||
356 | #endif | ||
357 | swr_free (resample_context); | ||
358 | return error; | ||
359 | } | ||
360 | } | ||
361 | return 0; | ||
362 | } | ||
363 | |||
364 | |||
365 | /** Initialize a FIFO buffer for the audio samples to be encoded. */ | ||
366 | static int | ||
367 | init_fifo (AVAudioFifo **fifo) | ||
368 | { | ||
369 | /** Create the FIFO buffer based on the specified output sample format. */ | ||
370 | if (! (*fifo = av_audio_fifo_alloc (OUTPUT_SAMPLE_FORMAT, OUTPUT_CHANNELS, | ||
371 | 1))) | ||
372 | { | ||
373 | #if DEBUG | ||
374 | fprintf (stderr, "Could not allocate FIFO\n"); | ||
375 | #endif | ||
376 | return AVERROR (ENOMEM); | ||
377 | } | ||
378 | return 0; | ||
379 | } | ||
380 | |||
381 | |||
382 | /** Write the header of the output file container. */ | ||
383 | static int | ||
384 | write_output_file_header (AVFormatContext *output_format_context) | ||
385 | { | ||
386 | int error; | ||
387 | if ((error = avformat_write_header (output_format_context, NULL)) < 0) | ||
388 | { | ||
389 | #if DEBUG | ||
390 | fprintf (stderr, "Could not write output file header (error '%s')\n", | ||
391 | get_error_text (error)); | ||
392 | #endif | ||
393 | return error; | ||
394 | } | ||
395 | return 0; | ||
396 | } | ||
397 | |||
398 | |||
399 | /** Decode one audio frame from the input file. */ | ||
400 | static int | ||
401 | decode_audio_frame (AVFrame *frame, | ||
402 | AVFormatContext *input_format_context, | ||
403 | AVCodecContext *input_codec_context, int audio_stream_index, | ||
404 | int *data_present, int *finished) | ||
405 | { | ||
406 | /** Packet used for temporary storage. */ | ||
407 | AVPacket input_packet; | ||
408 | int error; | ||
409 | init_packet (&input_packet); | ||
410 | |||
411 | /** Read one audio frame from the input file into a temporary packet. */ | ||
412 | while (1) | ||
413 | { | ||
414 | if ((error = av_read_frame (input_format_context, &input_packet)) < 0) | ||
415 | { | ||
416 | /** If we are the the end of the file, flush the decoder below. */ | ||
417 | if (error == AVERROR_EOF) | ||
418 | { | ||
419 | #if DEBUG | ||
420 | fprintf (stderr, "EOF in decode_audio\n"); | ||
421 | #endif | ||
422 | *finished = 1; | ||
423 | } | ||
424 | else | ||
425 | { | ||
426 | #if DEBUG | ||
427 | fprintf (stderr, "Could not read frame (error '%s')\n", | ||
428 | get_error_text (error)); | ||
429 | #endif | ||
430 | return error; | ||
431 | } | ||
432 | } | ||
433 | |||
434 | if (input_packet.stream_index == audio_stream_index) | ||
435 | break; | ||
436 | } | ||
437 | |||
438 | /** | ||
439 | * Decode the audio frame stored in the temporary packet. | ||
440 | * The input audio stream decoder is used to do this. | ||
441 | * If we are at the end of the file, pass an empty packet to the decoder | ||
442 | * to flush it. | ||
443 | */if ((error = avcodec_decode_audio4 (input_codec_context, frame, | ||
444 | data_present, &input_packet)) < 0) | ||
445 | { | ||
446 | #if DEBUG | ||
447 | fprintf (stderr, "Could not decode frame (error '%s')\n", | ||
448 | get_error_text (error)); | ||
449 | #endif | ||
450 | av_packet_unref (&input_packet); | ||
451 | return error; | ||
452 | } | ||
453 | |||
454 | /** | ||
455 | * If the decoder has not been flushed completely, we are not finished, | ||
456 | * so that this function has to be called again. | ||
457 | */ | ||
458 | if (*finished && *data_present) | ||
459 | *finished = 0; | ||
460 | av_packet_unref (&input_packet); | ||
461 | return 0; | ||
462 | } | ||
463 | |||
464 | |||
465 | /** | ||
466 | * Initialize a temporary storage for the specified number of audio samples. | ||
467 | * The conversion requires temporary storage due to the different format. | ||
468 | * The number of audio samples to be allocated is specified in frame_size. | ||
469 | */ | ||
470 | static int | ||
471 | init_converted_samples (uint8_t ***converted_input_samples, | ||
472 | int*out_linesize, | ||
473 | AVCodecContext *output_codec_context, | ||
474 | int frame_size) | ||
475 | { | ||
476 | int error; | ||
477 | |||
478 | /** | ||
479 | * Allocate as many pointers as there are audio channels. | ||
480 | * Each pointer will later point to the audio samples of the corresponding | ||
481 | * channels (although it may be NULL for interleaved formats). | ||
482 | */if (! (*converted_input_samples = calloc (output_codec_context->channels, | ||
483 | sizeof(**converted_input_samples)))) | ||
484 | { | ||
485 | #if DEBUG | ||
486 | fprintf (stderr, "Could not allocate converted input sample pointers\n"); | ||
487 | #endif | ||
488 | return AVERROR (ENOMEM); | ||
489 | } | ||
490 | |||
491 | /** | ||
492 | * Allocate memory for the samples of all channels in one consecutive | ||
493 | * block for convenience. | ||
494 | */ | ||
495 | if ((error = av_samples_alloc (*converted_input_samples, | ||
496 | out_linesize, | ||
497 | output_codec_context->channels, | ||
498 | frame_size, | ||
499 | output_codec_context->sample_fmt, 0)) < 0) | ||
500 | { | ||
501 | #if DEBUG | ||
502 | fprintf (stderr, | ||
503 | "Could not allocate converted input samples (error '%s')\n", | ||
504 | get_error_text (error)); | ||
505 | #endif | ||
506 | av_freep (&(*converted_input_samples)[0]); | ||
507 | free (*converted_input_samples); | ||
508 | return error; | ||
509 | } | ||
510 | return 0; | ||
511 | } | ||
512 | |||
513 | |||
514 | /** | ||
515 | * Convert the input audio samples into the output sample format. | ||
516 | * The conversion happens on a per-frame basis, the size of which is specified | ||
517 | * by frame_size. | ||
518 | */ | ||
519 | static int | ||
520 | convert_samples (uint8_t **input_data, | ||
521 | uint8_t **converted_data, | ||
522 | int in_sample, | ||
523 | int out_sample, | ||
524 | int out_linesize, | ||
525 | SwrContext *resample_context) | ||
526 | { | ||
527 | int error; | ||
528 | |||
529 | /** Convert the samples using the resampler. */ | ||
530 | if ((error = swr_convert (resample_context, | ||
531 | converted_data, | ||
532 | out_linesize, | ||
533 | out_sample, | ||
534 | input_data, | ||
535 | 0, | ||
536 | in_sample)) < 0) | ||
537 | { | ||
538 | #if DEBUG | ||
539 | fprintf (stderr, | ||
540 | "Could not convert input samples (error '%s')\n", | ||
541 | get_error_text (error)); | ||
542 | #endif | ||
543 | return error; | ||
544 | } | ||
545 | |||
546 | |||
547 | /** | ||
548 | * Perform a sanity check so that the number of converted samples is | ||
549 | * not greater than the number of samples to be converted. | ||
550 | * If the sample rates differ, this case has to be handled differently | ||
551 | */ | ||
552 | if (avresample_available (resample_context)) | ||
553 | { | ||
554 | #if DEBUG | ||
555 | fprintf (stderr, | ||
556 | "%i Converted samples left over\n", | ||
557 | avresample_available (resample_context)); | ||
558 | #endif | ||
559 | } | ||
560 | |||
561 | |||
562 | return 0; | ||
563 | } | ||
564 | |||
565 | |||
566 | /** Add converted input audio samples to the FIFO buffer for later processing. */ | ||
567 | static int | ||
568 | add_samples_to_fifo (AVAudioFifo *fifo, | ||
569 | uint8_t **converted_input_samples, | ||
570 | const int frame_size) | ||
571 | { | ||
572 | int error; | ||
573 | |||
574 | /** | ||
575 | * Make the FIFO as large as it needs to be to hold both, | ||
576 | * the old and the new samples. | ||
577 | */ | ||
578 | if ((error = av_audio_fifo_realloc (fifo, av_audio_fifo_size (fifo) | ||
579 | + frame_size)) < 0) | ||
580 | { | ||
581 | #if DEBUG | ||
582 | fprintf (stderr, "Could not reallocate FIFO\n"); | ||
583 | #endif | ||
584 | return error; | ||
585 | } | ||
586 | |||
587 | /** Store the new samples in the FIFO buffer. */ | ||
588 | if (av_audio_fifo_write (fifo, (void **) converted_input_samples, | ||
589 | frame_size) < frame_size) | ||
590 | { | ||
591 | #if DEBUG | ||
592 | fprintf (stderr, "Could not write data to FIFO\n"); | ||
593 | #endif | ||
594 | return AVERROR_EXIT; | ||
595 | } | ||
596 | return 0; | ||
597 | } | ||
598 | |||
599 | |||
600 | /** | ||
601 | * Read one audio frame from the input file, decodes, converts and stores | ||
602 | * it in the FIFO buffer. | ||
603 | */ | ||
604 | static int | ||
605 | read_decode_convert_and_store (AVAudioFifo *fifo, | ||
606 | AVFormatContext *input_format_context, | ||
607 | AVCodecContext *input_codec_context, | ||
608 | AVCodecContext *output_codec_context, | ||
609 | SwrContext *resampler_context, int | ||
610 | audio_stream_index, | ||
611 | int *finished) | ||
612 | { | ||
613 | /** Temporary storage of the input samples of the frame read from the file. */ | ||
614 | AVFrame *input_frame = NULL; | ||
615 | /** Temporary storage for the converted input samples. */ | ||
616 | uint8_t **converted_input_samples = NULL; | ||
617 | int data_present; | ||
618 | int ret = AVERROR_EXIT; | ||
619 | |||
620 | /** Initialize temporary storage for one input frame. */ | ||
621 | if (init_input_frame (&input_frame)) | ||
622 | { | ||
623 | #if DEBUG | ||
624 | fprintf (stderr, "Failed at init frame\n"); | ||
625 | #endif | ||
626 | goto cleanup; | ||
627 | |||
628 | } | ||
629 | /** Decode one frame worth of audio samples. */ | ||
630 | if (decode_audio_frame (input_frame, | ||
631 | input_format_context, | ||
632 | input_codec_context, | ||
633 | audio_stream_index, | ||
634 | &data_present, | ||
635 | finished)) | ||
636 | { | ||
637 | #if DEBUG | ||
638 | fprintf (stderr, "Failed at decode audio\n"); | ||
639 | #endif | ||
640 | |||
641 | goto cleanup; | ||
642 | } | ||
643 | /** | ||
644 | * If we are at the end of the file and there are no more samples | ||
645 | * in the decoder which are delayed, we are actually finished. | ||
646 | * This must not be treated as an error. | ||
647 | */ | ||
648 | if (*finished && ! data_present) | ||
649 | { | ||
650 | ret = 0; | ||
651 | #if DEBUG | ||
652 | fprintf (stderr, "Failed at finished or no data\n"); | ||
653 | #endif | ||
654 | goto cleanup; | ||
655 | } | ||
656 | /** If there is decoded data, convert and store it */ | ||
657 | if (data_present) | ||
658 | { | ||
659 | int out_linesize; | ||
660 | // FIXME: I'm losing samples, but can't get it to work. | ||
661 | int out_samples = avresample_available (resampler_context) | ||
662 | + avresample_get_delay (resampler_context) | ||
663 | + input_frame->nb_samples; | ||
664 | |||
665 | |||
666 | // fprintf(stderr, "Input nbsamples %i out_samples: %i \n",input_frame->nb_samples,out_samples); | ||
667 | |||
668 | /** Initialize the temporary storage for the converted input samples. */ | ||
669 | if (init_converted_samples (&converted_input_samples, | ||
670 | &out_linesize, | ||
671 | output_codec_context, | ||
672 | out_samples)) | ||
673 | { | ||
674 | #if DEBUG | ||
675 | fprintf (stderr, "Failed at init_converted_samples\n"); | ||
676 | #endif | ||
677 | goto cleanup; | ||
678 | } | ||
679 | |||
680 | /** | ||
681 | * Convert the input samples to the desired output sample format. | ||
682 | * This requires a temporary storage provided by converted_input_samples. | ||
683 | */ | ||
684 | if (convert_samples (input_frame->extended_data, | ||
685 | converted_input_samples, | ||
686 | input_frame->nb_samples, | ||
687 | out_samples, | ||
688 | out_linesize, | ||
689 | resampler_context)) | ||
690 | { | ||
691 | #if DEBUG | ||
692 | fprintf (stderr, | ||
693 | "Failed at convert_samples, input frame %i \n", | ||
694 | input_frame->nb_samples); | ||
695 | #endif | ||
696 | goto cleanup; | ||
697 | } | ||
698 | /** Add the converted input samples to the FIFO buffer for later processing. */ | ||
699 | if (add_samples_to_fifo (fifo, | ||
700 | converted_input_samples, | ||
701 | out_samples)) | ||
702 | { | ||
703 | #if DEBUG | ||
704 | fprintf (stderr, "Failed at add_samples_to_fifo\n"); | ||
705 | #endif | ||
706 | goto cleanup; | ||
707 | } | ||
708 | ret = 0; | ||
709 | } | ||
710 | ret = 0; | ||
711 | |||
712 | cleanup: | ||
713 | if (converted_input_samples) | ||
714 | { | ||
715 | av_freep (&converted_input_samples[0]); | ||
716 | free (converted_input_samples); | ||
717 | } | ||
718 | av_frame_free (&input_frame); | ||
719 | return ret; | ||
720 | } | ||
721 | |||
722 | |||
723 | /** | ||
724 | * Initialize one input frame for writing to the output file. | ||
725 | * The frame will be exactly frame_size samples large. | ||
726 | */ | ||
727 | static int | ||
728 | init_output_frame (AVFrame **frame, | ||
729 | AVCodecContext *output_codec_context, | ||
730 | int frame_size) | ||
731 | { | ||
732 | int error; | ||
733 | |||
734 | /** Create a new frame to store the audio samples. */ | ||
735 | *frame = av_frame_alloc (); | ||
736 | if (NULL == *frame) | ||
737 | { | ||
738 | #if DEBUG | ||
739 | fprintf (stderr, "Could not allocate output frame\n"); | ||
740 | #endif | ||
741 | return AVERROR_EXIT; | ||
742 | } | ||
743 | |||
744 | /** | ||
745 | * Set the frame's parameters, especially its size and format. | ||
746 | * av_frame_get_buffer needs this to allocate memory for the | ||
747 | * audio samples of the frame. | ||
748 | * Default channel layouts based on the number of channels | ||
749 | * are assumed for simplicity. | ||
750 | */(*frame)->nb_samples = frame_size; | ||
751 | (*frame)->channel_layout = output_codec_context->channel_layout; | ||
752 | (*frame)->format = output_codec_context->sample_fmt; | ||
753 | (*frame)->sample_rate = output_codec_context->sample_rate; | ||
754 | |||
755 | |||
756 | // fprintf(stderr, "%i %i \n",frame_size , (*frame)->format,(*frame)->sample_rate); | ||
757 | |||
758 | /** | ||
759 | * Allocate the samples of the created frame. This call will make | ||
760 | * sure that the audio frame can hold as many samples as specified. | ||
761 | */ | ||
762 | if ((error = av_frame_get_buffer (*frame, 0)) < 0) | ||
763 | { | ||
764 | #if DEBUG | ||
765 | fprintf (stderr, "Could allocate output frame samples (error '%s')\n", | ||
766 | get_error_text (error)); | ||
767 | #endif | ||
768 | av_frame_free (frame); | ||
769 | return error; | ||
770 | } | ||
771 | |||
772 | return 0; | ||
773 | } | ||
774 | |||
775 | |||
776 | /** Encode one frame worth of audio to the output file. */ | ||
777 | static int | ||
778 | encode_audio_frame (AVFrame *frame, | ||
779 | AVFormatContext *output_format_context, | ||
780 | AVCodecContext *output_codec_context, | ||
781 | int *data_present) | ||
782 | { | ||
783 | /** Packet used for temporary storage. */ | ||
784 | AVPacket output_packet; | ||
785 | int error; | ||
786 | init_packet (&output_packet); | ||
787 | |||
788 | /** | ||
789 | * Encode the audio frame and store it in the temporary packet. | ||
790 | * The output audio stream encoder is used to do this. | ||
791 | */ | ||
792 | if ((error = avcodec_encode_audio2 (output_codec_context, &output_packet, | ||
793 | frame, data_present)) < 0) | ||
794 | { | ||
795 | #if DEBUG | ||
796 | fprintf (stderr, "Could not encode frame (error '%s')\n", | ||
797 | get_error_text (error)); | ||
798 | #endif | ||
799 | av_packet_unref (&output_packet); | ||
800 | return error; | ||
801 | } | ||
802 | |||
803 | /** Write one audio frame from the temporary packet to the output file. */ | ||
804 | if (*data_present) | ||
805 | { | ||
806 | if ((error = av_write_frame (output_format_context, &output_packet)) < 0) | ||
807 | { | ||
808 | #if DEBUG | ||
809 | fprintf (stderr, "Could not write frame (error '%s')\n", | ||
810 | get_error_text (error)); | ||
811 | #endif | ||
812 | |||
813 | av_packet_unref (&output_packet); | ||
814 | return error; | ||
815 | } | ||
816 | |||
817 | av_packet_unref (&output_packet); | ||
818 | } | ||
819 | |||
820 | return 0; | ||
821 | } | ||
822 | |||
823 | |||
824 | /** | ||
825 | * Load one audio frame from the FIFO buffer, encode and write it to the | ||
826 | * output file. | ||
827 | */ | ||
828 | static int | ||
829 | load_encode_and_write (AVAudioFifo *fifo, | ||
830 | AVFormatContext *output_format_context, | ||
831 | AVCodecContext *output_codec_context) | ||
832 | { | ||
833 | /** Temporary storage of the output samples of the frame written to the file. */ | ||
834 | AVFrame *output_frame; | ||
835 | /** | ||
836 | * Use the maximum number of possible samples per frame. | ||
837 | * If there is less than the maximum possible frame size in the FIFO | ||
838 | * buffer use this number. Otherwise, use the maximum possible frame size | ||
839 | */const int frame_size = FFMIN (av_audio_fifo_size (fifo), | ||
840 | output_codec_context->frame_size); | ||
841 | int data_written; | ||
842 | |||
843 | /** Initialize temporary storage for one output frame. */ | ||
844 | if (init_output_frame (&output_frame, output_codec_context, frame_size)) | ||
845 | return AVERROR_EXIT; | ||
846 | |||
847 | /** | ||
848 | * Read as many samples from the FIFO buffer as required to fill the frame. | ||
849 | * The samples are stored in the frame temporarily. | ||
850 | */ | ||
851 | if (av_audio_fifo_read (fifo, (void **) output_frame->data, frame_size) < | ||
852 | frame_size) | ||
853 | { | ||
854 | #if DEBUG | ||
855 | fprintf (stderr, "Could not read data from FIFO\n"); | ||
856 | #endif | ||
857 | av_frame_free (&output_frame); | ||
858 | return AVERROR_EXIT; | ||
859 | } | ||
860 | |||
861 | /** Encode one frame worth of audio samples. */ | ||
862 | if (encode_audio_frame (output_frame, output_format_context, | ||
863 | output_codec_context, &data_written)) | ||
864 | { | ||
865 | av_frame_free (&output_frame); | ||
866 | return AVERROR_EXIT; | ||
867 | } | ||
868 | av_frame_free (&output_frame); | ||
869 | return 0; | ||
870 | } | ||
871 | |||
872 | |||
873 | /** Write the trailer of the output file container. */ | ||
874 | static int | ||
875 | write_output_file_trailer (AVFormatContext *output_format_context) | ||
876 | { | ||
877 | int error; | ||
878 | if ((error = av_write_trailer (output_format_context)) < 0) | ||
879 | { | ||
880 | #if DEBUG | ||
881 | fprintf (stderr, "Could not write output file trailer (error '%s')\n", | ||
882 | get_error_text (error)); | ||
883 | #endif | ||
884 | return error; | ||
885 | } | ||
886 | return 0; | ||
887 | } | ||
888 | |||
889 | |||
890 | #define ENUM_CODEC_ID enum AVCodecID | ||
891 | |||
892 | |||
893 | /** | ||
894 | * Perform the audio snippet extraction | ||
895 | * | ||
896 | * @param ec extraction context to use | ||
897 | */ | ||
898 | static void | ||
899 | extract_audio (struct EXTRACTOR_ExtractContext *ec) | ||
900 | { | ||
901 | AVIOContext *io_ctx; | ||
902 | struct AVFormatContext *format_ctx; | ||
903 | AVCodecContext *codec_ctx; | ||
904 | AVFormatContext *output_format_context = NULL; | ||
905 | AVCodec *codec; | ||
906 | AVDictionary *options; | ||
907 | AVFrame *frame; | ||
908 | AVCodecContext*output_codec_context = NULL; | ||
909 | SwrContext *resample_context = NULL; | ||
910 | AVAudioFifo *fifo = NULL; | ||
911 | |||
912 | int audio_stream_index; | ||
913 | int i; | ||
914 | int err; | ||
915 | int duration; | ||
916 | unsigned char *iob; | ||
917 | |||
918 | |||
919 | totalSize = 0; | ||
920 | if (NULL == (iob = av_malloc (16 * 1024))) | ||
921 | return; | ||
922 | if (NULL == (io_ctx = avio_alloc_context (iob, | ||
923 | 16 * 1024, | ||
924 | 0, ec, | ||
925 | &read_cb, | ||
926 | NULL /* no writing */, | ||
927 | &seek_cb))) | ||
928 | { | ||
929 | av_free (iob); | ||
930 | return; | ||
931 | } | ||
932 | if (NULL == (format_ctx = avformat_alloc_context ())) | ||
933 | { | ||
934 | av_free (io_ctx); | ||
935 | return; | ||
936 | } | ||
937 | format_ctx->pb = io_ctx; | ||
938 | options = NULL; | ||
939 | if (0 != avformat_open_input (&format_ctx, "<no file>", NULL, &options)) | ||
940 | { | ||
941 | av_free (io_ctx); | ||
942 | return; | ||
943 | } | ||
944 | av_dict_free (&options); | ||
945 | if (0 > avformat_find_stream_info (format_ctx, NULL)) | ||
946 | { | ||
947 | #if DEBUG | ||
948 | fprintf (stderr, | ||
949 | "Failed to read stream info\n"); | ||
950 | #endif | ||
951 | avformat_close_input (&format_ctx); | ||
952 | av_free (io_ctx); | ||
953 | return; | ||
954 | } | ||
955 | codec = NULL; | ||
956 | codec_ctx = NULL; | ||
957 | audio_stream_index = -1; | ||
958 | for (i = 0; i<format_ctx->nb_streams; i++) | ||
959 | { | ||
960 | codec_ctx = format_ctx->streams[i]->codec; | ||
961 | if (AVMEDIA_TYPE_AUDIO != codec_ctx->codec_type) | ||
962 | continue; | ||
963 | if (NULL == (codec = avcodec_find_decoder (codec_ctx->codec_id))) | ||
964 | continue; | ||
965 | options = NULL; | ||
966 | if (0 != (err = avcodec_open2 (codec_ctx, codec, &options))) | ||
967 | { | ||
968 | codec = NULL; | ||
969 | continue; | ||
970 | } | ||
971 | av_dict_free (&options); | ||
972 | audio_stream_index = i; | ||
973 | break; | ||
974 | } | ||
975 | if ( (-1 == audio_stream_index) || | ||
976 | (0 == codec_ctx->channels) ) | ||
977 | { | ||
978 | #if DEBUG | ||
979 | fprintf (stderr, | ||
980 | "No audio streams or no suitable codec found\n"); | ||
981 | #endif | ||
982 | if (NULL != codec) | ||
983 | avcodec_close (codec_ctx); | ||
984 | avformat_close_input (&format_ctx); | ||
985 | av_free (io_ctx); | ||
986 | return; | ||
987 | } | ||
988 | |||
989 | frame = av_frame_alloc (); | ||
990 | if (NULL == frame) | ||
991 | { | ||
992 | #if DEBUG | ||
993 | fprintf (stderr, | ||
994 | "Failed to allocate frame\n"); | ||
995 | #endif | ||
996 | avcodec_close (codec_ctx); | ||
997 | avformat_close_input (&format_ctx); | ||
998 | av_free (io_ctx); | ||
999 | return; | ||
1000 | } | ||
1001 | |||
1002 | |||
1003 | if (! (buffer = malloc (HARD_LIMIT_SIZE))) | ||
1004 | goto cleanup; | ||
1005 | |||
1006 | |||
1007 | /** Open the output file for writing. */ | ||
1008 | if (open_output_file (codec_ctx, | ||
1009 | &output_format_context, | ||
1010 | &output_codec_context)) | ||
1011 | goto cleanup; | ||
1012 | /** Initialize the resampler to be able to convert audio sample formats. */ | ||
1013 | if (init_resampler (codec_ctx, | ||
1014 | output_codec_context, | ||
1015 | &resample_context)) | ||
1016 | goto cleanup; | ||
1017 | /** Initialize the FIFO buffer to store audio samples to be encoded. */ | ||
1018 | if (init_fifo (&fifo)) | ||
1019 | goto cleanup; | ||
1020 | |||
1021 | /** Write the header of the output file container. */ | ||
1022 | if (write_output_file_header (output_format_context)) | ||
1023 | goto cleanup; | ||
1024 | |||
1025 | |||
1026 | if (format_ctx->duration == AV_NOPTS_VALUE) | ||
1027 | { | ||
1028 | duration = -1; | ||
1029 | #if DEBUG | ||
1030 | fprintf (stderr, | ||
1031 | "Duration unknown\n"); | ||
1032 | #endif | ||
1033 | } | ||
1034 | else | ||
1035 | { | ||
1036 | duration = format_ctx->duration; | ||
1037 | #if DEBUG | ||
1038 | fprintf (stderr, | ||
1039 | "Duration: %lld\n", | ||
1040 | format_ctx->duration); | ||
1041 | #endif | ||
1042 | } | ||
1043 | |||
1044 | /* if duration is known, seek to first tried, | ||
1045 | * else use 10 sec into stream */ | ||
1046 | |||
1047 | if (-1 != duration) | ||
1048 | err = av_seek_frame (format_ctx, -1, (duration / 3), 0); | ||
1049 | else | ||
1050 | err = av_seek_frame (format_ctx, -1, 10 * AV_TIME_BASE, 0); | ||
1051 | |||
1052 | |||
1053 | if (err >= 0) | ||
1054 | avcodec_flush_buffers (codec_ctx); | ||
1055 | |||
1056 | |||
1057 | /** | ||
1058 | * Loop as long as we have input samples to read or output samples | ||
1059 | * to write; abort as soon as we have neither. | ||
1060 | */ | ||
1061 | while (1) | ||
1062 | { | ||
1063 | /** Use the encoder's desired frame size for processing. */ | ||
1064 | const int output_frame_size = output_codec_context->frame_size; | ||
1065 | int finished = 0; | ||
1066 | |||
1067 | /** | ||
1068 | * Make sure that there is one frame worth of samples in the FIFO | ||
1069 | * buffer so that the encoder can do its work. | ||
1070 | * Since the decoder's and the encoder's frame size may differ, we | ||
1071 | * need to FIFO buffer to store as many frames worth of input samples | ||
1072 | * that they make up at least one frame worth of output samples. | ||
1073 | */ | ||
1074 | while ((av_audio_fifo_size (fifo) < output_frame_size)) | ||
1075 | { | ||
1076 | /** | ||
1077 | * Decode one frame worth of audio samples, convert it to the | ||
1078 | * output sample format and put it into the FIFO buffer. | ||
1079 | */ | ||
1080 | if (read_decode_convert_and_store (fifo, | ||
1081 | format_ctx, | ||
1082 | codec_ctx, | ||
1083 | output_codec_context, | ||
1084 | resample_context, | ||
1085 | audio_stream_index, | ||
1086 | &finished)) | ||
1087 | { | ||
1088 | goto cleanup; | ||
1089 | } | ||
1090 | |||
1091 | /** | ||
1092 | * If we are at the end of the input file, we continue | ||
1093 | * encoding the remaining audio samples to the output file. | ||
1094 | */ | ||
1095 | if (finished) | ||
1096 | break; | ||
1097 | } | ||
1098 | |||
1099 | /* Already over our limit*/ | ||
1100 | if (totalSize >= MAX_SIZE) | ||
1101 | finished = 1; | ||
1102 | |||
1103 | /** | ||
1104 | * If we have enough samples for the encoder, we encode them. | ||
1105 | * At the end of the file, we pass the remaining samples to | ||
1106 | * the encoder. | ||
1107 | */// | ||
1108 | while (av_audio_fifo_size (fifo) >= output_frame_size || | ||
1109 | (finished && av_audio_fifo_size (fifo) > 0)) | ||
1110 | { | ||
1111 | /** | ||
1112 | * Take one frame worth of audio samples from the FIFO buffer, | ||
1113 | * encode it and write it to the output file. | ||
1114 | */ | ||
1115 | if (load_encode_and_write (fifo, | ||
1116 | output_format_context, | ||
1117 | output_codec_context)) | ||
1118 | goto cleanup; | ||
1119 | } | ||
1120 | /** | ||
1121 | * If we are at the end of the input file and have encoded | ||
1122 | * all remaining samples, we can exit this loop and finish. | ||
1123 | */ | ||
1124 | if (finished) | ||
1125 | { | ||
1126 | int data_written; | ||
1127 | /** Flush the encoder as it may have delayed frames. */ | ||
1128 | do { | ||
1129 | encode_audio_frame (NULL, | ||
1130 | output_format_context, | ||
1131 | output_codec_context, | ||
1132 | &data_written); | ||
1133 | } while (data_written); | ||
1134 | break; | ||
1135 | } | ||
1136 | } | ||
1137 | |||
1138 | /** Write the trailer of the output file container. */ | ||
1139 | if (write_output_file_trailer (output_format_context)) | ||
1140 | goto cleanup; | ||
1141 | ec->proc (ec->cls, | ||
1142 | "previewopus", | ||
1143 | EXTRACTOR_METATYPE_AUDIO_PREVIEW, | ||
1144 | EXTRACTOR_METAFORMAT_BINARY, | ||
1145 | "audio/opus", | ||
1146 | buffer, | ||
1147 | totalSize); | ||
1148 | |||
1149 | #if OUTPUT_FILE | ||
1150 | { | ||
1151 | FILE *f; | ||
1152 | |||
1153 | f = fopen ("example.opus", "wb"); | ||
1154 | if (! f) | ||
1155 | { | ||
1156 | fprintf (stderr, "Could not open %s\n", "file"); | ||
1157 | exit (1); | ||
1158 | } | ||
1159 | fwrite (buffer, 1, totalSize, f); | ||
1160 | fclose (f); | ||
1161 | } | ||
1162 | #endif | ||
1163 | |||
1164 | cleanup: | ||
1165 | av_free (frame); | ||
1166 | free (buffer); | ||
1167 | |||
1168 | if (fifo) | ||
1169 | av_audio_fifo_free (fifo); | ||
1170 | if (resample_context) | ||
1171 | { | ||
1172 | swr_close (resample_context); | ||
1173 | swr_free (&resample_context); | ||
1174 | } | ||
1175 | if (output_codec_context) | ||
1176 | avcodec_close (output_codec_context); | ||
1177 | |||
1178 | avcodec_close (codec_ctx); | ||
1179 | avformat_close_input (&format_ctx); | ||
1180 | av_free (io_ctx); | ||
1181 | } | ||
1182 | |||
1183 | |||
1184 | /** | ||
1185 | * Main method for the opus-preview plugin. | ||
1186 | * | ||
1187 | * @param ec extraction context | ||
1188 | */ | ||
1189 | void | ||
1190 | EXTRACTOR_previewopus_extract_method (struct EXTRACTOR_ExtractContext *ec) | ||
1191 | { | ||
1192 | ssize_t iret; | ||
1193 | void *data; | ||
1194 | |||
1195 | if (-1 == (iret = ec->read (ec->cls, | ||
1196 | &data, | ||
1197 | 16 * 1024))) | ||
1198 | return; | ||
1199 | |||
1200 | if (0 != ec->seek (ec->cls, 0, SEEK_SET)) | ||
1201 | return; | ||
1202 | |||
1203 | extract_audio (ec); | ||
1204 | } | ||
1205 | |||
1206 | |||
1207 | /** | ||
1208 | * Log callback. Does nothing. | ||
1209 | * | ||
1210 | * @param ptr NULL | ||
1211 | * @param level log level | ||
1212 | * @param format format string | ||
1213 | * @param ap arguments for format | ||
1214 | */ | ||
1215 | static void | ||
1216 | previewopus_av_log_callback (void*ptr, | ||
1217 | int level, | ||
1218 | const char *format, | ||
1219 | va_list ap) | ||
1220 | { | ||
1221 | #if DEBUG | ||
1222 | vfprintf (stderr, format, ap); | ||
1223 | #endif | ||
1224 | } | ||
1225 | |||
1226 | |||
1227 | /** | ||
1228 | * Initialize av-libs | ||
1229 | */ | ||
1230 | void __attribute__ ((constructor)) | ||
1231 | previewopus_lib_init (void) | ||
1232 | { | ||
1233 | av_log_set_callback (&previewopus_av_log_callback); | ||
1234 | } | ||
1235 | |||
1236 | |||
1237 | /** | ||
1238 | * Destructor for the library, cleans up. | ||
1239 | */ | ||
1240 | void __attribute__ ((destructor)) | ||
1241 | previewopus_ltdl_fini () | ||
1242 | { | ||
1243 | |||
1244 | } | ||
1245 | |||
1246 | |||
1247 | /* end of previewopus_extractor.c */ | ||