I need to have ffmpeg decode my video(e.g. h264) using hardware acceleration. I'm using the usual way of decoding frames: read packet -> decode frame. And I'd like to have ffmpeg speed up decoding. So I've built it with --enable-vaapi and --enable-hwaccel=h264. But I don't really know what should I do next. I've tried to use avcodec_find_decoder_by_name("h264_vaapi") but it returns nullptr. Anyway, I might want to use others API and not just VA API. How one is supposed to speed up ffmpeg decoding?

另外,我在网上没有找到任何使用ffmpeg和hwaccel的例子.

推荐答案

经过一些调查,我能够在OS X(VDA)和Linux(VDPAU)上实现必要的硬件加速解码.我也会在掌握Windows实现后更新答案.

Mac OS X

要在Mac OS上实现硬件加速,只需使用以下功能:

Linux VDPAU

在Linux上,事情要复杂得多(谁会感到惊讶呢?)FFmpeg在Linux上有两个硬件加速器:VDPAU(Nvidia)和VAAPI(Intel),只有一个硬件解码器:用于VDPAU.使用上面的Mac OS示例中的vdpau解码器似乎完全合理: avcodec_find_decoder_by_name("h264_vdpau");

你可能会惊讶地发现,它不会改变任何东西,你根本没有加速度.这是因为这只是一个开始,你必须编写更多的代码来加速.幸运的是,你不必自己想出解决方案:至少有两个很好的例子可以说明如何实现这一点:libavg和FFmpeg本身.libavg有一个非常清晰的VDPAUDecoder类,我是基于这个类实现的.您还可以参考ffmpeg_vdpau.c,以获得另一个要比较的实现.不过,在我看来,libavg的实现更容易掌握.

上述两个示例唯一缺少的是将解码帧正确复制到主存储器.这两个示例都使用VdpVideoSurfaceGetBitsYCbCr,这会毁掉我在机器上获得的所有性能.这就是您可能需要使用以下过程从GPU提取数据的原因:

bool VdpauDecoder::fillFrameWithData(AVCodecContext* context,
    AVFrame* frame)
{
    VdpauDecoder* vdpauDecoder = static_cast<VdpauDecoder*>(context->opaque);
    VdpOutputSurface surface;
    vdp_output_surface_create(m_VdpDevice, VDP_RGBA_FORMAT_B8G8R8A8, frame->width, frame->height, &surface);
    auto renderState = reinterpret_cast<vdpau_render_state*>(frame->data[0]);
    VdpVideoSurface videoSurface = renderState->surface;

    auto status = vdp_video_mixer_render(vdpauDecoder->m_VdpMixer,
        VDP_INVALID_HANDLE,
        nullptr,
        VDP_VIDEO_MIXER_PICTURE_STRUCTURE_FRAME,
        0, nullptr,
        videoSurface,
        0, nullptr,
        nullptr,
        surface,
        nullptr, nullptr, 0, nullptr);
    if(status == VDP_STATUS_OK)
    {
        auto tmframe = av_frame_alloc();
        tmframe->format = AV_PIX_FMT_BGRA;
        tmframe->width = frame->width;
        tmframe->height = frame->height;
        if(av_frame_get_buffer(tmframe, 32) >= 0)
        {
            VdpStatus status = vdp_output_surface_get_bits_native(surface, nullptr,
                reinterpret_cast<void * const *>(tmframe->data),
                reinterpret_cast<const uint32_t *>(tmframe->linesize));
            if(status == VDP_STATUS_OK && av_frame_copy_props(tmframe, frame) == 0)
            {
                av_frame_unref(frame);
                av_frame_move_ref(frame, tmframe);
                return;
            }
        }
        av_frame_unref(tmframe);
    }
    vdp_output_surface_destroy(surface);
    return 0;
}

虽然它内部使用了一些"外部"对象,但是一旦实现了"get buffer"部分(前面的示例对此很有帮助),您就应该能够理解它了.另外,我已经使用了更适合我需要的BGRA格式,也许您会 Select 另一种格式.

所有这些的问题是,您不能仅仅通过FFmpeg让它工作,您至少需要了解VDPAU API的基础知识.我希望我的回答能对某些人在Linux上实现硬件加速有所帮助.我自己花了很多时间在上面,直到我意识到在Linux上没有简单的单行方式实现硬件加速解码.

Linux VA-API

因为我最初的问题是关于VA-API的,所以我不能不回答它. 首先,FFmpeg中没有VA-API的解码器,所以avcodec_find_decoder_by_name("h264_vaapi")没有任何意义:它是nullptr. 我不知道有多难(或者可能更简单?)是通过VA-API实现解码,因为我看到的所有示例都非常吓人.因此,我 Select 根本不使用VA-API,并且我必须实现英特尔卡的加速.对我来说足够幸运的是,有一个VDPAU库(驱动程序?)它在VA-API上工作.因此,您可以在英特尔卡上使用VDPAU!

我已经使用下面的link在我的Ubuntu上进行了设置.

此外,您可能希望判断对原始问题的 comments ,其中@TIMOTHY_G还提到了一些关于VA-API的链接.

C++相关问答推荐

我可以动态分配具有空类型函数的矩阵吗?

修改pGM使用指针填充2-D数组但不起作用

C如何显示字符串数组中的第一个字母

与unions 的未定义行为

需要大整数和浮点数.使用long long int和long double

我编译了一个新的c程序,并收到以下错误

ESP32在vTaskDelay上崩溃

GTK3按钮信号错误

将变量或参数打包到 struct /联合中是否会带来意想不到的性能损失?

理解bzip2的BZ2_解压缩函数中的状态重新分配

我应该在递归中使用全局变量吗

如何将两个uint32_t值交织成一个uint64_t?

错误:字符串在C中获得意外输出

在文件描述符上设置FD_CLOEXEC与将其传递给POSIX_SPOWN_FILE_ACTIONS_ADCLOSE有区别吗?

根据输入/输出将 C 编译过程分为预处理、编译、汇编和链接步骤

使用替代日历打印日期

为什么写入关闭管道会返回成功

如何向 execl 创建的后台程序提供输入?

无法在线程内用 C 打印?

我们可以在不违反标准的情况下向标准函数声明添加属性吗?