如何使用ffmpeg进行硬件加速

时间:2014-04-25 09:26:31

标签: c++ c ffmpeg hardware-acceleration

我需要使用硬件加速对ffmpeg解码我的视频(例如h264)。我使用解码帧的常用方法:读取数据包 - >解码帧。我想让ffmpeg加速解码。所以我使用--enable-vaapi--enable-hwaccel=h264构建了它。但我真的不知道接下来该做什么。我尝试使用avcodec_find_decoder_by_name("h264_vaapi")但它返回nullptr。 无论如何,我可能想要使用其他API而不仅仅是VA API。如何加速ffmpeg解码?

P.S。我没有在互联网上找到任何使用ffmpeg和hwaccel的例子。

1 个答案:

答案 0 :(得分:40)

经过一番调查后,我能够在OS X(VDA)和Linux(VDPAU)上实现必要的硬件加速解码。当我接触到Windows实现时,我将更新答案。 所以,让我们从最简单的开始:

Mac OS X

要在Mac OS上运行硬件加速,您应该使用以下内容: avcodec_find_decoder_by_name("h264_vda"); 但请注意,您只能在使用FFmpeg的Mac OS上加速h264视频。

Linux VDPAU

在Linux上,事情要复杂得多(谁感到惊讶?)。 FFmpeg在Linux上有2个HW加速器:VDPAU(Nvidia)和VAAPI(Intel),只有一个HW解码器:用于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;
}

虽然它有一些&#34;外部&#34;一旦你实现了&#34; get buffer&#34;你内部使用的对象应该能够理解它。部分(上述例子对其有很大帮助)。我也使用了更适合我需要的BGRA格式,也许你会选择另一种格式。

所有这一切的问题在于,您无法从FFmpeg开始使用它,您至少需要了解VDPAU API的基础知识。我希望我的回答可以帮助某人在Linux上实现硬件加速。在我意识到在Linux上没有简单的单线方式实现硬件加速解码之前,我自己花了很多时间。

Linux VA-API

由于我最初的问题是关于VA-API,我不能不回答它。 首先,FFmpeg中没有VA-API的解码器,因此avcodec_find_decoder_by_name("h264_vaapi")没有任何意义:它是nullptr。 我不知道通过VA-API实现解码有多难(或者更简单?),因为我见过的所有例子都非常令人生畏。所以我选择不使用VA-API,我必须实现英特尔卡的加速。幸运的是,有一个VDPAU库(驱动程序?)可以在VA-API上运行。所以你可以在英特尔卡上使用VDPAU!

我已使用以下link在我的Ubuntu上设置它。

另外,您可能想查看原始问题的评论,其中@Timothy_G也提到了一些关于VA-API的链接。