CAM 开发日志

由于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秒。

posted on 2008-05-24 12:02  Luckyer  阅读(1874)  评论(0编辑  收藏  举报

导航