Práctica de decodificación de hardware FFmpeg DXVA2

Hola Este artículo es una continuación de mi artículo de FFmpeg que comienza con Visual Studio. Aquí llegamos a la decodificación de hardware de la transmisión FULL HD RTSP. Diré de antemano que incluso el Intel ATOM Z8350 puede hacer frente fácilmente a esta tarea.

Tarea: decodificación de hardware y grabación de hasta 4 cuadros en RAM para el procesamiento paralelo posterior (cuatro núcleos de procesador) desde una cámara RTSP h.264 IP. Muestro los marcos procesados ​​usando las funciones WinAPI. Como resultado, obtenemos un sistema de alta velocidad para el procesamiento por computadora del flujo RTSP en modo paralelo. A continuación, puede conectar los algoritmos de visión por computadora para procesar marcos en tiempo real .

imagen

Entrada


¿Por qué necesito decodificación de hardware? Desea decodificar video en tiempo real con un procesador débil y barato o desea descargar el procesador tanto como sea posible, entonces es hora de familiarizarse con la decodificación de hardware.

DirectX Video Acceleration (DXVA) es una API para usar la aceleración de hardware para acelerar el procesamiento de video con GPU. DXVA 2.0 le permite redirigir más operaciones a la GPU, incluidas las operaciones de captura y procesamiento de video.

Después de escribir el artículo anterior, me hicieron algunas preguntas: "¿por qué se usa FFmpeg?" Comenzaré con los problemas. La principal dificultad de la decodificación de hardware es escribir el marco decodificado en la RAM. Para Full HD, esto es 1920 x 1080 x 3 = 6.220.800 bytes. Incluso teniendo en cuenta el hecho de que la trama se almacena en el formato NV12, esto también es mucho de 1920 x 1080 x 1.5 = 3110400 bytes. Sobrescribir 75 MB por segundo es una tarea seria para cualquier procesador. Para resolver este problema, Intel ha agregado comandos SSE 4, que le permiten reescribir datos sin un procesador. Desafortunadamente, no todas las bibliotecas tienen esto implementado. He probado las siguientes bibliotecas:

  1. Ffmpeg
  2. VLC
  3. OpenCV

VLC : funciona con cámaras IP a través de la decodificación de hardware (carga de procesador muy baja), se puede construir un reproductor de flujo RTSP primitivo en solo 10 líneas de código, pero recibir marcos decodificados en RAM requiere demasiado tiempo de procesador.

OpenCV - RTSP usa FFmpeg para trabajar con la transmisión, por lo que se decidió trabajar sin intermediarios, es decir usa la biblioteca FFmpeg. Además, FFmpeg, que se instala por defecto, está integrado en OpenCV sin decodificación de hardware.

FFmpeg - mostró buenos resultados, en mi opinión, funciona de manera estable. El único inconveniente no se implementa al trabajar con cámaras WEB para la versión X86 (X64 parece permitirle trabajar) en Windows.

La decodificación de video por hardware es fácil


De hecho, la decodificación de hardware utilizando la biblioteca FFmpeg no es más complicada que el software. La configuración del proyecto es la misma que para la implementación del software, el diagrama de bloques se mantuvo sin cambios.

Puede mostrar una lista de métodos de decodificación de hardware compatibles con FFmpeg.

fprintf(stderr, " %s", av_hwdevice_get_type_name(type)); 

Lo primero que debemos hacer es decirle a FFmpeg con qué decodificador de hardware desea decodificar el video. En mi caso, Windows10 + Intel Atom Z8350 solo deja DXVA2:

 type = av_hwdevice_find_type_by_name("dxva2"); 

Puede elegir CUDA, D3D11VA, QSV o VAAPI (solo Linux) como decodificador de hardware. En consecuencia, debe tener esta solución de hardware y FFmpeg debe construirse con su soporte.

Abre la transmisión de video:

 avformat_open_input(&input_ctx, filename, NULL, NULL; 

Obtenemos información sobre la transmisión de video:

 av_find_best_stream(input_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, &decoder, 0); 

Asignar memoria:

 frame = av_frame_alloc(); //       sw_frame = av_frame_alloc(); //        

Esta función sobrescribe el archivo decodificado en RAM:

 av_hwframe_transfer_data(sw_frame, frame, 0); 

Un poco sobre el formato NV12


Entonces, tenemos un marco en la estructura sw_frame. El marco recibido se almacena en formato NV12. Este formato fue inventado por Microsoft. Le permite almacenar información de píxeles en 12 bits. Donde 8 bits es la intensidad y 4 bits describen el color (o más bien, el color se describe inmediatamente para 4 píxeles adyacentes de 2x2). Además, sw_frame.data [0]: la intensidad se almacena y en sw_frame.data [1] se almacena el color. Para convertir de NV-12 a RGB, puede usar la siguiente función:

Traducción de C ++ de NV12 a RGB
 void SaveFrame(uint8_t * f1, uint8_t * f2, int iFrame) { FILE *pFile; char szFilename[32]; int x, i, j; // char buff[1920 * 1080 * 3]; uint8_t *buff = new uint8_t(1920*3*2); int u=0, v=0, y=0; // Open file sprintf(szFilename, "frame%d.ppm", iFrame); pFile = fopen(szFilename, "wb"); if (pFile == NULL) return; //    fprintf(pFile, "P6\n%d %d\n255\n", 1920, 1080); for (j = 0; j < 1080 / 2; j++) { for (i = 0; i < 1920; i +=2) { // 1  rgb y = *(f1 + j * 1920 * 2 + i); v = *(f2 + j * 1920 + i) - 128; u = *(f2 + j * 1920 + i + 1) - 128; x = round(y + 1.370705 * v); if (x < 0) x = 0; if (x > 255) x = 255; // if (j > 34) printf("%i, ",(j * 1920 * 2 + i) * 3); buff[i * 3 + 2] = x; x = round(y - 0.698001 * v - 0.337633 * u); if (x < 0) x = 0; if (x > 255) x = 255; buff[i * 3 + 1] = x; x = round(y + 1.732446 * u); if (x < 0) x = 0; if (x > 255) x = 255; buff[i * 3] = x; // 2  rgb y = *(f1 + j * 1920 * 2 + i + 1); x = y + 1.370705 * v; if (x < 0) x = 0; if (x > 255) x = 255; buff[i * 3 + 5] = x; x = y - 0.698001 * v - 0.337633 * u; if (x < 0) x = 0; if (x > 255) x = 255; buff[i * 3 + 4] = x; x = y + 1.732446 * u; if (x < 0) x = 0; if (x > 255) x = 255; buff[i * 3 + 3] = x; // 3  rgb y = *(f1 + j * 1920 * 2 + 1920 + i); x = y + 1.370705 * v; if (x < 0) x = 0; if (x > 255) x = 255; buff[(1920 + i) * 3 + 2] = x; x = y - 0.698001 * v - 0.337633 * u; if (x < 0) x = 0; if (x > 255) x = 255; buff[(1920 + i) * 3 + 1] = x; x = y + 1.732446 * u; if (x < 0) x = 0; if (x > 255) x = 255; buff[(1920 + i) * 3 + 0] = x; // 4  rgb y = *(f1 + j * 1920 * 2 + 1920 + i + 1); x = y + 1.370705 * v; if (x < 0) x = 0; if (x > 255) x = 255; buff[(1920 + i) * 3 + 5] = x; x = y - 0.698001 * v - 0.337633 * u; if (x < 0) x = 0; if (x > 255) x = 255; buff[(1920 + i) * 3 + 4] = x; x = y + 1.732446 * u; if (x < 0) x = 0; if (x > 255) x = 255; buff[(1920 + i) * 3 + 3] = x; // printf("%i, ", i); } // for i fwrite(buff, 1, 1920 * 3 * 2, pFile); printf("\n %i\n", j); } // for j // printf("Save4\n"); // Write pixel data // fwrite(buff, 1, 1920*1080*3, pFile); // Close file printf("close\n"); fclose(pFile); printf("exit\n"); delete buff; // return; } 


Aunque trabajar con NV12 le permite acelerar la implementación de procedimientos como el desenfoque, Retinex y la obtención de imágenes en escala de grises (simplemente descartando el color). En mis tareas, no traduzco el formato NV12 a RGB, ya que esto lleva más tiempo.

Y así aprendimos a decodificar archivos de video en hardware y mostrarlos en una ventana. Nos reunimos en formato NV12 y cómo convertirlo a RGB familiar.

Dll decodificación de hardware


FFmpeg emite cuadros después de 40 ms (a 25 cuadros por segundo). Como regla general, el procesamiento de un cuadro Full HD lleva mucho más tiempo. Esto requiere subprocesos múltiples para maximizar la carga de los 4 núcleos de procesador. En la práctica, comienzo 6 hilos una vez y ya no los elimino, lo que simplifica enormemente el trabajo y aumenta la fiabilidad del programa. El esquema de operación se muestra en la Fig. 1

imagen
Fig. 1 Esquema de construcción de un programa multiproceso con FFmpeg

Escribí mi decodificador como * .dll (FFmpegD.DLL) para incluirlo en mis proyectos. Esto le permite reducir el código del proyecto, lo que aumenta la comprensión del código e incluirlo en cualquier lenguaje de programación, hasta Assembler (verificado :)). Utilizándolo, escribiremos nuestro reproductor de transmisión RTSP desde la cámara IP.

Para comenzar a trabajar con una DLL, debe pasar un puntero a una matriz int [13], una MANIJA de un nuevo evento de llegada de cuadros, una MANIJA para comenzar a procesar un nuevo paquete de datos desde la cámara y un carácter de matriz de la cámara.

La estructura de la matriz se da en la tabla 1.

imagen

Antes de llamar, debe restablecer los números de trama 1-4.

La DLL tomará todos los pasos necesarios para inicializar FFmpeg y registrará punteros y números de cuadros. Después establece el evento "Nueva llegada de trama". Solo es necesario procesar las tramas entrantes y escribir 0 en lugar del número de trama (esto significa que la trama se ha procesado y ya no se usa).

A continuación encontrará un reproductor de ejemplo con código fuente. El ejemplo es ShowDib3 Charles Petzold.

Archivo con el proyecto
Archivo FFmpegD.dll

RESULTADOS: el detector de movimiento de hardware FFmpeg incluso en Intel Atom Z8350 decodifica h264 Full HD en tiempo real con hasta un 20% de carga del procesador con detector de movimiento conectado.


Ejemplo de funcionamiento del detector de movimiento en Intel ATOM Z8350. Los primeros 30 segundos son el cálculo del fondo. Después de eso, el detector de movimiento funciona por el método de restar el fondo.

PD: ¡ También puedes decodificar archivos de video (comprimido h.264)!

Referencias

  1. Información útil diversa sobre FFmpeg
  2. Información sobre el uso de las diversas bibliotecas proporcionadas por FFmpeg
  3. Información sobre formatos y conversión a RGB

Source: https://habr.com/ru/post/461735/


All Articles