c++ - nvenc - Cómo usar la aceleración de hardware con ffmpeg
nvidia h264 encoder (1)
Después de algunas investigaciones, pude implementar la decodificación acelerada HW necesaria en OS X (VDA) y Linux (VDPAU). Actualizaré la respuesta cuando también tenga en mis manos la implementación de Windows. Comencemos con lo más fácil:
Mac OS X
Para que la aceleración de HW funcione en Mac OS, debe usar lo siguiente: avcodec_find_decoder_by_name("h264_vda");
Sin embargo, tenga en cuenta que puede acelerar videos h264 solo en Mac OS con FFmpeg.
VDPAU de Linux
En Linux, las cosas son mucho más complicadas (¿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 anterior de Mac OS: avcodec_find_decoder_by_name("h264_vdpau");
Puede que se sorprenda al descubrir que no cambia nada y que no tiene aceleración alguna. Eso es porque 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 lograr eso: libavg y FFmpeg en sí mismo. 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, la implementación libavg es más fácil de entender, sin embargo.
Lo único que les falta a los dos ejemplos mencionados anteriormente es una copia adecuada de la trama decodificada a la memoria principal. Ambos ejemplos usan VdpVideoSurfaceGetBitsYCbCr
que eliminó 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, debe ser capaz de comprenderlos una vez que haya implementado la parte "get buffer" (a la cual los ejemplos mencionados anteriormente son de gran ayuda). También he usado el formato BGRA
que era más adecuado para mis necesidades, tal vez elijas otro.
El problema con todo esto es que no puedes simplemente hacer que funcione desde FFmpeg, necesitas entender al menos los principios básicos de la API de VDPAU. Y espero que mi respuesta ayude a alguien a implementar la aceleración HW en Linux. He dedicado mucho tiempo a esto antes de darme cuenta de que no existe una forma simple y única de implementar decodificación HW acelerada en Linux.
Linux VA-API
Como mi pregunta original era sobre VA-API, no puedo dejarla sin respuesta. En primer lugar, no hay 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 quizás más simple?) Es implementar la decodificación a través de VA-API, ya que todos los ejemplos que he visto son bastante intimidantes. Así que elegí no usar VA-API en absoluto y tuve que implementar la aceleración para una tarjeta Intel. Afortunadamente para mí, hay una biblioteca VDPAU (¿controlador?) Que funciona sobre VA-API. ¡Entonces puedes usar VDPAU en tarjetas Intel!
He usado el siguiente link para configurarlo en mi Ubuntu.
Además, es posible que desee comprobar los comentarios a la pregunta original donde @Timothy_G también mencionó algunos enlaces con respecto a VA-API.
Necesito tener ffmpeg decodificar 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 tener ffmpeg acelerar 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. Intenté usar avcodec_find_decoder_by_name("h264_vaapi")
pero devuelve nullptr. De todos modos, es posible que desee utilizar otras API y no solo VA API. ¿Cómo se supone que uno debe acelerar la decodificación de ffmpeg?
PD: No encontré ningún ejemplo en Internet que use ffmpeg con hwaccel.