diff options
-rw-r--r-- | ChangeLog | 3 | ||||
-rw-r--r-- | configure.ac | 29 | ||||
-rw-r--r-- | src/plugins/Makefile.am | 37 | ||||
-rw-r--r-- | src/plugins/previewopus_extractor.c | 1247 | ||||
-rw-r--r-- | src/plugins/test_previewopus.c | 52 | ||||
-rw-r--r-- | src/plugins/test_thumbnailffmpeg.c | 51 | ||||
-rw-r--r-- | src/plugins/thumbnailffmpeg_extractor.c | 890 |
7 files changed, 3 insertions, 2306 deletions
@@ -1,3 +1,6 @@ | |||
1 | Tue 20 Apr 2021 06:33:46 PM CEST | ||
2 | Removing plugins depending on buggy, unstable libffmpeg library. -CG | ||
3 | |||
1 | Sat 30 Jan 2021 03:25:35 PM CET | 4 | Sat 30 Jan 2021 03:25:35 PM CET |
2 | Fix #6709: mask SIGPIPE to avoid test case failure | 5 | Fix #6709: mask SIGPIPE to avoid test case failure |
3 | with latest librpm. Remove plibc.h logic. | 6 | with latest librpm. Remove plibc.h logic. |
diff --git a/configure.ac b/configure.ac index 8005a39..80ce414 100644 --- a/configure.ac +++ b/configure.ac | |||
@@ -648,32 +648,6 @@ CPPFLAGS="$CPPFLAGS_OLD" | |||
648 | AC_CHECK_PROG([HAVE_ZZUF],[zzuf], 1, 0) | 648 | AC_CHECK_PROG([HAVE_ZZUF],[zzuf], 1, 0) |
649 | AM_CONDITIONAL(HAVE_ZZUF, test 0 != $HAVE_ZZUF) | 649 | AM_CONDITIONAL(HAVE_ZZUF, test 0 != $HAVE_ZZUF) |
650 | 650 | ||
651 | AC_MSG_CHECKING([whether to enable the FFmpeg thumbnail extractor]) | ||
652 | new_ffmpeg=0 | ||
653 | AC_ARG_ENABLE(ffmpeg, | ||
654 | [AC_HELP_STRING([--enable-ffmpeg],[Enable FFmpeg support]) | ||
655 | AC_HELP_STRING([--disable-ffmpeg],[Disable FFmpeg support])], | ||
656 | [AS_CASE(["$enableval"], | ||
657 | [no],[ | ||
658 | AC_MSG_RESULT(no) | ||
659 | ffmpeg_enabled=0 | ||
660 | ],[ | ||
661 | AC_MSG_RESULT(yes) | ||
662 | ffmpeg_enabled=1 | ||
663 | ])], | ||
664 | [ AC_MSG_RESULT(yes) | ||
665 | ffmpeg_enabled=1]) | ||
666 | AS_IF([test x$ffmpeg_enabled = x1], | ||
667 | [ffmpeg_enabled=0 | ||
668 | new_ffmpeg=0 | ||
669 | AC_CHECK_HEADERS([libavutil/avutil.h libavformat/avformat.h libavcodec/avcodec.h libavutil/frame.h], | ||
670 | AC_CHECK_HEADERS([libswresample/swresample.h], | ||
671 | AC_CHECK_LIB(avutil, av_audio_fifo_alloc, | ||
672 | ffmpeg_enabled=1))) | ||
673 | ]) | ||
674 | AM_CONDITIONAL(HAVE_FFMPEG, test x$ffmpeg_enabled != x0) | ||
675 | |||
676 | |||
677 | LE_INTLINCL="" | 651 | LE_INTLINCL="" |
678 | LE_LIBINTL="$LTLIBINTL" | 652 | LE_LIBINTL="$LTLIBINTL" |
679 | AC_ARG_ENABLE(framework, [ --enable-framework enable Mac OS X framework build helpers],enable_framework_build=$enableval) | 653 | AC_ARG_ENABLE(framework, [ --enable-framework enable Mac OS X framework build helpers],enable_framework_build=$enableval) |
@@ -769,9 +743,6 @@ AS_IF([test "x$HAVE_GIF_TRUE" = "x#"], | |||
769 | AS_IF([test "x$have_gsf" != "xtrue"], | 743 | AS_IF([test "x$have_gsf" != "xtrue"], |
770 | [AC_MSG_NOTICE([NOTICE: libgsf not found, no OLE2 (MS Office) support])]) | 744 | [AC_MSG_NOTICE([NOTICE: libgsf not found, no OLE2 (MS Office) support])]) |
771 | 745 | ||
772 | AS_IF([test "x$ffmpeg_enabled" = "x0"], | ||
773 | [AC_MSG_NOTICE([NOTICE: FFmpeg/opus audito thumbnailer plugin disabled])]) | ||
774 | |||
775 | AS_IF([test "x$have_gdk_pixbuf" = "x0"], | 746 | AS_IF([test "x$have_gdk_pixbuf" = "x0"], |
776 | [AC_MSG_NOTICE([NOTICE: libgdk-pixbuf not found, gtk thumbnail support disabled])]) | 747 | [AC_MSG_NOTICE([NOTICE: libgdk-pixbuf not found, gtk thumbnail support disabled])]) |
777 | 748 | ||
diff --git a/src/plugins/Makefile.am b/src/plugins/Makefile.am index 37e31e5..a0e0ae3 100644 --- a/src/plugins/Makefile.am +++ b/src/plugins/Makefile.am | |||
@@ -68,14 +68,6 @@ if HAVE_MAGIC | |||
68 | PLUGIN_MIME=libextractor_mime.la | 68 | PLUGIN_MIME=libextractor_mime.la |
69 | TEST_MIME=test_mime | 69 | TEST_MIME=test_mime |
70 | 70 | ||
71 | if HAVE_FFMPEG | ||
72 | PLUGIN_FFMPEG=libextractor_thumbnailffmpeg.la | ||
73 | TEST_FFMPEG=test_thumbnailffmpeg | ||
74 | |||
75 | #PLUGIN_PREVIEWOPUS=libextractor_previewopus.la | ||
76 | #TEST_PREVIEWOPUS=test_previewopus | ||
77 | endif | ||
78 | |||
79 | if HAVE_GDK_PIXBUF | 71 | if HAVE_GDK_PIXBUF |
80 | # Gtk-thumbnailer requires MAGIC and GDK_PIXBUF | 72 | # Gtk-thumbnailer requires MAGIC and GDK_PIXBUF |
81 | PLUGIN_GTK=libextractor_thumbnailgtk.la | 73 | PLUGIN_GTK=libextractor_thumbnailgtk.la |
@@ -185,7 +177,6 @@ plugin_LTLIBRARIES = \ | |||
185 | libextractor_xm.la \ | 177 | libextractor_xm.la \ |
186 | $(PLUGIN_ARCHIVE) \ | 178 | $(PLUGIN_ARCHIVE) \ |
187 | $(PLUGIN_EXIV2) \ | 179 | $(PLUGIN_EXIV2) \ |
188 | $(PLUGIN_FFMPEG) \ | ||
189 | $(PLUGIN_FLAC) \ | 180 | $(PLUGIN_FLAC) \ |
190 | $(PLUGIN_GIF) \ | 181 | $(PLUGIN_GIF) \ |
191 | $(PLUGIN_GSF) \ | 182 | $(PLUGIN_GSF) \ |
@@ -199,7 +190,6 @@ plugin_LTLIBRARIES = \ | |||
199 | $(PLUGIN_MPEG) \ | 190 | $(PLUGIN_MPEG) \ |
200 | $(PLUGIN_OGG) \ | 191 | $(PLUGIN_OGG) \ |
201 | $(PLUGIN_PDF) \ | 192 | $(PLUGIN_PDF) \ |
202 | $(PLUGIN_PREVIEWOPUS) \ | ||
203 | $(PLUGIN_RPM) \ | 193 | $(PLUGIN_RPM) \ |
204 | $(PLUGIN_TIFF) \ | 194 | $(PLUGIN_TIFF) \ |
205 | $(PLUGIN_ZLIB) | 195 | $(PLUGIN_ZLIB) |
@@ -225,8 +215,6 @@ check_PROGRAMS = \ | |||
225 | test_zip \ | 215 | test_zip \ |
226 | $(TEST_ARCHIVE) \ | 216 | $(TEST_ARCHIVE) \ |
227 | $(TEST_EXIV2) \ | 217 | $(TEST_EXIV2) \ |
228 | $(TEST_FFMPEG) \ | ||
229 | $(TEST_PREVIEWOPUS) \ | ||
230 | $(TEST_FLAC) \ | 218 | $(TEST_FLAC) \ |
231 | $(TEST_GIF) \ | 219 | $(TEST_GIF) \ |
232 | $(TEST_GSF) \ | 220 | $(TEST_GSF) \ |
@@ -612,19 +600,6 @@ test_sid_LDADD = \ | |||
612 | $(top_builddir)/src/plugins/libtest.la | 600 | $(top_builddir)/src/plugins/libtest.la |
613 | 601 | ||
614 | 602 | ||
615 | libextractor_thumbnailffmpeg_la_SOURCES = \ | ||
616 | thumbnailffmpeg_extractor.c | ||
617 | libextractor_thumbnailffmpeg_la_LDFLAGS = \ | ||
618 | $(PLUGINFLAGS) | ||
619 | libextractor_thumbnailffmpeg_la_LIBADD = \ | ||
620 | -lavutil -lavformat -lavcodec -lswscale -lmagic $(XLIB) | ||
621 | |||
622 | test_thumbnailffmpeg_SOURCES = \ | ||
623 | test_thumbnailffmpeg.c | ||
624 | test_thumbnailffmpeg_LDADD = \ | ||
625 | $(top_builddir)/src/plugins/libtest.la | ||
626 | |||
627 | |||
628 | libextractor_thumbnailgtk_la_SOURCES = \ | 603 | libextractor_thumbnailgtk_la_SOURCES = \ |
629 | thumbnailgtk_extractor.c | 604 | thumbnailgtk_extractor.c |
630 | libextractor_thumbnailgtk_la_LDFLAGS = \ | 605 | libextractor_thumbnailgtk_la_LDFLAGS = \ |
@@ -639,18 +614,6 @@ test_thumbnailgtk_SOURCES = \ | |||
639 | test_thumbnailgtk_LDADD = \ | 614 | test_thumbnailgtk_LDADD = \ |
640 | $(top_builddir)/src/plugins/libtest.la | 615 | $(top_builddir)/src/plugins/libtest.la |
641 | 616 | ||
642 | libextractor_previewopus_la_SOURCES = \ | ||
643 | previewopus_extractor.c | ||
644 | libextractor_previewopus_la_LDFLAGS = \ | ||
645 | $(PLUGINFLAGS) | ||
646 | libextractor_previewopus_la_LIBADD = \ | ||
647 | -lavutil -lavformat -lavcodec -lswscale -lavresample -lmagic $(XLIB) | ||
648 | |||
649 | test_previewopus_SOURCES = \ | ||
650 | test_previewopus.c | ||
651 | test_previewopus_LDADD = \ | ||
652 | $(top_builddir)/src/plugins/libtest.la | ||
653 | |||
654 | 617 | ||
655 | libextractor_tiff_la_SOURCES = \ | 618 | libextractor_tiff_la_SOURCES = \ |
656 | tiff_extractor.c | 619 | tiff_extractor.c |
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 */ | ||
diff --git a/src/plugins/test_previewopus.c b/src/plugins/test_previewopus.c deleted file mode 100644 index 6b0b1b8..0000000 --- a/src/plugins/test_previewopus.c +++ /dev/null | |||
@@ -1,52 +0,0 @@ | |||
1 | /* | ||
2 | This file is part of libextractor. | ||
3 | Copyright (C) 2013 Vidyut Samanta 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 plugins/test_previewopus.c | ||
22 | * @brief stub testcase for previewopus plugin | ||
23 | * @author Bruno Cabral | ||
24 | */ | ||
25 | #include "platform.h" | ||
26 | #include "test_lib.h" | ||
27 | |||
28 | |||
29 | /** | ||
30 | * Main function for the previewopus testcase. | ||
31 | * | ||
32 | * @param argc number of arguments (ignored) | ||
33 | * @param argv arguments (ignored) | ||
34 | * @return 0 on success | ||
35 | */ | ||
36 | int | ||
37 | main (int argc, char *argv[]) | ||
38 | { | ||
39 | struct SolutionData previewopus_audio_sol[] = { | ||
40 | // TODO. Can't test as it depends on the encoder. | ||
41 | { 0, 0, NULL, NULL, 0, -1 } | ||
42 | }; | ||
43 | struct ProblemSet ps[] = { | ||
44 | { "testdata/mpeg_alien.mpg", | ||
45 | previewopus_audio_sol }, | ||
46 | { NULL, NULL } | ||
47 | }; | ||
48 | return ET_main ("previewopus", ps); | ||
49 | } | ||
50 | |||
51 | |||
52 | /* end of test_thumbnailffmpeg.c */ | ||
diff --git a/src/plugins/test_thumbnailffmpeg.c b/src/plugins/test_thumbnailffmpeg.c deleted file mode 100644 index 3efd69b..0000000 --- a/src/plugins/test_thumbnailffmpeg.c +++ /dev/null | |||
@@ -1,51 +0,0 @@ | |||
1 | /* | ||
2 | This file is part of libextractor. | ||
3 | Copyright (C) 2012 Vidyut Samanta 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 plugins/test_thumbnailffmpeg.c | ||
22 | * @brief testcase for thumbnailffmpeg plugin | ||
23 | * @author Christian Grothoff | ||
24 | */ | ||
25 | #include "platform.h" | ||
26 | #include "test_lib.h" | ||
27 | |||
28 | |||
29 | /** | ||
30 | * Main function for the THUMBNAILFFMPEG testcase. | ||
31 | * | ||
32 | * @param argc number of arguments (ignored) | ||
33 | * @param argv arguments (ignored) | ||
34 | * @return 0 on success | ||
35 | */ | ||
36 | int | ||
37 | main (int argc, char *argv[]) | ||
38 | { | ||
39 | struct SolutionData thumbnailffmpeg_video_sol[] = { | ||
40 | { 0, 0, NULL, NULL, 0, -1 } | ||
41 | }; | ||
42 | struct ProblemSet ps[] = { | ||
43 | { "testdata/thumbnailffmpeg_video.mov", | ||
44 | thumbnailffmpeg_video_sol }, | ||
45 | { NULL, NULL } | ||
46 | }; | ||
47 | return ET_main ("thumbnailffmpeg", ps); | ||
48 | } | ||
49 | |||
50 | |||
51 | /* end of test_thumbnailffmpeg.c */ | ||
diff --git a/src/plugins/thumbnailffmpeg_extractor.c b/src/plugins/thumbnailffmpeg_extractor.c deleted file mode 100644 index ef7a05f..0000000 --- a/src/plugins/thumbnailffmpeg_extractor.c +++ /dev/null | |||
@@ -1,890 +0,0 @@ | |||
1 | /* | ||
2 | This file is part of libextractor. | ||
3 | Copyright Copyright (C) 2008, 2012 Heikki Lindholm 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 thumbnailffmpeg_extractor.c | ||
22 | * @author Heikki Lindholm | ||
23 | * @author Christian Grothoff | ||
24 | * @brief this extractor produces a binary encoded | ||
25 | * thumbnail of images and videos using the ffmpeg libs. | ||
26 | * | ||
27 | * This is a thumbnail extractor using the ffmpeg libraries that will eventually | ||
28 | * support extracting thumbnails from both image and video files. | ||
29 | * | ||
30 | * Note that ffmpeg has a few issues: | ||
31 | * (1) there are no recent official releases of the ffmpeg libs | ||
32 | * (2) ffmpeg has a history of having security issues (parser is not robust) | ||
33 | * | ||
34 | * So this plugin cannot be recommended for system with high security | ||
35 | *requirements. | ||
36 | */ | ||
37 | #include "platform.h" | ||
38 | #include "extractor.h" | ||
39 | #include <magic.h> | ||
40 | |||
41 | #include <libavutil/avutil.h> | ||
42 | #include <libavutil/imgutils.h> | ||
43 | #include <libavformat/avformat.h> | ||
44 | #include <libavcodec/avcodec.h> | ||
45 | #include <libswscale/swscale.h> | ||
46 | |||
47 | #if USE_JPEG | ||
48 | #ifdef PIX_FMT_YUVJ420P | ||
49 | #define PIX_OUTPUT_FORMAT PIX_FMT_YUVJ420P | ||
50 | #else | ||
51 | #define PIX_OUTPUT_FORMAT AV_PIX_FMT_YUVJ420P | ||
52 | #endif | ||
53 | #else | ||
54 | #ifdef PIX_FMT_RGB24 | ||
55 | #define PIX_OUTPUT_FORMAT PIX_FMT_RGB24 | ||
56 | #else | ||
57 | #define PIX_OUTPUT_FORMAT AV_PIX_FMT_RGB24 | ||
58 | #endif | ||
59 | #endif | ||
60 | |||
61 | /** | ||
62 | * Set to 1 to use JPEG, PNG otherwise | ||
63 | */ | ||
64 | #define USE_JPEG 1 | ||
65 | |||
66 | /** | ||
67 | * Set to 1 to enable a output file for testing. | ||
68 | */ | ||
69 | #define OUTPUT_FILE 0 | ||
70 | |||
71 | |||
72 | /** | ||
73 | * Set to 1 to use jpeg. | ||
74 | */ | ||
75 | #define DEBUG 0 | ||
76 | |||
77 | /** | ||
78 | * max dimension in pixels for the thumbnail. | ||
79 | */ | ||
80 | #define MAX_THUMB_DIMENSION 128 | ||
81 | |||
82 | /** | ||
83 | * Maximum size in bytes for the thumbnail. | ||
84 | */ | ||
85 | #define MAX_THUMB_BYTES (100 * 1024) | ||
86 | |||
87 | /** | ||
88 | * Number of bytes to feed to libav in one go. | ||
89 | */ | ||
90 | #define BUFFER_SIZE (32 * 1024) | ||
91 | |||
92 | /** | ||
93 | * Number of bytes to feed to libav in one go, with padding (padding is zeroed). | ||
94 | */ | ||
95 | #ifdef AV_INPUT_BUFFER_PADDING_SIZE | ||
96 | #define PADDED_BUFFER_SIZE (BUFFER_SIZE + AV_INPUT_BUFFER_PADDING_SIZE) | ||
97 | #else | ||
98 | /* legacy */ | ||
99 | #define PADDED_BUFFER_SIZE (BUFFER_SIZE + FF_INPUT_BUFFER_PADDING_SIZE) | ||
100 | #endif | ||
101 | |||
102 | #ifndef AV_CODEC_FLAG_QSCALE | ||
103 | #define AV_CODEC_FLAG_QSCALE CODEC_FLAG_QSCALE | ||
104 | #endif | ||
105 | |||
106 | |||
107 | /** | ||
108 | * Global handle to MAGIC data. | ||
109 | */ | ||
110 | static magic_t magic; | ||
111 | |||
112 | |||
113 | /** | ||
114 | * Read callback. | ||
115 | * | ||
116 | * @param opaque the 'struct EXTRACTOR_ExtractContext' | ||
117 | * @param buf where to write data | ||
118 | * @param buf_size how many bytes to read | ||
119 | * @return -1 on error (or for unknown file size) | ||
120 | */ | ||
121 | static int | ||
122 | read_cb (void *opaque, | ||
123 | uint8_t *buf, | ||
124 | int buf_size) | ||
125 | { | ||
126 | struct EXTRACTOR_ExtractContext *ec = opaque; | ||
127 | void *data; | ||
128 | ssize_t ret; | ||
129 | |||
130 | ret = ec->read (ec->cls, &data, buf_size); | ||
131 | if (ret <= 0) | ||
132 | return ret; | ||
133 | memcpy (buf, data, ret); | ||
134 | return ret; | ||
135 | } | ||
136 | |||
137 | |||
138 | /** | ||
139 | * Seek callback. | ||
140 | * | ||
141 | * @param opaque the 'struct EXTRACTOR_ExtractContext' | ||
142 | * @param offset where to seek | ||
143 | * @param whence how to seek; AVSEEK_SIZE to return file size without seeking | ||
144 | * @return -1 on error (or for unknown file size) | ||
145 | */ | ||
146 | static int64_t | ||
147 | seek_cb (void *opaque, | ||
148 | int64_t offset, | ||
149 | int whence) | ||
150 | { | ||
151 | struct EXTRACTOR_ExtractContext *ec = opaque; | ||
152 | |||
153 | if (AVSEEK_SIZE == whence) | ||
154 | return ec->get_size (ec->cls); | ||
155 | return ec->seek (ec->cls, offset, whence); | ||
156 | } | ||
157 | |||
158 | |||
159 | /** | ||
160 | * Rescale and encode a PNG thumbnail. | ||
161 | * | ||
162 | * @param src_width source image width | ||
163 | * @param src_height source image height | ||
164 | * @param src_stride | ||
165 | * @param src_pixfmt | ||
166 | * @param src_data source data | ||
167 | * @param dst_width desired thumbnail width | ||
168 | * @param dst_height desired thumbnail height | ||
169 | * @param output_data where to store the resulting PNG data | ||
170 | * @param output_max_size maximum size of result that is allowed | ||
171 | * @return the number of bytes used, 0 on error | ||
172 | */ | ||
173 | static size_t | ||
174 | create_thumbnail (AVCodecContext *pCodecCtx, int src_width, int src_height, | ||
175 | int src_stride[], | ||
176 | enum AVPixelFormat src_pixfmt, | ||
177 | const uint8_t *const src_data[], | ||
178 | int dst_width, int dst_height, | ||
179 | uint8_t **output_data, | ||
180 | size_t output_max_size) | ||
181 | { | ||
182 | AVCodecContext *encoder_codec_ctx; | ||
183 | AVDictionary *opts; | ||
184 | AVCodec *encoder_codec; | ||
185 | struct SwsContext *scaler_ctx; | ||
186 | AVFrame *dst_frame; | ||
187 | uint8_t *dst_buffer; | ||
188 | uint8_t *encoder_output_buffer; | ||
189 | size_t encoder_output_buffer_size; | ||
190 | int err; | ||
191 | |||
192 | AVPacket pkt; | ||
193 | av_init_packet (&pkt); | ||
194 | pkt.data = NULL; | ||
195 | pkt.size = 0; | ||
196 | int gotPacket; | ||
197 | #if USE_JPEG | ||
198 | if (NULL == (encoder_codec = avcodec_find_encoder (AV_CODEC_ID_MJPEG))) | ||
199 | #else | ||
200 | if (NULL == (encoder_codec = avcodec_find_encoder_by_name ("png"))) | ||
201 | #endif | ||
202 | { | ||
203 | #if DEBUG | ||
204 | fprintf (stderr, | ||
205 | "Couldn't find a encoder\n"); | ||
206 | #endif | ||
207 | return 0; | ||
208 | } | ||
209 | |||
210 | /* NOTE: the scaler will be used even if the src and dst image dimensions | ||
211 | * match, because the scaler will also perform colour space conversion */ | ||
212 | if (NULL == | ||
213 | (scaler_ctx = | ||
214 | sws_getContext (src_width, src_height, src_pixfmt, | ||
215 | dst_width, dst_height, | ||
216 | PIX_OUTPUT_FORMAT, | ||
217 | SWS_BILINEAR, NULL, NULL, NULL))) | ||
218 | { | ||
219 | #if DEBUG | ||
220 | fprintf (stderr, | ||
221 | "Failed to get a scaler context\n"); | ||
222 | #endif | ||
223 | return 0; | ||
224 | } | ||
225 | |||
226 | dst_frame = av_frame_alloc (); | ||
227 | if (NULL == dst_frame) | ||
228 | { | ||
229 | #if DEBUG | ||
230 | fprintf (stderr, | ||
231 | "Failed to allocate the destination image frame\n"); | ||
232 | #endif | ||
233 | sws_freeContext (scaler_ctx); | ||
234 | return 0; | ||
235 | } | ||
236 | if (NULL == (dst_buffer = | ||
237 | av_malloc (av_image_get_buffer_size (PIX_OUTPUT_FORMAT, | ||
238 | dst_width, | ||
239 | dst_height, | ||
240 | 1)))) | ||
241 | { | ||
242 | #if DEBUG | ||
243 | fprintf (stderr, | ||
244 | "Failed to allocate the destination image buffer\n"); | ||
245 | #endif | ||
246 | av_frame_free (&dst_frame); | ||
247 | sws_freeContext (scaler_ctx); | ||
248 | return 0; | ||
249 | } | ||
250 | av_image_fill_arrays (dst_frame->data, | ||
251 | dst_frame->linesize, | ||
252 | dst_buffer, | ||
253 | PIX_OUTPUT_FORMAT, | ||
254 | dst_width, | ||
255 | dst_height, | ||
256 | 1); | ||
257 | sws_scale (scaler_ctx, | ||
258 | src_data, | ||
259 | src_stride, | ||
260 | 0, src_height, | ||
261 | dst_frame->data, | ||
262 | dst_frame->linesize); | ||
263 | |||
264 | encoder_output_buffer_size = output_max_size; | ||
265 | if (NULL == (encoder_output_buffer = av_malloc (encoder_output_buffer_size))) | ||
266 | { | ||
267 | #if DEBUG | ||
268 | fprintf (stderr, | ||
269 | "Failed to allocate the encoder output buffer\n"); | ||
270 | #endif | ||
271 | av_free (dst_buffer); | ||
272 | av_frame_free (&dst_frame); | ||
273 | sws_freeContext (scaler_ctx); | ||
274 | return 0; | ||
275 | } | ||
276 | |||
277 | if (NULL == (encoder_codec_ctx = avcodec_alloc_context3 (encoder_codec))) | ||
278 | { | ||
279 | #if DEBUG | ||
280 | fprintf (stderr, | ||
281 | "Failed to allocate the encoder codec context\n"); | ||
282 | #endif | ||
283 | av_free (encoder_output_buffer); | ||
284 | av_free (dst_buffer); | ||
285 | av_frame_free (&dst_frame); | ||
286 | sws_freeContext (scaler_ctx); | ||
287 | return 0; | ||
288 | } | ||
289 | encoder_codec_ctx->width = dst_width; | ||
290 | encoder_codec_ctx->height = dst_height; | ||
291 | #if USE_JPEG | ||
292 | encoder_codec_ctx->bit_rate = pCodecCtx->bit_rate; | ||
293 | encoder_codec_ctx->codec_id = AV_CODEC_ID_MJPEG; | ||
294 | encoder_codec_ctx->codec_type = AVMEDIA_TYPE_VIDEO; | ||
295 | encoder_codec_ctx->time_base.num = pCodecCtx->time_base.num; | ||
296 | encoder_codec_ctx->time_base.den = pCodecCtx->time_base.den; | ||
297 | encoder_codec_ctx->pix_fmt = PIX_OUTPUT_FORMAT; | ||
298 | #else | ||
299 | encoder_codec_ctx->pix_fmt = PIX_OUTPUT_FORMAT; | ||
300 | #endif | ||
301 | |||
302 | opts = NULL; | ||
303 | if (avcodec_open2 (encoder_codec_ctx, encoder_codec, &opts) < 0) | ||
304 | { | ||
305 | #if DEBUG | ||
306 | fprintf (stderr, | ||
307 | "Failed to open the encoder\n"); | ||
308 | #endif | ||
309 | avcodec_free_context (&encoder_codec_ctx); | ||
310 | av_free (encoder_output_buffer); | ||
311 | av_free (dst_buffer); | ||
312 | av_frame_free (&dst_frame); | ||
313 | sws_freeContext (scaler_ctx); | ||
314 | return 0; | ||
315 | } | ||
316 | |||
317 | |||
318 | #ifdef USE_JPEG | ||
319 | #if FF_API_MPV_OPT | ||
320 | encoder_codec_ctx->mb_lmin = encoder_codec_ctx->lmin = | ||
321 | encoder_codec_ctx->qmin * FF_QP2LAMBDA; | ||
322 | encoder_codec_ctx->mb_lmax = encoder_codec_ctx->lmax = | ||
323 | encoder_codec_ctx->qmax * FF_QP2LAMBDA; | ||
324 | #else | ||
325 | encoder_codec_ctx->mb_lmin = encoder_codec_ctx->qmin * FF_QP2LAMBDA; | ||
326 | encoder_codec_ctx->mb_lmax = encoder_codec_ctx->qmax * FF_QP2LAMBDA; | ||
327 | #endif | ||
328 | encoder_codec_ctx->flags = AV_CODEC_FLAG_QSCALE; | ||
329 | encoder_codec_ctx->global_quality = encoder_codec_ctx->qmin * FF_QP2LAMBDA; | ||
330 | |||
331 | dst_frame->pts = 1; | ||
332 | dst_frame->quality = encoder_codec_ctx->global_quality; | ||
333 | #endif | ||
334 | |||
335 | err = avcodec_encode_video2 (encoder_codec_ctx, | ||
336 | &pkt, | ||
337 | dst_frame, &gotPacket); | ||
338 | |||
339 | if (err < 0) | ||
340 | goto cleanup; | ||
341 | err = pkt.size; | ||
342 | memcpy (encoder_output_buffer,pkt.data, pkt.size); | ||
343 | |||
344 | av_packet_unref (&pkt); | ||
345 | cleanup: | ||
346 | av_dict_free (&opts); | ||
347 | avcodec_close (encoder_codec_ctx); | ||
348 | avcodec_free_context (&encoder_codec_ctx); | ||
349 | av_free (dst_buffer); | ||
350 | av_frame_free (&dst_frame); | ||
351 | sws_freeContext (scaler_ctx); | ||
352 | *output_data = encoder_output_buffer; | ||
353 | |||
354 | return err < 0 ? 0 : err; | ||
355 | } | ||
356 | |||
357 | |||
358 | /** | ||
359 | * calculate the thumbnail dimensions, taking pixel aspect into account | ||
360 | * | ||
361 | * @param src_width source image width | ||
362 | * @param src_height source image height | ||
363 | * @param src_sar_num | ||
364 | * @param src_sar_den | ||
365 | * @param dst_width desired thumbnail width (set) | ||
366 | * @param dst_height desired thumbnail height (set) | ||
367 | */ | ||
368 | static void | ||
369 | calculate_thumbnail_dimensions (int src_width, | ||
370 | int src_height, | ||
371 | int src_sar_num, | ||
372 | int src_sar_den, | ||
373 | int *dst_width, | ||
374 | int *dst_height) | ||
375 | { | ||
376 | if ( (src_sar_num <= 0) || (src_sar_den <= 0) ) | ||
377 | { | ||
378 | src_sar_num = 1; | ||
379 | src_sar_den = 1; | ||
380 | } | ||
381 | if ((src_width * src_sar_num) / src_sar_den > src_height) | ||
382 | { | ||
383 | *dst_width = MAX_THUMB_DIMENSION; | ||
384 | *dst_height = (*dst_width * src_height) | ||
385 | / ((src_width * src_sar_num) / src_sar_den); | ||
386 | } | ||
387 | else | ||
388 | { | ||
389 | *dst_height = MAX_THUMB_DIMENSION; | ||
390 | *dst_width = (*dst_height | ||
391 | * ((src_width * src_sar_num) / src_sar_den)) | ||
392 | / src_height; | ||
393 | } | ||
394 | if (*dst_width < 8) | ||
395 | *dst_width = 8; | ||
396 | if (*dst_height < 1) | ||
397 | *dst_height = 1; | ||
398 | #if DEBUG | ||
399 | fprintf (stderr, | ||
400 | "Thumbnail dimensions: %d %d\n", | ||
401 | *dst_width, *dst_height); | ||
402 | #endif | ||
403 | } | ||
404 | |||
405 | |||
406 | #define ENUM_CODEC_ID enum AVCodecID | ||
407 | |||
408 | /** | ||
409 | * Perform thumbnailing when the input is an image. | ||
410 | * | ||
411 | * @param image_codec_id ffmpeg codec for the image format | ||
412 | * @param ec extraction context to use | ||
413 | */ | ||
414 | static void | ||
415 | extract_image (ENUM_CODEC_ID image_codec_id, | ||
416 | struct EXTRACTOR_ExtractContext *ec) | ||
417 | { | ||
418 | AVDictionary *opts; | ||
419 | AVCodecContext *codec_ctx; | ||
420 | AVCodec *codec; | ||
421 | AVPacket avpkt; | ||
422 | AVFrame *frame; | ||
423 | uint8_t *encoded_thumbnail; | ||
424 | int thumb_width; | ||
425 | int thumb_height; | ||
426 | int err; | ||
427 | int frame_finished; | ||
428 | ssize_t iret; | ||
429 | void *data; | ||
430 | unsigned char padded_data[PADDED_BUFFER_SIZE]; | ||
431 | |||
432 | if (NULL == (codec = avcodec_find_decoder (image_codec_id))) | ||
433 | { | ||
434 | #if DEBUG | ||
435 | fprintf (stderr, | ||
436 | "No suitable codec found\n"); | ||
437 | #endif | ||
438 | return; | ||
439 | } | ||
440 | if (NULL == (codec_ctx = avcodec_alloc_context3 (codec))) | ||
441 | { | ||
442 | #if DEBUG | ||
443 | fprintf (stderr, | ||
444 | "Failed to allocate codec context\n"); | ||
445 | #endif | ||
446 | return; | ||
447 | } | ||
448 | opts = NULL; | ||
449 | if (0 != avcodec_open2 (codec_ctx, codec, &opts)) | ||
450 | { | ||
451 | #if DEBUG | ||
452 | fprintf (stderr, | ||
453 | "Failed to open image codec\n"); | ||
454 | #endif | ||
455 | avcodec_free_context (&codec_ctx); | ||
456 | return; | ||
457 | } | ||
458 | av_dict_free (&opts); | ||
459 | frame = av_frame_alloc (); | ||
460 | if (NULL == frame) | ||
461 | { | ||
462 | #if DEBUG | ||
463 | fprintf (stderr, | ||
464 | "Failed to allocate frame\n"); | ||
465 | #endif | ||
466 | avcodec_close (codec_ctx); | ||
467 | avcodec_free_context (&codec_ctx); | ||
468 | return; | ||
469 | } | ||
470 | |||
471 | frame_finished = 0; | ||
472 | while (! frame_finished) | ||
473 | { | ||
474 | if (0 >= (iret = ec->read (ec->cls, | ||
475 | &data, | ||
476 | BUFFER_SIZE))) | ||
477 | break; | ||
478 | memcpy (padded_data, data, iret); | ||
479 | memset (&padded_data[iret], 0, PADDED_BUFFER_SIZE - iret); | ||
480 | av_init_packet (&avpkt); | ||
481 | avpkt.data = padded_data; | ||
482 | avpkt.size = iret; | ||
483 | avcodec_decode_video2 (codec_ctx, frame, &frame_finished, &avpkt); | ||
484 | } | ||
485 | if (! frame_finished) | ||
486 | { | ||
487 | #if DEBUG | ||
488 | fprintf (stderr, | ||
489 | "Failed to decode a complete frame\n"); | ||
490 | #endif | ||
491 | av_frame_free (&frame); | ||
492 | avcodec_close (codec_ctx); | ||
493 | avcodec_free_context (&codec_ctx); | ||
494 | return; | ||
495 | } | ||
496 | calculate_thumbnail_dimensions (codec_ctx->width, codec_ctx->height, | ||
497 | codec_ctx->sample_aspect_ratio.num, | ||
498 | codec_ctx->sample_aspect_ratio.den, | ||
499 | &thumb_width, &thumb_height); | ||
500 | |||
501 | err = create_thumbnail (codec_ctx, codec_ctx->width, codec_ctx->height, | ||
502 | frame->linesize, codec_ctx->pix_fmt, | ||
503 | (const uint8_t *const*) frame->data, | ||
504 | thumb_width, thumb_height, | ||
505 | &encoded_thumbnail, MAX_THUMB_BYTES); | ||
506 | if (err > 0) | ||
507 | { | ||
508 | ec->proc (ec->cls, | ||
509 | "thumbnailffmpeg", | ||
510 | EXTRACTOR_METATYPE_THUMBNAIL, | ||
511 | EXTRACTOR_METAFORMAT_BINARY, | ||
512 | "image/png", | ||
513 | (const char*) encoded_thumbnail, | ||
514 | err); | ||
515 | |||
516 | #if OUTPUT_FILE | ||
517 | FILE *f; | ||
518 | #ifdef USE_JPEG | ||
519 | f = fopen ("thumb.jpg", "wb"); | ||
520 | #else | ||
521 | f = fopen ("thumb.png", "wb"); | ||
522 | #endif | ||
523 | if (! f) | ||
524 | { | ||
525 | fprintf (stderr, "Could not open %s\n", "file"); | ||
526 | exit (1); | ||
527 | } | ||
528 | |||
529 | fwrite (encoded_thumbnail, 1, err, f); | ||
530 | fclose (f); | ||
531 | |||
532 | #endif | ||
533 | |||
534 | |||
535 | av_free (encoded_thumbnail); | ||
536 | } | ||
537 | av_frame_free (&frame); | ||
538 | avcodec_close (codec_ctx); | ||
539 | avcodec_free_context (&codec_ctx); | ||
540 | } | ||
541 | |||
542 | |||
543 | /** | ||
544 | * Perform thumbnailing when the input is a video | ||
545 | * | ||
546 | * @param ec extraction context to use | ||
547 | */ | ||
548 | static void | ||
549 | extract_video (struct EXTRACTOR_ExtractContext *ec) | ||
550 | { | ||
551 | AVPacket packet; | ||
552 | AVIOContext *io_ctx; | ||
553 | struct AVFormatContext *format_ctx; | ||
554 | AVCodecContext *codec_ctx; | ||
555 | AVCodecParameters *codecpar; | ||
556 | AVCodec *codec; | ||
557 | AVDictionary *options; | ||
558 | AVFrame *frame; | ||
559 | uint8_t *encoded_thumbnail; | ||
560 | int video_stream_index; | ||
561 | int thumb_width; | ||
562 | int thumb_height; | ||
563 | int i; | ||
564 | int err; | ||
565 | int frame_finished; | ||
566 | unsigned char *iob; | ||
567 | int duration; | ||
568 | |||
569 | if (NULL == (iob = av_malloc (16 * 1024))) | ||
570 | return; | ||
571 | if (NULL == (io_ctx = avio_alloc_context (iob, | ||
572 | 16 * 1024, | ||
573 | 0, ec, | ||
574 | &read_cb, | ||
575 | NULL /* no writing */, | ||
576 | &seek_cb))) | ||
577 | { | ||
578 | av_free (iob); | ||
579 | return; | ||
580 | } | ||
581 | if (NULL == (format_ctx = avformat_alloc_context ())) | ||
582 | { | ||
583 | av_free (io_ctx); | ||
584 | return; | ||
585 | } | ||
586 | format_ctx->pb = io_ctx; | ||
587 | options = NULL; | ||
588 | if (0 != avformat_open_input (&format_ctx, "<no file>", NULL, &options)) | ||
589 | { | ||
590 | av_free (io_ctx); | ||
591 | return; | ||
592 | } | ||
593 | av_dict_free (&options); | ||
594 | if (0 > avformat_find_stream_info (format_ctx, NULL)) | ||
595 | { | ||
596 | #if DEBUG | ||
597 | fprintf (stderr, | ||
598 | "Failed to read stream info\n"); | ||
599 | #endif | ||
600 | avformat_close_input (&format_ctx); | ||
601 | av_free (io_ctx); | ||
602 | return; | ||
603 | } | ||
604 | codec = NULL; | ||
605 | codec_ctx = NULL; | ||
606 | video_stream_index = -1; | ||
607 | for (i = 0; i<format_ctx->nb_streams; i++) | ||
608 | { | ||
609 | codecpar = format_ctx->streams[i]->codecpar; | ||
610 | codec_ctx = format_ctx->streams[i]->codec; | ||
611 | if (AVMEDIA_TYPE_VIDEO != codec_ctx->codec_type) | ||
612 | continue; | ||
613 | if (NULL == (codec = avcodec_find_decoder (codecpar->codec_id))) | ||
614 | continue; | ||
615 | options = NULL; | ||
616 | if (0 != (err = avcodec_open2 (codec_ctx, codec, &options))) | ||
617 | { | ||
618 | codec = NULL; | ||
619 | continue; | ||
620 | } | ||
621 | av_dict_free (&options); | ||
622 | video_stream_index = i; | ||
623 | break; | ||
624 | } | ||
625 | if ( (-1 == video_stream_index) || | ||
626 | (0 == codec_ctx->width) || | ||
627 | (0 == codec_ctx->height) ) | ||
628 | { | ||
629 | #if DEBUG | ||
630 | fprintf (stderr, | ||
631 | "No video streams or no suitable codec found\n"); | ||
632 | #endif | ||
633 | if (NULL != codec) | ||
634 | avcodec_close (codec_ctx); | ||
635 | avformat_close_input (&format_ctx); | ||
636 | av_free (io_ctx); | ||
637 | return; | ||
638 | } | ||
639 | |||
640 | frame = av_frame_alloc (); | ||
641 | if (NULL == frame) | ||
642 | { | ||
643 | #if DEBUG | ||
644 | fprintf (stderr, | ||
645 | "Failed to allocate frame\n"); | ||
646 | #endif | ||
647 | avcodec_close (codec_ctx); | ||
648 | avformat_close_input (&format_ctx); | ||
649 | av_free (io_ctx); | ||
650 | return; | ||
651 | } | ||
652 | |||
653 | if (format_ctx->duration == AV_NOPTS_VALUE) | ||
654 | { | ||
655 | duration = -1; | ||
656 | #if DEBUG | ||
657 | fprintf (stderr, | ||
658 | "Duration unknown\n"); | ||
659 | #endif | ||
660 | } | ||
661 | else | ||
662 | { | ||
663 | duration = format_ctx->duration; | ||
664 | } | ||
665 | |||
666 | /* if duration is known, seek to first tried, | ||
667 | * else use 10 sec into stream */ | ||
668 | |||
669 | if (-1 != duration) | ||
670 | err = av_seek_frame (format_ctx, -1, (duration / 3), 0); | ||
671 | else | ||
672 | err = av_seek_frame (format_ctx, -1, 10 * AV_TIME_BASE, 0); | ||
673 | |||
674 | if (err >= 0) | ||
675 | avcodec_flush_buffers (codec_ctx); | ||
676 | frame_finished = 0; | ||
677 | |||
678 | while (1) | ||
679 | { | ||
680 | err = av_read_frame (format_ctx, &packet); | ||
681 | if (err < 0) | ||
682 | break; | ||
683 | if (packet.stream_index == video_stream_index) | ||
684 | { | ||
685 | avcodec_decode_video2 (codec_ctx, | ||
686 | frame, | ||
687 | &frame_finished, | ||
688 | &packet); | ||
689 | if (frame_finished && frame->key_frame) | ||
690 | { | ||
691 | av_packet_unref (&packet); | ||
692 | break; | ||
693 | } | ||
694 | } | ||
695 | av_packet_unref (&packet); | ||
696 | } | ||
697 | |||
698 | if (! frame_finished) | ||
699 | { | ||
700 | #if DEBUG | ||
701 | fprintf (stderr, | ||
702 | "Failed to decode a complete frame\n"); | ||
703 | #endif | ||
704 | av_frame_free (&frame); | ||
705 | avcodec_close (codec_ctx); | ||
706 | avformat_close_input (&format_ctx); | ||
707 | av_free (io_ctx); | ||
708 | return; | ||
709 | } | ||
710 | calculate_thumbnail_dimensions (codec_ctx->width, codec_ctx->height, | ||
711 | codec_ctx->sample_aspect_ratio.num, | ||
712 | codec_ctx->sample_aspect_ratio.den, | ||
713 | &thumb_width, &thumb_height); | ||
714 | |||
715 | err = create_thumbnail (codec_ctx, codec_ctx->width, codec_ctx->height, | ||
716 | frame->linesize, codec_ctx->pix_fmt, | ||
717 | (const uint8_t*const *) frame->data, | ||
718 | thumb_width, thumb_height, | ||
719 | &encoded_thumbnail, MAX_THUMB_BYTES); | ||
720 | if (err > 0) | ||
721 | { | ||
722 | ec->proc (ec->cls, | ||
723 | "thumbnailffmpeg", | ||
724 | EXTRACTOR_METATYPE_THUMBNAIL, | ||
725 | EXTRACTOR_METAFORMAT_BINARY, | ||
726 | "image/png", | ||
727 | (const char*) encoded_thumbnail, | ||
728 | err); | ||
729 | #if OUTPUT_FILE | ||
730 | FILE *f; | ||
731 | #ifdef USE_JPEG | ||
732 | f = fopen ("thumb.jpg", "wb"); | ||
733 | #else | ||
734 | f = fopen ("thumb.png", "wb"); | ||
735 | #endif | ||
736 | if (! f) | ||
737 | { | ||
738 | fprintf (stderr, "Could not open %s\n", "file"); | ||
739 | exit (1); | ||
740 | } | ||
741 | |||
742 | fwrite (encoded_thumbnail, 1, err, f); | ||
743 | fclose (f); | ||
744 | #endif | ||
745 | av_free (encoded_thumbnail); | ||
746 | } | ||
747 | av_frame_free (&frame); | ||
748 | avcodec_close (codec_ctx); | ||
749 | avformat_close_input (&format_ctx); | ||
750 | av_free (io_ctx); | ||
751 | } | ||
752 | |||
753 | |||
754 | /** | ||
755 | * Pair of mime type and ffmpeg decoder ID. | ||
756 | */ | ||
757 | struct MIMEToDecoderMapping | ||
758 | { | ||
759 | /** | ||
760 | * String for a mime type. | ||
761 | */ | ||
762 | const char *mime_type; | ||
763 | |||
764 | /** | ||
765 | * Corresponding ffmpeg decoder ID. | ||
766 | */ | ||
767 | ENUM_CODEC_ID codec_id; | ||
768 | }; | ||
769 | |||
770 | |||
771 | /** | ||
772 | * map MIME image types to an ffmpeg decoder | ||
773 | */ | ||
774 | static const struct MIMEToDecoderMapping m2d_map[] = { | ||
775 | |||
776 | #if LIBAVCODEC_BUILD >= AV_VERSION_INT (54,25,0) | ||
777 | { "image/x-bmp", AV_CODEC_ID_BMP }, | ||
778 | { "image/gif", AV_CODEC_ID_GIF }, | ||
779 | { "image/jpeg", AV_CODEC_ID_MJPEG }, | ||
780 | { "image/png", AV_CODEC_ID_PNG }, | ||
781 | { "image/x-png", AV_CODEC_ID_PNG }, | ||
782 | { "image/x-portable-pixmap", AV_CODEC_ID_PPM }, | ||
783 | { NULL, AV_CODEC_ID_NONE } | ||
784 | #else | ||
785 | { "image/x-bmp", CODEC_ID_BMP }, | ||
786 | { "image/gif", CODEC_ID_GIF }, | ||
787 | { "image/jpeg", CODEC_ID_MJPEG }, | ||
788 | { "image/png", CODEC_ID_PNG }, | ||
789 | { "image/x-png", CODEC_ID_PNG }, | ||
790 | { "image/x-portable-pixmap", CODEC_ID_PPM }, | ||
791 | { NULL, CODEC_ID_NONE } | ||
792 | #endif | ||
793 | |||
794 | }; | ||
795 | |||
796 | |||
797 | /** | ||
798 | * Main method for the ffmpeg-thumbnailer plugin. | ||
799 | * | ||
800 | * @param ec extraction context | ||
801 | */ | ||
802 | void | ||
803 | EXTRACTOR_thumbnailffmpeg_extract_method (struct EXTRACTOR_ExtractContext *ec) | ||
804 | { | ||
805 | unsigned int i; | ||
806 | ssize_t iret; | ||
807 | void *data; | ||
808 | const char *mime; | ||
809 | |||
810 | if (-1 == (iret = ec->read (ec->cls, | ||
811 | &data, | ||
812 | 16 * 1024))) | ||
813 | return; | ||
814 | if (NULL == (mime = magic_buffer (magic, data, iret))) | ||
815 | return; | ||
816 | if (0 != ec->seek (ec->cls, 0, SEEK_SET)) | ||
817 | return; | ||
818 | for (i = 0; NULL != m2d_map[i].mime_type; i++) | ||
819 | if (0 == strcmp (m2d_map[i].mime_type, mime)) | ||
820 | { | ||
821 | extract_image (m2d_map[i].codec_id, ec); | ||
822 | return; | ||
823 | } | ||
824 | extract_video (ec); | ||
825 | } | ||
826 | |||
827 | |||
828 | /** | ||
829 | * This plugin sometimes is installed under the alias 'thumbnail'. | ||
830 | * So we need to provide a second entry method. | ||
831 | * | ||
832 | * @param ec extraction context | ||
833 | */ | ||
834 | void | ||
835 | EXTRACTOR_thumbnail_extract_method (struct EXTRACTOR_ExtractContext *ec) | ||
836 | { | ||
837 | EXTRACTOR_thumbnailffmpeg_extract_method (ec); | ||
838 | } | ||
839 | |||
840 | |||
841 | /** | ||
842 | * Log callback. Does nothing. | ||
843 | * | ||
844 | * @param ptr NULL | ||
845 | * @param level log level | ||
846 | * @param format format string | ||
847 | * @param ap arguments for format | ||
848 | */ | ||
849 | static void | ||
850 | thumbnailffmpeg_av_log_callback (void*ptr, | ||
851 | int level, | ||
852 | const char *format, | ||
853 | va_list ap) | ||
854 | { | ||
855 | #if DEBUG | ||
856 | vfprintf (stderr, format, ap); | ||
857 | #endif | ||
858 | } | ||
859 | |||
860 | |||
861 | /** | ||
862 | * Initialize av-libs and load magic file. | ||
863 | */ | ||
864 | void __attribute__ ((constructor)) | ||
865 | thumbnailffmpeg_lib_init (void) | ||
866 | { | ||
867 | av_log_set_callback (&thumbnailffmpeg_av_log_callback); | ||
868 | magic = magic_open (MAGIC_MIME_TYPE); | ||
869 | if (0 != magic_load (magic, NULL)) | ||
870 | { | ||
871 | /* FIXME: how to deal with errors? */ | ||
872 | } | ||
873 | } | ||
874 | |||
875 | |||
876 | /** | ||
877 | * Destructor for the library, cleans up. | ||
878 | */ | ||
879 | void __attribute__ ((destructor)) | ||
880 | thumbnailffmpeg_ltdl_fini () | ||
881 | { | ||
882 | if (NULL != magic) | ||
883 | { | ||
884 | magic_close (magic); | ||
885 | magic = NULL; | ||
886 | } | ||
887 | } | ||
888 | |||
889 | |||
890 | /* end of thumbnailffmpeg_extractor.c */ | ||