Verwenden des Quelllesers zum Verarbeiten von Mediendaten
In diesem Thema wird beschrieben, wie Der Quellleser zum Verarbeiten von Mediendaten verwendet wird.
Führen Sie die folgenden grundlegenden Schritte aus, um den Quellleser zu verwenden:
- Erstellen Sie eine Instanz des Quelllesers.
- Aufzählen der möglichen Ausgabeformate.
- Legen Sie das tatsächliche Ausgabeformat für jeden Stream fest.
- Verarbeiten von Daten aus der Quelle.
Im weiteren Verlauf dieses Themas werden diese Schritte ausführlich beschrieben.
- Erstellen des Quelllesers
- Aufzählen von Ausgabeformaten
- Festlegen von Ausgabeformaten
- Verarbeiten von Mediendaten
- Entladen der Datenpipeline
- Abrufen der Dateidauer
- Suchen
- Wiedergaberate
- Hardwarebeschleunigung
- Zugehörige Themen
Erstellen des Quelllesers
Um eine Instanz des Quelllesers zu erstellen, rufen Sie eine der folgenden Funktionen auf:
| Funktion | BESCHREIBUNG |
|---|---|
| MFCreateSourceReaderFromURL |
Nimmt eine URL als Eingabe an. Diese Funktion verwendet den Quell resolver, um eine Medienquelle aus der URL zu erstellen. |
| MFCreateSourceReaderFromByteStream |
Verwendet einen Zeiger auf einen Bytestream. Diese Funktion verwendet auch den Quell resolver, um die Medienquelle zu erstellen. |
| MFCreateSourceReaderFromMediaSource |
Nimmt 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 Erfassungsgeräten in Microsoft Media Foundation finden Sie unter Audio-/Videoaufnahme.)
Jede dieser Funktionen verwendet einen optionalen POINTERATTRIBUTES-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 POINTERSourceReader-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();
}
}
Aufzählen von Ausgabeformaten
Jede Medienquelle verfügt über mindestens einen Stream. Beispielsweise kann eine Videodatei einen Videostream und einen Audiostream enthalten. Das Format der einzelnen Datenströme wird mithilfe eines Medientyps beschrieben, der durch die INTERFACESMediaType-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 weist jeder Stream ein Standardformat auf, das Sie durch Aufrufen der METHODE VONSOURCEReader::GetCurrentMediaType finden können:
Für jeden Stream bietet die Medienquelle eine Liste der möglichen 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 kann dagegen Videos in verschiedenen Formaten streamen. In diesem Fall kann die Anwendung in der Liste der Medientypen das zu verwendende Format auswählen.
Um einen der Medientypen für einen Stream abzurufen, rufen Sie die METHODE VONSOURCEReader::GetNativeMediaType auf. Diese Methode verwendet zwei Indexparameter: den Index des Streams und einen Index in der 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 die Grenzen überläuft, gibt GetNativeMediaType MF 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 aufzuzählen, erhöhen Sie den Streamindex. Wenn der Streamindex die Grenzen übersteigt, gibt GetNativeMediaType MF 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 METHODE VONSOURCEReader::SetCurrentMediaType 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 GetNativeMediaTypezurückgegebenen Typen.
- Erstellen Sie zum Decodieren des Streams einen neuen Medientyp, der das gewünschte nicht komprimierte Format beschreibt.
Erstellen Sie im Fall des Decoders den Medientyp wie folgt:
- Rufen Sie MFCreateMediaType auf, um einen neuen Medientyp zu erstellen.
- Legen Sie das MF _ MT MAJOR _ _ TYPE-Attribut fest, um Audio oder Video anzugeben.
- Legen Sie das MF _ MT _ SUBTYPE-Attribut fest, um den Untertyp des Decodierungsformats anzugeben. (Weitere Informationen finden Sie unter Audio-Untertyp-GUIDs und Videountertyp-GUIDs.)
- Rufen Sie DENSOURCEReader::SetCurrentMediaTypeauf.
Der Quellleser lädt den Decoder automatisch. Rufen Sie ZUM Abrufen der vollständigen Details des decodierten Formats NACH dem Aufruf von SetCurrentMediaType DENKSOURCEReader::GetCurrentMediaType 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 METHODE VONSOURCEReader::ReadSample 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 Streams, 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 Flags finden Sie unter MF SOURCE READER CONTROL _ _ _ _ FLAG. Der dritte Parameter empfängt den Index des Streams, 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 Statusflags, die verschiedene Ereignisse angeben, die beim Lesen der Daten auftreten können, z. B. Formatänderungen im Stream. Eine Liste der Statusflags 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 INTERFACESSample-Schnittstelle eines Medienbeispielobjekts. Verwenden Sie das Medienbeispiel für Folgendes:
- Abrufen eines Zeigers auf die Mediendaten.
- Abrufen der Präsentationszeit und der Beispieldauer.
- Abrufen von Attributen, die Interlacing, Felddominanz und andere Aspekte des Beispiels beschreiben.
Der Inhalt der Mediendaten hängt vom Format des Streams ab. Für einen nicht komprimierten Videostream enthält jedes Medienbeispiel einen einzelnen Videoframe. Für einen unkomprimierten Audiostream enthält jedes Medienbeispiel eine Sequenz von Audioframes.
Die ReadSample-Methode kann S _ OK und noch 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-Methode S _ 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;
}
Entladen 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, die t1 entspricht. Der Decoder hält Stichproben für t2 und t3.

Beim nächsten Aufruf von ReadSamplegibt der Quellleser möglicherweise t4 an den Decoder weiter und gibt t2 an die Anwendung zurück.
Wenn Sie alle derzeit im Decoder gepufferten Beispiele decodieren möchten, ohne neue Stichproben an den Decoder zu übergeben, legen Sie das Flag MF _ SOURCE READER _ _ CONTROLF _ DRAIN im dwControlFlags-Parameter von ReadSamplefest. Setzen Sie dies in einer Schleife fort, bis ReadSample einen NULL-Beispielzeiger zurückgibt. Je nachdem, wie der Decoder Stichproben puffert, kann dies sofort oder nach mehreren Aufrufen von ReadSample geschehen.
Abrufen der Dateidauer
Um die Dauer einer Mediendatei zu erhalten, rufen Sie die METHODE VORLESE::GetPresentationAttribute auf, und fordern Sie das MF _ PD _ DURATION-Attribut 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 Einheiten von 100 Nanosekunden ab. Dividieren Sie durch 10.000.000, um die Dauer in Sekunden zu erhalten.
Suchen
Eine Medienquelle, die Daten aus einer lokalen Datei erhält, kann in der Regel nach beliebigen Positionen in der Datei suchen. Erfassungsgeräte wie Webcams können in der Regel nicht suchen, da die Daten live sind. Eine Quelle, die Daten über ein Netzwerk streamt, kann abhängig vom Netzwerkstreamingprotokoll möglicherweise suchen.
Um herauszufinden, ob eine Medienquelle suchen kann, rufen Sie DIE SOURCEReader::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 einen Satz von Funktionsflags aus der Quelle ab. Diese Flags werden in der MFMEDIASOURCE _ CHARACTERISTICS-Enumeration definiert. Zwei Flags beziehen sich auf Suchsuchen:
| Flag | Beschreibung |
|---|---|
| MFMEDIASOURCE KANN _ _ SUCHEN |
Die Quelle kann suchen. |
| MFMEDIASOURCE _ VERFÜGT ÜBER _ LANGSAMES _ SUCHEN |
Die Suche kann sehr lange dauern. Beispielsweise muss die Quelle möglicherweise die gesamte Datei herunterladen, bevor sie suchen kann. (Es gibt keine strengen Kriterien, damit eine Quelle dieses Flag zurück gibt.) |
Der folgende Code testet auf das MFMEDIASOURCE _ CAN _ SEEK-Flag.
BOOL SourceCanSeek(IMFSourceReader *pReader)
{
BOOL bCanSeek = FALSE;
ULONG flags;
if (SUCCEEDED(GetSourceFlags(pReader, &flags)))
{
bCanSeek = ((flags & MFMEDIASOURCE_CAN_SEEK) == MFMEDIASOURCE_CAN_SEEK);
}
return bCanSeek;
}
Um zu suchen, rufen Sie wie im folgenden Code gezeigt die METHODE DURCHLESENQuelle::SetCurrentPosition auf.
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 sind erforderlich, um Einheiten von 100 Nanosekunden zu unterstützen, die durch den Wert GUID _ NULL angegeben werden. Der zweite Parameter ist eine PROPVARIANT,die die Suchposition enthält. Für 100-Nanosekunden-Zeiteinheiten ist der Datentyp LONGLONG.
Beachten Sie, dass nicht jede Medienquelle framegenaue Suchdaten bietet. Die Genauigkeit der Suche hängt von mehreren Faktoren ab, z. B. vom Keyframeintervall, davon, ob die Mediendatei einen Index enthält und ob die Daten eine konstante oder variable Bitrate haben. Daher gibt es nach dem Suchen nach einer Position in einer Datei keine Garantie, dass der Zeitstempel für das nächste Beispiel genau mit der angeforderten Position übereinstimmen wird. Im Allgemeinen ist die tatsächliche Position nicht nach der angeforderten Position, sodass Sie Stichproben 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 in der Regel aus den folgenden Gründen nicht sehr nützlich:
- Der Quellleser unterstützt auch dann keine umgekehrte Wiedergabe, 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 festlegen zu müssen.
- Einige Medienquellen unterstützen den Thinning-Modus, in dem die Quelle weniger Stichproben liefert – in der Regel nur die Keyframes. Wenn Sie jedoch Frames ohne Schlüssel ablegen möchten, können Sie jedes Beispiel auf das _ CleanPoint-Attribut MFSampleExtension überprüfen.
Rufen Sie zum Festlegen der Wiedergaberate mithilfe des Quelllesers die METHODE VERERBSourceReader::GetServiceForStream auf, um die Schnittstellen DURCHSTRateSupport und 1000000000000000000000017 aus der Medienquelle zu erhalten.
Hardwarebeschleunigung
Der Quellleser ist für die hardwarebeschleunigte Videodecodierung mit Microsoft DirectX Video Acceleration (DXVA) 2.0 kompatibel. Führen Sie die folgenden Schritte aus, um DXVA mit dem Quellleser zu verwenden.
- Erstellen Sie ein Microsoft Direct3D-Gerät.
- Rufen Sie die DXVA2CreateDirect3DDeviceManager9-Funktion auf, um den Direct3D-Geräte-Manager zu erstellen. Diese Funktion ruft einen Zeiger auf die IDirect3DDeviceManager9-Schnittstelle ab.
- Rufen Sie die IDirect3DDeviceManager9::ResetDevice-Methode mit einem Zeiger auf das Direct3D-Gerät auf.
- Erstellen Sie einen Attributspeicher, indem Sie die MFCreateAttributes-Funktion aufrufen.
- 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 dxva video processing (DXVA-Videoverarbeitung) verwenden, um Hardwaredeinterlacing oder Videomischung durchzuführen. Weitere Informationen finden Sie unter DXVA-Videoverarbeitung. Wenn der Decoder DXVA 2.0 unterstützt, verwendet er außerdem das Direct3D-Gerät, um die hardwarebeschleunigte Decodierung durchzuführen.
Wichtig
Ab dem Windows 8 kann EINDXGIDeviceManager anstelle von IDirect3DDeviceManager9 verwendet werden. Für Windows Store-Apps müssen Sie DIEDXGIDeviceManager-Klasse verwenden. Weitere Informationen finden Sie in den Direct3D 11-Video-APIs.