Este artigo foi traduzido por máquina.

Codificação de vídeo

Salvando e reutilizando as configurações de codificação de vídeo

Adi Shavit

Baixar o exemplo de código

De sistemas de vigilância remota para plataformas robóticos incorporados, aplicativos que automaticamente ou autonomamente gravar vídeo estão ficando cada vez mais onipresentes, como o preço dos equipamentos, como câmeras e gotas de espaço de armazenamento e aumenta o desempenho computacional. No entanto, configurar remotamente muitas vezes tais sistemas não uma opção, enquanto implantação e instalação devem ser simples e automática.

Recentemente eu precisava de uma maneira pré-configurar as configurações de gravação de vídeo em um sistema remoto para que ele iria automaticamente carregar e usar as mesmas configurações em tempo de inicialização. Uma pesquisa na Web trouxe muitas questões semelhantes em vários fóruns que remontam mais de uma década, mas com soluções apenas parciais e insatisfatória.

Neste artigo, apresentarei uma forma simple mas geral para permitir que os aplicativos de processamento de vídeo para vídeo com configurações de compactação consistente, evitando assim ter que especificar manualmente as configurações de codec sempre que a máquina ou o aplicativo é iniciada. Mostrarei como acessar os buffers de configuração interna de um codec para que eles podem ser facilmente salvos, recarregados e reutilizados. Isto irá funcionar para qualquer codec de vídeo instalado na máquina, sem exigir a utilização de quaisquer APIs específicos do codec.

Vídeo 101

Dados brutos de vídeo são enormes. Considere uma câmera VGA modesta lentos junto a uma taxa humilde de 15 quadros por segundo. Cada canal de três 640 × 480 pixel frame é 900 KB e gera uma taxa de dados de mais de 13MBps (que é sobre 105Mbps)! Ligeiramente menos modesto equipamento de vídeo, do tipo que você pode encontrar em um telefone celular, pode ter resoluções de alta definição (HD), taxas de quadros mais altas ou mais de 8 bits de profundidade de cor. Um filme em HD 90 minutos irá consumir aproximadamente 1 TB em formato raw. Felizmente, os dados de vídeo contém uma grande quantidade de redundância e algoritmos de compressão de vídeo podem armazenar arquivos de vídeo bastante eficiente. O tipo de algoritmo de compactação escolhido depende do aplicativo em particular, seus requisitos e suas restrições. Vários fatores variam entre os métodos de compressão, incluindo taxas em tempo real, considerações sobre o desempenho e a compensação entre qualidade visual e o tamanho do arquivo resultante.

No Windows, serviços de compactação de vídeo são fornecidos por codecs (compactação-descompactação). Falarei sobre o vídeo para Windows (VfW) API para salvar arquivos de vídeo AVI compactado. Audio Video Interleave (. avi) é um formato de arquivo de contêiner multimídia que pode conter vários fluxos de áudio e vídeo e permitir a reprodução de áudio com vídeo síncrona de fluxos selecionados. Eu vou mostrar como configurações de codec de compressão podem ser manualmente selecionadas e armazenadas de forma persistente para que possam ser carregados voltar mais tarde e reutilizados automaticamente pela aplicação em qualquer computador no qual está instalado o mesmo codec.

Salvar vídeo com VfW

Embora ele foi introduzido em 1992, VfW é uma API duradoura, ainda amplamente usada hoje. Novas implementações de codecs e codec, tais como o ffmpeg suite (ffmpeg.org), continuar a fornecer uma interface VfW no Microsoft Windows.

O fluxo típico para um programa de gravação de vídeo geralmente assume a forma das seguintes etapas:

  1. Criar um novo arquivo AVI, criar um novo fluxo de vídeo descompactado dentro dele, escolha as configurações de compactação necessárias e criar um novo fluxo de comprimidos daquele descompactado.
  2. Adicione novos quadros conforme desejado.
  3. Quando feito, libere recursos em ordem inversa.

Usando VfW parece da seguinte forma:

  1. Abrir e preparar o arquivo de vídeo AVI usando o seguinte:
    1. AVIFileInit: Inicializa a biblioteca AVIFile. Deve ser chamado antes de usar quaisquer outras funções AVIFile.
    2. AVIFileOpen: Abre um ficheiro AVI.
    3. AVIFileCreateStream: Cria um novo fluxo de vídeo dentro do arquivo AVI. Um arquivo AVI pode incluir vários fluxos (de vários tipos).
    4. AVISaveOptions: Apresenta uma caixa de diálogo Opções de compactação padrão (consulte Figura 1). Quando o usuário é terminado selecionando as opções de compactação, as opções são retornadas em uma estrutura AVICOMPRESSOPTIONS.
    5. AVIMakeCompressedStream: Cria um fluxo compactado de descompactado fluxo retornado de AVIFileCreateStream e o filtro de compressão retornado de AVISaveOptions.
    6. AVIStreamSetFormat: Define o formato de imagem para o fluxo.
  2. Adicione quadros para o fluxo de vídeo:
    1. AVIStreamWrite: Adicione um quadro único para o fluxo de vídeo. Chamar repetidamente com quadros adicionais conforme desejado.
  3. Limpar e fechar:
    1. AVIStreamRelease: Fecha o fluxo compactado (de AVIMakeCompressedStream).
    2. AVIStreamRelease: Fecha o fluxo não compactado (de AVIFileCreateStream).
    3. AVISaveOptionsFree: Libera os recursos alocados pela função AVISaveOptions (isto é muitas vezes esquecido, levando a vazamentos de memória).
    4. AVIFileRelease: Fecha o arquivo AVI (de AVIFileOpen).
    5. AVIFileExit: Sair da biblioteca AVIFile.

The Video Compression Dialog Box Opened by the AVISaveOptions Function
Figura 1 caixa de diálogo de compactação de vídeo aberto pela função AVISaveOptions

Explicar detalhadamente como usar VfW para gravação de vídeo está além do escopo deste artigo. Você pode encontrar muitos exemplos e tutoriais on-line. Eu vou focar aqui apenas 1,4 etapa: a parte de configurações de seleção e codec do compressor. O código-fonte que acompanha este artigo baseia-se na implementação do projeto de código aberto OpenCV (opencv.willowgarage.com) do C++ e pode ser baixado do code.msdn.microsoft.com/mag201112Video. No entanto, as técnicas que eu mostrar aqui são aplicáveis a — e facilmente integrados — qualquer aplicação que utiliza a API VfW e não é especificamente orientada para uma implementação específica de gravação de vídeo ou linguagem de programação.

Soluções comuns

Como mostrado anteriormente, a forma comum para especificar as configurações de compactação é chamando a função AVISaveOptions para exibir a caixa de diálogo Opções de compactação. O usuário pode selecionar manualmente o codec desejado. O codec escolhido pode ou não ter um codec opção "Configure", que permite fazer personalizações de configurações específicas do codec. Naturalmente, em sistemas sem um usuário ativo ou até mesmo um GUI, essa caixa de diálogo não é uma opção.

A alternativa comum para seleção manual é programaticamente selecionar um codec específico usando o fourcc do codec (código de quatro caracteres. identificador de ver fourcc.org) e para definir algumas configurações adicionais, gerais comuns a todos os codecs, tais como taxa de dados, a qualidade e a taxa de quadros de chave. A principal desvantagem deste método é que ele não permite definir todas as configurações específicas do codec. Além disso, faz o codec usar quaisquer que sejam suas atuais configurações internas. Estes podem ser padrões ou, pior ainda, as configurações definidas arbitrariamente por outros programas. Além disso, nem todos os codecs de fourcc disponíveis em um sistema de suporte a VfW API, que pode causar falha na criação da fluxo compactado.

Alguns codecs oferecem ferramentas de configuração especial que permitem alterar suas configurações internas externamente. Essas ferramentas podem assumir a forma de uma GUI ou uma ferramenta de linha de comando. No entanto, a maioria dos codecs não fornecer tais ferramentas, e porque cada ferramenta é personalizada para um codec específico, você teria que aprender sua sintaxe particular e uso para tirar proveito dela.

O que eu queria era a flexibilidade do modo de seleção manual com seleção automática de codec e todas as suas configurações internas.

O melhor dos dois mundos

Na primeira abordagem, a chamada AVISaveOptions retorna um objeto AVICOMPRESSOPTIONS totalmente cheia-nos. A segunda abordagem é, essencialmente, encher alguns dos valores no objeto AVICOMPRESSOPTIONS, de dentro do código, sem o uso de AVISaveOptions.

Na verdade, é possível salvar o estado de codec completo, interno após uma chamada para AVISaveOptions para que este Estado pode ser restaurado mais tarde sem ter que chamar AVISaveOptions novamente. O segredo dos dados específicos de compressor de vídeo internos e o formato é opaco ponteiros lpParms e lpFormat de AVICOMPRESSOPTIONS. Quando voltou de AVISaveOptions, esses dois ponteiros contêm tudo o AVIMakeCompressedStream função precisa para fazer um fluxo compactado (estas são presumivelmente os recursos liberados por AVISaveOptionsFree).

A essência da minha técnica, em seguida, é salvar os dados opacos de lpParms e lpFormat para permitir que ele seja reutilizado. Felizmente, isso é simple de fazer. O AVICOMPRESSOPTIONS struct contém membros dois útil inteiro, cbParms e cbFormat, que indicar o tamanho dos buffers binários opacos apontada pelo lpParms e lpFormat, respeitar­vamente. Estes tamanhos mudam de acordo com o codec específico selecionado.

Para resumir, há três binárias "bolhas" que precisam ser salvo após uma chamada para AVISaveOptions. Estes são o próprio objeto AVICOMPRESSOPTIONS e os dois buffers binários apontada por sua lpParms e lpFormat Membros. O comprimento em bytes desses buffers é dado, respectivamente, pela cbParms e cbFormat. A restauração das configurações de codec é só reverter o processo: Leia os AVICOMPRESSOPTIONS do arquivo de objeto e definir suas lpParms e lpFormat Membros para apontar os respectivos buffers binários para leiam do arquivo. Os buffers binários restaurados são alocados e gerenciados pelo próprio aplicativo.

Há muitas maneiras de armazenar dados binários. Como mostrado na Figura 2, meu código de exemplo salva os dados no formato binário. Como alternativa, em casos onde os caracteres não imprimíveis devem ser evitados (como arquivos. txt ou. Xml), eu tenho usado com sucesso Base-64 codificação para armazenar buffers.

Figura 2 exemplo de guardar e restaurar as configurações de Codec

bool CvVideoWriter_VFW::writeCodecParams( char const* configFileName ) const
{
  using namespace std;   
  try
  { // Open config file
    ofstream cfgFile(configFileName, ios::out | ios::binary);          
    cfgFile.exceptions ( fstream::failbit | fstream::badbit );
    // Write AVICOMPRESSOPTIONS struct data
    cfgFile.write(reinterpret_cast<char const*>(&copts_), sizeof(copts_)); 
    if (copts_.cbParms != 0)// Write codec param buffer
      cfgFile.write(reinterpret_cast<char const*>(copts_.lpParms), copts_.cbParms);
    if (copts_.cbFormat != 0)// Write codec format buffer
      cfgFile.write(reinterpret_cast<char const*>(copts_.lpFormat),
        copts_.cbFormat);
  }
  catch (fstream::failure const&)
  { return false; } // Write failed
  return true;
}
bool CvVideoWriter_VFW::readCodecParams( char const* configFileName )
{ 
  using namespace std;
  try
  { // Open config file
    ifstream cfgFile(configFileName, ios::in | ios::binary);
    cfgFile.exceptions (
      fstream::failbit | fstream::badbit | fstream::eofbit );
    // Read AVICOMPRESSOPTIONS struct data
    cfgFile.read(reinterpret_cast<char*>(&copts_), sizeof(copts_));        
    if (copts_.cbParms != 0)
    { copts_Parms_.resize(copts_.cbParms,0);                // Allocate buffer
      cfgFile.read(&copts_Parms_[0], copts_Parms_.size());  // Read param buffer
      copts_.lpParms = &copts_Parms_[0];                    // Set lpParms to buffer
    }
    else
    { copts_Parms_.clear();
      copts_.lpParms = NULL;
    }
    if (copts_.cbFormat != 0)
    { copts_Format_.resize(copts_.cbFormat,0);              // Allocate buffer
      cfgFile.read(&copts_Format_[0], copts_Format_.size());// Read format buffer
      copts_.lpFormat = &copts_Format_[0];                  // Set lpFormat to buffer
    }
    else
    { copts_Format_.clear();
      copts_.lpFormat = NULL;
    }
  }
  catch (fstream::failure const&)
  { // A read failed, clean up
    ZeroMemory(&copts_,sizeof(AVICOMPRESSOPTIONS));
    copts_Parms_.clear();
    copts_Format_.clear();   
    return false; 
  }
  return true;
}

Notas de código de amostra

O código de exemplo captura de vídeo de uma câmera de vídeo ou webcam e o salva em um arquivo AVI. Ele usa a classe OpenCV VideoCapture para acessar a câmera e capturar o vídeo. Salvar o vídeo é feito usando uma versão da classe OpenCV CvVideoWriter_VFW que eu modifiquei para oferecer suporte a salvar as configurações de codec. Eu tentei manter as alterações ao código original tão pequeno quanto possível. No código, o segundo argumento de CvVideoWriter_VFW::open é um std::string. Se essa Cadeia de caracteres é quatro caracteres tempo (como em um fourcc), ele é considerado um fourcc e o codec correspondente a esta fourcc é escolhido — conversão de quatro caracteres 1 byte para o código inteiro fourcc é geralmente feito com a macro mmioFOURCC, como em mmioFOURCC('D','I','V','X'); consulte tinyurl.com/mmioFOURCC para obter detalhes. Caso contrário, a Cadeia de caracteres é tomada como o nome de um arquivo de configurações de codec. Se o arquivo existir, as configurações são lidos do arquivo e usadas para a compressão de vídeo. Se ele não existir, em seguida, abre a caixa de diálogo Opções de compactação e um codec e suas configurações podem ser selecionadas manualmente; as configurações escolhidas, em seguida, são salvas no arquivo de nome selecionado para ser reutilizado na próxima vez que o aplicativo é executado. Nota: Você precisará para compilar e executar o código de exemplo, a instalação de OpenCV.

Várias vantagens

A solução que apresentei tem várias vantagens. Alterar as configurações de compactação de um aplicativo é apenas uma questão de alterar um dos seus argumentos. Não há nenhuma necessidade de recompilá-lo ou mesmo parar o aplicativo em execução. O método é independente do codec e funciona para todos os codecs VfW. Alguns codecs permitem até mesmo imagem personalizada adicional de processamento, como quadro de redimensionamento e Desentrelaçamento. Todas estas opções são automaticamente capturadas dentro do arquivo de configurações de codec.

O método funciona assim como em sistemas legados do Microsoft Windows (a partir de Windows 2000 avante) e também com compiladores mais antigos, como o Visual Studio 6.0. Além disso, ele pode ser usado com uma variedade de idiomas que expõem a interface VfW, tais como Visual Basic e c#. Ele pode ser implantado em várias máquinas independentemente do atual estado de codec interno de cada máquina.

A principal limitação é que essa solução é garantida para trabalhar somente quando exatamente a mesma versão do codec é usado. Se um codec estiver ausente em um determinado computador, ou é uma versão diferente daquele usado para tornar o arquivo de configurações, os resultados podem ser indeterminados. Obviamente, não pode ser usado nonVfW codecs, porque esta solução se baseia o VfW API.

Extensões possíveis para este trabalho incluem similarmente salvando as configurações do compressor de áudio, que são manipuladas de maneira idêntica às configurações de vídeo compressor. Também, porque AVISaveOptions suporta nativamente AVIs com vários fluxos, a mesma solução funcionará também nestes casos, e as configurações de codec para cada um dos fluxos podem ser escritas para um único arquivo.

O outro comum e mais moderno vídeo e áudio processamento API no Windows é DirectShow. Deve ser possível implementar uma solução semelhante para o DirectShow também, mas isso é um assunto para um artigo futuro.

Adi Shavit é um consultor de visão de computador. Ele tem trabalhado em imagem e vídeo, sistemas de processamento de mais de 15 anos, especializada em análise de vídeo em tempo real. Ele pode ser contatado pelo adishavit@gmail.com.

Graças aos seguintes especialistas técnicos para revisão deste artigo: Dana Shavit e Matthew Wilson