Compartir a través de


Uso del agarrador de ejemplo

[La característica asociada a esta página, DirectShow, es una característica heredada. Se ha reemplazado por MediaPlayer, IMFMediaEngine y Captura de audio/vídeo en Media Foundation. Esas características se han optimizado para Windows 10 y Windows 11. Microsoft recomienda encarecidamente que el nuevo código use MediaPlayer, IMFMediaEngine y Audio/Video Capture en Media Foundation en lugar de DirectShow, siempre que sea posible. Microsoft sugiere que el código existente que usa las API heredadas se reescriba para usar las nuevas API si es posible.

[Esta API no se admite y puede modificarse o no estar disponible en el futuro].

El filtro Sample Grabber es un filtro de transformación que se puede usar para obtener muestras multimedia de una secuencia a medida que pasan por el gráfico de filtros.

Si simplemente desea obtener un mapa de bits de un archivo de vídeo, es más fácil usar el objeto Media Detector (MediaDet). Para obtener más información, consulte Captura de un marco de póster . Sample Grabber es más flexible, sin embargo, porque funciona con casi cualquier tipo de medio (vea ISampleGrabber::SetMediaType para las pocas excepciones) y ofrece más control a la aplicación.

Creación del Administrador de gráficos de filtros

Para empezar, cree el Administrador de gráficos de filtros y consulte las interfaces IMediaControl e IMediaEventEx .

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

Agregar el agarrador de ejemplo al gráfico de filtros

Cree una instancia del filtro Sample Grabber y agregueit al gráfico de filtros. Consulte el filtro Sample Grabber para la interfaz 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;
    }

Establecer el tipo de medio

Al crear por primera vez sample Grabber, no tiene ningún tipo de medio preferido. Esto significa que puede conectarse a casi cualquier filtro del grafo, pero no tendría ningún control sobre el tipo de datos que recibió. Antes de compilar el resto del grafo, por lo tanto, debe establecer un tipo de medio para sample Grabber mediante una llamada al método ISampleGrabber::SetMediaType .

Cuando se conecta Sample Grabber, comparará este tipo de medio con el tipo de medio ofrecido por el otro filtro. Los únicos campos que comprueba son el tipo principal, el subtipo y el tipo de formato. Para cualquiera de ellos, el valor GUID_NULL significa "aceptar cualquier valor". La mayoría de las veces, querrá establecer el tipo principal y el subtipo. Por ejemplo, el código siguiente especifica vídeo RGB sin comprimir de 24 bits:

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

Compilación del gráfico de filtros

Ahora puede compilar el resto del gráfico de filtros. Dado que sample Grabber solo se conectará con el tipo de medio especificado, esto le permite aprovechar los mecanismos de Conexión inteligente de Filter Graph Manager al compilar el grafo.

Por ejemplo, si especificó vídeo sin comprimir, puede conectar un filtro de origen a Sample Grabber y filter Graph Manager agregará automáticamente el analizador de archivos y el descodificador. En el ejemplo siguiente se usa la función auxiliar ConnectFilters, que aparece en Conectar dos filtros:

    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 es un filtro de transformación, por lo que el pin de salida debe estar conectado a otro filtro. A menudo, es posible que simplemente quiera descartar las muestras una vez que haya terminado con ellos. En ese caso, conecte sample Grabber al filtro representador null, que descarta los datos que recibe.

En el ejemplo siguiente se conecta Sample Grabber al filtro Representador null:

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

Tenga en cuenta que colocar sample Grabber entre un descodificador de vídeo y el representador de vídeo puede dañar significativamente el rendimiento de la representación. Sample Grabber es un filtro trans-in-place, lo que significa que el búfer de salida es el mismo que el búfer de entrada. Para la representación de vídeo, es probable que el búfer de salida se encuentre en la tarjeta gráfica, donde las operaciones de lectura son mucho más lentas, en comparación con las operaciones de lectura en la memoria principal.

Ejecución del grafo

Sample Grabber funciona en uno de los dos modos:

  • El modo de almacenamiento en búfer realiza una copia de cada ejemplo antes de entregar el ejemplo de bajada.
  • El modo de devolución de llamada invoca una función de devolución de llamada definida por la aplicación en cada ejemplo.

En este artículo se describe el modo de almacenamiento en búfer. (Antes de usar el modo de devolución de llamada, tenga en cuenta que la función de devolución de llamada debe estar bastante limitada. De lo contrario, puede reducir drásticamente el rendimiento o incluso provocar interbloqueos. Para obtener más información, vea ISampleGrabber::SetCallback). Para activar el modo de almacenamiento en búfer, llame al método ISampleGrabber::SetBufferSamples con el valor TRUE.

Opcionalmente, llame al método ISampleGrabber::SetOneShot con el valor TRUE. Esto hace que sample Grabber se detenga después de recibir la primera muestra multimedia, lo que resulta útil si desea obtener un único fotograma de la secuencia. Busque el tiempo deseado, ejecute el gráfico y espere al evento EC_COMPLETE . Tenga en cuenta que el nivel de precisión del fotograma depende del origen. Por ejemplo, buscar un archivo MPEG a menudo no es preciso enmarcar.

Para ejecutar el gráfico lo más rápido posible, desactive el reloj del grafo como se describe en Configuración del reloj del grafo.

En el ejemplo siguiente se habilita el modo de captura única y el modo de almacenamiento en búfer, se ejecuta el gráfico de filtros y se espera la finalización.

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

Obtener el ejemplo

En el modo de almacenamiento en búfer, sample Grabber almacena una copia de cada ejemplo. El método ISampleGrabber::GetCurrentBuffer copia el búfer en una matriz asignada por el autor de la llamada. Para determinar el tamaño de la matriz necesaria, primero llame a GetCurrentBuffer con un puntero NULL para la dirección de la matriz. A continuación, asigne la matriz y llame al método una segunda vez para copiar el búfer. En el ejemplo siguiente se muestran estos pasos.

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

Deberá conocer el formato exacto de los datos en el búfer. Para obtener esta información, llame al método ISampleGrabber::GetConnectedMediaType . Este método rellena una estructura AM_MEDIA_TYPE con el formato .

Para una secuencia de vídeo sin comprimir, la información de formato se incluye en una estructura VIDEOINFOHEADER . En el ejemplo siguiente se muestra cómo obtener la información de formato de una secuencia de vídeo sin comprimir.

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

Nota:

Sample Grabber no admite VIDEOINFOHEADER2.

 

Código de ejemplo

Este es el código completo de los ejemplos anteriores.

Nota:

En este ejemplo se usa la función SafeRelease para liberar punteros de interfaz.

 

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

Uso de DirectShow Editing Services