diff options
Diffstat (limited to 'src/plugins/old/thumbnailffmpeg_extractor.c')
-rw-r--r-- | src/plugins/old/thumbnailffmpeg_extractor.c | 655 |
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 | |||
66 | static 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 | |||
76 | void __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 | */ | ||
89 | static 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 | |||
222 | struct MIMEToDecoderMapping | ||
223 | { | ||
224 | const char *mime_type; | ||
225 | enum CodecID codec_id; | ||
226 | }; | ||
227 | |||
228 | /* map MIME image types to an ffmpeg decoder */ | ||
229 | static 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 | |||
239 | static char *mime_type; | ||
240 | |||
241 | static int | ||
242 | mime_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 */ | ||
262 | static 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 | |||
298 | static int | ||
299 | extract_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 | |||
402 | static int | ||
403 | extract_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; | ||
436 | RETRY_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 | |||
610 | int | ||
611 | EXTRACTOR_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 | |||
645 | int | ||
646 | EXTRACTOR_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 */ | ||