MPEG-2 Procesamiento de imágenes Digitales Enrique Pozo Márquez Miguel Ángel Oliva Delgado 1 Índice 1 Introducción 3 1.1 Compresión 4 1.1.1 Técnicas básicas de compresión 4 1.2 Algunos estándares de compresión de video 5 2 MPEG-2 10 2.1 ¿Por qué MPEG-2? 11 2.2 Niveles y perfiles 12 2.2.1 Niveles 12 2.2.2 Perfiles 13 2.3 Los modos escalables de MEPG-2 14 2.4 Esquema de codificación intertrama de video 15 2.4.1 Funcionamiento del algoritmo 16 2.4.1.1 Qué ocurriría en el caso de que algunas partes se moviesen a la izquierda y otras ala derecha 20 2.5 Audio MPEG-2 21 2.6 Sistemas MPEG-2 para multiplexaje y transporte ITU-t Rec. H.222 23 2 2.7 Donde hay MPEG-2 en la actualidad? 25 3. Transformada Discreta del Coseno (DCT) 26 4. Documentación de la aplicación 36 5. Conclusiones 40 6.Bibliografía 40 7. Anexo 41 3 1.Introducción Un vídeo es una secuencia de imágenes que se muestran en orden. Cada una de esas imágenes será lo que se llame “trama”. Normalmente se muestran unas 30 tramas por segundo. Esta información se puede comprimir perdiendo cierta calidad proporcional al nivel de compresión. Si un segundo de vídeo contiene 30 tramas podemos jugar con estas para obtener esta compresión al basar unas tramas en otras cercanas, que pueden contener información acerca, por ejemplo, del movimiento. Esta compresión se puede basar en las tramas del entorno, interframes, o bien, en la información de esa trama solamente, intraframe. Las intraframes permitirán operaciones de acceso aleatorio, como puede ser el avance rápido, y poseen una alta tolerancia a los fallos, como es lógico al basarse su compresión tan sólo en sí mismas. Así si una parte de una trama se pierde, la próxima intraframe y las tramas posteriores podrán visionarse sin problema al depender exclusivamente de su propia información. Todo color se puede representar por la combinación de tres colores básicos, rojo, verde y azul, este espacio de color se conoce como RGB (Red, Green, Blue), pero esta forma de representar los colores no se tiene en cuenta ya que no considera la percepción humana. Así tenemos el espacio de color YUV donde solo con Y ya tenemos la escala de grises, a la cual el ojo humano es más sensitiva, y que es la que realmente se utiliza para los temas de compresión. También se utiliza en NTSC, PAL, SECAM. Para obtener unos mejores ratios de compresión se predicen los píxeles basándolos en otros píxeles. Tenemos una predicción espacial donde podemos obtener un píxel basándonos en otros píxeles de la propia imagen y la predicción temporal en la que un píxel se puede obtener de una imagen transmitida anteriormente. También existe una codificación híbrida que consiste una predicción temporal con una técnica de correlación en el dominio espacial. La compensación del movimiento establece una correspondencia entre elementos de imágenes cercanas en la secuencia de vídeo. Esta compensación del movimiento nos suministra una fácil predicción para una imagen dada de una imagen de referencia. 4 De todos los algoritmos de codificación estandarizados el más usado es la Transformada Discreta del Coseno (DCT, Discrete Cosine Transform) de la cual trataremos más adelante ampliamente. 1.1.Compresión Básicamente la compresión se divide en: - Compresión sin perdidas: Esta técnica se utiliza principalmente, cuando es necesario transmitir mensajes de emisor a receptor sin que se produzca ningún tipo de distorsión o de cambio significativo respecto al mensaje original. Estas técnicas se basan, principalmente, en utilizar más o menos bits según la frecuencia de utilización, es decir, se observa si se producen repeticiones para intentar codificarlas con menos bits de los que en principio serian necesarios. - Compresión con perdidas: Cuando es necesario llegar a unos flujos de información que no sobrepasen unos determinados limites de ancho de banda y esto no es posible utilizando técnicas de compresión sin perdidas, será necesario comprimir aún más la señal aunque esto repercuta en su calidad. Aunque en principio no lo parezca, en algunos casos nos podemos permitir el lujo de aumentar la compresión en decremento de la calidad. 1.1.1.Técnicas básicas de compresión: - Cuantificación: Se basan principalmente en utilizar menos bits de los necesarios para describir una información, utilizando la misma combinación de bits para datos semejantes o próximos. - Predicción: Las técnicas de predicción se basan en la correlación entre píxeles próximos o imágenes próximas, es decir, si por ejemplo tenemos una imagen donde la mayor parte de los píxeles representan el cielo, todos estos píxeles serán semejantes, por lo tanto la utilización de menos bits para describirlos no repercutirá de forma notable en el decremento de la calidad. 5 -Transformación y aplicación de filtros: Una de las técnicas más utilizadas en la compresión de señales analógicas es el filtraje. Por ejemplo, una señal de voz humana de 20Khz puede ser transmitido por una línea telefónica con un ancho de banda de 4Khz utilizando un filtro de paso bajo (lowpass filter) - Compresión basada en modelos Se utiliza cuando es necesario llegar a niveles de compresión extremos donde un gran nivel de distorsión puede ser aceptable. 1.2.Algunos estándares de compresión de video: H.261 Fue diseñado para la transmisión de vídeo a velocidades múltiples de 64 Kbps. Se ha aplicado en el videoteléfono y en la videoconferencia. Es similar a la compresión de imágenes estáticas JPEG. Posee una estructura de 4 capas, que son multiplexadas para su transmisión en serie. H.263 Se diseñó para aplicaciones con velocidades de codificación de muy baja velocidad. Posee una eficiencia más alta que el H.261, aunque está basado en él. La compresión se lleva a cabo por partición de cada imagen en macrobloques, cada uno de los cuales se compone de un bloque de luminancia de 16x16 y de dos bloques de crominancia de 8x8. H.263+ Es una extensión del H.263 aunque incorpora muchas características adicionales. Suministra la escalabilidad SNR y la espacial y temporal. 6 MPEG-1 MPEG está basado en un sistema de capas, con un estándar de compresión de vídeo basado en la DCT, y que nos da unas velocidades de aproximadamente 1,5 Mbps con una resolución de aproximadamente 352x240. Las secuencias de vídeo se componen de diferentes capas que permiten el acceso aleatorio a una secuencia determinada así como una especie de barrera contra la información errónea. Las tramas se codifican de tres formas: las Intra-coded (I-frames), las Predictivecoded (P-frames) y las Bidirectionally-predictive-coded (B-frames). Las I-frames se codifican independientemente de cualquier otra trama, y son las que suministran los puntos de acceso aleatorio al vídeo. Poseen el peor nivel de compresión de los tres tipos. Las P-frames se codifican teniendo en cuenta una trama ya pasada, que puede ser una Iframe u otra P-frame. Su compresión es más alta que en el caso anterior. Y por último las B-frames que requieren una trama anterior y otra posterior para su codificación, que pueden ser o I-frames o P-frames, y son las que ofrecen el mayor nivel de compresión de los tres tipos. Un vídeo MPEG consiste en muchas secuencias de vídeo, cada una de las cuales posee muchos grupos de imágenes. Típicamente un grupo de imágenes (GOP) posee una única I-frame y muchas P-frames y B-frames. Las tramas se dividen en bloques de tamaño 16x16 que reciben el nombre de macrobloques. Una secuencia de macrobloques compone un slice. Y cada trama se compone de muchos slices. Muchos de los parámetros se pueden especificar en la fase de codificación. Cada capa añade una cabecera para sincronización. Así si parte de un slice se pierde, el decodificador se salta el resto del slice y continuará decodificando en el principio del siguiente slice. 7 MPEG-2 Se diseña para aplicaciones que requieren velocidades de hasta 100 Mbps. Por ejemplo, la televisión digital de alta definición, los medios de almacenamiento interactivos, o la televisión por cable. En MPEG-2 se pueden usar múltiples formatos de vídeo para soportar las distintas aplicaciones. Posee escalabilidad de la cadena de bits, así es posible extraer cadenas más pequeñas para obtener una resolución o una velocidad de trama más pequeña. La decodificación es más costosa que en el caso anterior, aunque la escalabilidad de las cadenas de bits permite flexibilizar un poco los requerimientos de procesamiento para la decodificación. MPEG-2 es compatible hacia arriba, hacia abajo, hacia delante y hacia atrás. Esto quiere decir lo siguiente: - Compatibilidad hacia arriba: el decodificador puede decodificar imágenes generadas por un codificador de inferior resolución. - Compatibilidad hacia abajo: el decodificador puede decodificar imágenes generadas por un codificador de superior resolución. - Compatibilidad hacia delante: un decodificador de nueva generación puede decodificar las imágenes generadas por un codificador ya existente. - Compatibilidad hacia atrás: los decodificadores actuales pueden decodificar las imágenes generadas por los nuevos codificadores. Las capas de secuencia de vídeo en MPEG-2 son similares a las de MPEG-1. Los macro bloques en MPEG-2 tienen dos bloques de crominancia adicionales cuando se usa el formato de entrada 4:2:2. MPEG-2 usa el tamaño de bloque 8x8 aunque puede usar otros tamaños, 1x1, 2x2, 4x4 para resoluciones mejoradas. Las tramas P y B poseen la trama y los vectores del campo de movimiento. La decodificación en tiempo real es algo ardua con los procesadores normales. 8 MPEG-3: Se intentó desarrollar para televisión con velocidades de transmisión altas pero acabó desechándose y fusionándose con MPEG-2. MPEG-4: Al principio, el estándar MPEG-4 se creó como un intento para mejorar la calidad del video codificado de bajas velocidades a través de la estandarización de nuevas técnicas mejoradas de compresión. Más adelante, su progresión recondujo este estándar al mundo de la TV interactiva, la computación y las telecomunicaciones. Las nuevas características ofrecidas por este estándar se pueden resumir en: - Las escenas se descomponen en 2 componentes básicas: Audio y Vídeo. Estos dos objetos son codificados de forma independiente. - Los objetos pueden ser tanto video natural (p.ej. generado por una cámara) como imágenes sintéticas (generadas por un ordenador). - Ofrece soporte para manipulación de las imágenes sintéticas (soporte para animación, utilización de imágenes estáticas 2D-3D como logos etc). - Permite interacción de los usuarios sobre la escena que se está renderizando. - Se ha mejorado la base del algoritmo MPEG para incrementar la robustez para el trato de errores. Cabe destacar que tanto MPEG-4 como H.263 se encuentran principalmente enfocados a intentar mantener una calidad de video en tiempo real utilizando los mínimos bits posibles, por lo tanto, los dos estándares están enormemente relacionados . Algoritmo Motion-JPEG: MJPEG es un estándar internacional creado para la compresión de frames estáticos. Es decir, en este caso lo que se realiza es una compresión de cada frame de forma estática sin utilizar técnicas de predicción y cada frame es enviado individualmente uno detrás de otro. MJPEG utiliza la DCT para transformar bloques de 8x8 píxeles, luego cuantifica los valores en dominio transformado y finalmente utiliza una compresión con perdidas para codificarlos. 9 Como se puede observar, la naturaleza de este tipo de técnica de compresión no se puede considerar como un estándar de imágenes en movimiento puesto que lo que en realidad se está haciendo es utilizar un algoritmo para imágenes estáticas y utilizarlo en cada uno de los frames que forman la película para conseguir un efecto de movimiento. Cabe destacar que a veces, MJPEG utiliza también un tipo de compresión por predicción teniendo en cuenta la diferencia temporal entre frames consecutivos. En este caso, debido a la utilización de una compresión con perdidas, este formato podrá considerarse como una especie de MPEG degenerado, es decir, mejoramos el tamaño de los archivos y conseguimos más simplicidad pero a la vez el resultado final presentará una calidad menor. Motion Wavelets Este tipo de compresión está basado en aplicar el algoritmo de la DCT pero en toda la imagen a la vez y no en bloques de 8x8. Los valores de la transformada son cuantificados con compresión sin perdidas. En principio, este formato se utiliza de la misma forma que los MJPEG, es decir, se destina principalmente a frames estáticos y se puede aplicar a imágenes en movimiento mediante la transmisión individual de cada frame codificado. La ventaja más importante respecto al formato MJPEG se puede observar a niveles de compresión muy altos del orden de 32:1 o 64:1 ya que, mientras la utilización de bloques de 8x8 (como en el caso de MJPEG) produce bastante distorsión en la imagen, la no utilización de bloques, es decir, la aplicación de la transformada sobre toda la imagen a la vez, produce un grado más subjetivo de distorsión ya que no se percibe tanto ese efecto de "bloque". Normalmente este formato se utiliza en el caso de necesidad de grandes factores de compresión sin la utilización de redundancia entre frames. 10 2.MPEG-2 La segunda fase de MPEG, llamada MPEG 2, también consta de tres partes o estándares, cubiertas por la: ISO/IEC 13818-1 Sistemas MPEG-2 (Draft ITU-T Rec. H.222), ISO/IEC 13818-2 Vídeo MPEG-2 (Draft ITU-T Rec. H.262) y ISO/IEC 138183 Audio MPEG-2. Estas fueron aprobadas finalmente como Estándar Internacional (IS) por la asamblea N° 29 de la ISO/IEC JTC1/SC29/WG11 (MPEG) hecha en Singapore en Noviembre de 1994. El registro ITU-T H.262 trata con codificación de vídeo de alta calidad con posible vídeo entrelazado de NTSC, PAL o Televisión de Alta Definición (HDTV). Esto es un intento para operar en un rango de 2 a 15 Mbit/s. Sin embargo puede funcionar a velocidades superiores de 100 Mbit/s. Un amplio rango de aplicaciones, velocidades, resolución calidades de las señales y servicios son direccionados, incluyendo todas las formas de medios de almacenamiento digital, televisión (incluyendo HDTV), broadcasting y comunicaciones. Entre las varias mejoras o extensiones introducidas en los codificadores MPEG 2, tenemos: - Nuevos modos de predicción de campos y tramas para scanning entrelazado. - Cuantización mejorada. - Nuevos códigos intra-trama de longitud variable (VLC). - Extensión escalada de resoluciones para compatibilidad, servicios jerárquicos y robustos. - Dos nuevas capas de sistema para multiplexaje y transporte que provee celdas/paquetes de vídeo de alta o baja prioridad, cuando son llevados a través de una red conmutada. - Incrementos soportados por accesos aleatorios. - Soporte resistente para incremento de errores. - Múltiples programas con un multiplexor (MPEG 1 no puede hacer esto, y esto fue un driver principal para el MPEG 2). 11 Al igual que el H.261 y JPEG (Joint Photographic Expert Group), el estándar MPEG 2 es un esquema híbrido de compresión para imágenes en pleno movimiento que usa codificación inter-trama y codificación intra-trama y combina la codificación predictiva con la codificación con la transformada DCT 8x8 (Discrete Cosine Transform, o sea, transformada discreta de coseno). La DCT es un algoritmo matemático (conversión del dominio del tiempo hacia el dominio de la frecuencia), que es aplicado típicamente a un bloque de 8x8 elementos de imagen, dentro de un cuadro. La DCT elimina redundancia en la imagen a través de la compresión de la información contenida en 64 píxeles. El cuantizador otorga los bits para los coeficientes DCT más importantes, los cuales son transmitidos 2.1.¿Por qué MPEG-2? El concepto de MPEG 2 es similar al MPEG 1, pero incluye extensiones para cubrir un amplio rango de aplicaciones. La principal aplicación destinada durante el proceso de definición de MPEG 2 fue todas las transmisiones de vídeo con calidad de TV codificadas a velocidades entre 5 y 10 Mbit/s. Sin embargo, la sintaxis del MPEG 2 ha sido descubierta para ser eficiente para otras aplicaciones como las de altas velocidades binarias y velocidades de muestreo (HDTV). La característica más resaltante con respecto a MPEG 1 es la sintaxis para codificación eficiente de vídeo entrelazado. Otras características más específicas (precisión 10 bit DCT DC, cuantización nolineal, tablas VLC) son incluidas, y tienen un mejoramiento notable en la eficiencia de la codificación. Otra característica clave de MPEG 2 son las extensiones escalables las cuales permiten la división de continuas señales de vídeo dentro de dos o más cadenas binarias codificadas, representando el vídeo en diferentes resoluciones, calidades (por ejemplo SNR), o velocidades. 12 2.2.Niveles y perfiles MPEG-2 es una recomendación muy compleja, tiene una larga variedad de combinaciones (sobre 106). Sin embargo, un reducido conjunto de combinaciones son definidas bajo "perfiles" y "niveles". Dentro de los perfiles, una larga variación de desempeños son posibles. Por otra parte los niveles son un conjunto de derivaciones impuestas para los perfiles. Las combinación de un perfil y un nivel produce una arquitectura muy bien definida para una cadena particular de bit. Los perfile limitan la sintaxis (por ejemplo los algoritmos), mientras los niveles limitan los parámetros (velocidad de muestreo, dimensiones de las tramas, velocidad binaria codificada, etc.). 2.2.1.Niveles: Proveen un rango de cualidades potenciales, definen los máximos y mínimos para la resolución de la imagen, muestras Y por segundo (luminancia), el número de capas de audio y vídeo soportados por los perfiles escalados, y la máxima velocidad binaria por perfil. A continuación una explicación resumida de cada uno de ellos: - Nivel Bajo: tiene un formato de entrada el cual es un cuarto de la imagen definida en el registro ITU-R 601. - Nivel Principal: tiene una trama de entrada completa definida en el registro ITU-R 601. - Nivel Alto 1440: tiene un formato de alta definición con 1440 muestras por línea. - Nivel Alto: tiene un formato de alta definición con 1920 muestras por línea (para aplicaciones sin cualquier limitación en velocidades de datos). 13 2.2.2.Perfiles: Son definidos subconjuntos con características de sintaxis (por ejemplo: algoritmos), usados para converger la información. Hay cinco diferentes perfiles y cada uno es progresivamente más sofisticado y agrega herramientas adicionales (y por supuesto más costoso para el cliente) con la característica adicional de ser compatible con el anterior. Esto significa que un decodificador equipado con un alto perfil descodificará perfiles simples. A continuación una pequeña explicación de los perfiles: - Perfil Simple: es el que ofrece pocas herramientas. - Perfil Principal: tiene herramientas extendidas o mejoradas del perfil simple y predicción bidireccional. Tendrá mejor calidad para la misma velocidad binaria que el perfil simple. - Perfil Escalable SNR y Perfil Escalable Espacial: son los próximos pasos. Estos dos niveles son llamados escalables porque ellos permitirán codificar datos de vídeo que sean particionados dentro de una capa base y una o más señales "Top-up". La señal Top-up puede tanto tratar la proporción S/N (SNR escalable) o la resolución (escalable espacial). - Perfil Alto: este incluye todas las herramientas de las versiones anteriores y mejoradas. Tiene la habilidad de codificar diferencias de color entre líneas simultáneamente. Este es un super sistema diseñado para aplicaciones donde no están contraídas sobre las velocidades de los bits. Para muchas de las aplicaciones (incluyendo transmisión de satélites) el Perfil Principal, Nivel Principal (MP@ML, siglas en ingles) provee una buena relación entre calidad de imagen y la complejidad VLSI, como resultado, MP@ML el punto de desarrollo para los actuales sistemas DCTV. 14 2.3.Los modos escalables de mpeg 2 Actualmente hay cuatro modos escalables en MPEG 2. Estos modos rompen el vídeo MPEG 2 en diferentes capas (base, media, y alta) para propósitos de proritización de datos de vídeo. Otro propósito de la escalabilidad es para divisiones complejas. Por ejemplo, en HDTV, la alta prioridad de la cadena binarias (720x480) puede ser descodificada bajo condiciones de ruido donde la baja prioridad (1440x960) no pueda. A continuación una breve explicación de los modos escalables: - Escalabilidad espacial: Este método de dominio espacial codifica la capa base a una dimensión de muestro bajo (por ejemplo: resolución) que las capas superiores. Las capas bajas (base) reconstruidas del muestro son usadas como predicción de las capas superiores. - Particionamiento de datos: es un método de dominio de frecuencia que rompe los bloques de 64 coeficientes cuantizados de la transformada dentro de dos cadenas binarias. La primera, cadena de alta prioridad contiene los coeficientes más críticos de las frecuencias bajas e información (tales como valores DC, vectores, etc.), la segunda, cadena binaria de baja prioridad lleva datos AC de las altas frecuencias. - Escalabilidad SNR: es un método de dominio espacial donde los canales son codificados a velocidades de muestreo idénticas, pero con diferentes calidades de imágenes. La cadena binaria de alta prioridad contiene datos de la capa base que pueden ser añadidos a la capa de refinamiento de baja prioridad para construir una imagen de alta calidad. - Escalabilidad temporal: Un método de dominio temporal usado por ejemplo en vídeo estereoscopico. La primera, la cadena binaria de alta prioridad codifica vídeo a una baja velocidad de tramas, y las tramas intermedias pueden ser codificadas en una segunda cadena binaria usando la reconstrucción de la primera cadena binaria como predicción. Por ejemplo en una visión estereoscopica, el canal de vídeo izquierdo puede ser predecido del canal derecho. 15 2.4.Esquema de codificacion intertrama de video Para explorar todas las capacidades de compresión de compensación de movimiento y para incorporar capacidades de adelantado rápido y retroceso rápido (fast forward y fast reverse FF/FR), requeridos para servicios de almacenamiento digital, MPEG 2, incorpora algunos esquemas de codificación intertrama. El concepto está basado en Intra-trama (I), tramas predecibles (P), tramas interpoladas o bidireccionales (B) y tramas D (Imágenes DC). - Una trama I es codificada sin referencia para otras imágenes o tramas contenidas en la secuencia del vídeo. Cualquier trama I trabaja como un punto de referencia para funcionalidad y accesos FF/FR. Libera muy baja compresión. - Las tramas P son codificadas con la referencia de las tramas previamente codificadas, tanto I y P. Ellas incorporan compensación de movimiento, la compresión es más alta que las tramas I. - Las tramas B requieren como referencia tanto las tramas futuras como pasadas, las tramas B usan compensación e interpolación de movimientos y logra alta compresión. - Tramas D (imágenes DC) son imágenes que contienen solamente la DC (bloques de 8x8) para cada bloque. El soporte de éste tipo de trama es opcional, y las secuencias pueden no contener tramas D mezcladas con los otros tipos de tramas. La proporción entre las tramas I, P y B es conocida como N/M, donde N representa el número de tramas entre imágenes o tramas I y M es el número de tramas entre imágenes o tramas P. Valores típicos son de 15 y 3 para N y M respectivamente. La incorporación de estas tres tipos de tramas, aportan alta compresión, buen acceso aleatorio y funcionalidad FF/FR. Este esquema de codificación también incrementa significativamente el retraso de codificación porque las tramas de las imágenes deben ser almacenadas en un buffer. Por ejemplo, el codificador considerará la primera trama como una trama I, la segunda y tercera trama serán tramas B, luego ellas son predecidas e interpoladas basadas en la trama previa I (o P) y la próxima trama P, serán puestas en el buffer y codificada la próxima trama como P, la cual sólo es referida a la trama previa I. Después de codificar la trama P, el codificador retornará a trabajar con la trama 16 almacenada B. El descodificador revertirá el proceso. El recibirá la trama I, la trama P y la trama B y reconstruirá la trama original del vídeo. El proceso, requiere más memoria en el descodificador que en el codificador. Este retraso de codificación hace que MPEG 2 no sea bueno para aplicaciones interactívas. 2.4.1.Funcionamiento del algoritmo En la figura vemos como se muestran una serie de tramas de distintos tipos, vemos que la secuencia se compone exactamente de una trama I, que la inicia, tres tramas B, una trama P, otras tres tramas B y por último otra trama P. Esta es la forma en que la secuencia se nos visionará, pero que ocurre si las tres primeras tramas B dependen de la trama I y de la trama P interior, tenemos que no podremos visionar las tramas B hasta que hayamos recibido la trama P, por lo tanto se nos plantea un problema, que no es tal ya que este asunto ya está pensado y se resuelve sabiendo que el orden de codificación no es el mismo que el de visualización. Así las tramas se codifican de tal forma que la trama referida precede a las tramas de referencia. Veamos esto en el ejemplo. El orden de visualización era I B B B P B B B P, pues bien, el orden de codificación será I P B B B P B B B. El decodificador tiene el cometido de reordenar las tramas, que poseen un número de trama ascendente para cada una en módulo 1024. 17 18 MPEG-2 se basa en la predicción para realizar una mejor compresión, así un objeto, que puede ser un triángulo aparece en la trama I en una determinada posición, la siguiente trama P nos muestra el mismo triángulo pero en otra posición, entonces si pensamos un poco veremos que el triángulo es el mismo, tan solo varía su posición dentro de la escena, por tanto podemos representarlo mediante un vector de movimiento que nos indique como mover el triángulo desde la posición que ocupa en la trama I hasta la posición que ocupa en la trama P. Este vector de movimiento forma parte de la cadena de MPEG y se divide en una parte horizontal y otra vertical, las cuales pueden ser positivas o negativas de forma indistinta, así un valor positivo significa que el movimiento hacia la derecha o hacia abajo, y un valor negativo significa que el movimiento es hacia la izquierda o hacia arriba. El rango que cubren estos valores es de –64 a +63, con lo que pueden hacer referencia a un área de 64x64 píxeles. Sin embargo esta forma de desplazamiento no es completa, podemos encontrarnos con el caso en que una figura se desplace de un lugar a otro y encima rote equis grados. Aquí entramos en lo que se llama la predicción de error que MPEG contempla y para lo cual posee los mecanismos necesarios para su compensación. Al reconstruir tramas inter coded haremos lo siguiente, en primer lugar aplicamos el vector de movimiento a la trama y en segundo lugar añadimos la compensación de la predicción de error al resultado, como vemos en la siguiente figura. 19 trama I + Predicción hacia delante + vector de movimiento resultado compensación del error de la predicción NOTA.- en la figura vemos que el rectángulo de ejemplo lleva un borde negro que ha sido añadido simplemente para que se aprecie mejor la compensación del error de la predicción. Debemos señalar que la compensación del error de la predicción requiere menos bytes que la trama completa porque las partes blancas son cero y se pueden descartar de la cadena MPEG. De todas formas se aplica la compresión DCT a la predicción de error lo que hace que el tamaño disminuya enormemente. También vemos como en un principio se le aplica (añade, +) a la trama I el vector de movimiento, y que al resultado le aplicamos la compensación del error de la predicción. 20 2.4.1.1.¿Qué ocurriría en el caso de que algunas partes se moviesen a la izquierda y otras a la derecha? Pues bien, en este caso no podemos aplicar el vector de movimiento a la trama completa, sino que la trama se divide en macro bloques de 16x16 píxeles cada uno con su propio vector de movimiento. Con esto se reduce la probabilidad de movimiento contradictorio, aunque no lo elimina totalmente. De todas formas si se diese un movimiento contradictorio que no se pueda evitar no existe problema ya que no hay que cometer el fallo de suponer que la técnica de compresión de MPEG asume que todos los macro bloques de tramas P se pueden predecir, si existe un error de predicción muy grande el codificador puede tomar la determinación de intra codificar un macro bloque, con lo cual se evitaría el problema. Cada macro bloque contiene 4 bloques de luminancia y 2 de crominancia. Cada bloque tiene una dimensión de 8x8 valores. Los bloques de luminancia contienen información sobre el brillo de cada píxel en el macro bloque. Los bloques de crominancia contienen información del color. Esta información no es sobre el color de cada píxel, ya que el ojo humano es incapaz de distinguirlo. En lugar de eso se asocian 4 pixeles para cada valor de color. Este valor de color se divide en dos partes, la primera es el bloque de color Cb y la segunda el bloque de color Cr. En la figura siguiente podemos ver como son estos bloques y como se aplican. lum lum lum lum Cb Cr 8 8 8 8 21 En resumen, el modelo de codificación de video es el siguiente: 2.5.Audio mpeg 2 El sistema de multiplexaje MPEG 2, soporta cualquier número de canales de entrada de audio tan largos que la velocidad de transporte seleccionada pueda soportar la suma de datos. Los usuarios tienen la flexibilidad para seleccionar su propio algoritmo de compresión de audio, tales como: Audio MPEG 2, MUSICAM, DOLBY AC-2 o AC-3. Los canales pueden ser configurados independientemente o en pares estéreo. Diferentes 22 velocidades de audio es otra de las características de el sistema. Una vez más, la velocidad también será asociada con la calidad. La compresión de audio MPEG 2 es un algoritmo que, como el vídeo MPEG 2, explota las limitaciones del sistema humano, en este caso el oído. Como en la compresión de vídeo, el algoritmo de compresión de audio también elimina la información irrelevante dentro de la señal de audio. La información irrelevante es cualquier señal imperceptible. Por ejemplo, en presencia de una señal fuerte, todas las señales vecinas flojas son enmascaradas y aunque son parte del espectro, no son percibidas por el oído. El algoritmo MPEG 2 es del tipo "lossy" o con pérdidas pero la distorsión insertada por la señal será inaudible. La configuración básica del audio MPEG 2 ofrece seis canales de audio. Esta característica debe ser usada para distribuir tres pares de estéreos ( o seis canales mono) para aplicaciones multilenguajes o para crear un sistema estereofónico multicanal. Lo anterior crea una realidad como a la de un campo de audio. La recomendación de cornetas configuradas para sistemas estereofónicos multicanales es conocido como estéreo -p/q , donde p es el número de cornetas en el frente y q es el número de cornetas en el fondo. Por ejemplo un estéreo-3/2 proveerá un sistema con canales al frente en la derecha, centro y a la izquierda más canales posteriores que rodean el área y ofrecen un mejor e impresionante realismo a la audiencia. Las configuraciones típicas para sistemas estereofónicos multicanales son: 1 Canal modo 1/0: Mono 2 Canales estéreo 2/0: izquierda y derecha 3 Canales estéreo 3/0: izquierda, derecha y centro 4 Canales estéreo 3/1: izquierda, derecha, centro y posterior 5 Canales estéreo 3/2: izquierda, derecha, centro, posterior izquierda y derecha 5.1 Canales estéreo 3/2: izquierda, derecha, centro, posterior izquierda y derecha y un canal de efectos especiales de 100 Hz LFE (Low Frequency Enhancement). 23 Audio MPEG 2 ofrece tres diferentes capas de compresión (capa -I, -II, y -III). Cada capa usa un esquema de reducción incremental de la velocidad binaria, con la ayuda de el incremento de la velocidad de compresión mientras se mantiene la calidad. Para la capa -II, la técnica de reducción de bit corresponde para el algoritmo MUSICAM, el permite varias combinaciones de velocidades de bit (32 a 224 Kbit/s por canal), y calidad de audio sin comprometer la complejidad del hardware. Por ejemplo una velocidad de muestreo de 56 Kbit/s y 16 KHz en la capa -II ofrece mejor calidad que la definida en el registro ITU-G.722. El modelo de codificación de audio en MPEG es el siguiente: 2.6.Sistemas mpeg 2 para multiplexaje y transporte (ITU-T Rec. H.222) El multiplexaje y transporte definidos bajo MPEG 2 específica el formato de codificación para multiplexar audio, vídeo y datos dentro de una forma manejable para almacenar o transmitir. Hay dos formatos de cadenas de datos definidos: 1. Cadena de transporte (TS), la cual porta o lleva uno o más programas simultáneamente, es optimo para aplicaciones donde la pérdida de datos puede ser requerida (como los enlaces satelitales). Tales errores pueden ser manifestados como 24 errores de bit o pérdida de paquetes. Una TS es una sucesión de paquetes de 188 bytes de longitud llamados "paquetes de transporte". Es posible construir una TS a partir de: - Cadenas elementalmente paquetizadas (PES), vienen de múltiples programas. Cada programa debe tener velocidad variable, sin embargo, la TS será de velocidad fija. Cada programa tiene un Reloj Referencial Primario (PCR) asociado con el para indicar la actual velocidad del programa. - Cadenas de Programa u otra TS las cuales puedan contener uno o más programas. La cadena de transporte es optima para transmisiones de satélites, cable, ISDN, redes ATM y vídeotelefonía. 2. Cadena de programa (PS). Es optima para usarse en ambientes libres de errores, como aplicaciones multimedia. Llevan paquetes largos de longitud variable. Cada paquete comenzará con una cabecera. Un error en la cabecera puede causar la pérdida del paquete completo y puede representar la corrupción de una trama entera de vídeo. PS puede llevar uno o más programas simultáneamente, pero la PES tiene que compartir un tiempo base común. 3. Cadena Elementalmente Paquetizada (PES): El transporte es desarrollado dividiendo las salidas del compresor (vídeo y audio) para formar paquetes de longitud variable marcados por tiempo llamados cadenas elementalmente paquetizadas (PES). Las PES son paquetes de longitud variable sujeto a la máxima longitud de 64 Kbytes. El proceso de paquetización es aplicado para tanto vídeo y audio. Cada PES es marcado con el tiempo para darse una referencia antes y después del demultiplexaje, con la finalidad de reproducir las tramas de vídeo completamente sincronizadas con el audio PES. Este es un tiempo de referencia para cada programa llamado Reloj Referencial Primario (PCR). El PCR es una información independiente usada para crear un reloj de referencia en el descodificador. Cada programa prescinde de la velocidad y si la velocidad es variable o fija son referidas para un PCR. Finalmente, la PES viniendo de diferentes programas puede compartir un único PCR. 25 4. Multiplexaje: Un multiplexaje basado en paquete es muy flexible. Paquetes pertenecientes a diferentes programas pueden ser distribuido de varias maneras. Un buffer es usado en el descodificador para asegurarse que todos los datos son descodificados y presentados a tiempo. Ya que la naturaleza inherente de la PES es variable en velocidad, el multiplexaje basado en paquetes es de gran aprovechamiento porque puede distribuir PES de diferentes fuentes con diferentes velocidades instantáneas dentro de una salida común con velocidad fija. Explotando la no coincidencia de picos de velocidad, se puede transportar altas velocidades instantáneamente en una salida de baja velocidad. Este tipo de multiplexor es algunas veces referido como "multiplexor estadístico". Las capas del sistema ofrecen diferentes modos de operación: - Multiplexaje estadístico, descrito anteriormente. - Multiplexaje estadístico limitado en el cual la velocidad de cualquier cadena de vídeo no podrá caer bajo un umbral programable. - Multiplexaje con velocidad fija, las velocidades de vídeo son fijas. - Modo mixto. Todos los modos previos pueden ser mezclados dentro de un simple TS. Después del multiplexaje, la señal sufrirá el proceso final, cada paquete de transporte puede ser encriptado y revuelta, bajo control de acceso condicional. La salida de un multiplexor portando múltiples canales siempre será a velocidad fija. 2.7.¿Donde hay mpeg 2 en la actualidad? DBS (Emisión Directa de Satélite): El servicio Hughes/USSB usa vídeo y audio MPEG 2. Hughes/USSB DBS comenzó a dar servicios en Norte America en 1994. Dos satélites a 101 grados al oeste comparten los requerimientos de poder de unos transponder con 120 Watts y 27 MHz. CATV (Televisión por cable): La industria del cable ha colocado más o menos vídeo MPEG 2. Y el audio en menor proporción. 26 DigiCipher: La sintaxis del DigiCipher I es similar a la del MPEG 2, pero usa pequeños "macrobloques de predicción" y no tramas B. La especificación DigiCipher II incluye modos para soportar tanto GI como sintaxis del perfil principal de vídeo MPEG 2. Servicios como HBO fueron actualizados con DigiCipher II en 1994. HDTV: La gran alianza de Estados Unidos (Grand Alliance), un consorcio de compañías que formalmente compiten por el estándar HDTV, ha agregado el uso de vídeo MPEG 2 y sintaxis de los sistemas (incluyendo imágenes o tramas B). Serán soportados ambos modos, entrelazado (1440x960x30 Hz) y progresivo (1280x720x60 Hz). Para esto, la alianza debe colocarse bajo una modulación (QAM, VSB, OFDM), convolución (MS o Viterbi), y corrección de errores (RSPC, RSFC). En septiembre de 1993, el consorcio de 85 compañías europeas firmó un convenio para invertir en un proyecto conocido como Emisión de Vídeo Digital ( DVB) el cual desarrolló un estándar para transmisión terrestre y de cable, y éste esquema usa MPEG 2. Este consorcio puso el último clavo en el ataúd del esquema D-MAC, para una migración gradual hacia un todo digital: HDTV. 3.La transformada discreta del coseno (DCT) Para explicar la transformada usaremos la siguiente figura: 27 La forma normal es determinar el brillo de cada uno de los 64 píxeles y escalarlos dentro de unos limites, normalmente de 0 a 255 (en MPEG se usa un rango de – 256,255) donde 0 significa negro y 255 blanco. 12 0 10 90 8 12 7 11 97 5 13 4 12 2 13 7 12 13 1 11 7 5 10 0 89 1 8 88 70 77 59 7 5 4 4 8 3 6 2 4 2 5 1 10 0 7 5 3 8 9 6 4 10 6 3 9 9 9 8 6 10 3 9 3 5 5 9 8 95 9 6 0 0 8 8 8 89 8 7 6 2 7 8 9 8 2 9 3 6 87 7 8 10 7 3 5 2 1 10 8 10 11 6 9 9 7 9 5 10 5 5 7 85 69 58 También podemos representarlo mediante un diagrama de barras 8x8: 28 Normalmente los valores son procesados línea a línea. Esto requiere 64 bits de almacenaje. Pero podemos definir todos los 64 valores por solo 5 enteros, aplicando la fórmula de la DCT: Donde f(x,y) es el brillo del píxel en la posición [x,y]. El resultado es F, una matriz 8x8 también. Siguiendo el ejemplo anterior: 29 700 9 0 100 0 0 0 0 0 90 0 0 0 0 0 0 0 -89 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 Como se puede ver, la mayoría de los valores son 0. Como los valores distintos de 0 están concentrados en la esquina superior derecha la matriz es transferida al receptor en orden de escaneado en zigzag: 30 Esto resulta en: 700 90 90 -89 0 100 0 0 0 .... 0. Por supuesto, los ceros no se transmiten, en su lugar se codifica una señal de final de bloque. El decodificador puede reconstruir los valores de los píxeles usando la fórmula de la inversa de la transformada del coseno (IDCT): Donde F(u,v) es el valor de la matriz transformada en la posición [u,v]. Los resultados son los valores originales de los píxeles. De esta manera, podríamos considerar la compresión MPEG como sin perdidas, pero esto no es cierto, porque los valores transformados están cuantizados. Están divididos (división entera) por un cierto valor mayor o igual que 0 debido a que la DCT soporta valores hasta 2047. Para reducirlos hasta estar al menos bajo la longitud del byte se aplica el valor de cuantización 8. El decodificador multiplica los resultados por el mismo valor. Lógicamente los resultados difieren de los valores originales, pero debido a algunas propiedades del ojo humano el error no es visible. En MPEG hay una matriz de cuantización que define un valor diferente de cuantización para cada valor transformado dependiendo de su posición. El valor de la esquina superior izquierda de la matriz transformada es llamado valor DC (direct current) y determina la media de brillo en el bloque.El resto de los valores son llamados valores AC (alternating current) y describen la variación sobre el valor DC. Así, supongamos una matriz transformada tal que así: 31 700 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 El valor DC seria 700. El resultado de aplicarle la IDCT seria: 8 7 8 7 8 7 7 8 7 8 7 8 8 8 8 8 8 8 8 8 8 8 7 8 7 8 8 7 7 7 8 8 8 8 8 7 7 7 7 8 8 8 8 7 7 7 7 7 8 8 8 8 7 7 7 7 7 8 8 8 8 7 7 7 7 7 8 8 8 8 7 7 7 7 7 8 8 8 8 7 7 7 7 8 8 7 8 8 32 7 7 8 7 7 8 7 8 7 8 7 8 7 7 7 8 7 8 7 8 7 8 7 7 8 7 8 7 7 8 7 8 7 7 8 7 8 7 8 7 En diagrama de barras: La imagen, pues, es un cuadrado gris. Si añadimos un valor AC de 100: 700 100 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 33 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 El resultado de aplicar IDCT sería: 1 05 1 05 1 05 1 05 1 05 1 05 1 05 1 05 1 02 1 02 1 02 1 02 1 02 1 02 1 02 1 02 9 9 8 7 7 7 7 1 4 8 3 0 9 9 8 7 7 7 7 1 4 8 3 0 9 9 8 7 7 7 7 1 4 8 3 0 9 9 8 7 7 7 7 1 4 8 3 0 9 9 8 7 7 7 7 1 4 8 3 0 9 9 8 7 7 7 7 1 4 8 3 0 9 9 8 7 7 7 7 1 4 8 3 0 9 9 8 7 7 7 7 1 4 8 3 0 En diagrama de barras: 34 La imagen resultante sería: Como ultimo ejemplo, añadamos un valor AC 100 en otra posición distinta: 35 700 0 100 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 Al aplicar la IDCT obtenemos: 10 4 9 4 10 4 9 10 4 4 4 1 4 10 1 9 4 1 1 8 1 1 1 7 1 1 10 4 9 4 8 1 4 4 1 10 9 8 7 4 4 1 10 9 8 7 4 4 1 10 9 8 7 7 4 1 1 9 8 7 7 8 1 1 1 8 7 7 8 9 1 1 1 7 7 8 9 10 1 1 4 7 8 9 10 4 1 4 4 8 10 4 9 4 10 4 36 10 4 9 4 10 4 8 1 9 4 7 1 8 1 7 1 7 1 8 1 7 1 9 4 8 1 10 4 9 4 10 4 En diagrama de barras: La imagen resultante sería: 37 4.Documentación de la aplicación. De las posibles aplicaciones que podrían realizarse sobre videos mpeg, hemos pensado que la mejor opción era hacer un reproductor. La principal razón para tomar esta decisión era que la mayoría de las veces que se utilizaba vídeo mpeg era simplemente para su reproducción. El problema que se planteaba con esta elección era que la realización de esta aplicación implicaba una dificultad que en principio sobrepasaba los objetivos de la asignatura, ya que era necesario no sólo conocer el estándar mpeg en todos sus niveles, sino que además era necesario hacer gran cantidad de funciones de tratamiento de vídeo. Por todo esto decidimos buscar aplicaciones que nos facilitasen la tarea. Las aplicaciones utilizadas son libmpeg2 y libvo que a partir de ellas se obtienen las librerías libmpeg2.lib y libvo.lib. La elección de estas librerías se debe a que la inmensa mayoría de aplicaciones encontradas en código abierto eran para GNU/Linux y por diversas razones era necesario que nuestra aplicación fuese para entorno Windows. Por esto hemos utilizado estas librerías ya que son válidas para ambos sistemas operativos, 38 debido a que por una parte tiene archivos comunes a Linux y Windows y por otra tiene archivos específicos para cada una de estas plataformas. Aplicaciones de Linux muy utilizadas usan estas librerías, como pueden ser: xine, Mplayer, movietime, etc. La dirección de internet de las que nos hemos bajado los ficheros necesarios para la obtención de las librerías libmpeg2.lib y libvo.lib es: http://libmpeg.sourceforge.net Pues bien, el reproductor está formado a partir de dos partes bien diferenciadas una que llamaremos mpeg y otra denominada decoder. Veamos a continuación cada uno de ellos. Mpeg El resultado de este programa son tres librerías denominadas libmpeg2dec.lib libmpeg2.lib libvo.lib que posteriormente se utilizarán en la aplicación decoder. Para la obtención de estas librerías se ha realizado un Workspace con tres proyectos, uno por cada una de las librerías. libmpeg2dec Este proyecto está formado por un único archivo en C a partir del cual se obtiene el fichero libmpeg2dec.lib. Este fichero utiliza las otras dos librerías y es básicamente el reproductor de vídeo en modo consola. mpeg2dec.c no contiene método principal, ya que el objetivo no es obtener un ejecutable sino una librería que será usada por el programa decoder y así añadir más funcionalidades y un entorno gráfico que facilite su uso. Las principales funciones que contiene esta librería son: 39 static void inicializar (void) Inicializa los drivers, llamando a la función vo_drivers() de libvo y abre el fichero seleccionado en el reproductor. Si el fichero no se puede abrir, el programa termina. static void decodificar_mpeg2 (uint8_t * current, uint8_t * end) Realiza la decodificación mpeg utilizando la libreria libmpeg2 void set_fichero(char* f) Pone el valor de la cadena fichero a f. Esta función será llamada desde decoder, y la cadena contiene el nombre del fichero que se quiere reproducir. char* get_fichero(void) Devuelve el nombre de la cadena fichero, la cual contiene el nobre del fichero que se quiere reproducir. void set_stop(int x) Pone el valor de la variable global stop a x. int get_stop(void) Devuelve el valor de la variable global stop. void pulsar_pause(void) Cambia el valor de la variable global pause, si este está a uno lo pone a cero y viceversa. Esta función cuando es llamada desde el decoder cuando se pulsa el botón de pausa y hace que la reproducción de detenga con un determinado frame en pantalla. Cuando se pulsa el botón otra vez, la reproducción continúa por el mismo momento en el que se dejó. static void extraer_frames (void) 40 Va estrayendo los frames del fichero al buffer para que posteriormente sean decodificados y visualizados. int play (void) Función llamada desde decoder, que inicializa las variables globales stop y pause a cero y llama a la función inicializar(). Si todo correcto llama a ps_loop() , libmpeg2.lib Está implementada en C y se ha obtenido tras la compilación del proyecto libmpeg2 con Microsoft Visual C++. El proyecto y todos los ficheros se encuentran en la carpeta \codigo\mpeg\vc++. Muchos de los ficheros de este proyecto contienen funciones que no son utilizadas por el reproductor, pero que incluimos para posibles ampliaciones. El principal contenido de esta librería son funciones para la decodificación de mpeg. libvo.lib Está implementada en C y se ha obtenido tras la compilación del proyecto libvo con Microsoft Visual C++. El proyecto y todos los ficheros se encuentran en la carpeta \codigo\mpeg\vc++. Al igual que libmpeg2.lib muchos de los ficheros de este proyecto contienen funciones que no son utilizadas por el reproductor. El principal cometido de esta librería es el control de la salida de vídeo (drivers de la tarjeta gráfica) y mostrarlo por pantalla. Decoder Este es el módulo del entorno gráfico del reproductor, para ello crea la ventana principal y los botones y espera las pulsaciones de los mismos. Su misión es recibir las órdenes que el usuario le mande y traducirlas en llamadas a las principales funciones del reproductor libmpeg2dec. Las funciones de la librería libmpeg2dec.lib que decoder utiliza son: 41 int play (play); void pulsar_pause (void); int get_stop (void); void set_stop (int); void set_fichero (char*); char* get_fichero (void); Para que el programa espere posibles pulsaciones de los botones una vez que se está reproduciendo el vídeo, ha sido necesario la creación de un hilo de ejecución. De no haber sido así, una vez que el programa estuviese reproduciendo un fichero, no se podría haber interactuado con él hasta que concluyese la reproducción. Esto se ha hecho de la siguiente forma: Una vez que se crean los botones, se crea el hilo de ejecución mediante la función _beginthread ( decod, 0, NULL ). Por otra parte la función void __cdecl decod(void *x) lo que hace es esperar a que la variable stop valga cero, producido por la pulsación del botón reproducir. Cuando esto ocurre llama a la función play() que hace comenzar la reproducción del vídeo, permitiendo al mismo tiempo la emisión de nuevas órdenes sobre el reproductor. 5.Conclusiones El MPEG 2 es un estándar de compresión para imágenes con movimiento a velocidades de píxel entre 5 y 10 Mbit/s. El estándar de vídeo consiste de cinco perfiles, referido a la complejidad del algoritmo de compresión y cuatro niveles, los cuales se refieren a la resolución del vídeo original. El nivel principal y el perfil principal es la combinación más usada por las opciones MPEG 2. MPEG 2 es un estándar emergente para reproducir vídeo en pantalla completa y audio con calidad de transmisión; está mas orientado hacia TV que MPEG 1, por ejemplo, MPEG 2 sabe cómo se entrelazan los cuadros en TV, además de que la calidad 42 de la imagen es superior. Por otra parte, como no puede dependerse de la llegada del paquete anterior antes de descomprimir el paquete actual, MPEG 2 no es ideal para transmisión vía Internet. 6.Bibliografía MPEG video compresión technique http://rnvs.informatik.tu-chemnitz.de/~jan/MPEG/HTML/mpeg_tech.html Trabajo dirigido MPEG4 José Baena Roca MPEG-2: Descripción del estándar http://videomaster.dragonport.net/documentos/mpg2.htm Decodificación de audio y video http://www.tid.es/presencia/publicaciones/comsid/esp/articulos/vol61/decod/decod.html Video Compression: MEPG-4 and Beyond Ali Saman Tosun 43 7.Anexo A continuación adjuntamos el código de los dos fichero principales que componen el reproductor: Codigo del fichero mpeg2dec.c #include "config.h" #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <signal.h> #include <inttypes.h> #include "mpeg2.h" #include "video_out.h" #include "convert.h" #define BUFFER_SIZE 4096 int stop=1; int pause=0; char* fichero=NULL; static uint8_t buffer[BUFFER_SIZE]; static FILE * in_file; static int demux_track = 0; static int demux_pid = 0; 44 static mpeg2dec_t * mpeg2dec; static vo_open_t * output_open = NULL; static vo_instance_t * output; static void inicializar (void) { vo_driver_t * drivers; drivers = vo_drivers (); //Devuelve los drivers para la salida del video demux_track = 0xe0; if (output_open == NULL) output_open = drivers[0].open; in_file = fopen (fichero, "rb"); //Utiliza el primer driver //Abre el fichero mpeg if (!in_file) //No se puede abrir el fichero exit (1); } void set_fichero(char* f) // establece el nombre del fichero mpeg a f { fichero=f; } char* get_fichero(void) //devuelve el nombre del fichero mpeg { 45 return fichero; } void set_stop(int x) //pone la variable stop a x { stop=x; } int get_stop(void) //devuelve el valor de la variable stop { return stop; } void pulsar_pause(void) //Modifica el valor de la variable pause { if(pause==1) pause=0; else pause=1; } static void decodificar_mpeg2 (uint8_t * current, uint8_t * end) { //realiza la decodificación mpeg utilizando la libreria libmpeg2 const mpeg2_info_t * info; int state; vo_setup_result_t setup_result; 46 mpeg2_buffer (mpeg2dec, current, end); //donde comienza y acaba el buffer con los frames info = mpeg2_info (mpeg2dec); //obtiene la informacion while (1) { state = mpeg2_parse (mpeg2dec); //analiza el estado switch (state){ case -1: //si falla la funcion return; case STATE_SEQUENCE: if (output->setup (output, info->sequence->width,info->sequence->height, &setup_result)) { //Ha fallado el display exit (1); } if (setup_result.convert) mpeg2_convert (mpeg2dec, setup_result.convert, NULL); if (output->set_fbuf) { uint8_t * buf[3]; void * id; mpeg2_custom_fbuf (mpeg2dec, 1); output->set_fbuf (output, buf, &id); mpeg2_set_buf (mpeg2dec, buf, id); output->set_fbuf (output, buf, &id); mpeg2_set_buf (mpeg2dec, buf, id); } else if (output->setup_fbuf) 47 { uint8_t * buf[3]; void * id; output->setup_fbuf (output, buf, &id); mpeg2_set_buf (mpeg2dec, buf, id); output->setup_fbuf (output, buf, &id); mpeg2_set_buf (mpeg2dec, buf, id); output->setup_fbuf (output, buf, &id); mpeg2_set_buf (mpeg2dec, buf, id); } break; case STATE_PICTURE: if (output->set_fbuf) { uint8_t * buf[3]; void * id; output->set_fbuf (output, buf, &id); mpeg2_set_buf (mpeg2dec, buf, id); } if (output->start_fbuf) output->start_fbuf (output, info->current_fbuf->buf,info->current_fbuf->id); break; case STATE_PICTURE_2ND: //no debe hacer nada break; case STATE_SLICE: 48 case STATE_END: //termina de decodificar un frame if (info->display_fbuf) { //muestra el frame output->draw (output, info->display_fbuf->buf,info->display_fbuf->id); } if (output->discard && info->discard_fbuf) //descarta el frame output->discard (output, info->discard_fbuf->buf,info->discard_fbuf->id); break; } } } //Nota: la función demux ha sido tomada de otro reproductor que utiliza las mismas librerias #define DEMUX_PAYLOAD_START 1 static int demux (uint8_t * buf, uint8_t * end, int flags) { static int mpeg1_skip_table[16] = {0, 0, 4, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; #define DEMUX_HEADER 0 #define DEMUX_DATA 1 #define DEMUX_SKIP 2 static int state = DEMUX_SKIP; static int state_bytes = 0; static uint8_t head_buf[264]; uint8_t * header; int bytes; 49 int len; #define NEEDBYTES(x) \ do { \ int missing; \ \ missing = (x) - bytes; \ if (missing > 0) { \ if (header == head_buf) { \ if (missing <= end - buf) { \ memcpy (header + bytes, buf, missing); \ buf += missing; \ bytes = (x); \ } else { \ memcpy (header + bytes, buf, end - buf); \ state_bytes = bytes + end - buf; \ return 0; \ } \ } else { \ memcpy (head_buf, header, bytes); \ state = DEMUX_HEADER; \ state_bytes = bytes; \ return 0; \ } \ } \ } while (0) #define DONEBYTES(x) do { if (header != head_buf) \ \ \ 50 buf = header + (x); \ } while (0) if (flags & DEMUX_PAYLOAD_START) goto payload_start; switch (state) { case DEMUX_HEADER: if (state_bytes > 0) { header = head_buf; bytes = state_bytes; goto continue_header; } break; case DEMUX_DATA: if (demux_pid || (state_bytes > end - buf)) { decodificar_mpeg2 (buf, end); state_bytes -= end - buf; return 0; } decodificar_mpeg2 (buf, buf + state_bytes); buf += state_bytes; break; case DEMUX_SKIP: if (demux_pid || (state_bytes > end - buf)) { state_bytes -= end - buf; return 0; } buf += state_bytes; break; } 51 while (1) { if (demux_pid) { state = DEMUX_SKIP; return 0; } payload_start: header = buf; bytes = end - buf; continue_header: NEEDBYTES (4); if (header[0] || header[1] || (header[2] != 1)) { if (demux_pid) { state = DEMUX_SKIP; return 0; } else if (header != head_buf) { buf++; goto payload_start; } else { header[0] = header[1]; header[1] = header[2]; header[2] = header[3]; bytes = 3; goto continue_header; } } if (demux_pid) { if ((header[3] >= 0xe0) && (header[3] <= 0xef)) goto pes; fprintf (stderr, "bad stream id %x\n", header[3]); 52 exit (1); } switch (header[3]) { case 0xb9: /* program end code */ /* DONEBYTES (4); */ /* break; */ return 1; case 0xba: /* pack header */ NEEDBYTES (12); if ((header[4] & 0xc0) == 0x40) { /* mpeg2 */ NEEDBYTES (14); len = 14 + (header[13] & 7); NEEDBYTES (len); DONEBYTES (len); /* header points to the mpeg2 pack header */ } else if ((header[4] & 0xf0) == 0x20) { /* mpeg1 */ DONEBYTES (12); /* header points to the mpeg1 pack header */ } else { fprintf (stderr, "weird pack header\n"); exit (1); } break; default: if (header[3] == demux_track) { pes: NEEDBYTES (7); if ((header[6] & 0xc0) == 0x80) { /* mpeg2 */ NEEDBYTES (9); len = 9 + header[8]; 53 NEEDBYTES (len); /* header points to the mpeg2 pes header */ if (header[7] & 0x80) { uint32_t pts; pts = (((buf[9] >> 1) << 30) | (buf[10] << 22) | ((buf[11] >> 1) << 15) | (buf[12] << 7) | (buf[13] >> 1)); mpeg2_pts (mpeg2dec, pts); } } else { /* mpeg1 */ int len_skip; uint8_t * ptsbuf; len = 7; while (header[len - 1] == 0xff) { len++; NEEDBYTES (len); if (len > 23) { fprintf (stderr, "too much stuffing\n"); break; } } if ((header[len - 1] & 0xc0) == 0x40) { len += 2; NEEDBYTES (len); } len_skip = len; len += mpeg1_skip_table[header[len - 1] >> 4]; NEEDBYTES (len); 54 /* header points to the mpeg1 pes header */ ptsbuf = header + len_skip; if (ptsbuf[-1] & 0x20) { uint32_t pts; pts = (((ptsbuf[-1] >> 1) << 30) | (ptsbuf[0] << 22) | ((ptsbuf[1] >> 1) << 15) | (ptsbuf[2] << 7) | (ptsbuf[3] >> 1)); mpeg2_pts (mpeg2dec, pts); } } DONEBYTES (len); bytes = 6 + (header[4] << 8) + header[5] - len; if (demux_pid || (bytes > end - buf)) { decodificar_mpeg2 (buf, end); state = DEMUX_DATA; state_bytes = bytes - (end - buf); return 0; } else if (bytes > 0) { decodificar_mpeg2 (buf, buf + bytes); buf += bytes; } } else if (header[3] < 0xb9) { fprintf (stderr, "looks like a video stream, not system stream\n"); DONEBYTES (4); } else { NEEDBYTES (6); DONEBYTES (6); bytes = (header[4] << 8) + header[5]; 55 if (bytes > end - buf) { state = DEMUX_SKIP; state_bytes = bytes - (end - buf); return 0; } buf += bytes; } } } } static void extraer_frames (void) //Va estrayendo los frames { uint8_t * end; do { end = buffer + fread (buffer, 1, BUFFER_SIZE, in_file); if (demux (buffer, end, 0)) //demultiplexa break; else if (stop==1) break; else if (pause==1) { while (pause==1) {;} } } while (end == buffer + BUFFER_SIZE); } 56 int play (void) //Funcion principal, aunque no es un método main por ser una libreria { //inicializamos las variables globales stop y pause a cero stop=0; pause=0; inicializar (); output = output_open (); if (output == NULL) { fprintf (stderr, "No se puede mostrar\n"); return 1; } mpeg2dec = mpeg2_init(); if (mpeg2dec == NULL) exit (1); extraer_frames (); mpeg2_close (mpeg2dec); if (output->close) output->close (output); return 0; } 57 Codigo del fichero decoder.cpp #include "stdafx.h" #include "resource.h" #include <process.h> /* _beginthread, _endthread */ #include <windows.h> #include <commdlg.h> #include <stdlib.h> #define MAX_LOADSTRING 100 extern "C" int play(); extern "C" void pulsar_pause(); extern "C" int get_stop(); extern "C" void set_stop(int); extern "C" void set_fichero(char*); extern "C" char* get_fichero(void); char* fich; void __cdecl decod(void*); // Variables globales: HINSTANCE hInst; TCHAR szTitle[MAX_LOADSTRING]; 58 TCHAR szWindowClass[MAX_LOADSTRING]; static OPENFILENAME ofn ; static char szFilter[] = "Mpeg files (*.mpg)\0*.mpg\0" \ "All Files (*.*)\0*.*\0\0" ; const int ButtonId1 = 1; const int ButtonId2 = 2; const int ButtonId3 = 3; const int ButtonId4 = 4; const int ButtonId5 = 5; const int ButtonId6 = 6; // Cabeceras de funciones incluidas en este modulo. ATOM MyRegisterClass(HINSTANCE hInstance); BOOL InitInstance(HINSTANCE, int); LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK About(HWND, UINT, WPARAM, LPARAM); int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR int lpCmdLine, nCmdShow) { MSG msg; HACCEL hAccelTable; char cad[_MAX_PATH+1]; 59 ofn.lStructSize = sizeof (OPENFILENAME) ; ofn.hwndOwner = NULL ; ofn.hInstance = NULL ; ofn.lpstrFilter = szFilter ; ofn.lpstrCustomFilter = NULL ; ofn.nMaxCustFilter ofn.nFilterIndex ofn.lpstrFile =0; =0; = cad ; // Set in Open and Close functions ofn.nMaxFile = _MAX_PATH ; ofn.lpstrFileTitle = NULL ; // Set in Open and Close functions ofn.nMaxFileTitle = _MAX_FNAME + _MAX_EXT ; ofn.lpstrInitialDir = NULL ; ofn.lpstrTitle = "Abrir fichero" ; ofn.nFileOffset =0; ofn.nFileExtension ofn.Flags =0; = OFN_HIDEREADONLY | OFN_CREATEPROMPT ; ofn.lpstrDefExt = "mpg" ; ofn.lCustData = 0L ; ofn.lpfnHook = NULL ; ofn.lpTemplateName = NULL ; // Initializamos las cadenas strcpy(cad,""); LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING); LoadString(hInstance, IDC_DECODER, szWindowClass, MAX_LOADSTRING); MyRegisterClass(hInstance); if (!InitInstance (hInstance, nCmdShow)) 60 { return FALSE; } hAccelTable = LoadAccelerators(hInstance, (LPCTSTR)IDC_DECODER); while (GetMessage(&msg, NULL, 0, 0)) { if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } } return msg.wParam; } ATOM MyRegisterClass(HINSTANCE hInstance) { WNDCLASSEX wcex; wcex.cbSize = sizeof(WNDCLASSEX); wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = (WNDPROC)WndProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; 61 wcex.hInstance = hInstance; wcex.hIcon = LoadIcon(hInstance, (LPCTSTR)IDI_DECODER); wcex.hCursor = LoadCursor(NULL, IDC_ARROW); wcex.hbrBackground = (HBRUSH)(COLOR_WINDOWFRAME+1); wcex.lpszMenuName = (LPCSTR)IDC_DECODER; wcex.lpszClassName = szWindowClass; wcex.hIconSm = LoadIcon(wcex.hInstance, (LPCTSTR)IDI_SMALL); return RegisterClassEx(&wcex); } // En esta función se crea y muestra la ventana principal del programa BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) { HWND hWnd; hInst = hInstance; hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 400, 250, NULL, NULL, hInstance, NULL); if (!hWnd) { return FALSE; } ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); 62 return TRUE; } // Esta función procesa los mensajes para la ventana principal LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { int wmId, wmEvent,res; PAINTSTRUCT ps; HDC hdc; TCHAR szHello[MAX_LOADSTRING]; LoadString(hInst, IDS_HELLO, szHello, MAX_LOADSTRING); switch (message) { case WM_CREATE: //Creamos los botones del reproductor CreateWindow //Botón reproducir ( "BUTTON", "Reproducir", WS_CHILD | WS_VISIBLE, 50, 50, 100, 50, hWnd, (HMENU) ButtonId1, hInst, NULL ); 63 CreateWindow //Botón pausa ( "BUTTON", "Pausa", WS_CHILD | WS_VISIBLE, 150, 50, 100, 50, hWnd, (HMENU) ButtonId2, hInst, NULL ); CreateWindow //Botón parar ( "BUTTON", "Parar", WS_CHILD | WS_VISIBLE, 250, 50, 100, 50, hWnd, (HMENU) ButtonId3, hInst, NULL ); CreateWindow //Botón para cargar el fichero ( "BUTTON", "Cargar", WS_CHILD | WS_VISIBLE, 50, 100, 100, 50, hWnd, (HMENU) ButtonId4, hInst, NULL ); 64 CreateWindow //Botón para abandonar la aplicación ( "BUTTON", "Salir", WS_CHILD | WS_VISIBLE, 150, 100, 100, 50, hWnd, (HMENU) ButtonId5, hInst, NULL ); CreateWindow //Botón Acerca de ( "BUTTON", "Acerca de", WS_CHILD | WS_VISIBLE, 250, 100, 100, 50, hWnd, (HMENU) ButtonId6, hInst, NULL ); _beginthread( decod, 0, NULL ); case WM_COMMAND: wmId = LOWORD(wParam); wmEvent = HIWORD(wParam); switch (wmId) { 65 case IDM_ABOUT: DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, (DLGPROC)About); break; case IDM_EXIT: DestroyWindow(hWnd); break; case ButtonId1: //Se ha pulsado reproducir if(get_fichero()==NULL) MessageBox(hWnd, "Debe cargar el fichero", "Info", MB_OK); else set_stop(0); break; case ButtonId2: //Se ha pulsado pausa pulsar_pause(); break; case ButtonId3: //Se ha pulsado stop set_stop(1); break; case ButtonId4: //Se ha pulsado cargar fichero res=GetOpenFileName (&ofn); fich=ofn.lpstrFile; if(res) set_fichero(fich); break; case ButtonId5: //Se ha pulsado salir DestroyWindow(hWnd); break; case ButtonId6: //Se ha pulsado Acerca de 66 DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, (DLGPROC)About); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } break; case WM_PAINT: hdc = BeginPaint(hWnd, &ps); RECT rt; GetClientRect(hWnd, &rt); DrawText(hdc, szHello, strlen(szHello), &rt, DT_CENTER); EndPaint(hWnd, &ps); break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; } LRESULT CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_INITDIALOG: 67 return TRUE; case WM_COMMAND: if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) { EndDialog(hDlg, LOWORD(wParam)); return TRUE; } break; } return FALSE; } void __cdecl decod(void *x) { while(1) { if(!get_stop()){ play(); break; } else Sleep(1); } _endthread(); } 68