web-dev-qa-db-ja.com

ffmpegでハードウェアアクセラレーションを使用する方法

ハードウェアアクセラレーションを使用して、ffmpegでビデオ(h264など)をデコードする必要があります。私はフレームをデコードする通常の方法を使用しています:パケットを読む->フレームをデコードします。そして、ffmpegでデコードを高速化したいと思います。それで、私は_--enable-vaapi_と_--enable-hwaccel=h264_で構築しました。しかし、私は次に何をすべきか本当にわかりません。 avcodec_find_decoder_by_name("h264_vaapi")を使用しようとしましたが、nullptrを返します。とにかく、私はVA API。ではなく、他のAPIを使用したいと思うかもしれません。

追伸hwaccelでffmpegを使用するインターネット上の例は見つかりませんでした。

42
ixSci

いくつかの調査の後、OS X(VDA)およびLinux(VDPAU)で必要なHWアクセラレーションデコードを実装することができました。 Windowsの実装にも手を加えたときに、答えを更新します。それでは、最も簡単なものから始めましょう。

Mac OS X

Mac OSでHWアクセラレーションを機能させるには、次を使用する必要があります。avcodec_find_decoder_by_name("h264_vda");ただし、FFmpegを搭載したMac OSでのみh264ビデオを加速できます。

Linux VDPAU

Linuxでは、事態はもっと複雑です(誰が驚いていますか?)。 FFmpegには、Linux上にVDPAU(Nvidia)とVAAPI(Intel)の2つのHWアクセラレーターがあり、VDPAU用のHWデコーダーは1つだけです。また、上記のMac OSの例のようにvdpauデコーダーを使用することは完全に合理的と思われるかもしれません:avcodec_find_decoder_by_name("h264_vdpau");

あなたはそれが何も変えず、加速がまったくないことを知って驚くかもしれません。それはほんの始まりに過ぎないため、アクセラレーションを機能させるにはさらに多くのコードを記述する必要があります。幸いなことに、自分で解決策を考え出す必要はありません。それを実現する方法の少なくとも2つの良い例があります: 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形式を使用しました。別の形式を選択することもできます。

すべての問題は、少なくともVDPAU APIの基本を理解する必要があるFFmpegから動作させることができないことです。そして、私の答えがLinuxでHWアクセラレーションを実装するのに役立つことを願っています。 LinuxにHWアクセラレーションデコードを実装する簡単な1行の方法はないことに気付く前に、私は自分自身で多くの時間を費やしました。

Linux VA-API

私の元々の質問はVA-APIに関するものだったので、答えを残しておくことはできません。まず、FFmpegにはVA-APIのデコーダがないため、avcodec_find_decoder_by_name("h264_vaapi")は意味をなしません。それはnullptrです。私が見たすべての例は非常に威圧的だったので、VA-APIを介してデコードを実装するのがどれほど難しい(または簡単なのでしょうか)かわかりません。そのため、VA-APIをまったく使用しないことを選択し、Intelカードのアクセラレーションを実装する必要がありました。幸いなことに、VA-APIで動作するVDPAUライブラリ(ドライバー?)があります。したがって、IntelカードでVDPAUを使用できます!

次の link を使用して、Ubuntuでセットアップしました。

また、@ Timothy_GがVA-APIに関するいくつかのリンクに言及した元の質問へのコメントを確認することもできます。

40
ixSci