00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028 #include "platform.h"
00029 #include "extractor.h"
00030
00031 #include <avformat.h>
00032 #include <avcodec.h>
00033 #include <swscale.h>
00034
00035 #define DEBUG 0
00036
00037 struct StreamDescriptor
00038 {
00039 const uint8_t *data;
00040 size_t offset;
00041 size_t size;
00042 };
00043
00044
00045 void __attribute__ ((constructor)) ffmpeg_lib_init (void)
00046 {
00047 #if DEBUG
00048 printf ("av_register_all()\n");
00049 #endif
00050 av_register_all ();
00051 }
00052
00053 static int
00054 stream_read (void *opaque, uint8_t * buf, int buf_size)
00055 {
00056 struct StreamDescriptor *rs = (struct StreamDescriptor *) opaque;
00057 size_t len;
00058 #if DEBUG
00059 printf ("read_packet: %zu\n", buf_size);
00060 #endif
00061 if (rs)
00062 {
00063 if (rs->data == NULL)
00064 return -1;
00065 if (rs->offset >= rs->size)
00066 return 0;
00067 len = buf_size;
00068 if (rs->offset + len > rs->size)
00069 len = rs->size - rs->offset;
00070
00071 memcpy (buf, rs->data + rs->offset, len);
00072 rs->offset += len;
00073 #if DEBUG
00074 printf ("read_packet: len: %zu\n", len);
00075 #endif
00076 return len;
00077 }
00078 return -1;
00079 }
00080
00081 static offset_t
00082 stream_seek (void *opaque, offset_t offset, int whence)
00083 {
00084 struct StreamDescriptor *rs = (struct StreamDescriptor *) opaque;
00085 offset_t off_abs;
00086 #if DEBUG
00087 printf ("my_seek: %lld %d\n", offset, whence);
00088 #endif
00089 if (rs)
00090 {
00091 if (whence == AVSEEK_SIZE)
00092 return (offset_t) rs->size;
00093 else if (whence == SEEK_CUR)
00094 off_abs = (offset_t) rs->offset + offset;
00095 else if (whence == SEEK_SET)
00096 off_abs = offset;
00097 else if (whence == SEEK_END)
00098 off_abs = (offset_t) rs->size + offset;
00099 else
00100 {
00101 printf ("whence error %d\n", whence);
00102 abort ();
00103 return AVERROR (EINVAL);
00104 }
00105 if (off_abs >= 0 && off_abs < (offset_t) rs->size)
00106 rs->offset = (size_t) off_abs;
00107 else
00108 off_abs = AVERROR (EINVAL);
00109 return off_abs;
00110 }
00111 return -1;
00112 }
00113
00114 static EXTRACTOR_KeywordList *
00115 addKeyword (EXTRACTOR_KeywordType type,
00116 char *keyword, EXTRACTOR_KeywordList * next)
00117 {
00118 EXTRACTOR_KeywordList *result;
00119
00120 if (keyword == NULL)
00121 return next;
00122 result = malloc (sizeof (EXTRACTOR_KeywordList));
00123 result->next = next;
00124 result->keyword = keyword;
00125 result->keywordType = type;
00126 return result;
00127 }
00128
00129
00130 struct MimeToDecoderMapping
00131 {
00132 const char *mime_type;
00133 enum CodecID codec_id;
00134 };
00135
00136
00137 static const struct MimeToDecoderMapping m2d_map[] = {
00138 {"image/x-bmp", CODEC_ID_BMP},
00139 {"image/gif", CODEC_ID_GIF},
00140 {"image/jpeg", CODEC_ID_MJPEG},
00141 {"image/png", CODEC_ID_PNG},
00142 {"image/x-portable-pixmap", CODEC_ID_PPM},
00143 {NULL, CODEC_ID_NONE}
00144 };
00145
00146 #define PROBE_MAX (1<<20)
00147 #define BIOBUF_SIZE (64*1024)
00148 #define THUMBSIZE 128
00149 #define MAX_THUMB_SIZE (100*1024)
00150
00151 struct EXTRACTOR_Keywords *
00152 libextractor_thumbnailffmpeg_extract (const char *filename,
00153 const unsigned char *data,
00154 size_t size,
00155 struct EXTRACTOR_Keywords *prev)
00156 {
00157 int score;
00158
00159 AVInputFormat *fmt;
00160 AVProbeData pdat;
00161
00162 ByteIOContext *bio_ctx = NULL;
00163 uint8_t *bio_buffer;
00164 struct StreamDescriptor reader_state;
00165
00166 AVFormatContext *format_ctx = NULL;
00167 AVCodecContext *codec_ctx = NULL;
00168 AVPacket packet;
00169 int video_stream_index;
00170 AVCodec *codec;
00171 AVFrame *frame = NULL;
00172 AVFrame *thumb_frame = NULL;
00173 int64_t ts;
00174
00175 struct SwsContext *scaler_ctx;
00176 int sws_flags = SWS_BILINEAR;
00177 uint8_t *thumb_buffer;
00178 int thumb_width, thumb_height;
00179 int sar_num, sar_den;
00180
00181 uint8_t *encoder_output_buffer;
00182 size_t encoder_output_buffer_size;
00183 AVCodecContext *enc_codec_ctx;
00184 AVCodec *enc_codec;
00185
00186 int i;
00187 int err;
00188 int frame_finished;
00189
00190 char *binary;
00191 const char *mime;
00192 int is_image;
00193 enum CodecID image_codec_id;
00194
00195 bio_ctx = NULL;
00196 bio_buffer = NULL;
00197 format_ctx = NULL;
00198 codec = NULL;
00199 frame = NULL;
00200 thumb_frame = NULL;
00201 thumb_buffer = NULL;
00202 scaler_ctx = NULL;
00203 encoder_output_buffer = NULL;
00204 enc_codec = NULL;
00205 enc_codec_ctx = NULL;
00206
00207 is_image = 0;
00208
00209 mime = EXTRACTOR_extractLast (EXTRACTOR_MIMETYPE, prev);
00210 if (mime != NULL)
00211 {
00212 i = 0;
00213 while (m2d_map[i].mime_type != NULL)
00214 {
00215 if (!strcmp (m2d_map[i].mime_type, mime))
00216 {
00217 is_image = 1;
00218 image_codec_id = m2d_map[i].codec_id;
00219 break;
00220 }
00221 i++;
00222 }
00223 }
00224
00225 #if DEBUG
00226 printf ("is_image: %d codec:%d\n", is_image, image_codec_id);
00227 #endif
00228 if (!is_image)
00229 {
00230 pdat.filename = filename;
00231 pdat.buf = (unsigned char *) data;
00232 pdat.buf_size = (size > PROBE_MAX) ? PROBE_MAX : size;
00233
00234 fmt = av_probe_input_format (&pdat, 1);
00235 if (fmt == NULL)
00236 return prev;
00237 #if DEBUG
00238 printf ("format %p [%s] [%s]\n", fmt, fmt->name, fmt->long_name);
00239 #endif
00240 pdat.buf = (unsigned char *) data;
00241 pdat.buf_size = size > PROBE_MAX ? PROBE_MAX : size;
00242 score = fmt->read_probe (&pdat);
00243 #if DEBUG
00244 printf ("score: %d\n", score);
00245 #endif
00246
00247 }
00248
00249 if (is_image)
00250 {
00251 codec_ctx = avcodec_alloc_context ();
00252 codec = avcodec_find_decoder (image_codec_id);
00253 if (codec != NULL)
00254 {
00255 if (avcodec_open (codec_ctx, codec) != 0)
00256 {
00257 #if DEBUG
00258 printf ("open codec failed\n");
00259 #endif
00260 codec = NULL;
00261 }
00262 }
00263 }
00264 else
00265 {
00266 bio_ctx = malloc (sizeof (ByteIOContext));
00267 bio_buffer = malloc (BIOBUF_SIZE);
00268
00269 reader_state.data = data;
00270 reader_state.offset = 0;
00271 reader_state.size = size;
00272
00273 init_put_byte (bio_ctx, bio_buffer,
00274 BIOBUF_SIZE, 0, &reader_state,
00275 stream_read, NULL, stream_seek);
00276
00277 fmt->flags |= AVFMT_NOFILE;
00278 err = av_open_input_stream (&format_ctx, bio_ctx, "", fmt, NULL);
00279 if (err < 0)
00280 {
00281 #if DEBUG
00282 printf ("couldn't open input stream\n");
00283 #endif
00284 goto out;
00285 }
00286
00287 err = av_find_stream_info (format_ctx);
00288 if (err < 0)
00289 {
00290 #if DEBUG
00291 printf ("couldn't find codec params\n");
00292 #endif
00293 goto out;
00294 }
00295
00296 for (i = 0; i < format_ctx->nb_streams; i++)
00297 {
00298 codec_ctx = format_ctx->streams[i]->codec;
00299 if (codec_ctx->codec_type == CODEC_TYPE_VIDEO)
00300 {
00301 video_stream_index = i;
00302 codec = avcodec_find_decoder (codec_ctx->codec_id);
00303 if (codec == NULL)
00304 {
00305 #if DEBUG
00306 printf ("find_decoder failed\n");
00307 #endif
00308 break;
00309 }
00310 err = avcodec_open (codec_ctx, codec);
00311 if (err != 0)
00312 {
00313 #if DEBUG
00314 printf ("failed to open codec\n");
00315 #endif
00316 codec = NULL;
00317 }
00318 break;
00319 }
00320 }
00321 }
00322
00323 if (codec_ctx == NULL || codec == NULL)
00324 {
00325 #if DEBUG
00326 printf ("failed to open codec");
00327 #endif
00328 goto out;
00329 }
00330 frame = avcodec_alloc_frame ();
00331 if (frame == NULL)
00332 {
00333 #if DEBUG
00334 printf ("failed to alloc frame");
00335 #endif
00336 goto out;
00337 }
00338
00339 if (!is_image)
00340 {
00341 #if DEBUG
00342 printf ("duration: %lld\n", format_ctx->duration);
00343 if (format_ctx->duration == AV_NOPTS_VALUE)
00344 printf ("duration unknown\n");
00345 #endif
00346
00347 ts = 10;
00348 ts = ts * AV_TIME_BASE;
00349 err = av_seek_frame (format_ctx, -1, ts, 0);
00350 if (err >= 0)
00351 {
00352 avcodec_flush_buffers (codec_ctx);
00353 }
00354 #if DEBUG
00355 else
00356 printf ("seeking failed %d\n", err);
00357 #endif
00358 }
00359
00360 frame_finished = 0;
00361 if (is_image)
00362 {
00363 avcodec_decode_video (codec_ctx, frame, &frame_finished, data, size);
00364 }
00365 else
00366 {
00367 while (1)
00368 {
00369 err = av_read_frame (format_ctx, &packet);
00370 if (err < 0)
00371 break;
00372 if (packet.stream_index == video_stream_index)
00373 {
00374 avcodec_decode_video (codec_ctx,
00375 frame,
00376 &frame_finished,
00377 packet.data, packet.size);
00378 if (frame_finished && frame->key_frame)
00379 {
00380 av_free_packet (&packet);
00381 break;
00382 }
00383 }
00384 av_free_packet (&packet);
00385 }
00386 }
00387
00388 if (!frame_finished || codec_ctx->width == 0 || codec_ctx->height == 0)
00389 goto out;
00390
00391 sar_num = codec_ctx->sample_aspect_ratio.num;
00392 sar_den = codec_ctx->sample_aspect_ratio.den;
00393 if (sar_num <= 0 || sar_den <= 0)
00394 {
00395 sar_num = 1;
00396 sar_den = 1;
00397 }
00398 if ((codec_ctx->width * sar_num) / sar_den > codec_ctx->height)
00399 {
00400 thumb_width = THUMBSIZE;
00401 thumb_height = (thumb_width * codec_ctx->height) /
00402 ((codec_ctx->width * sar_num) / sar_den);
00403 }
00404 else
00405 {
00406 thumb_height = THUMBSIZE;
00407 thumb_width = (thumb_height *
00408 ((codec_ctx->width * sar_num) / sar_den)) /
00409 codec_ctx->height;
00410 }
00411 if (thumb_width < 8)
00412 thumb_width = 8;
00413 if (thumb_height < 1)
00414 thumb_height = 1;
00415 #if DEBUG
00416 printf ("thumb dim: %d %d\n", thumb_width, thumb_height);
00417 #endif
00418
00419 scaler_ctx =
00420 sws_getContext (codec_ctx->width, codec_ctx->height, codec_ctx->pix_fmt,
00421 thumb_width, thumb_height, PIX_FMT_RGB24, sws_flags, NULL,
00422 NULL, NULL);
00423 if (scaler_ctx == NULL)
00424 {
00425 #if DEBUG
00426 printf ("failed to alloc scaler\n");
00427 #endif
00428 goto out;
00429 }
00430 thumb_frame = avcodec_alloc_frame ();
00431 thumb_buffer =
00432 av_malloc (avpicture_get_size (PIX_FMT_RGB24, thumb_width, thumb_height));
00433 if (thumb_frame == NULL || thumb_buffer == NULL)
00434 {
00435 #if DEBUG
00436 printf ("failed to alloc thumb frame\n");
00437 #endif
00438 goto out;
00439 }
00440 avpicture_fill ((AVPicture *) thumb_frame, thumb_buffer,
00441 PIX_FMT_RGB24, thumb_width, thumb_height);
00442
00443 sws_scale (scaler_ctx,
00444 frame->data, frame->linesize,
00445 0, codec_ctx->height, thumb_frame->data, thumb_frame->linesize);
00446
00447 encoder_output_buffer_size = MAX_THUMB_SIZE;
00448 encoder_output_buffer = av_malloc (encoder_output_buffer_size);
00449 if (encoder_output_buffer == NULL)
00450 {
00451 #if DEBUG
00452 printf ("couldn't alloc encoder output buf\n");
00453 #endif
00454 goto out;
00455 }
00456
00457 enc_codec = avcodec_find_encoder_by_name ("png");
00458 if (enc_codec == NULL)
00459 {
00460 #if DEBUG
00461 printf ("couldn't find encoder\n");
00462 #endif
00463 goto out;
00464 }
00465 enc_codec_ctx = avcodec_alloc_context ();
00466 enc_codec_ctx->width = thumb_width;
00467 enc_codec_ctx->height = thumb_height;
00468 enc_codec_ctx->pix_fmt = PIX_FMT_RGB24;
00469
00470 if (avcodec_open (enc_codec_ctx, enc_codec) < 0)
00471 {
00472 #if DEBUG
00473 printf ("couldn't open encoder\n");
00474 #endif
00475 enc_codec = NULL;
00476 goto out;
00477 }
00478
00479 err = avcodec_encode_video (enc_codec_ctx,
00480 encoder_output_buffer,
00481 encoder_output_buffer_size, thumb_frame);
00482 if (err <= 0)
00483 goto out;
00484
00485 binary =
00486 EXTRACTOR_binaryEncode ((const unsigned char *) encoder_output_buffer,
00487 err);
00488 if (binary != NULL)
00489 prev = addKeyword (EXTRACTOR_THUMBNAIL_DATA, binary, prev);
00490
00491 out:
00492 if (enc_codec != NULL)
00493 avcodec_close (enc_codec_ctx);
00494 if (enc_codec_ctx != NULL)
00495 av_free (enc_codec_ctx);
00496 if (encoder_output_buffer != NULL)
00497 av_free (encoder_output_buffer);
00498 if (scaler_ctx != NULL)
00499 sws_freeContext(scaler_ctx);
00500 if (codec != NULL)
00501 avcodec_close (codec_ctx);
00502 if (format_ctx != NULL)
00503 av_close_input_file (format_ctx);
00504 if (frame != NULL)
00505 av_free (frame);
00506 if (thumb_buffer != NULL)
00507 av_free (thumb_buffer);
00508 if (thumb_frame != NULL)
00509 av_free (thumb_frame);
00510 if (bio_ctx != NULL)
00511 free (bio_ctx);
00512 if (bio_buffer != NULL)
00513 free (bio_buffer);
00514
00515 return prev;
00516 }
00517
00518 struct EXTRACTOR_Keywords *
00519 libextractor_thumbnail_extract (const char *filename,
00520 const unsigned char *data,
00521 size_t size,
00522 struct EXTRACTOR_Keywords *prev,
00523 const char *options)
00524 {
00525 return libextractor_thumbnailffmpeg_extract (filename, data, size, prev);
00526 }
00527
00528