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

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

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

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

ベストアンサー1

調査の結果、OS X (VDA) と Linux (VDPAU) で必要な HW アクセラレーション デコードを実装することができました。Windows 実装も入手したら、回答を更新します。では、最も簡単な方法から始めましょう。

Mac OS X

Mac OS で HW アクセラレーションを動作させるには、次の操作を行うだけです。avcodec_find_decoder_by_name("h264_vda");ただし、h264 ビデオをアクセラレーションできるのは Mac OS で FFmpeg を使用する場合のみであることに注意してください。

Linux VDPAU

Linux では、状況ははるかに複雑です (驚く人はいないでしょう)。Linux 上の FFmpeg には、VDPAU (Nvidia) と VAAPI (Intel) の 2 つの HW アクセラレータと、VDPAU 用の HW デコーダが 1 つだけあります。上記の Mac OS の例のように、vdpau デコーダを使用するのが合理的に思えるかもしれません。avcodec_find_decoder_by_name("h264_vdpau");

何も変化せず、まったく加速されないことに驚かれるかもしれません。これはまだ始まりに過ぎず、加速を機能させるにはさらに多くのコードを書く必要があるためです。幸いなことに、自分で解決策を考え出す必要はありません。それを実現する方法の良い例が少なくとも 2 つあります。libavgFFmpeg自体もそうです。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自分のニーズにより適した形式を使用しましたが、おそらく別の形式を選択するでしょう。

これらすべての問題は、FFmpeg から動作させるだけでは不十分で、少なくとも VDPAU API の基本を理解する必要があることです。私の回答が、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 を使用できます。

私は以下を使用しましたリンクUbuntu にセットアップします。

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

おすすめ記事