我正在尝试使用 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() 函数似乎没有被调用。有什么指示我做错了什么吗?
谢谢, 维奈
您的代码存在一些问题,包括它不是自行构建的以及 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