libextractor

GNU libextractor
Log | Files | Refs | Submodules | README | LICENSE

commit d546872fc3ba31b29047b01ae6b34da9d47b77d7
parent a0c515b5b52eab49f357b1ebff2468c7e998e722
Author: Heikki Lindholm <holin@iki.fi>
Date:   Thu, 24 Dec 2009 15:26:29 +0000

clean up and remove (fix) oop-only and force-kill requirements


Diffstat:
Msrc/plugins/thumbnailffmpeg_extractor.c | 408+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
1 file changed, 235 insertions(+), 173 deletions(-)

diff --git a/src/plugins/thumbnailffmpeg_extractor.c b/src/plugins/thumbnailffmpeg_extractor.c @@ -46,6 +46,7 @@ void __attribute__ ((constructor)) ffmpeg_lib_init (void) { + printf("av_register_all\n"); av_register_all (); } @@ -56,15 +57,151 @@ void __attribute__ ((constructor)) ffmpeg_lib_init (void) const char * EXTRACTOR_thumbnailffmpeg_options () { - return "force-kill;oop-only;close-stderr"; + return "close-stderr"; } const char * EXTRACTOR_thumbnail_options () { - return "force-kill;oop-only;close-stderr"; + return "close-stderr"; } +/* + * Rescale and encode a png thumbnail + * on success, fills in output_data and returns the number of bytes used + */ +static size_t create_thumbnail( + int src_width, int src_height, int src_stride[], + enum PixelFormat src_pixfmt, uint8_t *src_data[], + int dst_width, int dst_height, + uint8_t **output_data, size_t output_max_size) +{ + AVCodecContext *encoder_codec_ctx = NULL; + AVCodec *encoder_codec = NULL; + struct SwsContext *scaler_ctx = NULL; + int sws_flags = SWS_BILINEAR; + AVFrame *dst_frame = NULL; + uint8_t *dst_buffer = NULL; + uint8_t *encoder_output_buffer = NULL; + size_t encoder_output_buffer_size; + int err; + + encoder_codec = avcodec_find_encoder_by_name ("png"); + if (encoder_codec == NULL) + { +#if DEBUG + fprintf (stderr, + "Couldn't find a PNG encoder\n"); +#endif + return 0; + } + + /* NOTE: the scaler will be used even if the src and dst image dimensions + * match, because the scaler will also perform colour space conversion */ + scaler_ctx = + sws_getContext (src_width, src_height, src_pixfmt, + dst_width, dst_height, PIX_FMT_RGB24, + sws_flags, NULL, NULL, NULL); + if (scaler_ctx == NULL) + { +#if DEBUG + fprintf (stderr, + "Failed to get a scaler context\n"); +#endif + return 0; + } + + dst_frame = avcodec_alloc_frame (); + if (dst_frame == NULL) + { +#if DEBUG + fprintf (stderr, + "Failed to allocate the destination image frame\n"); +#endif + sws_freeContext(scaler_ctx); + return 0; + } + dst_buffer = + av_malloc (avpicture_get_size (PIX_FMT_RGB24, dst_width, dst_height)); + if (dst_buffer == NULL) + { +#if DEBUG + fprintf (stderr, + "Failed to allocate the destination image buffer\n"); +#endif + av_free (dst_frame); + sws_freeContext(scaler_ctx); + return 0; + } + avpicture_fill ((AVPicture *) dst_frame, dst_buffer, + PIX_FMT_RGB24, dst_width, dst_height); + + sws_scale (scaler_ctx, + src_data, + src_stride, + 0, src_height, + dst_frame->data, + dst_frame->linesize); + + encoder_output_buffer_size = output_max_size; + encoder_output_buffer = av_malloc (encoder_output_buffer_size); + if (encoder_output_buffer == NULL) + { +#if DEBUG + fprintf (stderr, + "Failed to allocate the encoder output buffer\n"); +#endif + av_free (dst_buffer); + av_free (dst_frame); + sws_freeContext(scaler_ctx); + return 0; + } + + encoder_codec_ctx = avcodec_alloc_context (); + if (encoder_codec_ctx == NULL) + { +#if DEBUG + fprintf (stderr, + "Failed to allocate the encoder codec context\n"); +#endif + av_free (encoder_output_buffer); + av_free (dst_buffer); + av_free (dst_frame); + sws_freeContext(scaler_ctx); + return 0; + } + encoder_codec_ctx->width = dst_width; + encoder_codec_ctx->height = dst_height; + encoder_codec_ctx->pix_fmt = PIX_FMT_RGB24; + + if (avcodec_open (encoder_codec_ctx, encoder_codec) < 0) + { +#if DEBUG + fprintf (stderr, + "Failed to open the encoder\n"); +#endif + av_free (encoder_codec_ctx); + av_free (encoder_output_buffer); + av_free (dst_buffer); + av_free (dst_frame); + sws_freeContext(scaler_ctx); + return 0; + } + + err = avcodec_encode_video (encoder_codec_ctx, + encoder_output_buffer, + encoder_output_buffer_size, dst_frame); + + avcodec_close (encoder_codec_ctx); + av_free (encoder_codec_ctx); + av_free (dst_buffer); + av_free (dst_frame); + sws_freeContext(scaler_ctx); + + *output_data = encoder_output_buffer; + + return err < 0 ? 0 : err; +} int EXTRACTOR_thumbnailffmpeg_extract (const unsigned char *data, @@ -75,22 +212,14 @@ EXTRACTOR_thumbnailffmpeg_extract (const unsigned char *data, { AVProbeData pd; AVPacket packet; - AVInputFormat *m_pInputFormat; - ByteIOContext *bio; - struct AVFormatContext *fmt; + AVInputFormat *input_format; + int input_format_nofileflag; + ByteIOContext *bio_ctx; + struct AVFormatContext *format_ctx; AVCodecContext *codec_ctx; AVCodec *codec = NULL; AVFrame *frame = NULL; - AVFrame *thumb_frame = NULL; - uint8_t *encoder_output_buffer = NULL; - AVCodecContext *enc_codec_ctx = NULL; - AVCodec *enc_codec = NULL; - const AVFrame *tframe; - struct SwsContext *scaler_ctx = NULL; - uint8_t *thumb_buffer = NULL; - int sws_flags = SWS_BILINEAR; - int64_t ts; - size_t encoder_output_buffer_size; + uint8_t *encoded_thumbnail; int video_stream_index; int thumb_width; int thumb_height; @@ -105,42 +234,59 @@ EXTRACTOR_thumbnailffmpeg_extract (const unsigned char *data, fprintf (stderr, "ffmpeg starting\n"); #endif - pd.buf_size = size; + /* probe format + * initial try with a smaller probe size for efficiency */ + pd.filename = ""; pd.buf = (void *) data; - pd.filename = "__url_prot"; - - if (NULL == (m_pInputFormat = av_probe_input_format(&pd, 1))) + pd.buf_size = 128*1024 > size ? size : 128*1024; +RETRY_PROBE: + if (NULL == (input_format = av_probe_input_format(&pd, 1))) { #if DEBUG fprintf (stderr, "Failed to probe input format\n"); #endif + if (pd.buf_size != size) /* retry probe once with full data size */ + { + pd.buf_size = size; + goto RETRY_PROBE; + } return 0; } - m_pInputFormat->flags |= AVFMT_NOFILE; - bio = NULL; - url_open_buf(&bio, pd.buf, pd.buf_size, URL_RDONLY); - bio->is_streamed = 1; - if ((av_open_input_stream(&fmt, bio, pd.filename, m_pInputFormat, NULL)) < 0) + input_format_nofileflag = input_format->flags & AVFMT_NOFILE; + input_format->flags |= AVFMT_NOFILE; + bio_ctx = NULL; + pd.buf_size = size; + url_open_buf(&bio_ctx, pd.buf, pd.buf_size, URL_RDONLY); + bio_ctx->is_streamed = 1; + if ((av_open_input_stream(&format_ctx, bio_ctx, pd.filename, input_format, NULL)) < 0) { - url_close_buf (bio); + #if DEBUG fprintf (stderr, "Failed to open input stream\n"); +#endif + url_close_buf (bio_ctx); + if (!input_format_nofileflag) + input_format->flags ^= AVFMT_NOFILE; return 0; } - if (0 > av_find_stream_info (fmt)) + if (0 > av_find_stream_info (format_ctx)) { - av_close_input_stream (fmt); - url_close_buf (bio); + #if DEBUG fprintf (stderr, - "Failed to read stream info\n"); + "Failed to read stream info\n"); +#endif + av_close_input_stream (format_ctx); + url_close_buf (bio_ctx); + if (!input_format_nofileflag) + input_format->flags ^= AVFMT_NOFILE; return 0; } codec_ctx = NULL; - for (i=0; i<fmt->nb_streams; i++) + for (i=0; i<format_ctx->nb_streams; i++) { - codec_ctx = fmt->streams[i]->codec; + codec_ctx = format_ctx->streams[i]->codec; if (codec_ctx->codec_type != CODEC_TYPE_VIDEO) continue; codec = avcodec_find_decoder (codec_ctx->codec_id); @@ -160,39 +306,46 @@ EXTRACTOR_thumbnailffmpeg_extract (const unsigned char *data, (codec_ctx->width == 0) || (codec_ctx->height == 0) ) { +#if DEBUG + fprintf (stderr, + "No video streams or no suitable codec found\n"); +#endif if (codec_ctx != NULL) avcodec_close (codec_ctx); - av_close_input_stream (fmt); - url_close_buf (bio); - fprintf (stderr, - "No video codec found\n"); + av_close_input_stream (format_ctx); + url_close_buf (bio_ctx); + if (!input_format_nofileflag) + input_format->flags ^= AVFMT_NOFILE; return 0; } frame = avcodec_alloc_frame (); if (frame == NULL) { +#if DEBUG + fprintf (stderr, + "Failed to allocate frame\n"); +#endif if (codec != NULL) avcodec_close (codec_ctx); - av_close_input_stream (fmt); - url_close_buf (bio); - fprintf (stderr, - "Failed to allocate frame\n"); + av_close_input_stream (format_ctx); + url_close_buf (bio_ctx); + if (!input_format_nofileflag) + input_format->flags ^= AVFMT_NOFILE; return 0; } #if DEBUG - if (fmt->duration == AV_NOPTS_VALUE) + if (format_ctx->duration == AV_NOPTS_VALUE) fprintf (stderr, - "duration unknown\n"); + "Duration unknown\n"); else fprintf (stderr, - "duration: %lld\n", - fmt->duration); + "Duration: %lld\n", + format_ctx->duration); #endif - /* TODO: if duration is known seek to to some better place(?) */ - ts = 10; /* s */ - ts = ts * AV_TIME_BASE; - err = av_seek_frame (fmt, -1, ts, 0); + /* TODO: if duration is known, seek to some better place, + * but use 10 sec into stream for now */ + err = av_seek_frame (format_ctx, -1, 10 * AV_TIME_BASE, 0); if (err >= 0) avcodec_flush_buffers (codec_ctx); frame_finished = 0; @@ -205,7 +358,7 @@ EXTRACTOR_thumbnailffmpeg_extract (const unsigned char *data, { while (1) { - err = av_read_frame (fmt, &packet); + err = av_read_frame (format_ctx, &packet); if (err < 0) break; if (packet.stream_index == video_stream_index) @@ -225,15 +378,18 @@ EXTRACTOR_thumbnailffmpeg_extract (const unsigned char *data, } if (!frame_finished) { + fprintf (stderr, + "Failed to decode a complete frame\n"); if (frame != NULL) av_free (frame); - av_close_input_stream (fmt); - free (bio); - fprintf (stderr, - "Failed to seek to frame\n"); + av_close_input_stream (format_ctx); + free (bio_ctx); + if (!input_format_nofileflag) + input_format->flags ^= AVFMT_NOFILE; return 0; } + /* calculate the thumbnail dimensions, taking pixel aspect into account */ sar_num = codec_ctx->sample_aspect_ratio.num; sar_den = codec_ctx->sample_aspect_ratio.den; if (sar_num <= 0 || sar_den <= 0) @@ -241,143 +397,49 @@ EXTRACTOR_thumbnailffmpeg_extract (const unsigned char *data, sar_num = 1; sar_den = 1; } - if ( (codec_ctx->width < THUMBSIZE) && - (codec_ctx->height < THUMBSIZE) ) + if ((codec_ctx->width * sar_num) / sar_den > codec_ctx->height) { - /* no resize */ - thumb_width = codec_ctx->width; - thumb_height = codec_ctx->height; - tframe = frame; + thumb_width = THUMBSIZE; + thumb_height = (thumb_width * codec_ctx->height) / + ((codec_ctx->width * sar_num) / sar_den); } else { - /* need resize */ - if ((codec_ctx->width * sar_num) / sar_den > codec_ctx->height) - { - thumb_width = THUMBSIZE; - thumb_height = (thumb_width * codec_ctx->height) / - ((codec_ctx->width * sar_num) / sar_den); - } - else - { - thumb_height = THUMBSIZE; - thumb_width = (thumb_height * - ((codec_ctx->width * sar_num) / sar_den)) / - codec_ctx->height; - } - if (thumb_width < 8) - thumb_width = 8; - if (thumb_height < 1) - thumb_height = 1; -#if DEBUG - fprintf (stderr, - "thumb dim: %d %d\n", - thumb_width, - thumb_height); -#endif - - scaler_ctx = - sws_getContext (codec_ctx->width, codec_ctx->height, codec_ctx->pix_fmt, - thumb_width, thumb_height, PIX_FMT_RGB24, sws_flags, NULL, - NULL, NULL); - if (scaler_ctx == NULL) - { -#if DEBUG - fprintf (stderr, - "failed to alloc scaler\n"); -#endif - goto out; - } - thumb_frame = avcodec_alloc_frame (); - thumb_buffer = - av_malloc (avpicture_get_size (PIX_FMT_RGB24, thumb_width, thumb_height)); - if (thumb_frame == NULL || thumb_buffer == NULL) - { -#if DEBUG - fprintf (stderr, - "failed to alloc thumb frame\n"); -#endif - goto out; - } - avpicture_fill ((AVPicture *) thumb_frame, thumb_buffer, - PIX_FMT_RGB24, thumb_width, thumb_height); - - sws_scale (scaler_ctx, - frame->data, - frame->linesize, - 0, codec_ctx->height, - thumb_frame->data, - thumb_frame->linesize); - tframe = thumb_frame; - } - - encoder_output_buffer_size = MAX_THUMB_SIZE; - encoder_output_buffer = av_malloc (encoder_output_buffer_size); - if (encoder_output_buffer == NULL) - { -#if DEBUG - fprintf (stderr, - "couldn't alloc encoder output buf\n"); -#endif - goto out; + thumb_height = THUMBSIZE; + thumb_width = (thumb_height * + ((codec_ctx->width * sar_num) / sar_den)) / + codec_ctx->height; } - - enc_codec = avcodec_find_encoder_by_name ("png"); - if (enc_codec == NULL) - { + if (thumb_width < 8) + thumb_width = 8; + if (thumb_height < 1) + thumb_height = 1; #if DEBUG - fprintf (stderr, - "couldn't find encoder\n"); + fprintf (stderr, + "Thumbnail dimensions: %d %d\n", + thumb_width, thumb_height); #endif - goto out; - } - enc_codec_ctx = avcodec_alloc_context (); - enc_codec_ctx->width = thumb_width; - enc_codec_ctx->height = thumb_height; - enc_codec_ctx->pix_fmt = PIX_FMT_RGB24; - if (avcodec_open (enc_codec_ctx, enc_codec) < 0) - { -#if DEBUG - fprintf (stderr, - "couldn't open encoder\n"); -#endif - enc_codec = NULL; - goto out; - } + err = create_thumbnail (codec_ctx->width, codec_ctx->height, + frame->linesize, codec_ctx->pix_fmt, frame->data, + thumb_width, thumb_height, + &encoded_thumbnail, MAX_THUMB_SIZE); - err = avcodec_encode_video (enc_codec_ctx, - encoder_output_buffer, - encoder_output_buffer_size, tframe); if (err > 0) ret = proc (proc_cls, "thumbnailffmpeg", EXTRACTOR_METATYPE_THUMBNAIL, EXTRACTOR_METAFORMAT_BINARY, "image/png", - (const char*) encoder_output_buffer, + (const char*) encoded_thumbnail, err); -out: - if (enc_codec != NULL) - avcodec_close (enc_codec_ctx); - if (enc_codec_ctx != NULL) - av_free (enc_codec_ctx); - if (encoder_output_buffer != NULL) - av_free (encoder_output_buffer); - if (scaler_ctx != NULL) - sws_freeContext(scaler_ctx); - if (codec_ctx != NULL) - avcodec_close (codec_ctx); - if (fmt != NULL) - av_close_input_stream (fmt); - if (frame != NULL) - av_free (frame); - if (thumb_buffer != NULL) - av_free (thumb_buffer); - if (thumb_frame != NULL) - av_free (thumb_frame); - if (bio != NULL) - url_close_buf (bio); + + avcodec_close (codec_ctx); + av_close_input_stream (format_ctx); + if (!input_format_nofileflag) + input_format->flags ^= AVFMT_NOFILE; + av_free (frame); + url_close_buf (bio_ctx); return ret; }