使用 DirectShow 时未调用 ISampleGrabberCB SampleCB()

问题描述 投票:0回答:1

我正在尝试使用 DirectShow 从 USB 视频设备(UVC)捕获视频帧。实际上将 Media Foundation 与 DirectShow 进行比较,因为使用 Media Foundation 时我看到大约 11 毫秒的缓冲,因此无法获得实际的帧速率。想要将 11 毫秒缓冲隔离到 Media Foundation 或低级 usbVideo.sys 驱动程序,因此使用 Directshow 来查看是否遇到相同的 11 毫秒缓冲问题。

为了测量帧速率,我使用 ISampleGrabberCB 注册了 CB。但出于某种原因,CB 没有接到电话。我已经检查过是否有错误。

这是获取帧缓冲区的正确方法吗?

#include <iostream>
#include <windows.h>
#include <dshow.h>
#include "qedit.h"

#pragma comment(lib, "strmiids.lib")

void EnumerateVideoDevices() {
    // Initialize COM
    CoInitialize(NULL);

    // Create System Device Enumerator
    ICreateDevEnum* pCreateDevEnum = nullptr;
    CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum, (void**)&pCreateDevEnum);

    // Create EnumMoniker
    IEnumMoniker* pEnumMoniker = nullptr;
    pCreateDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pEnumMoniker, 0);

    // Check if there are devices
    if (pEnumMoniker != nullptr) {
        IMoniker* pMoniker = nullptr;

        // Display available devices
        int index = 1;
        while (pEnumMoniker->Next(1, &pMoniker, NULL) == S_OK) {
            IPropertyBag* pPropBag;
            pMoniker->BindToStorage(0, 0, IID_IPropertyBag, (void**)&pPropBag);

            // Retrieve device name
            VARIANT var;
            VariantInit(&var);
            pPropBag->Read(L"FriendlyName", &var, 0);
            std::wcout << index << L". " << var.bstrVal << std::endl;

            // Release resources
            VariantClear(&var);
            pPropBag->Release();
            pMoniker->Release();

            index++;
        }

        // Release enumerator
        pEnumMoniker->Release();
    }

    // Release COM objects
    pCreateDevEnum->Release();
    CoUninitialize();
}

IBaseFilter* ChooseVideoDevice() {
    // Prompt the user to choose a video device
    std::cout << "Choose a video device by entering its index: ";
    int selectedIndex;
    std::cin >> selectedIndex;

    // Initialize COM
    CoInitialize(NULL);

    // Create System Device Enumerator
    ICreateDevEnum* pCreateDevEnum = nullptr;
    CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum, (void**)&pCreateDevEnum);

    // Create EnumMoniker
    IEnumMoniker* pEnumMoniker = nullptr;
    pCreateDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pEnumMoniker, 0);

    // Check if there are devices
    if (pEnumMoniker != nullptr) {
        IMoniker* pMoniker = nullptr;

        // Find the selected device
        int index = 1;
        while (pEnumMoniker->Next(1, &pMoniker, NULL) == S_OK) {
            if (index == selectedIndex) {
                // Bind to the selected device
                IBaseFilter* pVideoCaptureFilter = nullptr;
                pMoniker->BindToObject(NULL, NULL, IID_IBaseFilter, (void**)&pVideoCaptureFilter);

                // Release enumerator
                pEnumMoniker->Release();
                pCreateDevEnum->Release();
                CoUninitialize();

                return pVideoCaptureFilter;
            }

            pMoniker->Release();
            index++;
        }

        // Release enumerator
        pEnumMoniker->Release();
    }

    // Release COM objects
    pCreateDevEnum->Release();
    CoUninitialize();

    return nullptr;
}

IPin* GetOutPin(IBaseFilter* pFilter, PIN_DIRECTION PinDir) {
    IEnumPins* pEnum;
    IPin* pPin = NULL;

    if (SUCCEEDED(pFilter->EnumPins(&pEnum))) {
        ULONG fetched;
        while (pEnum->Next(1, &pPin, &fetched) == S_OK) {
            PIN_DIRECTION ThisPinDir;
            if (SUCCEEDED(pPin->QueryDirection(&ThisPinDir)) && (ThisPinDir == PinDir)) {
                break;
            }
            pPin->Release();
        }
        pEnum->Release();
    }

    return pPin;
}

IPin* GetInPin(IBaseFilter* pFilter, PIN_DIRECTION PinDir) {
    IEnumPins* pEnum;
    IPin* pPin = NULL;

    if (SUCCEEDED(pFilter->EnumPins(&pEnum))) {
        ULONG fetched;
        while (pEnum->Next(1, &pPin, &fetched) == S_OK) {
            PIN_DIRECTION ThisPinDir;
            if (SUCCEEDED(pPin->QueryDirection(&ThisPinDir)) && (ThisPinDir == PinDir)) {
                break;
            }
            pPin->Release();
        }
        pEnum->Release();
    }

    return pPin;
}


class CallbackObject : public ISampleGrabberCB {
public:

    CallbackObject() {};

    STDMETHODIMP QueryInterface(REFIID riid, void** ppv)
    {
        if (NULL == ppv) return E_POINTER;
        if (riid == __uuidof(IUnknown)) {
            *ppv = static_cast<IUnknown*>(this);
            return S_OK;
        }
        if (riid == IID_ISampleGrabberCB) {
            *ppv = static_cast<ISampleGrabberCB*>(this);
            return S_OK;
        }
        return E_NOINTERFACE;
    }
    STDMETHODIMP_(ULONG) AddRef() { return S_OK; }
    STDMETHODIMP_(ULONG) Release() { return S_OK; }

    //ISampleGrabberCB
    STDMETHODIMP SampleCB(double SampleTime, IMediaSample* pSample);
    STDMETHODIMP BufferCB(double SampleTime, BYTE* pBuffer, long BufferLen) { return S_OK; }
};

STDMETHODIMP CallbackObject::SampleCB(double SampleTime, IMediaSample* pSample)
{
    if (!pSample)
        return E_POINTER;
    long sz = pSample->GetActualDataLength();
    BYTE* pBuf = NULL;
    pSample->GetPointer(&pBuf);
    if (sz <= 0 || pBuf == NULL) return E_UNEXPECTED;
    for (int i = 0;i < sz;i += 2)
        pBuf[i] = 255 - pBuf[i];
    pSample->Release();
    return S_OK;
}

int main() {
    // Enumerate video devices
    std::cout << "Available Video Devices:" << std::endl;
    EnumerateVideoDevices();

    // Choose a video device
    IBaseFilter* pVideoCaptureFilter = ChooseVideoDevice();
    if (pVideoCaptureFilter == nullptr) {
        std::cerr << "Failed to choose a video device." << std::endl;
        return 1;
    }
    
    HRESULT hr;

    // Initialize COM
    CoInitialize(NULL);

    // Create the Filter Graph Manager
    IGraphBuilder* pGraph = nullptr;
    CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void**)&pGraph);

    // Add the video capture filter to the filter graph
    hr = pGraph->AddFilter(pVideoCaptureFilter, L"Video Capture");
    if (FAILED(hr)) {
        std::cerr << "Failed to pVideoCaptureFilter. Error: " << hr << std::endl;
    }

    // Create the Capture Graph Builder
    ICaptureGraphBuilder2* pCaptureGraphBuilder = nullptr;
    CoCreateInstance(CLSID_CaptureGraphBuilder2, NULL, CLSCTX_INPROC_SERVER, IID_ICaptureGraphBuilder2, (void**)&pCaptureGraphBuilder);

    // Set the filter graph for the capture graph builder
    pCaptureGraphBuilder->SetFiltergraph(pGraph);

    // Render the video stream
    pCaptureGraphBuilder->RenderStream(&PIN_CATEGORY_PREVIEW, &MEDIATYPE_Video, pVideoCaptureFilter, NULL, NULL);

    // Create the SampleGrabber filter
    IBaseFilter* pSampleGrabberFilter = nullptr;
    CoCreateInstance(CLSID_SampleGrabber, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void**)&pSampleGrabberFilter);

    // Add the SampleGrabber filter to the graph
    hr = pGraph->AddFilter(pSampleGrabberFilter, L"Sample Grabber");
    if (FAILED(hr)) {
        std::cerr << "Failed to pSampleGrabberFilter. Error: " << hr << std::endl;
    }

    // Connect the filters
    IPin* pCapturePin = GetOutPin(pVideoCaptureFilter, PINDIR_OUTPUT);
    IPin* pSamplePin = GetInPin(pSampleGrabberFilter, PINDIR_INPUT);
    pGraph->Connect(pCapturePin, pSamplePin);

    // Configure SampleGrabber to receive callbacks
    ISampleGrabber* pSampleGrabber = nullptr;
    pSampleGrabberFilter->QueryInterface(IID_ISampleGrabber, (void**)&pSampleGrabber);


    AM_MEDIA_TYPE mt;
    ZeroMemory(&mt, sizeof(AM_MEDIA_TYPE));
    mt.majortype = MEDIATYPE_Video;
    mt.subtype = MEDIASUBTYPE_YUY2;
    hr = pSampleGrabber->SetMediaType(&mt);
    if (FAILED(hr)) {
        std::cerr << "Failed to SetMediaType. Error: " << hr << std::endl;
    }
    //hr = pSampleGrabber->SetOneShot(FALSE);
    //hr = pSampleGrabber->SetBufferSamples(TRUE);
    CallbackObject* pCallback = new CallbackObject();
    pSampleGrabber->SetCallback(pCallback, 0);

    // Run the graph to start capturing
    IMediaControl* pMediaControl = nullptr;
    pGraph->QueryInterface(IID_IMediaControl, (void**)&pMediaControl);
    pMediaControl->Run();


    // Wait for user input to stop capturing
    std::cout << "Press Enter to stop capturing." << std::endl;
    std::cin.ignore();
    std::cin.get();

    // Stop capturing
    pMediaControl->Stop();

    // Release COM objects
    pMediaControl->Release();
    pCaptureGraphBuilder->Release();
    pGraph->Release();
    pVideoCaptureFilter->Release();
    pSampleGrabber->Release();
    pSampleGrabberFilter->Release();
    delete pCallback; // Release the callback object

    // Uninitialize COM
    CoUninitialize();

    return 0;
}


来自笔记本电脑集成网络摄像头的视频似乎正在被捕获和渲染,但 SampleCB() 函数似乎没有被调用。有什么指示我做错了什么吗?

谢谢, 维奈

c++ video directshow ms-media-foundation samplegrabber
1个回答
0
投票

您的代码存在一些问题,包括它不是自行构建的以及 COM 的使用不准确。

不过,最简单的修复方法是将

cin
替换为
MessageBox
调用:

    //std::cout << "Press Enter to stop capturing." << std::endl;
    //std::cin.ignore();
    //std::cin.get();
    MessageBoxW(GetActiveWindow(), L"Close to stop capturing", nullptr, MB_OK);

此时,设置将变得或多或少可用,您将能够发现您正在创建这样的过滤器图,其中您的样本抓取器位于管道之外:

要解决这个问题,您的代码需要进行一些修补。我评论了一些您不需要的调用并添加了一行。从那里您可以收到回电。

    // Render the video stream
    //pCaptureGraphBuilder->RenderStream(&PIN_CATEGORY_PREVIEW, &MEDIATYPE_Video, pVideoCaptureFilter, NULL, NULL);

    // Create the SampleGrabber filter
    IBaseFilter* pSampleGrabberFilter = nullptr;
    CoCreateInstance(__uuidof(SampleGrabber), NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void**)&pSampleGrabberFilter);

    // Add the SampleGrabber filter to the graph
    hr = pGraph->AddFilter(pSampleGrabberFilter, L"Sample Grabber");
    if (FAILED(hr)) {
        std::cerr << "Failed to pSampleGrabberFilter. Error: " << hr << std::endl;
    }

    // Connect the filters
    //IPin* pCapturePin = GetOutPin(pVideoCaptureFilter, PINDIR_OUTPUT);
    //IPin* pSamplePin = GetInPin(pSampleGrabberFilter, PINDIR_INPUT);
    //pGraph->Connect(pCapturePin, pSamplePin);

    // Configure SampleGrabber to receive callbacks
    ISampleGrabber* pSampleGrabber = nullptr;
    pSampleGrabberFilter->QueryInterface(__uuidof(ISampleGrabber), (void**)&pSampleGrabber);


    AM_MEDIA_TYPE mt;
    ZeroMemory(&mt, sizeof(AM_MEDIA_TYPE));
    mt.majortype = MEDIATYPE_Video;
    mt.subtype = MEDIASUBTYPE_YUY2;
    hr = pSampleGrabber->SetMediaType(&mt);
    if (FAILED(hr)) {
        std::cerr << "Failed to SetMediaType. Error: " << hr << std::endl;
    }
    //hr = pSampleGrabber->SetOneShot(FALSE);
    //hr = pSampleGrabber->SetBufferSamples(TRUE);
    CallbackObject* pCallback = new CallbackObject();
    pSampleGrabber->SetCallback(pCallback, 0);

    pCaptureGraphBuilder->RenderStream(&PIN_CATEGORY_PREVIEW, &MEDIATYPE_Video, pVideoCaptureFilter, pSampleGrabberFilter, NULL); // <<--- Added

© www.soinside.com 2019 - 2024. All rights reserved.