SampleGrabber开发问题与解决方案

这篇文章主要针对《DirectShow中利用SampleGrabber捕获摄像头每一帧图像,并转为BMP文件信息写入内存》所写,记录了开发过程中遇到的比较深刻的问题。文中代码均来自上文。欢迎大家交流斧正。

P1:如何合理放置SampleGrabber在Filter Graph中的位置?

S1 :在放置SampleGrabber位置时应该很小心,因为SampleGrabber通常是作为一个TransformFilter存在的,而不是一个RendererFilter存在。当然,它也能作为一个RendererFilter存在,但是这样一来就不能实现通常所要求的预览功能了。写法如下:
[cpp]  view plain  copy
  1. hr = m_pCapture->RenderStream(&PIN_CATEGORY_PREVIEW, &MEDIATYPE_Video,m_pBF, NULL, pGrabBase);    


   那么应该怎样使得SampleGrabber和RendererFilter都得到Media Sample数据呢?容易发现,对于ICaptureGraphBuilder2::RenderStream(const GUID *pCategory,const GUID *pType,IUnknown *pSource,IBaseFilter *pIntermediate,IBaseFilter*pSink)的最后三个参数,pSource指向一个作为源头的SourceFilter或者输出Pin;pIntermediate指向一个中间Filter或者一个解压缩Filter(当然TransformFilter包括在内),可以为NULL;pSink指向一个RendererFilter或者一个MuxFilter,如果为空,将使用默认Filter。
   有了以上信息,我们不难把pIntermediate和我们的SampleGrabber Filter联系起来。所以可以如下写法:
 
[cpp]  view plain  copy
  1. hr = m_pCapture->RenderStream(&PIN_CATEGORY_PREVIEW, &MEDIATYPE_Video, m_pBF, pGrabBase, m_pVMR);  


   这样一来,就能实现捕获和预览功能了。
  
P2: 如何合理copy回调函数BufferCB得到的内存数据,比如大小?
S2:
 在FormatImage函数中,我们需要copy回调函数BufferCB得到的内存数据到一块内存中,进行处理。但是这个时候copy的大小就很重要了。不能大也不能小。这个地方其实不难想到大小是多少,但是容易犯“想当然”的错误。比如如下写法:
[cpp]  view plain  copy
  1. memset( m_pImgFileData, 0, nDataWidth*nImgHeight ) ;  
  2. memcpy( m_pImgFileData, lpImageData, nImgWidth*nImgHeight) ;  


注意了,很多时候可能会想当然地认为nImgWidth*nImgHeight就是buffer的大小,其实不然。如果想如上写法,能得到一定的图片的信息,但是不完整。我当时就只能看到图片的下半部分。其实,在回调函数BufferCB中我们已经保存了这块buffer的大小:
[cpp]  view plain  copy
  1. if (!cb.pBuffer)  
  2. {  
  3. cb.pBuffer = new BYTE[lBufferSize];  
  4. cb.lBufferSize = lBufferSize;  
  5. }  


正确的方法应该是直接使用已经保存起来的buffer大小去copy,如下:
[cpp]  view plain  copy
  1. memset( m_pImgFileData, 0, nDataWidth*nImgHeight ) ;  
  2. memcpy( m_pImgFileData, lpImageData, cb.lBufferSize) ;  



P3:关于可能出现的内存泄露问题,比如BufferCB的内存管理问题?
S3:
之前在没有使用SampleGrabber时,程序不存在内存泄露的问题。但是用了之后就出现了泄露问题。说实话,我当时是一头雾水,就拼命地寻找哪块buffer或者哪个filter没有及时释放。在可能的地方都加上释放语句。结果最终证明这样做是错的,即是弄巧成拙。我的愚蠢做法如下:
[cpp]  view plain  copy
  1. BYTE* pImageData = new BYTE[cb.lBufferSize];  
  2. CopyMemory(pImageData, cb.pBuffer, cb.lBufferSize);  
  3. if (mCB.FormatImage(pImageData, 24,  cb.bih.biWidth, cb.bih.biHeight))  
  4. {  
  5. ScaleRgb24BmpData(TransformBuffer, 320, 240,( mCB.GetImgFileData()),cb.bih.biWidth, cb.bih.biHeight);  
  6. ZeroMemory(m_pData, BUFFER_SIZE);  
  7. CopyMemory(m_pData, TransformBuffer, BUFFER_SIZE);  
  8. }  
  9. //to free memory  
  10. cb.pBuffer = NULL;  
  11. delete pImageData;  


看似没问题的代码,其实有一个很大的泄漏问题。我们注意到,在BufferCB中有这样的语句:
[cpp]  view plain  copy
  1. if (!cb.pBuffer)  
  2. {  
  3. cb.pBuffer = new BYTE[lBufferSize];  
  4. cb.lBufferSize = lBufferSize;  
  5. }  


这就是说,如果cb.pBuffer为空的话,就会重新new一次,就会重新分配一块IBufferSize大小的内存。与此同时,你别以为之前那个cb.pBuffer所分配的内存已经回收了,它只是被只设置为NULL了而已!接着,你就会使用new出来的内存,之前的cb.pBuffer内存就泄露了。事实上,BufferCB函数会自己处理内存释放的问题,你不用瞎操心,要不然就画蛇添足了。所以做法应该是删除cb.pBuffer = NULL;这句。

[cpp]  view plain  copy
  1. //END ICTwangbiao  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,以下是一个简单的Windows应用程序示例,可以使用UVC相机进行拍照和录像: ```c++ #include <Windows.h> #include <dshow.h> #pragma comment(lib, "strmiids.lib") // UVC相机操作类 class UvcCamera { public: UvcCamera() : m_pGraph(nullptr), m_pCapture(nullptr), m_pControl(nullptr), m_pStill(nullptr), m_pBuilder(nullptr), m_pSampleGrabber(nullptr), m_pCallback(nullptr), m_hWnd(nullptr), m_bPreviewing(false) {} ~UvcCamera() { if (m_bPreviewing) { StopPreview(); } if (m_pCallback) { m_pCallback->Release(); m_pCallback = nullptr; } if (m_pSampleGrabber) { m_pSampleGrabber->Release(); m_pSampleGrabber = nullptr; } if (m_pBuilder) { m_pBuilder->Release(); m_pBuilder = nullptr; } if (m_pStill) { m_pStill->Release(); m_pStill = nullptr; } if (m_pControl) { m_pControl->Release(); m_pControl = nullptr; } if (m_pCapture) { m_pCapture->Release(); m_pCapture = nullptr; } if (m_pGraph) { m_pGraph->Release(); m_pGraph = nullptr; } } // 初始化UVC相机 bool Init(HWND hWnd) { HRESULT hr = CoCreateInstance(CLSID_FilterGraph, nullptr, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void**)&m_pGraph); if (FAILED(hr)) { return false; } hr = CoCreateInstance(CLSID_CaptureGraphBuilder2, nullptr, CLSCTX_INPROC_SERVER, IID_ICaptureGraphBuilder2, (void**)&m_pBuilder); if (FAILED(hr)) { return false; } hr = m_pBuilder->SetFiltergraph(m_pGraph); if (FAILED(hr)) { return false; } hr = CoCreateInstance(CLSID_SampleGrabber, nullptr, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void**)&m_pSampleGrabber); if (FAILED(hr)) { return false; } hr = m_pGraph->AddFilter(m_pSampleGrabber, L"Sample Grabber"); if (FAILED(hr)) { return false; } hr = m_pBuilder->FindInterface(&PIN_CATEGORY_STILL, &MEDIATYPE_Video, m_pCapture, IID_IAMStreamConfig, (void**)&m_pStill); if (FAILED(hr)) { return false; } hr = m_pBuilder->FindInterface(&PIN_CATEGORY_PREVIEW, &MEDIATYPE_Video, m_pCapture, IID_IAMVideoControl, (void**)&m_pControl); if (FAILED(hr)) { return false; } hr = m_pGraph->QueryInterface(IID_IMediaControl, (void**)&m_pMediaControl); if (FAILED(hr)) { return false; } hr = m_pGraph->QueryInterface(IID_IMediaEvent, (void**)&m_pMediaEvent); if (FAILED(hr)) { return false; } hr = m_pSampleGrabber->QueryInterface(IID_ISampleGrabber, (void**)&m_pGrabber); if (FAILED(hr)) { return false; } hr = m_pGrabber->SetCallback(this, 1); if (FAILED(hr)) { return false; } m_hWnd = hWnd; m_bPreviewing = false; return true; } // 开始预览 bool StartPreview() { HRESULT hr = CoCreateInstance(CLSID_VideoCapture, nullptr, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void**)&m_pCapture); if (FAILED(hr)) { return false; } hr = m_pGraph->AddFilter(m_pCapture, L"UVC Camera"); if (FAILED(hr)) { return false; } hr = m_pBuilder->RenderStream(&PIN_CATEGORY_PREVIEW, &MEDIATYPE_Video, m_pCapture, m_pSampleGrabber, nullptr); if (FAILED(hr)) { return false; } hr = m_pMediaControl->Run(); if (FAILED(hr)) { return false; } m_bPreviewing = true; return true; } // 停止预览 bool StopPreview() { HRESULT hr = m_pMediaControl->Stop(); if (FAILED(hr)) { return false; } hr = m_pGraph->RemoveFilter(m_pCapture); if (FAILED(hr)) { return false; } hr = m_pGraph->RemoveFilter(m_pSampleGrabber); if (FAILED(hr)) { return false; } m_pCapture->Release(); m_pCapture = nullptr; m_bPreviewing = false; return true; } // 拍照 bool CaptureImage(const wchar_t* szFileName) { AM_MEDIA_TYPE mt; ZeroMemory(&mt, sizeof(AM_MEDIA_TYPE)); HRESULT hr = m_pStill->GetFormat(&mt); if (FAILED(hr)) { return false; } hr = m_pStill->SetFormat(&mt); if (FAILED(hr)) { return false; } hr = m_pMediaControl->Stop(); if (FAILED(hr)) { return false; } hr = m_pBuilder->RenderStream(&PIN_CATEGORY_STILL, &MEDIATYPE_Video, m_pCapture, m_pSampleGrabber, nullptr); if (FAILED(hr)) { return false; } hr = m_pMediaControl->Run(); if (FAILED(hr)) { return false; } long evCode; hr = m_pMediaEvent->WaitForCompletion(INFINITE, &evCode); hr = m_pGrabber->GetCurrentBuffer(&mt.cbFormat, (BYTE*)mt.pbFormat); if (FAILED(hr)) { return false; } BITMAPINFOHEADER& bmih = *(BITMAPINFOHEADER*)mt.pbFormat; BYTE* pData = nullptr; hr = m_pGrabber->GetCurrentBuffer(&mt.cbBuffer, (BYTE**)&pData); if (FAILED(hr)) { return false; } BITMAPFILEHEADER bmfh; ZeroMemory(&bmfh, sizeof(BITMAPFILEHEADER)); bmfh.bfType = 'MB'; bmfh.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + mt.cbBuffer; bmfh.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER); HANDLE hFile = CreateFile(szFileName, GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); if (hFile == INVALID_HANDLE_VALUE) { return false; } DWORD dwBytesWritten; WriteFile(hFile, &bmfh, sizeof(BITMAPFILEHEADER), &dwBytesWritten, nullptr); WriteFile(hFile, &bmih, sizeof(BITMAPINFOHEADER), &dwBytesWritten, nullptr); WriteFile(hFile, pData, mt.cbBuffer, &dwBytesWritten, nullptr); CloseHandle(hFile); return true; } private: IGraphBuilder* m_pGraph; // 图形构建器 ICaptureGraphBuilder2* m_pBuilder; // 捕获图形构建器 IBaseFilter* m_pCapture; // 捕获过滤器 IAMStreamConfig* m_pStill; // 静态图像流配置器 IAMVideoControl* m_pControl; // 视频控制器 IMediaControl* m_pMediaControl; // 媒体控制器 IMediaEvent* m_pMediaEvent; // 媒体事件 ISampleGrabber* m_pSampleGrabber; // 采样抓取器 ISampleGrabberCB* m_pCallback; // 采样抓取回调函数 HWND m_hWnd; // 窗口句柄 bool m_bPreviewing; // 是否正在预览 }; // 采样抓取回调函数 class SampleGrabberCallback : public ISampleGrabberCB { public: SampleGrabberCallback() : m_pWnd(nullptr), m_pUvcCamera(nullptr) {} void SetWindowHandle(HWND hWnd) { m_pWnd = hWnd; } void SetUvcCamera(UvcCamera* pUvcCamera) { m_pUvcCamera = pUvcCamera; } STDMETHODIMP QueryInterface(REFIID riid, void** ppv) { if (riid == IID_ISampleGrabberCB) { *ppv = (void*)this; return S_OK; } return E_NOINTERFACE; } STDMETHODIMP_(ULONG) AddRef() { return 2; } STDMETHODIMP_(ULONG) Release() { return 1; } STDMETHODIMP SampleCB(double dSampleTime, IMediaSample* pSample) { return S_OK; } STDMETHODIMP BufferCB(double dSampleTime, BYTE* pBuffer, long lBufferSize) { if (m_pWnd) { HDC hDC = GetDC(m_pWnd); BITMAPINFOHEADER bmih; ZeroMemory(&bmih, sizeof(BITMAPINFOHEADER)); bmih.biSize = sizeof(BITMAPINFOHEADER); bmih.biWidth = 640; bmih.biHeight = 480; bmih.biPlanes = 1; bmih.biBitCount = 24; bmih.biCompression = BI_RGB; SetStretchBltMode(hDC, COLORONCOLOR); StretchDIBits(hDC, 0, 0, 640, 480, 0, 0, 640, 480, pBuffer, (BITMAPINFO*)&bmih, DIB_RGB_COLORS, SRCCOPY); ReleaseDC(m_pWnd, hDC); } return S_OK; } private: HWND m_pWnd; // 窗口句柄 UvcCamera* m_pUvcCamera; // UVC相机操作类 }; // 应用程序窗口过程 LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { static UvcCamera uvcCamera; static SampleGrabberCallback callback; switch (uMsg) { case WM_CREATE: if (!uvcCamera.Init(hWnd)) { MessageBox(hWnd, L"Failed to initialize UVC camera!", L"Error", MB_ICONERROR); return -1; } callback.SetWindowHandle(hWnd); callback.SetUvcCamera(&uvcCamera); uvcCamera.SetSampleGrabberCallback(&callback); if (!uvcCamera.StartPreview()) { MessageBox(hWnd, L"Failed to start preview!", L"Error", MB_ICONERROR); return -1; } break; case WM_PAINT: break; case WM_DESTROY: uvcCamera.StopPreview(); PostQuitMessage(0); break; default: return DefWindowProc(hWnd, uMsg, wParam, lParam); } return 0; } // WinMain函数 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { WNDCLASSEX wc; ZeroMemory(&wc, sizeof(wc)); wc.cbSize = sizeof(wc); wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = WindowProc; wc.hInstance = hInstance; wc.hCursor = LoadCursor(nullptr, IDC_ARROW); wc.lpszClassName = L"UvcCamera"; if (!RegisterClassEx(&wc)) { MessageBox(nullptr, L"Failed to register window class!", L"Error", MB_ICONERROR); return -1; } HWND hWnd = CreateWindowEx(0, L"UvcCamera", L"Uvc Camera", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 640, 480, nullptr, nullptr, hInstance, nullptr); if (!hWnd) { MessageBox(nullptr, L"Failed to create window!", L"Error", MB_ICONERROR); return -1; } ShowWindow(hWnd, nCmdShow); MSG msg; while (GetMessage(&msg, nullptr, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return (int)msg.wParam; } ``` 以上代码中,UvcCamera类是一个封装了UVC相机操作的类,主要实现了相机的初始化、预览、停止预览、拍照等功能。SampleGrabberCallback类是一个采样抓取回调函数,实现了当采样抓取完成时的回调函数,用来将采样数据显示到窗口中。WindowProc函数是应用程序窗口过程,主要用来处理窗口消息,包括初始化UVC相机、启动预览、停止预览、拍照等操作。WinMain函数是应用程序入口函数,主要是创建窗口并运行消息循环。 需要注意的是,在使用此示例代码之前,你需要安装DirectShow SDK并链接strmiids.lib库。示例代码中使用的是位图格式保存拍摄的照片,你可以根据需要修改保存格式。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值