diff mbox

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

Message ID c8af0586-d89a-6a08-1eeb-97df0b1bbbf8@gmail.com
State Superseded
Headers show

Commit Message

Jun Zhao July 20, 2017, 7:54 a.m. UTC
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@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@intel.com>
Signed-off-by: Jun Zhao <jun.zhao@intel.com>
---
 doc/examples/hw_decode.c | 249 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 249 insertions(+)
 create mode 100644 doc/examples/hw_decode.c

Comments

Mark Thompson July 20, 2017, 10:44 p.m. UTC | #1
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@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@intel.com>
> Signed-off-by: Jun Zhao <jun.zhao@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
Jun Zhao July 21, 2017, 1:22 a.m. UTC | #2
On 2017/7/21 6:44, Mark Thompson wrote:
> 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@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@intel.com>
>> Signed-off-by: Jun Zhao <jun.zhao@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.

Will split 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,

Thanks for you test dxva2|d3d11, will submit V5 patch to follow the comments.

> 
> - Mark
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
diff mbox

Patch

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_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,
+};
+
+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];
+    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;
+}