De fato, o artigo é dedicado ao desenvolvimento de um programa para reembalar vídeo DVR de um contêiner para outro, se puder ser chamado de conversão. Embora, durante toda a minha vida, pensei que o conversor estivesse envolvido na conversão (transcodificação) do formato de vídeo. Este artigo é a segunda parte da minha última publicação, onde falei detalhadamente sobre como acessar todas as gravações de vídeo do DVR. Mas, no início da publicação, eu defino outra tarefa: estudar o algoritmo pelo qual o programa padrão do repacker 264-avi funciona e criar o mesmo programa que executaria as mesmas operações, mas não em um, mas em um grupo inteiro de arquivos, e "Um clique".
Vou explicar mais uma vez a essência de todas as coisas em linguagem simples.
O usuário possui um gravador de vídeo, por exemplo, o popular modelo QCM-08DL. Ele precisa de um vídeo para uma data e hora específicas. Ele pode removê-lo em uma unidade flash USB ou através de uma interface baseada na Web de um DVR para um computador. O arquivo de vídeo extraído (extensão .264) será aberto apenas no programa player que acompanha o DVR. O jogador está muito desconfortável. Você ainda pode abri-lo no VLC player configurando o modo RAW H264 nas configurações de desmultiplexação (configurações para usuários avançados). Mas, ao mesmo tempo, aparentemente, blocos de fluxos de áudio, que são interpretados como vídeo, e não há som, interferem na reprodução normal. E, para abrir o vídeo em qualquer player, o arquivo .264 deve primeiro ser convertido para algum formato popular, por exemplo, avi. Um programa de conversão também está incluído no DVR. Mas ela também é muito desconfortável. Quando se trata de um ou mais arquivos, não há problemas. No entanto, quando a tarefa é obter acesso a todos os vídeos no disco rígido e, ainda mais, convertê-los para o formato popular, as ferramentas padrão praticamente não são adequadas.

O problema de acesso a todos os arquivos foi resolvido. Este foi o assunto da última publicação. Continuamos a resolver o segundo problema. Já recebi "conselhos práticos": basta renomear a extensão de "264" para "avi" no nome do arquivo e tudo vai dar errado, dizem eles, não há nada para incomodar. Mas esse é o erro mais comum de qualquer usuário comum que, em regra, não entende os problemas relevantes.
Em uma publicação anterior, eu já escrevi brevemente sobre a estrutura do arquivo de origem .264. Deixe-me lembrá-lo.
As informações principais com fluxos de áudio e vídeo são originadas em um deslocamento de 65.536 bytes. Os blocos de fluxo de vídeo começam com um cabeçalho de 8 bytes "01dcH264" (também encontrado "00dcH264"). Os 4 bytes a seguir descrevem o tamanho do bloco atual do fluxo de vídeo em bytes. Após 4 bytes de zeros (00 00 00 00), o próprio bloco de fluxo de vídeo é iniciado. Os blocos de fluxo de áudio têm o título "03wb" (embora, de acordo com minhas observações, o primeiro caractere do cabeçalho em alguns casos não tenha sido necessariamente "0"). Depois - 12 bytes de informações que ainda não descobri. E começando com o 17º byte - um fluxo de áudio com um comprimento fixo de 160 bytes. Não há tags no final do arquivo.Vou comentar o acima. Tudo o que tem um deslocamento de 65.536 bytes acabou não sendo resolvido e desnecessário. De um deslocamento de 65.536 bytes para o primeiro cabeçalho do fluxo, há uma pequena lacuna, cujo conteúdo também não é resolvido e, além disso, como verifiquei, ele não aparece no arquivo avi de saída após a conversão por um programa regular.
Cada bloco do fluxo de vídeo representa um quadro. O primeiro caractere no cabeçalho dos blocos do fluxo de vídeo é opcionalmente "0". Não descobri o objetivo dele, porque, como descobri, não é a chave para resolver a tarefa. O segundo caractere do cabeçalho do fluxo de vídeo pode ser "1" ou "0". No segundo caso, o conteúdo do bloco de fluxo de vídeo é o chamado quadro de referência. E, no primeiro caso, o conteúdo do bloco de fluxo de vídeo é um quadro compactado codificado, que depende do quadro de referência. O tamanho do conteúdo do quadro de referência é significativamente maior que o tamanho do conteúdo do sub-quadro compactado. O período de repetição do quadro de referência provavelmente depende das configurações da taxa de compressão no DVR. Mas no meu caso, o período de repetição foi de 1 quadro / s.

O programa regular para reembalar o vídeo do contêiner “264” para o contêiner “avi” deu resultados diferentes em relação à taxa de quadros. No caso de vídeos gravados no modo de alta resolução (704 * 576), a taxa de quadros foi de 20 quadros / s. E no caso de baixa resolução (352 * 288) - 25 quadros / seg. Essas informações são fornecidas pelo utilitário MediaInfo e também indicam que o tamanho do vídeo é o mesmo em qualquer caso: 720 * 576 e o tamanho do fluxo de vídeo (este utilitário relata) é 704 * 576 ou 352 * 288. A maioria dos players é implantada especificamente para o tamanho do fluxo de vídeo. No entanto, conheci um player que exibia incorretamente o modo de meia tela ao reproduzir um arquivo 352 * 288. Queria corrigir essa falha menor do reembalador em tempo integral, observando os bytes do conteúdo do fluxo de vídeo e retirando informações sobre o tamanho do quadro a partir daí. Mas com pressa, eu não poderia fazer isso. O acima é mostrado na figura abaixo.

Agora sobre a taxa de quadros. Como descobri, o reembalador regular não acessa nenhum campo de cabeçalho do contêiner “264”. Ele avalia a taxa de quadros calculando a proporção do número de blocos de vídeo e fluxos de áudio. E esse valor no cálculo nem é arredondado para o valor inteiro mais próximo, como pode ser visto na figura acima (circulada em verde). Como descobri, o número de blocos de fluxo de áudio por unidade de tempo é sempre e em qualquer lugar (em qualquer arquivo) fixo, ou seja, 25 blocos por segundo. Se você examinar o arquivo de vídeo com uma frequência de 20 quadros / s., O quadro de referência (bloco) ocorrerá a cada 19 quadros compactados e, no caso de 25 quadros / s. - a cada 24 quadros compactados.
Continuamos estudando a estrutura do cabeçalho do fluxo de vídeo. Descobrimos os oito primeiros bytes: este é o rótulo da referência ou do quadro compactado mais a palavra-chave “H264”. Os quatro bytes a seguir descrevem, como descobri, não o exato, mas o tamanho aproximado do conteúdo do fluxo de vídeo. Um reembalador regular lança todos os bytes desse conteúdo completamente e depois grava o tamanho resultante nos campos correspondentes do contêiner avi. E esse valor é diferente do valor especificado no campo correspondente do arquivo .264 de origem.
Em parte, adivinhei doze bytes de informações após o cabeçalho do bloco de fluxo de áudio. De qualquer forma, os principais elementos são os últimos 4 bytes, após o qual o fluxo de áudio é iniciado. Esses são dois números de 16 bits que descrevem os parâmetros iniciais do esquema de decodificação iterativa do ADPCM para o PCM. A decodificação aumenta o tamanho do fluxo de áudio em 4 vezes. Quando examinei os arquivos com antecedência, descobri que o reembalador em tempo integral decodifica o áudio, mas mantém o conteúdo do vídeo inalterado.
Sem conhecimento profundo, tentei por um longo tempo descobrir qual algoritmo de decodificação era usado no meu caso. Intuitivamente já adivinhou que o método de compactação ADPCM foi aplicado. Mais precisamente, não intuitivamente, mas com uma abordagem competente, com base no fato de que o fluxo de áudio é compactado exatamente 4 vezes. E quando abro um fragmento no Adobe Audition como RAW em vários formatos (e comparando o mesmo fragmento depois de reembalar com um programa regular), um resultado sonoro muito semelhante (mas não preciso) foi dado a mim pelo ADPCM. Para analisar o algoritmo de compactação, as informações no site
wiki.multimedia.cx/index.php/IMA_ADPCM me ajudaram. Aqui eu aprendi sobre os dois parâmetros iniciais de decodificação e, usando o "método de puxão", percebi que esses parâmetros iniciais eram gravados em 4 bytes antes do início do fluxo de áudio. Descreverei a operação do algoritmo e darei uma interpretação matemática aproximada (sob o spoiler).
Detalhes do algoritmo de decodificação ADPCMHá uma sequência de amostras
x0,x1,x2, pontos. Além disso, como já mencionado, existem dois parâmetros iniciais
y0 e
s0 . Precisa obter uma nova sequência de amostra
y0,y1,y2 pontos. . Como você já pode imaginar, a primeira amostra de saída já é conhecida: ela coincide com um dos parâmetros iniciais
y0 . Isso é algo diferente de um "deslocamento inicial". Vale ressaltar que as amostras de entrada (fonte) são codificadas em quatro bits. Para tipos assinados, números inteiros de -8 a 7, inclusive, se enquadram na codificação. O bit mais significativo, de fato, é responsável pelo sinal do número. As amostras PCM de saída, obtidas após decodificação, têm um formato padrão assinado de 16 bits.
Analisando o código do algoritmo em C, você pode ver duas tabelas. Eles estão listados abaixo.
int ima_index_table[] = { -1, -1, -1, -1, 2, 4, 6, 8 };
int ima_step_table[] = { 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, 130, 143, 157, 173, 190, 209, 230, 253, 279, 307, 337, 371, 408, 449, 494, 544, 598, 658, 724, 796, 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767 };
Pode-se dizer que essas duas matrizes "mágicas" são funções tabulares, nos argumentos em que os mesmos dois parâmetros iniciais são substituídos. Durante a iteração, a cada etapa, os parâmetros são recalculados e substituídos nessas tabelas novamente. Primeiro, vamos ver como isso é implementado no código.
Declaramos o necessário, incluindo variáveis auxiliares.
int current1; int step; int stepindex1; int diff; int current; int stepindex; int value;
Antes de iniciar a iteração, você precisa atribuir o parâmetro inicial à variável atual
y0 e a variável stepindex é
s0 . Isso é feito fora do algoritmo em questão, então não reflito isso com o código. A seguir, são apresentadas as transformações executadas em um círculo (em um loop).
value = read(input_sample);
Na etapa da variável auxiliar da matriz ima_step_table, o valor no índice stepindex1 é gravado. Para a primeira iteração, este é o parâmetro inicial
s0 , para iterações adicionais, este é um parâmetro recalculado
si . Em seguida, o valor desse array é dividido por 8 (aparentemente, completamente) por uma operação de deslocamento de bits para a direita, e a variável diff é inicializada como resultado dessa divisão. Em seguida, os três bits menos significativos do valor da amostra de entrada são analisados e, dependendo de seus estados, a variável diff pode ser ajustada em três termos. Os termos são uma divisão inteira semelhante do valor diff por 4 (>> 2), 2 (>> 1) ou diff sem alterações (seja uma divisão por 1 para generalização). Em seguida, o bit mais significativo (assinado) do valor da amostra de entrada é analisado. Dependendo do seu estado, a variável diff que foi gerada antes disso é adicionada ou subtraída à variável current1. Este será o valor da amostra de saída. Para correção, os valores são limitados à parte superior e inferior. Então, stepindex1 é ajustado adicionando o valor da matriz ima_index_table pelo índice do valor da amostra de entrada com o bit de sinal redefinido para zero. Os valores do índice de etapa 1 também estão sujeitos a um limite. No final, antes de repetir esse algoritmo, os valores atual e stepindex recebem os valores apenas recontados de current1 e stepindex1, e o algoritmo é repetido novamente.
Você pode tentar descobrir isso para entender aproximadamente como a variável diff é formada. Vamos
fi=f(si) . Estes são os valores da variável step em cada i-ésima etapa da iteração, como os valores da função (matriz) do argumento
si onde
i=0,1,2, pontos . Por conveniência, denotamos a variável diff como
d . Seguindo a lógica do raciocínio descrito acima, temos:
di= fracfi8+x(0)i fracfi4+x(1)i fracfi2+x(2)ifi,
onde
x(0)i,x(1)i,x(2)i - baixo número de 3 bits
xi . Levando a um denominador comum, transformamos essa expressão em uma forma mais conveniente:
di= fracfi8 Bigg(1+2x(0)i+4x(1)i+8x(2)i Bigg)=
= fracfi8 Bigg(1+2 Big(x(0)i+2x(1)i+4x(2)i Big) Bigg)= fracfi8(2xi+1).
A última conversão é baseada no fato de que, em certo sentido, pelo menos três bits (0 ou 1) de um número
xi com os coeficientes apresentados, existe algo além de escrever o valor absoluto desse número e o bit mais significativo
xi irá corresponder ao sinal de toda a expressão. Ainda de acordo com a fórmula
yi+1=yi+di
um novo valor de amostra é calculado com base no antigo. Além disso, um novo valor variável é calculado.
s :
si+1=si+t(|xi|).
O módulo na fórmula indica que a variável
xi entra em função
t excluindo o bit de sinal mais significativo, que é refletido no código. Uma função
t É o valor da matriz ima_index_table com o índice correspondente ao argumento.
Na descrição das fórmulas, negligenciei as operações de restrição acima e abaixo. O esquema iterativo total é mais ou menos assim:
y0; s0; x0,x1,x2, pontos
di= fracf(si)8 grande(2xi+1 grande)
yi+1=yi+di
si+1=si+t(|xi|)
i=0,1,2, pontos.
Muito profundamente na teoria da codificação / decodificação do ADPCM, que eu não mergulhei. No entanto, os valores da tabela da matriz ima_step_table (de 89 peças), a julgar pela reflexão no gráfico (veja a figura abaixo), descrevem a distribuição probabilística de amostras em relação à linha zero. Na prática, esse geralmente é o caso: quanto mais próxima a amostra está da linha zero, mais ela ocorre. Portanto, o ADPCM é baseado em um modelo probabilístico e, de maneira alguma, qualquer conjunto de fontes de amostras PCM de 16 bits pode ser convertido corretamente em amostras ADPCM de 4 bits. De um modo geral, o ADPCM é um PCM com uma etapa de quantização variável. Aparentemente, este gráfico reflete essa etapa muito variável. Ele é escolhido corretamente, com base na lei de distribuição de dados de áudio na prática.

Agora vamos descrever a estrutura do contêiner avi. De fato, é uma estrutura hierárquica complexa.
Mas, tendo simplificado a tarefa para um caso especial, apresentei a estrutura avi de forma linear. O resultado é o seguinte: o arquivo avi consiste em um cabeçalho grande, zero pular bytes (JUNK), uma área de fluxos de áudio e vídeo (com seus cabeçalhos e tamanhos de conteúdo) e uma lista de índices. O último serve, em particular, para rolar pelo vídeo no player. Sem essa lista, a rolagem não funcionará (marcada). É apenas um índice, que lista os nomes das chaves dos blocos de fluxo (correspondendo aos nomes nos cabeçalhos dos blocos), os tamanhos correspondentes do conteúdo e os valores dos deslocamentos (endereços) relativos ao início da área de fluxo.
Agora você pode avançar para o desenvolvimento do programa. Uma descrição específica do problema é a seguinte.
Na raiz da seção X: existe um diretório "DVR". Este diretório contém muitos subdiretórios não vazios (e apenas subdiretórios) com nomes que correspondem a determinadas datas. Em cada um desses subdiretórios, existem muitos arquivos com nomes diferentes e a extensão "264". Necessário na seção Y: crie o diretório "DVR" e nele os mesmos subdiretórios da seção X:. Preencha cada um desses subdiretórios com arquivos com os mesmos nomes correspondentes, mas com extensões não "264", mas "avi". Esses arquivos avi devem ser obtidos dos arquivos 264 originais, processando-os, o que, de uma maneira ou de outra, repete o algoritmo de um programa existente. O processamento consiste em reembalar diretamente os fluxos de vídeo, reembalar com decodificar os fluxos de áudio e formatar o arquivo avi. O programa deve ser iniciado a partir da linha de comando da seguinte maneira: “264toavi.exe X: Y:”, onde “264toavi.exe” é o nome do programa, “X:” é a seção de origem, “Y:” é a seção de destino.De fato, para simplificar a tarefa, foi possível escrever um programa que lidaria apenas com a conversão (reembalagem) de um arquivo, tornando-o em dois dias: o nome do arquivo de entrada e o nome do arquivo de saída. E, para implementar apenas a reembalagem do grupo, você pode gravar um arquivo em lote de comando (bat) usando outras ferramentas, por exemplo, o Excel. Mas eu implementei um programa completo, muito complicado. É improvável que o código fonte mereça a atenção dos leitores. Vou descrever a estrutura do código do programa.
O programa é escrito em C no ambiente de desenvolvimento Dev-C ++ com elementos WinAPI. O programa implementa três grandes funções auxiliares: a função de gerar o cabeçalho avi inicial, a função de decodificar uma amostra de áudio e a função de escanear o arquivo de origem “264” por palavras. Em palavras, eu chamo uma parte de 4 bytes. Foi observado que o tamanho dos cabeçalhos e o conteúdo de todos os fluxos são múltiplos de quatro bytes. A função de varredura pode retornar cinco valores: 0 - se forem os 4 bytes comuns do fluxo de vídeo para reembalagem, 1 - se for o cabeçalho do bloco do fluxo de vídeo do quadro de referência, 2 - se for o título do bloco do fluxo de vídeo do quadro compactado, 3 - se for o cabeçalho do bloco de fluxo de áudio, 4 - se Bloco "Corrompido" a ser ignorado durante a reembalagem. Muito, muito raro, mas aconteceu. O bloco danificado (como eu o chamei) é um cabeçalho como "\ 0 \ 0 \ 0 \ 0H264", em que "\ 0" é o byte zero. O reembalador regular ignora blocos desse tipo. Obviamente, o conteúdo de um bloco desse tipo pode estar funcionando bastante, mas eu os ignoro para aproximar meu programa do programa padrão.
Na função principal, além de organizar os diretórios, o arquivo de entrada é lido pela função de digitalização. Dependendo do que essa função retornou, outras ações ocorrem. Se esses são os cabeçalhos dos fluxos de vídeo, os cabeçalhos correspondentes são formados no arquivo avi de saída. Lá eles são chamados de forma diferente: “00db” é o título do bloco do fluxo de vídeo do quadro de referência e “00dc” é para o quadro compactado. Após a operação de reembalagem (reescrita de palavras) antes do novo cabeçalho encontrado recentemente, o tamanho do conteúdo reembalado é calculado e esse valor é gravado no campo que segue imediatamente o cabeçalho do fluxo que acabou de ser processado. Se um cabeçalho de fluxo de áudio for encontrado durante a varredura, o nome do cabeçalho “03wb” é gerado no arquivo avi de saída e o fluxo de áudio é decodificado do ADPCM para o PCM no loop ao mesmo tempo em que o conteúdo decodificado é gravado no arquivo avi. Juntamente com todas as opções acima, uma breve informação (sumário) é registrada em um arquivo de índice temporário “index”. Você não pode executar uma função de digitalização, mas escreva tudo na função principal. Mas o programa seria muito complicado e quase difícil de ler.
No final de toda a operação, quando o arquivo de entrada "264" terminar, antes de passar para um novo arquivo, o programa concluirá todas as operações com competência. Primeiro, determinados campos no cabeçalho do arquivo avi são ajustados, cujos valores dependem do tamanho e número de fluxos lidos e, em seguida, o conteúdo do arquivo temporário de "índice" é anexado ao arquivo avi quase finalizado, que é excluído. Após essas operações, o arquivo avi de saída está pronto para reprodução.
Enquanto o programa está sendo executado, uma visualização de texto ocorre na linha de comando, que exibe o diretório atual, o arquivo e o número do bloco do fluxo de vídeo por quadro de referência e o ponto de tempo correspondente do vídeo em minutos e segundos. E se o arquivo de entrada não tiver um nome arbitrário, mas o original (contendo o número do canal, data e hora em que a gravação começou), ocorrerá uma visualização mais interativa com base na aritmética da data e hora.
Ao testar e depurar o programa, os principais problemas que tive ao trabalhar com decodificação de som. A aritmética simples não funcionou corretamente se, ao declarar variáveis na função de decodificação, eu não digitasse corretamente os tipos. Por esse motivo, alguns blocos de fluxos de áudio foram quebrados e houve cliques de ouvido. Alguns campos de cabeçalho incompreensíveis do arquivo 264 original que eu não conseguia descobrir eram insensíveis ao resultado. Diferentemente de um programa regular, meu programa não lança o último bloco de fluxo incompleto da operação de reembalagem. Embora sua ausência não tenha nenhum papel prático. Outro programa regular, diferente do meu, deixa para trás uma pequena quantidade de "lixo" (este é o conteúdo do último fluxo) no final do arquivo avi após os índices. Por tudo isso, o vídeo é reproduzido quase da mesma forma. E o programa executa reembalagem pelo mesmo período de tempo que o programa regular.
Concluindo, darei ilustrações demonstrando a estrutura da organização dos fluxos em um arquivo .264 (no editor hexadecimal do WinHex) usando um dos arquivos como exemplo e a aparência do programa RiffPad com o arquivo avi reembalado aberto. Este programa simplificou bastante o processo de estudo da estrutura de um arquivo avi. Ele demonstra claramente a estrutura hierárquica, mostra o conteúdo de bytes de cada membro da estrutura e interpreta de maneira inteligente o conteúdo dos cabeçalhos na forma de uma lista de parâmetros. A imagem, em particular, demonstra o fato de que o conteúdo do fluxo de vídeo é substituído sem alterações.

