由于3G手机应用视频电话的时候要双camera,那么就存在切换的问题,我就在开发板上进行了调试。marvell的zylonite开发板Windows Mobile6.0 BSP关于camera的驱动变化还是很明显的,采用了wince6.0的camera驱动。
找了半天才在微软的MSDN上看到了各个pdd函数的说明。整个设计的原理就是APP先停止preview,发出切换sensor的指令,驱动响应,把sensor相关的都换成另外一个sensor,然后再重新preview。
首先camera的应用程序得自己写,不然双camera的切换无法在应用上修改,于是找了个camera的应用代码参考了下,居然都编译不过,晕,我不是做app的,但是又没有人帮我,只好硬着头皮看资料看代码。幸好自己也做过一点PC上的应用程序,终于看懂,原来采用WTL+directshow写的camera应用,mainframe+manager+ view+setting结构很清晰。应用程序写上如下代码:
BOOL CCameraManager::CameraSwitchSensor(CAMERA_SWITCH sensor)
{ LRESULT hr; CComPtr < IAMCameraControl > pCameraControl; long lCurrentVal,lCurrentFlags; long CameraControl_Switch = CameraControl_Flash+0xff ; if (sensor >= SENSOR_MAX) return FALSE; CHK(m_pVideoCapFilter -> QueryInterface( & pCameraControl)); CHK(pCameraControl -> Get(CameraControl_Switch, & lCurrentVal, & lCurrentFlags)); if (lCurrentVal == sensor) { // return TRUE; } CHK(pCameraControl-> Set(CameraControl_Switch , sensor, CameraControl_Flags_Manual)); err_exit: if ( FAILED( hr )) { CCameraParam::GetCameraParam() -> GetErrorlog() -> WriteLog(L " Error: CCameraManager::CameraSwitchSensor " ); return FALSE; } return TRUE; }
红色字体为关键,由于通过dshow才控制camera,所以得找到dshow动作对应于驱动的接口,参考wm的文档并调试,发现这个可以控制camera,会调用CAM_IOControl,Ioctl=IOCTL_CS_PROPERTY,
EXTERN_C BOOL CAM_IOControl ( DWORD dwContext, DWORD Ioctl, UCHAR
*
pInBufUnmapped, DWORD InBufLen, UCHAR
*
pOutBufUnmapped, DWORD OutBufLen, DWORD
*
pdwBytesTransferred )
{ DEBUGMSG( ZONE_FUNCTION, ( _T( " CAM_IOControl(%08x): IOCTL:0x%x, InBuf:0x%x, InBufLen:%d, OutBuf:0x%x, OutBufLen:0x%x) " ), dwContext, Ioctl, pInBufUnmapped, InBufLen, pOutBufUnmapped, OutBufLen ) ); // NKDbgPrintfW(L"CAM_IOControl 0x%x", Ioctl); UCHAR * pInBuf = NULL; UCHAR * pOutBuf = NULL; DWORD dwErr = ERROR_INVALID_PARAMETER; BOOL bRc = FALSE; if ( ( NULL == pInBufUnmapped ) || ( InBufLen < sizeof ( CSPROPERTY ) ) || ( NULL == pdwBytesTransferred ) ) { SetLastError( dwErr ); return bRc; } // All buffer accesses need to be protected by try/except pInBuf = pInBufUnmapped; pOutBuf = pOutBufUnmapped; CAMERAOPENHANDLE * pCamOpenHandle = reinterpret_cast < CAMERAOPENHANDLE *> ( dwContext ); CAMERADEVICE * pCamDevice = pCamOpenHandle -> pCamDevice; CSPROPERTY * pCsProp = reinterpret_cast < CSPROPERTY *> (pInBuf); if ( NULL == pCsProp ) { DEBUGMSG( ZONE_IOCTL | ZONE_ERROR, (_T( " CAM_IOControl(%08x): Invalid Parameter. " ), dwContext ) ); return dwErr; } switch ( Ioctl ) { // Power Management Support. case IOCTL_POWER_CAPABILITIES: case IOCTL_POWER_QUERY: case IOCTL_POWER_SET: case IOCTL_POWER_GET: { NKDbgPrintfW(L " camera IOCTL_POWER+ " ); DEBUGMSG( ZONE_IOCTL, ( _T( " CAM_IOControl(%08x): Power Management IOCTL " ), dwContext ) ); __try { dwErr = pCamDevice -> AdapterHandlePowerRequests(Ioctl, pInBuf, InBufLen, pOutBuf, OutBufLen, pdwBytesTransferred ); } __except ( EXCEPTION_EXECUTE_HANDLER ) { DEBUGMSG( ZONE_IOCTL, ( _T( " CAM_IOControl(%08x):Exception in Power Management IOCTL " ), dwContext ) ); } break ; } case IOCTL_CS_PROPERTY : { DEBUGMSG( ZONE_IOCTL, ( _T( " CAM_IOControl(%08x): IOCTL_CS_PROPERTY " ), dwContext ) ); __try { dwErr = pCamDevice -> AdapterHandleCustomRequests ( pInBuf,InBufLen, pOutBuf, OutBufLen, pdwBytesTransferred ); if ( ERROR_NOT_SUPPORTED == dwErr ) { if ( TRUE == IsEqualGUID( pCsProp -> Set, CSPROPSETID_Pin ) ) { dwErr = pCamDevice -> AdapterHandlePinRequests( pInBuf, InBufLen, pOutBuf, OutBufLen, pdwBytesTransferred ); } else if ( TRUE == IsEqualGUID( pCsProp -> Set, CSPROPSETID_VERSION ) ) { dwErr = pCamDevice -> AdapterHandleVersion( pOutBuf, OutBufLen, pdwBytesTransferred ); } else if ( TRUE == IsEqualGUID( pCsProp -> Set, PROPSETID_VIDCAP_VIDEOPROCAMP ) ) { dwErr = pCamDevice -> AdapterHandleVidProcAmpRequests( pInBuf,InBufLen, pOutBuf, OutBufLen, pdwBytesTransferred ); } else if ( TRUE == IsEqualGUID( pCsProp -> Set, PROPSETID_VIDCAP_CAMERACONTROL ) ) { dwErr = pCamDevice -> AdapterHandleCamControlRequests( pInBuf,InBufLen, pOutBuf, OutBufLen, pdwBytesTransferred ); } else if ( TRUE == IsEqualGUID( pCsProp -> Set, PROPSETID_VIDCAP_VIDEOCONTROL ) ) { dwErr = pCamDevice -> AdapterHandleVideoControlRequests( pInBuf,InBufLen, pOutBuf, OutBufLen, pdwBytesTransferred ); } else if ( TRUE == IsEqualGUID( pCsProp -> Set, PROPSETID_VIDCAP_DROPPEDFRAMES) ) { dwErr = pCamDevice -> AdapterHandleDroppedFramesRequests( pInBuf,InBufLen, pOutBuf, OutBufLen, pdwBytesTransferred ); } } } __except ( EXCEPTION_EXECUTE_HANDLER ) { DEBUGMSG( ZONE_IOCTL, ( _T( " CAM_IOControl(%08x):Exception in IOCTL_CS_PROPERTY " ), dwContext ) ); } break ; } default : { DEBUGMSG( ZONE_IOCTL, (_T( " CAM_IOControl(%08x): Unsupported IOCTL code %u " ), dwContext, Ioctl ) ); dwErr = ERROR_NOT_SUPPORTED; break ; } } // pass back appropriate response codes SetLastError( dwErr ); return ( ( dwErr == ERROR_SUCCESS ) ? TRUE : FALSE ); }
哈哈,红色字体就是
IAMCameraControl的set接口对应于 driver会被调用的地方,由于这里还是driver的mdd层,所以尽量不改,于是放到pdd层来实现,很自然的想到
AdapterHandleCustomRequests ,该函数会调用pdd的DWORD CCameraPdd::HandleAdapterCustomProperties( PUCHAR pInBuf, DWORD InBufLen, PUCHAR pOutBuf, DWORD OutBufLen, PDWORD pdwBytesTransferred ),本来这函数是空函数,结果这里就可以放入双camera的切换代码了(不知道是微软写的还是marvell写的,想的真周到,以前还没有的,现在就有了
,赞一个),实现如下:
DWORD CCameraPdd::HandleAdapterCustomProperties( PUCHAR pInBuf, DWORD InBufLen, PUCHAR pOutBuf, DWORD OutBufLen, PDWORD pdwBytesTransferred )
{ typedef enum { SENSOR_FRONT, SENSOR_REAR, SENSOR_MAX, } CAMERA_SWITCH; CONST LONG CAMERACONTROL_SWITCH = CSPROPERTY_CAMERACONTROL_FLASH+0XFF ; PCSPROPERTY_CAMERACONTROL_S pCsPropCamControlInput = NULL; CSPROPERTY * pCsProp = reinterpret_cast < CSPROPERTY *> (pInBuf); if( ( TRUE == IsEqualGUID( pCsProp->Set, PROPSETID_VIDCAP_CAMERACONTROL ) ) && (pCsProp->Id == CAMERACONTROL_SWITCH)) { pCsPropCamControlInput = reinterpret_cast < PCSPROPERTY_CAMERACONTROL_S > (pInBuf); switch (pCsProp -> Flags) { case CSPROPERTY_TYPE_GET: break ; case CSPROPERTY_TYPE_SET: if(pCsPropCamControlInput->Value == SENSOR_FRONT) { sensor=sensor_ov7660; set_video_Format(m_pModeVideoFormat, sensor); NKDbgPrintfW(L"sensor=sensor_ov7660; "); } else if((pCsPropCamControlInput->Value == SENSOR_REAR)) { sensor=sensor_ov2630; set_video_Format(m_pModeVideoFormat, sensor); NKDbgPrintfW(L"sensor=sensor_ov2630; "); } break ; default : break ; } return ERROR_SUCCESS; } else { DEBUGMSG( ZONE_IOCTL, ( _T( " IOControl Adapter PDD: Unsupported PropertySet Request " ))); return ERROR_NOT_SUPPORTED; } }
这里都是要自己写的啦,太多了就不讲了,不过函数
set_video_Format要讲一下,这个函数很重要,调试时发现不是dma没有,就是应用获取的格式不对,这个函数的实现就是为了双camera切换专门定做的
DWORD CCameraPdd::set_video_Format(PPINVIDEOFORMAT pModeVideoFormat, Sensor
*
sensor)
{ if (NULL == pModeVideoFormat || NULL == sensor) return ERROR_INVALID_TARGET_HANDLE; for (ULONG i = 0 ; i < m_ulCTypes; i ++ ) { unsigned int nformats; PCS_DATARANGE_VIDEO ** formats; formats = & pModeVideoFormat[i].pCsDataRangeVideo; nformats = sensor-> get_formats(i, formats); pModeVideoFormat[i].ulAvailFormats = nformats; if ( NULL == pModeVideoFormat[i].pCsDataRangeVideo ) return ERROR_INSUFFICIENT_BUFFER; } for (ULONG i = 0 ; i < m_ulCTypes; i ++ ) { for (ULONG j = 0 ; j < pModeVideoFormat[i].ulAvailFormats; j ++ ) { PCS_VIDEOINFOHEADER pCsVideoInfoHdr; pCsVideoInfoHdr = & pModeVideoFormat[i].pCsDataRangeVideo[j] -> VideoInfoHeader; camera_cfg_t * cfg = sensor -> get_camera_cfg(pCsVideoInfoHdr, i); if ( ! cfg) NKDbgPrintfW(L " failed to get camera configuration!!! " ); sensor -> init_camera_cfg(cfg, i); qci_notify_formats( &cfg-> qci_format); } } return ERROR_SUCCESS; }
这样不同sensor对应于QCI的DMA buf也有了,APP preview的格式也与不同的sensor对应起来了很好,而且还不会多分配buf。
CComPtr
<
IAMStreamConfig
>
pStremConfig; CHK(m_pCapGraphBuilder
->
FindInterface(
&
PIN_CATEGORY_CAPTURE,
0
, m_pVideoCapFilter, IID_IAMStreamConfig, (
void
**
)
&
pStremConfig)) CHK(pStremConfig
->
GetNumberOfCapabilities(
&
iCount,
&
iSize));
if
(iSize
==
sizeof
(VIDEO_STREAM_CONFIG_CAPS))
{ for ( int iFormat = 0 ; iFormat < iCount; iFormat ++ ) { VIDEO_STREAM_CONFIG_CAPS scc; AM_MEDIA_TYPE * pmtConfig; CHK(pStremConfig ->GetStreamCaps(iFormat, &pmtConfig, (BYTE*)& scc)); if (SUCCEEDED(hr)) { if (scc.MinOutputSize.cx == pSize -> cx && scc.MinOutputSize.cy == pSize -> cy) { CHK(pStremConfig -> SetFormat(pmtConfig)); DeleteMediaType(pmtConfig); break ; } DeleteMediaType(pmtConfig); } } }
上面红色代码终于得到正确的结果了,于是添加app代码
LRESULT CMainFrame::OnSwitchsensorFront(WORD
/* wNotifyCode */
, WORD
/* wID */
, HWND
/* hWndCtl */
, BOOL
&
/* bHandled */
)
{ // TODO: Add your command handler code here m_view.StopGraph(); m_view.CameraSwitchSensor(SENSOR_FRONT); m_view.SaveElectricity(TRUE); m_view.StartupCameraMode(m_view.GetCurrentCameraMode()); m_view.RunGraph(); m_view.RedrawWindow(); return 0 ; }
LRESULT CMainFrame::OnSwitchsensorRear(WORD
/* wNotifyCode */
, WORD
/* wID */
, HWND
/* hWndCtl */
, BOOL
&
/* bHandled */
)
{ // TODO: Add your command handler code here m_view.StopGraph(); m_view.CameraSwitchSensor(SENSOR_REAR); m_view.SaveElectricity(TRUE); m_view.StartupCameraMode(m_view.GetCurrentCameraMode()); m_view.RunGraph(); m_view.RedrawWindow(); return 0 ; }
bsp和APP一起调试一下通过。
这是最后的结果,过程做了很多调试,发现只有这些不能去掉。在添加一个切换的菜单时,发现居然出现不了,原来是多语言包的问题,资源和程序是分开的,用0804.MUI。在.rc文件添加
/// //
//
//
Menu
//
IDR_MAINFRAME MENU BEGIN MENUITEM
"
拍摄
"
, IDB_CAPTUREORSTOP POPUP
"
菜单
"
BEGIN POPUP
"切换摄像头" BEGIN MENUITEM "前置", ID_SWITCHSENSOR_FRONT MENUITEM "后置"
, ID_SWITCHSENSOR_REAR END END END
编译后放到exe文件同一个目录下搞定。
还有很多bsp和directshow的细节问题太多,就不写了,主要是说明下思路,现在还有个问题就是切换的太慢了,要5-9秒钟,有空看看瓶颈在哪里,优化下看看。去掉了些调试功能,好像快了一个2秒一个4秒。
转自:http://www.cnblogs.com/lucky-apple/archive/2008/05/24/1206350.html