7. 将设备从系统中移走时的事件通知(Device remove Notify) 如果用户将一个graph正在使用的即插即用型的设备从系统中去掉,filter图表管理器就会发送一个EC_DEVICE_LOST事件通知,如果该设备又可以使用了,filter图表管理器就发送另外的一个EC_DEVICE_LOST通知,但是先前组建的捕捉filter graph图就没法用了,用户必须重新组建graph图。 当系统中有新的设备添加时,dshow是不会发送任何通知的,所以,应用程序如果想要知道系统中何时添加新的设备,应用程序可以监控WM_DEVICECHANGE消息。 8. 从静止图像pin中捕捉图片 有些照相机,摄像头除了可以捕获视频流以外还可以捕获单张的,静止的图片。通常,静止的图片的质量要比流的质量要高。摄像头一般都一个按钮来触发,或者是支持软件触发。支持输出静态图片的摄像头一般都要提供一个静态图片pin,这个pin的种类是PIN_CATEGORY_STILL。 从设备中获取静态图片,我们一般推荐使用windows Image Acquisition (WIA) APIs。当然,你也可以用dshow来获取图片。 在graph运行的时候利用IAMVideoControl::SetMode来触发静态的pin。代码如下 pControl->Run(); // Run the graph. IAMVideoControl *pAMVidControl = NULL; hr = pCap->QueryInterface(IID_IAMVideoControl, (void**)&pAMVidControl); if (SUCCEEDED(hr)) { // Find the still pin. IPin *pPin = 0; hr = pBuild->FindPin(pCap, PINDIR_OUTPUT, &PIN_CATEGORY_STILL, 0, FALSE, 0, &pPin); if (SUCCEEDED(hr)) { hr = pAMVidControl->SetMode(pPin, VideoControlFlag_Trigger); pPin->Release(); } pAMVidControl->Release(); } 首先向capture Filter 请求IAMVideoContol,如果支持该接口,就调用ICaptureGraphBuilder2::FindPin请求指向静止pin 的指针,然后调用pin的put_Mode方法。 根据不同的摄像头,你可能静态pin连接前要render 该pin。 捕捉静态图片常用的filter是Sample Grabber filter,Sample Grabber使用了一个用户定义的回调汗水来处理图片。关于这个filter的详细用法,参见Using the Sample Grabber.。 下面的例子假设静态pin传递的是没有压缩的RGB图片。首先定义一个类,从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; 然后将捕捉filter的静态pin连接到Sample Grabber,将Sample Grabber连接到Null Renderer filter。Null Renderer仅仅是将她接收到的sample丢弃掉。实际的工作都是在回调函数里进行,连接Null Renderer 仅仅是为了给Sample Grabber's 输出pin上连接点东西。具体见下面的代码 // 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_NullRendere, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void**)&pNull); hr = pGraph->AddFilter(pSG_Filter, L"NullRender"); 然后通过RenderStream将still pin ,sample grabber ,null Renderer连接起来 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指针,来通过这个指针可以分配内存。 // Configure the Sample Grabber. ISampleGrabber *pSG; hr = pSG_Filter->QueryInterface(IID_ISampleGrabber, (void**)&pSG); pSG->SetOneShot(FALSE); pSG->SetBufferSamples(TRUE); 设置你的回调对象 pSG->SetCallback(&g_StillCapCB, 0); // 0 = Use the SampleCB callback method 获取静态pin和sample 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); 看看下面的回调类吧。这个类从ISampleGrabber接口派生,但是它没有保持引用计数,因为应用程序在堆上创建这个对象,在整个graph的生存周期它都存在。 所有的工作都在BufferCB函数里完成,当有一个新的sample到来的时候,这个函数就会被sample Grabber调用到。在下面的例子里,bitmap被写入到一个文件中 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 "MB". 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; } };

评论