[FFmpeg-devel] [PATCH] doc/examples: add a simple decoding example.

Stefano Sabatini stefasab at gmail.com
Fri Sep 6 14:36:09 CEST 2013


On date Friday 2013-09-06 10:29:01 +0200, Clément Bœsch encoded:
> On Wed, Sep 04, 2013 at 05:55:35PM +0200, Clément Bœsch wrote:
> > From: Clément Bœsch <clement at stupeflix.com>
> > 
> > ---
> >  .gitignore              |   1 +
> >  doc/examples/Makefile   |   3 +-
> >  doc/examples/decoding.c | 153 ++++++++++++++++++++++++++++++++++++++++++++++++
> >  3 files changed, 156 insertions(+), 1 deletion(-)
> >  create mode 100644 doc/examples/decoding.c
> > 
> 
> Here is the new version, showing how to use both reference counting and
> classic method. I'd like to push this soon unless someone disagrees.
> 
> This is tested with a MPEG sample with B-frames, and Valgrind is happy in
> both modes about memory leaks and corruptions.
> 
> I also added various additional comments, you're welcome to look for typo
> and other frenglish derping in them.
> 
> Thanks.
> 
> -- 
> Clément B.

> From 8a0c389ae71ff083106535c1b5a1661c94066999 Mon Sep 17 00:00:00 2001
> From: =?UTF-8?q?Cl=C3=A9ment=20B=C5=93sch?= <clement at stupeflix.com>
> Date: Wed, 4 Sep 2013 17:27:38 +0200
> Subject: [PATCH] doc/examples: add a simple decoding example.
> 
> ---
>  .gitignore                    |   1 +
>  doc/examples/Makefile         |   1 +
>  doc/examples/decoding_video.c | 190 ++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 192 insertions(+)
>  create mode 100644 doc/examples/decoding_video.c
> 
> diff --git a/.gitignore b/.gitignore
> index 1f13ec4..edcb671 100644
> --- a/.gitignore
> +++ b/.gitignore
> @@ -36,6 +36,7 @@
>  /doc/avoptions_codec.texi
>  /doc/avoptions_format.texi
>  /doc/examples/decoding_encoding
> +/doc/examples/decoding_video
>  /doc/examples/demuxing
>  /doc/examples/filtering_audio
>  /doc/examples/filtering_video
> diff --git a/doc/examples/Makefile b/doc/examples/Makefile
> index 3d698cc..a19d321 100644
> --- a/doc/examples/Makefile
> +++ b/doc/examples/Makefile
> @@ -12,6 +12,7 @@ CFLAGS := $(shell pkg-config --cflags $(FFMPEG_LIBS)) $(CFLAGS)
>  LDLIBS := $(shell pkg-config --libs $(FFMPEG_LIBS)) $(LDLIBS)
>  
>  EXAMPLES=       decoding_encoding                  \
> +                decoding_video                     \
>                  demuxing                           \
>                  filtering_video                    \
>                  filtering_audio                    \
> diff --git a/doc/examples/decoding_video.c b/doc/examples/decoding_video.c
> new file mode 100644
> index 0000000..f9fa652
> --- /dev/null
> +++ b/doc/examples/decoding_video.c
> @@ -0,0 +1,190 @@
> +/*
> + * Copyright (c) 2010 Nicolas George

> + * Copyright (c) 2011 Stefano Sabatini

Feel free to discard my copyright.

> + * Copyright (c) 2013 Clément Bœsch
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a copy
> + * of this software and associated documentation files (the "Software"), to deal
> + * in the Software without restriction, including without limitation the rights
> + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> + * copies of the Software, and to permit persons to whom the Software is
> + * furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included in
> + * all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
> + * THE SOFTWARE.
> + */
> +
> +/**
> + * @file

IIRC you need to add an @example line or doxygen will forget about it.

> + * Video decoding example.
> + *
> + * This example demonstrates how to demux and decode the video data from a file
> + * using either the classical method, or the frame reference counting API.
> + */
> +
> +#include <libavcodec/avcodec.h>
> +#include <libavformat/avformat.h>
> +

> +static AVFormatContext *fmt_ctx;
> +static AVCodecContext *dec_ctx;
> +static int video_stream_index = -1;
> +static int use_ref_counting = 0;

Note: ideally we should get rid of static variables, but this remark
is obviously not blocking

> +
> +static int open_input_file(const char *filename)
> +{
> +    int ret;
> +    AVCodec *dec;
> +    AVDictionary *opts = NULL;
> +
> +    /* Open the specified input */
> +    if ((ret = avformat_open_input(&fmt_ctx, filename, NULL, NULL)) < 0) {
> +        av_log(NULL, AV_LOG_ERROR, "Cannot open input file\n");
> +        return ret;
> +    }
> +
> +    /* Extract a maximum of stream information from the opened media */
> +    if ((ret = avformat_find_stream_info(fmt_ctx, NULL)) < 0) {
> +        av_log(NULL, AV_LOG_ERROR, "Cannot find stream information\n");
> +        return ret;
> +    }
> +
> +    /* Select the video stream */
> +    ret = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, &dec, 0);
> +    if (ret < 0) {
> +        av_log(NULL, AV_LOG_ERROR, "Cannot find a video stream in the input file\n");
> +        return ret;
> +    }
> +    video_stream_index = ret;
> +    dec_ctx = fmt_ctx->streams[video_stream_index]->codec;
> +

> +    /* Init the video decoder, with or without reference counting */
> +    if (use_ref_counting)
> +        av_dict_set(&opts, "refcounted_frames", "1", 0);
> +    if ((ret = avcodec_open2(dec_ctx, dec, &opts)) < 0) {
> +        av_log(NULL, AV_LOG_ERROR, "Cannot open video decoder\n");
> +        return ret;
> +    }
> +
> +    return 0;
> +}
> +
> +int main(int argc, char **argv)
> +{
> +    int ret;
> +    AVPacket packet;
> +    AVFrame *frame;
> +    int got_frame;
> +    const char *filename;
> +
> +    /* This tool demonstrates how to do decoding using ref counting or not
> +     * using the command line. In practice, you choose one way or another; ref
> +     * counting notably allowing you to keep the data as long as you wish. */
> +    if (argc < 2) {
> +        av_log(NULL, AV_LOG_ERROR, "Usage: %s [-refcount] <file>\n", argv[0]);
> +        return 1;
> +    }

Please print an extended usage notice as in the other examples. Also
we could allow to specify all the options using the option parsing API.

> +    if (argc == 3) {
> +        filename = argv[2];
> +        use_ref_counting = strcmp(filename, "refcount");
> +    } else {
> +        filename = argv[1];
> +    }
> +    av_log(NULL, AV_LOG_INFO,
> +           "[decoding video example] use reference counting API: %s\n",
> +            use_ref_counting ? "YES" : "NO");
> +

> +    /* When using the ref counting system, you need to use the
> +     * libavutil/frame.h API, while the classic frame management is available
> +     * in libavcodec */
> +    if (use_ref_counting)
> +        frame = av_frame_alloc();
> +    else
> +        frame = avcodec_alloc_frame();

Now the real question, is why the user should care about "classical"
and "non-classical" API? Are we supposed to swith to the frame.h API
or keep both of them at the same time (and there is any advantage into
using one or the other)? I'm sorry for the many questions.

> +
> +    if (!frame) {

> +        perror("Could not allocate frame");

You didn't set errno, so perror will print a random error code at the
end of the message. I suggest to jump to the end label and print the
error string with av_str2err().

> +        return 1;
> +    }
> +
> +    av_register_all();
> +
> +    if ((ret = open_input_file(filename)) < 0)
> +        goto end;
> +
> +    /* Demux the file */
> +    while ((ret = av_read_frame(fmt_ctx, &packet)) >= 0) {
> +
> +        /* Pick only the packets from the video stream */
> +        if (packet.stream_index == video_stream_index) {
> +

> +            /* Reset the properties of our frame */
> +            avcodec_get_frame_defaults(frame);

is this actually required?

> +            /* Attempt to decode the frame from the AVPacket */
> +            got_frame = 0;
> +            ret = avcodec_decode_video2(dec_ctx, frame, &got_frame, &packet);
> +            if (ret < 0) {
> +                av_log(NULL, AV_LOG_ERROR, "Error decoding video\n");
> +                av_free_packet(&packet);
> +                break;
> +            }
> +
> +            /* Make sure it is a complete frame before doing something with it */
> +            if (got_frame) {
> +
> +                /* Grab the estimation of the timestamp for the frame; this is
> +                 * the simplest way to get a frame timing if you don't wish to
> +                 * deal with the various PTS and DTS subtleties yourself */
> +                int64_t pts = av_frame_get_best_effort_timestamp(frame);
> +
> +                /* Convert it into seconds using the stream time base */
> +                double dpts = pts * av_q2d(dec_ctx->time_base);
> +
> +                /* Do whatever you want with the content, here we display
> +                 * miscellaneous information about the frame */
> +                av_log(NULL, AV_LOG_INFO, "[%.3f] [%c] [%dx%d] [%p %p %p %p] [%d %d %d %d]\n", dpts,
> +                       av_get_picture_type_char(frame->pict_type), frame->width, frame->height,
> +                       frame->data[0],     frame->data[1],     frame->data[2],     frame->data[3],
> +                       frame->linesize[0], frame->linesize[1], frame->linesize[2], frame->linesize[3]);
> +            }
> +
> +            /* If we use the reference counter API, we own the data and need to
> +             * de-reference it when we don't use it anymore */
> +            if (use_ref_counting)
> +                av_frame_unref(frame);
> +        }
> +
> +        /* Release the data from the packet before re-using it */
> +        av_free_packet(&packet);
> +    }
> +
> +end:
> +
> +    /* Display internal FFmpeg error */
> +    if (ret < 0 && ret != AVERROR_EOF) {
> +        char buf[1024];
> +        av_strerror(ret, buf, sizeof(buf));
> +        av_log(NULL, AV_LOG_ERROR, "Error occurred: %s\n", buf);

str2err macro will save you an intermediate buffer

> +        return 1;
> +    }
> +
> +    /* Cleanup */

> +    if (dec_ctx)
> +        avcodec_close(dec_ctx);
> +    if (fmt_ctx)
> +        avformat_close_input(&fmt_ctx);

the NULL checks should not be required anymore since your recent patches.

> +    if (use_ref_counting)
> +        av_frame_free(&frame);
> +    else
> +        avcodec_free_frame(&frame);
> +
> +    return 0;
> +}
-- 
FFmpeg = Faithful & Fascinating Multimedia Power Ecumenical Gigant


More information about the ffmpeg-devel mailing list