aboutsummaryrefslogtreecommitdiff
path: root/src/plugins/old/thumbnailffmpeg_extractor.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/old/thumbnailffmpeg_extractor.c')
-rw-r--r--src/plugins/old/thumbnailffmpeg_extractor.c655
1 files changed, 0 insertions, 655 deletions
diff --git a/src/plugins/old/thumbnailffmpeg_extractor.c b/src/plugins/old/thumbnailffmpeg_extractor.c
deleted file mode 100644
index e93e0b2..0000000
--- a/src/plugins/old/thumbnailffmpeg_extractor.c
+++ /dev/null
@@ -1,655 +0,0 @@
1/*
2 This file is part of libextractor.
3 Copyright (C) 2008 Heikki Lindholm
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 2, 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., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21/**
22 * @file thumbnailffmpeg_extractor.c
23 * @author Heikki Lindholm
24 * @brief this extractor produces a binary encoded
25 * thumbnail of images and videos using the ffmpeg libs.
26 */
27
28/* This is a thumbnail extractor using the ffmpeg libraries that will eventually
29 support extracting thumbnails from both image and video files.
30
31 Note that ffmpeg has a few issues:
32 (1) there are no recent official releases of the ffmpeg libs
33 (2) ffmpeg has a history of having security issues (parser is not robust)
34
35 So this plugin cannot be recommended for system with high security
36 requirements.
37*/
38
39#include "platform.h"
40#include "extractor.h"
41#if HAVE_LIBAVUTIL_AVUTIL_H
42#include <libavutil/avutil.h>
43#elif HAVE_FFMPEG_AVUTIL_H
44#include <ffmpeg/avutil.h>
45#endif
46#if HAVE_LIBAVFORMAT_AVFORMAT_H
47#include <libavformat/avformat.h>
48#elif HAVE_FFMPEG_AVFORMAT_H
49#include <ffmpeg/avformat.h>
50#endif
51#if HAVE_LIBAVCODEC_AVCODEC_H
52#include <libavcodec/avcodec.h>
53#elif HAVE_FFMPEG_AVCODEC_H
54#include <ffmpeg/avcodec.h>
55#endif
56#if HAVE_LIBSWSCALE_SWSCALE_H
57#include <libswscale/swscale.h>
58#elif HAVE_FFMPEG_SWSCALE_H
59#include <ffmpeg/swscale.h>
60#endif
61
62#include "mime_extractor.c" /* TODO: do this cleaner */
63
64#define DEBUG 0
65
66static void thumbnailffmpeg_av_log_callback(void* ptr,
67 int level,
68 const char *format,
69 va_list ap)
70{
71#if DEBUG
72 vfprintf(stderr, format, ap);
73#endif
74}
75
76void __attribute__ ((constructor)) ffmpeg_lib_init (void)
77{
78 av_log_set_callback (thumbnailffmpeg_av_log_callback);
79 av_register_all ();
80}
81
82#define MAX_THUMB_DIMENSION 128 /* max dimension in pixels */
83#define MAX_THUMB_BYTES (100*1024)
84
85/*
86 * Rescale and encode a PNG thumbnail
87 * on success, fills in output_data and returns the number of bytes used
88 */
89static size_t create_thumbnail(
90 int src_width, int src_height, int src_stride[],
91 enum PixelFormat src_pixfmt, const uint8_t * const src_data[],
92 int dst_width, int dst_height,
93 uint8_t **output_data, size_t output_max_size)
94{
95 AVCodecContext *encoder_codec_ctx = NULL;
96 AVCodec *encoder_codec = NULL;
97 struct SwsContext *scaler_ctx = NULL;
98 int sws_flags = SWS_BILINEAR;
99 AVFrame *dst_frame = NULL;
100 uint8_t *dst_buffer = NULL;
101 uint8_t *encoder_output_buffer = NULL;
102 size_t encoder_output_buffer_size;
103 int err;
104
105 encoder_codec = avcodec_find_encoder_by_name ("png");
106 if (encoder_codec == NULL)
107 {
108#if DEBUG
109 fprintf (stderr,
110 "Couldn't find a PNG encoder\n");
111#endif
112 return 0;
113 }
114
115 /* NOTE: the scaler will be used even if the src and dst image dimensions
116 * match, because the scaler will also perform colour space conversion */
117 scaler_ctx =
118 sws_getContext (src_width, src_height, src_pixfmt,
119 dst_width, dst_height, PIX_FMT_RGB24,
120 sws_flags, NULL, NULL, NULL);
121 if (scaler_ctx == NULL)
122 {
123#if DEBUG
124 fprintf (stderr,
125 "Failed to get a scaler context\n");
126#endif
127 return 0;
128 }
129
130 dst_frame = avcodec_alloc_frame ();
131 if (dst_frame == NULL)
132 {
133#if DEBUG
134 fprintf (stderr,
135 "Failed to allocate the destination image frame\n");
136#endif
137 sws_freeContext(scaler_ctx);
138 return 0;
139 }
140 dst_buffer =
141 av_malloc (avpicture_get_size (PIX_FMT_RGB24, dst_width, dst_height));
142 if (dst_buffer == NULL)
143 {
144#if DEBUG
145 fprintf (stderr,
146 "Failed to allocate the destination image buffer\n");
147#endif
148 av_free (dst_frame);
149 sws_freeContext(scaler_ctx);
150 return 0;
151 }
152 avpicture_fill ((AVPicture *) dst_frame, dst_buffer,
153 PIX_FMT_RGB24, dst_width, dst_height);
154
155 sws_scale (scaler_ctx,
156 src_data,
157 src_stride,
158 0, src_height,
159 dst_frame->data,
160 dst_frame->linesize);
161
162 encoder_output_buffer_size = output_max_size;
163 encoder_output_buffer = av_malloc (encoder_output_buffer_size);
164 if (encoder_output_buffer == NULL)
165 {
166#if DEBUG
167 fprintf (stderr,
168 "Failed to allocate the encoder output buffer\n");
169#endif
170 av_free (dst_buffer);
171 av_free (dst_frame);
172 sws_freeContext(scaler_ctx);
173 return 0;
174 }
175
176 encoder_codec_ctx = avcodec_alloc_context ();
177 if (encoder_codec_ctx == NULL)
178 {
179#if DEBUG
180 fprintf (stderr,
181 "Failed to allocate the encoder codec context\n");
182#endif
183 av_free (encoder_output_buffer);
184 av_free (dst_buffer);
185 av_free (dst_frame);
186 sws_freeContext(scaler_ctx);
187 return 0;
188 }
189 encoder_codec_ctx->width = dst_width;
190 encoder_codec_ctx->height = dst_height;
191 encoder_codec_ctx->pix_fmt = PIX_FMT_RGB24;
192
193 if (avcodec_open (encoder_codec_ctx, encoder_codec) < 0)
194 {
195#if DEBUG
196 fprintf (stderr,
197 "Failed to open the encoder\n");
198#endif
199 av_free (encoder_codec_ctx);
200 av_free (encoder_output_buffer);
201 av_free (dst_buffer);
202 av_free (dst_frame);
203 sws_freeContext(scaler_ctx);
204 return 0;
205 }
206
207 err = avcodec_encode_video (encoder_codec_ctx,
208 encoder_output_buffer,
209 encoder_output_buffer_size, dst_frame);
210
211 avcodec_close (encoder_codec_ctx);
212 av_free (encoder_codec_ctx);
213 av_free (dst_buffer);
214 av_free (dst_frame);
215 sws_freeContext(scaler_ctx);
216
217 *output_data = encoder_output_buffer;
218
219 return err < 0 ? 0 : err;
220}
221
222struct MIMEToDecoderMapping
223{
224 const char *mime_type;
225 enum CodecID codec_id;
226};
227
228/* map MIME image types to an ffmpeg decoder */
229static const struct MIMEToDecoderMapping m2d_map[] = {
230 {"image/x-bmp", CODEC_ID_BMP},
231 {"image/gif", CODEC_ID_GIF},
232 {"image/jpeg", CODEC_ID_MJPEG},
233 {"image/png", CODEC_ID_PNG},
234 {"image/x-png", CODEC_ID_PNG},
235 {"image/x-portable-pixmap", CODEC_ID_PPM},
236 {NULL, CODEC_ID_NONE}
237};
238
239static char *mime_type;
240
241static int
242mime_processor (void *cls,
243 const char *plugin_name,
244 enum EXTRACTOR_MetaType type,
245 enum EXTRACTOR_MetaFormat format,
246 const char *data_mime_type,
247 const char *data,
248 size_t data_len)
249{
250 switch (format)
251 {
252 case EXTRACTOR_METAFORMAT_UTF8:
253 mime_type = strdup(data);
254 break;
255 default:
256 break;
257 }
258 return 0;
259}
260
261/* calculate the thumbnail dimensions, taking pixel aspect into account */
262static void calculate_thumbnail_dimensions(int src_width,
263 int src_height,
264 int src_sar_num,
265 int src_sar_den,
266 int *dst_width,
267 int *dst_height)
268{
269 if (src_sar_num <= 0 || src_sar_den <= 0)
270 {
271 src_sar_num = 1;
272 src_sar_den = 1;
273 }
274 if ((src_width * src_sar_num) / src_sar_den > src_height)
275 {
276 *dst_width = MAX_THUMB_DIMENSION;
277 *dst_height = (*dst_width * src_height) /
278 ((src_width * src_sar_num) / src_sar_den);
279 }
280 else
281 {
282 *dst_height = MAX_THUMB_DIMENSION;
283 *dst_width = (*dst_height *
284 ((src_width * src_sar_num) / src_sar_den)) /
285 src_height;
286 }
287 if (*dst_width < 8)
288 *dst_width = 8;
289 if (*dst_height < 1)
290 *dst_height = 1;
291#if DEBUG
292 fprintf (stderr,
293 "Thumbnail dimensions: %d %d\n",
294 *dst_width, *dst_height);
295#endif
296}
297
298static int
299extract_image (enum CodecID image_codec_id,
300 const unsigned char *data,
301 size_t size,
302 EXTRACTOR_MetaDataProcessor proc,
303 void *proc_cls,
304 const char *options)
305{
306 AVCodecContext *codec_ctx;
307 AVCodec *codec = NULL;
308 AVFrame *frame = NULL;
309 uint8_t *encoded_thumbnail;
310 int thumb_width;
311 int thumb_height;
312 int err;
313 int frame_finished;
314 int ret = 0;
315
316 codec_ctx = avcodec_alloc_context ();
317 if (codec_ctx == NULL)
318 {
319#if DEBUG
320 fprintf (stderr,
321 "Failed to allocate codec context\n");
322#endif
323 return 0;
324 }
325
326 codec = avcodec_find_decoder (image_codec_id);
327 if (codec != NULL)
328 {
329 if (avcodec_open (codec_ctx, codec) != 0)
330 {
331#if DEBUG
332 fprintf (stderr,
333 "Failed to open image codec\n");
334#endif
335 av_free (codec_ctx);
336 return 0;
337 }
338 }
339 else
340 {
341#if DEBUG
342 fprintf (stderr,
343 "No suitable codec found\n");
344#endif
345 av_free (codec_ctx);
346 return 0;
347 }
348
349 frame = avcodec_alloc_frame ();
350 if (frame == NULL)
351 {
352#if DEBUG
353 fprintf (stderr,
354 "Failed to allocate frame\n");
355#endif
356 avcodec_close (codec_ctx);
357 av_free (codec_ctx);
358 return 0;
359 }
360
361 avcodec_decode_video (codec_ctx, frame, &frame_finished, data, size);
362
363 if (!frame_finished)
364 {
365 fprintf (stderr,
366 "Failed to decode a complete frame\n");
367 av_free (frame);
368 avcodec_close (codec_ctx);
369 av_free (codec_ctx);
370 return 0;
371 }
372
373 calculate_thumbnail_dimensions (codec_ctx->width, codec_ctx->height,
374 codec_ctx->sample_aspect_ratio.num,
375 codec_ctx->sample_aspect_ratio.den,
376 &thumb_width, &thumb_height);
377
378 err = create_thumbnail (codec_ctx->width, codec_ctx->height,
379 frame->linesize, codec_ctx->pix_fmt,
380 (const uint8_t * const*) frame->data,
381 thumb_width, thumb_height,
382 &encoded_thumbnail, MAX_THUMB_BYTES);
383
384 if (err > 0)
385 {
386 ret = proc (proc_cls,
387 "thumbnailffmpeg",
388 EXTRACTOR_METATYPE_THUMBNAIL,
389 EXTRACTOR_METAFORMAT_BINARY,
390 "image/png",
391 (const char*) encoded_thumbnail,
392 err);
393 av_free (encoded_thumbnail);
394 }
395
396 av_free (frame);
397 avcodec_close (codec_ctx);
398 av_free (codec_ctx);
399 return ret;
400}
401
402static int
403extract_video (const unsigned char *data,
404 size_t size,
405 EXTRACTOR_MetaDataProcessor proc,
406 void *proc_cls,
407 const char *options)
408{
409 AVProbeData pd;
410 AVPacket packet;
411 AVInputFormat *input_format;
412 int input_format_nofileflag;
413 ByteIOContext *bio_ctx;
414 struct AVFormatContext *format_ctx;
415 AVCodecContext *codec_ctx;
416 AVCodec *codec = NULL;
417 AVFrame *frame = NULL;
418 uint8_t *encoded_thumbnail;
419 int video_stream_index = -1;
420 int thumb_width;
421 int thumb_height;
422 int i;
423 int err;
424 int frame_finished;
425 int ret = 0;
426
427#if DEBUG
428 fprintf (stderr,
429 "ffmpeg starting\n");
430#endif
431 /* probe format
432 * initial try with a smaller probe size for efficiency */
433 pd.filename = "";
434 pd.buf = (void *) data;
435 pd.buf_size = 128*1024 > size ? size : 128*1024;
436RETRY_PROBE:
437 if (NULL == (input_format = av_probe_input_format(&pd, 1)))
438 {
439#if DEBUG
440 fprintf (stderr,
441 "Failed to probe input format\n");
442#endif
443 if (pd.buf_size != size) /* retry probe once with full data size */
444 {
445 pd.buf_size = size;
446 goto RETRY_PROBE;
447 }
448 return 0;
449 }
450 input_format_nofileflag = input_format->flags & AVFMT_NOFILE;
451 input_format->flags |= AVFMT_NOFILE;
452 bio_ctx = NULL;
453 pd.buf_size = size;
454 url_open_buf(&bio_ctx, pd.buf, pd.buf_size, URL_RDONLY);
455 bio_ctx->is_streamed = 1;
456 if ((av_open_input_stream(&format_ctx, bio_ctx, pd.filename, input_format, NULL)) < 0)
457 {
458 #if DEBUG
459 fprintf (stderr,
460 "Failed to open input stream\n");
461#endif
462 url_close_buf (bio_ctx);
463 if (!input_format_nofileflag)
464 input_format->flags ^= AVFMT_NOFILE;
465 return 0;
466 }
467 if (0 > av_find_stream_info (format_ctx))
468 {
469 #if DEBUG
470 fprintf (stderr,
471 "Failed to read stream info\n");
472#endif
473 av_close_input_stream (format_ctx);
474 url_close_buf (bio_ctx);
475 if (!input_format_nofileflag)
476 input_format->flags ^= AVFMT_NOFILE;
477 return 0;
478 }
479
480 codec_ctx = NULL;
481 for (i=0; i<format_ctx->nb_streams; i++)
482 {
483 codec_ctx = format_ctx->streams[i]->codec;
484 if (codec_ctx->codec_type != CODEC_TYPE_VIDEO)
485 continue;
486 codec = avcodec_find_decoder (codec_ctx->codec_id);
487 if (codec == NULL)
488 continue;
489 err = avcodec_open (codec_ctx, codec);
490 if (err != 0)
491 {
492 codec = NULL;
493 continue;
494 }
495 video_stream_index = i;
496 break;
497 }
498
499 if ( (video_stream_index == -1) ||
500 (codec_ctx->width == 0) ||
501 (codec_ctx->height == 0) )
502 {
503#if DEBUG
504 fprintf (stderr,
505 "No video streams or no suitable codec found\n");
506#endif
507 if (codec != NULL)
508 avcodec_close (codec_ctx);
509 av_close_input_stream (format_ctx);
510 url_close_buf (bio_ctx);
511 if (!input_format_nofileflag)
512 input_format->flags ^= AVFMT_NOFILE;
513 return 0;
514 }
515
516 frame = avcodec_alloc_frame ();
517 if (frame == NULL)
518 {
519#if DEBUG
520 fprintf (stderr,
521 "Failed to allocate frame\n");
522#endif
523 avcodec_close (codec_ctx);
524 av_close_input_stream (format_ctx);
525 url_close_buf (bio_ctx);
526 if (!input_format_nofileflag)
527 input_format->flags ^= AVFMT_NOFILE;
528 return 0;
529 }
530#if DEBUG
531 if (format_ctx->duration == AV_NOPTS_VALUE)
532 fprintf (stderr,
533 "Duration unknown\n");
534 else
535 fprintf (stderr,
536 "Duration: %lld\n",
537 format_ctx->duration);
538#endif
539 /* TODO: if duration is known, seek to some better place,
540 * but use 10 sec into stream for now */
541 err = av_seek_frame (format_ctx, -1, 10 * AV_TIME_BASE, 0);
542 if (err >= 0)
543 avcodec_flush_buffers (codec_ctx);
544 frame_finished = 0;
545
546 while (1)
547 {
548 err = av_read_frame (format_ctx, &packet);
549 if (err < 0)
550 break;
551 if (packet.stream_index == video_stream_index)
552 {
553 avcodec_decode_video (codec_ctx,
554 frame,
555 &frame_finished,
556 packet.data, packet.size);
557 if (frame_finished && frame->key_frame)
558 {
559 av_free_packet (&packet);
560 break;
561 }
562 }
563 av_free_packet (&packet);
564 }
565 if (!frame_finished)
566 {
567 fprintf (stderr,
568 "Failed to decode a complete frame\n");
569 av_free (frame);
570 avcodec_close (codec_ctx);
571 av_close_input_stream (format_ctx);
572 url_close_buf (bio_ctx);
573 if (!input_format_nofileflag)
574 input_format->flags ^= AVFMT_NOFILE;
575 return 0;
576 }
577
578 calculate_thumbnail_dimensions (codec_ctx->width, codec_ctx->height,
579 codec_ctx->sample_aspect_ratio.num,
580 codec_ctx->sample_aspect_ratio.den,
581 &thumb_width, &thumb_height);
582
583 err = create_thumbnail (codec_ctx->width, codec_ctx->height,
584 frame->linesize, codec_ctx->pix_fmt,
585 (const uint8_t* const *) frame->data,
586 thumb_width, thumb_height,
587 &encoded_thumbnail, MAX_THUMB_BYTES);
588
589 if (err > 0)
590 {
591 ret = proc (proc_cls,
592 "thumbnailffmpeg",
593 EXTRACTOR_METATYPE_THUMBNAIL,
594 EXTRACTOR_METAFORMAT_BINARY,
595 "image/png",
596 (const char*) encoded_thumbnail,
597 err);
598 av_free (encoded_thumbnail);
599 }
600
601 av_free (frame);
602 avcodec_close (codec_ctx);
603 av_close_input_stream (format_ctx);
604 url_close_buf (bio_ctx);
605 if (!input_format_nofileflag)
606 input_format->flags ^= AVFMT_NOFILE;
607 return ret;
608}
609
610int
611EXTRACTOR_thumbnailffmpeg_extract (const unsigned char *data,
612 size_t size,
613 EXTRACTOR_MetaDataProcessor proc,
614 void *proc_cls,
615 const char *options)
616{
617 enum CodecID image_codec_id;
618 int is_image = 0;
619 int i;
620
621 mime_type = NULL;
622 EXTRACTOR_mime_extract((const char*) data, size, mime_processor, NULL, NULL);
623 if (mime_type != NULL)
624 {
625 i = 0;
626 while (m2d_map[i].mime_type != NULL)
627 {
628 if (!strcmp (m2d_map[i].mime_type, mime_type))
629 {
630 is_image = 1;
631 image_codec_id = m2d_map[i].codec_id;
632 break;
633 }
634 i++;
635 }
636 free(mime_type);
637 }
638
639 if (is_image)
640 return extract_image (image_codec_id, data, size, proc, proc_cls, options);
641 else
642 return extract_video (data, size, proc, proc_cls, options);
643}
644
645int
646EXTRACTOR_thumbnail_extract (const unsigned char *data,
647 size_t size,
648 EXTRACTOR_MetaDataProcessor proc,
649 void *proc_cls,
650 const char *options)
651{
652 return EXTRACTOR_thumbnailffmpeg_extract (data, size, proc, proc_cls, options);
653}
654
655/* end of thumbnailffmpeg_extractor.c */