Sdílet prostřednictvím


Použití nástroje Sample Grabber

[Funkce přidružená k této stránce DirectShow je starší funkce. Byla nahrazena nástroji MediaPlayer, IMFMediaEnginea Audio/Video Capture v Media Foundation. Tyto funkce jsou optimalizované pro Windows 10 a Windows 11. Microsoft důrazně doporučuje, aby nový kód používal MediaPlayer, MMFMediaEngine a Audio/Video Capture v Media Foundation místo DirectShow, pokud je to možné. Microsoft navrhuje, aby se stávající kód, který používá starší rozhraní API, přepsal, aby se nová rozhraní API používala, pokud je to možné.]

[Toto rozhraní API není podporováno a může být v budoucnu změněno nebo nedostupné.]

Ukázkový filtr Grabber je transformační filtr, který lze použít k získání vzorků médií z datového proudu při průchodu grafem filtru.

Pokud jednoduše chcete získat rastrový obrázek z videosouboru, je jednodušší použít objekt Media Detector (MediaDet ). Podrobnosti najdete v části Grabbing a Poster Frame. Sample Grabber je ale flexibilnější, protože funguje s téměř jakýmkoli typem média (viz ISampleGrabber::SetMediaType pro několik výjimek) a nabízí pro aplikaci větší kontrolu.

Vytvoření Správce grafů filtru

Začněte tím, že vytvoříte Správce grafů filtru a odešlete dotaz na rozhraní IMediaControl aIMediaEventEx .

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

Přidání Sample Grabberu do filtračního grafu

Vytvořte instanci filtru Sample Grabber a přidejte ji do grafu filtru. Zadejte dotaz na filtr Sample Grabber pro rozhraní ISampleGrabber .

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

Nastavení typu média

Při prvním vytvoření Sample Grabberu nemá žádný preferovaný typ média. To znamená, že se můžete připojit k téměř libovolnému filtru v grafu, ale nemáte žádnou kontrolu nad typem dat, která přijala. Před sestavením zbytku grafu proto musíte nastavit typ média pro Sample Grabber zavoláním metody ISampleGrabber::SetMediaType .

Když se Sample Grabber připojí, porovná tento typ média s typem média nabízeným jiným filtrem. Jedinými poli, která kontroluje, jsou hlavní typ, podtyp a typ formátu. Pro kteroukoli z těchto hodnot hodnota GUID_NULL znamená "přijmout libovolnou hodnotu". Ve většině případů budete chtít nastavit hlavní typ a podtyp. Například následující kód určuje nekomprimované 24bitové video RGB:

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

Vytvoření grafu filtru

Teď můžete sestavit zbytek grafu filtru. Vzhledem k tomu, že Sample Grabber se připojí pouze pomocí zadaného typu média, můžete při vytváření grafu využít mechanismy Inteligentního připojení Správce filtrů grafů.

Pokud jste například zadali nekomprimované video, můžete zdrojový filtr připojit k nástroji Sample Grabber a Správce grafů filtru automaticky přidá analyzátor souborů a dekodér. Následující příklad používá pomocnou funkci ConnectFilters, která je uvedena v Připojení dvou filtrů:

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

Sample Grabber je transformační filtr, takže výstupní špendlík musí být připojený k jinému filtru. Po dokončení práce s ukázkami můžete často chtít ukázky jednoduše zahodit. V takovém případě připojte Sample Grabber k filtru Null Renderer, který zahodí data, která obdrží.

Následující příklad připojí Sample Grabber k filtru 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;
    }

Mějte na paměti, že umístění nástroje Sample Grabber mezi dekodér videa a renderer videa může výrazně poškodit výkon vykreslování. Sample Grabber je trans-in-place filtr, což znamená, že výstupní vyrovnávací paměť je stejná jako vstupní vyrovnávací paměť. Při vykreslování videa bude výstupní vyrovnávací paměť pravděpodobně umístěna na grafické kartě, kde operace čtení jsou mnohem pomalejší než operace čtení v hlavní paměti.

Spuštění grafu

Sample Grabber funguje v jednom ze dvou režimů:

  • Režim ukládání do vyrovnávací paměti vytvoří kopii každého vzorku před jeho předáním dál v procesu.
  • Režim zpětného volání vyvolá u každé ukázky funkci zpětného volání definovanou aplikací.

Tento článek popisuje režim ukládání do vyrovnávací paměti. (Před použitím režimu zpětného volání mějte na paměti, že funkce zpětného volání musí být poměrně omezená. Jinak může výrazně snížit výkon nebo dokonce způsobit zablokování. Další informace naleznete v tématu ISampleGrabber::SetCallback.) Chcete-li aktivovat režim ukládání do vyrovnávací paměti, zavolejte ISampleGrabber::SetBufferSamples metoda s hodnotou TRUE.

Volitelně můžete zavolat metodu ISampleGrabber::SetOneShot s hodnotou TRUE. To způsobí, že se Sample Grabber zastaví, jakmile obdrží první mediální vzorek, což je užitečné, pokud chcete zachytit jeden snímek ze streamu. Vyhledejte požadovaný čas, spusťte graf a počkejte na EC_COMPLETE událost. Mějte na paměti, že úroveň přesnosti rámce závisí na zdroji. Hledání souboru MPEG například často není přesné.

Pokud chcete graf spustit co nejrychleji, vypněte hodiny grafu, jak je popsáno v části Nastavení hodin grafu.

Následující příklad umožňuje režim jednorázového snímku a režim ukládání do vyrovnávací paměti, spustí graf filtru a počká na dokončení.

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

Získání ukázky

V režimu ukládání do vyrovnávací paměti ukládá vzorkovač kopii každého vzorku. Metoda ISampleGrabber::GetCurrentBuffer zkopíruje vyrovnávací paměť do pole přiděleného volajícím. Chcete-li určit velikost pole, které je potřeba, nejprve zavolejte GetCurrentBuffer s ukazatelem NULL pro maticovou adresu. Pak přidělte pole a zavolejte metodu podruhé pro zkopírování vyrovnávací paměti. Následující příklad ukazuje tyto kroky.

    // 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;
    }

Budete muset znát přesný formát dat ve vyrovnávací paměti. Chcete-li získat tyto informace, zavolejte metodu ISampleGrabber::GetConnectedMediaType. Tato metoda vyplní strukturu AM_MEDIA_TYPE formátem.

U nekomprimovaného video streamu jsou informace o formátu obsaženy ve struktuře VIDEOINFOHEADER . Následující příklad ukazuje, jak získat informace o formátu pro nekomprimovaný video stream.

    // 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; 
    }

Poznámka:

Sample Grabber nepodporuje VIDEOINFOHEADER2.

 

Příklad kódu

Tady je úplný kód pro předchozí příklady.

Poznámka:

Tento příklad používá funkci SafeRelease k uvolnění ukazatelů rozhraní.

 

#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 = &#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;
}

Použití služeb úprav DirectShow