Поделиться через


Создание примеров потоков из существующего объекта данных ASF

Объект разделения ASF — это компонент слоя WMContainer, который анализирует объект данных AS F файла расширенного формата систем (ASF).

Перед передачей пакетов данных в разделитель приложение должно инициализировать, настроить и выбрать потоки в разделитете, чтобы подготовить его к процессу синтаксического анализа. Дополнительные сведения см. в разделе Создании объекта разделения ASF и настройке объекта разделения ASF.

Ниже приведены методы, необходимые для синтаксического анализа объекта данных ASF:

  • IMFASFSplitter::ParseData, который запускает процесс синтаксического анализа, отправляя буфер с пакетами данных на разделитель.
  • IMFASFSplitter::GetNextSample, который собирает образцы потоков, созданные из буфера, переданного в разделитель.

Поиск смещения данных

Перед началом процесса синтаксического анализа приложение должно найти объект данных в файле ASF. С начала файла можно получить смещение объекта данных двумя способами:

  • Перед инициализацией объекта ContentInfo можно вызвать метод IMFASFContentInfo::GetHeaderSize. Для этого метода требуется буфер, содержащий первые 30 байт заголовка ASF. Он возвращает размер всего заголовка, который указывает на смещение к первому пакету данных. Это значение также включает размер заголовка объекта данных размером 50 байт.

  • После инициализации объекта ContentInfo можно получить дескриптор презентации, вызвав МВФASFContentInfo::GeneratePresentationDescriptor, а затем запросив дескриптор презентации для атрибута MF_PD_ASF_DATA_START_OFFSET. Значение этого атрибута — это размер заголовка.

    Заметка

    Атрибут MF_PD_ASF_DATA_LENGTH в дескрипторе презентации указывает длину объекта данных ASF.

     

В обоих случаях возвращаемое значение — это размер объекта заголовка плюс размер раздела заголовка объекта данных. Таким образом, результирующее значение является смещением к началу пакетов данных в объекте данных ASF. Когда вы начинаете отправлять данные в разделитель, данные должны начинаться с указанного смещения относительно начала файла ASF.

Значение смещения передается в качестве параметра в ParseData, который запускает процесс синтаксического анализа.

Объект данных делится на пакеты данных. Каждый пакет данных содержит заголовок пакета данных, предоставляющий информацию для разбора пакетов, и данные полезной нагрузки — фактические цифровые данные мультимедиа. В сценарии поиска приложение может потребовать, чтобы разделитель начал синтаксический анализ в определенном пакете данных. Для этого можно использовать индексатор ASF для получения смещения. Индексатор возвращает значение смещения, начинающееся с границы пакета. Если индексатор не используется, убедитесь, что смещение начинается в начале заголовка пакета данных. Если недопустимое смещение передается в разделитель, например, значение не указывает на границу пакета, вызовы ParseHeader и GetNextSample проходят успешно, но GetNextSample не извлекает никаких образцов, и значение NULL получается в параметре pSample.

Если разделитель настроен для анализа в обратном направлении, он всегда начинает анализ с конца буфера мультимедиа, передаваемого в ParseData. Поэтому для обратного анализа в вызове ParseDataпередайте смещение в параметре cbLength, который определяет длину данных, и задайте cbBufferOffset равным нулю.

Создание примеров для пакетов данных ASF

Приложение запускает процесс синтаксического анализа путем передачи пакетов данных в разделитель. Входные данные в разделитель — это ряд буферов мультимедиа, содержащих все или фрагменты объекта данных. Выходные данные из разделителя — это набор медиасэмплов, содержащих пакетные данные.

Чтобы передать входные данные в разделитель, создайте буфер мультимедиа и заполните его данными из раздела "Объект данных" файла ASF. (Дополнительные сведения о буферах мультимедиа см. в разделе Буферы мультимедиа.) Затем передайте буфер мультимедиа в метод IMFASFSplitter::P arseData. Можно также указать следующее:

  • Смещение в буфере, с которого разделитель должен начать разбор. Если смещение равно нулю, синтаксический анализ начинается в начале буфера. Сведения о настройке смещения данных см. в разделе "Поиск смещения данных" в этом разделе.
  • Объем данных для синтаксического анализа. Если это значение равно нулю, разделитель анализирует данные до тех пор, пока не достигнет конца буфера, как указано в методе IMFMediaBuffer::GetCurrentLength.

Разделитель создает медиасэмплы, опираясь на данные в буферах мультимедиа. Клиент может получить выходные образцы, вызывая IMFASFSplitter::GetNextSample в цикле до тех пор, пока данные для анализа не закончатся. Если GetNextSample возвращает флаг ASF_STATUSFLAGS_INCOMPLETE в параметре pdwStatusFlags, это означает, что есть больше примеров для извлечения, и приложение может снова вызывать GetNextSample. В противном случае вызовите ParseData для передачи дополнительных данных в разделитель. Для созданных примеров разделитель задает следующие сведения:

  • Разделитель задает метку времени для всех создаваемых примеров. Пример времени представляет время презентации и не включает время предварительной подготовки. Приложение может вызывать IMFSample::GetSampleTime, чтобы получить время презентации в 100-наносекундах.
  • Если прерывание возникает во время генерации образца, разделитель задает атрибут MFSampleExtension_Discontinuity в первом образце после прерывания. Разрывы обычно вызваны потерянными пакетами в сетевом подключении, поврежденными данными файла или переключением разделителя с одного исходного потока на другой.
  • Для видео разделитель проверяет, содержит ли пример ключевой кадр. Если это так, разделитель задает атрибут MFSampleExtension_CleanPoint в примере.

Если разделитель анализирует пакеты данных, полученные от сервера мультимедиа, возможно, что длина пакета переменна. В этом случае клиент должен вызывать ParseData для каждого пакета и задать атрибут MFASFSPLITTER_PACKET_BOUNDARY для каждого буфера, который отправляется в разделитель. Этот атрибут указывает на разделитель, содержит ли буфер мультимедиа начало пакета ASF. Задайте для атрибута значение TRUE, если буфер содержит начало нового пакета. Если буфер содержит продолжение предыдущего пакета, задайте для атрибута значение FALSE. Буферы не могут охватывать несколько пакетов.

Перед передачей новых медиа-буферов в разделитель приложение должно вызвать IMFASFSplitter::Flush. Этот метод сбрасывает разделитель и очищает любой частичный кадр, ожидающий завершения. Это полезно в сценарии поиска, где смещение находится в другом месте.

Пример

В следующем примере кода показано, как анализировать пакеты данных. Этот пример анализирует с начала объекта данных до конца потока и отображает сведения о примерах, содержащих ключевые кадры. Для получения полного примера использования этого кода см. руководство: "Чтение файла 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;
}

разделитель ASF