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


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

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

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

Для анализа объекта данных ASF требуются следующие методы:

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

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

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

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

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

    Примечание

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

     

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

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

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

Если разделитель настроен для анализа в обратном направлении, то разделитель всегда начинает анализ в конце буфера мультимедиа, передаваемого в 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