Gerando exemplos de fluxo de um objeto de dados ASF existente
O objeto divisor ASF é um componente de camada WMContainer que analisa o Objeto de Dados ASF de um arquivo ASF (Advanced Systems Format).
Antes de passar pacotes de dados para o divisor, o aplicativo deve inicializar, configurar e selecionar fluxos no divisor para prepará-lo para o processo de análise. Para obter informações, consulte Criando o objeto divisor ASF e Configurando o objeto divisor ASF.
Os métodos necessários para analisar o Objeto de Dados ASF são:
- IMFASFSplitter::P arseData que inicia o processo de análise enviando por push o buffer que contém pacotes de dados para o divisor.
- IMFASFSplitter::GetNextSample que coleta amostras de fluxo que foram geradas do buffer passado para o divisor.
Localizando o deslocamento de dados
Antes de iniciar o processo de análise, o aplicativo deve localizar o Objeto de Dados no arquivo ASF. Há duas maneiras de obter o deslocamento do Objeto de Dados desde o início do arquivo:
Antes de inicializar o objeto ContentInfo, você pode chamar o método IMFASFContentInfo::GetHeaderSize . Esse método requer um buffer que contém os primeiros 30 bytes do cabeçalho ASF. Ele retorna o tamanho de todo o cabeçalho que indica o deslocamento para o primeiro pacote de dados. Esse valor também inclui o tamanho do cabeçalho objeto de dados de 50 bytes.
Depois de inicializar o objeto ContentInfo, você poderá obter o descritor de apresentação chamando IMFASFContentInfo::GeneratePresentationDescriptor e consultando o descritor de apresentação para o atributo MF_PD_ASF_DATA_START_OFFSET . O valor desse atributo é o tamanho do cabeçalho.
Observação
O atributo MF_PD_ASF_DATA_LENGTH no descritor de apresentação especifica o comprimento do Objeto de Dados ASF.
Em ambos os casos, o valor retornado é o tamanho do Objeto Header mais o tamanho da seção de cabeçalho do Objeto de Dados. Portanto, o valor resultante é o deslocamento para o início dos pacotes de dados no Objeto de Dados ASF. Quando você começa a enviar dados para o divisor, os dados devem começar nesse deslocamento desde o início do arquivo ASF.
O valor de deslocamento é passado como um parâmetro para ParseData que inicia o processo de análise.
O Objeto de Dados é dividido em pacotes de dados. Cada pacote de dados contém um cabeçalho de pacote de dados que fornece informações de análise de pacotes e os dados de conteúdo — os dados reais da mídia digital. Em um cenário de busca, o aplicativo pode querer que o divisor inicie a análise em um pacote de dados específico. Para fazer isso, você pode usar o Indexador ASF usado para recuperar o deslocamento. O indexador retorna um valor de deslocamento que começa no limite do pacote. Se você não estiver usando o indexador, verifique se o deslocamento começa no início do cabeçalho do pacote de dados. Se um deslocamento inválido for passado para o divisor, como o valor não apontar para o limite do pacote, as chamadas ParseHeader e GetNextSample serão bem-sucedidas, mas GetNextSample não recuperará nenhum exemplo e NULL será recebido no parâmetro pSample .
Se o divisor estiver configurado para analisar na direção inversa, o divisor sempre iniciará a análise no final do buffer de mídia que é passado para ParseData. Portanto, para análise inversa na chamada para ParseData, passe o deslocamento no parâmetro cbLength , que especifica o comprimento dos dados e define cbBufferOffset como zero.
Gerando exemplos para pacotes de dados ASF
Um aplicativo inicia o processo de análise passando os pacotes de dados para o divisor. A entrada para o divisor é uma série de buffers de mídia que contêm todos os fragmentos ou do Objeto de Dados. A saída do divisor é uma série de exemplos de mídia que contêm os dados do pacote.
Para passar dados de entrada para o divisor, crie um buffer de mídia e preencha-os com dados da seção Objeto de Dados do arquivo ASF. (Para obter mais informações sobre buffers de mídia, consulte Buffers de mídia.) Em seguida, passe o buffer de mídia para o método IMFASFSplitter::P arseData . Você também pode especificar:
- O deslocamento para o buffer em que o divisor deve iniciar a análise. Se o deslocamento for zero, a análise começará no início do buffer. Para obter informações sobre como definir o deslocamento de dados, consulte a seção "Localizando o deslocamento de dados" neste tópico.
- A quantidade de dados a serem analisados. Se esse valor for zero, o divisor analisará até chegar ao final do buffer, conforme especificado pelo método IMFMediaBuffer::GetCurrentLength .
O divisor gera exemplos de mídia fazendo referência aos dados nos buffers de mídia. O cliente pode recuperar os exemplos de saída chamando IMFASFSplitter::GetNextSample em um loop até que não haja mais dados para analisar. Se GetNextSample retornar o sinalizador ASF_STATUSFLAGS_INCOMPLETE no parâmetro pdwStatusFlags , isso significa que há mais exemplos a serem recuperados e o aplicativo poderá chamar GetNextSample novamente. Caso contrário, chame ParseData para passar mais dados para o divisor. Para os exemplos gerados, o divisor define as seguintes informações:
- O divisor define um carimbo de data/hora em todos os exemplos gerados. O tempo de exemplo representa o tempo de apresentação e não inclui o tempo de pré-registro. O aplicativo pode chamar IMFSample::GetSampleTime para obter o tempo de apresentação, em unidades de 100 nanossegundos.
- Se ocorrer uma interrupção durante a geração de exemplo, o divisor definirá o atributo MFSampleExtension_Discontinuity no primeiro exemplo após a descontinuidade. As descontinuações geralmente são causadas por pacotes descartados em uma conexão de rede, dados de arquivo corrompidos ou o divisor alternando de um fluxo de origem para outro.
- Para vídeo, o divisor verifica se o exemplo contém um quadro-chave. Se isso acontecer, o divisor definirá o atributo MFSampleExtension_CleanPoint no exemplo.
Se o divisor estiver analisando pacotes de dados recebidos de um servidor de mídia, é possível que o comprimento do pacote seja variável. Nesse caso, o cliente deve chamar ParseData para cada pacote e definir o atributo MFASFSPLITTER_PACKET_BOUNDARY em cada buffer enviado para o divisor. Esse atributo indica ao divisor se o buffer de mídia contém o início de um pacote ASF. Defina o atributo como TRUE se o buffer contiver o início de um novo pacote. Se o buffer contiver uma continuação do pacote anterior, defina o atributo como FALSE. Os buffers não podem abranger vários pacotes.
Antes de passar novos buffers de mídia para o divisor, o aplicativo deve chamar IMFASFSplitter::Flush. Esse método redefine o divisor e limpa qualquer quadro parcial que esteja aguardando para ser concluído. Isso é útil em um cenário de busca em que o deslocamento está em um local diferente.
Exemplo
O exemplo de código a seguir mostra como analisar pacotes de dados. Este exemplo analisa desde o início do Objeto de Dados até o final do fluxo e exibe informações sobre os exemplos que contêm quadros-chave. Para obter um exemplo completo que usa esse código, consulte Tutorial: Lendo um arquivo ASF.
// Parse the video stream and display information about the video samples.
//
// The current read position of the byte stream must be at the start of the ASF
// Data Object.
HRESULT DisplayKeyFrames(IMFByteStream *pStream, IMFASFSplitter *pSplitter)
{
const DWORD cbReadSize = 2048; // Read size (arbitrary value)
IMFMediaBuffer *pBuffer = NULL;
IMFSample *pSample = NULL;
HRESULT hr = S_OK;
while (SUCCEEDED(hr))
{
// The parser must get a newly allocated buffer each time.
hr = MFCreateMemoryBuffer(cbReadSize, &pBuffer);
if (FAILED(hr))
{
break;
}
// Read data into the buffer.
hr = ReadFromByteStream(pStream, pBuffer, cbReadSize);
if (FAILED(hr))
{
break;
}
// Get the amound of data that was read.
DWORD cbData;
hr = pBuffer->GetCurrentLength(&cbData);
if (FAILED(hr))
{
break;
}
if (cbData == 0)
{
break; // End of file.
}
// Send the data to the ASF splitter.
hr = pSplitter->ParseData(pBuffer, 0, 0);
SafeRelease(&pBuffer);
if (FAILED(hr))
{
break;
}
// Pull samples from the splitter.
DWORD parsingStatus = 0;
do
{
WORD streamID;
hr = pSplitter->GetNextSample(&parsingStatus, &streamID, &pSample);
if (FAILED(hr))
{
break;
}
if (pSample == NULL)
{
// No samples yet. Parse more data.
break;
}
if (IsRandomAccessPoint(pSample))
{
DisplayKeyFrame(pSample);
}
SafeRelease(&pSample);
} while (parsingStatus & ASF_STATUSFLAGS_INCOMPLETE);
}
SafeRelease(&pSample);
SafeRelease(&pBuffer);
return hr;
}
Tópicos relacionados