使用C++调用FFmpeg硬件加速

22

如何使用ffmpeg硬件加速解码文件?

我编写了一个使用ffmpeg的视频播放器。 我使用“av_hwaccel_next”检查支持情况,找到了“mpeg2_dxva”。

然而,当我像往常一样加载mpeg2文件时,我没有获得任何硬件加速。AVCodecContext->hwaccel和AVCodecContext->hwaccelcontext都为null。

我需要在某个地方传递一些标志来启用硬件加速吗?

我无法找到任何关于此的信息,有人知道一个好的来源吗?

1个回答

28

从阅读 FFmpeg 文档开始:https://trac.ffmpeg.org/wiki/HWAccelIntro,更好的答案请参考这个如何使用 FFmpeg 硬件加速,(对于 Linux 系统,请查看https://wiki.archlinux.org/index.php/Hardware_video_acceleration)。

在使用 FFmpeg 工具时,通过 -hwaccel 选项启用硬件辅助解码,可以启用特定的解码器。每种解码器可能都有特定的限制(例如 H.264 解码器仅支持基本配置文件)。通过使用特定编码器(例如 h264_nvenc)来启用 HW 辅助编码。仅有少数过滤器支持使用硬件辅助处理。。。 有几种硬件加速标准 API,其中一些被 FFmpeg 支持

HW 加速的激活由类似下面的代码控制(位于 2013 年之后的代码修正版中https://github.com/FFmpeg/FFmpeg/commit/08303d774132775d49d4ba767092de5d426f089d)。

avctx->hwaccel = ff_find_hwaccel(avctx->codec->id, avctx->pix_fmt);
例如,在libavcodec/mpeg12dec.c中 https://github.com/FFmpeg/FFmpeg/blob/6c7254722ad43712db5686feee8bf75c74d8635b/libavcodec/mpeg12dec.c
avctx->pix_fmt = mpeg_get_pixelformat(avctx);
avctx->hwaccel = ff_find_hwaccel(avctx->codec->id, avctx->pix_fmt);

ff_find_hwaccel函数检查图像的编解码器和像素格式,并检查所有可用的硬件加速器。

AVHWAccel *ff_find_hwaccel(enum CodecID codec_id, enum PixelFormat pix_fmt)
{
    AVHWAccel *hwaccel=NULL;

    while((hwaccel= av_hwaccel_next(hwaccel))){
        if (   hwaccel->id      == codec_id
            && hwaccel->pix_fmt == pix_fmt)
            return hwaccel;
    }
    return NULL;
}

例如,dxva2(https://en.wikipedia.org/wiki/DirectX_Video_Acceleration)拥有:

AVHWAccel mpeg2_dxva2_hwaccel = {
    .name           = "mpeg2_dxva2",
    .type           = AVMEDIA_TYPE_VIDEO,
    .id             = CODEC_ID_MPEG2VIDEO,
    .pix_fmt        = PIX_FMT_DXVA2_VLD,
    .capabilities   = 0,
    .start_frame    = start_frame,
    .decode_slice   = decode_slice,
    .end_frame      = end_frame,
    .priv_data_size = sizeof(struct dxva2_picture_context),
};

而且libavutil/pixfmt.h会按照像素格式列出所有支持的硬件解码器/加速器https://ffmpeg.org/doxygen/3.2/pixfmt_8h.html

AV_PIX_FMT_XVMC_MPEG2_MC    - XVideo Motion Acceleration via common packet passing.
AV_PIX_FMT_XVMC_MPEG2_IDCT  - undocumented
AV_PIX_FMT_XVMC         - undocumented
AV_PIX_FMT_VDPAU_H264   - H.264 HW decoding with VDPAU, data[0] contains a vdpau_render_state struct which contains the bitstream of the slices as well as various fields extracted from headers.
AV_PIX_FMT_VDPAU_MPEG1  - MPEG-1 HW decoding with VDPAU, data[0] contains a vdpau_render_state struct which contains the bitstream of the slices as well as various fields extracted from headers.
AV_PIX_FMT_VDPAU_MPEG2  - MPEG-2 HW decoding with VDPAU, data[0] contains a vdpau_render_state struct which contains the bitstream of the slices as well as various fields extracted from headers.
AV_PIX_FMT_VDPAU_WMV3   - WMV3 HW decoding with VDPAU, data[0] contains a vdpau_render_state struct which contains the bitstream of the slices as well as various fields extracted from headers.
AV_PIX_FMT_VDPAU_VC1    - VC-1 HW decoding with VDPAU, data[0] contains a vdpau_render_state struct which contains the bitstream of the slices as well as various fields extracted from headers. 
AV_PIX_FMT_VAAPI_MOCO   - HW acceleration through VA API at motion compensation entry-point, Picture.data[3] contains a vaapi_render_state struct which contains macroblocks as well as various fields extracted from headers.
AV_PIX_FMT_VAAPI_IDCT   - HW acceleration through VA API at IDCT entry-point, Picture.data[3] contains a vaapi_render_state struct which contains fields extracted from headers.
AV_PIX_FMT_VAAPI_VLD    - HW decoding through VA API, Picture.data[3] contains a VASurfaceID. 
AV_PIX_FMT_VDPAU_MPEG4  - MPEG-4 HW decoding with VDPAU, data[0] contains a vdpau_render_state struct which contains the bitstream of the slices as well as various fields extracted from headers.
AV_PIX_FMT_DXVA2_VLD    - HW decoding through DXVA2, Picture.data[3] contains a LPDIRECT3DSURFACE9 pointer. 
AV_PIX_FMT_VDPAU        - HW acceleration through VDPAU, Picture.data[3] contains a VdpVideoSurface. 
AV_PIX_FMT_VDA          - HW acceleration through VDA, data[3] contains a CVPixelBufferRef. 
AV_PIX_FMT_QSV          - HW acceleration through QSV, data[3] contains a pointer to the mfxFrameSurface1 structure.
AV_PIX_FMT_MMAL         - HW acceleration though MMAL, data[3] contains a pointer to the MMAL_BUFFER_HEADER_T structure.
AV_PIX_FMT_D3D11VA_VLD  - HW decoding through Direct3D11, Picture.data[3] contains a ID3D11VideoDecoderOutputView pointer.
AV_PIX_FMT_CUDA         - HW acceleration through CUDA. data[i] contain CUdeviceptr pointers exactly as for system memory frames. 

实际像素格式的选择在调用ff_find_hwaccel之前的函数中进行,对于mpeg1/2使用libavcodec/mpeg12dec.c中的static enum PixelFormat mpeg_get_pixelformat(AVCodecContext *avctx)。在早期版本的ffmpeg/libavcodec中,它检查avctx->xvmc_acceleration和/或avctx->codec->capabilities&CODEC_CAP_HWACCEL_VDPAU或调用avctx->get_format(avctx,ff_hwaccel_pixfmt_list_420);在某些情况下启用硬件解码。

在最近的版本(2017年),它和周围几个函数都选择了hw编码器https://github.com/FFmpeg/FFmpeg/blob/aff8cf18cb0b1fa4f2e3d163c3da2f25aa6d1906/libavcodec/mpeg12dec.c#L1189

基本上:硬件解码器及其api(过时的XVMCVDPAUVA API,MS DXVA或MS Direct3D11,或视频工具箱)应启用在您的ffmpeg版本中,并且由硬件支持以及其驱动程序/库(许多是专有的,需要单独下载)。有时需要给ffmpeg提供-hwaccel选项或加载插件。在Linux中,您可以使用vainfovdpauinfo命令来测试大多数流行标准视频hw解码API的可用性和支持的配置文件。

输入文件(对于mpeg1/2)不应为灰度,它的s->chroma_format应小于2(4:2:0色度子采样,这在ISO/IEC MPEG和ITU-T VCEG H.26x中很常见;但在某些MPEG-4 Part 2和高4:4:4变体的H.264/MPEG-4 AVC中不是这样)。

static const enum AVPixelFormat mpeg2_hwaccel_pixfmt_list_420[] = {
#if CONFIG_MPEG2_XVMC_HWACCEL
    AV_PIX_FMT_XVMC,
#endif
#if CONFIG_MPEG_VDPAU_DECODER && FF_API_VDPAU
    AV_PIX_FMT_VDPAU_MPEG2,
#endif
#if CONFIG_MPEG2_VDPAU_HWACCEL
    AV_PIX_FMT_VDPAU,
#endif
#if CONFIG_MPEG2_DXVA2_HWACCEL
    AV_PIX_FMT_DXVA2_VLD,
#endif
#if CONFIG_MPEG2_D3D11VA_HWACCEL
    AV_PIX_FMT_D3D11VA_VLD,
#endif
#if CONFIG_MPEG2_VAAPI_HWACCEL
    AV_PIX_FMT_VAAPI,
#endif
#if CONFIG_MPEG2_VIDEOTOOLBOX_HWACCEL
    AV_PIX_FMT_VIDEOTOOLBOX,
#endif
    AV_PIX_FMT_YUV420P,
    AV_PIX_FMT_NONE
};

static const enum AVPixelFormat mpeg12_pixfmt_list_422[] = {
    AV_PIX_FMT_YUV422P,
    AV_PIX_FMT_NONE
};

static const enum AVPixelFormat mpeg12_pixfmt_list_444[] = {
    AV_PIX_FMT_YUV444P,
    AV_PIX_FMT_NONE
};

#if FF_API_VDPAU
static inline int uses_vdpau(AVCodecContext *avctx) {
    return avctx->pix_fmt == AV_PIX_FMT_VDPAU_MPEG1 || avctx->pix_fmt == AV_PIX_FMT_VDPAU_MPEG2;
}
#endif

static enum AVPixelFormat mpeg_get_pixelformat(AVCodecContext *avctx)
{
    Mpeg1Context *s1  = avctx->priv_data;
    MpegEncContext *s = &s1->mpeg_enc_ctx;
    const enum AVPixelFormat *pix_fmts;

    if (CONFIG_GRAY && (avctx->flags & AV_CODEC_FLAG_GRAY))
        return AV_PIX_FMT_GRAY8;

    if (s->chroma_format < 2)
        pix_fmts = avctx->codec_id == AV_CODEC_ID_MPEG1VIDEO ?
                                mpeg1_hwaccel_pixfmt_list_420 :
                                mpeg2_hwaccel_pixfmt_list_420;
    else if (s->chroma_format == 2)
        pix_fmts = mpeg12_pixfmt_list_422;
    else
        pix_fmts = mpeg12_pixfmt_list_444;

    return ff_thread_get_format(avctx, pix_fmts);
}

static void setup_hwaccel_for_pixfmt(AVCodecContext *avctx)
{
    // until then pix_fmt may be changed right after codec init
    if (avctx->hwaccel
#if FF_API_VDPAU
        || uses_vdpau(avctx)
#endif
        )
        if (avctx->idct_algo == FF_IDCT_AUTO)
            avctx->idct_algo = FF_IDCT_SIMPLE;

    if (avctx->hwaccel && avctx->pix_fmt == AV_PIX_FMT_XVMC) {
        Mpeg1Context *s1 = avctx->priv_data;
        MpegEncContext *s = &s1->mpeg_enc_ctx;

        s->pack_pblocks = 1;
#if FF_API_XVMC
FF_DISABLE_DEPRECATION_WARNINGS
        avctx->xvmc_acceleration = 2;
FF_ENABLE_DEPRECATION_WARNINGS
#endif /* FF_API_XVMC */
    }
}

那么我需要手动覆盖avctx的pix_fmt为PIX_FMT_DXVA2_VLD吗?当我查看ffmpeg代码时,我只看到该值被读取,从未被设置。 - ronag
他的问题是关于如何在程序中集成hwaccel,官方文档只涉及如何通过ffmpeg.exe使用它。在调用avctx->hwaccel = ff_find_hwaccel(avctx->codec->id, avctx->pix_fmt)之前,缺少了avctx->pix_fmt = mpeg_get_pixelformat(avctx);,这似乎没有改变任何东西!感谢您的完整回复,我今天会尝试一下。 - Maypeur
@Maypeur,另一个有用的是ffmpeg.exe代码来解析hwaccel命令行选项:https://github.com/FFmpeg/FFmpeg/blob/6294247730549064b1c948c423cf12aa6ff8cf03/ffmpeg_opt.c#L754 MATCH_PER_STREAM_OPT(hwaccels, str, hwaccel, ic, st);if (hwaccel) & int show_hwaccels() & https://github.com/FFmpeg/FFmpeg/blob/6294247730549064b1c948c423cf12aa6ff8cf03/ffmpeg_opt.c#L3585 - osgx
我发现av_hwaccel_next总是返回NULL。https://github.com/FFmpeg/FFmpeg/blob/release/4.2/libavcodec/utils.c#L1796。我该如何使用avctx->hwaccel = ff_find_hwaccel(avctx->codec->id, avctx->pix_fmt);手动设置hwaccel。 - tbago
@tbago,感谢您的更新。在2017年11月26日,av_hwaccel_next已被弃用:https://github.com/FFmpeg/FFmpeg/blob/5c0d1f78968ce088b6750a0bfdc2a9c1ddc3692d/doc/APIchanges#L238 "*2017-11-26 - 3536a3efb9 - lavc 58.5.100 - avcodec.h Deprecate user visibility of the AVHWAccel structure and the functions av_register_hwaccel() and av_hwaccel_next()。" https://patchwork.ffmpeg.org/project/ffmpeg/patch/20171124005134.5683-4-sw@jkqxz.net/ 我们需要重新检查ffmpeg现在的工作方式;如果您有详细的问题,请提出新的问题。有人会帮助您。 - osgx
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接