Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
[Bei dem dieser Seite zugeordneten Feature DirectShow handelt es sich um ein Legacyfeature. Es wurde von MediaPlayer, IMFMediaEngine sowie der Audio- und Videoaufnahme in Media Foundation abgelöst. Diese Features wurden für Windows 10 und Windows 11 optimiert. Microsoft empfiehlt für neuen Code nach Möglichkeit dringend die Verwendung von MediaPlayer, IMFMediaEngine sowie der Audio- und Videoaufnahme in Media Foundation anstelle von DirectShow. Microsoft schlägt vor, dass vorhandener Code, der die Legacy-APIs verwendet, wenn möglich umgeschrieben wird, um die neuen APIs zu verwenden.]
[Diese API wird nicht unterstützt und kann in Zukunft geändert oder nicht verfügbar sein.]
Der Beispiel-Grabber-Filter ist ein Transformationsfilter, mit dem Medienbeispiele aus einem Datenstrom abgerufen werden können, während sie das Filterdiagramm durchlaufen.
Wenn Sie einfach eine Bitmap aus einer Videodatei abrufen möchten, ist es einfacher, das Media Detector (MediaDet) -Objekt zu verwenden. Weitere Informationen finden Sie unter "Aufgreifen eines Posterrahmens ". Der Sample Grabber ist jedoch flexibler, da er mit nahezu jedem Medientyp funktioniert (siehe ISampleGrabber::SetMediaType für die wenigen Ausnahmen), und bietet der Anwendung mehr Kontrolle.
- Erstellen des Filterdiagramm-Managers
- Hinzufügen des Beispiel-Grabbers zum Filterdiagramm
- Festlegen des Medientyps
- Erstellen des Filterdiagramms
- Ausführen von Graph
- Beispiel suchen
- Beispielcode-
- Verwandte Themen
Erstellen des Filterdiagramm-Managers
Erstellen Sie zunächst den Filter-Graph-Manager und fragen Sie die IMediaControl- und IMediaEventEx-Schnittstellen ab.
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;
}
Hinzufügen des Beispiel-Grabbers zum Filterdiagramm
Erstellen Sie eine Instanz des Sample Grabber-Filters, und fügen Sie ihn dem Filterdiagramm hinzu. Abfragen des Sample Grabber-Filters für die ISampleGrabber-Schnittstelle .
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;
}
Festlegen des Medientyps
Wenn Sie den Sample Grabber zum ersten Mal erstellen, hat er keinen bevorzugten Medientyp. Dies bedeutet, dass Sie eine Verbindung mit fast jedem Filter im Diagramm herstellen können, aber Sie hätten keine Kontrolle über den Typ der empfangenen Daten. Bevor Sie den Rest des Diagramms erstellen, müssen Sie daher einen Medientyp für den Sample Grabber festlegen, indem Sie die ISampleGrabber::SetMediaType-Methode aufrufen.
Wenn der Sample Grabber eine Verbindung herstellt, wird dieser Medientyp mit dem medientyp verglichen, der vom anderen Filter angeboten wird. Die einzigen Felder, die überprüft werden, sind der Haupttyp, Untertyp und Formattyp. Für jeden dieser Werte bedeutet der Wert GUID_NULL "beliebige Werte akzeptieren". Meistens möchten Sie den Haupttyp und untertyp festlegen. Der folgende Code gibt beispielsweise unkomprimierte 24-Bit-RGB-Video an:
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;
}
Erstellen des Filterdiagramms
Jetzt können Sie den Rest des Filterdiagramms erstellen. Da der Sample Grabber nur mithilfe des von Ihnen angegebenen Medientyps eine Verbindung herstellt, können Sie die Intelligenten Connect-Mechanismen des Filter Graph-Managers nutzen, wenn Sie das Diagramm erstellen.
Wenn Sie z. B. unkomprimierte Videos angegeben haben, können Sie einen Quellfilter mit dem Sample Grabber verbinden, und der Filter Graph-Manager fügt automatisch den Dateiparser und den Decoder hinzu. Im folgenden Beispiel wird die Hilfsfunktion ConnectFilters verwendet, die in "Zwei Filter verbinden" aufgeführt ist:
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;
}
Der Sample Grabber ist ein Transformationsfilter, so muss der Ausgangspin mit einem anderen Filter verbunden sein. Häufig möchten Sie die Beispiele einfach verwerfen, nachdem Sie mit ihnen fertig sind. Verbinden Sie in diesem Fall den Sample Grabber mit dem Null-Rendererfilter, der die empfangenen Daten verwirft.
Im folgenden Beispiel wird der Sample Grabber mit dem Null-Renderer-Filter verbunden:
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;
}
Beachten Sie, dass das Platzieren des Sample Grabbers zwischen einem Videodecoder und dem Videorenderer die Renderingleistung erheblich beeinträchtigen kann. Der Sample Grabber ist ein trans-in-place-Filter, was bedeutet, dass der Ausgabepuffer mit dem Eingabepuffer identisch ist. Für das Videorendering befindet sich der Ausgabepuffer wahrscheinlich auf der Grafikkarte, bei der Lesevorgänge im Vergleich zu Lesevorgängen im Hauptspeicher viel langsamer sind.
Ausführen von Graph
Der Sample Grabber wird in einem von zwei Modi ausgeführt:
- Der Puffer-Modus erstellt eine Kopie jeder Probe, bevor die Probe an den nachfolgenden Prozess geliefert wird.
- Der Rückrufmodus ruft in jedem Beispiel eine anwendungsdefinierte Rückruffunktion auf.
In diesem Artikel wird der Puffermodus beschrieben. (Bevor Sie den Rückrufmodus verwenden, müssen Sie beachten, dass die Rückruffunktion ziemlich begrenzt sein muss. Andernfalls kann sie die Leistung drastisch reduzieren oder sogar Deadlocks verursachen. Weitere Informationen finden Sie unter "ISampleGrabber::SetCallback".) Rufen Sie zum Aktivieren des Puffermodus die ISampleGrabber::SetBufferSamples-Methode mit dem Wert TRUE auf.
Rufen Sie optional die ISampleGrabber::SetOneShot-Methode mit dem Wert TRUE auf. Dies bewirkt, dass der Sample Grabber angehalten wird, nachdem es das erste Medienbeispiel empfängt. Dies ist nützlich, wenn Sie einen einzelnen Frame aus dem Datenstrom erfassen möchten. Suchen Sie nach der gewünschten Zeit, führen Sie das Diagramm aus, und warten Sie auf das EC_COMPLETE-Ereignis . Beachten Sie, dass die Framegenauigkeitsebene von der Quelle abhängt. Die Suche nach einer MPEG-Datei ist z. B. häufig nicht framegenau.
Um das Diagramm so schnell wie möglich laufen zu lassen, deaktivieren Sie die Diagrammuhr, wie unter Festlegen der Diagrammuhr beschrieben.
Im folgenden Beispiel werden der Einzelbetrieb und der Puffermodus aktiviert, der Filtergraph ausgeführt und auf den Abschluss gewartet.
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);
Probe nehmen
Im Puffermodus speichert der Sample Grabber eine Kopie jedes Beispiels. Die ISampleGrabber::GetCurrentBuffer-Methode kopiert den Puffer in ein vom Aufrufer zugewiesenes Array. Um die Größe des erforderlichen Arrays zu ermitteln, rufen Sie zuerst GetCurrentBuffer mit einem NULL-Zeiger für die Arrayadresse auf. Weisen Sie dann das Array zu, und rufen Sie die Methode ein zweites Mal auf, um den Puffer zu kopieren. Das folgende Beispiel zeigt diese Schritte.
// 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;
}
Sie müssen das genaue Format der Daten im Puffer kennen. Rufen Sie zum Abrufen dieser Informationen die ISampleGrabber::GetConnectedMediaType-Methode auf. Diese Methode füllt eine AM_MEDIA_TYPE Struktur mit dem Format aus.
Bei einem nicht komprimierten Videostream sind die Formatinformationen in einer VIDEOINFOHEADER-Struktur enthalten. Das folgende Beispiel zeigt, wie Die Formatinformationen für einen nicht komprimierten Videostream abgerufen werden.
// 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;
}
Hinweis
Der Sample Grabber unterstützt VIDEOINFOHEADER2 nicht.
Beispielcode
Hier sehen Sie den vollständigen Code für die vorherigen Beispiele.
Hinweis
In diesem Beispiel wird die SafeRelease--Funktion verwendet, um Schnittstellenzeiger freizugeben.
#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;
}
Zugehörige Themen