aboutsummaryrefslogtreecommitdiff
path: root/src/plugins/thumbnailffmpeg_extractor.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/thumbnailffmpeg_extractor.c')
-rw-r--r--src/plugins/thumbnailffmpeg_extractor.c890
1 files changed, 0 insertions, 890 deletions
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 */
110static 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 */
121static int
122read_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 */
146static int64_t
147seek_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 */
173static size_t
174create_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);
345cleanup:
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 */
368static void
369calculate_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 */
414static void
415extract_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 */
548static void
549extract_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 */
757struct 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 */
774static 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 */
802void
803EXTRACTOR_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 */
834void
835EXTRACTOR_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 */
849static void
850thumbnailffmpeg_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 */
864void __attribute__ ((constructor))
865thumbnailffmpeg_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 */
879void __attribute__ ((destructor))
880thumbnailffmpeg_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 */