在nokia的论坛上提供了调用S60系统摄像头的示例代码,但在使用中我们发现,(1)需要指定系统摄像头app的UID,因为不同型号的S60手机系统摄像头app的UID可能不同,就需要开发者跟据不同手机的摄像头ID进行适配;(2)没有提供拍照后的文件信息,当完成拍照或录像后没法知道所对应的影像文件;(3)完成摄像后没有关闭摄像头,这样在某些手机上(如N70)无法第二次启动摄像头;针对存在的这三个问题,我们做了如下工作:
Ø 自动查找摄像头app的UID;
Ø 打开摄像头
Ø 查找完成操作后的相关影像文件;
Ø 关闭摄像头的系统调用;
一、自动查找摄像头app的UID
通过RApaLsSession的GetAllApps接口可以得到所有系统上应用程序的信息,包括UID,Full path name,Caption ,在其中一一比对系统摄像头所在的文件路径,找到其UID。
具体代码见附录二中的CLaunchCamera::FindCamera
其中对KCameraAppString定义说明如下:
1、KCameraAppString定义了系统摄像头在设备上的路径
模拟器及大部分S60手机上定义如下(测试通过了6681,7610):
_LIT(KCameraAppString , "z://system//apps//camcorder//camcorder.app") ;
但N70手机上,要定义如下:
_LIT(KCameraAppString , "z://system//apps//cammojave//cammojave.app") ;
2、如果FindCamera返回TURE,则找到系统摄像头
二、打开摄像头
通过系统调用,打开设备的摄像头,具体的代码见附录二中的CLaunchCamera::OpenCamera() :
三、查找完成操作后的相关影像文件
当用户完成摄像功能后,会通过MApaEmbeddedDocObserver类的回调函数NotifyExit进行通知,改函数具体定义如下:
void NotifyExit(TExitMode aMode)
其中:
enum TExitMode {
EKeepChanges,
ERevertToSaved,
ENoChanges,
EEmpty
};
EKeepChanges表示用户进行了拍照或摄像操作,ENoChanges表示用户取消了拍照或摄像操作,其它两个不用处理。
在这个唯一可以知道用户进行了操作的通知函数中,没有所拍照的图像文件路径信息,因此我们只有通过遍例设备上的图像文件来找到最新的拍照文件。遍例包括对设备存储分区和存储卡分区(即C盘与E盘),PathInfo类提供了对这两个分区的图像文件与影像文件的访问路径,分别是:
TFileName PhoneIamgePath ;
TFileName PhoneVideoPath ;
TFileName MemoryIamgePath ;
TFileName MemoryVideoPath ;
PhoneIamgePath.Append(PathInfo::PhoneMemoryRootPath()) ;
PhoneIamgePath.Append(PathInfo::ImagesPath());
PhoneVideoPath.Append(PathInfo::PhoneMemoryRootPath()) ;
PhoneVideoPath.Append(PathInfo::VideosPath()) ;
MemoryIamgePath.Append(PathInfo::MemoryCardRootPath()) ;
MemoryIamgePath.Append(PathInfo::ImagesPath()) ;
MemoryVideoPath.Append(PathInfo::MemoryCardRootPath()) ;
MemoryVideoPath.Append(PathInfo::VideosPath()) ;
同时还要注意有些手机可能会对拍的图片上边的文件夹中再进行分类存储,所以查找的时候查找到以上目录的二级子目录就可以满足要求。
查找使用RFs类的GetDir接口,具体定义如下:
TInt GetDir(const TDesC& aName,TUint anEntryAttMask,TUint anEntrySortKey,CDir*& anEntryList) const;
TInt GetDir(const TDesC& aName,TUint anEntryAttMask,TUint anEntrySortKey,CDir*& anEntryList,CDir*& aDirList) const;
其中:
aName:文件夹的完整路径
anEntryAttMask:查找文件选项,这里使用KEntryAttNormal
anEntrySortKey:查找结果的排序选项,这里使用ESortByDate|EDescending,即按时间进行降序排序
anEntryList:查找到的文件例表;
aDirList:查找到的文件目录例表;
具体查找代码见附录二中的CLaunchCamera::GetLastFile与LaunchCamera::GetLastNewFile
四:关闭摄像头的系统调用
在使用完摄像头后要调用CEikProcess的DestroyDocument接口,删除对系统摄像头的调用,否则在有些手机上无法第二次调用摄像,但要注意的时DestroyDocument接口不能在回调通知函数NotifyExit中调用,否则程序会crash,我们的变通方法是CLaunchCamera的外部调用启动一下timer,在timer中轮询调用EnableCloseCamera来判断当前的摄像头是否可以关闭。
附录一:CLaunchCamera的类定义:
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/1327ab569c1ae82736693a50b8e33378.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/1327ab569c1ae82736693a50b8e33378.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/0196c3df5ea9e936f21e9932cca91014.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/1327ab569c1ae82736693a50b8e33378.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/0196c3df5ea9e936f21e9932cca91014.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/1327ab569c1ae82736693a50b8e33378.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/a41954a27d6ad96fa2c2cf816e677448.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/1327ab569c1ae82736693a50b8e33378.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6a9c071a08f1dae2d3e1c512000eef41.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/0196c3df5ea9e936f21e9932cca91014.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
附录二:CLaunchCamera类实现
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
#include <eikenv.h> //for CEikProcess
#include <eikproc.h> //for CEikProcess
#include <PathInfo.h> //for PathInfo
#include <apgcli.h> //for RApaLsSession
#include <apacln.h> // for TApaDocCleanupItem
#include "LaunchCamera.h"
CLaunchCamera* CLaunchCamera::NewL(MCameraEvenCallBack * pEvenCallBack,CEikonEnv * pEikonEnv )
{
CLaunchCamera* self = NewLC(pEvenCallBack ,pEikonEnv);
CleanupStack::Pop(self);
return self;
}
CLaunchCamera* CLaunchCamera::NewLC(MCameraEvenCallBack * pEvenCallBack,CEikonEnv * pEikonEnv )
{
CLaunchCamera* self = new (ELeave) CLaunchCamera;
CleanupStack::PushL(self);
self->ConstructL(pEvenCallBack,pEikonEnv);
return self;
}
void CLaunchCamera::ConstructL(MCameraEvenCallBack * pEvenCallBack , CEikonEnv * pEikonEnv)
{
m_pProcess = NULL ;
m_pAppdoc = NULL ;
m_pCallBack = pEvenCallBack ;
m_pEikonEnv = pEikonEnv ;
m_bCloseCamera = FALSE ;
}
CLaunchCamera::CLaunchCamera()
{
m_pAppdoc = NULL ;
}
CLaunchCamera::~CLaunchCamera()
{
//CloseCamera() ;
m_pEikonEnv = NULL ;
m_pCallBack = NULL ;
}
// Function name : CLaunchCamera::OpenCamera
// Description : Open camera and review
// Return type : TURE if successful, otherwise FALSE
TBool CLaunchCamera::OpenCamera()
{
if( m_pEikonEnv == NULL ) return FALSE ;
TApaAppInfo appInfo;
if( FindCamera( appInfo) == FALSE ) return FALSE ;
m_pProcess = m_pEikonEnv->Process();
m_pAppdoc = m_pProcess->AddNewDocumentL(appInfo.iFullName, appInfo.iUid);
ASSERT( m_pAppdoc != NULL ) ;
TApaDocCleanupItem cleanup(m_pProcess, m_pAppdoc);
CleanupStack::PushL(cleanup);
m_pAppdoc->NewDocumentL();
CleanupStack::Pop(); // cleanup
m_bCloseCamera = FALSE ;
m_pAppdoc->EditL(this, EFalse );
return TRUE ;
}
void CLaunchCamera::CloseCamera()
{
if( m_pProcess )
{
m_pProcess->DestroyDocument(m_pAppdoc) ;
m_pProcess = NULL ;
m_pAppdoc = NULL ;
}
m_bCloseCamera = TRUE ;
}
TBool CLaunchCamera::FindCamera(TApaAppInfo & appInfo)
{
RApaLsSession apaTmpLs;
User::LeaveIfError(apaTmpLs.Connect());
CleanupClosePushL(apaTmpLs);
User::LeaveIfError(apaTmpLs.GetAllApps());
TBool bFindCamera = FALSE ;
appInfo.iFullName.FillZ(appInfo.iFullName.MaxLength()) ;
appInfo.iFullName.Zero() ;
while(apaTmpLs.GetNextApp(appInfo) == KErrNone)
{
appInfo.iFullName.LowerCase() ;
TInt nTmpID = appInfo.iFullName.Compare(KCameraAppString) ;
if( nTmpID == 0 )
{
bFindCamera = TRUE ;
break ;
}
appInfo.iFullName.FillZ(appInfo.iFullName.MaxLength()) ;
appInfo.iFullName.Zero() ;
}
CleanupStack::PopAndDestroy(); // apaTmpLs
return bFindCamera ;
}
// Function name : CLaunchCamera::NotifyExit
// Description : when selected "select", frame will call this function
// Return type : void
// Argument : TExitMode aMode
void CLaunchCamera::NotifyExit(TExitMode aMode)
{
if( aMode == EKeepChanges )
{
//不能在这里调,否则设备为crash
//m_pProcess->DestroyDocument(m_pAppdoc) ;
FindNewFile() ;
}
else
{
//m_pProcess->DestroyDocument(m_pAppdoc) ;
if( m_pCallBack )
m_pCallBack->Camera_HandleFile(CFT_NoFind, _L("FileNotSelect"));
}
m_bCloseCamera = TRUE ;
}
TBool CLaunchCamera::EnableCloseCamera()
{
return m_bCloseCamera ;
}
// Function name : CLaunchCamera::FindNewFile
// Description : Search the last new files
//if find the last new file, call Camera_HandleFile to caller
// Return type : void
void CLaunchCamera::FindNewFile()
{
if( m_pCallBack == NULL ) return ;
TFileName PhoneIamgePath ;
TFileName MemoryIamgePath ;
TFileName PhoneVideoPath ;
TFileName MemoryVideoPath ;
PhoneIamgePath.Append(PathInfo::PhoneMemoryRootPath()) ;
PhoneIamgePath.Append(PathInfo::ImagesPath());
MemoryIamgePath.Append(PathInfo::MemoryCardRootPath()) ;
MemoryIamgePath.Append(PathInfo::ImagesPath()) ;
PhoneVideoPath.Append(PathInfo::PhoneMemoryRootPath()) ;
PhoneVideoPath.Append(PathInfo::VideosPath()) ;
MemoryVideoPath.Append(PathInfo::MemoryCardRootPath()) ;
MemoryVideoPath.Append(PathInfo::VideosPath()) ;
TTime tFileTime(0) ;
TTime tLastFileTime(0) ;
TInt nIndex = -1 ;
//查找最新时间的文件
//find phone card image
if( GetLastFile(PhoneIamgePath , tFileTime) == TRUE )
{
if( tFileTime > tLastFileTime )
{
tLastFileTime = tFileTime ;
nIndex = 0 ;
}
}
//find memory card image
if( GetLastFile(MemoryIamgePath , tFileTime) == TRUE )
{
if( tFileTime > tLastFileTime )
{
tLastFileTime = tFileTime ;
nIndex = 1 ;
}
}
//find phone memory video file
if( GetLastFile(PhoneVideoPath , tFileTime) == TRUE )
{
if( tFileTime > tLastFileTime )
{
tLastFileTime = tFileTime ;
nIndex = 2 ;
}
}
//find memory card video files
if( GetLastFile(MemoryVideoPath , tFileTime) == TRUE )
{
if( tFileTime > tLastFileTime )
{
tLastFileTime = tFileTime ;
nIndex = 3 ;
}
}
//判断是那个目录下的文件
switch(nIndex)
{
case 0:
m_pCallBack->Camera_HandleFile(CFT_Image , PhoneIamgePath) ;
break;
case 1:
m_pCallBack->Camera_HandleFile(CFT_Image , MemoryIamgePath) ;
break;
case 2:
m_pCallBack->Camera_HandleFile(CFT_Video , PhoneVideoPath) ;
break;
case 3:
m_pCallBack->Camera_HandleFile(CFT_Video , MemoryVideoPath) ;
break;
default:
m_pCallBack->Camera_HandleFile(CFT_NoFind , TFileName(0) ) ;
break;
}
}
//
//查找最新的图像文件,只查找到二级目录
//
//
//
TBool CLaunchCamera::GetLastFile(TFileName & filePath , TTime & fileTime)
{
RFs sessionRFs;
User::LeaveIfError(sessionRFs.Connect());
CDir* FileList;
CDir* dirList ;
//读出文件夹下文件信息,并对所有的文件进行降序排列
TInt nError = sessionRFs.GetDir(filePath,KEntryAttNormal, ESortByDate|EDescending , FileList , dirList);
if( nError != KErrNone )
{
//ASSERT(FALSE) ;
sessionRFs.Close() ;
return FALSE;
}
else if( nError == KErrNone )
{
TFileName tmpFileName(0) ;
TTime tmpTime(0) ;
TTime tLastFileTime(0) ;
TFileName tLastFile(0) ;
//file
if( FileList->Count() > 0 )
{
tmpFileName.FillZ(KMaxFileName) ;
tmpFileName.Zero() ;
tmpFileName.Append(filePath) ;
tmpFileName.Append((*FileList)[0].iName) ;
//get file modification time
sessionRFs.Modified(tmpFileName , tmpTime ) ;
if( tmpTime > tLastFileTime )
{
tLastFileTime = tmpTime ;
tLastFile.FillZ(KMaxFileName) ;
tLastFile.Zero() ;
tLastFile.Append(tmpFileName) ;
}
}
delete FileList ;
//dir
TInt nDirCount = dirList->Count() ;
for( int i = 0 ; i< nDirCount ; i++ )
{
tmpFileName.FillZ(KMaxFileName) ;
tmpFileName.Zero() ;
tmpFileName.Append(filePath) ;
tmpFileName.Append((*dirList)[i].iName) ;
tmpFileName.Append(_L("//")) ;
if( GetLastNewFile( tmpFileName , tmpTime ) == TRUE )
{
if( tmpTime > tLastFileTime )
{
tLastFileTime = tmpTime ;
tLastFile.FillZ(KMaxFileName) ;
tLastFile.Zero() ;
tLastFile.Append(tmpFileName) ;
}
}
}
delete dirList;
//是否找到新的文件
if( tLastFileTime > TTime(0) )
{
filePath.FillZ(KMaxFileName) ;
filePath.Zero() ;
filePath.Append(tLastFile) ;
fileTime = tLastFileTime ;
}
else
{
sessionRFs.Close() ;
return FALSE ;
}
}
sessionRFs.Close() ;
return TRUE ;
}
//用KEntryAttNormal只查找目录中的文件
TBool CLaunchCamera::GetLastNewFile(TFileName & filePath , TTime & fileTime)
{
RFs sessionRFs;
User::LeaveIfError(sessionRFs.Connect());
CDir* dirList;
//读出文件夹下文件信息,并对所有的文件进行降序排列
TInt nError = sessionRFs.GetDir(filePath,KEntryAttNormal, ESortByDate|EDescending,dirList);
if( nError != KErrNone )
{
sessionRFs.Close() ;
return FALSE;
}
else if( dirList->Count() > 0 )
{
//get filename
filePath.Append((*dirList)[0].iName) ;
//get file modification time
sessionRFs.Modified(filePath , fileTime ) ;
delete dirList;
}
else
{
sessionRFs.Close() ;
return FALSE;
}
sessionRFs.Close() ;
return TRUE ;
}
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)