Prática de decodificação de hardware FFmpeg DXVA2

Oi Este artigo é uma continuação do meu artigo do FFmpeg, começando com o Visual Studio. Aqui chegamos à decodificação de hardware do fluxo RTSP FULL HD. Eu direi antecipadamente que mesmo o Intel ATOM Z8350 pode lidar facilmente com essa tarefa.

Tarefa: decodificação de hardware e gravação de até 4 quadros na RAM para processamento paralelo subsequente (quatro núcleos de processador) de uma câmera RTSP h.264 IP. Eu exibo os quadros processados ​​usando as funções WinAPI. Como resultado, obtemos um sistema de alta velocidade para processamento por computador do fluxo RTSP em modo paralelo. Em seguida, você pode conectar os algoritmos de visão computacional para processar quadros em tempo real .

imagem

Entrada


Por que preciso de decodificação de hardware? Você deseja decodificar vídeo em tempo real com um processador fraco e barato ou deseja descarregar o processador o máximo possível, é hora de se familiarizar com a decodificação de hardware.

O DirectX Video Acceleration (DXVA) é uma API para usar a aceleração de hardware para acelerar o processamento de vídeo com GPUs. O DXVA 2.0 permite redirecionar mais operações para a GPU, incluindo operações de captura e processamento de vídeo.

Depois de escrever o artigo anterior, me fizeram algumas perguntas: “por que ele é usado FFmpeg?” Vou começar com os problemas. A principal dificuldade da decodificação de hardware é gravar o quadro decodificado na RAM. Para Full HD, isso é 1920 x 1080 x 3 = 6.220.800 bytes. Mesmo levando em consideração o fato de o quadro estar armazenado no formato NV12, também são muitos 1920 x 1080 x 1,5 = 3 110 400 bytes. Substituir 75 MB por segundo é uma tarefa séria para qualquer processador. Para resolver esse problema, a Intel adicionou comandos SSE 4, que permitem reescrever dados sem um processador. Infelizmente, nem todas as bibliotecas têm isso implementado. Eu testei as seguintes bibliotecas:

  1. Ffmpeg
  2. VLC
  3. Opencv

VLC - trabalha com câmeras IP através de decodificação de hardware (carga do processador muito baixa), um reprodutor de fluxo RTSP primitivo pode ser construído em apenas 10 linhas de código, mas receber quadros decodificados na RAM leva muito tempo do processador.

O OpenCV - RTSP usa o FFmpeg para trabalhar com o fluxo, por isso foi decidido trabalhar sem intermediários, ou seja, use a biblioteca FFmpeg. Além disso, o FFmpeg, instalado por padrão, é construído no OpenCV sem decodificação de hardware.

FFmpeg - mostrou bons resultados, na minha opinião, funciona de forma estável. A única desvantagem não é o trabalho implementado com câmeras WEB para a versão X86 (o X64 parece permitir que você trabalhe) no Windows.

A decodificação de vídeo por hardware é fácil


De fato, a decodificação de hardware usando a biblioteca FFmpeg não é mais complicada que o software. As configurações do projeto são as mesmas da implementação do software, o diagrama de blocos permaneceu inalterado.

Você pode exibir uma lista dos métodos de decodificação de hardware suportados pelo FFmpeg.

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

A primeira coisa que precisamos fazer é informar ao FFmpeg com qual decodificador de hardware você deseja decodificar o vídeo. No meu caso, o Windows 10 + Intel Atom Z8350 deixa apenas o DXVA2:

 type = av_hwdevice_find_type_by_name("dxva2"); 

Você pode escolher CUDA, D3D11VA, QSV ou VAAPI (apenas Linux) como decodificador de hardware. Portanto, você deve ter essa solução de hardware e o FFmpeg deve ser construído com seu suporte.

Abra o fluxo de vídeo:

 avformat_open_input(&input_ctx, filename, NULL, NULL; 

Obtemos informações sobre o fluxo de vídeo:

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

Alocar memória:

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

Esta função substitui o arquivo decodificado na RAM:

 av_hwframe_transfer_data(sw_frame, frame, 0); 

Um pouco sobre o formato NV12


Então, temos um quadro na estrutura sw_frame. O quadro recebido é armazenado no formato NV12. Este formato foi inventado pela Microsoft. Permite armazenar informações de pixel em 12 bits. Onde 8 bits é a intensidade e 4 bits descrevem a cor (ou melhor, a cor é descrita imediatamente para 4 pixels 2x2 adjacentes). Além disso, sw_frame.data [0] - a intensidade é armazenada, e em sw_frame.data [1] - a cor é armazenada. Para converter de NV-12 para RGB, você pode usar a seguinte função:

Tradução C ++ de NV12 para 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; } 


Embora o trabalho com o NV12 permita acelerar a implementação de procedimentos como desfoque, Retinex e obtenção de imagens em escala de cinza (apenas descartando a cor). Nas minhas tarefas, não traduzo o formato NV12 para RGB, pois isso leva tempo extra.

E assim aprendemos como decodificar arquivos de vídeo no hardware e exibi-los em uma janela. Nos conhecemos no formato NV12 e como convertê-lo em RGB familiar.

Decodificação de hardware DLL


O FFmpeg emite quadros após 40 ms (a 25 quadros por segundo). Como regra, o processamento de um quadro Full HD leva muito mais tempo. Isso requer multithreading para maximizar a carga de todos os 4 núcleos do processador. Na prática, inicio 6 threads uma vez e não os removo mais, o que simplifica bastante o trabalho e aumenta a confiabilidade do programa. O esquema de operação é mostrado na Fig. 1

imagem
Fig. 1 Esquema de construção de um programa multiencadeado com FFmpeg

Eu escrevi meu decodificador como * .dll (FFmpegD.DLL) para inclusão em meus projetos. Isso permite reduzir o código do projeto, o que aumenta a compreensão do código e o inclui em qualquer linguagem de programação, até Assembler (verificado :)). Usando-o, escreveremos nosso reprodutor de fluxo RTSP a partir da câmera IP.

Para começar a trabalhar com uma DLL, você precisa passar um ponteiro para uma matriz int [13], um HANDLE de um novo evento de chegada de quadro, um HANDLE para iniciar o processamento de um novo pacote de dados da câmera e um caractere da matriz.

A estrutura da matriz é fornecida na tabela 1.

imagem

Antes de ligar, você deve redefinir os números de quadro 1-4.

A DLL executará todas as etapas necessárias para inicializar o FFmpeg e registrará ponteiros e números de quadros. Depois de definir o evento "Chegada do novo quadro". Só é necessário processar os quadros recebidos e escrever 0 em vez do número do quadro (isso significa que o quadro foi processado e não é mais usado).

Abaixo, você encontrará um exemplo de player com código fonte. O exemplo é ShowDib3 Charles Petzold.

Arquivar com o projeto
Arquivo FFmpegD.dll

RESULTADOS: O detector de movimento FFmpeg, mesmo no Intel Atom Z8350, decodifica o h264 Full HD em tempo real, com o processador carregando até 20% com um detector de movimento conectado.


Exemplo de operação do detector de movimento no Intel ATOM Z8350. Os primeiros 30 segundos são o cálculo do plano de fundo. Depois disso, o detector de movimento funciona pelo método de subtrair o fundo.

PS Você também pode decodificar arquivos de vídeo (h.264 compactado) !!!

Referências:

  1. Informações úteis diversas sobre o FFmpeg
  2. Informações sobre o uso das várias bibliotecas fornecidas pelo FFmpeg
  3. Informações sobre formatos e conversão para RGB

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


All Articles