Cómo usar la aceleración de hardware con ffmpeg


Necesito que ffmpeg decodifique mi video(por ejemplo, h264) usando aceleración de hardware. Estoy usando la forma habitual de decodificar marcos: leer paquete - > decodificar marco. Y me gustaría que ffmpeg acelerara la decodificación. Así que lo he construido con --enable-vaapi y --enable-hwaccel=h264. Pero realmente no sé qué debo hacer a continuación. He intentado usar avcodec_find_decoder_by_name("h264_vaapi") pero devuelve nullptr. De todos modos, es posible que desee utilizar otros API y no solo VA API. ¿Cómo se supone que se debe acelerar la decodificación ffmpeg?

P.d. No encontré ningún ejemplo en Internet que utiliza ffmpeg con hwaccel.

Author: manlio, 2014-04-25

1 answers

Después de algunas investigaciones, pude implementar la decodificación acelerada de HW necesaria en OS X (VDA) y Linux (VDPAU). Actualizaré la respuesta cuando tenga en mis manos la implementación de Windows también. Así que comencemos con lo más fácil:

Mac OS X

Para que la aceleración HW funcione en Mac OS, solo debe usar lo siguiente: avcodec_find_decoder_by_name("h264_vda"); Tenga en cuenta, sin embargo, que puede acelerar videos h264 solo en Mac OS con FFmpeg.

Linux VDPAU

En Linux las cosas son mucho más complicado (¿quién se sorprende?). FFmpeg tiene 2 aceleradores HW en Linux: VDPAU (Nvidia) y VAAPI(Intel) y solo un decodificador HW: para VDPAU. Y puede parecer perfectamente razonable usar el decodificador vdpau como en el ejemplo de Mac OS anterior: avcodec_find_decoder_by_name("h264_vdpau");

Es posible que se sorprenda al descubrir que no cambia nada y que no tiene aceleración en absoluto. Eso es porque es solo el principio, tienes que escribir mucho más código para conseguir que la aceleración funcione. Felizmente, no tienes que venir con una solución por su cuenta: hay al menos 2 buenos ejemplos de cómo lograrlo: libavg y FFmpeg en sí. libavg tiene la clase VDPAUDecoder que es perfectamente clara y en la que he basado mi implementación. También puede consultar ffmpeg_vdpau.c para obtener otra implementación para comparar. Sin embargo, en mi opinión, la implementación de libavg es más fácil de comprender.

Lo único que les falta a ambos ejemplos mencionados es copiar correctamente el marco decodificado a la memoria principal. Ambos ejemplos usan VdpVideoSurfaceGetBitsYCbCr que mató todo el rendimiento que obtuve en mi máquina. Es por eso que es posible que desee utilizar el siguiente procedimiento para extraer los datos de una 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;
}

Si bien tiene algunos objetos "externos" utilizados en su interior, debería ser capaz de entenderlo una vez que haya implementado la parte "get buffer" (para lo cual los ejemplos mencionados anteriormente son de gran ayuda). También he utilizado el formato BGRA que era más adecuado para mis necesidades, tal vez elija otro.

El problema con todo esto es que no se puede hacer que funcione desde FFmpeg, necesita comprender al menos los conceptos básicos de la API de VDPAU. Y espero que mi respuesta ayude a alguien a implementar la aceleración HW en Linux. He pasado mucho tiempo en él antes de darme cuenta de que no hay una forma simple de implementar la decodificación acelerada de HW en Linux.

Linux VA-API

Dado que mi pregunta original se refería a VA-API, no puedo dejar sin respuesta. En primer lugar no hay decodificador para VA-API en FFmpeg así que avcodec_find_decoder_by_name("h264_vaapi") no tiene ningún sentido: es nullptr. No se cuánto más difícil (o tal vez más simple?) es implementar la decodificación a través de VA-API ya que todos los ejemplos que he visto eran bastante intimidantes. Así que elijo no usar VA-API en absoluto y tuve que implementar la aceleración para una tarjeta Intel. Afortunadamente para mí, hay una biblioteca VDPAU(driver?) que funciona sobre VA-API. Para que pueda utilizar VDPAU en tarjetas Intel!

He utilizado el siguiente enlace para configurar en mi Ubuntu.

También, es posible que desee comprobar los comentarios a la pregunta original donde @Timothy_G también mencionó algunos enlaces con respecto a VA-API.

 34
Author: ixSci,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-06-15 22:40:48