Partilhar via


Gerando amostras de fluxo de um objeto de dados ASF existente

O objeto separador ASF é um componente de camada WMContainer que analisa o objeto de dados ASF de um ficheiro ASF (Formato de Sistemas Avançados).

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:

Localizando o deslocamento de dados

Antes de iniciar o processo de análise, o aplicativo deve localizar o objeto de dados dentro do 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 IMFASFContentInfo::GetHeaderSize método. Este método requer um buffer que contém os primeiros 30 bytes do cabeçalho ASF. Retorna o tamanho do cabeçalho inteiro que indica o offset para o primeiro pacote de dados. Esse valor também inclui o tamanho do cabeçalho Data Object de 50 bytes.

  • Depois de inicializar o objeto ContentInfo, você pode obter o descritor de apresentação chamando IMFASFContentInfo::GeneratePresentationDescriptore, em seguida, 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 de cabeçalho 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 parâmetro para ParseData, que dá início ao 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 carga útil — os dados reais de mídia digital. Em um cenário de busca, o aplicativo pode desejar que o separador comece a analisar em um pacote de dados específico. Para fazer isso, pode usar o indexador ASF para recuperar o deslocamento. O indexador retorna um valor de offset que começa na extremidade 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 se 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 um 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 reversa na chamada para ParseData, passe o deslocamento no parâmetro cbLength, que especifica o comprimento dos dados e defina cbBufferOffset como zero.

Gerando amostras 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 o todo ou fragmentos do objeto de dados. A saída do divisor é uma série de amostras de mídia que contêm dados do pacote.

Para passar dados de entrada para o divisor, crie um buffer de mídia e preencha-o com dados da seção Data Object do arquivo ASF. (Para obter mais informações sobre buffers de mídia, consulte Media Buffers.) Em seguida, passe o buffer de mídia para o método IMFASFSplitter::P arseData. Pode também especificar:

  • O deslocamento para o buffer onde o divisor deve começar a analisar. 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 analisar. Se esse valor for zero, o divisor analisará até atingir o final do buffer, conforme especificado pelo métodoIMFMediaBuffer::GetCurrentLength.

O divisor gera amostras de mídia fazendo referência aos dados nos buffers de mídia. O cliente pode recuperar as amostras 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, tal significa que há mais amostras para recuperar e a aplicação pode chamar GetNextSample novamente. Caso contrário, chame ParseData para passar mais dados para o divisor. Para as amostras geradas, o divisor define as seguintes informações:

  • O divisor define um carimbo de data/hora em todas as amostras que gera. O tempo de amostra representa o tempo de apresentação e não inclui o tempo de pré-rolagem. O aplicativo pode chamar IMFSample::GetSampleTime para obter o tempo de apresentação, em unidades de 100 nanossegundos.
  • Se ocorrer uma quebra durante a geração da amostra, o divisor define o atributo MFSampleExtension_Discontinuity na primeira amostra após a descontinuidade. As descontinuidades geralmente são causadas por pacotes descartados em uma ligação de rede, dados de ficheiros corrompidos ou o divisor de sinal alternando de um fluxo de origem para outro.
  • Para vídeo, o divisor verifica se a amostra tem uma frame-chave. Se isso acontecer, o divisor define o atributo MFSampleExtension_CleanPoint na amostra.

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 que é 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. Este método reinicia o divisor e limpa qualquer quadro parcial que esteja à espera de ser concluído. Isso é útil em um cenário de busca onde 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 Data Object 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;
}

Divisor ASF