[FFmpeg-devel] [PATCH V4] examples/hw_decode: Add a HWAccel decoding example.

Mark Thompson sw at jkqxz.net
Fri Jul 21 01:44:20 EEST 2017


On 20/07/17 08:54, Jun Zhao wrote:
> V4: fix potential memory leak issue base on Steven Liu's review.
> V3: re-work to support the other hwaccels, rename from vaapi_dec.c to hw_decode.c.
>    just test with vaapi, dxva2|d3d11va|videotoolbox might work as well.
> V2: re-work with new hw decoding API.
> 
> From 718f92731d308423e5a09d0384f7bf2361f5a307 Mon Sep 17 00:00:00 2001
> From: Jun Zhao <jun.zhao at intel.com>
> Date: Thu, 20 Jul 2017 00:58:56 -0400
> Subject: [PATCH V4] examples/hw_decode: Add a HWAccel decoding example.
> 
> Add a HWAccel decoding example.
> 
> Just test with vaapi, dxva2|d3d11va|videotoolbox might work as well.
> 
> Signed-off-by: Liu, Kaixuan <kaixuan.liu at intel.com>
> Signed-off-by: Jun Zhao <jun.zhao at intel.com>
> ---
>  doc/examples/hw_decode.c | 249 +++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 249 insertions(+)
>  create mode 100644 doc/examples/hw_decode.c
> 
> diff --git a/doc/examples/hw_decode.c b/doc/examples/hw_decode.c
> new file mode 100644
> index 0000000000..0e77ee877f
> --- /dev/null
> +++ b/doc/examples/hw_decode.c
> @@ -0,0 +1,249 @@
> +/*
> + * Copyright (c) 2017 Jun Zhao
> + * Copyright (c) 2017 Kaixuan Liu
> + *
> + * HW Acceleration API (video decoding) decode sample
> + *
> + * This file is part of FFmpeg.
> + *
> + * FFmpeg is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * FFmpeg is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with FFmpeg; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
> + */
> +
> +/**
> + * @file
> + * HW-Accelerated decoding example.
> + *
> + * @example hw_decode.c
> + * This example shows how to do HW-accelerated decoding with output
> + * frames from the HW video surfaces.
> + */
> +
> +#include <stdio.h>
> +
> +#include <libavcodec/avcodec.h>
> +#include <libavformat/avformat.h>
> +#include <libavutil/pixdesc.h>
> +#include <libavutil/hwcontext.h>
> +#include <libavutil/opt.h>
> +#include <libavutil/avassert.h>
> +#include <libavutil/imgutils.h>
> +
> +static AVBufferRef *hw_device_ctx = NULL;
> +static enum AVPixelFormat hw_pix_fmt;
> +FILE *output_file = NULL;
> +
> +static enum AVPixelFormat hw_pix_fmts[] = {
> +    [AV_HWDEVICE_TYPE_CUDA]         = AV_PIX_FMT_CUDA,
> +    [AV_HWDEVICE_TYPE_DXVA2]        = AV_PIX_FMT_DXVA2_VLD,
> +    [AV_HWDEVICE_TYPE_D3D11VA]      = AV_PIX_FMT_D3D11VA_VLD,

AV_PIX_FMT_D3D11 (this was changed recently).

> +    [AV_HWDEVICE_TYPE_QSV]          = AV_PIX_FMT_QSV,
> +    [AV_HWDEVICE_TYPE_VAAPI]        = AV_PIX_FMT_VAAPI,
> +    [AV_HWDEVICE_TYPE_VDPAU]        = AV_PIX_FMT_VDPAU,
> +    [AV_HWDEVICE_TYPE_VIDEOTOOLBOX] = AV_PIX_FMT_VIDEOTOOLBOX,
> +};

Neither CUDA nor QSV will work here, as they are dummy hwaccels used on other decoders.  Just leave the five real hwaccels.

> +
> +static enum AVPixelFormat find_fmt_by_hw_type(const enum AVHWDeviceType type)
> +{
> +    if (type >= 0 && type < FF_ARRAY_ELEMS(hw_pix_fmts))
> +        return hw_pix_fmts[type];
> +    else
> +        return AV_PIX_FMT_NONE;
> +}
> +
> +static int hw_decoder_init(AVCodecContext *ctx, const enum AVHWDeviceType type)
> +{
> +    int err = 0;
> +
> +    if ((err = av_hwdevice_ctx_create(&hw_device_ctx, type,
> +                                      NULL, NULL, 0)) < 0) {
> +        fprintf(stderr, "Failed to create specified HW device.\n");
> +        return err;
> +    }
> +    ctx->hw_device_ctx = av_buffer_ref(hw_device_ctx);
> +
> +    return err;
> +}
> +
> +static enum AVPixelFormat get_hw_format(AVCodecContext *ctx,
> +                                        const enum AVPixelFormat *pix_fmts)
> +{
> +    const enum AVPixelFormat *p;
> +
> +    for (p = pix_fmts; *p != -1; p++) {
> +        if (*p == hw_pix_fmt)
> +            return *p;
> +    }
> +
> +    fprintf(stderr, "Failed to get HW surface format.\n");
> +    return AV_PIX_FMT_NONE;
> +}
> +
> +static int decode_write(AVCodecContext *avctx, AVPacket *packet)
> +{
> +    AVFrame *frame = NULL, *sw_frame = NULL;
> +    AVFrame *tmp_frame = NULL;
> +    uint8_t *buffer = NULL;
> +    int size;
> +    int ret = 0;
> +
> +    ret = avcodec_send_packet(avctx, packet);
> +    if (ret < 0) {
> +        fprintf(stderr, "Error during decoding\n");
> +        return ret;
> +    }
> +
> +    while (ret >= 0) {
> +        if (!(frame = av_frame_alloc()) || !(sw_frame = av_frame_alloc())) {
> +            fprintf(stderr, "Can not alloc frame\n");
> +            ret = AVERROR(ENOMEM);
> +            goto fail;
> +        }
> +
> +        ret = avcodec_receive_frame(avctx, frame);
> +        if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
> +            break;
> +        else if (ret < 0) {
> +            fprintf(stderr, "Error while decoding\n");
> +            goto fail;
> +        }
> +
> +        if (frame->format == hw_pix_fmt) {
> +            /* retrieve data from GPU to CPU */
> +            if ((ret = av_hwframe_transfer_data(sw_frame, frame, 0)) < 0) {
> +                fprintf(stderr, "Error transferring the data to system memory\n");
> +                goto fail;
> +            }
> +            tmp_frame = sw_frame;
> +        } else
> +            tmp_frame = frame;
> +
> +        size = av_image_get_buffer_size(tmp_frame->format, tmp_frame->width,
> +                                        tmp_frame->height, 1);
> +        buffer = av_malloc(size);
> +        if (!buffer) {
> +            fprintf(stderr, "Can not alloc buffer\n");
> +            ret = AVERROR(ENOMEM);
> +            goto fail;
> +        }
> +        ret = av_image_copy_to_buffer(buffer, size,
> +                                      (const uint8_t * const *)tmp_frame->data,
> +                                      (const int *)tmp_frame->linesize, tmp_frame->format,
> +                                      tmp_frame->width, tmp_frame->height, 1);
> +        if (ret < 0) {
> +            fprintf(stderr, "Can not copy image to buffer\n");
> +            goto fail;
> +        }
> +
> +        if ((ret = fwrite(buffer, 1, size, output_file)) < 0) {
> +            fprintf(stderr, "Failed to dump raw data.\n");
> +            goto fail;
> +        }
> +
> +    fail:
> +        av_frame_free(&frame);
> +        av_frame_free(&sw_frame);
> +        if (buffer)
> +            av_freep(&buffer);
> +        if (ret < 0)
> +            return ret;
> +    }
> +
> +    return 0;
> +}
> +
> +int main(int argc, char *argv[])
> +{
> +    AVFormatContext *input_ctx = NULL;
> +    int video_stream, ret;
> +    AVCodecContext *decoder_ctx = NULL;
> +    AVCodec *decoder = NULL;
> +    AVPacket packet;
> +    enum AVHWDeviceType type;
> +
> +    if (argc < 4) {
> +        fprintf(stderr, "Usage: %s <vaapi|dxva2|d3d11va> <input file> <output file>\n", argv[0]);
> +        return -1;
> +    }
> +
> +    av_register_all();
> +
> +    type = av_hwdevice_find_type_by_name(argv[1]);
> +    hw_pix_fmt = find_fmt_by_hw_type(type);
> +
> +    /* open the input file */
> +    if (avformat_open_input(&input_ctx, argv[2], NULL, NULL) != 0) {
> +        fprintf(stderr, "Cannot open input file '%s'\n", argv[1]);
> +        return -1;
> +    }
> +
> +    if (avformat_find_stream_info(input_ctx, NULL) < 0) {
> +        fprintf(stderr, "Cannot find input stream information.\n");
> +        return -1;
> +    }
> +
> +    /* find the video stream information */
> +    ret = av_find_best_stream(input_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, &decoder, 0);
> +    if (ret < 0) {
> +        fprintf(stderr, "Cannot find a video stream in the input file\n");
> +        return -1;
> +    }
> +    video_stream = ret;
> +
> +    if (!(decoder_ctx = avcodec_alloc_context3(decoder)))
> +        return AVERROR(ENOMEM);
> +
> +    AVStream *video = input_ctx->streams[video_stream];

Mixed declarations and code.

> +    if (avcodec_parameters_to_context(decoder_ctx, video->codecpar) < 0)
> +        return -1;
> +
> +    decoder_ctx->get_format  = get_hw_format;
> +    av_opt_set_int(decoder_ctx, "refcounted_frames", 1, 0);
> +
> +    if (hw_decoder_init(decoder_ctx, type) < 0)
> +        return -1;
> +
> +    if ((ret = avcodec_open2(decoder_ctx, decoder, NULL)) < 0) {
> +        fprintf(stderr, "Failed to open codec for stream #%u\n", video_stream);
> +        return -1;
> +    }
> +
> +    /* open the file to dump raw data */
> +    output_file = fopen(argv[3], "w+");
> +
> +    /* actual decoding and dump the raw data */
> +    while (ret >= 0) {
> +        if ((ret = av_read_frame(input_ctx, &packet)) < 0)
> +            break;
> +
> +        if (video_stream == packet.stream_index)
> +            ret = decode_write(decoder_ctx, &packet);
> +
> +        av_packet_unref(&packet);
> +    }
> +
> +    /* flush the decoder */
> +    packet.data = NULL;
> +    packet.size = 0;
> +    ret = decode_write(decoder_ctx, &packet);
> +    av_packet_unref(&packet);
> +
> +    if (output_file)
> +        fclose(output_file);
> +    avcodec_free_context(&decoder_ctx);
> +    avformat_close_input(&input_ctx);
> +    av_buffer_unref(&hw_device_ctx);
> +
> +    return 0;
> +}
> -- 
> 2.11.0
> 

I tested DXVA2 and D3D11 (with the pixfmt changed) and they both work as expected.

This should be in the build system so it's actually built by "make examples" (in configure and the Makefiles under doc - just copy one of the other codec examples).

Can anyone else test videotoolbox?

Even without that, I'd be happy to apply this with the above fixed.

Thanks,

- Mark


More information about the ffmpeg-devel mailing list