Mediagegevens verwerken met behulp van de bronlezer
In dit onderwerp wordt beschreven hoe u de bronlezer gebruikt voor het verwerken van mediagegevens.
Voer de volgende basisstappen uit om de bronlezer te gebruiken:
- Maak een exemplaar van de bronlezer.
- Noem de mogelijke uitvoerindelingen.
- Stel de werkelijke uitvoerindeling voor elke stream in.
- Gegevens uit de bron verwerken.
In de rest van dit onderwerp worden deze stappen uitgebreid beschreven.
- De bronlezer maken
- uitvoerindelingen opsommen
- Uitvoerindelingen instellen
- Mediagegevens verwerken
- de gegevenspijplijn leegmaken
- Het verkrijgen van de bestandstijd
- Op zoek naar
- afspeelsnelheid
- Hardwareversnelling
- Verwante onderwerpen
De bronlezer maken
Als u een exemplaar van de bronlezer wilt maken, roept u een van de volgende functies aan:
Functie | Beschrijving |
---|---|
MFCreateSourceReaderFromURL |
Neemt een URL als invoer. Deze functie maakt gebruik van de Source Resolver om een mediabron te maken op basis van de URL. |
MFCreateSourceReaderFromByteStream |
Hiermee wordt een aanwijzer naar een bytestroom gebruikt. Deze functie maakt ook gebruik van de bronresolver om de mediabron te maken. |
MFCreateSourceReaderFromMediaSource |
Hiermee wordt een aanwijzer naar een mediabron verplaatst die al is gemaakt. Deze functie is handig voor mediabronnen die de bron-resolver niet kan maken, zoals opnameapparaten of aangepaste mediabronnen. |
Gebruik doorgaans MFCreateSourceReaderFromURLvoor mediabestanden. Gebruik MFCreateSourceReaderFromMediaSourcevoor apparaten, zoals webcams. (Zie Audio/Video Capturevoor meer informatie over het vastleggen van apparaten in Microsoft Media Foundation.)
Elk van deze functies heeft een optionele IMFAttributes aanwijzer, die wordt gebruikt voor het instellen van verschillende opties voor de bronlezer, zoals beschreven in de referentieonderwerpen voor deze functies. Als u het standaardgedrag wilt ophalen, stelt u deze parameter in op NULL-. Elke functie retourneert een IMFSourceReader aanwijzer als uitvoerparameter. U moet CoInitialize(Ex) aanroepen en MFStartup functie aanroepen voordat u een van deze functies aanroept.
Met de volgende code wordt de bronlezer gemaakt op basis van een 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();
}
}
Uitvoerindelingen inventariseren
Elke mediabron heeft ten minste één stream. Een videobestand kan bijvoorbeeld een videostream en een audiostream bevatten. De indeling van elke stream wordt beschreven met behulp van een mediatype, vertegenwoordigd door de IMFMediaType interface. Zie Mediatypenvoor meer informatie over mediatypen. U moet het mediatype onderzoeken om inzicht te krijgen in de indeling van de gegevens die u van de bronlezer krijgt.
In eerste instantie heeft elke stream een standaardindeling, die u kunt vinden door de methode IMFSourceReader::GetCurrentMediaType aan te roepen:
Voor elke stream biedt de mediabron een lijst met mogelijke mediatypen voor die stream. Het aantal typen is afhankelijk van de bron. Als de bron een mediabestand vertegenwoordigt, is er meestal slechts één type per stream. Aan de andere kant kan een webcam video in verschillende indelingen streamen. In dat geval kan de toepassing selecteren welke indeling moet worden gebruikt in de lijst met mediatypen.
Als u een van de mediatypen voor een stream wilt ophalen, roept u de methode IMFSourceReader::GetNativeMediaType aan. Deze methode gebruikt twee indexparameters: de index van de stream en een index in de lijst met mediatypen voor de stream. Als u alle typen voor een stream wilt inventariseren, moet u de lijstindex verhogen terwijl de stroomindex constant blijft. Wanneer de lijstindex buiten bereik valt, retourneert GetNativeMediaTypeMF_E_NO_MORE_TYPES.
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;
}
Als u de mediatypen voor elke stream wilt inventariseren, moet u de streamindex verhogen. Wanneer de streamindex buiten de grenzen valt, geeft GetNativeMediaTypeMF_E_INVALIDSTREAMNUMBERterug.
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;
}
Uitvoerindelingen instellen
Als u de uitvoerindeling wilt wijzigen, roept u de methode IMFSourceReader::SetCurrentMediaType aan. Deze methode gebruikt de streamindex en een mediatype:
hr = pReader->SetCurrentMediaType(dwStreamIndex, pMediaType);
Voor het mediatype is het afhankelijk van of u een decoder wilt invoegen.
- Als u gegevens rechtstreeks uit de bron wilt ophalen zonder deze te decoderen, gebruikt u een van de typen die worden geretourneerd door GetNativeMediaType.
- Als u de stream wilt decoderen, maakt u een nieuw mediatype dat de gewenste niet-gecomprimeerde indeling beschrijft.
Maak in het geval van de decoder het mediatype als volgt:
- Roep MFCreateMediaType- aan om een nieuw mediatype te maken.
- Stel het kenmerk MF_MT_MAJOR_TYPE in om audio of video op te geven.
- Stel het kenmerk MF_MT_SUBTYPE in om het subtype van de coderingsindeling op te geven. (Zie audio-subtype-GUID's en videosubtype-GUID's.)
- Roep IMFSourceReader::SetCurrentMediaTypeaan.
De bronlezer laadt de decoder automatisch. Als u de volledige details van de gedecodeerde indeling wilt ophalen, roept u IMFSourceReader::GetCurrentMediaType na de aanroep naar SetCurrentMediaType
Met de volgende code configureert u de videostream voor RGB-32 en de audiostream voor 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;
}
Mediagegevens verwerken
Als u mediagegevens uit de bron wilt ophalen, roept u de methode IMFSourceReader::ReadSample aan, zoals wordt weergegeven in de volgende code.
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.
);
De eerste parameter is de index van de stream waarvoor u gegevens wilt ophalen. U kunt ook MF_SOURCE_READER_ANY_STREAM opgeven om de volgende beschikbare gegevens uit elke stream op te halen. De tweede parameter bevat optionele vlaggen; zie MF_SOURCE_READER_CONTROL_FLAG voor een lijst met deze items. De derde parameter ontvangt de index van de stream die daadwerkelijk de gegevens produceert. U hebt deze informatie nodig als u de eerste parameter instelt op MF_SOURCE_READER_ANY_STREAM. De vierde parameter ontvangt statusvlaggen, die verschillende gebeurtenissen aangeven die kunnen optreden tijdens het lezen van de gegevens, zoals wijzigingen in het formaat van de stream. Zie MF_SOURCE_READER_FLAGvoor een lijst met statusvlagmen.
Als de mediabron gegevens kan produceren voor de aangevraagde stream, ontvangt de laatste parameter van ReadSample- een aanwijzer naar de IMFSample-interface van een mediavoorbeeldobject. Gebruik het mediavoorbeeld om het volgende te doen:
- Haal een aanwijzer naar de mediagegevens op.
- De presentatietijd en voorbeeldduur ophalen.
- Kenmerken ophalen die interlacing, velddominantie en andere aspecten van de steekproef beschrijven.
De inhoud van de mediagegevens is afhankelijk van de indeling van de stream. Voor een niet-gecomprimeerde videostream bevat elk mediavoorbeeld één videoframe. Voor een niet-gecomprimeerde audiostream bevat elk mediavoorbeeld een reeks audioframes.
De methode ReadSample kan S_OK retourneren en nog geen mediavoorbeeld retourneren in de parameter pSample. Wanneer u bijvoorbeeld het einde van het bestand bereikt, stelt ReadSample de vlag MF_SOURCE_READERF_ENDOFSTREAM in dwFlags in en zet pSample op NULL. In dit geval retourneert de methode ReadSampleS_OK omdat er geen fout is opgetreden, ook al is de parameter pSample ingesteld op NULL-. Controleer daarom altijd de waarde van pSample voordat u deze dereferenceren.
De volgende code laat zien hoe u ReadSample- in een lus aanroept en de informatie controleert die door de methode wordt geretourneerd, totdat het einde van het mediabestand is bereikt.
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;
}
De gegevenspijplijn leegmaken
Tijdens de gegevensverwerking kan een decoder of een andere transformatie invoervoorbeelden bufferen. In het volgende diagram roept de toepassing ReadSample- aan en ontvangt een voorbeeld met een presentatietijd die gelijk is aan t1. De decoder houdt vast aan monsters voor t2 en t3.
Bij de volgende aanroep van ReadSample-kan de bronlezer t4- aan de decoder geven en t2 aan de toepassing retourneren.
Als u alle voorbeelden wilt decoderen die momenteel in de decoder zijn gebufferd, zonder nieuwe steekproeven door te geven aan de decoder, stelt u de vlag MF_SOURCE_READER_CONTROLF_DRAIN in de dwControlFlags-parameter van ReadSample-in. Ga door in een lus totdat ReadSample- een NULL- voorbeeldpointer retourneert. Afhankelijk van hoe de decoder steekproeven buffert, kan dat onmiddellijk of na verschillende aanroepen naar ReadSample-gebeuren.
De duur van het bestand verkrijgen
Als u de duur van een mediabestand wilt ophalen, roept u de IMFSourceReader::GetPresentationAttribute methode aan en vraagt u het kenmerk MF_PD_DURATION aan, zoals wordt weergegeven in de volgende code.
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;
}
De hier weergegeven functie haalt de duur op in eenheden van 100 nanoseconden. Deel door 10.000.000 om de duur in seconden op te halen.
Op zoek naar
Een mediabron die gegevens ophaalt uit een lokaal bestand, kan meestal naar willekeurige posities in het bestand zoeken. Apparaten voor het vastleggen, zoals webcams, kunnen over het algemeen niet worden doorzocht, omdat de gegevens live zijn. Een bron die gegevens via een netwerk streamt, kan mogelijk zoeken, afhankelijk van het netwerkstreamingprotocol.
Als u wilt weten of een mediabron kan zoeken, roept u IMFSourceReader::GetPresentationAttribute aan en vraagt u het kenmerk MF_SOURCE_READER_MEDIASOURCE_CHARACTERISTICS aan, zoals wordt weergegeven in de volgende code:
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;
}
Met deze functie verkrijg je een set mogelijksheidsvlaggen van de bron. Deze vlaggen worden gedefinieerd in de opsomming MFMEDIASOURCE_CHARACTERISTICS. Twee vlaggen hebben betrekking op het zoeken naar:
Vlag | Beschrijving |
---|---|
MFMEDIASOURCE_CAN_SEEK |
De bron kan op zoek gaan. |
MFMEDIASOURCE_HAS_SLOW_SEEK |
Het kan lang duren voordat het zoeken is voltooid. De bron moet bijvoorbeeld het hele bestand downloaden voordat er in het bestand gebladerd kan worden. (Er zijn geen strikte criteria voor een bron om deze vlag te retourneren.) |
De volgende codetests voor de vlag 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;
}
Als u wilt zoeken, roept u de methode IMFSourceReader::SetCurrentPosition aan, zoals wordt weergegeven in de volgende code.
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;
}
De eerste parameter geeft de tijdnotatie die u gebruikt om de zoekpositie op te geven. Alle mediabronnen in Media Foundation moeten 100-nanoseconde-eenheden ondersteunen, zoals aangegeven door de waarde GUID_NULL. De tweede parameter is een PROPVARIANT die de zoekpositie bevat. Voor tijdseenheden van 100 nanoseconden is het gegevenstype LONGLONG-.
Houd er rekening mee dat niet elke mediabron frame-per-frame nauwkeurig zoeken biedt. De nauwkeurigheid van het zoeken is afhankelijk van verschillende factoren, zoals het sleutelframeinterval, of het mediabestand een index bevat en of de gegevens een constante of variabele bitsnelheid hebben. Daarom is er na het zoeken naar een positie in een bestand geen garantie dat het tijdstempel op het volgende voorbeeld exact overeenkomt met de aangevraagde positie. Over het algemeen is de werkelijke positie niet later dan de aangevraagde positie, zodat u voorbeelden kunt verwijderen totdat u het gewenste punt in de stroom bereikt.
Afspeelsnelheid
Hoewel u de afspeelsnelheid kunt instellen met behulp van de bronlezer, is dit meestal niet erg nuttig, om de volgende redenen:
- De bronlezer biedt geen ondersteuning voor omgekeerd afspelen, zelfs niet als de mediabron dat wel doet.
- De toepassing bepaalt de presentatietijden, zodat de toepassing snel of langzaam afspelen kan implementeren zonder de snelheid op de bron in te stellen.
- Sommige mediabronnen ondersteunen thinning modus, waarbij de bron minder voorbeelden levert, meestal alleen de sleutelframes. Als u echter niet-sleutelframes wilt verwijderen, kunt u elk voorbeeld controleren op het kenmerk MFSampleExtension_CleanPoint.
Als u de afspeelsnelheid wilt instellen met behulp van de bronlezer, roept u de methode IMFSourceReader::GetServiceForStream aan om de IMFRateSupport- en IMFRateControl interfaces van de mediabron op te halen.
Hardwareversnelling
De bronlezer is compatibel met Microsoft DirectX Video Acceleration (DXVA) 2.0 voor hardwareversnelde videodecodering. Voer de volgende stappen uit om DXVA te gebruiken met de bronlezer.
- Maak een Microsoft Direct3D-apparaat.
- Roep de DXVA2CreateDirect3DDeviceManager9 functie aan om direct3D-apparaatbeheer te maken. Deze functie krijgt een aanwijzer naar de IDirect3DDeviceManager9 interface.
- Roep de methode IDirect3DDeviceManager9::ResetDevice aan met een aanwijzer naar het Direct3D-apparaat.
- Maak een kenmerkarchief door de functie MFCreateAttributes aan te roepen.
- Maak de bronlezer. Geef het kenmerkarchief door in de pAttributes parameter van de aanmaakfunctie.
Wanneer u een Direct3D-apparaat opgeeft, wijst de bronlezer videovoorbeelden toe die compatibel zijn met de DXVA-videoprocessor-API. U kunt DXVA-videoverwerking gebruiken om hardwaredeinterlacing of videomixing uit te voeren. Zie DXVA Video Processingvoor meer informatie. Als de decoder DXVA 2.0 ondersteunt, wordt ook het Direct3D-apparaat gebruikt om hardware-versnelde decodering uit te voeren.
Belangrijk
Vanaf Windows 8 kan IMFDXGIDeviceManager- worden gebruikt in plaats van de IDirect3DDeviceManager9. Voor Windows Store-apps moet u IMFDXGIDeviceManagergebruiken. Zie de Direct3D 11 Video-API'svoor meer informatie.
Verwante onderwerpen