So verwenden Sie die Hardwarebeschleunigung mit ffmpeg

Lesezeit: 6 Minuten

Benutzeravatar von ixSci
ixSci

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

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

  • mögliches Duplikat der Verwendung von ffmpeg hwaccel von C++

    – gbjbaanb

    25. April 2014 um 10:45 Uhr

  • @gbjbaanb, die Antwort auf die oben genannte Frage löst nichts. Nur das Einstellen von hwaccel des Codec-Kontexts funktioniert bei mir nicht.

    – ixSci

    25. April 2014 um 11:02 Uhr

  • Auf welcher Plattform arbeiten Sie (z. B. Mac, Windows oder Linux; physische Hardware oder virtualisiert)? Welche Art von Hardware zur Hardwarebeschleunigung haben Sie (glauben Sie) zur Verfügung?

    – Multimedia-Mike

    30. April 2014 um 1:11 Uhr

  • @Multimedia Mike, ich habe Ubuntu auf einem Macbook Pro (2011) installiert, es hat eine Intel-GPU

    – ixSci

    30. April 2014 um 4:34 Uhr


  • In erster Linie ist AVHwaccel nichts anderes als ein Skelett, also erwarten Sie keine einfache Implementierung damit. Sie sollten sich den Quellcode von ansehen mpv, das ist ein Player, der auf MPlayer basiert, aber mit vielen Dingen behoben oder aufgeräumt wurde. Es verfügt über eine ausgereifte VA-API-Decodierungsfunktion und verwendet dafür ffmpeg. Sie können sich auch die Quelle von VLC ansehen.

    – Timotheus Gu

    15. Mai 2014 um 4:17 Uhr


Benutzeravatar von ixSci
ixSci

Nach einigen Recherchen konnte ich die notwendige HW-beschleunigte Dekodierung auf OS X (VDA) und Linux (VDPAU) implementieren. Ich werde die Antwort aktualisieren, wenn ich auch die Windows-Implementierung in die Hände bekomme. Beginnen wir also mit dem Einfachsten:

Mac OS X

Um die HW-Beschleunigung unter Mac OS zum Laufen zu bringen, sollten Sie einfach 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 (wen wundert das?). FFmpeg hat 2 HW-Beschleuniger unter Linux: VDPAU (Nvidia) und VAAPI (Intel) und nur einen HW-Decoder: für VDPAU. Und es mag vollkommen vernünftig erscheinen, den vdpau-Decoder wie im obigen Mac OS-Beispiel zu verwenden:
avcodec_find_decoder_by_name("h264_vdpau");

Sie werden vielleicht überrascht sein herauszufinden, dass es nichts ändert und Sie überhaupt keine Beschleunigung haben. Das liegt daran, dass dies nur der Anfang ist. Sie müssen viel mehr Code schreiben, damit die Beschleunigung funktioniert. Glücklicherweise müssen Sie nicht alleine auf eine Lösung kommen: Es gibt mindestens 2 gute Beispiele dafür, wie Sie das erreichen können: libavg und FFmpeg selbst. libavg hat eine VDPAUDecoder-Klasse, die vollkommen klar ist und auf der ich meine Implementierung basiert habe. Sie können sich auch beraten lassen ffmpeg_vdpau.c um eine andere Implementierung zum Vergleich zu erhalten. Meiner Meinung nach ist die libavg-Implementierung jedoch einfacher zu verstehen.

Das einzige, was beiden oben genannten Beispielen fehlt, ist das richtige Kopieren des decodierten Rahmens in den Hauptspeicher. Beide Beispiele verwendet VdpVideoSurfaceGetBitsYCbCr was die gesamte Leistung, die ich auf meiner Maschine gewonnen habe, zerstört hat. Aus diesem Grund möchten Sie möglicherweise das folgende Verfahren verwenden, 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 einige “externe” Objekte im Inneren verwendet werden, sollten Sie in der Lage sein, es zu verstehen, sobald Sie den Teil “Puffer abrufen” implementiert haben (für den die oben genannten Beispiele eine große Hilfe sind). Auch ich habe verwendet BGRA Format, das für meine Bedürfnisse besser geeignet war, vielleicht wählen Sie ein anderes.

Das Problem bei all dem ist, dass Sie es nicht einfach von FFmpeg aus zum Laufen bringen können, Sie müssen zumindest die Grundlagen der VDPAU-API verstehen. Und ich hoffe, dass meine Antwort jemandem bei der Implementierung der HW-Beschleunigung unter Linux hilft. Ich habe selbst viel Zeit damit verbracht, bevor mir klar wurde, dass es keinen einfachen, einzeiligen Weg gibt, HW-beschleunigte Dekodierung unter Linux zu implementieren.

Linux-VA-API

Da meine ursprüngliche Frage bezüglich VA-API war, kann ich sie nicht unbeantwortet lassen. Zunächst einmal gibt es in FFmpeg also keinen Decoder für VA-API avcodec_find_decoder_by_name("h264_vaapi") macht keinen Sinn: es ist nullptr. Ich weiß nicht, wie viel schwieriger (oder vielleicht einfacher?) es ist, die Dekodierung über die VA-API zu implementieren, da alle Beispiele, die ich gesehen habe, ziemlich einschüchternd waren. Also habe ich mich dafür 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. Sie können also VDPAU auf Intel-Karten verwenden!

Ich habe folgendes verwendet Verknüpfung um es auf meinem Ubuntu einzurichten.

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

  • Vielen Dank für die Beispielquelle für Linux und vdpau, in einer perfekten Welt würde die von Ihnen aufgelistete osx-Lösung unter Linux genauso reibungslos funktionieren (einfach die Absicht zur Hardwarebeschleunigung zu identifizieren, wäre eine wunderbare Schnittstelle).

    – Markus Essel

    13. Oktober 2014 um 12:05 Uhr

  • @MarkEssel, müssen Sie verwenden sws_scale Um einen Rahmen zu konvertieren, den Sie nach dem VDA erhalten, in das Format, das Sie benötigen.

    – ixSci

    20. Oktober 2014 um 15:28 Uhr

  • @MarkEssel, leider kann ich die SW, in der ich die HW-Decodierung implementiert habe, nicht ausführen, daher kann ich nicht überprüfen, was das Problem ist. Aber ich habe mir den Code angesehen und nichts Besonderes getan, um den Frame nach der Dekodierung zu handhaben: Hier ist meine sws-Kontexterstellung: sws_getCachedContext(nullptr, frame->width, frame->height, static_cast<AVPixelFormat>(frame->format), frame->width, frame->height, AV_PIX_FMT_RGB24, SWS_BILINEAR | SWS_ACCURATE_RND, nullptr, nullptr, nullptr);

    – ixSci

    20. Oktober 2014 um 15:59 Uhr

  • @MarkEssel, wenn ich mich recht erinnere, hatte ich das gleiche Problem. Es lag daran, dass ich bei der Initialisierung einmal einen sws-Kontext erstellt habe. In diesem Schritt hat der Kontext das VDA-Pixelformat, was für den sws-Scaler keinen Sinn ergibt. Um also das richtige Pixelformat zu haben, müssen Sie das tatsächliche Frame-Pixelformat für die Kontexterstellung verwenden, und Sie haben es direkt nach der Dekodierung. Ich hoffe, ich erinnere mich richtig.

    – ixSci

    20. Oktober 2014 um 16:02 Uhr


  • sicher bin. avcodec_decode_video2(m_pCodecCtx, m_pFrame, &frameFinished,&packet); if(frameFinished) { Sachen erledigen }

    – Markus Essel

    22. Oktober 2014 um 15:07 Uhr

1409840cookie-checkSo verwenden Sie die Hardwarebeschleunigung mit ffmpeg

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy