DirectShow 相机流驱动程序
DirectShow camera stream driver
我是 DirectShow 编程的新手,我正在尝试创建一个示例抓取器应用程序,以便经常从视频捕获设备抓取帧(即每 100 毫秒)。
我的源代码基于 Windows SDK 示例中可用的 SampleGrabber 应用程序。
一般来说,驱动程序在使用我的 PC 网络摄像头时工作正常。但是,我在将它与华硕 T100 平板电脑一起使用时确实遇到了一些问题。在这种情况下,我在设置相机分辨率后尝试使用 ConnectFilters 函数时收到错误 "libtbd error data is not tagged properly"。我试图为相机设置的分辨率是 1280x720,我知道该设备可以支持它。
下面我附上我关于捕获功能的部分代码:
ICreateDevEnum *pSysDevEnum = 0;
IEnumMoniker *pEnumCams = 0;
IMoniker *pMon = 0;
IBaseFilter *CameraF = 0;
IGraphBuilder *pGraph = NULL;
IMediaControl *pControl = NULL;
IMediaEventEx *pEvent = NULL;
IBaseFilter *pGrabberF = NULL;
ISampleGrabber *pSGrabber = NULL;
IBaseFilter *pSourceF = NULL;
IEnumPins *pEnum = NULL;
IPin *pPin = NULL;
IBaseFilter *pNullF = NULL;
BYTE *pBuffer = NULL;
HRESULT hr;
AM_MEDIA_TYPE mt;
int count = 0;
printf("Statting with capturing\n");
//Device list
hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER,
IID_ICreateDevEnum, (void **)&pSysDevEnum);
if (FAILED(hr))
return false;
// Obtain a class enumerator for the video compressor category.
hr = pSysDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory,
&pEnumCams, 0);
if (hr != S_OK)
return false;
// Enumerate the monikers. to desired device (0,1,2,...)
for (int i = 0; i <= cur_cam; ++i)
{
hr = pEnumCams->Next(1, &pMon, NULL);
if (hr != S_OK)
return false;
}
//Get BaseFilter of chosen camera
hr = pMon->BindToObject(0, 0, IID_IBaseFilter, (void**)&CameraF);
if (hr != S_OK)
return false;
//Create the Filter Graph Manager
hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pGraph));
if (FAILED(hr))
return false;
//Query for the IMediaControl and IMediaEventEx interfaces
hr = pGraph->QueryInterface(IID_PPV_ARGS(&pControl));
if (FAILED(hr))
return false;
hr = pGraph->QueryInterface(IID_PPV_ARGS(&pEvent));
if (FAILED(hr))
return false;
// Add web camera to graph as source filter (because first?)
hr = pGraph->AddFilter(CameraF, L"Capture Source");
if (hr != S_OK)
return false;
// Create an instance of the Sample Grabber Filter
hr = CoCreateInstance(CLSID_SampleGrabber, NULL, CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&pGrabberF));
if (FAILED(hr))
return false;
// Add it to the filter graph.
hr = pGraph->AddFilter(pGrabberF, L"Sample Grabber");
if (FAILED(hr))
return false;
hr = pGrabberF->QueryInterface(IID_PPV_ARGS(&pSGrabber));
if (FAILED(hr))
return false;
//Set the Media Type
ZeroMemory(&mt, sizeof(mt));
mt.majortype = MEDIATYPE_Video;
mt.subtype = MEDIASUBTYPE_RGB24 ;
hr = pSGrabber->SetMediaType(&mt);
if (FAILED(hr))
return false;
//Building graph. Connecting Source (CameraF) and Sample Grabber
hr = CameraF->EnumPins(&pEnum);
if (FAILED(hr))
return false;
while (S_OK == pEnum->Next(1, &pPin, NULL))
{
hr = SetResolution(width_, height_, pPin);
if (FAILED(hr))
std::cout << "Failed to set resolution" << std::endl;
hr = ConnectFilters(pGraph, pPin, pGrabberF);
if (SUCCEEDED(hr))
{
std::cout << "Success to Connect Filter Graphs" << std::endl;
break;
}
else{
std::cout << "Failed to Connect Filter Graphs" << std::endl;
}
}
if (FAILED(hr)){
std::cout << "Failed A" << std::endl;
return false;
}
SafeRelease(pPin);
//The following code connects the Sample Grabber to the Null Renderer filter:
hr = CoCreateInstance(CLSID_NullRenderer, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pNullF));
if (FAILED(hr))
return false;
hr = pGraph->AddFilter(pNullF, L"Null Filter");
if (FAILED(hr))
return false;
//ConnectFilters SamplerGrabber and Null Filter
hr = ConnectFilters(pGraph, pGrabberF, pNullF);
if (FAILED(hr))
return false;
hr = pSGrabber->SetBufferSamples(TRUE);
if (FAILED(hr))
return false;
hr = pSGrabber->SetOneShot(TRUE);//Halt after sample received
if (FAILED(hr))
return false;
hr = pControl->Run();//Run filter graph
if (FAILED(hr))
return false;
printf("Wait for a seconds to let cam sensor adopt to light\n");
Sleep(3000);//Wait 3 seconds
printf("OK\n");
//char filename[50];
printf("Go for the loop");
for (;;){
long evCode;
while (1)
{
hr = pEvent->WaitForCompletion(INFINITE, &evCode);//Wait for frame
if (evCode == EC_COMPLETE || evCode == EC_SYSTEMBASE)
break;
printf("Not complete. event: %d\n", evCode);
pControl->Pause();//pause the Graph
pSGrabber->SetOneShot(TRUE);//Set to halt after first frame
hr = pControl->Run();//resume filter graph
if (FAILED(hr))
return false;
Sleep(1000);//wait for a second
}
printf("out of while loop");
//if (hr != S_OK)
// return false;
Sleep(100);
//there frame got. Graph still running, but Sample Grabber halted
// Find the required buffer size.
long cbBufSize;
hr = pSGrabber->GetCurrentBuffer(&cbBufSize, NULL);
if (FAILED(hr))
return false;
pBuffer = (BYTE*)CoTaskMemAlloc(cbBufSize);
if (!pBuffer)
{
hr = E_OUTOFMEMORY;
return false;
}
hr = pSGrabber->GetCurrentBuffer(&cbBufSize, (long*)pBuffer);
if (FAILED(hr))
return false;
hr = pSGrabber->GetConnectedMediaType(&mt);
if (FAILED(hr))
return false;
// Examine the format block.
if ((mt.formattype == FORMAT_VideoInfo) &&
(mt.cbFormat >= sizeof(VIDEOINFOHEADER)) && (mt.pbFormat != NULL))
{
VIDEOINFOHEADER *pVih = (VIDEOINFOHEADER*)mt.pbFormat;
//sprintf_s(filename, sizeof(filename), "photo%d.bmp",++count);
time_t t1 = time(NULL);
SYSTEMTIME st;
GetSystemTime(&st);
printf("Save frame to file: %s - %02ld,%02ld.%03ld seconds\n", FileName, st.wMinute,st.wSecond, st.wMilliseconds );
hr = WriteBitmap(FileName, &pVih->bmiHeader, mt.cbFormat - SIZE_PREHEADER,pBuffer, cbBufSize);
}
else
{
// Invalid format.
hr = VFW_E_INVALIDMEDIATYPE;
printf("Invalid frame format\n");
}
CoTaskMemFree(pBuffer);
if ((GetAsyncKeyState(VK_ESCAPE) & 0x01))
break;
else if ((GetAsyncKeyState(VK_SPACE) & 0x01))
CameraProperties(CameraF);
}
SafeRelease(pEnum);
SafeRelease(pNullF);
SafeRelease(pSourceF);
SafeRelease(pSGrabber);
SafeRelease(pGrabberF);
SafeRelease(pControl);
SafeRelease(pEvent);
SafeRelease(pGraph);
下面是设置相机分辨率的函数:
// Set the grabbing size
// First we iterate through the available media types and
// store the first one that fits the requested size.
// If we have found one, we set it.
// In any case we query the size of the current media type
// to have this information for clients of this class.
HRESULT hr;
IAMStreamConfig *pConfig;
IEnumMediaTypes *pMedia;
AM_MEDIA_TYPE *pmt = NULL, *pfnt = NULL;
hr = pPin->EnumMediaTypes(&pMedia);
if (SUCCEEDED(hr))
{
while (pMedia->Next(1, &pmt, 0) == S_OK)
{
if (pmt->formattype == FORMAT_VideoInfo)
{
VIDEOINFOHEADER *vih = (VIDEOINFOHEADER *)pmt->pbFormat;
// printf("Size %i %i\n", vih->bmiHeader.biWidth, vih->bmiHeader.biHeight );
if (vih->bmiHeader.biWidth == width_ && vih->bmiHeader.biHeight == height_)
{
pfnt = pmt;
printf("found mediatype with %i %i\n", vih->bmiHeader.biWidth, vih->bmiHeader.biHeight );
break;
}
DeleteMediaType(pmt);
}
}
pMedia->Release();
}
hr = pPin->QueryInterface(IID_IAMStreamConfig, (void **)&pConfig);
if (SUCCEEDED(hr))
{
if (pfnt != NULL)
{
hr = pConfig->SetFormat(pfnt);
DeleteMediaType(pfnt);
}
hr = pConfig->GetFormat(&pfnt);
if (SUCCEEDED(hr))
{
m_nWidth = ((VIDEOINFOHEADER *)pfnt->pbFormat)->bmiHeader.biWidth;
m_nHeight = ((VIDEOINFOHEADER *)pfnt->pbFormat)->bmiHeader.biHeight;
DeleteMediaType(pfnt);
}
}
有没有人遇到过这种问题?捕获过滤器设置是否有错误?
任何想法都会非常有帮助。
更新 1
我正在添加应用程序执行输出的屏幕截图。 Output
更新 2
我尝试使用 OpenCV 3.1 设置分辨率,但我遇到了完全相同的问题。平板电脑正在使用 Intel(R) 成像信号处理器 2400 驱动程序作为摄像头,这就是问题所在。奇怪的是,使用 AmCap 我能够毫无问题地设置相机分辨率和处理相机设备。可以请人帮忙吗?有谁知道 AmCap 使用什么接口与相机通信?
我设法解决了这个问题。报告的 libdbd 错误似乎与分辨率更改无关。
问题是我试图通过在 SetFormat() 函数中传递不受支持的视频格式来设置分辨率。详细来说,新的工作 SetResolution() 函数变为:
// Set the grabbing size
// First we iterate through the available media types and
// store the first one that fits the requested size.
// If we have found one, we set it.
// In any case we query the size of the current media type
// to have this information for clients of this class.
HRESULT hr;
IAMStreamConfig *pConfig=NULL;
//IEnumMediaTypes *pMedia;
BYTE *pSCC = NULL;
AM_MEDIA_TYPE *pmt = NULL, *pfnt = NULL, *mfnt=NULL;
//hr = pPin->EnumMediaTypes(&pMedia);
int iCount, iSize;
hr = pPin->QueryInterface(IID_IAMStreamConfig, (void **)&pConfig);
hr = pConfig->GetNumberOfCapabilities(&iCount, &iSize);
pSCC = new BYTE[iSize];
int i = 0;
if (SUCCEEDED(hr))
{
while (i<=iCount)
{
pConfig->GetStreamCaps(i++, &pfnt, pSCC);
VIDEOINFOHEADER *vih = (VIDEOINFOHEADER *)pfnt->pbFormat;
printf("Size %i %i\n", vih->bmiHeader.biWidth, vih->bmiHeader.biHeight );
if (vih->bmiHeader.biWidth == width_ && vih->bmiHeader.biHeight == height_)
{
//pfnt = mfnt;
printf("Found mediatype with %i %i - %x\n", vih->bmiHeader.biWidth, vih->bmiHeader.biHeight, pfnt->formattype );
hr = pConfig->SetFormat(pfnt);
//break;
}
}
}
//DeleteMediaType(pfnt);
delete[] pSCC;
pConfig->Release();
return hr;
我是 DirectShow 编程的新手,我正在尝试创建一个示例抓取器应用程序,以便经常从视频捕获设备抓取帧(即每 100 毫秒)。
我的源代码基于 Windows SDK 示例中可用的 SampleGrabber 应用程序。
一般来说,驱动程序在使用我的 PC 网络摄像头时工作正常。但是,我在将它与华硕 T100 平板电脑一起使用时确实遇到了一些问题。在这种情况下,我在设置相机分辨率后尝试使用 ConnectFilters 函数时收到错误 "libtbd error data is not tagged properly"。我试图为相机设置的分辨率是 1280x720,我知道该设备可以支持它。
下面我附上我关于捕获功能的部分代码:
ICreateDevEnum *pSysDevEnum = 0;
IEnumMoniker *pEnumCams = 0;
IMoniker *pMon = 0;
IBaseFilter *CameraF = 0;
IGraphBuilder *pGraph = NULL;
IMediaControl *pControl = NULL;
IMediaEventEx *pEvent = NULL;
IBaseFilter *pGrabberF = NULL;
ISampleGrabber *pSGrabber = NULL;
IBaseFilter *pSourceF = NULL;
IEnumPins *pEnum = NULL;
IPin *pPin = NULL;
IBaseFilter *pNullF = NULL;
BYTE *pBuffer = NULL;
HRESULT hr;
AM_MEDIA_TYPE mt;
int count = 0;
printf("Statting with capturing\n");
//Device list
hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER,
IID_ICreateDevEnum, (void **)&pSysDevEnum);
if (FAILED(hr))
return false;
// Obtain a class enumerator for the video compressor category.
hr = pSysDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory,
&pEnumCams, 0);
if (hr != S_OK)
return false;
// Enumerate the monikers. to desired device (0,1,2,...)
for (int i = 0; i <= cur_cam; ++i)
{
hr = pEnumCams->Next(1, &pMon, NULL);
if (hr != S_OK)
return false;
}
//Get BaseFilter of chosen camera
hr = pMon->BindToObject(0, 0, IID_IBaseFilter, (void**)&CameraF);
if (hr != S_OK)
return false;
//Create the Filter Graph Manager
hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pGraph));
if (FAILED(hr))
return false;
//Query for the IMediaControl and IMediaEventEx interfaces
hr = pGraph->QueryInterface(IID_PPV_ARGS(&pControl));
if (FAILED(hr))
return false;
hr = pGraph->QueryInterface(IID_PPV_ARGS(&pEvent));
if (FAILED(hr))
return false;
// Add web camera to graph as source filter (because first?)
hr = pGraph->AddFilter(CameraF, L"Capture Source");
if (hr != S_OK)
return false;
// Create an instance of the Sample Grabber Filter
hr = CoCreateInstance(CLSID_SampleGrabber, NULL, CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&pGrabberF));
if (FAILED(hr))
return false;
// Add it to the filter graph.
hr = pGraph->AddFilter(pGrabberF, L"Sample Grabber");
if (FAILED(hr))
return false;
hr = pGrabberF->QueryInterface(IID_PPV_ARGS(&pSGrabber));
if (FAILED(hr))
return false;
//Set the Media Type
ZeroMemory(&mt, sizeof(mt));
mt.majortype = MEDIATYPE_Video;
mt.subtype = MEDIASUBTYPE_RGB24 ;
hr = pSGrabber->SetMediaType(&mt);
if (FAILED(hr))
return false;
//Building graph. Connecting Source (CameraF) and Sample Grabber
hr = CameraF->EnumPins(&pEnum);
if (FAILED(hr))
return false;
while (S_OK == pEnum->Next(1, &pPin, NULL))
{
hr = SetResolution(width_, height_, pPin);
if (FAILED(hr))
std::cout << "Failed to set resolution" << std::endl;
hr = ConnectFilters(pGraph, pPin, pGrabberF);
if (SUCCEEDED(hr))
{
std::cout << "Success to Connect Filter Graphs" << std::endl;
break;
}
else{
std::cout << "Failed to Connect Filter Graphs" << std::endl;
}
}
if (FAILED(hr)){
std::cout << "Failed A" << std::endl;
return false;
}
SafeRelease(pPin);
//The following code connects the Sample Grabber to the Null Renderer filter:
hr = CoCreateInstance(CLSID_NullRenderer, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pNullF));
if (FAILED(hr))
return false;
hr = pGraph->AddFilter(pNullF, L"Null Filter");
if (FAILED(hr))
return false;
//ConnectFilters SamplerGrabber and Null Filter
hr = ConnectFilters(pGraph, pGrabberF, pNullF);
if (FAILED(hr))
return false;
hr = pSGrabber->SetBufferSamples(TRUE);
if (FAILED(hr))
return false;
hr = pSGrabber->SetOneShot(TRUE);//Halt after sample received
if (FAILED(hr))
return false;
hr = pControl->Run();//Run filter graph
if (FAILED(hr))
return false;
printf("Wait for a seconds to let cam sensor adopt to light\n");
Sleep(3000);//Wait 3 seconds
printf("OK\n");
//char filename[50];
printf("Go for the loop");
for (;;){
long evCode;
while (1)
{
hr = pEvent->WaitForCompletion(INFINITE, &evCode);//Wait for frame
if (evCode == EC_COMPLETE || evCode == EC_SYSTEMBASE)
break;
printf("Not complete. event: %d\n", evCode);
pControl->Pause();//pause the Graph
pSGrabber->SetOneShot(TRUE);//Set to halt after first frame
hr = pControl->Run();//resume filter graph
if (FAILED(hr))
return false;
Sleep(1000);//wait for a second
}
printf("out of while loop");
//if (hr != S_OK)
// return false;
Sleep(100);
//there frame got. Graph still running, but Sample Grabber halted
// Find the required buffer size.
long cbBufSize;
hr = pSGrabber->GetCurrentBuffer(&cbBufSize, NULL);
if (FAILED(hr))
return false;
pBuffer = (BYTE*)CoTaskMemAlloc(cbBufSize);
if (!pBuffer)
{
hr = E_OUTOFMEMORY;
return false;
}
hr = pSGrabber->GetCurrentBuffer(&cbBufSize, (long*)pBuffer);
if (FAILED(hr))
return false;
hr = pSGrabber->GetConnectedMediaType(&mt);
if (FAILED(hr))
return false;
// Examine the format block.
if ((mt.formattype == FORMAT_VideoInfo) &&
(mt.cbFormat >= sizeof(VIDEOINFOHEADER)) && (mt.pbFormat != NULL))
{
VIDEOINFOHEADER *pVih = (VIDEOINFOHEADER*)mt.pbFormat;
//sprintf_s(filename, sizeof(filename), "photo%d.bmp",++count);
time_t t1 = time(NULL);
SYSTEMTIME st;
GetSystemTime(&st);
printf("Save frame to file: %s - %02ld,%02ld.%03ld seconds\n", FileName, st.wMinute,st.wSecond, st.wMilliseconds );
hr = WriteBitmap(FileName, &pVih->bmiHeader, mt.cbFormat - SIZE_PREHEADER,pBuffer, cbBufSize);
}
else
{
// Invalid format.
hr = VFW_E_INVALIDMEDIATYPE;
printf("Invalid frame format\n");
}
CoTaskMemFree(pBuffer);
if ((GetAsyncKeyState(VK_ESCAPE) & 0x01))
break;
else if ((GetAsyncKeyState(VK_SPACE) & 0x01))
CameraProperties(CameraF);
}
SafeRelease(pEnum);
SafeRelease(pNullF);
SafeRelease(pSourceF);
SafeRelease(pSGrabber);
SafeRelease(pGrabberF);
SafeRelease(pControl);
SafeRelease(pEvent);
SafeRelease(pGraph);
下面是设置相机分辨率的函数:
// Set the grabbing size
// First we iterate through the available media types and
// store the first one that fits the requested size.
// If we have found one, we set it.
// In any case we query the size of the current media type
// to have this information for clients of this class.
HRESULT hr;
IAMStreamConfig *pConfig;
IEnumMediaTypes *pMedia;
AM_MEDIA_TYPE *pmt = NULL, *pfnt = NULL;
hr = pPin->EnumMediaTypes(&pMedia);
if (SUCCEEDED(hr))
{
while (pMedia->Next(1, &pmt, 0) == S_OK)
{
if (pmt->formattype == FORMAT_VideoInfo)
{
VIDEOINFOHEADER *vih = (VIDEOINFOHEADER *)pmt->pbFormat;
// printf("Size %i %i\n", vih->bmiHeader.biWidth, vih->bmiHeader.biHeight );
if (vih->bmiHeader.biWidth == width_ && vih->bmiHeader.biHeight == height_)
{
pfnt = pmt;
printf("found mediatype with %i %i\n", vih->bmiHeader.biWidth, vih->bmiHeader.biHeight );
break;
}
DeleteMediaType(pmt);
}
}
pMedia->Release();
}
hr = pPin->QueryInterface(IID_IAMStreamConfig, (void **)&pConfig);
if (SUCCEEDED(hr))
{
if (pfnt != NULL)
{
hr = pConfig->SetFormat(pfnt);
DeleteMediaType(pfnt);
}
hr = pConfig->GetFormat(&pfnt);
if (SUCCEEDED(hr))
{
m_nWidth = ((VIDEOINFOHEADER *)pfnt->pbFormat)->bmiHeader.biWidth;
m_nHeight = ((VIDEOINFOHEADER *)pfnt->pbFormat)->bmiHeader.biHeight;
DeleteMediaType(pfnt);
}
}
有没有人遇到过这种问题?捕获过滤器设置是否有错误?
任何想法都会非常有帮助。
更新 1
我正在添加应用程序执行输出的屏幕截图。 Output
更新 2
我尝试使用 OpenCV 3.1 设置分辨率,但我遇到了完全相同的问题。平板电脑正在使用 Intel(R) 成像信号处理器 2400 驱动程序作为摄像头,这就是问题所在。奇怪的是,使用 AmCap 我能够毫无问题地设置相机分辨率和处理相机设备。可以请人帮忙吗?有谁知道 AmCap 使用什么接口与相机通信?
我设法解决了这个问题。报告的 libdbd 错误似乎与分辨率更改无关。
问题是我试图通过在 SetFormat() 函数中传递不受支持的视频格式来设置分辨率。详细来说,新的工作 SetResolution() 函数变为:
// Set the grabbing size
// First we iterate through the available media types and
// store the first one that fits the requested size.
// If we have found one, we set it.
// In any case we query the size of the current media type
// to have this information for clients of this class.
HRESULT hr;
IAMStreamConfig *pConfig=NULL;
//IEnumMediaTypes *pMedia;
BYTE *pSCC = NULL;
AM_MEDIA_TYPE *pmt = NULL, *pfnt = NULL, *mfnt=NULL;
//hr = pPin->EnumMediaTypes(&pMedia);
int iCount, iSize;
hr = pPin->QueryInterface(IID_IAMStreamConfig, (void **)&pConfig);
hr = pConfig->GetNumberOfCapabilities(&iCount, &iSize);
pSCC = new BYTE[iSize];
int i = 0;
if (SUCCEEDED(hr))
{
while (i<=iCount)
{
pConfig->GetStreamCaps(i++, &pfnt, pSCC);
VIDEOINFOHEADER *vih = (VIDEOINFOHEADER *)pfnt->pbFormat;
printf("Size %i %i\n", vih->bmiHeader.biWidth, vih->bmiHeader.biHeight );
if (vih->bmiHeader.biWidth == width_ && vih->bmiHeader.biHeight == height_)
{
//pfnt = mfnt;
printf("Found mediatype with %i %i - %x\n", vih->bmiHeader.biWidth, vih->bmiHeader.biHeight, pfnt->formattype );
hr = pConfig->SetFormat(pfnt);
//break;
}
}
}
//DeleteMediaType(pfnt);
delete[] pSCC;
pConfig->Release();
return hr;