Share via


Generieren von Streambeispielen aus einem vorhandenen ASF-Datenobjekt

Das ASF-Splitterobjekt ist eine WMContainer-Layerkomponente, die das ASF-Datenobjekt einer ASF-Datendatei (Advanced Systems Format, ASF) analysiert.

Bevor Sie Datenpakete an den Splitter übergeben, muss die Anwendung Datenpakete initialisieren, konfigurieren und auswählen, um sie für den Analyseprozess vorzubereiten. Weitere Informationen finden Sie unter Erstellen des ASF Splitter-Objekts und Konfigurieren des ASF Splitter-Objekts.

Die methoden, die für die Analyse des ASF-Datenobjekts erforderlich sind:

Suchen des Datenversatzs

Bevor Sie den Analyseprozess starten, muss die Anwendung das Datenobjekt in der ASF-Datei suchen. Es gibt zwei Möglichkeiten zum Abrufen des Offsets des Data-Objekts aus dem Anfang der Datei:

  • Bevor Sie das ContentInfo-Objekt initialisiert haben, können Sie die IMFASFContentInfo::GetHeaderSize-Methode aufrufen. Diese Methode erfordert einen Puffer, der die ersten 30 Bytes des ASF-Headers enthält. Es gibt die Größe des gesamten Headers zurück, der den Offset auf das erste Datenpaket angibt. Dieser Wert enthält auch die Datenobjektheadergröße von 50 Bytes.

  • Nachdem Sie das ContentInfo-Objekt initialisiert haben, können Sie das Präsentationsdeskriptor abrufen, indem Sie IMFASFContentInfo::GeneratePresentationDescriptor aufrufen und dann den Präsentationsdeskriptor für das MF_PD_ASF_DATA_START_OFFSET-Attribut abfragen. Der Wert dieses Attributs ist die Kopfzeilengröße.

    Hinweis

    Das MF_PD_ASF_DATA_LENGTH-Attribut im Präsentationsdeskriptor gibt die Länge des ASF-Datenobjekts an.

     

In beiden Fällen ist der zurückgegebene Wert die Größe des Headerobjekts plus die Größe des Kopfzeilenabschnitts des Datenobjekts. Der resultierende Wert ist daher der Offset zum Anfang der Datenpakete im ASF-Datenobjekt. Wenn Sie mit dem Senden von Daten an den Splitter beginnen, müssen die Daten mit diesem Offset von Beginn der ASF-Datei beginnen.

Der Offsetwert wird als Parameter an ParseData übergeben, der den Analyseprozess startet.

Das Data-Objekt wird in Datenpakete unterteilt. Jedes Datenpaket enthält einen Datenpaketheader, der Paketanalyseinformationen und die Nutzlastdaten bereitstellt – die tatsächlichen digitalen Mediendaten. In einem suchenden Szenario möchte die Anwendung möglicherweise den Splitter zum Analysieren bei einem bestimmten Datenpaket starten. Dazu können Sie den ASF-Indexer verwenden, um den Offset abzurufen. Der Indexer gibt einen Offsetwert zurück, der an der Paketgrenze beginnt. Wenn Sie den Indexer nicht verwenden, stellen Sie sicher, dass der Offset am Anfang des Datenpaketheaders beginnt. Wenn ein ungültiger Offset an den Splitter übergeben wird, z. B. der Wert nicht auf die Paketgrenze verweist, wird ParseHeader und GetNextSample-Aufruf erfolgreich ausgeführt, aber GetNextSample ruft keine Beispiele ab und NULL wird im pSample-Parameter empfangen.

Wenn der Splitter so konfiguriert ist, dass er in umgekehrter Richtung analysiert wird, beginnt der Splitter immer am Ende des Medienpuffers, der an ParseData übergeben wird. Übergeben Sie daher für die Umgekehrte Analyse im Aufruf von ParseData den Offset im cbLength-Parameter , der die Länge der Daten angibt und cbBufferOffset auf Null legt.

Generieren von Beispielen für ASF-Datenpakete

Eine Anwendung startet den Analyseprozess, indem Sie die Datenpakete an den Splitter übergeben. Die Eingabe für den Splitter ist eine Reihe von Medienpuffern, die die gesamten oder Fragmente des Data-Objekts enthalten. Die Ausgabe aus dem Splitter ist eine Reihe von Medienbeispielen, die die Paketdaten enthalten.

Um Eingabedaten an den Splitter zu übergeben, erstellen Sie einen Medienpuffer und füllen Sie sie mit Daten aus dem Datenobjektabschnitt der ASF-Datei aus. (Weitere Informationen zu Medienpuffern finden Sie unter Medienpuffer.) Übergeben Sie dann den Medienpuffer an die IMFASFSplitter::P arseData-Methode . Sie können auch Folgendes angeben:

  • Der Offset in den Puffer, in dem der Splitter mit der Analyse beginnen soll. Wenn der Offset null ist, beginnt die Analyse am Anfang des Puffers. Informationen zum Festlegen des Datenversatzs finden Sie im Abschnitt "Suchen nach dem Datenversatz" in diesem Thema.
  • Die Datenmenge, die analysiert werden soll. Wenn dieser Wert null ist, analysiert der Splitter, bis er das Ende des Puffers erreicht, wie durch die IMFMediaBuffer::GetCurrentLength-Methode angegeben.

Der Splitter generiert Medienbeispiele durch Verweisen auf die Daten in den Medienpuffern. Der Client kann die Ausgabebeispiele abrufen, indem IMFASFSplitter::GetNextSample in einer Schleife aufgerufen wird, bis keine weiteren Daten analysiert werden. Wenn GetNextSample das ASF_STATUSFLAGS_INCOMPLETE Flag im pdwStatusFlags-Parameter zurückgibt, bedeutet dies, dass weitere Beispiele zum Abrufen vorhanden sind, und die Anwendung kann GetNextSample erneut aufrufen. Rufen Sie andernfalls ParseData auf, um weitere Daten an den Splitter zu übergeben. Für die generierten Beispiele legt der Splitter die folgenden Informationen fest:

  • Der Splitter legt einen Zeitstempel für alle Beispiele fest, die sie generiert. Die Beispielzeit stellt die Präsentationszeit dar und enthält keine Vorabrollzeit. Die Anwendung kann IMFSample::GetSampleTime aufrufen, um die Präsentationszeit in 100-Nanosekundeneinheiten abzurufen.
  • Wenn während der Beispielgenerierung ein Bruch auftritt, legt der Splitter das MFSampleExtension_Discontinuity Attribut nach der Unterbrechung des ersten Beispiels fest. Unterbrechungen werden in der Regel durch abgelegte Pakete in einer Netzwerkverbindung, beschädigte Dateidaten oder den Splitter, der von einem Quelldatenstrom zu einem anderen wechselt, verursacht.
  • Für Video überprüft der Splitter, ob das Beispiel einen Schlüsselrahmen enthält. Wenn dies der Fall ist, legt der Splitter das MFSampleExtension_CleanPoint-Attribut im Beispiel fest.

Wenn der Splitter Datenpakete analysiert, die von einem Medienserver empfangen werden, ist es möglich, dass die Paketlänge variable ist. In diesem Fall muss der Client ParseData für jedes Paket aufrufen und das MFASFSPLITTER_PACKET_BOUNDARY-Attribut für jeden Puffer festlegen, der an den Splitter gesendet wird. Dieses Attribut gibt an, ob der Medienpuffer den Start eines ASF-Pakets enthält. Legen Sie das Attribut auf TRUE fest, wenn der Puffer den Start eines neuen Pakets enthält. Wenn der Puffer eine Fortsetzung des vorherigen Pakets enthält, legen Sie das Attribut auf FALSE fest. Die Puffer können mehrere Pakete nicht umfassen.

Bevor neue Medienpuffer an den Splitter übergeben werden, muss die Anwendung IMFASFSplitter::Flush aufrufen. Diese Methode setzt den Splitter zurück und löscht alle Teilframes, die darauf warten, abgeschlossen zu werden. Dies ist nützlich in einem Suchszenario, in dem sich der Offset an einem anderen Ort befindet.

Beispiel

Im folgenden Codebeispiel wird gezeigt, wie Datenpakete analysiert werden. In diesem Beispiel wird vom Anfang des Datenobjekts bis zum Ende des Datenstroms analysiert und Informationen zu den Beispielen angezeigt, die Schlüsselframes enthalten. Ein vollständiges Beispiel, das diesen Code verwendet, finden Sie im Lernprogramm: Lesen einer ASF-Datei.

// 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 Splitter