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

Necesito que ffmpeg decodifique mi video (por ejemplo, h264) usando la aceleración de hardware. Estoy usando la forma habitual de deencoding de cuadros: leer paquete -> decodificar cuadro. Y me gustaría que ffmpeg acelerara la deencoding. 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 la API de otros y no solo la API de VA. ¿Cómo se supone que uno debe acelerar la deencoding ffmpeg?

PD: No encontré ningún ejemplo en Internet que use ffmpeg con hwaccel.

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

Mac OS X

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

Linux VDPAU

En Linux las cosas son mucho más complicadas (¿a quién sorprende?). FFmpeg tiene 2 aceleradores de HW en Linux: VDPAU (Nvidia) y VAAPI (Intel) y solo un decodificador de 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 ninguna aceleración. Esto se debe a que es solo el comienzo, tienes que escribir mucho más código para que la aceleración funcione. Afortunadamente, no tiene que encontrar una solución por su cuenta: hay al menos 2 buenos ejemplos de cómo lograrlo: libavg y FFmpeg en sí. libavg tiene una clase VDPAUDecoder que está 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. En mi opinión, sin embargo, la implementación de libavg es más fácil de entender.

Lo único que faltan de los dos ejemplos mencionados anteriormente es la copia correcta del cuadro decodificado a la memoria principal. Ambos ejemplos utilizan VdpVideoSurfaceGetBitsYCbCr que mató todo el rendimiento que obtuve en mi máquina. Por eso 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(context->opaque); VdpOutputSurface surface; vdp_output_surface_create(m_VdpDevice, VDP_RGBA_FORMAT_B8G8R8A8, frame->width, frame->height, &surface); auto renderState = reinterpret_cast(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(tmframe->data), reinterpret_cast(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 poder entenderlo una vez que haya implementado la parte “obtener búfer” (para la cual los ejemplos mencionados son de gran ayuda). También he usado el formato BGRA que era más adecuado para mis necesidades, tal vez usted elija otro.

El problema con todo esto es que simplemente no puede hacer que funcione desde FFmpeg, sino que debe comprender al menos los aspectos básicos de la API de VDPAU. Y espero que mi respuesta ayude a alguien a implementar la aceleración HW en Linux. Yo mismo pasé mucho tiempo en él antes de darme cuenta de que no existe una forma simple y de una línea de implementar la deencoding acelerada HW en Linux.

Linux VA-API

Dado que mi pregunta original era sobre VA-API, no puedo dejar de responderla. En primer lugar, no hay ningún decodificador para VA-API en FFmpeg, por lo que avcodec_find_decoder_by_name("h264_vaapi") no tiene ningún sentido: es nullptr . No sé cuánto más difícil (o tal vez más simple) es implementar la deencoding a través de VA-API ya que todos los ejemplos que he visto fueron bastante intimidantes. Así que elegí no usar VA-API y tuve que implementar la aceleración para una tarjeta Intel. Afortunadamente para mí, hay una biblioteca VDPAU (¿controlador?) Que funciona sobre VA-API. ¡Así que puedes usar VDPAU en tarjetas Intel!

He usado el siguiente enlace para configurarlo en mi Ubuntu.

Además, es posible que desee consultar los comentarios a la pregunta original donde @Timothy_G también mencionó algunos enlaces relacionados con VA-API.