Академический Документы
Профессиональный Документы
Культура Документы
Florianópolis
2009
i
Banca examinadora:
Dedicatória
Dedico este trabalho em memória dos meus falecidos pais, principalmente a minha mãe,
Amélia Maciel Ferreira (1950-2007), que sempre me apoiou em meus projetos e me ensinou que
perseverança, acima de tudo, é o que nos leva a alcançar nossos objetivos.
v
Agradecimentos
Agradeço primeiramente aos meus pais, que com muito esforço deram uma educação de
qualidade aos meus irmãos e a mim.
Um agradecimento especial ao meu orientador, Prof. Cristian Koliver, que sempre foi muito
atencioso, dando feedback sobre os resultados do trabalho. Ao Prof. Jean-Marie Farines, que me
incentivou a dar continuidade ao projeto durante o primeiro ano de desenvolvimento. E ao Prof.
Willrich que por muitas vezes fez a ponte entre o Departamento de Automação e Sistemas (DAS) e o
Departamento de Informática e Estatística (INE).
Também tenho que agradecer a sociedade brasileira como um todo, pois foram verbas
públicas que me subsidiaram durante esses anos de estudo. Obrigado Brasilllll!
v
Resumo
Abstract
This graduation conclusion work describe the project and implementation of a video encoder
in H.264 standard using the Java programming language. The choice of this standard to implement is
given by the nonexistence of platform independent H.264 implementations and the rising interest in
this standard, once its becoming a replacement for MPEG-2 due its greater video encoding efficiency,
in terms of compression ratio and quality obtained.
Once the source code used as reference to this project development was originally written in
C language, this work also describe the architectural modifications in the original encoder, based on a
structured architecture model, to a object oriented model. This model can be used as basis to new
H.264 encoder implementations or even other standards.
Finally, in this work is performed a comparison among the C and the Java encoder
implementations performance, in parameters terms as speed and memory consumption, among
others, and assessed the feasibility of using this encoder for interactive multimedia applications, such
as videophone.
As a implementation work, there's a high level of details on the H.264 encoder operation.
Some details in this work are not available in any reference material.
v
Lista de figuras
FIGURA 4.1 – (A) ESTRUTURA E (B) EXEMPLO DE QUADRO YUV 4:2:0 PLANAR......................19
FIGURA 5.4 – REORDENAÇÃO ZIG-ZAG PARA BLOCOS 4X4 LUMA (MODO FRAME)................28
ACRÔNIMOS......................................................................................................................................XV
GLOSSÁRIO......................................................................................................................................XVI
1.1 MOTIVAÇÕES.........................................................................................................................................1
1.2 ORGANIZAÇÃO DO DOCUMENTO..................................................................................................................2
CAPÍTULO 6: CONCLUSÕES.............................................................................................................35
APÊNDICE A: CÓDIGO-FONTE..........................................................................................................37
A1 CLASSES DE SUPORTE...........................................................................................................................37
A1.1 CLASSE REGISTRY.............................................................................................................................37
A2 CLASSES PARA LEITURA DO ARQUIVO DE VÍDEO YUV....................................................................................38
A2.1 CLASSE YUVPARSER........................................................................................................................38
A2.2 CLASSE YUVVIDEOTRACK..................................................................................................................41
A2.3 CLASSE YUVFORMATHANDLER............................................................................................................43
A2.4 CLASSE YUVFRAMEBUFFER...............................................................................................................44
A3 CLASSES PARA ESCRITA NO ARQUIVO H.264..............................................................................................47
A3.1 CLASSE H264MUX...........................................................................................................................47
A3.2 CLASSE NALUBYTESTREAM...............................................................................................................49
A3.3 CLASSE NALU................................................................................................................................50
A4 CLASSES DE CODIFICAÇÃO 1 – CONTROLE ................................................................................................52
A4.1 CLASSE H264ENCODER.....................................................................................................................52
A4.2 CLASSE BASELINEPROFILEFACTORY.......................................................................................................55
A5 CLASSES DE CODIFICAÇÃO 2 – ALGORITMOS..............................................................................................56
A5.1 CLASSE INTEGERTRANSFORM................................................................................................................56
A5.2 CLASSE ZIGZAGFRAMESCANNER..........................................................................................................59
A5.3 CLASSE INTEGERROUNDQUANTIZER.......................................................................................................60
A6 CLASSES DE CODIFICAÇÃO 3 – MODOS DE CODIFICAÇÃO...............................................................................64
A6.1 INTERFACE ENCODINGMODE.................................................................................................................64
A6.2 CLASSE ABSTRACTENCODINGMODE.......................................................................................................65
A6.3 CLASSE INTRA16X16ENCODINGMODE....................................................................................................66
A6.4 CLASSE IPCMENCODINGMODE...........................................................................................................69
A6.5 CLASSE INTRA16X16LUMAABSTRACTPREDICTOR......................................................................................70
A6.6 CLASSE INTRA8X8CHROMAABSTACTPREDICTOR.......................................................................................75
A6.7 CLASSE INTRA16X16LUMADCPREDICTOR..............................................................................................81
A6.8 CLASSE INTRA8X8CHROMADCPREDICTOR..............................................................................................82
A7 CLASSES PARA MEDIÇÃO DE DISTORÇÃO.....................................................................................................84
A7.1 INTERFACE DISTORTIONMETRIC.............................................................................................................84
A7.1 CLASSE SATD................................................................................................................................84
A8 CLASSES PARA CODIFICAÇÃO DE ENTROPIA.................................................................................................86
A8.1 CLASSE VLCTABLE...........................................................................................................................87
A8.2 CLASSE CAVLC..............................................................................................................................88
A9 CLASSES PARA CONTROLE DOS MACROBLOCOS VIZINHOS................................................................................96
A9.1 CLASSE MACROBLOCKACCESS..............................................................................................................96
A9.2 CLASSE MACROBLOCKACCESSNONMBAFF............................................................................................98
A9.3 CLASSE MACROBLOCKINFO..................................................................................................................99
A9.4 CLASSE MACROBLOCKPOSITION..........................................................................................................102
B1 TIPOS DE COMPRESSÃO.......................................................................................................................111
B2 CATEGORIAS DE COMPRESSÃO..............................................................................................................111
B2.1 CODIFICAÇÃO DE ENTROPIA................................................................................................................111
B2.1.1 Supressão de sequências repetitivas......................................................................................112
B2.1.2 Codificação Estatística............................................................................................................112
B2.2 CODIFICAÇÃO DA FONTE....................................................................................................................113
B2.2.1 Codificação de Transformada.................................................................................................113
B2.2.2 Codificação Diferencial............................................................................................................114
B2.2.3 Quantização Vetorial...............................................................................................................114
B3 COMPRESSÃO DE IMAGEM.....................................................................................................................115
B3.1 O PADRÃO JPEG..........................................................................................................................115
B3.1.1 Passos da Codificação Progressiva........................................................................................115
B3.2 PADRÃO MPEG.............................................................................................................................117
B3.2.1 Quadros de Referência e Intracodificados..............................................................................118
B3.2.2 Compressão de Quadros I......................................................................................................120
B3.2.3 Compressão de Quadros P e B...............................................................................................120
REFERÊNCIAS BIBLIOGRÁFICAS..................................................................................................121
Acrônimos
Artefato
Refere-se a algum tipo de distorção visual em uma imagem.
Artefato de blocagem
Tradução do termo blocking artifacts que se refere ao padrão do bloco em uma seqüência
comprimida devido à quantização individual de cada bloco, levando à discontinuidades entre os
blocos adjacentes. Artefato de blocagem é uma das mais perceptíveis distorções visuais.
CODEC
Um CODEC (Compression Decompression Algorithm) é um programa que codifica e
decodifica dados digitais com intuito de comprimir esses dados, reduzindo a quantidade de
espaço necessária para armazenar ou a largura de banda para transmiti-los.
Crominância (Chroma)
Corresponde à amostra dos dois sinais de cor (U e V). Geralmente possui uma freqüência de
amostragem menor em relação às amostras de luma.
Framework
É um conjunto de classes que fornecem uma funcionalidade genérica comum a vários
projetos de software. Diferente das bibliotecas, o Framework quem dita o fluxo de controle da
aplicação, o que é chamado de Inversão de Controle.
Luma
Corresponde às amostras do sinal acromático (componente Y do espaço de cor YCbCr), ou
brilho. Segundo [4], luma é diferente de luminância, uma vez que esta é uma medida, definida
pelo CIE, puramente fotométrica e independente de dispositivo.
Partição
É uma região do macrobloco que possui seu próprio Vetor de Movimento.
Predição
Processo no qual os valores de uma amostra são estimados com base em uma amostra
previamente codificada.
Quadro
É o conjunto de amostras que formam uma imagem estática. Um vídeo é o conjunto de
quadros (figuras estáticas) exibidos em intervalos de tempo. No caso de vídeo entrelaçado,
um quadro é o conjunto dos campos superior e inferior, correspondentes às linhas pares e
ímpares, respectivamente.
Quantização
É a redução da informação, por meio do truncamento de números, para obter uma maior taxa
de compressão. O parâmetro de quantização QP seleciona o nível de truncamento dos
coeficientes. Quanto mais alto o QP, maior o truncamento e compressão dos dados, e
consequentemente menor a qualidade do vídeo decodificado.
1
O H.264 é um padrão para compressão de vídeo desenvolvido pela ITU-T Video Coding
Experts Group (VCEG) em conjunto com a ISO/IEC MPEG que formaram uma parceria conhecida por
Joint Video Team (JVT). O padrão H.264 foi baseado no padrão MPEG-4 Part 10 ou AVC (Advanced
Video Coding). Sua versão final, formalmente chamada por ISO/IEC 14496-10, foi lançada em 2003.
Posteriormente, foram desenvolvidas extensões da versão original do padrão, conhecidas por Fidelity
Range Extensions (FRExt).
O projeto do H.264/AVC foi norteado pela criação de um padrão de compressão de vídeo
capaz de fornecer boa qualidade a uma taxa de bits baixa em relação aos padrões já existentes,
como o MPEG-1, MPEG-2 e H.263. Outra meta do projeto foi a de criar um padrão que permitisse a
codificação de vídeos com diferentes taxas de bits/resolução..
Uma lacuna ainda existente em relação ao padrão H.264, é que, a despeito da existência de
várias implementações na forma de codificadores (encoders) e decodificadores/players (decoders),
ainda não há implementações do codificador independentes de plataforma (processador + sistema
operacional), o que exige novas implementações para novos dispositivos com plataformas muito
específicas (por exemplo, dispositivos móveis como celulares). É pertinente salientar que, devido às
melhorias introduzidas pelo padrão H.264 em relação ao seus antecessores, aumentou
razoavelmente a complexidade dos algoritmos utilizados, exigindo otimizações no código que
dificultam bastante a obtenção de implementações eficazes e, ao mesmo tempo, eficientes, condição
essencial para o seu uso em máquinas com recursos computacionais (capacidade de processamento
e memória, particularmente) restritos.
O desenvolvimento deste trabalho também tem como objetivo fornecer uma implementação e
um texto de fácil compreensão para estudo e desenvolvimento de outros codificadores.
1.1 Motivações
1. Código de máquina independente de plataforma: muito mais que uma linguagem orientada a
objetos, Java é uma tecnologia na qual um programa Java é compilado gerando um bytecode
(código de máquina) que é executado por qualquer máquina virtual Java (JVM);
2. Diversidade de ambientes e recursos para programação: além de possuir uma gama enorme
de recursos para facilitar a programação, a máquina virtual Java é disponível em diversos
equipamentos e ambientes, como navegadores, mainframes, SOs, celulares, palmtops e
2
cartões inteligentes, entre outros. O que abre um grande leque de consumidores para os
produtos desenvolvidos nessa plataforma; e
3. Desempenho: ao longo dos anos, foram agregadas à plataforma Java diversas otimizações
que tornaram o desempenho de um programa Java próximo a um mesmo programa
codificado em C++, com código compilado “nativo”. Dentre essas otimizações, destaca-se a
compilação “especulativa”, que aproveita o tempo ocioso do processador para pré-compilar o
bytecode para código nativo. Outro mecanismo - o HotSpot da Sun - guarda informações
disponíveis somente em tempo de execução (por exemplo, número de usuários,
processamento usado, memória disponível), que possibilitam que a JVM vá "aprendendo" e
melhorando seu desempenho;
Não obstante a implementação descrita neste trabalho ter tido, como referência, uma
implementação pré-existente em C, nosso código não representa, de forma alguma, uma mera
transcodificação de C para Java, o que, per si, já seria demasiado trabalhoso, dada não só à
diferença entre os paradigmas de programação utilizados por essas linguagens, mas também a
aspectos como formato e tipos de dados e uso de chamadas de sistemas específicas de plataforma
(no caso do código em C). Nossa implementação teve como base a criação de modelo orientado a
objetos a partir do código de referência em C, estruturado de forma procedural. O projeto desse
modelo envolveu a aplicação de diversos conceitos de Engenharia de Software
.
Em um primeiro momento, este capítulo aborda de maneira geral os conceitos gerais relativos
à codificação de vídeo. E em um segundo momento, trata sobre as melhorias e pontos principais da
codificação de vídeo especificada pelo padrão H.264. O Anexo B contém informações mais
detalhadas a respeito dos algoritmos de codificação de vídeo.
A maioria dos documentos de referência utiliza o termo figura (picture) para designar tanto
frames (amostras progressivas) quanto fields (amostras entrelaçadas). Entretanto, como o codificador
descrito neste trabalho se restringe a vídeos progressivos, utilizaremos apenas o termo quadro
(frame) para designar tanto figura quanto quadro.
Esta seção aborda de maneira geral o modo pelo qual funciona a compactação (ou
codificação) de vídeo para o formato H.264. Será adotada a mesma divisão apresentada por
Richardson [1], que consiste em separar a codificação de vídeo em três grandes blocos: modelo
temporal, modelo espacial e codificação por entropia. A Figura 2.1 mostra o diagrama do codificador.
Conforme esse diagrama, o codificador recebe como entrada um arquivo ou um stream de vídeo
gerado em tempo real na forma “crua” (raw video), no qual cada quadro é representado por matrizes
de pixels contendo os valores dos componentes Y, U e V; a saída é o vídeo codificado (comprimido).
Nas seções seguintes, são detalhados os papeis de cada bloco desse diagrama.
2.1.1 Pré-Processamento
relação entre esses componentes é 4:1:1, então o componente Y consiste de uma matriz 640x480 e
os outros dois consistem de matrizes 320x240. A preparação dos blocos irá fornecer para o passo
seguinte 4800 blocos para o componente Y, 1200 para U e 1200 para V.
O modelo temporal tem como objetivo reduzir a redundância temporal, ou seja, porções da
imagem que se repetem por vários quadros.
Predição inter quadro é o processo que consiste em estimar valores dos bloco de um quadro
baseado nos valores dos blocos de quadros previamente codificados.
2.1.3.2 Transformada
do sinal amostrado que é função de duas coordenadas espaciais, ou seja, a = f(x,y) onde x e y são as
duas dimensões. Após a transformação, obtém-se a função c= g(Fx,Fy) onde c é um coeficiente e Fx
e Fy são as frequências espaciais para cada direção. O resultado é outro bloco de 64 valores onde
cada valor representa um coeficiente DCT - isto é, uma determinada frequência - e não mais a
amplitude do sinal na posição amostrada (x,y). O coeficiente g(0,0) correspondente às frequências
zero, é chamado de coeficiente DC. Ele representa o valor médio das 64 amostras. Como em um
bloco representando uma porção da imagem os valores amostrados geralmente variam pouco de um
ponto para outro, os coeficientes de mais baixa frequência serão altos e os de média e alta frequência
terão valores baixos ou zero, podendo ser descartados. A energia do sinal é concentrada nas
frequências espaciais mais baixas. A Figura 2.2 [6] é uma representação tridimensional da
transformação DCT.
2.1.3.3 Quantização
A quantização é basicamente uma redução na amplitude do sinal por meio de uma divisão
seguida por arredondamento. O objetivo por trás dessa redução de amplitude das amostras é
transmitir valores que ocupem menos bits. Uma vez recebidos (ou descompactados), esses valores
são redimensionados por meio de uma multiplicação, obtendo valores próximos aos originais.
A quantização é o processo que mais acumula erros durante a codificação de vídeo. Dessa
forma, o H.264/AVC possui algumas melhorias nesse processo com intuito de amenizar esses erros.
A codificação por entropia converte uma série de símbolos, que representam os elementos da
sequência de vídeo, em um fluxo de bits compactados apropriados para transmissão ou
armazenamento [1]. Símbolos de entrada podem incluir coeficientes quantizados de transformadas,
vetores de movimento (deslocamentos no eixo x e y para cada bloco com compensação de
movimento), marcadores (códigos indicando pontos de sincronização), cabeçalhos (de macroblocos,
quadros ou sequência de quadros) e informações suplementares (informações não essenciais para
correta decodificação da mídia).
O padrão H.264 define todo um novo conjunto de divisões de cada quadro do vídeo. Fatias,
Macroblocos e Blocos continuam existindo, tal como nos padrões anteriores. Todavia, a forma como
eles funcionam foi alterada.
7
A fatia (ou slice) tem grande importância no H.264 uma vez que agora ela é o elemento
espacial independente básico [26].
No padrão H.262 (MPEG-2) há três tipos de quadros: I, P e B [15]. Esses tipos são
determinados pela forma como os macroblocos do quadro foram codificados e, consequentemente,
quais informações eles vão necessitar para serem decodificados. Quadros I são codificados sem
fazer referência a qualquer outro quadro, usando apenas informações presentes neles mesmos.
Quadros P usam informações de um outro quadro de referência preditas por meio da compensação
de movimento. Quadros B usam informações preditas de mais de um quadro de referência (um antes
e outro depois, por isso é bidirecional).
No padrão H.264 os tipos I, P e B foram deslocados do nível de Quadro para o nível de Fatia.
Dessa forma, existem fatias I, fatias P e fatias B, além de serem adicionados dois novos tipos de
fatias: switching I pictures (SI) e switching P pictures (SP). Essas novas fatias visam reduzir
significativamente a taxa de bits resultante da compressão.
Para suprir a ausência de um quadro intra codificado – decorrente, por exemplo, de peradas
de pacotes na rede – foi adicionado o quadro IDR, um quadro codificado contendo apenas fatias I ou
SI e que serve como referência primária para os outros quadros.
Esta seção descreve brevemente os aspectos técnicos propostos pelo padrão para melhoria
na habilidade de predizer os valores do conteúdo de um quadro a ser codificado.
Permite uma segmentação mais precisa das regiões de movimento de um quadro por meio
de blocos com tamanhos variando entre 16x16 e 4x4.
No H.264/AVC é possível ao vetor de movimento apontar para áreas fora dos limites do
quadro usado como referência.
8
Além de especificar o deslocamento do vetor de movimento (i.e. quantos pixels uma área
deve ser deslocada), no padrão H.264/AVC é possível especificar a dimensão do vetor, aumentando
significativamente desempenho em casos especiais, tal como transições fade-to-black, fade-in e
cross-fade.
Este recurso não está presente no perfil Baseline.
Da mesma forma como é feita a predição temporal – que utiliza amostras de quadros
anteriores previamente decodificadas para predizer os valores das amostras do quadro sendo
decodificado – a predição espacial utiliza amostras decodificadas do mesmo quadro para predizer os
valores das amostras que estão sendo codificadas.
A predição espacial é uma técnica comum na codificação de imagens estáticas, tal como
JPEG.
9
Em casos especiais é possível aplicar uma transformada Hadamard 2×2, 2×4 ou 4×4 nos
coeficientes DC (de mais baixa frequência) dos blocos 4×4. Essa transformação extra estende a
transformada 4×4 para os tamanhos 8×8, 8×16 (utilizadas para as amostras croma de um MB) ou
16×16 (utilizada para as amostras de um MB Intra especial denominado Intra_16×16).
O padrão H.264/AVC utiliza palavras de 16 bits para aritmética, ao invés das palavras de 32
bits utilizadas nos padrões anteriores. Isso reduz a carga computacional das operações tanto no
codificador quanto no decodificador.
Nos padrões anteriores não era possível obter um transformada inversa exata, apenas um
limite de tolerância a erros, resultando em diferença na qualidade do vídeo decodificado entre as
várias implementações.
O padrão H.264/AVC é o primeiro padrão a obter a mesma qualidade de vídeo decodificado
entre as várias implementações de decodificadores [6], tudo graças à transformada DCT1 ser
realizada sobre números inteiros ao invés de números de ponto flutuante. Tal como é apresentado em
[33], transformação e quantização sobre números reais causam erros de precisão entre o codificador
e o decodificador, além de serem mais difíceis de implementar e custosas para processar.
Esta seção apresenta todos os métodos de codificação por entropia disponíveis no padrão
H.264/AVC, entretanto, no perfil Baseline (o qual é implementado pelo presente trabalho), os
coeficientes da transformada são codificados usando CAVLC2 e todos os outros elementos de sintaxe
são codificados usando códigos de largura fixa ou Exp-Golomb de largura variável.
1
A transformada é na verdade realizada por meio de uma aproximação ortogonal da DCT [33].
2
CABAC é utilizado no lugar de CAVLC em alguns perfis.
10
A Codificação de Largura Variável (CAVLC) mapeia uma série de símbolos de entrada para
uma série de códigos de tamanho variável. Símbolos que ocorrem com maior frequência são
mapeados para códigos com tamanho menor e símbolos menos frequentes são mapeados para
códigos de tamanho maior.
O mapeamento entre valor real e código é feita utilizando uma tabela. No H.264/AVC foi
incluída uma forma aprimorada da CAVLC, que determina qual a melhor tabela de mapeamento usar
de acordo com o contexto. Assim, essa codificação é adaptável ao contexto.
Exp-Golomb é uma codificação ideal quando valores pequenos têm uma grande freqüência.
Nela, o tamanho do código é proporcional ao valor a ser codificado e consiste de duas partes: uma
codificação unária de tamanho variável e uma codificação binária de tamanho fixo (dado pelo valor da
codificação unária).
Os itens destacados nesta seção fazem parte das melhorias para evitar perda e erros em
dados, além daquelas que possibilitam maior flexibilidade para operar em diversos ambientes de
rede. Essas melhorias não representam mudanças no codificador de vídeo propriamente dito, VCL,
mas na camada de abstração de rede, NAL.
11
2.2.5.1 Fatias SI e SP
Essa nova característica permite que cada fatia de um quadro seja decodificada
independentemente das outras fatias do mesmo quadro.
Devido ao fato de cada fatia de um quadro poder ser decodificada independentemente das
outras fatias do mesmo quadro, é possível enviar e receber as fatias de um quadro em qualquer
ordem relacionada as outras fatias do mesmo quadro. Isso possibilita uma menor espera fim-a-fim em
aplicações de tempo real distribuídas, particularmente quando usadas em redes com entrega de
pacotes sem ordenação, tal como os protocolos da Internet.
Tal como os padrões anteriores (MPEG1, MPEG2 e MPEG4 Parte 2), o padrão H.264/AVC
não define um codificador explicitamente, mas uma sintaxe de fluxo de bits de vídeo codificado
juntamente com um método para decodificar esse fluxo de bits.
Na prática, um codificador de vídeo inclui os elementos funcionais básicos mostrados na
figura 2.3; no H.264 esses elementos são um pouco diferente dos padrões anteriores, sendo que o
12
acúmulo dessas pequenas melhorias proporciona uma grande taxa de compactação por parte desse
padrão.
2.3.1 Esquema
FIGURA 2.3 – MODELO DE ENCADEAMENTO DOS PROCESSOS NO CODIFICADOR PADRÃO H.264 [4].
No modo Intra, P é formado a partir de amostras da fatia atual que foram previamente
codificadas, decodificadas e reconstruídas (sF’n na figura 2.3; perceba que são utilizadas amostras
não filtradas para formar P).
No modo Inter, P é formado a partir da predição de compensação de movimento de um ou
dois quadros selecionados do conjunto de quadros de referência das listas 0 ou 1. Na figura 2.3, o
quadro de referência é mostrado como um quadro já codificado F’n-1. Mas a predição de referência de
cada partição de macrobloco (no modo Inter) pode ser escolhida a partir de uma seleção de quadros
passados ou futuros (na ordem de apresentação) que tenham sido codificados, reconstruídos e
filtrados.
A predição P é subtraída do bloco atual para formar um bloco residual (diferença) Dn que é
transformado e quantizado para produzir X, um conjunto de coeficientes que são reordenados e
codificados por entropia. Os coeficientes codificados por entropia, juntamente as informações de
predição de modos, parâmetros de quantização, vetor de movimento, etc. formam o fluxo de bit
comprimido que é passado à Camada de Abstração de Rede (NAL) para transmissão ou
armazenamento.
No presente momento serão explicados os aspectos relevantes a cerca das tecnologias para
o qual o código de referência foi portado. Ou seja, a plataforma para a qual o codificador proposto
pelo trabalho foi desenvolvido. Não entrarei no mérito pelo qual essas tecnologias foram escolhidas,
apenas ressalto os detalhes mais importantes.
Este capítulo está subdividido em duas seções: a primeira que trata sobre a plataforma Java,
descrevendo as características mais relevantes ao desenvolvimento do trabalho; e uma segunda que
aborda a Framework para a qual o codificador foi desenvolvido.
A plataforma para o qual o código foi portado é a Java Standard Edition 1.5.0, incluindo todos
os recursos incorporados à linguagem até essa versão. É importante ressaltar que a implementação
não foi desenvolvida para ser compatível com versões anteriores, apenas posteriores, desde de que
sejam compatíveis com a dada versão na qual o trabalho foi desenvolvido.
Diferente da linguagem C, na qual o código de referência [2] foi escrito, a linguagem Java
fornece suporte de mais alto nível para a manipulação dos bytes do arquivo. Nesta seção serão
descritas quais classes da plataforma Java foram utilizadas para substituir as funções
desempenhadas por algumas estruturas implementadas no código de referência.
- OutputStream: o código de referência utiliza uma estrutura chamada Bitstream para
escrever um fluxo de bytes [2]. Todavia, a plataforma Java oferece uma classe abstrata
padrão para fluxos de dados, a OutputStream. Foi criada para este trabalho uma classe
que unia todas as funcionalidades daquela presente no código de referência com a
padronização da classe presente na plataforma Java. Essa classe foi chamada de
BitOutputStream.
15
A Java Media Framework (JMF) é uma API destinada a incorporar dados multimídia, tal como
áudio e vídeo, em aplicações Java e Applets. Ela foi especialmente desenvolvida para tirar proveito
das características da plataforma Java.
A versão utilizada neste trabalho foi a 2.1.1e, por ser a última disponível na data de início do
desenvolvimento.
3.2.1 Histórico
A JMF 1.0, conhecida como Java Media Player API, permitia aos programadores Java
desenvolverem programas para reproduzir mídias de tempo real. A JMF 2.0 estendeu a 1.0 para
fornecer suporte para captura e armazenamento de dados multimídia, controlando o tipo de
processamento que era realizado durante a reprodução, e realizando processamento personalizado
nos fluxos de dados multimídia. Além disso, a JMF 2.0 define uma API de plug-in, que permite
desenvolvedores avançados e provedores de tecnologia uma personalização mais fácil para estender
a funcionalidade da JMF [17].
3.2.2 Componentes
A figura 3.1 mostra a relação entre os principais componentes da JMF que são usados para
codificar dados multimídia. O DataSource da esquerda, e os objetos associados a ele, lidam com a
leitura da mídia de origem. Enquanto o DataSource da direita trata do esquema de gravação da
mídia processada. Os objetos dentro do quadrado tracejado realizam tarefas pré determinadas dentro
do processamento do objeto Processor.
O Demultiplexer separa a mídia em objetos trilhas (Track), cada uma destas
representando um tipo de dado multimídia (vídeo, áudio, legenda, etc.).
A codificação (ou decodificação) de cada uma dessas trilhas é feita individualmente por um
determinado Codec, esse modelo de arquitetura permite que os mesmos CODEC sejam usados por
tipos diferentes de arquivos. Por exemplo, tanto os arquivos AVI quanto MPEG podem conter áudio
em formato MP3, então, após o Demultiplexer específico de cada um desses arquivos separar a
17
trilha de áudio MP3 da trilha de vídeo, é utilizado o mesmo Codec para decodificar o MP3 de ambos
os tipos de arquivos.
Há ainda a possibilidade de aplicar efeitos especiais (como por exemplo, eco) em cada trilha
por meio de objetos Effect.
No estágio final, é possível reproduzir cada uma das trilhas por meio de um objeto
Renderer, de acordo com o tipo de mídia que elas contém, assim, reproduzindo vídeo em um
monitor e áudio em caixas de som, por exemplo. Ainda é possível mesclar as trilhas por meio de um
objeto Multiplexer e salvá-las em um novo arquivo, que pode conter as trilhas decodificadas ou
codificadas.
18
Como todo software, o codificador implementado neste trabalho possui um nicho específico
de aplicação, um tipo de dado sobre o qual trabalha. Neste capítulo são descritas as especificações
técnicas do trabalho, ou seja, os tipos de dados processados pelo codificador, tal como seus
parâmetros de funcionamento.
Parâmetro Resumo
O único formato de quadro aceito pelo codificador implementado neste trabalho é o YUV 4:2:0
Planar, onde cada quadro é composto por 3 planos, ou matrizes, tal como mostrado na figura 4.1 (a).
Sendo N a largura e M a altura em pixels do vídeo, os primeiros NxM bytes de cada quadro
representam a matriz de luma Y, os próximos (N/2)x(M/2) bytes correspondem à matriz de
crominância U e os últimos (N/2)x(M/2) bytes do quadro são a matriz de crominância V.
Por exemplo, em uma resolução de 176x144 pixels (QCIF), os primeiros 25344 bytes de cada
quadro correspondem ao componente Y, os próximos 6336 bytes representam o componente de cor
U e os últimos 6336 bytes o componente V, totalizando 12672 bytes de crominância UV em cada
quadro. Podemos considerar que são 12 bits por pixel nesse formato.
19
FIGURA 4.1 – (A) ESTRUTURA E (B) EXEMPLO DE QUADRO YUV 4:2:0 PLANAR.
Para cada quadro foi adotado o formato QCIF, que define as dimensões em 176 pixels de
largura por 144 pixels de altura, a uma taxa de 15 fps, o que gera uma taxa de aproximadamente 60
kbps.
Parâmetro Resumo
Perfis Baseline
Níveis 1 e 1.2
4.2.1 Perfis
De acordo com [26], um perfil especifica qual sintaxe de codificação (algoritmo) é usada,
enquanto um nível especifica os vários parâmetros (resolução, taxa de quadros, taxa de bit, etc.).
Os padrões de codificação de vídeo em geral possuem uma gama enorme de recursos que,
quando aplicados em conjunto, permitem obter altas taxas de compactação de vídeo. Um perfil nada
mais é que um subconjunto desses recursos, especialmente selecionados para funcionar em
determinados equipamentos e abranger um determinado público.
O perfil implementado neste trabalho é o Baseline, que é destinado aos dispositivos de baixa
capacidade de processamento e o público de aplicações via Internet, tal como vídeo conferências,
vídeo telefonia e comunicação sem fio.
4.2.1.1 Baseline
Dos cinco tipos de fatias disponíveis no padrão H.264, o perfil Baseline suporta apenas duas,
I e P. Por meio desses dois tipos de fatias são implementadas as codificações Intra e Inter.
As fatias nesse perfil também não podem ser particionadas, ou seja, toda a fatia deverá ser
enviada em uma única unidade NAL.
As codificações por entropia presentes são Exp-Golomb para parâmetros e CAVLC para
dados.
Os recursos que não são permitidos nesse perfil são: a codificação CABAC; predição com
peso; fatias SP, SI e B; particionamento de dado (fatias);
A função que testa o endian foi fixada para retornar little-endian, que é o padrão da
arquitetura Intel.
21
O processamento inicia pela leitura dos quadros descompactados no formato YCbCr, que
eqüivale ao quadro original Fn da figura 2.3.
No código de referência, há um laço de repetição que percorre todos os quadros do arquivo a
ser codificado. A cada iteração, é chamada a função encode_one_frame do arquivo image.c, que
por sua vez chama a função ReadOneFrame, presente no mesmo arquivo, para ler um quadro do
arquivo a ser codificado.
Neste trabalho, a leitura é realizada por meio da JMF, que lê cada quadro de vídeo do arquivo
de entrada e permite acesso aos bytes individuais desse quadro por meio de um objeto da classe
javax.media.Buffer.
A predição Intra é aquela que utiliza apenas as amostras contidas no mesmo quadro, sem
fazer referência nenhum outro quadro anteriormente codificado, para reduzir a redundância entre
duas amostras. A predição intra quadro visa reduzir a redundância espacial de cada quadro de um
vídeo.
23
5.2.1 I_PCM
O modo I_PCM é uma alternativa à predição intra e consiste em transmitir os valores das
amostras diretamente, sem predição, transformada ou codificação por entropia. Um macrobloco
I_PCM é gravado com os mesmos valores que possuia no arquivo descompactado.
Neste trabalho, a classe IPCMEncodingMode implementa a predição I_PCM.
O padrão H.264/AVC define quatro modos de predição Intra 16x16 Luma: Vertical, Horizontal,
DC e Plano. Cada qual sendo melhor aplicado em um determinado padrão de pixels. Por exemplo, o
modo Plano se aplica bem em regiões onde há transição de tons.
No código de referência [2], a função Intra16x16_Mode_Decision calcula os quatro
modos de predição por meio da função intrapred_16x16, logo em seguida verifica qual dos modos
24
possui a menor distorção por meio da função find_sad_16x16 e por fim aplica a transformada e
quantização usando a função dct_16x16.
O código desenvolvido neste trabalho separa cada um dos quatros modos em classes:
Intra16x16LumaDCPredictor, Intra16x16LumaHorizontalPredictor,
Intra16x16LumaVerticalPredictor e Intra16x16LumaChromaPlanePredictor. Cada uma
dessas classes sobrescreve o método doIntraPrediction, o qual é invocado pela classe abstrata
Intra16x16LumaAbstractPredictor que é a classe base dos modos. Toda funcionalidade
comum aos quatro modos foi implementada nesta classe, tal como as rotinas de transformação,
reconstrução, codificação por entropia e cálculo da distorção. Assim, cada classe é responsável por
sua codificação.
Para saber qual é o melhor modo para um determinado macrobloco, basta obter a distorção
de cada um dos modos e comparar os valores entre si.
Os modos de predição Intra 8x8 Chroma são bem parecidos com os Intra 16x16 Luma,
exceto pela ordem (DC, Horizontal, Vertical e Plano) e pelo fato do modo 0, Plano, realizar a predição
individualmente nos quatro blocos 4x4 que compõem o bloco 8x8, ao contrário do modo Intra 16x16
Luma Plano, que realiza uma única predição no bloco inteiro.
No código de referência [2], a função IntraChromaPrediction calcula os quatro modos de
predição Intra Chroma (DC, Horizontal, Vertical e Plana) para cada um dos componentes de cor (Cb e
Cr) do macrobloco sendo codificado. Nessa mesma função são computados todos os quatros modos
de predição. Em seguida é chamada na função ChromaResidualCoding, para cada um dos
componente de cor (Cb e Cr), é decidido o tipo de predição e realizada a transformada e quantização
do resíduo dos coeficientes. A função ChromaPrediction4x4 fica responsável por decidir o tipo de
predição, Intra ou Inter, e a função IntraChromaPrediction4x4 copia os valores da predição para
um buffer global e atribui os valores residuais à outro buffer. Por fim, a função dct_chroma aplica a
transformada e quantização no resíduo dos coeficientes.
O modo Intra 8x8 Chroma adota o mesmo esquema do modo Intra 16x16 Luma, separando
cada um dos quatros modos de predição em classes: Intra8x8ChromaDCPredictor,
Intra8x8ChromaHorizontalPredictor, Intra8x8ChromaVerticalPredictor e
Intra8x8ChromaPlanePredictor. Cada uma dessas classes sobrescreve o método
doIntraPrediction, invocado pela classe abstrata Intra8x8ChromaAbstractPredictor que
é a classe base dos modos e onde toda funcionalidade comum aos quatro modos de predição foi
implementada.
Esta predição visa reduzir a redundância temporal, ou seja, aquela que existe entre dois ou
mais quadros.
26
5.4.1 Transformada
A transformada é uma operação sobre matrizes que tem como objetivo homogeneizar os
valores dos coeficientes dessa matriz, possibilitando uma melhor codificação por entropia.
A transformada utilizada no H.264 é uma aproximação ortogonal da Transformada Discreta
de Cosenos, DCT. Ela não é uma implementação da fórmula da DCT, mas uma operação cujo
resultado se aproxima muito com os da DCT usando apenas números inteiros. Outro fator de grande
importância é o da transformada ser aplicada aos blocos de tamanho 4x4, permitindo grande redução
nos artefatos de blocagem característicos da codificação de vídeo.
No código do trabalho, a transformada é implementada pela classe IntegerTransform que
fornece exclusivamente serviços de transformação de matrizes.
5.4.2 Quantização
A operação de quantização tem como intuito reduzir a magnitude dos coeficientes resultantes
da transformada por meio de uma divisão, e a restituição dos valores originais por meio de uma
multiplicação. A figura 5.3 mostra graficamente o processo de quantização.
27
5.4.3 Reordenação
FIGURA 5.4 – REORDENAÇÃO ZIG-ZAG PARA BLOCOS 4X4 LUMA (MODO FRAME).
O padrão H.264 define dois tipos de codificação por entropia, CAVLC e CABAC. O método
utilizado neste trabalho é o CAVLC, por ser o tipo usado pelo perfil Baseline.
O padrão H.264 codifica todas as informações de fatias, macroblocos e blocos usando ou
CAVLC, ou CABAC, enquanto as informações acima da camada de fatia são codificadas usando
códigos binários de tamanho fixo ou variável.
A classe CAVLC, juntamente com BitOutputStream, são responsáveis por implementar as
funções para codificação por entropia CAVLC e Exp-Golomb. Abaixo está um exemplo que codifica o
parâmetro profile_idc usando a função u(v)1, onde stream é um objeto da classe CAVLC.
A classe CAVLC é responsável por construir os códigos da codificação por entropia, enquanto
a classe BitOutputStream é quem escreve os bits na stream de saída, que pode ser um arquivo ou
a rede.
1
Nomenclatura utilizada pelo documento padrão do ITU-T [3].
29
5.5.1 Exp-Golomb
5.5.1.1 Conceito
Exp-Golomb são códigos de largura variável com construções regulares [1], dispensando o
uso de tabelas ou árvores de código. Esse método é usado para codificação da maior parte das
informações do vídeo codificado pelo H.264 e é ideal para comprimir dados cujos valores pequenos
têm uma freqüência muito grande[4]. O código Exp-Golomb possui a seguinte forma:
[M zeros][1][INFO]
Onde:
M = floor( log2(code_num + 1) )
INFO = code_num + 1 – 2M
Sendo INFO um campo de M bits contendo a informação codificada, a largura do código Exp-
Golomb é (2M + 1) bits.
As funções Exp-Golomb recebem um número inteiro v como argumento e o mapeiam para
code_num. No padrão H.264 são usadas algumas formas distintas de mapeamento entre v e
code_num, dentre as quais estão presentes neste trabalho:
- ue(v) é a função que codifica inteiros sem sinal (unsigned) usando Exp-Golomb. Essa função
utiliza mapeamento direto, ou seja, code_num = v, e é representada no código pelo método
CAVLC.write_ue_v(int value);
- se(v) é uma variação da função ue(v) que codifica inteiros com sinal (signed). Representada
pelo método CAVLC.write_se_v(int value). Nela, code_num é mapeado da seguinte
forma:
code_num = 2 * |v| (v < 0)
code_num = 2 * |v| - 1 (v > 0)
As tabelas 5.1 e 5.2 ilustram os primeiros códigos Exp-Golomb para ue(v) e se(v),
respectivamente.
0 0 1
1 1 010
2 2 011
3 3 00100
4 4 00101
5 5 00110
6 6 00111
7 7 0001000
0 0 1
1 1 010
-1 2 011
2 3 00100
-2 4 00101
3 5 00110
-3 6 00111
4 7 0001000
5.5.1.2 Aplicação
Exp-Golomb é a técnica adotada para codificar a maioria dos parâmetros do vídeo H.264.
Dentre os quais se destacam aqueles apresentes na tabela 5.3.
Parâmetro Descrição
Como regra, tudo que estiver acima da camada de fatia é codificado usando Exp-Golomb ou
códigos binários. Enquanto que tudo que estiver na camada de fatia e abaixo é codificado usando
CAVLC ou CABAC, dependendo do modo de codificação por entropia.
5.5.2 CAVLC
O sinal de até três coeficientes com valor 1 é codificado, sendo usado um bit para cada sinal.
A ordem de codificação desses sinais é invertida, começando pelo último coeficiente 1 e terminando
no terceiro, se houver.
Dado o código binária contendo o sinal dos TrailingOnes e a quantidade deles, o método
writeTrailingOnesSignFlag da classe CAVLC escreve esse código no fluxo de saída.
A magnitude e o sinal dos coeficientes diferentes de zero restantes são codificados em ordem
inversa, iniciando pelo coeficiente logo após o último TrailingOnes e indo em direção ao
coeficiente DC. Note que os coeficientes com valor 1 que não fazem parte dos TrailingOnes são
codificados nesta etapa.
A partir da magnitude (nível) e do tamanho do nível anterior, o método writeLevel da
classe CAVLC codifica a magnitude de um coeficiente. Em [1] é descrito o algoritmo para codificação
do nível dos coeficientes diferentes de zero remanescentes e em [36] é ilustrado um exemplo da
aplicação desse algoritmo.
A quantidade de zeros que precedem o coeficiente de mais alta freqüência (mais distante do
DC) é codificado por meio do método writeTotalZeros da classe CAVLC. O par totalZeros e
totalCoeff define uma palavra de código em uma tabela. A maioria dos blocos contém coeficientes
diferentes de zero no começo do vetor ordenado. Dessa forma, a seqüência de zeros que aparecem
no início do vetor não precisa ser codificada, sendo indicada nesta etapa.
Esse método recebe ainda um terceiro valor, que indica a quantidade de coeficientes no
vetor, pois existem tabelas distintas para codificar vetores com 4 ou 16 coeficientes.
33
5.5.2.5 Codificação das seqüências de zeros antes de cada coeficiente diferente de zero
Dentre os mais comuns tipos de arquivos contendo vídeo H.264 estão o container MP4 e
fluxo de bits puro 264, ou raw bitstream, que não é armazenado em container.
O anexo B do documento do padrão ISO 14496-10 [3] define um formato de escrita de vídeo
H.264 chamado fluxo de bits puro.
34
O documento ISO 14496-14 especifica um container, que dentre outras coisas pode conter
vídeo H.264.
35
Capítulo 6: Conclusões
Sem dúvida a maior dificuldade foi organizar o código de referência de modo a entender o
que cada módulo fazia. O código de referência [2] é um retalho de funções, mal organizado, pouco
comentado e com diversas ambiguidades. Outro fator que também dificulta é a desconexão parcial
com o documento padrão [3], sendo exigido um estudo muito detalhado de ambos para poder
estabelecer uma ligação entre eles.
O nível de detalhes no código também foi fonte de atrasos no projeto. Foram necessários
testes constantes durante todo o desenvolvimento para assegurar que o vídeo codificado estava de
acordo com aquele gerado pelo código de referência, bastando a escrita errada de apenas um bit
para invalidar toda a codificação.
Uma das principais preocupações durante o desenvolvimento deste trabalho foi a de permitir
que ele seja continuado, por meio de uma cuidadosa modelagem orientada a objetos e
documentação. A seguir estão algumas da sugestões para extensão deste trabalho.
Estimation), DCT e Quantização, para permitir uso com dispositivos móveis, os quais possuem menos
poder de processamento.
Segundo artigo [44], a estimação de movimento consome a maior parte do tempo de
processamento de um codificador. Por exemplo, ao invés de utilizar o algoritmo Full Search para
estimação de movimento, utilizar um algoritmo de busca parcial, tal como o Predictive Algorithm (PA)
ou o Diamond Search.
Utilizar uma DCT com inteiros ao invés de pontos flutuantes, permitindo utilizar somas e
deslocamentos no lugar das multiplicações.
A gravação deste arquivo é feita de acordo com o Anexo B do documento padrão [3], que é a
forma de gravação em fluxo contínuo de bytes. Há diversas propostas para implementar uma forma
de gravação de pacotes NAL para sobre RTP, uma delas é a indicada em [43], que sugere um
formato de carga útil para pacotes RTP com H.264.
Um outra melhoria seria implementar um multiplexador MP4 (MPEG-4 Parte 14), para que a
saída do codificador fosse compatível com a maioria dos reprodutores disponíveis.
6.2.3 Perfis
Extensões futuras poderiam adicionar os outros perfis do padrão, tal como o Main, Extended,
High, etc. Com isso seria necessário implementar recursos ainda não disponíveis neste codificador,
como por exemplo, a codificação por entropia CABAC.
6.2.4 Decodificador
Um decodificador H.264 em Java seria muito útil, e sua implementação seria facilitada pelo
reuso de diversas classes já implementadas pelo codificador.
37
Apêndice A: Código-fonte
Este apêndice contém o código-fonte desenvolvido neste trabalho. Devido ao grande volume
de código desenvolvido, apenas as classes principais são exibidas. E pelo mesmo motivo, classes
semelhantes são omitidas. Por exemplo, os modos de codificação, os quais mudam pouco um do
outro.
As classes estão apresentadas em seções, de acordo com sua função dentro do codificador.
A1 Classes de suporte
package br.ufsc.inf.guiga.media;
import java.io.IOException;
import java.util.Vector;
import javax.media.Codec;
import javax.media.Demultiplexer;
import javax.media.Multiplexer;
import javax.media.PackageManager;
import javax.media.PlugInManager;
import br.ufsc.inf.guiga.media.codec.video.colorspace.JavaYUVToRGB;
import br.ufsc.inf.guiga.media.codec.video.h264.H264Encoder;
import br.ufsc.inf.guiga.media.multiplexer.video.H264Mux;
import br.ufsc.inf.guiga.media.multiplexer.video.MP4Mux;
import br.ufsc.inf.guiga.media.parser.video.YUVParser;
import com.sun.media.MimeManager;
/**
* H.264 CODEC registration routines.
*
* @author Guilherme Ferreira <guiga@inf.ufsc.br>
*/
public class Registry {
@SuppressWarnings("unchecked")
public static void registerPackage() {
String packageName = "br.ufsc.inf.guiga";
Vector<String> contentPrefixList;
Vector<String> protocolPrefixList;
protocolPrefixList = PackageManager.getProtocolPrefixList();
if (!protocolPrefixList.contains(packageName)) {
protocolPrefixList.add(0, packageName);
PackageManager.setProtocolPrefixList(protocolPrefixList);
}
PackageManager.commitContentPrefixList();
PackageManager.commitProtocolPrefixList();
// Register the YCbCr parser plugin. Allow JMF read .yuv files
Demultiplexer demuplexer = (Demultiplexer) new YUVParser();
String name = demuplexer.getClass().getName();
PlugInManager.addPlugIn(name, demuplexer.getSupportedInputContentDescriptors(),
null, PlugInManager.DEMULTIPLEXER);
try {
PlugInManager.commit();
} catch (IOException ex) {
ex.printStackTrace();
}
}
// Register the 264 raw byte stream Multiplexer plugin. Allowing JMF write .264
// files
Multiplexer mulplexer = (Multiplexer) new H264Mux();
name = mulplexer.getClass().getName();
PlugInManager.addPlugIn(name, null, mulplexer
.getSupportedOutputContentDescriptors(null), PlugInManager.MULTIPLEXER);
// Register the MP4 File Format Multiplexer plugin. Allowing JMF write .mp4
// files
mulplexer = (Multiplexer) new MP4Mux();
name = mulplexer.getClass().getName();
PlugInManager.addPlugIn(name, null, mulplexer
.getSupportedOutputContentDescriptors(null), PlugInManager.MULTIPLEXER);
try {
PlugInManager.commit();
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
package br.ufsc.inf.guiga.media.parser.video;
import java.io.IOException;
39
import javax.media.BadHeaderException;
import javax.media.Control;
import javax.media.Demultiplexer;
import javax.media.IncompatibleSourceException;
import javax.media.Time;
import javax.media.Track;
import javax.media.control.FormatControl;
import javax.media.format.YUVFormat;
import javax.media.protocol.ContentDescriptor;
import javax.media.protocol.DataSource;
import javax.media.protocol.Positionable;
import javax.media.protocol.PullSourceStream;
import br.ufsc.inf.guiga.media.Global;
import com.sun.media.parser.BasicPullParser;
/**
* YCbCr raw video file parser. Extract video track from a raw YUV stream.
*
* @author Guilherme Ferreira <guiga@inf.ufsc.br>
*/
public class YUVParser extends BasicPullParser implements Positionable {
/**
* Default constructor.
*/
public YUVParser() {
super();
tracks = new Track[1];
supportedFormat = new ContentDescriptor[] { new ContentDescriptor("video.yuv") };
stream = null;
controls = new Control[2];
}
/**
* Gets the duration of this media stream when played at the default rate.
* <p>
* Note that each track can have a different duration and a different start time. This
* method returns the total duration from when the first track starts and the last
* track ends.
*
* @return A Time object that represents the duration or <code>DURATION_UNKNOWN</code>
* if the duration cannot be determined.
*/
public Time getDuration() {
return tracks[VIDEO_TRACK].getDuration();
}
/**
* Gets the current media time. This is the stream position that the next readFrame
* will read.
*
* @return The current position in the media stream as a Time object.
*/
public Time getMediaTime() {
YUVVideoTrack videoTrack = (YUVVideoTrack) tracks[VIDEO_TRACK];
Time t = videoTrack.mapFrameToTime(videoTrack.getCurrentFrame());
return t;
}
/**
* Sets the stream position (media time) to the specified Time. Returns the rounded
* position that was actually set. Implementations should set the position to a key
* frame, if possible.
*
* @param where The new stream position, specified as a Time.
* @param rounding The rounding technique to be used: RoundUp, RoundDown, or
* RoundNearest.
40
* @return The actual stream position that was set as a Time object.
*/
public Time setPosition(Time where, int rounding) {
YUVVideoTrack videoTrack = (YUVVideoTrack) tracks[VIDEO_TRACK];
Time oldTime = videoTrack.mapFrameToTime(videoTrack.getCurrentFrame());
videoTrack.setCurrentFrame(videoTrack.mapTimeToFrame(where));
return oldTime;
}
/**
* Sets the media source this MediaHandler should use to obtain content.
*
* @param source The DataSource used by this MediaHandler.
* @throws java.io.IOException Thrown if there is an error using the DataSource
* @throws javax.media.IncompatibleSourceException Thrown if this MediaHandler cannot
* make use of the DataSource (cannot handle the media).
*/
public void setSource(DataSource source)
throws IOException, IncompatibleSourceException
{
super.setSource(source);
/**
* Gets the name of this plug-in as a human-readable string.
*
* @return A String that contains the descriptive name of the plug-in.
*/
public String getName() {
return PLUGIN_NAME;
}
/**
* @return a lists of all of the input content descriptors that this
* {@link Demultiplexer} supports.
*/
public ContentDescriptor[] getSupportedInputContentDescriptors() {
return supportedFormat;
}
/**
* Retrieves the individual tracks that the media stream contains. A stream can
* contain multiple media tracks, such as separate tracks for audio, video, and midi
* data. The information specific to a track is abstracted by an instance of a class
* that implements the {@link Track} interface. The {@link Track} interface also
* provides methods for enabling or disabling a track.
* <p>
* When getTracks is called, the stream header is read and parsed (if there is one),
* the track information is retrieved, the maximum frame size for each track is
* computed, and the play list is built (if applicable).
*
* @return An array of Track objects. The length of the array is equal to the number
41
return tracks;
}
package br.ufsc.inf.guiga.media.parser.video;
import javax.media.Buffer;
import javax.media.Time;
import javax.media.Track;
import javax.media.format.VideoFormat;
import javax.media.format.YUVFormat;
import javax.media.protocol.PullSourceStream;
import com.sun.media.parser.BasicTrack;
/**
* YUV Video Track.
* <p>
* Once a YUV raw video is composed only by video, this is the only track available.
*
* @author Guilherme Ferreira <guiga@inf.ufsc.br>
*/
public class YUVVideoTrack extends BasicTrack {
/**
* Create a track to handle YUV video.
*
* @param parser the parser owning this video track
* @param format the YUV Video format
* @param enabled
* @param duration total video duration
* @param startTime
* @param numBuffers
* @param dataSize frame size in bytes. Each time <code>readFrame</code> is called,
* this amount of bytes is read from input stream.
* @param stream
*/
public YUVVideoTrack(
YUVParser parser,
YUVFormat format,
boolean enabled,
Time duration,
Time startTime,
int numBuffers,
int dataSize,
PullSourceStream stream)
{
super(parser, format, enabled, duration, startTime, numBuffers, dataSize, stream);
/**
42
return t;
}
/**
* Converts the given media time to the corresponding frame number.
* <p>
* The frame returned is the nearest frame that has a media time less than or equal to
* the given media time.
*
* @param mediaTime the input media time for the conversion.
* @return the converted frame number the given media time. If the conversion fails,
* <code>FRAME_UNKNOWN</code> is returned.
*/
public int mapTimeToFrame(Time mediaTime) {
double time = 0d;
int frameNumber = 0;
time = mediaTime.getSeconds();
if (time < 0.0) {
return Integer.MAX_VALUE;
}
frameNumber = (int) Math.round(time * ((VideoFormat) getFormat()).getFrameRate());
if ((frameNumber < 0) || (frameNumber >= numberOfFrames)) {
return (numberOfFrames - 1);
}
return frameNumber;
}
/**
* Reads the next frame for this Track.
*
* @param buffer The {@link Buffer} into which the data is to be read. If readFrame is
* successful, buffer.getLength returns the length of the data that was
* read.
*/
public void readFrame(Buffer buffer) {
// positioning the stream to read the current frame
setSeekLocation(currentFrame * dataSize);
currentFrame++;
// read frame data into buffer
super.readFrame(buffer);
}
/**
* @return the total number of frames this video contains. This total number is
* calculated through total content size by the frame size.
*/
public int getNumberOfFrames() {
return numberOfFrames;
}
/**
* @return the next frame to be read by this track. Starting from 0 to total number of
* frames.
*/
public int getCurrentFrame() {
return currentFrame;
}
/**
* Set the next frame to be read by this Track.
*
* @param currentFrame the next frame to be read by <code>readFrame</code>.
43
*/
public void setCurrentFrame(int currentFrame) {
this.currentFrame = currentFrame;
}
package br.ufsc.inf.guiga.media.parser.video;
import java.awt.Dimension;
import javax.media.format.YUVFormat;
/**
* Contains standard YUV formats and utilities.
*
* @author Guilherme
*/
public class YUVFormatHandler {
/**
* Compute the amount of bytes required to store a frame in YCbCr with a given format
* (e.g. 4:2:2, 4:2:0, 4:1:1).
*
* @param videoFormat the {@link YUVFormat} of this frame.
* @return the amount of bytes required to store a YUV frame with such format.
*/
public static int getFrameSize(YUVFormat videoFormat) {
int symbolSizeInBytes = 1; // 8-bit for each Y, Cb and Cr pixel
return frameSizeInBytes;
}
package br.ufsc.inf.guiga.media.parser.video;
import javax.media.format.YUVFormat;
import br.ufsc.inf.guiga.media.codec.video.h264.vcl.FrameBuffer;
/**
* Buffer for YUV color space video frame.
* <p>
* This class holds separate arrays for each YCbCr component, allowing to access individual
* components for each pixel.
*
* @author Guilherme Ferreira <guiga@inf.ufsc.br>
*/
public class YUVFrameBuffer extends FrameBuffer {
private int[][] Y;
private int[][] Cr;
private int[][] Cb;
protected int uvWidth;
protected int uvHeight;
/**
* Creates a YUV frame buffer from a {@link YUVFormat}, specifying width and height for
* luma and chroma.
*
* @param format the {@link YUVFormat} of this frame.
*/
public YUVFrameBuffer(YUVFormat format) {
super(format);
int uvWidth = 0;
int uvHeight = 0;
switch (format.getYuvType()) {
case YUVFormat.YUV_420:
uvWidth = format.getSize().width / 2;
uvHeight = format.getSize().height / 2;
break;
}
protected void init(int width, int height, int uvWidth, int uvHeight) {
this.uvWidth = uvWidth;
this.uvHeight = uvHeight;
Y = new int[height][width];
Cr = new int[uvHeight][uvWidth];
Cb = new int[uvHeight][uvWidth];
}
/**
* @return the chroma frame width.
*/
public int getUVWidth() {
return uvWidth;
}
/**
* @return the chroma frame height.
*/
public int getUVHeight() {
return uvHeight;
}
45
/**
* Initializes the Y, Cb and Cr arrays, coping the parameter to the internal buffer.
*
* @param data the YCbCr frame. The first part of data is Y array, the second part of
data
* is the Cb and then, the next is Cr. Each part size depends on YUV format
* selected. For example, in 4:2:0 format, Y has width * height size, and Cb
and
* Cr has one fourth of Y area each.
*/
public void setData(byte[] data) {
int indexY, indexUV; // YUV indexes
int cbOffset = getWidth() * getHeight();
int crOffset = cbOffset + (uvWidth * uvHeight);
/**
* Copy the internal Y, Cb and Cr arrays to the parameter.
*
* @param data The first part of data is Y array, the second part of data is the Cb and
* then, the next is Cr. Each part size depends on YUV format selected.
*/
public byte[] getData() {
return null;
}
/**
* Copy the parameter Y, Cb and Cr arrays to this object Y, Cb and Cr internal arrays.
*
* @param other another {@link YUVFrameBuffer}.
*/
public void copyData(FrameBuffer other) {
YUVFrameBuffer otherYUV = (YUVFrameBuffer) other;
System.arraycopy(otherYUV.Y, 0, Y, 0, Y.length);
System.arraycopy(otherYUV.Cb, 0, Cb, 0, Cb.length);
System.arraycopy(otherYUV.Cr, 0, Cr, 0, Cr.length);
}
/**
* Gets an 8-bit luma pixel value at a given position.
*
* @param x horizontal pixel position.
46
/**
* Sets an 8-bit luma pixel value at a given position.
*
* @param x horizontal pixel position.
* @param y vertical pixel position.
* @param value an eight bit length value of Y component at specific (x,y) position in
the
* frame.
*/
public void setY8bit(int x, int y, int value) {
Y[y][x] = value;
}
/**
* Gets an 8-bit chroma pixel value at a given position.
*
* @param x horizontal pixel position.
* @param y vertical pixel position.
* @return an eight bit length value of Cb component at specific (x,y) position in the
* frame.
*/
public int getCb8bit(int x, int y) {
return Cb[y][x];
}
/**
* Sets an 8-bit chroma pixel value at a given position.
*
* @param x horizontal pixel position.
* @param y vertical pixel position.
* @param value an eight bit length value of Cb component at specific (x,y) position in
the
* frame.
*/
public void setCb8bit(int x, int y, int value) {
Cb[y][x] = value;
}
/**
* Gets an 8-bit chroma pixel value at a given position.
*
* @param x horizontal pixel position.
* @param y vertical pixel position.
* @return an eight bit length value of Cr component at specific (x,y) position in the
* frame.
*/
public int getCr8bit(int x, int y) {
return Cr[y][x];
}
/**
* Sets an 8-bit chroma pixel value at a given position.
*
* @param x horizontal pixel position.
* @param y vertical pixel position.
* @param value an eight bit length value of Cr component at specific (x,y) position in
* the frame.
*/
public void setCr8bit(int x, int y, int value) {
Cr[y][x] = value;
}
}
47
package br.ufsc.inf.guiga.media.multiplexer.video;
import java.io.IOException;
import java.util.List;
import javax.media.Buffer;
import javax.media.Format;
import javax.media.Multiplexer;
import javax.media.PlugIn;
import javax.media.protocol.ContentDescriptor;
import br.ufsc.inf.guiga.media.Global;
import br.ufsc.inf.guiga.media.codec.video.h264.nal.NALU;
import br.ufsc.inf.guiga.media.codec.video.h264.nal.NALUByteStream;
import br.ufsc.inf.guiga.media.codec.video.h264.nal.datavalue.NalRefIdc;
import br.ufsc.inf.guiga.media.codec.video.h264.nal.datavalue.NaluType;
import br.ufsc.inf.guiga.media.codec.video.h264.parset.PictureParameterSet;
import br.ufsc.inf.guiga.media.codec.video.h264.parset.SequenceParameterSet;
import br.ufsc.inf.guiga.media.codec.video.h264.vcl.Picture;
import br.ufsc.inf.guiga.media.codec.video.h264.vcl.Slice;
import br.ufsc.inf.guiga.media.control.H264Control;
import br.ufsc.inf.guiga.media.format.H264Format;
import br.ufsc.inf.guiga.media.parser.video.YUVFormatHandler;
import br.ufsc.inf.guiga.media.util.io.BitOutputStream;
import br.ufsc.inf.guiga.media.util.io.BitOutputStreamImpl;
import br.ufsc.inf.guiga.media.util.io.H264EntropyOutputStream;
import br.ufsc.inf.guiga.media.util.io.H264EntropyOutputStreamImpl;
import com.sun.media.multiplexer.BasicMux;
/**
* Multiplexes the H.264 Video Track into a Raw Bit Stream as specified by Annex B from
* H.264 International Standard.
*
* @author Guilherme Ferreira <guiga@inf.ufsc.br>
*/
public class H264Mux extends BasicMux {
private static final String PLUGIN_NAME = "H.264/AVC Annex B Byte Stream Multiplexer";
private static final String MIME_TYPE = "video.264";
/**
* Creates a H.264 Byte Stream Multiplexer.
*/
public H264Mux() {
super.supportedOutputs = new ContentDescriptor[] { new ContentDescriptor(
MIME_TYPE) };
super.supportedInputs = new Format[] {
new H264Format(YUVFormatHandler.SQCIF, 18432, 15),
new H264Format(YUVFormatHandler.QCIF, 38016, 15),
new H264Format(YUVFormatHandler.CIF, 152064, 15),
new H264Format(YUVFormatHandler._4CIF, 608256, 15) };
}
/**
* Gets the name of this plug-in as a human-readable string.
*
* @return A String that contains the descriptive name of the plug-in.
*/
public String getName() {
return PLUGIN_NAME;
}
/**
* Sets the input {@link Format} for the specified track.
*
48
/**
* Processes the input {@link Buffer} and multiplexes it with data from other tracks.
* The multiplexed output is sent to an output DataSource.
* <p>
* This multiplexer simply write the raw H.264 video stream into the file.
*
* @param buffer The Buffer of data to process.
* @param trackID The index number of the track to which the input Buffer belongs.
* @return <code>BUFFER_PROCESSED_OK</code> If the processing is successful. Other
* possible return codes are defined in {@link PlugIn}.
*/
protected int doProcess(Buffer buffer, int trackID) {
try {
BitOutputStream bitstream = new BitOutputStreamImpl();
H264EntropyOutputStream stream = new H264EntropyOutputStreamImpl(bitstream,
sps, pps);
} catch (IOException e) {
e.printStackTrace();
}
return BUFFER_PROCESSED_OK;
}
/**
* This method generates the appropriate sequence header.
*/
protected void writeHeader() {
try {
BitOutputStream bitstream = new BitOutputStreamImpl();
H264EntropyOutputStream stream = new H264EntropyOutputStreamImpl(bitstream,
sps, pps);
49
// write now the Picture Parameter Set (only one for now).
nalu = new NALUByteStream(pps, NaluType.NALU_TYPE_PPS,
NalRefIdc.NALU_PRIORITY_HIGHEST);
nalu.write(stream);
} catch (IOException e) {
e.printStackTrace();
}
}
package br.ufsc.inf.guiga.media.codec.video.h264.nal;
import java.io.IOException;
import br.ufsc.inf.guiga.media.codec.video.h264.nal.datavalue.NalRefIdc;
import br.ufsc.inf.guiga.media.codec.video.h264.nal.datavalue.NaluType;
import br.ufsc.inf.guiga.media.util.io.H264EntropyOutputStream;
/**
* This class implements the NAL Unit from Annex B of ITU-T Recommendation
* H.264.
*
* @author Guilherme Ferreira <guiga@inf.ufsc.br>
*/
public class NALUByteStream extends NALU {
/**
* @see br.ufsc.inf.guiga.media.codec.video.h264.nal.NALU#NALU(RBSP,
* NaluType, NalRefIdc)
*/
public NALUByteStream(
RBSP rbsp,
NaluType nalUnitType,
NalRefIdc nalReferenceIdc)
{
super(rbsp, nalUnitType, nalReferenceIdc);
}
/**
* Writes an Annex B Byte Stream NAL Unit.
*
* @param output
* the output video stream, where this NAL Unit will be write.
* @return number of bits written
*/
protected int doWrite(H264EntropyOutputStream output) throws IOException {
int bitsWritten = 0;
output.write_u_v(8, 0);
output.write_u_v(8, 1);
bitsWritten = output.size();
return bitsWritten;
}
package br.ufsc.inf.guiga.media.codec.video.h264.nal;
import java.io.IOException;
import java.io.OutputStream;
import br.ufsc.inf.guiga.media.codec.video.h264.nal.datavalue.NalRefIdc;
import br.ufsc.inf.guiga.media.codec.video.h264.nal.datavalue.NaluType;
import br.ufsc.inf.guiga.media.util.io.H264EntropyOutputStream;
/**
* NAL Unit.
*
* @author Guilherme Ferreira <guiga@inf.ufsc.br>
*/
public abstract class NALU {
/**
* Creates a NAL Unit containing the given {@link RBSP}.
*
* @param rbsp An object that implements the {@link RBSP} and whom the data will be
* wrap by this NAL Unit.
* @param nalUnitType The {@link NaluType} value that specify the type of payload
* within this NAL unit.
* @param nalReferenceIdc One of {@link NalRefIdc} values to specify the picture
* priority.
*/
public NALU(RBSP rbsp, NaluType nalUnitType, NalRefIdc nalReferenceIdc) {
this.rbsp = rbsp;
this.forbidden_zero_bit = 0;
this.nal_unit_type = nalUnitType;
this.nal_ref_idc = nalReferenceIdc;
this.useAnnexbLongStartcode = true;
this.startcodeprefix_len = useAnnexbLongStartcode ? 4 : 3;
/**
* Writes a NALU to an {@link OutputStream}.
*
* @param output the output stream where this NAL Unit data will be write.
51
return doWrite(output);
}
/**
* This method must be overwrite in subclasses in order to write the RBSP
*
* @param output the output stream where the NALU subclass data will be write.
* @return number of bits written.
* @throws IOException
*/
protected abstract int doWrite(H264EntropyOutputStream output) throws IOException;
/**
* Converts an RBSP to a NALU.
*
* @param rbsp byte buffer with the Raw Byte Sequence Payload (RBSP)
* @param output bit stream where the RBSP will be write.
* @return length of the NALU in bytes.
*/
protected int writeRBSP(RBSP rbsp, H264EntropyOutputStream output) {
// delegate the payload write to the RBSP object
int rbsp_size = rbsp.write(output);
// check if there isn't any start code prefix within the RBSP
int length = 1 + preventEmulationOfStartCode(output, 0);
return length;
}
/**
* This function converts a RBSP payload to an EBSP (Encapsulated Byte Stream Payload)
* payload, preventing that no sequence of consecutive byte-aligned bytes in the NAL
* unit contains a start code prefix.
* <p>
* This is done by placing an emulation prevention byte, a byte equal to 0x03 that may
* be present within a NAL unit.
*
* @param output data bits
* @param beginBytePos The byte position after start-code, after which stuffing to
* prevent start-code emulation begins.
* @return size of <code>output</code> after stuffing.
*/
protected int preventEmulationOfStartCode(
H264EntropyOutputStream output, int beginBytePos)
{
int endBytePos = output.size() / 8;
int i, j;
// System.arraycopy(
// output.toByteArray(), beginBytePos, payloadBuffer, beginBytePos,
// (endBytePos - beginBytePos));
j = beginBytePos;
for (i = beginBytePos; i < endBytePos; i++) {
// TODO must implement, but I didn't find any file that need this
// emulation byte
j++;
}
return j;
}
/**
* @return the {@link NaluType} of this NAL Unit.
52
*/
public NaluType getNalUnitType() {
return nal_unit_type;
}
/**
* @return the {@link NalRefIdc} of this NAL Unit.
*/
public NalRefIdc getNalRefIdc() {
return nal_ref_idc;
}
package br.ufsc.inf.guiga.media.codec.video.h264;
import java.util.logging.Handler;
import java.util.logging.Logger;
import javax.media.Buffer;
import javax.media.Control;
import javax.media.Format;
import javax.media.format.YUVFormat;
import br.ufsc.inf.guiga.media.Global;
import br.ufsc.inf.guiga.media.Registry;
import br.ufsc.inf.guiga.media.codec.video.h264.vcl.Picture;
import br.ufsc.inf.guiga.media.codec.video.h264.vcl.VideoSequence;
import br.ufsc.inf.guiga.media.control.H264Adapter;
import br.ufsc.inf.guiga.media.control.H264Control;
import br.ufsc.inf.guiga.media.format.H264Format;
import br.ufsc.inf.guiga.media.parser.video.YUVFormatHandler;
import com.sun.media.BasicCodec;
/**
* H.264/AVC Encoder.
* <p>
* Process YUV video data, encoding into H.264/AVC coded video data.
*
* @author Guilherme Ferreira <guiga@inf.ufsc.br>
*/
public class H264Encoder extends BasicCodec {
// -------------------------------------------------------------------------
// DATA INITIALIZATION
// -------------------------------------------------------------------------
/**
* Register this encoder within JMF
*/
public static void register() {
Registry.registerPackage();
Registry.registerYUVHandler();
Registry.registerH264Encoder();
}
/**
* Create a H.264 Encoder with default configuration.
*/
public H264Encoder() {
init();
53
/**
* Create a H.264 Encoder with default configuration and a given {@link Handler} to
* log the messages from this encoder.
*
* @param handler a {@link Handler} to print log messages.
*/
public H264Encoder(Handler handler) {
init();
logger.addHandler(handler);
}
Global.getInstance().setH264Control(new H264Adapter());
logger = Logger.getLogger(getClass().getSimpleName());
}
// -------------------------------------------------------------------------
// DATA FORMAT CONFIGURATION
// -------------------------------------------------------------------------
/**
* Lists the output formats that this CODEC can generate. If input is non-null, this
* method lists the possible output formats that can be generated from input data of
* the specified {@link Format}. If input is <code>null</code>, this method lists
* all of the output formats supported by this plug-in.
*
* @param input The {@link Format} of the data to be used as input to the plug-in.
* @return An array that contains the supported output Formats.
*/
public Format[] getSupportedOutputFormats(Format input) {
Format[] supportedFormats = {};
if (input == null) {
supportedFormats = outputFormats;
} else {
if (input instanceof YUVFormat) {
supportedFormats = new Format[1];
supportedFormats[0] = H264Format.getMatchFormat((YUVFormat) input);
}
}
return supportedFormats;
}
/**
* Sets the format of the data to be input to this CODEC.
*
* @param format The {@link Format} to be set.
* @return The {@link Format} that was set, which might be the supported
* {@link Format} that most closely matches the one specified. Returns null if
* the specified {@link Format} is not supported and no reasonable match could
* be found.
*/
public Format setInputFormat(Format input) {
if (input instanceof YUVFormat) {
videoSequence = new VideoSequence((YUVFormat) input);
return super.setInputFormat(input);
} else {
return null;
}
54
/**
* Gets the name of this plug-in as a human-readable string.
*
* @return A String that contains the descriptive name of the plug-in.
*/
public String getName() {
return PLUGIN_NAME;
}
/**
* Obtain the collection of objects that control the object that implements this
* interface. If no controls are supported, a zero length array is returned.
*
* @return the collection of object controls
*/
public Object[] getControls() {
Control[] controls = { Global.getInstance().getH264Control() };
return controls;
}
/**
* Obtain the object that implements the specified <code>Class</code> or
* <code>Interface</code> The full class or interface name must be used. If the
* control is not supported then <code>null</code> is returned.
*
* @return the object that implements the control, or <code>null</code>.
*/
public Object getControl(String controlType) {
Object cls = null;
try {
cls = Class.forName(controlType);
} catch (ClassNotFoundException e) {
return null;
}
return null;
}
// -------------------------------------------------------------------------
// DATA PROCESSING
// -------------------------------------------------------------------------
/**
* Converts a byte array Buffer of raw YUV video data into an byte array Buffer of
* H.264/AVC (MPEG-4 Part 10) video data. Always inputs one raw video frame and
* outputs one coded frame.
*
* @return <code>BUFFER_PROCESSED_OK</code> The output buffer contains a valid coded
* frame <br>
* <code>BUFFER_PROCESSED_FAILED</code> A encoding problem was encountered
*/
public int process(Buffer input, Buffer output) {
// look for a valid input buffer
if (!isInputSupported(input.getFormat())) {
return BUFFER_PROCESSED_FAILED;
}
// code one frame, this method is called for each input frame read
encodeData(input, output);
55
return BUFFER_PROCESSED_OK;
}
/**
* @param inputBuffer
* @param outputBuffer
*/
private void encodeData(Buffer inputBuffer, Buffer outputBuffer) {
byte[] inputData = (byte[]) inputBuffer.getData();
Picture outputData = videoSequence.encode(inputData);
outputBuffer.setData(outputData);
}
// -------------------------------------------------------------------------
// DATA VERIFICATION AND VALIDATION
// -------------------------------------------------------------------------
/**
* Check if the input format is supported by this CODEC.
*/
private boolean isInputSupported(Format format) {
boolean supported = false;
for (int i = 0; i < inputFormats.length; i++) {
if (inputFormats[i].matches(format)) {
supported = true;
}
}
return supported;
}
package br.ufsc.inf.guiga.media.codec.video.h264;
import br.ufsc.inf.guiga.media.codec.video.h264.vcl.algorithm.AlgorithmFactory;
import br.ufsc.inf.guiga.media.codec.video.h264.vcl.algorithm.Quantizer;
import br.ufsc.inf.guiga.media.codec.video.h264.vcl.algorithm.Scanner;
import br.ufsc.inf.guiga.media.codec.video.h264.vcl.algorithm.Transform;
import br.ufsc.inf.guiga.media.codec.video.h264.vcl.algorithm.impl.IntegerRoundingQuantizer;
import br.ufsc.inf.guiga.media.codec.video.h264.vcl.algorithm.impl.IntegerTransform;
import br.ufsc.inf.guiga.media.codec.video.h264.vcl.algorithm.impl.ZigZagFrameScanner;
import br.ufsc.inf.guiga.media.codec.video.h264.vcl.mode.decision.DistortionMetric;
import br.ufsc.inf.guiga.media.codec.video.h264.vcl.mode.decision.SATD;
/**
* @param qp the Quantization Parameter used by the macroblock.
*/
public BaselineProfileFactory(int qp) {
transform = new IntegerTransform();
scanner = new ZigZagFrameScanner();
quantizer = new IntegerRoundingQuantizer(qp, scanner);
distortionMetric = new SATD(transform);
}
return transform;
}
package br.ufsc.inf.guiga.media.codec.video.h264.vcl.algorithm.impl;
import br.ufsc.inf.guiga.media.codec.video.h264.vcl.algorithm.Transform;
import br.ufsc.inf.guiga.media.codec.video.h264.vcl.block.Block;
/**
* This class implements the transform operations using only integer arithmetic's. The
* transforms are orthogonal approximations of DCT.
*
* @author Guilherme Ferreira <guiga@inf.ufsc.br>
*/
public class IntegerTransform extends Transform {
public IntegerTransform() {
tmp = new int[64];
}
@Override
public void forward4x4(int[][] src, int[][] dst, int pos_y, int pos_x) {
int ii;
int p0, p1, p2, p3;
int t0, t1, t2, t3;
int tmpIdx = 0;
// Horizontal
for (int i = pos_y; i < pos_y + Block.BLOCK_4x4_SIZE; i++) {
p0 = src[i][pos_x + 0];
p1 = src[i][pos_x + 1];
p2 = src[i][pos_x + 2];
p3 = src[i][pos_x + 3];
t0 = p0 + p3;
t1 = p1 + p2;
t2 = p1 - p2;
t3 = p0 - p3;
tmp[tmpIdx++] = t0 + t1;
tmp[tmpIdx++] = (t3 << 1) + t2;
tmp[tmpIdx++] = t0 - t1;
tmp[tmpIdx++] = t3 - (t2 << 1);
}
// Vertical
for (int i = 0; i < Block.BLOCK_4x4_SIZE; i++) {
tmpIdx = i;
p0 = tmp[tmpIdx];
p1 = tmp[tmpIdx += Block.BLOCK_4x4_SIZE];
p2 = tmp[tmpIdx += Block.BLOCK_4x4_SIZE];
p3 = tmp[tmpIdx += Block.BLOCK_4x4_SIZE];
t0 = p0 + p3;
t1 = p1 + p2;
t2 = p1 - p2;
t3 = p0 - p3;
57
ii = pos_x + i;
dst[pos_y + 0][ii] = t0 + t1;
dst[pos_y + 1][ii] = t2 + (t3 << 1);
dst[pos_y + 2][ii] = t0 - t1;
dst[pos_y + 3][ii] = t3 - (t2 << 1);
}
}
@Override
public void inverse4x4(int[][] src, int[][] dst, int pos_y, int pos_x) {
int ii;
int p0, p1, p2, p3;
int t0, t1, t2, t3;
int tmpIdx = 0;
// Horizontal
for (int i = pos_y; i < pos_y + Block.BLOCK_4x4_SIZE; i++) {
t0 = src[i][pos_x + 0];
t1 = src[i][pos_x + 1];
t2 = src[i][pos_x + 2];
t3 = src[i][pos_x + 3];
p0 = t0 + t2;
p1 = t0 - t2;
p2 = (t1 >> 1) - t3;
p3 = t1 + (t3 >> 1);
tmp[tmpIdx++] = p0 + p3;
tmp[tmpIdx++] = p1 + p2;
tmp[tmpIdx++] = p1 - p2;
tmp[tmpIdx++] = p0 - p3;
}
// Vertical
for (int i = 0; i < Block.BLOCK_4x4_SIZE; i++) {
tmpIdx = i;
t0 = tmp[tmpIdx];
t1 = tmp[tmpIdx += Block.BLOCK_4x4_SIZE];
t2 = tmp[tmpIdx += Block.BLOCK_4x4_SIZE];
t3 = tmp[tmpIdx += Block.BLOCK_4x4_SIZE];
p0 = t0 + t2;
p1 = t0 - t2;
p2 = (t1 >> 1) - t3;
p3 = t1 + (t3 >> 1);
ii = pos_x + i;
dst[pos_y + 0][ii] = p0 + p3;
dst[pos_y + 1][ii] = p1 + p2;
dst[pos_y + 2][ii] = p1 - p2;
dst[pos_y + 3][ii] = p0 - p3;
}
}
@Override
public void hadamard4x4(int[][] src, int[][] dst) {
int p0, p1, p2, p3;
int t0, t1, t2, t3;
int tmpIdx = 0;
// Horizontal
for (int i = 0; i < Block.BLOCK_4x4_SIZE; i++) {
p0 = src[i][0];
p1 = src[i][1];
p2 = src[i][2];
p3 = src[i][3];
t0 = p0 + p3;
t1 = p1 + p2;
t2 = p1 - p2;
t3 = p0 - p3;
tmp[tmpIdx++] = t0 + t1;
tmp[tmpIdx++] = t3 + t2;
tmp[tmpIdx++] = t0 - t1;
tmp[tmpIdx++] = t3 - t2;
58
// Vertical
for (int i = 0; i < Block.BLOCK_4x4_SIZE; i++) {
tmpIdx = i;
p0 = tmp[tmpIdx];
p1 = tmp[tmpIdx += Block.BLOCK_4x4_SIZE];
p2 = tmp[tmpIdx += Block.BLOCK_4x4_SIZE];
p3 = tmp[tmpIdx += Block.BLOCK_4x4_SIZE];
t0 = p0 + p3;
t1 = p1 + p2;
t2 = p1 - p2;
t3 = p0 - p3;
@Override
public void ihadamard4x4(int[][] src, int[][] dst) {
int p0, p1, p2, p3;
int t0, t1, t2, t3;
int tmpIdx = 0;
// Horizontal
for (int i = 0; i < Block.BLOCK_4x4_SIZE; i++) {
t0 = src[i][0];
t1 = src[i][1];
t2 = src[i][2];
t3 = src[i][3];
p0 = t0 + t2;
p1 = t0 - t2;
p2 = t1 - t3;
p3 = t1 + t3;
tmp[tmpIdx++] = p0 + p3;
tmp[tmpIdx++] = p1 + p2;
tmp[tmpIdx++] = p1 - p2;
tmp[tmpIdx++] = p0 - p3;
}
// Vertical
for (int i = 0; i < Block.BLOCK_4x4_SIZE; i++) {
tmpIdx = i;
t0 = tmp[tmpIdx];
t1 = tmp[tmpIdx += Block.BLOCK_4x4_SIZE];
t2 = tmp[tmpIdx += Block.BLOCK_4x4_SIZE];
t3 = tmp[tmpIdx += Block.BLOCK_4x4_SIZE];
p0 = t0 + t2;
p1 = t0 - t2;
p2 = t1 - t3;
p3 = t1 + t3;
dst[0][i] = p0 + p3;
dst[1][i] = p1 + p2;
dst[2][i] = p1 - p2;
dst[3][i] = p0 - p3;
}
}
@Override
public void hadamard2x2(int[][] src, int[][] dst) {
int p0, p1, p2, p3;
p0 = src[0][0] + src[0][1];
p1 = src[0][0] - src[0][1];
p2 = src[1][0] + src[1][1];
p3 = src[1][0] - src[1][1];
59
@Override
public void ihadamard2x2(int[][] src, int[][] dst) {
int t0, t1, t2, t3;
t0 = src[0][0] + src[0][1];
t1 = src[0][0] - src[0][1];
t2 = src[1][0] + src[1][1];
t3 = src[1][0] - src[1][1];
package br.ufsc.inf.guiga.media.codec.video.h264.vcl.algorithm.impl;
import br.ufsc.inf.guiga.media.codec.video.h264.vcl.algorithm.Scanner;
run++;
level = srcCoeff[j][i];
if (level != 0) {
dstLevel[scanPos] = level;
dstRun[scanPos] = run;
scanPos++;
run = -1;
}
}
run++;
level = srcCoeff[j][i];
if (level != 0) {
dstLevel[scanPos] = level;
dstRun[scanPos] = run;
scanPos++;
run = -1;
}
}
}
package br.ufsc.inf.guiga.media.codec.video.h264.vcl.algorithm.impl;
import br.ufsc.inf.guiga.media.codec.video.h264.vcl.algorithm.Quantizer;
import br.ufsc.inf.guiga.media.codec.video.h264.vcl.algorithm.Scanner;
import br.ufsc.inf.guiga.media.codec.video.h264.vcl.block.Block;
import br.ufsc.inf.guiga.media.codec.video.h264.vcl.entropy.CAVLC;
import br.ufsc.inf.guiga.media.util.SupportMath;
// -------------------------------------------------------------------------
// Definitions
// -------------------------------------------------------------------------
// -------------------------------------------------------------------------
// Attributes
// -------------------------------------------------------------------------
// -------------------------------------------------------------------------
// Methods
// -------------------------------------------------------------------------
this.scanner = scanner;
initQMatrix();
calculateQuantParam();
calculateOffsetParam();
}
int k = qp_per_matrix[qp];
int qp_per = Q_BITS + k - OffsetBits;
int nonZeroCoeff = 0;
int coeffCost = 0;
int qp_per = qp_per_matrix[qp];
int qp_rem = qp_rem_matrix[qp];
int qbits = Q_BITS + qp_per; // qbits = 15 + floor(QP/6)
int[][] mf = quantCoeff[qp_rem]; // MF = (2^qbits * PF) / Qstep
int[][] f = levelOffset4x4Intra[qp]; // f = 2^qbits/3 for Intra
if (level != 0) {
nonZeroCoeff++;
// sign(Z(i,j)) = sign(Y(i,j))
dst[j][i] = level * (int) (Math.signum(src[j][i]));
run = 0;
} else {
dst[j][i] = 0;
run++;
}
}
}
if (level != 0) {
nonZeroCoeff++;
if (qp < 4) {
level = Math.min(level, CAVLC.CAVLC_LEVEL_LIMIT);
}
// sign(Z(i,j)) = sign(Y(i,j))
dst[j][i] = level * (int) (Math.signum(src[j][i]));
} else {
dst[j][i] = 0;
}
}
}
63
if (level != 0) {
nonZeroCoeff++;
// sign(Z(i,j)) = sign(Y(i,j))
dst[jj][ii] = level * (int) (Math.signum(src[jj][ii]));
run = 0;
} else {
dst[jj][ii] = 0;
run++;
}
public void iquantization4x4AC(int[][] src, int[][] dst, int pos_y, int pos_x) {
int qp_per = qp_per_matrix[qp];
int[][] v = invLevelScale4x4; // V = Qstep * PF * 64
}
64
package br.ufsc.inf.guiga.media.codec.video.h264.vcl.mode;
import java.io.OutputStream;
import br.ufsc.inf.guiga.media.codec.video.h264.vcl.FrameBuffer;
import br.ufsc.inf.guiga.media.codec.video.h264.vcl.Macroblock;
import br.ufsc.inf.guiga.media.codec.video.h264.vcl.datatype.MacroblockType;
import br.ufsc.inf.guiga.media.parser.video.YUVFrameBuffer;
import br.ufsc.inf.guiga.media.util.io.H264EntropyOutputStream;
/**
* This class represents one {@link Macroblock} coding mode.
*
* @author Guilherme Ferreira <guiga@inf.ufsc.br>
*/
public interface EncodingMode {
/**
* Encode the macroblock using this {@link EncodingMode}.
*
* @param inFrameBuffer the {@link FrameBuffer} that contains the macroblock data to
* be encoded.
* @param outFrameBuffer the {@link FrameBuffer} that will hold the coded data.
*/
public void encode(YUVFrameBuffer inFrameBuffer, YUVFrameBuffer outFrameBuffer);
/**
* Call this method to make this encoding mode writes encoded data into the
* {@link FrameBuffer}.
* <p>
* Every coded macroblock in an H.264 slice is predicted from previously-encoded data.
* Samples within an intra macroblock are predicted from samples in the current slice
* that have already been encoded, decoded and reconstructed; samples in an inter
* macroblock are predicted from previously-encoded.
*
* @param outFrameBuffer the {@link FrameBuffer} that will hold the coded data. This
* data will be used by the next macroblocks.
*/
public void reconstruct(YUVFrameBuffer outFrameBuffer);
/**
* Calculate the Sum of Squared Differences (SSD):
*
* <pre>
* SSD = Sum(i=0 to M){ Sum(j=0 to N){ (Ori(i,j) - Dec(i,j))ˆ2 }}
* </pre>
*
* @return the Sum of Squared Differences of the macroblock.
*/
public int getDistortion();
/**
* Writes the macroblock coded data to the {@link OutputStream}.
*
* @param outStream the {@link OutputStream} where the macroblock encoded will be
* write.
*/
public void write(H264EntropyOutputStream outStream);
/**
* The type of the macroblock depends on the encode mode that this object represents.
*
* @return the {@link MacroblockType} of this encoding mode.
65
*/
public MacroblockType getMbType();
package br.ufsc.inf.guiga.media.codec.video.h264.vcl.mode;
import java.io.OutputStream;
import br.ufsc.inf.guiga.media.codec.video.h264.vcl.FrameBuffer;
import br.ufsc.inf.guiga.media.codec.video.h264.vcl.Macroblock;
import br.ufsc.inf.guiga.media.codec.video.h264.vcl.datatype.MacroblockType;
import br.ufsc.inf.guiga.media.parser.video.YUVFrameBuffer;
import br.ufsc.inf.guiga.media.util.io.H264EntropyOutputStream;
/**
* This superclass provides common services to encoding modes subclasses.
*
* @author Guilherme Ferreira <guiga@inf.ufsc.br>
*/
public abstract class AbstractEncodingMode implements EncodingMode {
/**
* @param macroblock the {@link Macroblock} to be encoded by this mode.
* @param mbType subclass provide this field to identify which encoding mode it
represents.
*/
public AbstractEncodingMode(Macroblock macroblock, MacroblockType mbType) {
this.macroblock = macroblock;
this.mbType = mbType;
}
doEncode(inFrameBuffer, outFrameBuffer);
}
// LUMA
for (int j = y; j < y + Macroblock.MB_HEIGHT; j++) {
for (int i = x; i < x + Macroblock.MB_WIDTH; i++)
distortion += Math.pow(inputFrameBuffer.getY8bit(i, j)
- outputFrameBuffer.getY8bit(i, j), 2);
}
// CHROMA
for (int j = cy; j < cy + Macroblock.MB_CHROMA_HEIGHT; j++) {
for (int i = cx; i < cx + Macroblock.MB_CHROMA_WIDTH; i++) {
distortion += Math.pow(inputFrameBuffer.getCb8bit(i, j)
- outputFrameBuffer.getCb8bit(i, j), 2);
distortion += Math.pow(inputFrameBuffer.getCr8bit(i, j)
- outputFrameBuffer.getCr8bit(i, j), 2);
}
}
return distortion;
}
doWrite(outStream);
}
/**
* @return the {@link Macroblock} which owns this encoding mode.
*/
public Macroblock getMacroblock() {
return macroblock;
}
/**
* Subclasses must implement this method to encode the macroblock data.
* <p>
* <b>Important:</b>The coded data shall not be placed on the output buffer until the
* {@link EncodingMode#reconstruct(YUVFrameBuffer)} method on this mode be called. The
* output buffer is passed here to provide previously coded data from neighbours
* macroblocks.
*
* @param inFrameBuffer the {@link FrameBuffer} that contains the macroblock data to be
* encoded.
* @param codedFrameBuffer the {@link FrameBuffer} with previously coded macroblocks
data
* for prediction use only.
*/
protected abstract void doEncode(
YUVFrameBuffer inFrameBuffer, YUVFrameBuffer codedFrameBuffer);
/**
* Subclasses must implement this method in order to write their coded data into the
* stream.
*
* @param outStream the {@link OutputStream} where the macroblock encoded will be write.
*/
protected abstract void doWrite(H264EntropyOutputStream outStream);
package br.ufsc.inf.guiga.media.codec.video.h264.vcl.mode;
import java.util.ArrayList;
import java.util.List;
import br.ufsc.inf.guiga.media.Global;
import br.ufsc.inf.guiga.media.codec.video.h264.BaselineProfileFactory;
import br.ufsc.inf.guiga.media.codec.video.h264.vcl.Macroblock;
import br.ufsc.inf.guiga.media.codec.video.h264.vcl.algorithm.AlgorithmFactory;
import br.ufsc.inf.guiga.media.codec.video.h264.vcl.datatype.MacroblockType;
import
br.ufsc.inf.guiga.media.codec.video.h264.vcl.mode.prediction.Intra16x16LumaDCPredictor;
import
br.ufsc.inf.guiga.media.codec.video.h264.vcl.mode.prediction.Intra16x16LumaHorizontalPredict
or;
import
br.ufsc.inf.guiga.media.codec.video.h264.vcl.mode.prediction.Intra16x16LumaPlanePredictor;
import
br.ufsc.inf.guiga.media.codec.video.h264.vcl.mode.prediction.Intra16x16LumaVerticalPredictor
;
import
br.ufsc.inf.guiga.media.codec.video.h264.vcl.mode.prediction.Intra8x8ChromaDCPredictor;
import
br.ufsc.inf.guiga.media.codec.video.h264.vcl.mode.prediction.Intra8x8ChromaHorizontalPredict
or;
import
br.ufsc.inf.guiga.media.codec.video.h264.vcl.mode.prediction.Intra8x8ChromaPlanePredictor;
import
br.ufsc.inf.guiga.media.codec.video.h264.vcl.mode.prediction.Intra8x8ChromaVerticalPredictor
;
import br.ufsc.inf.guiga.media.codec.video.h264.vcl.mode.prediction.IntraPredictor;
67
import br.ufsc.inf.guiga.media.parser.video.YUVFrameBuffer;
import br.ufsc.inf.guiga.media.util.io.H264EntropyOutputStream;
/**
* Basically, this class selects the best luma intra 16x16 prediction mode.
*
* @author Guilherme Ferreira <guiga@inf.ufsc.br>
*/
public class Intra16x16EncodingMode extends AbstractEncodingMode {
codedBlockPattern = 0;
int x = macroblock.getPixelX();
int y = macroblock.getPixelY();
int xc = macroblock.getPixelChromaX();
int yc = macroblock.getPixelChromaY();
int qp = Global.getInstance().getH264Control().getQuantizerParameter();
}
// get the best luma intra 16x16 mode
int bestSAD = Integer.MAX_VALUE;
int currentSAD = 0;
for (Integer i : successPredLumaModes) {
currentSAD = lumaModes[i].getDistortion();
if (currentSAD < bestSAD) {
bestSAD = currentSAD;
bestLumaMode = lumaModes[i];
bestLumaModeIdx = i;
}
}
// encode the chosen intra luma mode
codedBlockPattern += bestLumaMode.encode(inFrameBuffer, codedFrameBuffer);
// generates the chroma intra prediction samples for all four 8x8 modes
for (int i = 0; i < chromaModes.length; i++) {
if (chromaModes[i].predict(inFrameBuffer, codedFrameBuffer)) {
successPredChromaModes.add(i);
}
}
// get the best chroma intra 8x8 mode
bestSAD = Integer.MAX_VALUE;
currentSAD = 0;
for (Integer i : successPredChromaModes) {
currentSAD = chromaModes[i].getDistortion();
if (currentSAD < bestSAD) {
bestSAD = currentSAD;
bestChromaMode = chromaModes[i];
bestChromaModeIdx = i;
}
}
releaseUnusedModes();
}
// intra_chroma_pred_mode
int intra_chroma_pred_mode = bestChromaModeIdx;
outStream.writeIntraChromaPredMode(intra_chroma_pred_mode);
// mb_qp_delta
int mb_qp_delta = 0; // TODO this.delta_qp = this.qp - prev.qp;
outStream.writeMackoblockQpDelta(mb_qp_delta);
// residual_luma()
// -> residual_block_cavlc( i16x16DClevel, 0, 15, 16 )
// -> residual_block_cavlc( i16x16AClevel[i8x8*4+ i4x4], ...)
bestLumaMode.write(outStream, codedBlockPattern);
/**
* Unreferenced the modes for garbage collector save memory. Only the modes referenced
* by bestXXMode will remain in memory...at least I hope so.
*/
private void releaseUnusedModes() {
for (int i = 0; i < lumaModes.length; i++) {
lumaModes[i] = null;
}
lumaModes = null;
for (int i = 0; i < chromaModes.length; i++) {
chromaModes[i] = null;
}
69
chromaModes = null;
}
package br.ufsc.inf.guiga.media.codec.video.h264.vcl.mode;
import br.ufsc.inf.guiga.media.codec.video.h264.vcl.Macroblock;
import br.ufsc.inf.guiga.media.codec.video.h264.vcl.datatype.MacroblockType;
import br.ufsc.inf.guiga.media.codec.video.h264.vcl.datatype.SliceType;
import br.ufsc.inf.guiga.media.parser.video.YUVFrameBuffer;
import br.ufsc.inf.guiga.media.util.io.H264EntropyOutputStream;
/**
* I_PCM is an Intra coding mode that enables an encoder to transmit the values of the
* image samples directly (without prediction or transformation). In some special cases
* (e.g. anomalous image content and/or very low quantizer parameters), this mode may be
* more efficient than the ‘usual’ process of intra prediction, transformation,
* quantization and entropy coding.
* <p>
* The I_PCM option also makes it possible to place an absolute limit on the number of
* bits that may be contained in a coded macroblock without constraining decoded image
* quality.
*
* @author Guilherme Ferreira <guiga@inf.ufsc.br>
*/
public class IPCMEncodingMode extends AbstractEncodingMode {
int[][] bufferY;
int[][] bufferU;
int[][] bufferV;
{
int luma, chromaU, chromaV;
int x = macroblock.getPixelX();
int y = macroblock.getPixelY();
int cx = macroblock.getPixelChromaX();
int cy = macroblock.getPixelChromaY();
// LUMA
for (int j = 0; j < Macroblock.MB_HEIGHT; j++) {
for (int i = 0; i < Macroblock.MB_WIDTH; i++) {
luma = inputFrameBuffer.getY8bit(i + x, j + y);
bufferY[i][j] = luma;
}
}
// CHROMA
for (int j = 0; j < Macroblock.MB_CHROMA_HEIGHT; j++) {
for (int i = 0; i < Macroblock.MB_CHROMA_WIDTH; i++) {
chromaU = inputFrameBuffer.getCb8bit(i + cx, j + cy);
chromaV = inputFrameBuffer.getCr8bit(i + cx, j + cy);
bufferU[i][j] = chromaU;
bufferV[i][j] = chromaV;
}
}
}
// mb_type
if (macroblock.getSlice().getSliceType().equals(SliceType.I_SLICE)) {
outStream.writeMacroblockType(25);
} else {
outStream.writeMacroblockType(31);
}
package br.ufsc.inf.guiga.media.codec.video.h264.vcl.mode.prediction;
import java.util.Arrays;
import br.ufsc.inf.guiga.media.Global;
import br.ufsc.inf.guiga.media.codec.video.h264.vcl.FrameBuffer;
71
import br.ufsc.inf.guiga.media.codec.video.h264.vcl.Macroblock;
import br.ufsc.inf.guiga.media.codec.video.h264.vcl.algorithm.AlgorithmFactory;
import br.ufsc.inf.guiga.media.codec.video.h264.vcl.algorithm.Quantizer;
import br.ufsc.inf.guiga.media.codec.video.h264.vcl.algorithm.Scanner;
import br.ufsc.inf.guiga.media.codec.video.h264.vcl.algorithm.Transform;
import br.ufsc.inf.guiga.media.codec.video.h264.vcl.algorithm.Quantizer.QuantizerSummary;
import br.ufsc.inf.guiga.media.codec.video.h264.vcl.block.ResidualBlockInfo;
import br.ufsc.inf.guiga.media.codec.video.h264.vcl.datatype.ResidualBlockType;
import br.ufsc.inf.guiga.media.codec.video.h264.vcl.macroblock.MacroblockAccess;
import br.ufsc.inf.guiga.media.codec.video.h264.vcl.macroblock.MacroblockInfo;
import br.ufsc.inf.guiga.media.codec.video.h264.vcl.mode.decision.DistortionMetric;
import br.ufsc.inf.guiga.media.parser.video.YUVFrameBuffer;
import br.ufsc.inf.guiga.media.util.SupportMath;
import br.ufsc.inf.guiga.media.util.io.H264EntropyOutputStream;
/**
* This class provides common services for Intra 16x16 Luma Prediction Modes.
*
* @author Guilherme Ferreira <guiga@inf.ufsc.br>
*/
public abstract class Intra16x16LumaAbstractPredictor implements IntraPredictor {
protected YUVFrameBuffer inputFrameBuffer; // raw YUV frame
protected YUVFrameBuffer outputFrameBuffer; // encoded frame
static {
bitDepthY = Global.getInstance().getH264Control().getBitDepthLuma();
maxImagePelValue = (1 << bitDepthY) - 1; // 2 ^ bitDepth
}
/**
* @param x the macroblock upper left corner horizontal position.
* @param y the macroblock upper left corner vertical position.
* @param macroblock the {@link Macroblock} that allow access to the macroblock
* neighbours.
* @param algorithms the {@link AlgorithmFactory} for algorithms creation.
*/
public Intra16x16LumaAbstractPredictor(
int x,
int y,
Macroblock macroblock,
AlgorithmFactory algorithms)
{
this.x = x;
this.y = y;
this.access = macroblock.getMacroblockAccess();
this.info = macroblock.getMacroblockInfo();
transform = algorithms.createTransform();
quantizer = algorithms.createQuantizer();
distortion = algorithms.createDistortionMetric();
scanner = algorithms.createScanner();
72
// Fill the macroblock matrix with the residual samples, original minus
// predicted.
fillResidualMatrix();
predicted = mPred[j][i];
residualRecons = SupportMath.rshiftRound(mrInv[j][i], dq_bits);
originalRecons = residualRecons + predicted;
originalRecons = SupportMath.clip(maxImagePelValue, originalRecons);
outFrameBuffer.setY8bit(ii, jj, originalRecons);
}
}
// AC coefficients
if ((codedBlockPattern & 15) != 0) {
73
// Entropy code
outStream.writeResidualBlock(coeffLevel, coeffRun,
ResidualBlockType.Intra16x16LumaACLevel, blockInfo);
}
}
}
/**
* Subclasses must implement this method in order to predict samples.
*
* @param codedFrameBuffer the {@link FrameBuffer} containing previously coded samples
* necessary for prediction.
* @param mPredCb the matrix where the predicted samples must be placed.
* @return <code>true</code> if the prediction was successfully made, or
* <code>false</code> if there were anything that prevented the prediction
* complete.
*/
protected abstract boolean doIntraPrediction(
YUVFrameBuffer codedFrameBuffer, int[][] mp);
/**
* Fill the matrix with the original frame samples.
*
* @param origiFrameBuffer
*/
private void fillOriginalMatrix(YUVFrameBuffer origiFrameBuffer) {
for (int j = 0; j < mbHeight; j++) {
int jj = y + j;
for (int i = 0; i < mbWidth; i++) {
int ii = x + i;
mOrig[j][i] = origiFrameBuffer.getY8bit(ii, jj);
}
}
}
/**
* Fill the matrix with the predicted residual samples.
*/
private void fillResidualMatrix() {
for (int j = 0; j < mbHeight; j++) {
for (int i = 0; i < mbWidth; i++) {
mResd[j][i] = mOrig[j][i] - mPred[j][i];
}
}
}
/**
* Applies a 16x16 luma transform and quantization on the residual samples.
*
* @param mrSrc residual coefficients
* @param m4Dst transformed hadamard coefficients
* @param mrDst transformed residual coefficients
* @return the amount of non-zero coefficients.
*/
74
// The DC coefficient of each 4x4 block is transformed again using a 4x4 Hadamard
// transform
transform.hadamard4x4(m4Dst, m4Dst);
// Quantize AC coefficients
for (int block_y = 0; block_y < mbHeight; block_y += 4) {
for (int block_x = 0; block_x < mbHeight; block_x += 4) {
qs = quantizer.quantization4x4AC(mrDst, mrDst, block_y, block_x);
if (qs.nonZeroCoeff > 0) {
acCoeff = 15;
}
}
}
return acCoeff;
}
/**
* Applies a 16x16 luma inverse transform and quantization on the residual samples.
*
* @param m4Src transformed hadamard coefficients
* @param mrSrc transformed residual coefficients
* @param mrDst inverse transformed residual coefficients
*/
private void inverseTransform(int[][] m4Src, int[][] mrSrc, int[][] mrDst) {
int[][] m4Inv = new int[dcHeight][dcWidth];
}
}
package br.ufsc.inf.guiga.media.codec.video.h264.vcl.mode.prediction;
import java.util.Arrays;
import br.ufsc.inf.guiga.media.Global;
import br.ufsc.inf.guiga.media.codec.video.h264.vcl.FrameBuffer;
import br.ufsc.inf.guiga.media.codec.video.h264.vcl.Macroblock;
import br.ufsc.inf.guiga.media.codec.video.h264.vcl.algorithm.AlgorithmFactory;
import br.ufsc.inf.guiga.media.codec.video.h264.vcl.algorithm.Quantizer;
import br.ufsc.inf.guiga.media.codec.video.h264.vcl.algorithm.Scanner;
import br.ufsc.inf.guiga.media.codec.video.h264.vcl.algorithm.Transform;
import br.ufsc.inf.guiga.media.codec.video.h264.vcl.algorithm.Quantizer.QuantizerSummary;
import br.ufsc.inf.guiga.media.codec.video.h264.vcl.block.ResidualBlockInfo;
import br.ufsc.inf.guiga.media.codec.video.h264.vcl.datatype.ResidualBlockType;
import br.ufsc.inf.guiga.media.codec.video.h264.vcl.macroblock.MacroblockAccess;
import br.ufsc.inf.guiga.media.codec.video.h264.vcl.macroblock.MacroblockInfo;
import br.ufsc.inf.guiga.media.codec.video.h264.vcl.mode.decision.DistortionMetric;
import br.ufsc.inf.guiga.media.parser.video.YUVFrameBuffer;
import br.ufsc.inf.guiga.media.util.SupportMath;
import br.ufsc.inf.guiga.media.util.io.H264EntropyOutputStream;
/**
* This class provides common services for Intra 8x8 Chroma Prediction Modes.
* <p>
* Both chroma blocks (Cb and Cr) of the macroblock use the same prediction mode. The
* prediction mode is applied to each of the chroma blocks separately.
*
* @author Guilherme Ferreira <guiga@inf.ufsc.br>
*/
public abstract class Intra8x8ChromaAbstractPredictor implements IntraPredictor {
private enum ChromaType {
CB, CR
};
// MbWidthC and MbHeightC specify the width and height, respectively, of the chroma
// arrays
// for each macroblock
protected int mbWidthC = 8;
protected int mbHeightC = 8;
76
static {
bitDepthC = Global.getInstance().getH264Control().getBitDepthChroma();
maxImagePelValue = (1 << bitDepthC) - 1; // 2 ^ bitDepth
}
/**
* @param x the macroblock upper left corner horizontal position for chroma component.
* @param y the macroblock upper left corner vertical position for chroma component.
* @param access the {@link Macroblock} that allow access to the macroblock
* neighbours.
* @param algorithms the {@link AlgorithmFactory} for algorithms creation.
*/
public Intra8x8ChromaAbstractPredictor(
int x,
int y,
Macroblock macroblock,
AlgorithmFactory algorithms)
{
this.x = x;
this.y = y;
this.access = macroblock.getMacroblockAccess();
this.info = macroblock.getMacroblockInfo();
transform = algorithms.createTransform();
quantizer = algorithms.createQuantizer();
distortion = algorithms.createDistortionMetric();
scanner = algorithms.createScanner();
int residualReconsCr;
// Reconstructed original sample
int originalReconsCb;
int originalReconsCr;
// Predicted sample
int predictedCb;
int predictedCr;
predictedCb = mPredCb[j][i];
residualReconsCb = SupportMath.rshiftRound(mrInvCb[j][i], dq_bits);
originalReconsCb = residualReconsCb + predictedCb;
originalReconsCb = SupportMath.clip(maxImagePelValue, originalReconsCb);
outFrameBuffer.setCb8bit(ii, jj, originalReconsCb);
predictedCr = mPredCr[j][i];
residualReconsCr = SupportMath.rshiftRound(mrInvCr[j][i], dq_bits);
originalReconsCr = residualReconsCr + predictedCr;
originalReconsCr = SupportMath.clip(maxImagePelValue, originalReconsCr);
outFrameBuffer.setCr8bit(ii, jj, originalReconsCr);
}
}
return cost;
}
/**
* Subclasses must implement this method in order to predict samples.
*
* @param codedFrameBuffer the {@link FrameBuffer} containing previously coded samples
* necessary for prediction.
* @param mPredCb the matrix where the predicted U samples must be placed.
* @param mPredCr the matrix where the predicted V samples must be placed.
* @return <code>true</code> if the prediction was successfully performed, or
* <code>false</code> if there were anything that prevented the prediction
* complete.
*/
protected abstract boolean doIntraPrediction(
YUVFrameBuffer codedFrameBuffer, int[][] mpCb, int[][] mpCr);
/**
* Fill the matrix with the original frame samples.
*
* @param origiFrameBuffer
*/
private void fillOriginalMatrix(YUVFrameBuffer origiFrameBuffer) {
for (int j = 0; j < mbHeightC; j++) {
int jj = y + j;
for (int i = 0; i < mbWidthC; i++) {
int ii = x + i;
mOrigCb[j][i] = origiFrameBuffer.getCb8bit(ii, jj);
mOrigCr[j][i] = origiFrameBuffer.getCr8bit(ii, jj);
79
}
}
}
/**
* Fill the matrix with the predicted residual samples.
*/
private void fillResidualMatrix() {
for (int j = 0; j < mbHeightC; j++) {
for (int i = 0; i < mbWidthC; i++) {
mResdCb[j][i] = mOrigCb[j][i] - mPredCb[j][i];
mResdCr[j][i] = mOrigCr[j][i] - mPredCr[j][i];
}
}
}
/**
* Applies a 8x8 4:2:0 chroma transform and quantization on the residual samples.
*
* @param mrSrc residual coefficients.
* @param m2Dst transformed hadamard coefficients.
* @param mrDst transformed residual coefficients.
* @param chromaType the {@link ChromaType} of this block.
* @return the amount of non-zero coefficients.
*/
private int forwardTransform(
int[][] mrSrc, int[][] m2Dst, int[][] mrDst, ChromaType chromaType)
{
ResidualBlockType typeDC, typeAC;
if (chromaType == ChromaType.CB) {
typeDC = ResidualBlockType.CbIntra8x8ChromaDCLevel;
typeAC = ResidualBlockType.CbIntra8x8ChromaACLevel;
} else {
typeDC = ResidualBlockType.CrIntra8x8ChromaDCLevel;
typeAC = ResidualBlockType.CrIntra8x8ChromaACLevel;
}
long coeffCost = 0;
int nonZeroCoeff = 0;
int dcCoeff = 0;
int acCoeff = 0;
// Build a 2x2 DC matrix with the DC coefficients from each 4x4 block
for (int j = 0; j < dcHeightC; j++) {
for (int i = 0; i < dcWidthC; i++) {
m2Dst[j][i] = mrDst[j << 2][i << 2];
}
}
// Quantize AC coefficients
for (int block_y = 0; block_y < mbHeightC; block_y += 4) {
for (int block_x = 0; block_x < mbWidthC; block_x += 4) {
coeffCost += qs.coeffCost;
if (qs.nonZeroCoeff > 0) {
nonZeroCoeff = qs.nonZeroCoeff;
acCoeff = 1;
}
// Perform thresholding
if ((nonZeroCoeff > 0) && (coeffCost < CHROMA_COEFF_COST)) {
// If there's any AC block with coefficient different than zero, reset all
// chroma coefficients.
for (int block_y = 0; block_y < mbHeightC; block_y += 4) {
for (int block_x = 0; block_x < mbWidthC; block_x += 4) {
acCoeff = 0;
}
/**
* Applies a 8x8 4:2:0 chroma inverse transform and quantization on the residual
* samples.
*
* @param m2Src transformed hadamard coefficients
* @param mrSrc transformed residual coefficients
* @param mrDst inverse transformed residual coefficients
*/
private void inverseTransform(int[][] m2Src, int[][] mrSrc, int[][] mrDst) {
int[][] m2Inv = new int[dcHeightC][dcWidthC];
}
81
package br.ufsc.inf.guiga.media.codec.video.h264.vcl.mode.prediction;
import java.awt.Point;
import br.ufsc.inf.guiga.media.codec.video.h264.vcl.Macroblock;
import br.ufsc.inf.guiga.media.codec.video.h264.vcl.algorithm.AlgorithmFactory;
import br.ufsc.inf.guiga.media.parser.video.YUVFrameBuffer;
import br.ufsc.inf.guiga.media.util.SupportMath;
/**
* Intra 16x16 Luma Prediction Mode 2 (DC): Mean of upper and left-hand samples
* (H + V).
*
* @author Guilherme Ferreira <guiga@inf.ufsc.br>
*/
public class Intra16x16LumaDCPredictor extends Intra16x16LumaAbstractPredictor {
public Intra16x16LumaDCPredictor(
int x,
int y,
Macroblock macroblock,
AlgorithmFactory algorithms)
{
super(x, y, macroblock, algorithms);
}
// store DC prediction
for (int j = 0; j < Macroblock.MB_HEIGHT; j++) {
82
return true;
}
}
package br.ufsc.inf.guiga.media.codec.video.h264.vcl.mode.prediction;
import java.awt.Point;
import br.ufsc.inf.guiga.media.codec.video.h264.vcl.Macroblock;
import br.ufsc.inf.guiga.media.codec.video.h264.vcl.algorithm.AlgorithmFactory;
import br.ufsc.inf.guiga.media.parser.video.YUVFrameBuffer;
/**
* Intra 8x8 Chroma Prediction Mode 0 (DC).
*
* @author Guilherme Ferreira <guiga@inf.ufsc.br>
*/
public class Intra8x8ChromaDCPredictor extends Intra8x8ChromaAbstractPredictor {
public Intra8x8ChromaDCPredictor(
int x,
int y,
Macroblock macroblock,
AlgorithmFactory algorithms)
{
super(x, y, macroblock, algorithms);
}
int xO = posX(chroma4x4BlkIdx);
int yO = posY(chroma4x4BlkIdx);
} // TOP-RIGHT
else if ((xO > 0) && (yO == 0)) {
if (upAvail) {
// predC[x+xO, y+yO] = (sumUp + 2) >> 2
predCb = (sumUpCb + 2) >> 2;
predCr = (sumUpCr + 2) >> 2;
} else if (leftAvail) {
// predC[x+xO, y+yO] = (sumLeft + 2) >> 2
predCb = (sumLeftCb + 2) >> 2;
predCr = (sumLeftCr + 2) >> 2;
} else {
// predC[x+xO, y+yO] = (1 << ( BitDepthC – 1 ))
predCb = 1 << (bitDepthC - 1);
predCr = 1 << (bitDepthC - 1);
}
} // BOTTOM-LEFT
else if ((xO == 0) && (yO > 0)) {
if (leftAvail) {
// predC[x+xO, y+yO] = (sumLeft + 2) >> 2
predCb = (sumLeftCb + 2) >> 2;
predCr = (sumLeftCr + 2) >> 2;
} else if (upAvail) {
// predC[x+xO, y+yO] = (sumUp + 2) >> 2
predCb = (sumUpCb + 2) >> 2;
predCr = (sumUpCr + 2) >> 2;
} else {
// predC[x+xO, y+yO] = (1 << ( BitDepthC – 1 ))
predCb = 1 << (bitDepthC - 1);
predCr = 1 << (bitDepthC - 1);
}
// store DC prediction
for (int j = yO; j < CHROMA_BLK_HEIGHT + yO; j++) {
for (int i = xO; i < CHROMA_BLK_WIDTH + xO; i++) {
mpCb[j][i] = predCb;
mpCr[j][i] = predCr;
}
}
}
return true;
}
package br.ufsc.inf.guiga.media.codec.video.h264.vcl.mode.decision;
/**
* Error Metric measures the energy of the residual transform coefficients after
* quantization.
*
* @author Guilherme Ferreira <guiga@inf.ufsc.br>
*/
public interface DistortionMetric {
/**
* Computes the distortion between two 16x16 blocks.
*
* @param orig the original block.
* @param pred the predicted block.
* @return an integer number representing the distortion between the two
* blocks. The number magnitude tends to be lower as the two blocks
* match closer.
*/
public int getDistortion16x16(int[][] orig, int[][] pred);
/**
* Computes the distortion between two 4x4 blocks.
*
* @param orig the original block.
* @param pred the predicted block.
* @param pos_y a vertical offset shall be provide if the blocks height is
* wider than 4 elements.
* @param pos_x a horizontal offset shall be provide if the blocks width is
* wider than 4 elements.
* @return an integer number representing the distortion between the two
* blocks. The number magnitude tends to be lower as the two blocks
* match closer.
*/
public int getDistortion4x4(int[][] orig, int[][] pred, int pos_y, int pos_x);
package br.ufsc.inf.guiga.media.codec.video.h264.vcl.mode.decision;
import br.ufsc.inf.guiga.media.codec.video.h264.vcl.algorithm.Transform;
/**
* SA(T)D, the Sum of Absolute Differences of the Transformed residual data.
*
* @author Guilherme Ferreira <guiga@inf.ufsc.br>
*/
public class SATD implements DistortionMetric {
// sum SATD
for (j = 0; j < 4; j++) {
for (i = 0; i < 4; i++) {
satd += Math.abs(M4[j][i]);
}
}
return satd;
}
int satd = 0;
int[][] diff = new int[4][4];
int[] d = new int[16];
int[] m = new int[16];
// hadamard transform
m[0] = diff[0][0] + diff[3][0];
m[1] = diff[0][1] + diff[3][1];
m[2] = diff[0][2] + diff[3][2];
m[3] = diff[0][3] + diff[3][3];
m[4] = diff[1][0] + diff[2][0];
m[5] = diff[1][1] + diff[2][1];
m[6] = diff[1][2] + diff[2][2];
m[7] = diff[1][3] + diff[2][3];
m[8] = diff[1][0] - diff[2][0];
m[9] = diff[1][1] - diff[2][1];
86
// sum SATD
for (int k = 0; k < 16; ++k) {
satd += Math.abs(d[k]);
}
package br.ufsc.inf.guiga.media.codec.video.h264.vcl.entropy;
{ 3,3,3,3,3,4,4,4,4,5,5,6,6,6,6},
{ 4,3,3,3,4,4,3,3,4,5,5,6,5,6},
{ 5,3,4,4,3,3,3,4,3,4,5,5,5},
{ 4,4,4,3,3,3,3,3,4,5,4,5},
{ 6,5,3,3,3,3,3,3,4,3,6},
{ 6,5,3,3,3,2,3,4,3,6},
{ 6,4,5,3,2,2,3,3,6},
{ 6,6,4,2,2,3,2,5},
{ 5,5,3,2,2,2,4},
{ 4,4,3,3,1,3},
{ 4,4,2,1,3},
{ 3,3,1,2},
{ 2,2,1},
{ 1,1}
};
protected static final int[][] totalZeros4x4CodeValue =
{ {1,3,2,3,2,3,2,3,2,3,2,3,2,3,2,1},
{7,6,5,4,3,5,4,3,2,3,2,3,2,1,0},
{5,7,6,5,4,3,4,3,2,3,2,1,1,0},
{3,7,5,4,6,5,4,3,3,2,2,1,0},
{5,4,3,7,6,5,4,3,2,1,1,0},
{1,1,7,6,5,4,3,2,1,1,0},
{1,1,5,4,3,3,2,1,1,0},
{1,1,1,3,3,2,2,1,0},
{1,0,1,3,2,1,1,1,},
{1,0,1,3,2,1,1,},
{0,1,1,2,1,3},
{0,1,1,1,1},
{0,1,1,1},
{0,1,1},
{0,1}
};
// [tzVlcIndex (VLC Table)][total_zeros]
protected static final int[][] totalZeros2x2CodeLength =
{ { 1,2,3,3},
{ 1,2,2},
{ 1,1}
};
protected static final int[][] totalZeros2x2CodeValue =
{ { 1,1,1,0},
{ 1,1,0},
{ 1,0}
};
// [zerosLeft][run_before]
protected static final int[][] runBeforeCodeLength =
{ {1,1},
{1,2,2},
{2,2,2,2},
{2,2,2,3,3},
{2,2,3,3,3,3},
{2,3,3,3,3,3,3},
{3,3,3,3,3,3,3,4,5,6,7,8,9,10,11},
};
protected static final int[][] runBeforeCodeValue =
{ {1,0},
{1,1,0},
{3,2,1,0},
{3,2,1,1,0},
{3,2,3,2,1,0},
{3,0,1,3,2,5,4},
{7,6,5,4,3,2,1,1,1,1,1,1,1,1,1},
};
}
package br.ufsc.inf.guiga.media.codec.video.h264.vcl.entropy;
import br.ufsc.inf.guiga.media.codec.video.h264.vcl.block.ResidualBlockInfo;
import br.ufsc.inf.guiga.media.codec.video.h264.vcl.datatype.ResidualBlockType;
import br.ufsc.inf.guiga.media.util.io.BitOutputStream;
/**
* (CA)VLC coding methods.
89
*
* @author Guilherme Ferreira <guiga@inf.ufsc.br>
*/
public class CAVLC extends VLCTable implements EntropyOutputStream {
/**
* Creates an output stream that writes CAVLC coded values.
*
* @param bitStream the target bit stream that all values should be coded into.
*/
public CAVLC(BitOutputStream bitStream) {
this.bitStream = bitStream;
}
// -------------------------------------------------------------------------
// FIXED LENGTH
// -------------------------------------------------------------------------
se.bitpattern = value;
se.length = n;
se.value1 = value;
writeUVLC2buffer(se, bitStream);
return se.length;
}
se.bitpattern = value ? 1 : 0;
se.length = 1;
se.value1 = value ? 1 : 0;
writeUVLC2buffer(se, bitStream);
return se.length;
}
// -------------------------------------------------------------------------
// EXP-GOLOMB
// -------------------------------------------------------------------------
se.value1 = value;
se.value2 = 0;
ue_linfo(se);
symbol2uvlc(se);
writeUVLC2buffer(se, bitStream);
return se.length;
}
se.value1 = value;
se.value2 = 0;
se_linfo(se);
symbol2uvlc(se);
writeUVLC2buffer(se, bitStream);
90
return se.length;
}
/**
* Mapping for ue(v) syntax elements.<br>
* {@link SyntaxElement#length}: returns total mapped value length, including leading
* zeros<br>
* (i.e., the length of the string 0 0 .. 0 1 Xn .. X1 X0 )<br>
* {@link SyntaxElement#info}: returns mapped value Xn..X2 X1 X0
*
* @param se <br>
* <code>SyntaxElement.value1</code>: value to be mapped
*/
private void ue_linfo(SyntaxElement se) {
int nn = (se.value1 + 1) >> 1;
int i;
for (i = 0; i < 33 && nn != 0; i++) {
nn >>= 1;
}
se.length = (i << 1) + 1;
// read_bits( leadingZeroBits ) = codeNum - 2^leadingZeroBits (page 154)
se.info = se.value1 + 1 - (1 << i);
}
/**
* Mapping for se(v) syntax elements.<br>
* {@link SyntaxElement#length}: returns total mapped value length, including leading
* zeros<br>
* (i.e., the length of the string 0 0 .. 0 1 Xn .. X1 X0 )<br>
* {@link SyntaxElement#info}: returns mapped value Xn..X2 X1 X0
*
* @param se <br>
* {@link SyntaxElement#value1}: value to be mapped
*/
private void se_linfo(SyntaxElement se) {
int sign = (se.value1 <= 0) ? 1 : 0;
// n+1 is the number in the code table. Based on this we find length and
// info
int n = Math.abs(se.value1) << 1;
int nn = (n >> 1);
int i;
for (i = 0; i < 33 && nn != 0; i++) {
nn >>= 1;
}
se.length = (i << 1) + 1;
se.info = n - (1 << i) + sign;
}
/**
* Makes code word and passes it back through the attribute
* {@link SyntaxElement#bitpattern}, which receives a code word in the following
* format: <br>
* 0 0 0 ... 1 Xn ...X2 X1 X0.
*
* @param se <br>
* {@link SyntaxElement#info}: Xn..X2 X1 X0 <br>
* {@link SyntaxElement#length}: Total number of bits in the codeword
*/
private int symbol2uvlc(SyntaxElement se) {
int suffix_len = se.length >> 1;
return 0;
}
// -------------------------------------------------------------------------
// CAVLC
// -------------------------------------------------------------------------
/**
* Makes code word and passes it back.
*
* @param se <br>
* {@link SyntaxElement#info}: Xn..X2 X1 X0 <br>
91
// VLC coding
while (--info_len >= 0) {
se.bitpattern <<= 1;
se.bitpattern |= (0x01 & (se.info >> info_len));
}
return 0;
}
// -------------------------------------------------------------------------
// PRE-DEFINED SYMBOLS WRITING FUNCTIONS
// -------------------------------------------------------------------------
// Count the amount of non-zero coefficients and the trailing ones to build
// coeff_token.
for (int k = 0; k < maxNumCoeff; k++) {
int level = coeffLevel[k];
int run = coeffRun[k];
if (level != 0) {
totalZeros += run;
if (Math.abs(level) == 1) {
trailingOnes++;
trailingOnes = Math.min(trailingOnes, 3); // clip to 3
} else {
trailingOnes = 0;
}
totalCoeff++;
lastCoeff = k;
}
}
if (totalCoeff > 0) {
// Write trailing_ones_sign_flags: an integer with the signal of
// each trailing ones in reverse order, from highest frequency
// coefficient to DC coefficient.
92
int trailingOnesSignFlags = 0;
for (int k = lastCoeff; k > (lastCoeff - trailingOnes); k--) {
int level = coeffLevel[k];
trailingOnesSignFlags <<= 1;
if (level < 0) {
trailingOnesSignFlags |= 0x1;
}
}
if (trailingOnes > 0)
numBits += writeTrailingOnesSignFlag(trailingOnesSignFlags, trailingOnes);
if (levelTwoOrHigher != 0) {
levelTwoOrHigher = 0;
if (levelTmp > 0)
levelTmp--;
else
levelTmp++;
}
zerosLeft -= runBefore;
numCoeff--;
}
return numBits;
}
switch (type) {
case Intra16x16LumaDCLevel:
maxNumCoeff = 16; // 16 DCs from the sixteen 4x4 blocks
93
break;
case Intra16x16LumaACLevel:
maxNumCoeff = 15; // the first level/coefficient of each AC block is the DC
break;
case CbIntra8x8ChromaDCLevel:
case CrIntra8x8ChromaDCLevel:
maxNumCoeff = 4; // 4 DCs from the four 4x4 blocks
break;
case CbIntra8x8ChromaACLevel:
case CrIntra8x8ChromaACLevel:
maxNumCoeff = 15; // the first level of each AC block is the DC
break;
}
return maxNumCoeff;
}
int vlcTableIdx = 0;
if ((type == ResidualBlockType.CbIntra8x8ChromaDCLevel)
|| (type == ResidualBlockType.CrIntra8x8ChromaDCLevel))
{
// chroma DC (has its own VLC, Table 5) if nC == -1 for 4:2:0
vlcTableIdx = 4;
} else {
// selects VLC table based on nC
if (nC < 2) {
vlcTableIdx = 0;
} else if (nC < 4) {
vlcTableIdx = 1;
} else if (nC < 8) {
vlcTableIdx = 2;
} else {
vlcTableIdx = 3;
}
}
return vlcTableIdx;
}
if (totalCoeff > 0) {
se.info = ((totalCoeff - 1) << 2) | trailingOnes;
} else {
se.info = 3;
}
symbol2vlc(se);
writeUVLC2buffer(se, bitStream);
return se.length;
}
if (trailingOnes >= 0) {
SyntaxElement se = new SyntaxElement();
se.length = trailingOnes;
se.info = trailingOnesSignFlags;
symbol2vlc(se);
writeUVLC2buffer(se, bitStream);
numBits = se.length;
}
return numBits;
}
if (levabs < 8) {
se.length = levabs * 2 + sign - 1;
se.info = 1;
} else {
int iLength = 28, numPrefix = 15;
int iCodeword, addbit;
int levabsm16 = levabs - 16;
// escape code2
if ((levabsm16) >= 2048) {
numPrefix++;
while ((levabsm16) >= (1 << (numPrefix - 3)) - 4096) {
numPrefix++;
}
}
se.length = iLength;
se.info = iCodeword;
}
symbol2vlc(se);
writeUVLC2buffer(se, bitStream);
return se.length;
}
iLength = 28;
symbol2vlc(se);
writeUVLC2buffer(se, bitStream);
return se.length;
}
symbol2vlc(se);
writeUVLC2buffer(se, bitStream);
numBits = se.length;
}
return numBits;
}
if (zerosLeft > 0) {
SyntaxElement se = new SyntaxElement();
int vlcTable = Math.min(zerosLeft - 1, RUNBEFORE_NUM_M1);
se.length = runBeforeCodeLength[vlcTable][runBefore];
se.info = runBeforeCodeValue[vlcTable][runBefore];
symbol2vlc(se);
writeUVLC2buffer(se, bitStream);
}
return numBits;
}
// --------------------------------------------------------------------------
// HELPER
// --------------------------------------------------------------------------
/**
* Writes UVLC code to the appropriate buffer.
*
* @param se the syntax element to write in the stream.<br>
* {@link SyntaxElement#length}: number of bits to write.<br>
* {@link SyntaxElement#bitpattern}: the bits to write.
* @param currStream the stream in which the syntax element will be write.
*/
private void writeUVLC2buffer(SyntaxElement se, BitOutputStream currStream) {
currStream.write(se.bitpattern, se.length);
}
package br.ufsc.inf.guiga.media.codec.video.h264.vcl.macroblock;
import java.awt.Point;
import br.ufsc.inf.guiga.media.codec.video.h264.vcl.Macroblock;
/**
* Macroblock Neighbours Access.
* <p>
* This class provides facilities to access the macroblock neighbours.
*
* @author Guilherme Ferreira <guiga@inf.ufsc.br>
*/
public abstract class MacroblockAccess {
/**
* @param macroblockX the {@link Macroblock} owner of this {@link MacroblockAccess}.
*/
public MacroblockAccess(Macroblock macroblock) {
this.macroblockX = macroblock;
mbNr = macroblockX.getMbNr();
frameWidth = macroblockX.getSlice().getPicture().getWidth();
97
/**
* Checks the availability of neighboring macroblocks of the current macroblock for
* prediction and context determination.
*/
public abstract void checkAvailableNeighbours();
/**
* Gets the absolute frame pixel point from the macroblock relative point (xN, yN).
*
* @param xN input x position. The macroblock relative x coordinate within the interval
* [-1..15].
* @param yN input y position. The macroblock relative y coordinate within the interval
* [-1..15].
* @param maxW the block pixel width.
* @param maxH the block pixel height.
* @param p the {@link Point} where the absolute (x, y) coordinates will return.
* @return <code>true</code> if the {@link Point} was successfully filled with the
* absolute pixel coordinates, or <code>false</code> if there are no valid
* neighbours at the specified (xN, yN) coordinates.
*/
public abstract boolean getNeighbour(int xN, int yN, int maxW, int maxH, Point p);
/**
* Checks out if there are maxW pixels above this macroblock available for prediction.
*
* @param maxW the amount of pixels to check for availability.
* @return <code>true</code> if maxW pixels above this macroblock are available for
* prediction, or <code>false</code> otherwise.
*/
public boolean isUpAvailable(int maxW) {
Point p = new Point();
int maxH = 0;
/**
* Checks out if there are maxH pixels available for prediction on the left side of this
* macroblock.
*
* @param maxH the amount of pixels to check for availability
* @return <code>true</code> if maxW pixels on the left of this macroblock are available
* for prediction, or <code>false</code> otherwise.
*/
public boolean isLeftAvailable(int maxH) {
Point p = new Point();
int maxW = 0;
/**
* Checks out if the pixel on the left upper corner of this macroblock available for
* prediction.
*
* @return <code>true</code> if the pixel on the left upper corner of this macroblock
are
* available for prediction, or <code>false</code> otherwise.
*/
public boolean isLeftUpAvailable() {
Point p = new Point();
int maxW = 0;
int maxH = 0;
return getNeighbour(-1, -1, maxW, maxH, p);
}
/**
* Gets the {@link Macroblock} with the given number.
*
* @param mbNr
* @return the {@link Macroblock} with the given number or <code>null</code> if its not
* available.
98
*/
public Macroblock getMacroblock(int mbNr) {
if ((mbNr < 0) || (mbNr > (picSizeInMbs - 1)))
return null;
return macroblockX.getSlice().getMacroblocks().get(mbNr);
}
/**
* Gets the column number where lies the given macroblock number.
*
* @param mbNr
* @return the column this macroblock number belongs.
*/
public int column(int mbNr) {
return (mbNr % picWidthInMbs);
}
/**
* Gets the line number where lies the given macroblock number.
*
* @param mbNr
* @return the line this macroblock number belongs.
*/
public int line(int mbNr) {
return (mbNr / picWidthInMbs);
}
/**
* @return the {@link Macroblock} on the left side of this one.
*/
public Macroblock getMacroblockA() {
return macroblockA;
}
/**
* @return the {@link Macroblock} above this one, the upper side one.
*/
public Macroblock getMacroblockB() {
return macroblockB;
}
/**
* @return the upper-right {@link Macroblock}.
*/
public Macroblock getMacroblockC() {
return macroblockC;
}
/**
* @return the upper-left {@link Macroblock}.
*/
public Macroblock getMacroblockD() {
return macroblockD;
}
package br.ufsc.inf.guiga.media.codec.video.h264.vcl.macroblock;
import java.awt.Point;
import br.ufsc.inf.guiga.media.codec.video.h264.vcl.Macroblock;
/**
* Macroblock Neighbours Access for Non MBAFF coding.
*
* @author Guilherme Ferreira <guiga@inf.ufsc.br>
*/
public class MacroblockAccessNonMBAFF extends MacroblockAccess {
99
/**
* @param macroblockX the {@link Macroblock} owner of this {@link
MacroblockAccessNonMBAFF}.
*/
public MacroblockAccessNonMBAFF(Macroblock macroblock) {
super(macroblock);
}
if (column(mbNr) != 0)
macroblockA = getMacroblock(mbNrA);
macroblockB = getMacroblock(mbNrB);
if (column(mbNr + 1) != 0)
macroblockC = getMacroblock(mbNrC);
if (column(mbNr) != 0)
macroblockD = getMacroblock(mbNrD);
}
public boolean getNeighbour(int xN, int yN, int maxW, int maxH, Point p) {
Macroblock neighbour = null;
boolean available = false;
if (neighbour != null) {
int xW = xN & (maxW - 1); // xW = (xN + maxW) % maxW
int yW = yN & (maxH - 1); // yW = (yN + maxH) % maxH
int pos_x = xW + column(neighbour.getMbNr()) * maxW;
int pos_y = yW + line(neighbour.getMbNr()) * maxH;
p.setLocation(pos_x, pos_y);
available = true;
}
return available;
}
package br.ufsc.inf.guiga.media.codec.video.h264.vcl.macroblock;
import br.ufsc.inf.guiga.media.codec.video.h264.vcl.Macroblock;
import br.ufsc.inf.guiga.media.codec.video.h264.vcl.block.ResidualBlockInfo;
import br.ufsc.inf.guiga.media.codec.video.h264.vcl.datatype.ResidualBlockType;
/**
* Holds information about the residual blocks of a macroblock.
*
* @author Guilherme Ferreira <guiga@inf.ufsc.br>
*/
public class MacroblockInfo {
/**
* Get the {@link ResidualBlockInfo} object at a given (x,y) position.
*
* @param blkIdxX the x position.
* @param blkIdxY the y position.
* @param type the {@link ResidualBlockType}.
* @return the {@link ResidualBlockInfo} of type at position (x,y).
*/
public ResidualBlockInfo getBlockInfo(int blkIdxX, int blkIdxY, ResidualBlockType type)
{
ResidualBlockInfo blockInfo = null;
switch (type) {
case Intra16x16LumaDCLevel: // AC(0,0) and DC share the same prediction block
case Intra16x16LumaACLevel:
blockInfo = lumaAcBlk[blkIdxX][blkIdxY];
break;
case CbIntra8x8ChromaDCLevel: // AC(0,0) and DC share the same prediction block
case CbIntra8x8ChromaACLevel:
blockInfo = cbChromaAcBlk[blkIdxX][blkIdxY];
break;
case CrIntra8x8ChromaDCLevel: // AC(0,0) and DC share the same prediction block
case CrIntra8x8ChromaACLevel:
blockInfo = crChromaAcBlk[blkIdxX][blkIdxY];
break;
}
return blockInfo;
}
/**
* Set the {@link ResidualBlockInfo} object at a given (x,y) position.
*
* @param blkIdxX the x position.
* @param blkIdxY the y position.
* @param type the {@link ResidualBlockType}.
* @param blockInfo the {@link ResidualBlockInfo} of type at position (x,y).
*/
public void setBlockInfo(
int blkIdxX, int blkIdxY, ResidualBlockType type, ResidualBlockInfo blockInfo)
{
blockInfo.setMacroblockInfo(this);
switch (type) {
case Intra16x16LumaDCLevel: // AC(0,0) and DC share the same prediction block
case Intra16x16LumaACLevel:
blockInfo.setBlockIndexX(blkIdxX);
blockInfo.setBlockIndexY(blkIdxY);
lumaAcBlk[blkIdxX][blkIdxY] = blockInfo;
break;
case CbIntra8x8ChromaDCLevel: // AC(0,0) and DC share the same prediction block
case CbIntra8x8ChromaACLevel:
blockInfo.setBlockIndexX(blkIdxX);
blockInfo.setBlockIndexY(blkIdxY);
cbChromaAcBlk[blkIdxX][blkIdxY] = blockInfo;
break;
case CrIntra8x8ChromaDCLevel: // AC(0,0) and DC share the same prediction block
case CrIntra8x8ChromaACLevel:
blockInfo.setBlockIndexX(blkIdxX);
blockInfo.setBlockIndexY(blkIdxY);
crChromaAcBlk[blkIdxX][blkIdxY] = blockInfo;
break;
101
/**
* Get the Number of Nonzero Coefficients predicted from the neighboring 4x4 blocks.
*
* @param type the {@link ResidualBlockType}.
* @param residualBlock the {@link ResidualBlockInfo}.
* @return the number o nonzero coefficients predicted from the available neighbour
* blocks.
*/
public int getPredictedNonZeroCoeff(
ResidualBlockType type, ResidualBlockInfo residualBlock)
{
int blkIdxX = residualBlock.getBlockIndexX();
int blkIdxY = residualBlock.getBlockIndexY();
Macroblock mbA = null;
Macroblock mbB = null;
boolean isUpperBlockAvailable;
boolean isLeftBlockAvailable;
int nA, nB, nC = 0;
// Compute nC
if (!isLeftBlockAvailable && isUpperBlockAvailable) {
ResidualBlockInfo blockB = mbB.getMacroblockInfo().getBlockInfo(blkIdxXB,
blkIdxYB, type);
nB = blockB.getNonZeroCoeff();
blkIdxYB, type);
nA = blockA.getNonZeroCoeff();
nB = blockB.getNonZeroCoeff();
} else {
// nC = 0, when neither block is available
nC = 0;
}
return nC;
}
switch (type) {
case CbIntra8x8ChromaDCLevel:
case CrIntra8x8ChromaDCLevel:
case CbIntra8x8ChromaACLevel:
case CrIntra8x8ChromaACLevel:
prevBlkIdx = (blkIdx != 0) ? blkIdx - 1 : 1;
break;
case Intra16x16LumaDCLevel:
case Intra16x16LumaACLevel:
prevBlkIdx = (blkIdx != 0) ? blkIdx - 1 : 3;
break;
}
return prevBlkIdx;
}
}
package br.ufsc.inf.guiga.media.codec.video.h264.vcl.macroblock;
import br.ufsc.inf.guiga.media.codec.video.h264.vcl.Macroblock;
/**
* This class holds macroblock position.
*
* @author Guilherme Ferreira <guiga@inf.ufsc.br>
*/
public class MacroblockPosition {
}
103
Neste apêndice são mostrados os diagramas das classes que compõem o codificador
proposto neste trabalho.
A classe BasicMux, presente no diagrama abaixo, pertencem à JMF e foi estendida para
permitir a infra-estrutura de escrita em arquivos da JMF.
Tal como no parágrafo anterior, parte das classes presentes no diagrama abaixo pertencem à
JMF, as quais foram estendidas para criar a classe principal do codificador apresentado neste
trabalho.
104
B3 Classes de codificação
block.c/.h Block.java
Este apêndice contém uma breve descrição das ferramentas de suporte desenvolvidas
durante o projeto do codificador.
D1 YUVPlayer
D2 AVCEncoder
Múltiplos Quadros de Referência Sim Sim Sim Sim Sim Sim Sim
Codificação de entropia CAVLC Sim Sim Sim Sim Sim Sim Sim
Codificação de entropia CABAC Não Não Sim Sim Sim Sim Sim
Predição com peso Não Sim Sim Sim Sim Sim Sim
Seqüência Flexível de Macrobloco (FMO) Sim Sim Não Não Não Não Não
Seqüência Arbitrária de Fatia (ASO) Sim Sim Não Não Não Não Não
Fatias Redundantes (RS) Sim Sim Não Não Não Não Não
Particionamento de Dado (DP) Não Sim Não Não Não Não Não
Codificação Intrelaçada (PicAFF, MBAFF) Não Sim Sim Sim Sim Sim Sim
Formato de quadro 4:2:0 Sim Sim Sim Sim Sim Sim Sim
Formato de quadro 4:0:0 Não Não Não Sim Sim Sim Sim
Formato de quadro 4:2:2 Não Não Não Não Não Sim Sim
Formato de quadro 4:4:4 Não Não Não Não Não Não Sim
Adaptação de transformada 8x8 vs. 4x4 Não Não Não Sim Sim Sim Sim
Matrizes de Quantização Escalonáveis Não Não Não Sim Sim Sim Sim
Controle de QP Separado para Cb e Cr Não Não Não Sim Sim Sim Sim
Codificação Separada de Plano de Cor Não Não Não Não Não Não Sim
Codificação Preditiva sem Perda Não Não Não Não Não Não Sim
1
Perfis adicionados no Amendment 2, publicado em abril de 2007.
110
1
Determina o número máximo desse parâmetro.
2
Taxa de bit máxima para os perfis Baseline, Extended e Main.
111
Este anexo fornece uma introdução às técnicas de compressão que são mais relevantes em
aplicações multimídia distribuídas. O objetivo não é fornecer uma visão exaustiva mas sim enfatizar
aspectos relacionados aos algoritmos de compressão mencionados no decorrer deste trabalho.
B1 Tipos de Compressão
B2 Categorias de Compressão
1
O termo "codificação" ("encoding") é o mais usual na terminologia de processamento de sinais
digitais. Contudo, o que os algoritmos realizam é, de fato, uma compressão e não simplesmente uma
codificação.
112
B3 Compressão de Imagem
Existe um grande número de algoritmos de compressão para vídeo. Nesta seção serão vistos
apenas dois deles cujo funcionamento é similar a vários outros. São eles os algoritmos JPEG e
MPEG-1.
O padrão JPEG é um padrão ISO originário do "Joint Photographic Expert Group" da ISO/IEC
JTC1/Subcomitê 2 [46]. Ele foi desenvolvido em colaboração com a ITU.
O JPEG é um padrão de compressão para imagens coloridas ou com níveis de cinza. Para a
compressão, ele usa uma combinação de DCT, quantização, supressão de sequências repetitivas e
codificação de Huffman, permitindo os seguintes modos de operação:
Para ilustrar como o algoritmo de compressão JPEG usa as técnicas de codificação vistas
nas seções anteriores, serão vistos os passos usados por ele para a codificação progressiva e
mostrados na Figura AnB1.
FIGURA ANB.1 - PASSOS PARA COMPRESSÃO DE IMAGENS USANDO O ALGORITMO JPEG COM O MODO DE OPERAÇÃO
SEQUENCIAL.
O primeiro passo é a preparação dos blocos, na qual a imagem é dividida em blocos de 8×8
pixels. Seja, por exemplo, uma imagem de 640×480 pixels representada por três componentes: a
116
O terceiro passo é a quantização. Nesse passo, são introduzidas perdas (até o passo da
transformação, o processo era totalmente reversível1). O passo da quantização consiste em
normalizar cada coeficiente DCT através de sua divisão por valores pré-definidos, armazenados em
uma tabela chamada tabela de quantização. Cada elemento da tabela pode ter um valor de 1 a 255.
A tabela determina quais coeficientes serão mantidos ou descartados e quais serão representados
com mais ou menos precisão (se todos os elementos da tabela têm valor 1, a quantização não terá
nenhum efeito). O incremento dos valores dos coeficientes aumenta a taxa de compressão e reduz a
fidelidade da imagem resultante. A sequência de coeficientes DC de cada bloco (g(0,0)) também é
codificada usando DPCM, o que significa calcular o termo de erro existente entre os coeficientes DC
de blocos adjacentes.
O último passo antes da transmissão ou armazenamento consiste na aplicação de algum
esquema de codificação de entropia. Nesse passo, o algoritmo JPEG aplica ou a codificação de
Huffman ou alguma técnica mais dinâmica. A ordem com que os coeficientes são pegos é em
ziguezague, visando maximizar a probabilidade de ocorrência de valores idênticos sucessivos.
O algoritmo JPEG foi concebido para compressão de imagens estáticas mas ele pode ser
usado também para compressão de vídeo, sendo referenciado como M-JPEG ("motion''-JPEG). Os
resultados são bons em termos da qualidade da imagem, mas a largura de banda requerida é alta
(entre 8 e 10 Mbps).
1
Na prática, é difícil encontrar codificadores que calculem DCT e IDCT (a transformação inversa) com
uma precisão que assegure que nenhuma diferença ocorrerá.
118
A idéia que norteia a exploração da correlação temporal é que em uma seqüência de quadros
uma boa parte da informação é comum a eles, ou seja, os quadros possuem áreas semelhantes ou
mesmo iguais que podem ser codificadas apenas uma vez. Assim, determinados quadros
comprimidos armazenam apenas diferenças em relação a outros quadros. Um quadro que contém
informações necessárias para a reconstrução de um ou mais quadros é chamado quadro de
referência.
Sejam três quadros de um vídeo, como na Fig. AnB.4 (a). Como pode ser visto, os quadros
possuem áreas comuns, isto é, áreas de igual conteúdo (Fig. AnB.4 (b). Tais áreas, contudo, estão
situadas em diferentes posições nos três quadros. Essa diferença de posição é representada através
de um vetor chamado vetor de movimento (Fig. AnB.4 (c)) e os blocos nos quais esse vetor será
aplicado são chamados blocos combinantes ("matching blocks"). O tamanho desses blocos depende
dos componentes da imagem. No MPEG-1, uma imagem é formada por três componentes ou planos:
um plano para luminância e dois planos que representam a diferença de cor que são sub-amostrados.
Assim, um bloco combinante é, na prática, um quadrado de 16×16 pixels no plano da luminância e
quadrados de 8×8 pixels para cada um dos planos que representam a diferença de cor. A
combinação desses três quadrados é chamada de macrobloco1.
1
O termo "macrobloco" não deve ser confundido com os blocos de 8×8 pixels usados no JPEG (e
também no MPEG-1) para eliminar redundâncias espaciais via DCT.
119
Supondo que o quadro 3 tenha macroblocos em comum com o quadro 1 e supondo, ainda,
que o quadro 3 é construído a partir do quadro 1 (e somente dele). Neste caso, o quadro 3 é um
quadro predito ("predicted frame") ou quadro P. Ele é construído a partir do quadro de referência 1
que passa a ser um quadro intracodificado ("intracoded frame") ou quadro I. Supondo ainda que o
quadro 2 tem macroblocos em comum com o quadro 1 e o quadro 3 (Fig. AnB.4 d)). Assim,
conceitualmente, o quadro 2 pode ser reconstruído usando pedaços dos quadros 1 e 3, desde que o
quadro 3 esteja disponível quando o quadro 2 é codificado. Isso implica que os três quadros têm que
ser temporariamente armazenados.
O quadro 2 é chamado de quadro bidirecional ou quadro B, sendo construído a partir da
interpolação do intraquadro 1 e do quadro predito 3.
120
Muitas vezes dois macroblocos não combinam totalmente. Neste caso, existe uma diferença
representada aritmeticamente (o erro do termo). As áreas de um quadro P ou B para os quais não há
nenhum bloco combinante são codificadas como os macroblocos dos quadros I.
Existem algumas sequências-padrão para quadros I, P e B: IBBBPBBBI, IBBPBBPBBI e
IBBPBBPBBPBBI. Quanto mais quadros B tem a sequência, maior será taxa de compressão obtida,
porém, às custas de uma diminuição a correlação temporal entre eles e entre os quadros de
referência, prejudicando, assim, a qualidade da imagem. Além disso, os quadros I servem como
pontos de sincronização, sendo estimado que o atraso máximo entre as ocorrências de dois quadros
desse tipo não deve exceder 300 ou 400 milissegundos. Em aplicações de reprodução de vídeo onde
são oferecidas operações VCR, o intervalo de ocorrência entre quadros de referência (I ou P) não
deve exceder 150 milissegundos.
Referências Bibliográficas
[1] RICHARDSON, Iain E. G. H.264 and MPEG-4 Video Compression: Video Coding for Next-
Generation Multimedia. West Sussex, Inglaterra: John Wiley & Sons, 2003.
[2] ITU-T & ISO/IEC JVT. H.264/AVC Reference Software Encoder version JM 14.0. Disponível em:
<http://iphome.hhi.de/suehring/tml/>. Acesso em: 3 Janeiro 2009.1
[5] TOURAPIS, Alexis M, et al. H.264/MPEG-4 AVC Reference Software Manual. Joint Video Team
(JVT) of ISO/IEC MPEG & ITU-T VCEG, Julho de 2007.
[6] WIEGAND, Thomas, et al. “Overview of the H.264/AVC Video Coding Standard”. IEEE
Transactions on circuits and systems for video technology, vol. 13, no. 7, p. 563-565, Julho de
2003.
[7] RICHARDSON, Iain E. G. Vcodex White Paper: An overview of H.264. Disponível em:
<http://www.vcodex.com>. Acesso em: 19 Dezembro 2007.
[8] Wikipedia. H.264/MPEG-4 AVC. Disponível em: <http://en.wikipedia.org>. Acesso em: 7 Abril
2009.
[9] ATI AVIVO. Introduction to H.264. Disponível em: <http://www.atiavivo.com>. Acesso em: 26
Agosto 2008.
[12] Texas Instruments Developer Conference India, 8, 2005, Bangalore. Overview of the
H.264/AVC. Bangalore: 2005, 75 p.
[13] TERAOKA, Kleber. Implementação do codificador de vídeo H.264 com transformada FLICT.
2003. 75 f.. Dissertação (Mestrado em Engenharia Elétrica) – Faculdade de Engenharia Elétrica e
Computação, Universidade Estadual de Campinas, Campinas, 2003.
1
Inicialmente foi utilizada a versão 12.4 com acesso em 15 Outubro 2007.
2
Inicialmente foi usado o documento de Março de 2003.
122
[16] SUN MICROSYSTEMS, INC. Java Media Framework API Guide. Califórnia, EUA, 1999.
[17] SUN MICROSYSTEMS, INC. Java Media Framework 2.1.1e API README and BINARY CODE
LICENSE. Califórnia, EUA, 2003.
[18] SUN MICROSYSTEMS, INC. JMF 2.1.1 API Documentation. Disponível em:
<http://java.sun.com/javase/technologies/desktop/media/jmf/reference/api/>. Acesso em: 5 Agosto
2008.
[19] SUN MICROSYSTEMS, INC. JMF 2.1.1 - Supported Formats. Disponível em:
<http://java.sun.com/javase/technologies/desktop/media/jmf/2.1.1/formats.html>. Acesso em: 5 Agosto
2008.
[23] DEITEL, H. M.; DEITEL, P. J. Java Como Programar. Tradução: Carlos Arthur Lang Lisbôa.
4.ed. Porto Alegre: Bookman, 2003.
[24] FREEMAN, Eric; FREEMAN, Elisabeth. Head First Design Patterns. Califórnia, EUA: O’Reilly,
2004.
[25] WILLRICH, Roberto. Compressão de dados multimídia. Florianópolis, 2008. Cap. 3, p. 31-51.
[26] JACK, Keith. Video Demystified: A handbook for the Digital Engineer. 4.ed. Oxford, Inglaterra:
Elsevier, 2005.
[27] JANUS, Scott. Video in the 21st Century. Oregon, EUA: Intel Press, 2002.
[28] WESTWATER, Raymond; FURHT, Borko. Real-Time Video Compression: techiniques and
algorithms. Massachusetts, EUA: Kluwer Academic Publishers, 1997.
123
[29] GAHANBARI, Mohammed. Standard Codecs: Image Compression to Advanced Video Coding.
Londres, Reino Unido: The Institution of Electrical Engineers, 2003.
[30] SALOMON, David. Data Compression: The Complete Reference. 3.ed. Nova Iorque, EUA:
Springer, 2004.
[32] WINKLER, Stefan. Digital Video Quality: Vision Models and Metrics. West Sussex, Inglaterra:
John Wiley & Sons, 2005.
[33] MALVAR, H. S., et al. “Low-Complexity Transform and Quantization in H.264/AVC”. IEEE
Transactions on circuits and systems for video technology, vol. 13, no. 7, p. 598-603, Julho de
2003.
[34] WIEN, Mathias. “Variable Block-Size Transforms for H.264/AVC”. IEEE Transactions on circuits
and systems for video technology, vol. 13, no. 7, p. 604-613, Julho de 2003.
[35] BRITANAK, Vladimir. Discrete Cosine and Sine Transforms. In: RAO, K. R. e YIP, P. C. The
transform and data compression handbook. Nova Iorque, EUA: CRC Press, 2001. p. 138-212.
[37] MARPE, Detlev; SCHWARZ, Heiko; WIEGAND, Tomas. “Context-Based Adaptive Binary
Arithmetic Coding in the H.264/AVC Video Compression Standard”. IEEE Transactions on circuits
and systems for video technology, vol. 13, no. 7, p. 620-636, Julho de 2003.
[38] WITTEN, Ian H.; RADFORD, M. N.; CLEARY, John G. “Arithmetic coding for data compression”.
Computing Practices, vol. 30, no. 6, p. 520-540, Junho de 1987.
[39] KATSAGGELOS, Aggelos G.; MELNIKOV, Gerry. Rate-Distortion Techniques in Image and Video
Coding. GUAN, Ling; KUNG, Sun-Yuan; LARSEN, Jan. Multimedia Image and Video Processing.
Nova Iorque, EUA: CRC Press, 2001. p. 336-364.
[40] WIEGAND, Thomas; SULLIVAN, Gary J. “Rate-Distortion Optimization for Video Compression”.
IEEE Signal Processing Magazine. p. 74-90, Novembro de 1998.
[42] WIEGAND, Thomas, et al. “Rate-Constrained Coder Control and Comparison of Video Coding
Standards”. IEEE Transactions on circuits and systems for video technology, vol. 13, no. 7, p.
688-703, Julho de 2003.
[43] WENGER, S., et al. “RTP Payload Format for H.264 Video”, RFC 3984, Fevereiro de 2005.
[44] YU, Keman; LV, Jiangbo; LI, Jiang; LI, Shipeng. IEEE International Conference on Multimedia &
Expo. Pratical real-time video CODEC for mobile devices. Baltimore, Maryland, EUA: Julho de
2003, vol. 3, p. 509-512.
[45] ASSOCIAÇÃO BRASILEIRA DE NORMAS TÉCNICAS. NBR 15606-1: Televisão digital terrestre
– codificação de dados e especificações de transmissão para radiodifusão digital. Parte 1:
Codificação de dados. Rio de Janeiro: ABNT , 2008.
[46] WALLACE, G. K. The JPEG Still-Picture Compression Standard. Communications of the ACM,
34(4): 30-44. 1991.
[47] GALL, D. L. MPEG: A Video Compression Standard for Multimedia Applications. Communication
of the ACM, 34(4), 1991.
[48] FISCHER, Walter. Digital Video and Audio Broadcasting Technology: A pratical engineering
guide. 2. ed. Berlin, Alemanha: Springer-Verlag, 2008.