Comment utiliser l'accélération matérielle avec ffmpeg

j'ai besoin de ffmpeg décoder ma vidéo(par exemple h264) en utilisant l'accélération matérielle. J'utilise la façon habituelle de décoder les cadres: lire le paquet - > décoder le cadre. Et j'aimerais que ffmpeg accélère le décodage. J'ai donc construit avec --enable-vaapi et --enable-hwaccel=h264. Mais je ne sais pas vraiment ce que je dois faire ensuite. J'ai essayé d'utiliser avcodec_find_decoder_by_name("h264_vaapi") mais il retourne nullptr. Quoi qu'il en soit, je voudrais peut-être utiliser D'autres API et pas seulement L'API VA. Comment on est censé accélérer ffmpeg décodage?

P.S. Je n'ai pas trouvé tous les exemples sur Internet qui utilise ffmpeg avec hwaccel.

37
demandé sur manlio 2014-04-25 13:26:31

1 réponses

après quelques recherches j'ai pu implémenter le décodage accéléré HW nécessaire sur OS X (VDA) et Linux (VDPAU). Je mettrai à jour la réponse lorsque je mettrai la main sur L'implémentation de Windows. Alors commençons par le plus simple:

Mac OS X

pour obtenir l'accélération de HW travail sur Mac OS vous devez juste utiliser le suivant: avcodec_find_decoder_by_name("h264_vda"); Notez cependant que vous pouvez accélérer les vidéos h264 uniquement sur Mac OS avec FFmpeg.

Linux VDPAU

Sur Linux, les choses sont beaucoup plus compliquées(qui est surpris?). FFmpeg dispose de 2 accélérateurs HW sur Linux: VDPAU(Nvidia) et VAAPI(Intel) et un seul décodeur HW: pour VDPAU. Et il peut sembler parfaitement raisonnable d'utiliser le décodeur vdpau comme dans L'exemple de Mac OS ci-dessus.: avcodec_find_decoder_by_name("h264_vdpau");

Vous pourriez être surpris de savoir que ça ne change rien et vous n'avez pas d'accélération. C'est parce qu'il n'est que le début, vous devez écrire beaucoup plus de code pour obtenir l'accélération de travail. Heureusement, vous n'avez pas à trouver une solution sur votre propre: il y a au moins 2 bons exemples de la manière d'y parvenir: libavg et FFmpeg lui-même. libavg a la classe VDPAUDecoder qui est parfaitement claire et sur laquelle j'ai basé mon implémentation. Vous pouvez également consulter ffmpeg_vdpau.c pour obtenir une autre implémentation à comparer. À mon avis, l'implémentation de libavg est plus facile à comprendre.

Les seules choses à la fois les exemples mentionnés ci-dessus manquent est la copie correcte du cadre décodé à la mémoire principale. Les deux exemples utilisent VdpVideoSurfaceGetBitsYCbCr qui a tué toutes les performances que j'ai gagnées sur ma machine. C'est pourquoi vous pouvez utiliser la procédure suivante pour extraire les données d'un 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;
}

bien qu'il y ait quelques objets "externes" utilisés à l'intérieur, vous devriez être capable de le comprendre une fois que vous avez implémenté la partie "get buffer" (à laquelle les exemples ci-dessus sont d'une grande aide). Aussi j'ai utilisé BGRA format qui était plus adapté à mes besoins peut-être que vous choisirez un autre.

le problème avec tout cela est que vous ne pouvez pas simplement le faire fonctionner à partir de FFmpeg vous devez au moins comprendre les bases de L'API VDPAU. Et j'espère que ma réponse aidera quelqu'un à implémenter L'accélération HW sur Linux. J'ai passé beaucoup de temps dessus moi-même avant de réaliser qu'il n'y a pas de moyen simple, une seule ligne de mettre en œuvre le décodage accéléré HW sur Linux.

Linux VA-API

comme ma question initiale concernait L'API VA, Je ne peux pas la laisser sans réponse. Tout d'abord, il n'y a pas de décodeur pour VA-API dans FFmpeg donc avcodec_find_decoder_by_name("h264_vaapi") ça n'a aucun sens: c'est nullptr. Je ne sais pas à quel point c'est plus dur(ou peut-être plus simple?) est d'implémenter le décodage via va-API puisque tous les exemples que j'ai vus étaient assez intimidants. Donc j'ai choisi de ne pas utiliser L'API VA-et j'ai dû implémenter l'accélération pour une carte Intel. Heureusement pour moi, il y a un VDPAU bibliothèque(pilote?) qui fonctionne sur L'API VA. Vous pouvez donc utiliser VDPAU sur les cartes Intel!

j'ai utilisé ce qui suit lien pour le configurer sur mon Ubuntu.

aussi, vous pouvez vérifier les commentaires à la question originale où @Timothy_G a aussi mentionné quelques liens concernant L'API VA.

34
répondu ixSci 2014-06-15 22:40:48