Udostępnij za pośrednictwem


Generowanie przykładów strumienia z istniejącego obiektu danych ASF

Obiekt rozdzielacza ASF jest komponentem warstwy WMContainer, który analizuje Obiekt Danych ASF pliku Advance Systems Format (ASF).

Przed przekazaniem pakietów danych do splittera aplikacja musi zainicjować, skonfigurować i wybrać strumienie na rozdzielaczu, aby przygotować je do procesu analizowania. Aby uzyskać informacje, zobacz Tworzenie obiektu ASF Splitter i Konfiguracja obiektu ASF Splitter.

Metody wymagane do analizowania obiektu danych ASF to:

Znajdowanie przesunięcia danych

Przed rozpoczęciem procesu analizowania aplikacja musi zlokalizować obiekt danych w pliku ASF. Istnieją dwa sposoby przesunięcia obiektu danych od początku pliku:

  • Przed zainicjowaniem obiektu ContentInfo można wywołać metodę IMFASFContentInfo::GetHeaderSize. Ta metoda wymaga buforu zawierającego pierwsze 30 bajtów nagłówka ASF. Zwraca on rozmiar całego nagłówka, który wskazuje przesunięcie do pierwszego pakietu danych. Ta wartość zawiera również rozmiar nagłówka obiektu danych o rozmiarze 50 bajtów.

  • Po zainicjowaniu obiektu ContentInfo można pobrać deskryptor prezentacji, wywołując IMFASFContentInfo::GeneratePresentationDescriptor, a następnie wysyłając zapytanie do deskryptora prezentacji dla atrybutu MF_PD_ASF_DATA_START_OFFSET. Wartość tego atrybutu to rozmiar nagłówka.

    Uwaga

    Atrybut MF_PD_ASF_DATA_LENGTH w deskryptorze prezentacji określa długość obiektu danych ASF.

     

W obu przypadkach zwracana wartość jest rozmiarem obiektu nagłówka oraz rozmiarem sekcji nagłówka obiektu danych. W związku z tym wynikowa wartość stanowi przesunięcie do początku pakietów danych w obiekcie danych ASF. Po rozpoczęciu wysyłania danych do rozdzielacza dane muszą zaczynać się od tego przesunięcia względem samego początku pliku ASF.

Wartość przesunięcia jest przekazywana jako parametr do ParseData, co uruchamia proces parsowania.

Obiekt danych jest podzielony na pakiety danych. Każdy pakiet danych zawiera nagłówek pakietu danych, który udostępnia informacje dotyczące analizowania pakietów i danych ładunku — rzeczywiste dane multimediów cyfrowych. W scenariuszu wyszukiwania aplikacja może chcieć, aby rozdzielacz zaczął analizować określony pakiet danych. W tym celu możesz użyć indeksatora ASF, aby pobrać przesunięcie. Indeksator zwraca wartość przesunięcia rozpoczynającą się od granicy pakietu. Jeśli nie używasz indeksatora, upewnij się, że przesunięcie rozpoczyna się od początku nagłówka pakietu danych. Jeśli do rozdzielacza zostanie przekazane nieprawidłowe przesunięcie, takie jak wartość, która nie wskazuje na granicę pakietu, wywołania ParseHeader i GetNextSample kończą się powodzeniem, ale GetNextSample nie pobiera żadnych próbek, a w parametrze pSample jest odbierany NULL.

Jeśli rozdzielacz jest skonfigurowany do analizowania w odwrotnym kierunku, splitter zawsze rozpoczyna analizowanie na końcu buforu multimediów, który jest przekazywany do ParseData. W związku z tym w przypadku odwrotnego analizowania w wywołaniu ParseDatanależy przekazać w parametrze cbLength przesunięcie, które określa długość danych, i ustawić cbBufferOffset na wartość zero.

Generowanie przykładów dla pakietów danych ASF

Aplikacja uruchamia proces analizowania, przekazując pakiety danych do rozdzielającego. Wejście do rozdzielacza to seria multimedialnych buforów, które zawierają całe lub fragmenty obiektu danych. Dane wyjściowe z splittera to seria przykładów multimediów zawierających dane pakietu.

Aby przekazać dane wejściowe do modułu podziału, utwórz bufor multimedialny i wypełnij je danymi z sekcji Obiekt danych pliku ASF. (Aby uzyskać więcej informacji na temat buforów multimedialnych, zobacz Media Buffers.) Następnie przekaż bufor multimedialny do metody IMFASFSplitter::ParseData. Można również określić:

  • Przesunięcie w buforze, od którego rozdzielacz powinien rozpocząć analizowanie. Jeśli przesunięcie ma wartość zero, analizowanie rozpoczyna się na początku buforu. Aby uzyskać informacje na temat ustawiania przesunięcia danych, zobacz sekcję "Znajdowanie przesunięcia danych" w tym temacie.
  • Ilość danych do przeanalizowana. Jeśli ta wartość ma wartość zero, rozdzielacz analizuje, dopóki nie osiągnie końca buforu określonego przez metodę IMFMediaBuffer::GetCurrentLength.

Splitter generuje próbki multimedialne, odwołując się do danych w buforach multimedialnych. Klient może pobrać przykłady danych wyjściowych, wywołując IMFASFSplitter::GetNextSample w pętli, dopóki nie będzie więcej danych do przeanalizowania. Jeśli GetNextSample zwraca flagę ASF_STATUSFLAGS_INCOMPLETE w parametrze pdwStatusFlags, oznacza to, że istnieje więcej próbek do pobrania, a aplikacja może ponownie wywołać GetNextSample. W przeciwnym razie wywołaj ParseData, aby przekazać więcej danych do rozdzielacze. W przypadku wygenerowanych przykładów rozdzielacz ustawia następujące informacje:

  • Rozdzielacz ustawia sygnaturę czasową dla wszystkich wygenerowanych próbek. Czas próbki reprezentuje czas prezentacji i nie obejmuje czasu wstępnego. Aplikacja może wywołać IMFSample::GetSampleTime, aby uzyskać czas prezentacji w 100-nanosekundach.
  • Jeśli podczas generowania próbki wystąpi przerwanie, splitter ustawia atrybut MFSampleExtension_Discontinuity na pierwszym przykładzie po przerwaniu. Przerwania są zwykle spowodowane porzuconymi pakietami w połączeniu sieciowym, uszkodzonymi danymi plików lub rozdzielaczem przełączającym się z jednego strumienia źródłowego na inny.
  • W przypadku wideo splitter sprawdza, czy próbka zawiera klatkę kluczową. Jeśli tak, splitter ustawia atrybut MFSampleExtension_CleanPoint w próbce.

Jeśli rozdzielacz analizuje pakiety danych odbierane z serwera multimediów, możliwe, że długość pakietu jest zmienna. W takim przypadku klient musi wywołać ParseData dla każdego pakietu i ustawić atrybut MFASFSPLITTER_PACKET_BOUNDARY dla każdego buforu wysyłanego do rozdzielacze. Ten atrybut wskazuje demultiplekserowi, czy bufor nośnika zawiera początek pakietu ASF. Ustaw atrybut na wartość true, jeśli bufor zawiera początek nowego pakietu. Jeśli bufor zawiera kontynuację poprzedniego pakietu, ustaw atrybut na wartość FALSE. Bufory nie mogą obejmować wielu pakietów.

Przed przekazaniem nowych buforów multimedialnych do rozdzielacza aplikacja musi wywołać IMFASFSplitter::Flush. Ta metoda resetuje rozdzielnik i czyści wszystkie niekompletne ramki, które czekają na ukończenie. Jest to przydatne w scenariuszu wyszukiwania, w którym przesunięcie znajduje się w innej lokalizacji.

Przykład

Poniższy przykład kodu przedstawia sposób analizowania pakietów danych. Ten przykład parsuje od początku Obiektu Danych do końca strumienia i wyświetla informacje o próbkach zawierających klatki kluczowe. Aby zobaczyć pełny przykład używający tego kodu, przejdź do Samouczek: Odczytywanie pliku 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;
}

rozdzielacz ASF