Freigeben über


Verwenden des Beispielgrabbers

[Das dieser Seite zugeordnete Feature DirectShow ist ein Legacyfeature. Es wurde durch MediaPlayer, IMFMediaEngine und Audio/Video Capture in Media Foundation ersetzt. Diese Features wurden für Windows 10 und Windows 11 optimiert. Microsoft empfiehlt dringend, dass neuer Code nach Möglichkeit MediaPlayer, IMFMediaEngine und Audio/Video Capture in Media Foundation anstelle von DirectShow verwendet. Microsoft schlägt vor, vorhandenen Code, der die Legacy-APIs verwendet, um nach Möglichkeit die neuen APIs zu verwenden.]

[Diese API wird nicht unterstützt und kann in Zukunft geändert oder nicht mehr verfügbar sein.]

Der Filter Sample Grabber ist ein Transformationsfilter, mit dem Medienbeispiele aus einem Stream 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 -Objekt (MediaDet) zu verwenden. Weitere Informationen finden Sie unter Greifen eines Posterrahmens . Der Beispielgrabber ist jedoch flexibler, da er mit fast jedem Medientyp funktioniert (siehe ISampleGrabber::SetMediaType für die wenigen Ausnahmen), und bietet mehr Kontrolle für die Anwendung.

Erstellen des Filtergraph-Managers

Erstellen Sie zunächst den Filter Graph-Manager , und fragen Sie die Schnittstellen IMediaControl und IMediaEventEx 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 Beispielgrabbers zum Filterdiagramm

Erstellen Sie eine instance des Beispielgrabberfilters, und fügen Sie sie dem Filterdiagramm hinzu. Fragen Sie den Beispielgrabberfilter für die ISampleGrabber-Schnittstelle ab.

    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 Beispielgrabber zum ersten Mal erstellen, weist er keinen bevorzugten Medientyp auf. 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 Graphen erstellen, müssen Sie daher einen Medientyp für den Sample Grabber festlegen, indem Sie die ISampleGrabber::SetMediaType-Methode aufrufen.

Wenn der Beispielgrabber eine Verbindung herstellt, vergleicht er diesen Medientyp mit dem vom anderen Filter angebotenen Medientyp. Die einzigen Felder, die überprüft werden, sind der Haupttyp, der Untertyp und der Formattyp. Für jede dieser Werte bedeutet der Wert GUID_NULL "beliebige Werte akzeptieren". In den meisten Jahren möchten Sie den Haupt- und Untertyp festlegen. Der folgende Code gibt beispielsweise unkomprimiertes 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 Beispielgrabber nur eine Verbindung mit dem von Ihnen angegebenen Medientyp herstellt, können Sie beim Erstellen des Diagramms die Intelligenten Verbindungsmechanismen des Filter Graph-Managers nutzen.

Wenn Sie beispielsweise unkomprimiertes Video angegeben haben, können Sie einen Quellfilter mit dem Beispielgrabber verbinden, und der Filter Graph-Manager fügt automatisch den Dateiparser und den Decoder hinzu. Im folgenden Beispiel wird die Hilfsfunktion ConnectFilters verwendet, die unter Verbinden von zwei Filtern 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 Beispielgrabber ist ein Transformationsfilter, sodass der Ausgabenadel mit einem anderen Filter verbunden sein muss. Häufig möchten Sie die Beispiele einfach verwerfen, nachdem Sie mit ihnen fertig sind. Verbinden Sie in diesem Fall den Beispielgrabber mit dem Null-Rendererfilter, wodurch die empfangenen Daten verworfen werden.

Im folgenden Beispiel wird der Beispielgrabber mit dem Filter Null-Renderer 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 Beispielgrabbers zwischen einem Videodecoder und dem Videorenderer die Renderingleistung erheblich beeinträchtigen kann. Der Beispielgrabber ist ein trans-in-situ-Filter, was bedeutet, dass der Ausgabepuffer mit dem Eingabepuffer identisch ist. Beim Videorendering befindet sich der Ausgabepuffer wahrscheinlich auf der Grafik Karte, wobei Lesevorgänge im Vergleich zu Lesevorgängen in Standard Arbeitsspeicher viel langsamer sind.

Ausführen des Graphen

Der Beispielgrabber arbeitet in einem von zwei Modi:

  • Der Puffermodus erstellt eine Kopie jedes Beispiels, bevor das Beispiel nachgeschaltet wird.
  • Im Rückrufmodus wird für jedes Beispiel eine anwendungsdefinierte Rückruffunktion aufgerufen.

In diesem Artikel wird der Puffermodus beschrieben. (Beachten Sie vor der Verwendung des Rückrufmodus, dass die Rückruffunktion recht eingeschränkt sein muss. Andernfalls kann dies die Leistung drastisch reduzieren oder sogar Deadlocks verursachen. Weitere Informationen finden Sie unter ISampleGrabber::SetCallback.) Um den Puffermodus zu aktivieren, rufen Sie die ISampleGrabber::SetBufferSamples-Methode mit dem Wert TRUE auf.

Rufen Sie optional die ISampleGrabber::SetOneShot-Methode mit dem Wert TRUE auf. Dies führt dazu, dass der Beispielgrabber angehalten wird, nachdem er das erste Medienbeispiel empfangen hat. Dies ist nützlich, wenn Sie einen einzelnen Frame aus dem Stream abrufen 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 Framegenauigkeit von der Quelle abhängt. Beispielsweise ist die Suche nach einer MPEG-Datei oft nicht framegenau.

Um das Diagramm so schnell wie möglich auszuführen, deaktivieren Sie die Graphuhr, wie unter Festlegen der Graphuhr beschrieben.

Das folgende Beispiel aktiviert den One-Shot-Modus und den Puffermodus, führt das Filterdiagramm aus und wartet auf die Fertigstellung.

    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);

Beispiel abrufen

Im Puffermodus speichert der Beispielgrabber 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 bestimmen, rufen Sie zuerst GetCurrentBuffer mit einem NULL-Zeiger für die Arrayadresse auf. Ordnen Sie dann das Array zu, und rufen Sie die Methode ein zweites Mal auf, um den Puffer zu kopieren. Das folgende Beispiel zeigt die erforderlichen 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 Sie die Formatinformationen für einen nicht komprimierten Videostream abrufen.

    // 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 Beispielgrabber unterstützt VIDEOINFOHEADER2 nicht.

 

Beispielcode

Hier finden 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&quot;Sample Grabber&quot;);
    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&quot;Source&quot;, &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&quot;Null Filter&quot;);
    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 = &#39;MB&#39;;
    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;
}

Verwenden von DirectShow-Bearbeitungsdiensten