Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
[De functie die is gekoppeld aan deze pagina, DirectShow, is een verouderde functie. Het is vervangen door MediaPlayer, IMFMediaEngineen Audio/Video Capture in Media Foundation. Deze functies zijn geoptimaliseerd voor Windows 10 en Windows 11. Microsoft raadt ten zeerste aan om nieuwe code te gebruiken MediaPlayer, IMFMediaEngine en Audio/Video Capture in Media Foundation in plaats van DirectShow, indien mogelijk. Microsoft stelt voor dat bestaande code die gebruikmaakt van de verouderde API's, indien mogelijk opnieuw worden geschreven om de nieuwe API's te gebruiken.]
[Deze API wordt niet ondersteund en kan in de toekomst worden gewijzigd of niet beschikbaar.]
Het filter Sample Grabber is een transformatiefilter dat kan worden gebruikt voor het ophalen van mediavoorbeelden uit een stroom wanneer ze de filtergrafiek passeren.
Als u gewoon een bitmap uit een videobestand wilt ophalen, is het eenvoudiger om het Media Detector (MediaDet) object te gebruiken. Zie Pak een posterframe voor meer informatie. De Sample Grabber is echter flexibeler, omdat het werkt met vrijwel elk mediatype (zie ISampleGrabber::SetMediaType voor de paar uitzonderingen) en biedt meer controle over de toepassing.
- De Filter Graph Manager maken
- Voeg de voorbeeld-grabber toe aan de filtergrafiek
- Het mediatype instellen
- De filtergrafiek maken
- Graph uitvoeren
- Pak het monster
- voorbeeldcode
- Verwante onderwerpen
Filter Graph Manager maken
Maak eerst de Filter Graph Manager- en voer een query uit voor de IMediaControl-- en IMediaEventEx--interfaces.
IGraphBuilder *pGraph = NULL;
IMediaControl *pControl = NULL;
IMediaEventEx *pEvent = NULL;
HRESULT hr = CoCreateInstance(CLSID_FilterGraph, NULL,
CLSCTX_INPROC_SERVER,IID_PPV_ARGS(&pGraph));
if (FAILED(hr))
{
goto done;
}
hr = pGraph->QueryInterface(IID_PPV_ARGS(&pControl));
if (FAILED(hr))
{
goto done;
}
hr = pGraph->QueryInterface(IID_PPV_ARGS(&pEvent));
if (FAILED(hr))
{
goto done;
}
De Sample Grabber toevoegen aan de Filter Graph
Maak een exemplaar van het sample grabber-filter en voeg dit toe aan de filtergrafiek. Voer een query uit op het filter Sample Grabber voor de ISampleGrabber-interface.
IBaseFilter *pGrabberF = NULL;
ISampleGrabber *pGrabber = NULL;
// Create the Sample Grabber filter.
hr = CoCreateInstance(CLSID_SampleGrabber, NULL, CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&pGrabberF));
if (FAILED(hr))
{
goto done;
}
hr = pGraph->AddFilter(pGrabberF, L"Sample Grabber");
if (FAILED(hr))
{
goto done;
}
hr = pGrabberF->QueryInterface(IID_PPV_ARGS(&pGrabber));
if (FAILED(hr))
{
goto done;
}
Het mediatype instellen
Wanneer u de Sample Grabber voor het eerst maakt, heeft het geen voorkeursmediatype. Dit betekent dat u verbinding kunt maken met bijna elk filter in de grafiek, maar u hebt geen controle over het type gegevens dat het heeft ontvangen. Voordat u de rest van de grafiek bouwt, moet u daarom een mediatype instellen voor de Sample Grabber door de methode ISampleGrabber::SetMediaType aan te roepen.
Wanneer de Sample Grabber verbinding maakt, wordt dit mediatype vergeleken met het mediatype dat door het andere filter wordt aangeboden. De enige velden die worden gecontroleerd, zijn het primaire type, subtype en opmaaktype. Voor een van deze waarden betekent de waarde GUID_NULL 'elke waarde accepteren'. Meestal wilt u het primaire type en subtype instellen. Met de volgende code geeft u bijvoorbeeld niet-gecomprimeerde 24-bits RGB-video op:
AM_MEDIA_TYPE mt;
ZeroMemory(&mt, sizeof(mt));
mt.majortype = MEDIATYPE_Video;
mt.subtype = MEDIASUBTYPE_RGB24;
hr = pGrabber->SetMediaType(&mt);
if (FAILED(hr))
{
goto done;
}
De filtergrafiek bouwen
U kunt nu de rest van de filtergrafiek bouwen. Omdat de sample grabber alleen verbinding maakt met het mediatype dat u hebt opgegeven, kunt u hiermee profiteren van de Intelligent Connect mechanismen van Filter Graph Manager wanneer u de grafiek bouwt.
Als u bijvoorbeeld niet-gecomprimeerde video hebt opgegeven, kunt u een bronfilter verbinden met de Sample Grabber, waarna filtergrafiekbeheer automatisch de bestandsparser en de decoder toevoegt. In het volgende voorbeeld wordt de helperfunctie ConnectFilters gebruikt, die wordt vermeld in Twee filters verbinden:
IBaseFilter *pSourceF = NULL;
IEnumPins *pEnum = NULL;
IPin *pPin = NULL;
hr = pGraph->AddSourceFilter(pszVideoFile, L"Source", &pSourceF);
if (FAILED(hr))
{
goto done;
}
hr = pSourceF->EnumPins(&pEnum);
if (FAILED(hr))
{
goto done;
}
while (S_OK == pEnum->Next(1, &pPin, NULL))
{
hr = ConnectFilters(pGraph, pPin, pGrabberF);
SafeRelease(&pPin);
if (SUCCEEDED(hr))
{
break;
}
}
if (FAILED(hr))
{
goto done;
}
De Sample Grabber is een transformatie filter, dus de outputpin moet verbonden worden met een ander filter. Vaak wilt u de voorbeelden verwijderen nadat u er klaar mee bent. In dat geval verbindt u de Sample Grabber met het Null Renderer-filter, die de ontvangen gegevens verwijdert.
In het volgende voorbeeld wordt de sample grabber verbonden met het filter Null Renderer:
IBaseFilter *pNullF = NULL;
hr = CoCreateInstance(CLSID_NullRenderer, NULL, CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&pNullF));
if (FAILED(hr))
{
goto done;
}
hr = pGraph->AddFilter(pNullF, L"Null Filter");
if (FAILED(hr))
{
goto done;
}
hr = ConnectFilters(pGraph, pGrabberF, pNullF);
if (FAILED(hr))
{
goto done;
}
Houd er rekening mee dat het plaatsen van de Sample Grabber tussen een videodecoder en de video-renderer de renderingprestaties aanzienlijk kan schaden. De Sample Grabber is een trans-in-place filter, wat betekent dat de uitvoerbuffer hetzelfde is als de invoerbuffer. Voor videorendering bevindt de uitvoerbuffer zich waarschijnlijk op de grafische kaart, waarbij leesbewerkingen veel trager zijn, vergeleken met leesbewerkingen in het hoofdgeheugen.
De grafiek uitvoeren
De Sample Grabber werkt in een van de twee modi:
- De buffermodus maakt een kopie van elk voorbeeld voordat het voorbeeld downstream wordt geleverd.
- In de callbackmodus wordt een door de toepassing gedefinieerde callback-functie aangeroepen voor elk voorbeeld.
In dit artikel wordt de buffermodus beschreven. (Voordat u de callback-modus gebruikt, moet u er rekening mee houden dat de callback-functie vrij beperkt moet zijn. Anders kan het de prestaties drastisch verminderen of zelfs impasses veroorzaken. Zie ISampleGrabber::SetCallbackvoor meer informatie.) Als u de buffermodus wilt activeren, roept u de ISampleGrabber::SetBufferSamples methode aan met de waarde TRUE-.
Roep desgewenst de methode ISampleGrabber::SetOneShot aan met de waarde TRUE. Hierdoor stopt de Sample Grabber nadat het eerste mediavoorbeeld is ontvangen. Dit is handig als u één frame uit de stream wilt halen. Zoek de gewenste tijd, voer de grafiek uit en wacht op de EC_COMPLETE gebeurtenis. Houd er rekening mee dat het nauwkeurigheidsniveau van het frame afhankelijk is van de bron. Het zoeken naar een MPEG-bestand is bijvoorbeeld vaak niet frame-nauwkeurig.
Als u de grafiek zo snel mogelijk wilt uitvoeren, schakelt u de grafiekklok uit zoals beschreven in De grafiekklok instellen.
In het volgende voorbeeld wordt de modus voor één opname en buffermodus ingeschakeld, wordt de filtergrafiek uitgevoerd en wordt gewacht op voltooiing.
hr = pGrabber->SetOneShot(TRUE);
if (FAILED(hr))
{
goto done;
}
hr = pGrabber->SetBufferSamples(TRUE);
if (FAILED(hr))
{
goto done;
}
hr = pControl->Run();
if (FAILED(hr))
{
goto done;
}
long evCode;
hr = pEvent->WaitForCompletion(INFINITE, &evCode);
Pak het voorbeeld
In de buffermodus slaat de Sample Grabber een kopie van elk voorbeeld op. De ISampleGrabber::GetCurrentBuffer methode kopieert de buffer naar een aanroeper toegewezen matrix. Als u de grootte van de benodigde matrix wilt bepalen, roept u eerst GetCurrentBuffer- aan met een NULL- aanwijzer voor het matrixadres. Wijs vervolgens de matrix toe en roep de methode een tweede keer aan om de buffer te kopiëren. In het volgende voorbeeld ziet u deze stappen.
// Find the required buffer size.
long cbBuffer;
hr = pGrabber->GetCurrentBuffer(&cbBuffer, NULL);
if (FAILED(hr))
{
goto done;
}
pBuffer = (BYTE*)CoTaskMemAlloc(cbBuffer);
if (!pBuffer)
{
hr = E_OUTOFMEMORY;
goto done;
}
hr = pGrabber->GetCurrentBuffer(&cbBuffer, (long*)pBuffer);
if (FAILED(hr))
{
goto done;
}
U moet de exacte indeling van de gegevens in de buffer weten. Als u deze informatie wilt ophalen, roept u de methode ISampleGrabber::GetConnectedMediaType aan. Met deze methode wordt een AM_MEDIA_TYPE structuur ingevuld met het formaat.
Voor een niet-gecomprimeerde videostream is de indelingsinformatie opgenomen in een VIDEOINFOHEADER structuur. In het volgende voorbeeld ziet u hoe u de indelingsgegevens voor een niet-gecomprimeerde videostream kunt ophalen.
// Examine the format block.
if ((mt.formattype == FORMAT_VideoInfo) &&
(mt.cbFormat >= sizeof(VIDEOINFOHEADER)) &&
(mt.pbFormat != NULL))
{
VIDEOINFOHEADER *pVih = (VIDEOINFOHEADER*)mt.pbFormat;
hr = WriteBitmap(pszBitmapFile, &pVih->bmiHeader,
mt.cbFormat - SIZE_PREHEADER, pBuffer, cbBuffer);
}
else
{
// Invalid format.
hr = VFW_E_INVALIDMEDIATYPE;
}
Notitie
De Sample Grabber biedt geen ondersteuning voor VIDEOINFOHEADER2.
Voorbeeldcode
Hier volgt de volledige code voor de vorige voorbeelden.
Notitie
In dit voorbeeld wordt de functie SafeRelease gebruikt om interfaceaanwijzers vrij te geven.
#include <windows.h>
#include <dshow.h>
#include "qedit.h"
template <class T> void SafeRelease(T **ppT)
{
if (*ppT)
{
(*ppT)->Release();
*ppT = NULL;
}
}
HRESULT WriteBitmap(PCWSTR, BITMAPINFOHEADER*, size_t, BYTE*, size_t);
HRESULT GrabVideoBitmap(PCWSTR pszVideoFile, PCWSTR pszBitmapFile)
{
IGraphBuilder *pGraph = NULL;
IMediaControl *pControl = NULL;
IMediaEventEx *pEvent = NULL;
IBaseFilter *pGrabberF = NULL;
ISampleGrabber *pGrabber = NULL;
IBaseFilter *pSourceF = NULL;
IEnumPins *pEnum = NULL;
IPin *pPin = NULL;
IBaseFilter *pNullF = NULL;
BYTE *pBuffer = NULL;
HRESULT hr = CoCreateInstance(CLSID_FilterGraph, NULL,
CLSCTX_INPROC_SERVER,IID_PPV_ARGS(&pGraph));
if (FAILED(hr))
{
goto done;
}
hr = pGraph->QueryInterface(IID_PPV_ARGS(&pControl));
if (FAILED(hr))
{
goto done;
}
hr = pGraph->QueryInterface(IID_PPV_ARGS(&pEvent));
if (FAILED(hr))
{
goto done;
}
// Create the Sample Grabber filter.
hr = CoCreateInstance(CLSID_SampleGrabber, NULL, CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&pGrabberF));
if (FAILED(hr))
{
goto done;
}
hr = pGraph->AddFilter(pGrabberF, L"Sample Grabber");
if (FAILED(hr))
{
goto done;
}
hr = pGrabberF->QueryInterface(IID_PPV_ARGS(&pGrabber));
if (FAILED(hr))
{
goto done;
}
AM_MEDIA_TYPE mt;
ZeroMemory(&mt, sizeof(mt));
mt.majortype = MEDIATYPE_Video;
mt.subtype = MEDIASUBTYPE_RGB24;
hr = pGrabber->SetMediaType(&mt);
if (FAILED(hr))
{
goto done;
}
hr = pGraph->AddSourceFilter(pszVideoFile, L"Source", &pSourceF);
if (FAILED(hr))
{
goto done;
}
hr = pSourceF->EnumPins(&pEnum);
if (FAILED(hr))
{
goto done;
}
while (S_OK == pEnum->Next(1, &pPin, NULL))
{
hr = ConnectFilters(pGraph, pPin, pGrabberF);
SafeRelease(&pPin);
if (SUCCEEDED(hr))
{
break;
}
}
if (FAILED(hr))
{
goto done;
}
hr = CoCreateInstance(CLSID_NullRenderer, NULL, CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&pNullF));
if (FAILED(hr))
{
goto done;
}
hr = pGraph->AddFilter(pNullF, L"Null Filter");
if (FAILED(hr))
{
goto done;
}
hr = ConnectFilters(pGraph, pGrabberF, pNullF);
if (FAILED(hr))
{
goto done;
}
hr = pGrabber->SetOneShot(TRUE);
if (FAILED(hr))
{
goto done;
}
hr = pGrabber->SetBufferSamples(TRUE);
if (FAILED(hr))
{
goto done;
}
hr = pControl->Run();
if (FAILED(hr))
{
goto done;
}
long evCode;
hr = pEvent->WaitForCompletion(INFINITE, &evCode);
// Find the required buffer size.
long cbBuffer;
hr = pGrabber->GetCurrentBuffer(&cbBuffer, NULL);
if (FAILED(hr))
{
goto done;
}
pBuffer = (BYTE*)CoTaskMemAlloc(cbBuffer);
if (!pBuffer)
{
hr = E_OUTOFMEMORY;
goto done;
}
hr = pGrabber->GetCurrentBuffer(&cbBuffer, (long*)pBuffer);
if (FAILED(hr))
{
goto done;
}
hr = pGrabber->GetConnectedMediaType(&mt);
if (FAILED(hr))
{
goto done;
}
// Examine the format block.
if ((mt.formattype == FORMAT_VideoInfo) &&
(mt.cbFormat >= sizeof(VIDEOINFOHEADER)) &&
(mt.pbFormat != NULL))
{
VIDEOINFOHEADER *pVih = (VIDEOINFOHEADER*)mt.pbFormat;
hr = WriteBitmap(pszBitmapFile, &pVih->bmiHeader,
mt.cbFormat - SIZE_PREHEADER, pBuffer, cbBuffer);
}
else
{
// Invalid format.
hr = VFW_E_INVALIDMEDIATYPE;
}
FreeMediaType(mt);
done:
CoTaskMemFree(pBuffer);
SafeRelease(&pPin);
SafeRelease(&pEnum);
SafeRelease(&pNullF);
SafeRelease(&pSourceF);
SafeRelease(&pGrabber);
SafeRelease(&pGrabberF);
SafeRelease(&pControl);
SafeRelease(&pEvent);
SafeRelease(&pGraph);
return hr;
};
// Writes a bitmap file
// pszFileName: Output file name.
// pBMI: Bitmap format information (including pallete).
// cbBMI: Size of the BITMAPINFOHEADER, including palette, if present.
// pData: Pointer to the bitmap bits.
// cbData Size of the bitmap, in bytes.
HRESULT WriteBitmap(PCWSTR pszFileName, BITMAPINFOHEADER *pBMI, size_t cbBMI,
BYTE *pData, size_t cbData)
{
HANDLE hFile = CreateFile(pszFileName, GENERIC_WRITE, 0, NULL,
CREATE_ALWAYS, 0, NULL);
if (hFile == NULL)
{
return HRESULT_FROM_WIN32(GetLastError());
}
BITMAPFILEHEADER bmf = { };
bmf.bfType = 'MB';
bmf.bfSize = cbBMI+ cbData + sizeof(bmf);
bmf.bfOffBits = sizeof(bmf) + cbBMI;
DWORD cbWritten = 0;
BOOL result = WriteFile(hFile, &bmf, sizeof(bmf), &cbWritten, NULL);
if (result)
{
result = WriteFile(hFile, pBMI, cbBMI, &cbWritten, NULL);
}
if (result)
{
result = WriteFile(hFile, pData, cbData, &cbWritten, NULL);
}
HRESULT hr = result ? S_OK : HRESULT_FROM_WIN32(GetLastError());
CloseHandle(hFile);
return hr;
}
Verwante onderwerpen