Поделиться через


Захват изображения из закрепления неподвижного изображения

[Функция, связанная с этой страницей DirectShow, является устаревшей функцией. Он был заменен MediaPlayer, IMFMediaEngine, и аудио/ видео захвата в Media Foundation. Эти функции оптимизированы для Windows 10 и Windows 11. Корпорация Майкрософт настоятельно рекомендует использовать в новом коде MediaPlayer, IMFMediaEngine и аудио/видеозахват в Media Foundation вместо DirectShow, когда это возможно. Корпорация Майкрософт предлагает переписать существующий код, в котором используются устаревшие API, чтобы по возможности использовать новые API.]

Некоторые камеры могут создавать неподвижное изображение отдельно от потока захвата, и часто неподвижное изображение имеет более высокое качество, чем изображения, созданные потоком захвата. Камера может иметь кнопку, которая выступает в качестве аппаратного триггера, или она может поддерживать активацию программного обеспечения. Камера, поддерживающая неподвижные изображения, будет предоставлять закрепление неподвижного изображения, которое является категорией контактов PIN_CATEGORY_STILL.

Рекомендуемый способ получения неподвижных образов с устройства — использовать API получения образов Windows (WIA). Дополнительные сведения см. в разделе "Получение образа Windows" в документации по пакету SDK для платформы. Однако вы также можете использовать DirectShow для захвата образа.

Чтобы активировать неподвижное закрепление, используйте метод IAMVideoControl::SetMode при выполнении графа следующим образом:

IAMVideoControl *pAMVidControl = NULL;

hr = pControl->Run(); // Run the graph.
if (FAILED(hr))
{
    // Handle error.
}

hr = pCap->QueryInterface(IID_IAMVideoControl, (void**)&pAMVidControl);

if (SUCCEEDED(hr))
{
    // Find the still pin.
    IPin *pPin = NULL;

    // pBuild is an ICaptureGraphBuilder2 pointer.

    hr = pBuild->FindPin(
        pCap,                  // Filter.
        PINDIR_OUTPUT,         // Look for an output pin.
        &PIN_CATEGORY_STILL,   // Pin category.
        NULL,                  // Media type (don't care).
        FALSE,                 // Pin must be unconnected.
        0,                     // Get the 0'th pin.
        &pPin                  // Receives a pointer to thepin.
        );

    if (SUCCEEDED(hr))
    {
        hr = pAMVidControl->SetMode(pPin, VideoControlFlag_Trigger);
        pPin->Release();
    }
    pAMVidControl->Release();
}

Запросите фильтр отслеживания для IAMVideoControl. Если интерфейс поддерживается, получите указатель на интерфейс IPin еще остающегося контакта, вызвав метод ICaptureGraphBuilder2::FindPin , как показано в предыдущем примере. Затем вызовите IAMVideoControl::SetMode с указателем IPin и флагом VideoControlFlag_Trigger.

Примечание

В зависимости от камеры может потребоваться отрисовка контакта захвата (PIN_CATEGORY_CAPTURE), прежде чем он будет подключен.

 

Пример. Использование примера фильтра Grabber

Один из способов захвата изображения — с помощью фильтра Sample Grabber . Пример Grabber использует определяемую приложением функцию обратного вызова для обработки изображения. Дополнительные сведения о фильтре Sample Grabber см. в разделе Использование примера граббера.

В следующем примере предполагается, что по-прежнему закрепление предоставляет несжатое RGB-изображение. Сначала определите класс, который будет реализовывать интерфейс обратного вызова Примера Grabber , ISampleGrabberCB:

// Class to hold the callback function for the Sample Grabber filter.
class SampleGrabberCallback : public ISampleGrabberCB
{
    // Implementation is described later.
}

// Global instance of the class.
SampleGrabberCallback g_StillCapCB;

Реализация класса описана вкратце.

Затем соедините неподвижный контакт с примером граббера и соедините пример граббера с фильтром отрисовщика null . Отрисовщик null просто удаляет полученные примеры мультимедиа; фактическая работа будет выполнена в рамках обратного вызова. (Единственная причина для отрисовщика значений NULL заключается в том, чтобы подключить к чему-то выходной пин-код примера Grabber.) Вызовите CoCreateInstance , чтобы создать фильтры отрисовщика Sample Grabber и Null, и вызовите IFilterGraph::AddFilter , чтобы добавить оба фильтра в граф:

// Add the Sample Grabber filter to the graph.
IBaseFilter *pSG_Filter;
hr = CoCreateInstance(
    CLSID_SampleGrabber, 
    NULL, 
    CLSCTX_INPROC_SERVER,
    IID_IBaseFilter, 
    (void**)&pSG_Filter
    );

hr = pGraph->AddFilter(pSG_Filter, L"SampleGrab");

// Add the Null Renderer filter to the graph.
IBaseFilter *pNull;

hr = CoCreateInstance(
    CLSID_NullRenderer, 
    NULL, 
    CLSCTX_INPROC_SERVER,
    IID_IBaseFilter, 
    (void**)&pNull
    );

hr = pGraph->AddFilter(pNull, L"NullRender");

Вы можете использовать метод ICaptureGraphBuilder2::RenderStream , чтобы подключить все три фильтра в одном вызове метода, перейдя от неподвижного контакта к образцу Grabber, а от примера Grabber к отрисовщику null:

hr = pBuild->RenderStream(
    &PIN_CATEGORY_STILL, // Connect this pin ...
    &MEDIATYPE_Video,    // with this media type ...
    pCap,                // on this filter ...
    pSG_Filter,          // to the Sample Grabber ...
    pNull);              // ... and finally to the Null Renderer.

Теперь используйте интерфейс ISampleGrabber для настройки примера Grabber таким образом, чтобы он буферизует примеры:

// Configure the Sample Grabber.
ISampleGrabber *pSG = NULL;

hr = pSG_Filter->QueryInterface(IID_ISampleGrabber, (void**)&pSG);
if (SUCCEEDED(hr))
{
    hr = pSG->SetOneShot(FALSE);
    hr = pSG->SetBufferSamples(TRUE);

    ...

Задайте интерфейс обратного вызова с указателем на объект обратного вызова:

hr = pSG->SetCallback(&g_StillCapCB, 0); // 0 = Use the SampleCB callback method.

Получите тип носителя, который по-прежнему использовался для соединения с примером Grabber:

// Store the media type for later use.
AM_MEDIA_TYPE g_StillMediaType;

hr = pSG->GetConnectedMediaType(&g_StillMediaType);
pSG->Release();

Этот тип мультимедиа будет содержать структуру BITMAPINFOHEADER , которая определяет формат неподвижного изображения. Освободите тип носителя перед завершением работы приложения:

// On exit, remember to release the media type.
FreeMediaType(g_StillMediaType);

Ниже приведен пример класса обратного вызова. Обратите внимание, что класс реализует IUnknown, который наследуется через интерфейс ISampleGrabber , но не сохраняет количество ссылок. Это безопасно, так как приложение создает объект в стеке, а объект остается в область на протяжении всего времени существования графа фильтра.

Вся работа выполняется в методе BufferCB , который вызывается методом Sample Grabber при каждом получении нового примера. В следующем примере метод записывает растровое изображение в файл:

class SampleGrabberCallback : public ISampleGrabberCB
{
public:
    // Fake referance counting.
    STDMETHODIMP_(ULONG) AddRef() { return 1; }
    STDMETHODIMP_(ULONG) Release() { return 2; }

    STDMETHODIMP QueryInterface(REFIID riid, void **ppvObject)
    {
        if (NULL == ppvObject) return E_POINTER;
        if (riid == __uuidof(IUnknown))
        {
            *ppvObject = static_cast<IUnknown*>(this);
             return S_OK;
        }
        if (riid == __uuidof(ISampleGrabberCB))
        {
            *ppvObject = static_cast<ISampleGrabberCB*>(this);
             return S_OK;
        }
        return E_NOTIMPL;
    }

    STDMETHODIMP SampleCB(double Time, IMediaSample *pSample)
    {
        return E_NOTIMPL;
    }

    STDMETHODIMP BufferCB(double Time, BYTE *pBuffer, long BufferLen)
    {
        if ((g_StillMediaType.majortype != MEDIATYPE_Video) ||
            (g_StillMediaType.formattype != FORMAT_VideoInfo) ||
            (g_StillMediaType.cbFormat < sizeof(VIDEOINFOHEADER)) ||
            (g_StillMediaType.pbFormat == NULL))
        {
            return VFW_E_INVALIDMEDIATYPE;
        }
        HANDLE hf = CreateFile("C:\\Example.bmp", GENERIC_WRITE, 
        FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, 0, NULL);
        if (hf == INVALID_HANDLE_VALUE)
        {
            return E_FAIL;
        }
        long cbBitmapInfoSize = g_StillMediaType.cbFormat - SIZE_PREHEADER;
        VIDEOINFOHEADER *pVideoHeader =
           (VIDEOINFOHEADER*)g_StillMediaType.pbFormat;

        BITMAPFILEHEADER bfh;
        ZeroMemory(&bfh, sizeof(bfh));
        bfh.bfType = 'MB';  // Little-endian for "BM".
        bfh.bfSize = sizeof( bfh ) + BufferLen + cbBitmapInfoSize;
        bfh.bfOffBits = sizeof( BITMAPFILEHEADER ) + cbBitmapInfoSize;
        
        // Write the file header.
        DWORD dwWritten = 0;
        WriteFile( hf, &bfh, sizeof( bfh ), &dwWritten, NULL );
        WriteFile(hf, HEADER(pVideoHeader), cbBitmapInfoSize, &dwWritten, NULL);        
        WriteFile( hf, pBuffer, BufferLen, &dwWritten, NULL );
        CloseHandle( hf );
        return S_OK;

    }
};

Задачи захвата видео