Como o codec de vídeo funciona. Parte 2. O que, por que, como

Parte I: Noções básicas de vídeo e imagem




História do Kodek

O que? Um codec de vídeo é um software / hardware que compacta e / ou descompacta o vídeo digital.

Para que? Apesar de certas limitações em termos de largura de banda,
e em termos de quantidade de espaço de armazenamento, o mercado exige cada vez mais vídeos de alta qualidade. Lembre-se de como, na última postagem, calculamos o mínimo necessário para 30 quadros por segundo, 24 bits por pixel, com uma resolução de 480x240? Recebeu 82,944 Mbps sem compactação. A compressão é a única maneira de transferir HD / FullHD / 4K para telas de TV e Internet. Como isso é alcançado? Agora vamos considerar brevemente os principais métodos.
EDISON Software - desenvolvimento web
A tradução foi feita com o apoio do software EDISON.

Estamos envolvidos na integração de sistemas de vigilância por vídeo , bem como no desenvolvimento de um microtomógrafo .

Codec vs Container


Um erro comum para iniciantes é confundir um codec de vídeo digital e um contêiner de vídeo digital. Um contêiner é um determinado formato. Um wrapper contendo metadados de vídeo (e possivelmente áudio). O vídeo compactado pode ser considerado como uma carga útil do contêiner.

Normalmente, uma extensão de arquivo de vídeo indica um tipo de contêiner. Por exemplo, o arquivo video.mp4 provavelmente é um contêiner MPEG-4 Parte 14 e o arquivo video.mkv provavelmente é uma boneca russa. Para ter total confiança no codec e no formato do contêiner, você pode usar o FFmpeg ou o MediaInfo .

Um pouco de história


Antes de chegarmos a Como? , vamos explorar um pouco da história para entender um pouco melhor alguns codecs antigos.

O codec de vídeo H.261 apareceu em 1990 (tecnicamente - em 1988) e foi criado para funcionar com uma taxa de transferência de dados de 64 Kbps. Ele já usou idéias como subamostragem de cores, macroblocos etc. Em 1995, foi publicado o padrão de codec de vídeo H.263 , desenvolvido até 2001.

Em 2003, a primeira versão do H.264 / AVC foi concluída. No mesmo ano, o TrueMotion lançou seu codec de vídeo gratuito que comprime o vídeo com perdas chamado VP3 . Em 2008, o Google comprou esta empresa, lançando o VP8 no mesmo ano. Em dezembro de 2012, o Google lançou o VP9 e é suportado em aproximadamente ¾ do mercado de navegadores (incluindo dispositivos móveis).

O AV1 é um novo codec de vídeo de código aberto gratuito desenvolvido pela Open Media Alliance ( AOMedia ), que inclui empresas conhecidas como Google, Mozilla, Microsoft, Amazon, Netflix, AMD, ARM, NVidia, Intel e Cisco. . A primeira versão do codec 0.1.0 foi publicada em 7 de abril de 2016.

Nascimento do AV1


No início de 2015, o Google trabalhou no VP10 , o Xiph (que pertence à Mozilla) trabalhou no Daala e a Cisco criou seu codec de vídeo gratuito chamado Thor .

Em seguida, a MPEG LA anunciou pela primeira vez limites anuais para HEVC ( H.265 ) e uma taxa 8 vezes maior que para H.264, mas eles logo mudaram as regras novamente:

sem limite anual,
taxa de conteúdo (0,5% da receita) e
os custos unitários são cerca de 10 vezes superiores aos do H.264.

A Open Media Alliance foi criada por empresas de vários campos: fabricantes de equipamentos (Intel, AMD, ARM, Nvidia, Cisco), provedores de conteúdo (Google, Netflix, Amazon), fabricantes de navegadores (Google, Mozilla) e outros.

As empresas tinham um objetivo comum - um codec de vídeo sem royalties. Depois vem o AV1 com uma licença de patente muito mais simples. Timothy B. Terriberry fez uma apresentação impressionante, que se tornou a fonte do conceito atual do AV1 e seu modelo de licença.

Você ficará surpreso ao saber que pode analisar o codec AV1 por meio de um navegador (os interessados ​​podem ir ao aomanalyzer.org ).

imagem

Codec universal


Vamos analisar os mecanismos básicos subjacentes ao codec de vídeo universal. A maioria desses conceitos é útil e usada em codecs modernos, como VP9 , AV1 e HEVC . Eu aviso que muitas coisas explicadas serão simplificadas. Exemplos do mundo real às vezes serão usados ​​(como é o caso do H.264) para demonstrar a tecnologia.

1º passo - dividir a imagem


O primeiro passo é dividir o quadro em várias seções, subseções e muito mais.

imagem

Para que? Existem muitas razões. Quando dividimos a imagem, podemos prever com mais precisão o vetor de movimento usando pequenas seções para pequenas partes móveis. Quanto a um plano de fundo estático, você pode restringir-se a seções maiores.

Normalmente, os codecs organizam essas seções em seções (ou fragmentos), macroblocos (ou blocos de uma árvore de codificação) e muitas subseções. O tamanho máximo dessas partições varia, o HEVC define 64x64, enquanto o AVC usa 16x16, e as subseções podem ser divididas em 4x4.

Lembre-se das variedades de quadros do último artigo ?! O mesmo pode ser aplicado aos blocos, para que possamos ter um fragmento I, bloco B, macrobloco P, etc.

Para aqueles que querem praticar, veja como a imagem será dividida em seções e subseções. Para fazer isso, você pode usar o Intel Video Pro Analyzer já mencionado no artigo anterior (o que é pago, mas com uma versão de avaliação gratuita, que tem um limite nos 10 primeiros quadros). As seções do VP9 são analisadas aqui:

imagem

2º passo - previsão


Assim que tivermos seções, podemos fazer previsões astrológicas sobre elas. Para a previsão INTER, é necessário transferir vetores de movimento e o restante, e para a previsão INTRA, a direção da previsão e o restante são transmitidos.

3º passo - conversão


Depois de obtermos o bloco residual (a seção prevista → a seção real), é possível transformá-lo de forma a saber quais pixels podem ser descartados, mantendo a qualidade geral. Existem algumas transformações que fornecem comportamento preciso.

Embora existam outros métodos, vamos considerar mais detalhadamente a transformação discreta de cosseno ( DCT - da transformação discreta de cosseno ). Principais recursos do DCT:

  • Converte blocos de pixels em blocos de tamanho igual de coeficientes de frequência.
  • Sela a energia, ajudando a eliminar a redundância espacial.
  • Fornece reversibilidade.

2 de fevereiro de 2017 Sintra R.J. (Cintra, RJ) e Bayer F.M. (Bayer FM) publicou um artigo sobre conversão tipo DCT para compactação de imagem, exigindo apenas 14 adições.

Não se preocupe se você não entender os benefícios de cada item. Agora, com exemplos concretos, verificaremos seu valor real.

Vamos pegar um bloco de 8x8 pixels como este:

imagem

Este bloco é renderizado na seguinte imagem 8 por 8 pixels:

imagem

Aplique o DCT a esse bloco de pixels e obtenha um bloco de coeficientes de tamanho 8x8:

imagem

E se renderizarmos esse bloco de coeficientes, obteremos a seguinte imagem:

imagem

Como você pode ver, isso não é como a imagem original. Você pode perceber que o primeiro coeficiente é muito diferente de todos os outros. Esse primeiro coeficiente é conhecido como coeficiente DC, representando todas as amostras na matriz de entrada, algo semelhante ao valor médio.

Esse bloco de coeficientes tem uma propriedade interessante: separa os componentes de alta frequência dos de baixa frequência.

imagem

Na imagem, a maior parte da energia está concentrada em frequências mais baixas; portanto, se você converter a imagem em seus componentes de frequência e descartar os coeficientes de frequência mais altos, poderá reduzir a quantidade de dados necessários para descrever a imagem sem sacrificar muito a qualidade da imagem.
Frequência significa a rapidez com que o sinal muda.
Vamos tentar aplicar o conhecimento adquirido no exemplo de teste, convertendo a imagem original em sua frequência (bloco de coeficientes) usando o DCT e descartando alguns dos coeficientes menos importantes.

Primeiro, converta-o para o domínio da frequência.

imagem

Em seguida, descartamos parte (67%) dos coeficientes, principalmente o lado inferior direito.

imagem

Por fim, restauramos a imagem desse bloco de coeficientes descartados (lembre-se de que deve ser reversível) e comparamos com o original.

imagem

Vemos que ela se assemelha à imagem original, mas há muitas diferenças em relação à original. Jogamos 67,1875% e ainda temos algo que se assemelha à fonte original. Você poderia deliberadamente descartar os coeficientes para obter uma imagem de qualidade ainda melhor, mas este é o próximo tópico.

Cada coeficiente é gerado usando todos os pixels.



Importante: cada coeficiente não é exibido diretamente em um pixel, mas é uma soma ponderada de todos os pixels. Este gráfico incrível mostra como o primeiro e o segundo coeficientes são calculados usando pesos exclusivos para cada índice.

imagem

Você também pode tentar visualizar o DCT observando imagens simples com base nele. Por exemplo, aqui está o símbolo A gerado usando cada peso do coeficiente:

imagem


4º passo - quantização


Depois de jogarmos alguns coeficientes na etapa anterior, na última etapa (transformação), produzimos uma forma especial de quantização. Neste ponto, é permitido perder informações. Ou, mais simplesmente, quantizaremos os coeficientes para obter a compactação.

Como um bloco de coeficientes pode ser quantizado? Um dos métodos mais simples será a quantização uniforme; quando pegamos um bloco, dividimos por um valor (por 10) e arredondamos o que aconteceu.

imagem

Podemos reverter esse bloco de coeficientes? Sim, podemos multiplicar pelo mesmo valor pelo qual dividimos.

imagem

Essa abordagem não é a melhor, pois não leva em consideração a importância de cada coeficiente. Pode-se usar a matriz quantizadora em vez de um único valor, e essa matriz pode usar a propriedade DCT, quantizando a maior parte do canto inferior direito e uma minoria do canto superior esquerdo.

5 passo - codificação de entropia


Depois de quantizarmos os dados (blocos de imagens, fragmentos, quadros), ainda podemos compactá-los sem perda. Existem muitas maneiras algorítmicas de compactar dados. Vamos nos familiarizar brevemente com alguns deles, para uma compreensão mais profunda, você pode ler o livro “ Entendendo a Compactação: Compactação de Dados para Desenvolvedores Modernos ” (“ Entendendo a Compactação: Compactação de Dados para Desenvolvedores Modernos ”).

Codificação de vídeo com VLC


Suponha que tenhamos um fluxo de caracteres: a , e , r e t . A probabilidade (variando de 0 a 1) de quantas vezes cada símbolo ocorre no fluxo é apresentada nesta tabela.
aert
Probabilidade0,30,30,20,2
Podemos atribuir códigos binários únicos (de preferência pequenos) aos códigos mais prováveis ​​e maiores, menos prováveis.
aert
Probabilidade0,30,30,20,2
Código binário0 0101101110
Comprimimos o fluxo, assumindo que, no final, gastamos 8 bits para cada caractere. Sem compressão em um personagem, seriam necessários 24 bits. Se cada caractere for substituído por seu código, obteremos economia!

O primeiro passo é codificar o caractere e , que é 10, e o segundo caractere é a , que é adicionado (não matematicamente): [10] [0] e, finalmente, o terceiro caractere t , que torna nosso bitstream final comprimido igual [10] [0] [1110] ou 1001110 , que requer apenas 7 bits (3,4 vezes menos espaço que o original).

Observe que cada código deve ser um código exclusivo com um prefixo. O algoritmo de Huffman ajudará a encontrar esses números. Embora esse método não exista falhas, existem codecs de vídeo que ainda oferecem esse método algorítmico para compactação.

Tanto o codificador quanto o decodificador devem ter acesso à tabela de símbolos com seus códigos binários. Portanto, também é necessário enviar uma tabela na entrada.

Codificação aritmética


Suponha que tenhamos um fluxo de caracteres: a , e , r , se sua probabilidade é representada por esta tabela.
aerst
Probabilidade0,30,30,150,050,2
Com esta tabela, construímos intervalos contendo todos os caracteres possíveis, classificados pelo maior número.

imagem

Agora vamos codificar um fluxo de três caracteres: comer .

Primeiro, selecione o primeiro caractere e , que está no sub-intervalo de 0,3 a 0,6 (não incluindo). Pegamos esse subintervalo e o dividimos novamente nas mesmas proporções de antes, mas já para esse novo intervalo.

imagem

Vamos continuar a codificar nosso fluxo de comer . Agora, pegamos o segundo caractere a , que está no novo subintervalo de 0,3 a 0,39, e depois pegamos nosso último caractere te , repetindo o mesmo processo novamente, obtemos a última sub-banda de 0,354 a 0,372.

imagem

Só precisamos selecionar um número no último sub-intervalo de 0,354 a 0,372. Vamos escolher 0,36 (mas você pode escolher qualquer outro número neste sub-intervalo). Somente com esse número podemos restaurar nosso fluxo original. É como se estivéssemos desenhando uma linha dentro de intervalos para codificar nosso fluxo.

imagem

A operação reversa (isto é, decodificação ) é igualmente simples: com nosso número 0,36 e nosso intervalo inicial, podemos iniciar o mesmo processo. Mas agora, usando esse número, revelamos o fluxo codificado usando esse número.

Com o primeiro intervalo, notamos que nosso número corresponde a uma fatia, portanto, esse é o nosso primeiro caractere. Agora, novamente, compartilhamos essa sub-banda, executando o mesmo processo de antes. Aqui você pode ver que 0,36 corresponde ao caractere a e, após repetir o processo, chegamos ao último caractere t (formando nosso fluxo original codificado eat ).

Tanto o codificador quanto o decodificador devem ter uma tabela de probabilidades de símbolos; portanto, é necessário enviá-lo nos dados de entrada.

Muito elegante, não é? Alguém que veio com essa solução foi muito esperto. Alguns codecs de vídeo usam essa técnica (ou, em qualquer caso, oferecem-na como uma opção).

A idéia é comprimir um fluxo de bits quantificado sem perdas. Certamente neste artigo não há toneladas de detalhes, razões, compromissos, etc. Mas você, se você é um desenvolvedor, deve saber mais. Novos codecs estão tentando usar algoritmos diferentes de codificação de entropia, como o ANS .

6 etapas - formato bitstream


Depois de fazer tudo isso, resta descompactar os quadros compactados no contexto das etapas executadas. O decodificador deve ser informado explicitamente das decisões tomadas pelo codificador. O decodificador deve receber todas as informações necessárias: profundidade de bits, espaço de cores, resolução, informações sobre previsões (vetores de movimento, predição INTER direcional), perfil, nível, taxa de quadros, tipo de quadro, número de quadros e muito mais.

Vamos dar uma olhada no fluxo de bits H.264 . Nosso primeiro passo é criar um fluxo de bits H.264 mínimo (o FFmpeg, por padrão, adiciona todos os parâmetros de codificação, como o SEI NAL - um pouco mais adiante, descobriremos o que é). Podemos fazer isso usando nosso próprio repositório e o FFmpeg.

./s/ffmpeg -i /files/i/minimal.png -pix_fmt yuv420p /files/v/minimal_yuv420.h264

Este comando irá gerar um fluxo de bits H.264 bruto com um quadro, resolução 64x64, com o espaço de cores YUV420 . A imagem a seguir é usada como moldura.

imagem

H.264 Bitstream


O padrão AVC ( H.264 ) define que as informações serão enviadas em quadros de macro (no entendimento da rede) chamados NAL (esse é o nível de abstração da rede). O principal objetivo do NAL é fornecer uma apresentação de vídeo "amigável à rede". Esse padrão deve funcionar em TVs (com base em fluxos), na Internet (com base em pacotes).

imagem

Há um marcador de sincronização para definir os limites dos elementos NAL. Cada marcador de sincronização contém o valor 0x00 0x00 0x01, com exceção do primeiro, que é 0x00 0x00 0x00 0x01. Se executarmos o hexdump para o fluxo de bits H.264 gerado, identificaremos pelo menos três padrões NAL no início do arquivo.

imagem

Como afirmado, o decodificador deve conhecer não apenas os dados da imagem, mas também os detalhes do vídeo, quadro, cor, parâmetros usados ​​e muito mais. O primeiro byte de cada NAL define sua categoria e tipo.
Identificador de tipo NALDescrição do produto
0 0Tipo desconhecido
1Fragmento de imagem codificada sem IDR
2Seção de dados da fatia codificada A
3Dados de fatia codificada Seção B
4Seção de dados de fatia codificada em C
5Fragmento IDR codificado de uma imagem IDR
6Informações adicionais sobre a extensão SEI
7Conjunto de parâmetros de sequência SPS
8Conjunto de parâmetros de imagem PPS
9Delimitador de acesso
10Fim da sequência
11Fim do fluxo
......
Normalmente, o primeiro fluxo de bits NAL é o SPS . Esse tipo de NAL é responsável por relatar variáveis ​​de codificação comuns, como perfil, nível, resolução e muito mais.

Se pularmos o primeiro token de sincronização, podemos decodificar o primeiro byte para descobrir que tipo de NAL é o primeiro.

Por exemplo, o primeiro byte após o marcador de sincronização é 01100111 , onde o primeiro bit ( 0 ) está no campo f orbidden_zero_bit . Os próximos 2 bits ( 11 ) nos dizem o campo nal_ref_idc, que indica se esse NAL é um campo de referência ou não. E os 5 bits restantes ( 00111 ) nos informam o campo nal_unit_type; nesse caso, é o bloco SPS ( 7 ) NAL.

O segundo byte ( binário = 01100100 , hex = 0x64 , dec = 100 ) no SPS NAL é o campo profile_idc, que mostra o perfil que o codificador usou. Nesse caso, um perfil alto limitado foi usado (isto é, um perfil alto sem suporte para um segmento B bidirecional).

imagem

Se nos familiarizarmos com a especificação do fluxo de bits H.264 para o SPS NAL, encontraremos muitos valores para o nome do parâmetro, categoria e descrição. Por exemplo, vejamos os campos pic_width_in_mbs_minus_1 e pic_height_in_map_units_minus_1 .
Nome do ParâmetroCategoriaDescrição do produto
pic_width_in_mbs_minus_10 0ue (v)
pic_height_in_map_units_minus_10 0ue (v)
Se realizarmos algumas operações matemáticas com os valores desses campos, obteremos permissão. Você pode imaginar 1920 x 1080 usando pic_width_in_mbs_minus_1 com um valor de 119 ((119 + 1) * macroblock_size = 120 * 16 = 1920) . Mais uma vez, economizando espaço, em vez de codificar 1920, eles o fizeram com 119.

Se continuarmos a verificar o vídeo criado em formato binário (por exemplo: xxd -b -c 11 v / minimal_yuv420.h264 ), poderemos ir para o último NAL, que é o próprio quadro.

imagem

Aqui, vemos seus primeiros valores de 6 bytes: 01100101 10001000 10000100 00000000 00100001 11111111 . Como se sabe que o primeiro byte indica o tipo de NAL, neste caso ( 00101 ), este é um fragmento de IDR (5) e, em seguida, será possível estudá-lo mais:

imagem

Usando as informações de especificação, será possível decodificar o tipo de fragmento ( slice_type ) e o número do quadro ( frame_num ) entre outros campos importantes.

Para obter os valores de alguns campos ( ue ( v ), eu ( v ), se ( v ) ou te ( v )), precisamos decodificar o fragmento usando um decodificador especial baseado no código exponencial de Golomb . Este método é muito eficaz para codificar valores de variáveis, especialmente quando existem muitos valores padrão.

Os valores slice_type e frame_num deste vídeo são 7 (fragmento I) e 0 (primeiro quadro).

O fluxo de bits pode ser considerado como um protocolo. Se você quiser saber mais sobre o fluxo de bits, consulte a especificação ITU H.264 . Aqui está uma macro mostrando onde estão os dados da imagem ( YUV na forma compactada).

imagem

Você pode explorar outros fluxos de bits, como VP9 , H.265 ( HEVC ) ou até mesmo nosso novo melhor fluxo de bits AV1 . Eles são todos iguais? Não, mas lidar com pelo menos um deles é muito mais fácil de entender o resto.

Quer praticar? Explore o fluxo de bits H.264


Você pode gerar vídeo de quadro único e usar o MediaInfo para examinar o fluxo de bits H.264 . De fato, nada impede que você veja o código-fonte que analisa o fluxo de bits H.264 ( AVC ).

imagem

Para praticar, você pode usar o Intel Video Pro Analyzer (eu já disse que o programa é pago, mas existe uma versão de avaliação gratuita com um limite de 10 quadros?).

imagem

Revisão


Observe que muitos codecs modernos usam o mesmo modelo que acabaram de aprender. Aqui, vamos dar uma olhada no diagrama de blocos do codec de vídeo Thor . Ele contém todas as etapas que tomamos. O objetivo deste post é que você entenda pelo menos melhor as inovações e a documentação nessa área.

imagem

Anteriormente, estimava-se que 139 GB de espaço em disco seriam necessários para armazenar um arquivo de vídeo com duração de uma hora com qualidade 720p e 30 fps. Se você usar os métodos discutidos neste artigo (previsões internas e entre quadros, conversão, quantização, codificação de entropia etc.), poderá conseguir (assumindo que gastamos 0,031 bits por pixel), o vídeo é de qualidade bastante satisfatória, o que ocupa apenas 367,82 MB, não 139 GB de memória.

Como o H.265 alcança uma taxa de compressão melhor que o H.264?


Agora que você sabe mais sobre como os codecs funcionam, é mais fácil entender como os novos codecs podem fornecer uma resolução mais alta com menos bits.

Ao comparar o AVC e o HEVC , você não deve esquecer que quase sempre é uma opção entre uma carga de CPU mais alta e uma taxa de compactação.

O HEVC tem mais opções para seções (e subseções) que o AVC , mais instruções para previsão interna, codificação de entropia aprimorada e muito mais. Todas essas melhorias tornaram o H.265 capaz de compactar 50% mais que o H.264 .

imagem



Parte I: Noções básicas de vídeo e imagem






Leia também o blog
Empresa EDISON:


20 bibliotecas para
aplicação iOS espetacular

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


All Articles