it-swarm.com.de

Verwendung der Hardwarebeschleunigung mit ffmpeg

Ich muss mein Video (z. B. h264) mit ffmpeg mithilfe der Hardwarebeschleunigung dekodieren. Ich verwende die übliche Methode zum Dekodieren von Frames: Paket lesen -> Frame dekodieren. Und ich möchte, dass ffmpeg die Dekodierung beschleunigt. Also habe ich es mit --enable-vaapi Und --enable-hwaccel=h264 Erstellt. Aber ich weiß nicht wirklich, was ich als nächstes tun soll. Ich habe versucht, avcodec_find_decoder_by_name("h264_vaapi") zu verwenden, aber es gibt nullptr zurück. Wie auch immer, ich möchte vielleicht eine andere API verwenden und nicht nur VA API. Wie soll man die ffmpeg-Dekodierung beschleunigen?

P.S. Ich habe im Internet keine Beispiele gefunden, die ffmpeg mit hwaccel verwenden.

42
ixSci

Nach einiger Recherche konnte ich die notwendige HW-beschleunigte Dekodierung unter OS X (VDA) und Linux (VDPAU) implementieren. Ich werde die Antwort aktualisieren, wenn ich auch die Windows-Implementierung in den Händen habe. Fangen wir also mit dem Einfachsten an:

Mac OS X

Damit die HW-Beschleunigung unter Mac OS funktioniert, sollten Sie nur Folgendes verwenden: avcodec_find_decoder_by_name("h264_vda"); Beachten Sie jedoch, dass Sie h264-Videos nur unter Mac OS mit FFmpeg beschleunigen können.

Linux VDPAU

Unter Linux sind die Dinge viel komplizierter (wer ist überrascht?). FFmpeg hat 2 HW-Beschleuniger unter Linux: VDPAU (Nvidia) und VAAPI (Intel) und nur einen HW-Decoder: für VDPAU. Und es mag durchaus sinnvoll erscheinen, einen vdpau-Decoder wie im obigen Mac OS-Beispiel zu verwenden: avcodec_find_decoder_by_name("h264_vdpau");

Sie werden überrascht sein, dass es nichts ändert und Sie überhaupt keine Beschleunigung haben. Das liegt daran, dass es nur der Anfang ist. Sie müssen viel mehr Code schreiben, damit die Beschleunigung funktioniert. Glücklicherweise müssen Sie nicht selbst eine Lösung finden: Es gibt mindestens zwei gute Beispiele dafür: libavg und FFmpeg selbst. libavg hat eine VDPAUDecoder-Klasse, die völlig klar ist und auf der ich meine Implementierung basiert habe. Sie können auch ffmpeg_vdpau.c konsultieren, um eine andere Implementierung zum Vergleich anzufordern. Meiner Meinung nach ist die libavg-Implementierung jedoch leichter zu verstehen.

Das einzige, was den beiden vorgenannten Beispielen fehlt, ist das ordnungsgemäße Kopieren des decodierten Rahmens in den Hauptspeicher. In beiden Beispielen wird VdpVideoSurfaceGetBitsYCbCr verwendet, wodurch die gesamte Leistung, die ich auf meinem Computer erzielt habe, verloren ging. Aus diesem Grund möchten Sie möglicherweise die folgenden Schritte ausführen, um die Daten von einer GPU zu extrahieren:

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;
}

Obwohl es einige "externe" Objekte enthält, sollten Sie in der Lage sein, es zu verstehen, sobald Sie den Teil "get buffer" implementiert haben (für den die oben genannten Beispiele eine große Hilfe sind). Ich habe auch das BGRA -Format verwendet, das besser für meine Anforderungen geeignet war. Vielleicht werden Sie ein anderes auswählen.

Das Problem dabei ist, dass Sie FFmpeg nicht einfach zum Laufen bringen können, um zumindest die Grundlagen der VDPAU-API zu verstehen. Und ich hoffe, dass meine Antwort jemandem hilft, die HW-Beschleunigung unter Linux umzusetzen. Ich habe selbst viel Zeit damit verbracht, bevor mir klar wurde, dass es keine einfache Einzeilenmethode gibt, um die HW-beschleunigte Dekodierung unter Linux zu implementieren.

Linux VA-API

Da meine ursprüngliche Frage VA-API betraf, kann ich sie nicht unbeantwortet lassen. Erstens gibt es in FFmpeg keinen Dekoder für VA-API, sodass avcodec_find_decoder_by_name("h264_vaapi") keinen Sinn ergibt: es ist nullptr. Ich weiß nicht, wie viel schwieriger (oder vielleicht einfacher?) Die Implementierung der Dekodierung über VA-API ist, da alle Beispiele, die ich gesehen habe, ziemlich einschüchternd waren. Daher habe ich mich entschieden, VA-API überhaupt nicht zu verwenden, und ich musste die Beschleunigung für eine Intel-Karte implementieren. Zum Glück gibt es für mich eine VDPAU-Bibliothek (Treiber?), Die über VA-API funktioniert. So können Sie VDPAU auf Intel-Karten verwenden!

Ich habe das folgende link benutzt, um es auf meinem Ubuntu einzurichten.

Vielleicht möchten Sie auch die Kommentare zur ursprünglichen Frage überprüfen, in der @Timothy_G auch einige Links zu VA-API erwähnt hat.

40
ixSci