Cómo funciona el códec de video. Parte 2. Qué, por qué, cómo

Parte I: Conceptos básicos de video e imagen




La historia de Kodek

Que? Un códec de video es una pieza de software / hardware que comprime y / o descomprime video digital.

Para que? A pesar de ciertas limitaciones en términos de ancho de banda,
y en términos de la cantidad de espacio de almacenamiento, el mercado requiere más y más videos de alta calidad. ¿Recuerdas cómo en la última publicación calculamos el mínimo necesario para 30 fotogramas por segundo, 24 bits por píxel, con una resolución de 480x240? Recibió 82.944 Mbps sin compresión. La compresión es la única forma de transferir HD / FullHD / 4K a pantallas de TV e Internet. ¿Cómo se logra esto? Ahora consideraremos brevemente los métodos principales.
Software EDISON - desarrollo web
La traducción se realizó con el apoyo del software EDISON.

Nos dedicamos a la integración de sistemas de videovigilancia , así como al desarrollo de una microtomografía .

Códec vs Contenedor


Un error común de los novatos es confundir un códec de video digital y un contenedor de video digital. Un contenedor tiene cierto formato. Un contenedor que contiene metadatos de video (y posiblemente audio). El video comprimido se puede considerar como una carga útil de contenedor.

Por lo general, una extensión de archivo de video indica un tipo de contenedor. Por ejemplo, el archivo video.mp4 es probablemente un contenedor MPEG-4 Parte 14 , y el archivo llamado video.mkv es probablemente una muñeca rusa. Para tener plena confianza en el códec y el formato del contenedor, puede usar FFmpeg o MediaInfo .

Un poco de historia


Antes de llegar a ¿Cómo? , profundicemos un poco en la historia para comprender un poco mejor algunos códecs antiguos.

El códec de video H.261 apareció en 1990 (técnicamente, en 1988) y fue creado para trabajar con una velocidad de transferencia de datos de 64 Kbps. Ya ha utilizado ideas como submuestreo de color, macrobloques, etc. En 1995, se publicó el estándar de códec de video H.263 , que se desarrolló hasta 2001.

En 2003, se completó la primera versión de H.264 / AVC . En el mismo año, TrueMotion lanzó su códec de video gratuito que comprime el video con pérdida llamado VP3 . En 2008, Google compró esta compañía, lanzando VP8 en el mismo año. En diciembre de 2012, Google lanzó VP9 , y es compatible en aproximadamente ¾ del mercado de navegadores (incluidos los dispositivos móviles).

AV1 es un nuevo códec de video de código abierto gratuito desarrollado por Open Media Alliance ( AOMedia ), que incluye compañías conocidas como Google, Mozilla, Microsoft, Amazon, Netflix, AMD, ARM, NVidia, Intel y Cisco . La primera versión del códec 0.1.0 se publicó el 7 de abril de 2016.

Nacimiento de AV1


A principios de 2015, Google trabajó en VP10 , Xiph (que pertenece a Mozilla) trabajó en Daala y Cisco creó su códec de video gratuito llamado Thor .

Luego, MPEG LA anunció por primera vez los límites anuales para HEVC ( H.265 ) y una tarifa 8 veces mayor que para H.264, pero pronto cambiaron las reglas nuevamente:

sin límite anual,
tarifa de contenido (0.5% de los ingresos) y
los costos unitarios son aproximadamente 10 veces más altos que para H.264.

Open Media Alliance fue creada por empresas de diversos campos: fabricantes de equipos (Intel, AMD, ARM, Nvidia, Cisco), proveedores de contenido (Google, Netflix, Amazon), fabricantes de navegadores (Google, Mozilla) y otros.

Las compañías tenían un objetivo común: un códec de video sin regalías. Luego viene AV1 con una licencia de patente mucho más simple. Timothy B. Terriberry hizo una presentación sorprendente, que se convirtió en la fuente del concepto actual de AV1 y su modelo de licencia.

Te sorprenderá saber que puedes analizar el códec AV1 a través de un navegador (los interesados ​​pueden ir a aomanalyzer.org ).

imagen

Códec universal


Analicemos los mecanismos básicos que subyacen al códec de video universal. La mayoría de estos conceptos son útiles y se utilizan en códecs modernos como VP9 , AV1 y HEVC . Te advierto que muchas cosas explicadas se simplificarán. A veces se usarán ejemplos del mundo real (como es el caso con H.264) para demostrar la tecnología.

1er paso: dividir la imagen


El primer paso es dividir el marco en varias secciones, subsecciones y más.

imagen

Para que? Hay muchas razones Cuando dividimos la imagen, podemos predecir con mayor precisión el vector de movimiento utilizando pequeñas secciones para pequeñas partes móviles. Mientras que para un fondo estático, puede restringirse a secciones más grandes.

Normalmente, los códecs organizan estas secciones en secciones (o fragmentos), macrobloques (o bloques de un árbol de codificación) y muchas subsecciones. El tamaño máximo de estas particiones varía, HEVC establece 64x64, mientras que AVC usa 16x16, y las subsecciones se pueden dividir hasta 4x4.

¿Recuerdas las variedades de marcos del último artículo? Lo mismo se puede aplicar a los bloques, por lo tanto, podemos tener un fragmento I, un bloque B, un macrobloque P, etc.

Para aquellos que quieran practicar, miren cómo se dividirá la imagen en secciones y subsecciones. Para hacer esto, puede usar el analizador Intel Video Pro ya mencionado en el artículo anterior (el que se paga, pero con una versión de prueba gratuita, que tiene un límite en los primeros 10 cuadros). Las secciones de VP9 se analizan aquí:

imagen

2do paso - pronóstico


Tan pronto como tengamos secciones, podemos hacer pronósticos astrológicos sobre ellas. Para la predicción INTER, es necesario transferir los vectores de movimiento y el resto, y para la predicción INTRA, se transmiten la dirección del pronóstico y el resto.

3er paso - conversión


Después de obtener el bloque residual (la sección predicha → la sección real), es posible transformarlo de tal manera que sepa qué píxeles se pueden descartar, manteniendo la calidad general. Hay algunas transformaciones que proporcionan un comportamiento preciso.

Aunque existen otros métodos, consideremos con más detalle la transformada discreta del coseno ( DCT - de la transformada discreta del coseno ). Características clave de DCT:

  • Convierte bloques de píxeles en bloques de coeficientes de frecuencia de igual tamaño.
  • Sella la potencia, ayudando a eliminar la redundancia espacial.
  • Proporciona reversibilidad.

2 de febrero de 2017 Sintra R.J. (Cintra, RJ) y Bayer F.M. (Bayer FM) publicó un artículo sobre conversión tipo DCT para la compresión de imágenes, que requiere solo 14 adiciones.

No se preocupe si no comprende los beneficios de cada artículo. Ahora, con ejemplos concretos, verificaremos su valor real.

Tomemos un bloque de 8x8 píxeles como este:

imagen

Este bloque se representa en la siguiente imagen de 8 por 8 píxeles:

imagen

Aplique DCT a este bloque de píxeles y obtenga un bloque de coeficientes de tamaño 8x8:

imagen

Y si representamos este bloque de coeficientes, obtenemos la siguiente imagen:

imagen

Como puede ver, esto no es como la imagen original. Puede notar que el primer coeficiente es muy diferente de todos los demás. Este primer coeficiente se conoce como un coeficiente DC que representa todas las muestras en la matriz de entrada, algo similar al valor promedio.

Este bloque de coeficientes tiene una propiedad interesante: separa los componentes de alta frecuencia de los de baja frecuencia.

imagen

En la imagen, la mayor parte de la potencia se concentra a frecuencias más bajas, por lo tanto, si convierte la imagen en sus componentes de frecuencia y descarta los coeficientes de frecuencia más altos, puede reducir la cantidad de datos necesarios para describir la imagen sin sacrificar demasiado la calidad de la imagen.
Frecuencia significa qué tan rápido cambia la señal.
Intentemos aplicar el conocimiento adquirido en el ejemplo de prueba convirtiendo la imagen original a su frecuencia (bloque de coeficientes) usando DCT, y luego descartando algunos de los coeficientes menos importantes.

Primero, conviértalo al dominio de frecuencia.

imagen

A continuación, descartamos parte (67%) de los coeficientes, principalmente el lado inferior derecho.

imagen

Finalmente, restauramos la imagen de este bloque de coeficientes descartado (recuerde, debe ser reversible) y lo comparamos con el original.

imagen

Vemos que se parece a la imagen original, pero hay muchas diferencias con respecto a la original. Lanzamos 67.1875% y todavía obtuvimos algo que se parece a la fuente original. Podrías descartar más deliberadamente los coeficientes para obtener una imagen de mejor calidad, pero este es el siguiente tema.

Cada coeficiente se genera utilizando todos los píxeles.



Importante: cada coeficiente no se muestra directamente en un píxel, sino que es una suma ponderada de todos los píxeles. Este sorprendente gráfico muestra cómo se calculan los coeficientes primero y segundo utilizando pesos únicos para cada índice.

imagen

También puede intentar visualizar DCT mirando imágenes simples basadas en él. Por ejemplo, aquí está el símbolo A generado usando cada peso de coeficiente:

imagen


4to paso - cuantización


Después de arrojar algunos coeficientes en el paso anterior, en el último paso (transformación), producimos una forma especial de cuantización. En este punto, está permitido perder información. O, más simplemente, cuantificaremos los coeficientes para lograr la compresión.

¿Cómo se puede cuantificar un bloque de coeficientes? Uno de los métodos más simples será la cuantización uniforme, cuando tomamos un bloque, lo dividimos por un valor (por 10) y redondeamos lo que sucedió.

imagen

¿Podemos revertir este bloque de coeficientes? Sí, podemos multiplicar por el mismo valor por el que dividimos.

imagen

Este enfoque no es el mejor, ya que no tiene en cuenta la importancia de cada coeficiente. Se podría usar la matriz cuantificadora en lugar de un solo valor, y esta matriz podría usar la propiedad DCT, cuantificando la mayor parte de la parte inferior derecha y una minoría de la parte superior izquierda.

5 pasos - codificación de entropía


Después de cuantificar los datos (bloques de imagen, fragmentos, cuadros), aún podemos comprimirlos sin pérdida. Hay muchas formas algorítmicas para comprimir datos. Vamos a conocer brevemente algunos de ellos, para una comprensión más profunda, puede leer el libro " Comprender la compresión: compresión de datos para desarrolladores modernos " (" Comprender la compresión: compresión de datos para desarrolladores modernos ").

Codificación de video con VLC


Supongamos que tenemos una secuencia de caracteres: a , e , r y t . En esta tabla se presenta la probabilidad (que varía de 0 a 1) de la frecuencia con la que cada símbolo ocurre en la secuencia.
unert
Probabilidad0,30,30.2 0.20.2 0.2
Podemos asignar códigos binarios únicos (preferiblemente pequeños) a los más probables, y códigos más grandes menos probables.
unert
Probabilidad0,30,30.2 0.20.2 0.2
Código binario0 0101101110
Comprimimos la secuencia, suponiendo que al final gastemos 8 bits para cada personaje. Sin compresión en un personaje, se necesitarían 24 bits. Si cada personaje se reemplaza con su código, ¡obtenemos ahorros!

El primer paso es codificar el carácter e , que es 10, y el segundo carácter es a , que se agrega (no matemáticamente): [10] [0], y finalmente, el tercer carácter t , que hace que nuestro flujo de bits comprimido final sea igual [10] [0] [1110] o 1001110 , que requiere solo 7 bits (3,4 veces menos espacio que en el original).

Tenga en cuenta que cada código debe ser un código único con un prefijo. El algoritmo de Huffman ayudará a encontrar estos números. Aunque este método no está exento de fallas, hay códecs de video que aún ofrecen este método algorítmico para la compresión.

Tanto el codificador como el decodificador deben tener acceso a la tabla de símbolos con sus códigos binarios. Por lo tanto, también es necesario enviar una tabla en la entrada.

Codificación aritmética


Supongamos que tenemos una secuencia de caracteres: a , e , r , syt , y su probabilidad está representada por esta tabla.
unerst
Probabilidad0,30,30,150,050.2 0.2
Con esta tabla, construimos rangos que contienen todos los caracteres posibles, ordenados por el número más grande.

imagen

Ahora codifiquemos una secuencia de tres caracteres: comer .

Primero, seleccione el primer carácter e , que está en el sub-rango de 0.3 a 0.6 (sin incluir). Tomamos este subrango y lo dividimos nuevamente en las mismas proporciones que antes, pero ya para este nuevo rango.

imagen

Sigamos codificando nuestra secuencia de comer . Ahora tomamos el segundo símbolo a , que está en el nuevo subrango de 0.3 a 0.39, y luego tomamos nuestro último símbolo t y, repitiendo el mismo proceso nuevamente, obtenemos el último subrango de 0.354 a 0.372.

imagen

Solo necesitamos seleccionar un número en el último subrango de 0.354 a 0.372. Elijamos 0.36 (pero puede elegir cualquier otro número en este subrango). Solo con este número podemos restaurar nuestro flujo original. Es como si estuviéramos dibujando una línea dentro de los rangos para codificar nuestra secuencia.

imagen

La operación inversa (es decir, la decodificación ) es igual de simple: con nuestro número 0.36 y nuestro rango inicial, podemos comenzar el mismo proceso. Pero ahora, usando este número, revelamos la secuencia codificada usando este número.

Con el primer rango, notamos que nuestro número corresponde a un segmento, por lo tanto, este es nuestro primer carácter. Ahora, de nuevo, compartimos esta subbanda, realizando el mismo proceso que antes. Aquí puede ver que 0.36 corresponde al carácter a , y después de repetir el proceso, llegamos al último carácter t (formando nuestro flujo codificado original como comer ).

Tanto el codificador como el decodificador deben tener una tabla de probabilidades de símbolos, por lo tanto, es necesario enviarlo en los datos de entrada.

Bastante elegante, ¿no es así? Alguien a quien se le ocurrió esta solución fue muy inteligente. Algunos códecs de video usan esta técnica (o, en cualquier caso, la ofrecen como una opción).

La idea es comprimir un flujo de bits cuantificado sin pérdidas. Seguramente en este artículo no hay toneladas de detalles, razones, compromisos, etc. Pero usted, si es desarrollador, debería saber más. Los nuevos códecs están intentando utilizar diferentes algoritmos de codificación de entropía, como ANS .

6 pasos - formato de flujo de bits


Después de hacer todo esto, queda por desempaquetar los cuadros comprimidos en el contexto de los pasos tomados. El decodificador debe ser informado explícitamente de las decisiones tomadas por el codificador. Se debe proporcionar al decodificador toda la información necesaria: profundidad de bits, espacio de color, resolución, información de pronóstico (vectores de movimiento, predicción INTER direccional), perfil, nivel, velocidad de cuadros, tipo de cuadro, número de cuadro y mucho más.

Echaremos un vistazo al flujo de bits H.264 . Nuestro primer paso es crear un flujo de bits H.264 mínimo (FFmpeg por defecto agrega todos los parámetros de codificación, como SEI NAL ; un poco más, descubriremos de qué se trata). Podemos hacer esto usando nuestro propio repositorio y FFmpeg.

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

Este comando generará un flujo de bits H.264 sin formato con un cuadro, resolución 64x64, con el espacio de color YUV420 . La siguiente imagen se usa como marco.

imagen

H.264 Bitstream


El estándar AVC ( H.264 ) define que la información se enviará en marcos macro (en la comprensión de la red) llamada NAL (este es un nivel de abstracción de red). El objetivo principal de NAL es proporcionar una presentación de video "amigable para la red". Este estándar debería funcionar en televisores (basados ​​en transmisiones), en Internet (basados ​​en paquetes).

imagen

Hay un marcador de sincronización para definir los límites de los elementos NAL. Cada marcador de sincronización contiene el valor 0x00 0x00 0x01, con la excepción del primero, que es 0x00 0x00 0x00 0x01. Si ejecutamos hexdump para el flujo de bits H.264 generado, identificaremos al menos tres patrones NAL al comienzo del archivo.

imagen

Como se indicó, el decodificador debe conocer no solo los datos de la imagen, sino también los detalles del video, el marco, el color, los parámetros utilizados y mucho más. El primer byte de cada NAL define su categoría y tipo.
Identificador de tipo NALDescripción
0 0Tipo desconocido
1Fragmento de imagen codificada sin IDR
2Sección de datos codificada del segmento A
3Sección B codificada de datos de la sección
4 4Sección de datos de corte codificada en C
5 5Fragmento IDR codificado de una imagen IDR
6 6Información adicional sobre la extensión SEI
7 7Conjunto de parámetros de secuencia SPS
8Conjunto de parámetros de imagen PPS
9 9Delimitador de acceso
10Fin de secuencia
11Fin de la corriente
......
Por lo general, el primer flujo de bits NAL es SPS . Este tipo de NAL es responsable de informar variables de codificación comunes, como perfil, nivel, resolución y más.

Si omitimos el primer token de sincronización, podemos decodificar el primer byte para averiguar qué tipo de NAL es el primero.

Por ejemplo, el primer byte después del marcador de sincronización es 01100111 , donde el primer bit ( 0 ) está en el campo f orbidden_zero_bit . Los siguientes 2 bits ( 11 ) nos indican el campo nal_ref_idc, que indica si este NAL es un campo de referencia o no. Y los 5 bits restantes ( 00111 ) nos indican el campo nal_unit_type, en este caso es un bloque NPS SPS ( 7 ).

El segundo byte ( binario = 01100100 , hexadecimal = 0x64 , dec = 100 ) en el SPS NAL es el campo profile_idc, que muestra el perfil que utilizó el codificador. En este caso, se utilizó un perfil alto limitado (es decir, un perfil alto sin soporte para un segmento B bidireccional).

imagen

Si nos familiarizamos con la especificación del flujo de bits H.264 para SPS NAL, encontraremos muchos valores para el nombre, la categoría y la descripción del parámetro. Por ejemplo, veamos los campos pic_width_in_mbs_minus_1 y pic_height_in_map_units_minus_1 .
Nombre del parámetroCategoríaDescripción
pic_width_in_mbs_minus_10 0ue (v)
pic_height_in_map_units_minus_10 0ue (v)
Si realizamos algunas operaciones matemáticas con los valores de estos campos, obtendremos permiso. Puede imaginar 1920 x 1080 usando pic_width_in_mbs_minus_1 con un valor de 119 ((119 + 1) * macroblock_size = 120 * 16 = 1920) . Nuevamente, ahorrando espacio, en lugar de codificar 1920 lo hicieron con 119.

Si continuamos verificando nuestro video creado en forma binaria (por ejemplo: xxd -b -c 11 v / minimal_yuv420.h264 ), entonces podemos ir al último NAL, que es el marco en sí.

imagen

Aquí vemos sus primeros valores de 6 bytes: 01100101 10001000 10000100 00000000 00100001 11111111 . Como se sabe que el primer byte indica el tipo de NAL, en este caso ( 00101 ) este es un fragmento IDR (5), y luego será posible estudiarlo más a fondo:

imagen

Usando la información de especificación, será posible decodificar el tipo de fragmento ( slice_type ) y el número de cuadro ( frame_num ) entre otros campos importantes.

Para obtener los valores de algunos campos ( ue ( v ), me ( v ), se ( v ) o te ( v )), necesitamos decodificar el fragmento utilizando un decodificador especial basado en el código exponencial de Golomb . Este método es muy efectivo para codificar valores variables, especialmente cuando hay muchos valores predeterminados.

Los valores slice_type y frame_num de este video son 7 (fragmento I) y 0 (primer fotograma).

Bitstream puede considerarse como un protocolo. Si desea saber más sobre el flujo de bits, debe consultar la especificación ITU H.264 . Aquí hay una macro que muestra dónde están los datos de la imagen ( YUV en forma comprimida).

imagen

Puede explorar otros flujos de bits, como VP9 , H.265 ( HEVC ), o incluso nuestro nuevo mejor flujo de bits AV1 . ¿Son todos iguales? No, pero haber tratado con al menos uno es mucho más fácil de entender el resto.

¿Quieres practicar? Explore el flujo de bits H.264


Puede generar video de un solo cuadro y usar MediaInfo para examinar el flujo de bits H.264 . De hecho, nada le impide siquiera mirar el código fuente que analiza el flujo de bits H.264 ( AVC ).

imagen

Para practicar, puede usar Intel Video Pro Analyzer (ya dije que el programa es de pago, pero ¿hay una versión de prueba gratuita con un límite de 10 fotogramas?).

imagen

Revisar


Tenga en cuenta que muchos códecs modernos usan el mismo modelo que acaban de aprender. Aquí, echemos un vistazo al diagrama de bloques del códec de video Thor . Contiene todos los pasos que hemos tomado. El objetivo de esta publicación es que al menos comprenda mejor las innovaciones y la documentación en esta área.

imagen

Anteriormente, se estimaba que se necesitarían 139 GB de espacio en disco para almacenar un archivo de video que dura una hora con una calidad de 720p y 30 fps. Si usa los métodos que se discutieron en este artículo (pronósticos internos y entre cuadros, conversión, cuantización, codificación de entropía, etc.), entonces puede lograr (suponiendo que gastemos 0.031 bits por píxel), el video es de una calidad bastante satisfactoria, que ocupa solo 367.82 MB, no 139 GB de memoria.

¿Cómo logra H.265 una mejor relación de compresión que H.264?


Ahora que sabe más sobre cómo funcionan los códecs, es más fácil entender cómo los nuevos códecs pueden proporcionar una resolución más alta con menos bits.

Al comparar AVC y HEVC , no debe olvidar que esto casi siempre es una elección entre una mayor carga de CPU y una relación de compresión.

HEVC tiene más opciones para secciones (y subsecciones) que AVC , más instrucciones para pronósticos internos, codificación de entropía mejorada y mucho más. Todas estas mejoras hicieron que H.265 fuera capaz de comprimir un 50% más que H.264 .

imagen



Parte I: Conceptos básicos de video e imagen






Lee también el blog
Empresa EDISON:


20 bibliotecas para
espectacular aplicación para iOS

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


All Articles