Freigeben über


Verwenden des Quelllesers zum Verarbeiten von Mediendaten

In diesem Thema wird beschrieben, wie Sie den Quellleser zum Verarbeiten von Mediendaten verwenden.

Führen Sie die folgenden grundlegenden Schritte aus, um den Quellleser zu verwenden:

  1. Erstellen Sie eine instance des Quelllesers.
  2. Auflisten der möglichen Ausgabeformate.
  3. Legen Sie das tatsächliche Ausgabeformat für jeden Stream fest.
  4. Verarbeiten von Daten aus der Quelle.

Im weiteren Verlauf dieses Themas werden diese Schritte ausführlich beschrieben.

Erstellen des Quelllesers

Um eine instance des Quelllesers zu erstellen, rufen Sie eine der folgenden Funktionen auf:

Funktion BESCHREIBUNG
MFCreateSourceReaderFromURL
Akzeptiert eine URL als Eingabe. Diese Funktion verwendet den Quelllöser , um eine Medienquelle aus der URL zu erstellen.
MFCreateSourceReaderFromByteStream
Verwendet einen Zeiger auf einen Bytedatenstrom. Diese Funktion verwendet auch den Quelllöser, um die Medienquelle zu erstellen.
MFCreateSourceReaderFromMediaSource
Verwendet einen Zeiger auf eine Medienquelle, die bereits erstellt wurde. Diese Funktion ist nützlich für Medienquellen, die der Quelllöser nicht erstellen kann, z. B. Erfassungsgeräte oder benutzerdefinierte Medienquellen.

 

Verwenden Sie für Mediendateien in der Regel MFCreateSourceReaderFromURL. Verwenden Sie für Geräte wie Webcams MFCreateSourceReaderFromMediaSource. (Weitere Informationen zu Aufnahmegeräten in Microsoft Media Foundation finden Sie unter Audio/Video Capture.)

Jede dieser Funktionen verwendet einen optionalen IMFAttributes-Zeiger , der verwendet wird, um verschiedene Optionen für den Quellleser festzulegen, wie in den Referenzthemen für diese Funktionen beschrieben. Um das Standardverhalten abzurufen, legen Sie diesen Parameter auf NULL fest. Jede Funktion gibt einen IMFSourceReader-Zeiger als Ausgabeparameter zurück. Sie müssen die Funktionen CoInitialize(Ex) und MFStartup aufrufen, bevor Sie eine dieser Funktionen aufrufen.

Der folgende Code erstellt den Quellleser aus einer URL.

int __cdecl wmain(int argc, __in_ecount(argc) PCWSTR* argv)
{
    if (argc < 2)
    {
        return 1;
    }

    const WCHAR *pszURL = argv[1];

    // Initialize the COM runtime.
    HRESULT hr = CoInitializeEx(0, COINIT_MULTITHREADED);
    if (SUCCEEDED(hr))
    {
        // Initialize the Media Foundation platform.
        hr = MFStartup(MF_VERSION);
        if (SUCCEEDED(hr))
        {
            // Create the source reader.
            IMFSourceReader *pReader;
            hr = MFCreateSourceReaderFromURL(pszURL, NULL, &pReader);
            if (SUCCEEDED(hr))
            {
                ReadMediaFile(pReader);
                pReader->Release();
            }
            // Shut down Media Foundation.
            MFShutdown();
        }
        CoUninitialize();
    }
}

Auflisten von Ausgabeformaten

Jede Medienquelle verfügt über mindestens einen Stream. Beispielsweise kann eine Videodatei einen Videodatenstrom und einen Audiodatenstrom enthalten. Das Format jedes Streams wird mithilfe eines Medientyps beschrieben, der durch die IMFMediaType-Schnittstelle dargestellt wird. Weitere Informationen zu Medientypen finden Sie unter Medientypen. Sie müssen den Medientyp untersuchen, um das Format der Daten zu verstehen, die Sie vom Quellleser erhalten.

Zunächst hat jeder Stream ein Standardformat, das Sie durch Aufrufen der IMFSourceReader::GetCurrentMediaType-Methode finden können:

Für jeden Stream bietet die Medienquelle eine Liste möglicher Medientypen für diesen Stream. Die Anzahl der Typen hängt von der Quelle ab. Wenn die Quelle eine Mediendatei darstellt, gibt es in der Regel nur einen Typ pro Stream. Eine Webcam hingegen kann Videos in verschiedenen Formaten streamen. In diesem Fall kann die Anwendung aus der Liste der Medientypen auswählen, welches Format verwendet werden soll.

Um einen der Medientypen für einen Stream abzurufen, rufen Sie die IMFSourceReader::GetNativeMediaType-Methode auf. Diese Methode verwendet zwei Indexparameter: den Index des Datenstroms und einen Index in die Liste der Medientypen für den Stream. Um alle Typen für einen Stream aufzulisten, erhöhen Sie den Listenindex, während der Streamindex konstant bleibt. Wenn der Listenindex außerhalb der Grenzen verläuft, gibt GetNativeMediaTypeMF_E_NO_MORE_TYPES zurück.

HRESULT EnumerateTypesForStream(IMFSourceReader *pReader, DWORD dwStreamIndex)
{
    HRESULT hr = S_OK;
    DWORD dwMediaTypeIndex = 0;

    while (SUCCEEDED(hr))
    {
        IMFMediaType *pType = NULL;
        hr = pReader->GetNativeMediaType(dwStreamIndex, dwMediaTypeIndex, &pType);
        if (hr == MF_E_NO_MORE_TYPES)
        {
            hr = S_OK;
            break;
        }
        else if (SUCCEEDED(hr))
        {
            // Examine the media type. (Not shown.)

            pType->Release();
        }
        ++dwMediaTypeIndex;
    }
    return hr;
}

Um die Medientypen für jeden Stream aufzulisten, erhöhen Sie den Streamindex. Wenn der Streamindex außerhalb der Grenzen verläuft, gibt GetNativeMediaTypeMF_E_INVALIDSTREAMNUMBER zurück.

HRESULT EnumerateMediaTypes(IMFSourceReader *pReader)
{
    HRESULT hr = S_OK;
    DWORD dwStreamIndex = 0;

    while (SUCCEEDED(hr))
    {
        hr = EnumerateTypesForStream(pReader, dwStreamIndex);
        if (hr == MF_E_INVALIDSTREAMNUMBER)
        {
            hr = S_OK;
            break;
        }
        ++dwStreamIndex;
    }
    return hr;
}

Festlegen von Ausgabeformaten

Um das Ausgabeformat zu ändern, rufen Sie die IMFSourceReader::SetCurrentMediaType-Methode auf. Diese Methode verwendet den Streamindex und einen Medientyp:

hr = pReader->SetCurrentMediaType(dwStreamIndex, pMediaType);

Für den Medientyp hängt es davon ab, ob Sie einen Decoder einfügen möchten.

  • Um Daten direkt aus der Quelle abzurufen, ohne sie zu decodieren, verwenden Sie einen der von GetNativeMediaType zurückgegebenen Typen.
  • Um den Stream zu decodieren, erstellen Sie einen neuen Medientyp, der das gewünschte unkomprimierte Format beschreibt.

Erstellen Sie im Fall des Decoders den Medientyp wie folgt:

  1. Rufen Sie MFCreateMediaType auf, um einen neuen Medientyp zu erstellen.
  2. Legen Sie das attribut MF_MT_MAJOR_TYPE fest, um Audio oder Video anzugeben.
  3. Legen Sie das attribut MF_MT_SUBTYPE fest, um den Untertyp des Decodierungsformats anzugeben. (Weitere Informationen finden Sie unter AUDIO-Untertyp-GUIDs und Videountertyp-GUIDs.)
  4. Rufen Sie IMFSourceReader::SetCurrentMediaType auf.

Der Quellleser lädt den Decoder automatisch. Um die vollständigen Details des decodierten Formats abzurufen, rufen Sie IMFSourceReader::GetCurrentMediaType nach dem Aufruf von SetCurrentMediaType auf.

Der folgende Code konfiguriert den Videostream für RGB-32 und den Audiostream für PCM-Audio.

HRESULT ConfigureDecoder(IMFSourceReader *pReader, DWORD dwStreamIndex)
{
    IMFMediaType *pNativeType = NULL;
    IMFMediaType *pType = NULL;

    // Find the native format of the stream.
    HRESULT hr = pReader->GetNativeMediaType(dwStreamIndex, 0, &pNativeType);
    if (FAILED(hr))
    {
        return hr;
    }

    GUID majorType, subtype;

    // Find the major type.
    hr = pNativeType->GetGUID(MF_MT_MAJOR_TYPE, &majorType);
    if (FAILED(hr))
    {
        goto done;
    }

    // Define the output type.
    hr = MFCreateMediaType(&pType);
    if (FAILED(hr))
    {
        goto done;
    }

    hr = pType->SetGUID(MF_MT_MAJOR_TYPE, majorType);
    if (FAILED(hr))
    {
        goto done;
    }

    // Select a subtype.
    if (majorType == MFMediaType_Video)
    {
        subtype= MFVideoFormat_RGB32;
    }
    else if (majorType == MFMediaType_Audio)
    {
        subtype = MFAudioFormat_PCM;
    }
    else
    {
        // Unrecognized type. Skip.
        goto done;
    }

    hr = pType->SetGUID(MF_MT_SUBTYPE, subtype);
    if (FAILED(hr))
    {
        goto done;
    }

    // Set the uncompressed format.
    hr = pReader->SetCurrentMediaType(dwStreamIndex, NULL, pType);
    if (FAILED(hr))
    {
        goto done;
    }

done:
    SafeRelease(&pNativeType);
    SafeRelease(&pType);
    return hr;
}

Verarbeiten von Mediendaten

Um Mediendaten aus der Quelle abzurufen, rufen Sie die IMFSourceReader::ReadSample-Methode auf, wie im folgenden Code gezeigt.

        DWORD streamIndex, flags;
        LONGLONG llTimeStamp;

        hr = pReader->ReadSample(
            MF_SOURCE_READER_ANY_STREAM,    // Stream index.
            0,                              // Flags.
            &streamIndex,                   // Receives the actual stream index. 
            &flags,                         // Receives status flags.
            &llTimeStamp,                   // Receives the time stamp.
            &pSample                        // Receives the sample or NULL.
            );

Der erste Parameter ist der Index des Datenstroms, für den Sie Daten abrufen möchten. Sie können auch MF_SOURCE_READER_ANY_STREAM angeben, um die nächsten verfügbaren Daten aus einem beliebigen Stream abzurufen. Der zweite Parameter enthält optionale Flags. Eine Liste dieser MF_SOURCE_READER_CONTROL_FLAG finden Sie unter MF_SOURCE_READER_CONTROL_FLAG . Der dritte Parameter empfängt den Index des Datenstroms, der die Daten tatsächlich erzeugt. Sie benötigen diese Informationen, wenn Sie den ersten Parameter auf MF_SOURCE_READER_ANY_STREAM festlegen. Der vierte Parameter empfängt status Flags, die verschiedene Ereignisse angeben, die beim Lesen der Daten auftreten können, z. B. Formatänderungen im Stream. Eine Liste der status Flags finden Sie unter MF_SOURCE_READER_FLAG.

Wenn die Medienquelle Daten für den angeforderten Datenstrom erzeugen kann, empfängt der letzte Parameter von ReadSample einen Zeiger auf die IMFSample-Schnittstelle eines Medienbeispielobjekts. Verwenden Sie das Medienbeispiel für Folgendes:

  • Rufen Sie einen Zeiger auf die Mediendaten ab.
  • Rufen Sie die Präsentationszeit und die Beispieldauer ab.
  • Ruft Attribute ab, die Interlacing, Felddominanz und andere Aspekte des Beispiels beschreiben.

Der Inhalt der Mediendaten hängt vom Format des Datenstroms ab. Für einen unkomprimierten Videostream enthält jedes Medienbeispiel einen einzelnen Videoframe. Bei einem unkomprimierten Audiostream enthält jedes Medienbeispiel eine Sequenz von Audioframes.

Die ReadSample-Methode kann S_OK zurückgeben und dennoch kein Medienbeispiel im pSample-Parameter zurückgeben. Wenn Sie beispielsweise das Ende der Datei erreichen, legt ReadSample das flag MF_SOURCE_READERF_ENDOFSTREAM in dwFlags und pSample auf NULL fest. In diesem Fall gibt die ReadSample-MethodeS_OK zurück, da kein Fehler aufgetreten ist, obwohl der pSample-Parameter auf NULL festgelegt ist. Überprüfen Sie daher immer den Wert von pSample , bevor Sie ihn dereferenzieren.

Der folgende Code zeigt, wie ReadSample in einer Schleife aufgerufen und die von der -Methode zurückgegebenen Informationen überprüft werden, bis das Ende der Mediendatei erreicht ist.

HRESULT ProcessSamples(IMFSourceReader *pReader)
{
    HRESULT hr = S_OK;
    IMFSample *pSample = NULL;
    size_t  cSamples = 0;

    bool quit = false;
    while (!quit)
    {
        DWORD streamIndex, flags;
        LONGLONG llTimeStamp;

        hr = pReader->ReadSample(
            MF_SOURCE_READER_ANY_STREAM,    // Stream index.
            0,                              // Flags.
            &streamIndex,                   // Receives the actual stream index. 
            &flags,                         // Receives status flags.
            &llTimeStamp,                   // Receives the time stamp.
            &pSample                        // Receives the sample or NULL.
            );

        if (FAILED(hr))
        {
            break;
        }

        wprintf(L"Stream %d (%I64d)\n", streamIndex, llTimeStamp);
        if (flags & MF_SOURCE_READERF_ENDOFSTREAM)
        {
            wprintf(L"\tEnd of stream\n");
            quit = true;
        }
        if (flags & MF_SOURCE_READERF_NEWSTREAM)
        {
            wprintf(L"\tNew stream\n");
        }
        if (flags & MF_SOURCE_READERF_NATIVEMEDIATYPECHANGED)
        {
            wprintf(L"\tNative type changed\n");
        }
        if (flags & MF_SOURCE_READERF_CURRENTMEDIATYPECHANGED)
        {
            wprintf(L"\tCurrent type changed\n");
        }
        if (flags & MF_SOURCE_READERF_STREAMTICK)
        {
            wprintf(L"\tStream tick\n");
        }

        if (flags & MF_SOURCE_READERF_NATIVEMEDIATYPECHANGED)
        {
            // The format changed. Reconfigure the decoder.
            hr = ConfigureDecoder(pReader, streamIndex);
            if (FAILED(hr))
            {
                break;
            }
        }

        if (pSample)
        {
            ++cSamples;
        }

        SafeRelease(&pSample);
    }

    if (FAILED(hr))
    {
        wprintf(L"ProcessSamples FAILED, hr = 0x%x\n", hr);
    }
    else
    {
        wprintf(L"Processed %d samples\n", cSamples);
    }
    SafeRelease(&pSample);
    return hr;
}

Entleeren der Datenpipeline

Während der Datenverarbeitung kann ein Decoder oder eine andere Transformation Eingabebeispiele puffern. Im folgenden Diagramm ruft die Anwendung ReadSample auf und empfängt ein Beispiel mit einer Präsentationszeit von t1. Der Decoder enthält Beispiele für t2 und t3.

Eine Abbildung, die die Pufferung in einem Decoder zeigt.

Beim nächsten Aufruf von ReadSample gibt der Quellleser dem Decoder möglicherweise t4 und gibt t2 an die Anwendung zurück.

Wenn Sie alle Beispiele decodieren möchten, die derzeit im Decoder gepuffert sind, ohne neue Beispiele an den Decoder zu übergeben, legen Sie das flag MF_SOURCE_READER_CONTROLF_DRAIN im dwControlFlags-Parameter von ReadSample fest. Fahren Sie damit in einer Schleife fort, bis ReadSample einen NULL-Beispielzeiger zurückgibt. Je nachdem, wie der Decoder Beispiele puffert, kann dies unmittelbar oder nach mehreren Aufrufen von ReadSample geschehen.

Abrufen der Dateidauer

Um die Dauer einer Mediendatei abzurufen, rufen Sie die IMFSourceReader::GetPresentationAttribute-Methode auf, und fordern Sie das attribut MF_PD_DURATION an, wie im folgenden Code gezeigt.

HRESULT GetDuration(IMFSourceReader *pReader, LONGLONG *phnsDuration)
{
    PROPVARIANT var;
    HRESULT hr = pReader->GetPresentationAttribute(MF_SOURCE_READER_MEDIASOURCE, 
        MF_PD_DURATION, &var);
    if (SUCCEEDED(hr))
    {
        hr = PropVariantToInt64(var, phnsDuration);
        PropVariantClear(&var);
    }
    return hr;
}

Die hier gezeigte Funktion ruft die Dauer in 100-Nanosekundeneinheiten ab. Dividieren Sie durch 10.000.000, um die Dauer in Sekunden zu erhalten.

Suchen

Eine Medienquelle, die Daten aus einer lokalen Datei abruft, kann in der Regel beliebige Positionen in der Datei suchen. Erfassen von Geräten wie Webcams können in der Regel nicht gesucht werden, da die Daten live sind. Abhängig vom Netzwerkstreamingprotokoll kann eine Quelle gesucht werden, die Daten über ein Netzwerk streamt.

Um herauszufinden, ob eine Medienquelle suchen kann, rufen Sie IMFSourceReader::GetPresentationAttribute auf, und fordern Sie das attribut MF_SOURCE_READER_MEDIASOURCE_CHARACTERISTICS an, wie im folgenden Code gezeigt:

HRESULT GetSourceFlags(IMFSourceReader *pReader, ULONG *pulFlags)
{
    ULONG flags = 0;

    PROPVARIANT var;
    PropVariantInit(&var);

    HRESULT hr = pReader->GetPresentationAttribute(
        MF_SOURCE_READER_MEDIASOURCE, 
        MF_SOURCE_READER_MEDIASOURCE_CHARACTERISTICS, 
        &var);

    if (SUCCEEDED(hr))
    {
        hr = PropVariantToUInt32(var, &flags);
    }
    if (SUCCEEDED(hr))
    {
        *pulFlags = flags;
    }

    PropVariantClear(&var);
    return hr;
}

Diese Funktion ruft eine Reihe von Funktionenflags aus der Quelle ab. Diese Flags werden in der MFMEDIASOURCE_CHARACTERISTICS-Enumeration definiert. Zwei Flags beziehen sich auf die Suche:

Flag Beschreibung
MFMEDIASOURCE_CAN_SEEK
Die Quelle kann suchen.
MFMEDIASOURCE_HAS_SLOW_SEEK
Die Suche kann lange dauern. Beispielsweise muss die Quelle möglicherweise die gesamte Datei herunterladen, bevor sie suchen kann. (Es gibt keine strengen Kriterien für eine Quelle, um dieses Flag zurückzugeben.)

 

Der folgende Code testet das flag MFMEDIASOURCE_CAN_SEEK .

BOOL SourceCanSeek(IMFSourceReader *pReader)
{
    BOOL bCanSeek = FALSE;
    ULONG flags;
    if (SUCCEEDED(GetSourceFlags(pReader, &flags)))
    {
        bCanSeek = ((flags & MFMEDIASOURCE_CAN_SEEK) == MFMEDIASOURCE_CAN_SEEK);
    }
    return bCanSeek;
}

Rufen Sie zur Suche die IMFSourceReader::SetCurrentPosition-Methode auf, wie im folgenden Code gezeigt.

HRESULT SetPosition(IMFSourceReader *pReader, const LONGLONG& hnsPosition)
{
    PROPVARIANT var;
    HRESULT hr = InitPropVariantFromInt64(hnsPosition, &var);
    if (SUCCEEDED(hr))
    {
        hr = pReader->SetCurrentPosition(GUID_NULL, var);
        PropVariantClear(&var);
    }
    return hr;
}

Der erste Parameter gibt das Zeitformat an, das Sie zum Angeben der Suchposition verwenden. Alle Medienquellen in Media Foundation müssen 100 Nanosekundeneinheiten unterstützen, die durch den Wert GUID_NULL angegeben werden. Der zweite Parameter ist ein PROPVARIANT , der die Suchposition enthält. Für 100-Nanosekunden-Zeiteinheiten ist der Datentyp LONGLONG.

Beachten Sie, dass nicht jede Medienquelle eine framegenaue Suche bietet. Die Genauigkeit der Suche hängt von mehreren Faktoren ab, z. B. dem Keyframeintervall, ob die Mediendatei einen Index enthält und ob die Daten eine konstante oder variable Bitrate aufweisen. Nachdem Sie eine Position in einer Datei gesucht haben, gibt es daher keine Garantie dafür, dass der Zeitstempel im nächsten Beispiel genau mit der angeforderten Position übereinstimmt. Im Allgemeinen ist die tatsächliche Position nicht später als die angeforderte Position, sodass Sie Beispiele verwerfen können, bis Sie den gewünschten Punkt im Stream erreichen.

Wiedergaberate

Obwohl Sie die Wiedergaberate mit dem Quellleser festlegen können, ist dies aus den folgenden Gründen in der Regel nicht sehr nützlich:

  • Der Quellleser unterstützt die umgekehrte Wiedergabe nicht, auch wenn die Medienquelle dies tut.
  • Die Anwendung steuert die Präsentationszeiten, sodass die Anwendung schnelle oder langsame Wiedergabe implementieren kann, ohne die Rate für die Quelle festzulegen.
  • Einige Medienquellen unterstützen den Ausdünnungsmodus , bei dem die Quelle weniger Beispiele liefert – in der Regel nur die Keyframes. Wenn Sie jedoch Nicht-Keyframes löschen möchten, können Sie jedes Beispiel auf das attribut MFSampleExtension_CleanPoint überprüfen.

Um die Wiedergaberate mithilfe des Quelllesers festzulegen, rufen Sie die IMFSourceReader::GetServiceForStream-Methode auf, um die IMFRateSupport - und IMFRateControl-Schnittstellen von der Medienquelle abzurufen.

Hardwarebeschleunigung

Der Quellleser ist mit Microsoft DirectX Video Acceleration (DXVA) 2.0 für die hardwarebeschleunigte Videodecodierung kompatibel. Führen Sie die folgenden Schritte aus, um DXVA mit dem Quellleser zu verwenden.

  1. Erstellen Sie ein Microsoft Direct3D-Gerät.
  2. Rufen Sie die DXVA2CreateDirect3DDeviceManager9-Funktion auf, um den Direct3D-Geräte-Manager zu erstellen. Diese Funktion ruft einen Zeiger auf die IDirect3DDeviceManager9-Schnittstelle ab.
  3. Rufen Sie die IDirect3DDeviceManager9::ResetDevice-Methode mit einem Zeiger auf das Direct3D-Gerät auf.
  4. Erstellen Sie einen Attributspeicher, indem Sie die MFCreateAttributes-Funktion aufrufen.
  5. Erstellen Sie den Quellleser. Übergeben Sie den Attributspeicher im pAttributes-Parameter der Erstellungsfunktion.

Wenn Sie ein Direct3D-Gerät bereitstellen, ordnet der Quellleser Videobeispiele zu, die mit der DXVA-Videoprozessor-API kompatibel sind. Sie können die DXVA-Videoverarbeitung verwenden, um Hardwaredeinterlacing oder Videomischung durchzuführen. Weitere Informationen finden Sie unter DXVA-Videoverarbeitung. Wenn der Decoder DXVA 2.0 unterstützt, wird das Direct3D-Gerät zum Ausführen einer hardwarebeschleunigten Decodierung verwendet.

Wichtig

Ab Windows 8 kann IMFDXGIDeviceManager anstelle von IDirect3DDeviceManager9 verwendet werden. Für Windows Store-Apps müssen Sie IMFDXGIDeviceManager verwenden. Weitere Informationen finden Sie in den Direct3D 11-Video-APIs.

 

Quellleser