privatevoidinitCamera()
[code]{
Cameracamera;//定义系统所用的照相机
if(!isPreview)
{
camera=Camera.open();//1.调用Camera的open()方法打开相机。
}
if(camera!=null&&!isPreview)
{
try
{
//2.调用Camera的setParameters()方法获取拍照参数。该方法返回一个Camera.Parameters对象。
Camera.Parametersparameters=camera.getParameters();
//3.调用Camera.Paramers对象方法设置拍照参数
//设置预览照片的大小
parameters.setPreviewSize(screenWidth,screenHeight);
//每秒显示4帧
parameters.setPreviewFrameRate(4);
//设置图片格式
parameters.setPictureFormat(PixelFormat.JPEG);
//设置JPG照片的质量
parameters.set("jpeg-quality",85);
//设置照片的大小
parameters.setPictureSize(screenWidth,screenHeight);
//4.调用Camera的setParameters,并将Camera.Paramers作为参数传入,这样即可对相机的拍照参数进行控制
camera.setParameters(parameters);
/**
*5.调用Camera的startPreview()方法开始预览取景,在预览取景之前需要调用
*Camera的setPreViewDisplay(SurfaceHolderholder)方法设置使用哪个SurfaceView来显示取景图片。
*通过SurfaceView显示取景画面
*/
camera.setPreviewDisplay(surfaceHolder);
//6.开始预览
camera.startPreview();
//7.自动对焦
camera.autoFocus(afcb);
//8.调用Camera的takePicture()方法进行拍照.
camera.takePicture(null,null,myjpegCallback);
}
catch(Exceptione)
{
e.printStackTrace();
}
isPreview=true;
}
}
[/code]
别指望上边的程序能运行,这里只是为了介绍其流程,预览界面什么都没有.这里我们只看open的过程,它调用
--->frameworks/base/core/java/android/hardware/Camera.java
publicstaticCameraopen(){
intnumberOfCameras=getNumberOfCameras();
CameraInfocameraInfo=newCameraInfo();
for(inti=0;i<numberOfCameras;i++){
getCameraInfo(i,cameraInfo);
if(cameraInfo.facing==CameraInfo.CAMERA_FACING_BACK){//此处强制打开后置,遍历存在的摄像头之后还是没有后置则会返回null
returnnewCamera(i);//此处新建一个camera(id)对象
}
}
returnnull;
}
getNumberOfCameras和getCameraInfo这两个介绍cameraHAL的时候再具体介绍.下面看newcamera的实现.
Camera(intcameraId){
mShutterCallback=null;
mRawImageCallback=null;
mJpegCallback=null;
mPreviewCallback=null;
mPostviewCallback=null;
mZoomListener=null;
Looperlooper;
if((looper=Looper.myLooper())!=null){
mEventHandler=newEventHandler(this,looper);
}elseif((looper=Looper.getMainLooper())!=null){
mEventHandler=newEventHandler(this,looper);
}else{
mEventHandler=null;
}
native_setup(newWeakReference<Camera>(this),cameraId);//此处camera的初始化
}
privatenativefinalvoidnative_setup(Objectcamera_this,intcameraId);
上文我们就已经知道native_setup调用的是jni接口.即
frameworks/base/core/jni/android_hardware_Camera.cpp的android_hardware_Camera_native_setup
staticvoidandroid_hardware_Camera_native_setup(JNIEnv*env,jobjectthiz,
jobjectweak_this,jintcameraId)
{
sp<Camera>camera=Camera::connect(cameraId);//调用的是frameworks/base/libs/camera/Camera.cpp
...
}
然后来看client的调用(注意调用时sp<Camera>,有玄机哦,自行百度,我还不大懂).
frameworks/base/libs/camera/Camera.cpp
sp<Camera>Camera::connect(intcameraId)
{
LOGV("connect");
sp<Camera>c=newCamera();
constsp<ICameraService>&cs=getCameraService();
if(cs!=0){
c->mCamera=cs->connect(c,cameraId);//此处调用frameworks/base/services/camera/libcameraservice/CameraService.cpp
}
if(c->mCamera!=0){
c->mCamera->asBinder()->linkToDeath(c);
c->mStatus=NO_ERROR;
}else{
c.clear();
}
returnc;
}
就是这个connect,它直接调用了cameraservice.
frameworks/base/services/camera/libcameraservice/CameraService.cpp
sp<ICamera>CameraService::connect(
constsp<ICameraClient>&cameraClient,intcameraId){
.....
hardware=newCameraHardwareInterface(camera_device_name);
if(hardware->initialize(&mModule->common)!=OK){//此处调用frameworks/base/services/camera/libcameraservice/CameraHardwareInterface.h初始化camera
hardware.clear();
returnNULL;
}
client=newClient(this,cameraClient,hardware,cameraId,info.facing,callingPid);
mClient[cameraId]=client;
LOG1("CameraService::connectX");
returnclient;
}
继续往下调用,它应该去调用HAL层的初始化函数,刚开始找的时候怎么也找不到,原来是在头文件中调用的,我嘞个去.
frameworks/base/services/camera/libcameraservice/CameraHardwareInterface.h
status_tinitialize(hw_module_t*module)
{
LOGI("Openingcamera%s",mName.string());
intrc=module->methods->open(module,mName.string(),//module就是hardware所建立的module.通过hardware.h可以看出最终调用的是厂商的hardware
(hw_device_t**)&mDevice);
if(rc!=OK){
LOGE("Couldnotopencamera%s:%d",mName.string(),rc);
returnrc;
}
initHalPreviewWindow();
returnrc;
}
下面让我们看看这个open.以下就是所谓的HAL
它是这样定义的
statichw_module_methods_tcamera_module_methods={
open:HAL_camera_device_open
};
staticintHAL_camera_device_open(conststructhw_module_t*module,
constchar*id,
structhw_device_t**device)
{
intcameraId=atoi(id);
...//此处省略参数的配置,介绍HAL的时候再详细介绍
g_cam_device->priv=newCameraHardwareSec(cameraId,g_cam_device);//此处初始化具体的camera设备
done:
*device=(hw_device_t*)g_cam_device;
return0;
}
camera设备的初始化:
CameraHardwareSec::CameraHardwareSec(intcameraId,camera_device_t*dev)
:
mCaptureInProgress(false),
mParameters(),
mFrameSizeDelta(0),
mCameraSensorName(NULL),
mUseInternalISP(false),
mSkipFrame(0),
mNotifyCb(0),
mDataCb(0),
mDataCbTimestamp(0),
mCallbackCookie(0),
mMsgEnabled(CAMERA_MSG_RAW_IMAGE),
mRecordRunning(false),
mPostViewWidth(0),
mPostViewHeight(0),
mPostViewSize(0),
mCapIndex(0),
mCurrentIndex(-1),
mOldRecordIndex(-1),
mRecordHint(false),
mRunningSetParam(0),
mTouched(0),
mFDcount(0),
mRunningThread(0),
mHalDevice(dev)
{
.....
ret=mSecCamera->CreateCamera(cameraId);//Step1:此处新建camera
if(ret<0){
mSecCamera->DestroyCamera();
}
initDefaultParameters(cameraId);//使用默认的参数初始化指定摄像头
mPreviewThread=newPreviewThread(this);//预览线程
mPreviewFimcThread=newPreviewFimcThread(this);//预览线程
mRecordFimcThread=newRecordFimcThread(this);//录像线程
mSnapshotFimcThread=newSnapshotFimcThread(this);//快照线程
mCallbackThread=newCallbackThread(this);//回调线程
mAutoFocusThread=newAutoFocusThread(this);//自动对焦线程
mPictureThread=newPictureThread(this);//照相线程
}
介绍CreateCamera之前要先知道一些定义,这里也是三星驱动中FIMCx是怎么被配置成不同功能的:
#defineCAMERA_DEV_NAME"/dev/video0"
#defineCAMERA_DEV_NAME2"/dev/video2"
#defineCAMERA_DEV_NAME3"/dev/video1"
具体实现:
boolSecCamera::CreateCamera(intindex){
...//此处省略n行
/*FIMC0open*/
ret=createFimc(&m_cam_fd,CAMERA_DEV_NAME,V4L2_BUF_TYPE_VIDEO_CAPTURE,index);//Step1-1:和底层打交道开始打开FIMC0作为取景输入(##defineCAMERA_DEV_NAME"/dev/video0")
CHECK(ret);
m_camera_use_ISP=getUseInternalISP();
if(m_camera_use_ISP){
if(!m_recording_en)
fimc_v4l2_s_fmt_is(m_cam_fd,m_preview_max_width,m_preview_max_height,
m_preview_v4lformat,(enumv4l2_field)IS_MODE_PREVIEW_STILL);
else
fimc_v4l2_s_fmt_is(m_cam_fd,m_preview_max_width,m_preview_max_height,
m_preview_v4lformat,(enumv4l2_field)IS_MODE_PREVIEW_VIDEO);
}
ret=fimc_v4l2_s_fmt(m_cam_fd,m_preview_max_width,m_preview_max_height,
m_preview_v4lformat,V4L2_FIELD_ANY,PREVIEW_NUM_PLANE);
CHECK(ret);
initParameters(m_camera_use_ISP);
if(m_camera_use_ISP){//使用4412自带的ISP
/*FIMC2openforrecordingandzsl,videosnapshotm2m*/
ret=createFimc(&m_cam_fd3,CAMERA_DEV_NAME2,V4L2_BUF_TYPE_VIDEO_OUTPUT,index);//Step1-2:打开FIMC2作为输出设备,(将图像数据输出到FIMC2进行处理)
CHECK(ret);
/*FIMC1openforpreviewm2m*/
ret=createFimc(&m_cam_fd2,CAMERA_DEV_NAME3,V4L2_BUF_TYPE_VIDEO_OUTPUT,index);//Step1-3:打开FIMC1作为输出设备,(将图像数据输出到FIMC2进行处理)
CHECK(ret);
}else{//不使用4412自带的ISP
/*FIMC1open*///Step1-4:打开FIMC1作为输入设备
ret=createFimc(&m_cam_fd2,CAMERA_DEV_NAME3,V4L2_BUF_TYPE_VIDEO_CAPTURE,index);
CHECK(ret);
}
setExifFixedAttribute();
if(m_camera_use_ISP){//如果使用4412自带ISP,则定义如下
m_prev_fd=m_cam_fd2;//此处定义fimc1为预览fimc0为取景
m_cap_fd=m_cam_fd3;//此处定义fimc2为照相
m_rec_fd=m_cam_fd3;//此处定义fimc2为录像
m_num_capbuf=CAP_BUFFERS;
}else{//如果不使用4412自带ISP,则定义如下
m_prev_fd=m_cam_fd;//此处定义fimc0为预览
m_cap_fd=m_cam_fd;//此处定义fimc0为照相
m_rec_fd=m_cam_fd2;//此处定义fimc1为录像
m_num_capbuf=1;
}
}
return0;
}
看到了吧,如上就是配置不同的FIMC作为不同功能的地方.这里指跟踪createFimc函数
intSecCamera::createFimc(int*fp,char*dev_name,intmode,intindex)
{
structv4l2_formatfmt;
intret=0;
*fp=open(dev_name,O_RDWR);//打开指定的FIMCx即对应设备节点/dev/videox
if(fp<0){
return-1;
}
if(mode==V4L2_BUF_TYPE_VIDEO_CAPTURE){//如果buf类型为v4l2capture(捕获、输入)此处对应Step1-1和Step1-4
ret=fimc_v4l2_querycap(*fp);//查询设备是否支持V4L2_BUF_TYPE_VIDEO_CAPTUREStep1-1-1
CHECK(ret);
if(!fimc_v4l2_enuminput(*fp,index)){//查询是否存在此摄像头Step1-1-2
return-1;
}
ret=fimc_v4l2_s_input(*fp,index);//选择此摄像头作为输入设备Step1-1-3
CHECK(ret);
}elseif(mode==V4L2_BUF_TYPE_VIDEO_OUTPUT){//如果缓冲区类型为V4L2_BUF_TYPE_VIDEO_OUTPUT(输出)此处对应Step1-2和Step1-3
ret=fimc_v4l2_querycap_m2m(*fp);//查询是否存在此设备及其能力是否支持Step1-2-1
CHECK(ret);
/*mallocfimc_outinfostructure*/
fmt.type=V4L2_BUF_TYPE_VIDEO_OUTPUT;
if(ioctl(*fp,VIDIOC_G_FMT,&fmt)<0){//查询设备是否支持视频输出(处理图像)V4L2接口哦
return-1;
}
}
returnret;
}
具体函数:
Step1-1-1:
staticintfimc_v4l2_querycap(intfp)
{
structv4l2_capabilitycap;
if(ioctl(fp,VIDIOC_QUERYCAP,&cap)<0){//查询设备能力//Step1-1-1-1ioctl的底层实现
return-1;
}
if(!(cap.capabilities&V4L2_CAP_VIDEO_CAPTURE)){//判断设备是否是图像采集设备
return-1;
}
return0;
}
Step1-1-2:
staticconst__u8*fimc_v4l2_enuminput(intfp,intindex)
{
staticstructv4l2_inputinput;
input.index=index;
if(ioctl(fp,VIDIOC_ENUMINPUT,&input)!=0){//查询是否存在此摄像头设备
returnNULL;
}
LOGI("Nameofinputchannel[%d]is%s",input.index,input.name);
returninput.name;
}
Step1-1-1-1ioctl的底层实现:这里就该调用到内核层了,让我们先看一下V4L2基本定义:
VIDIOC_QUERYCAP/*获取设备支持的操作*/
VIDIOC_G_FMT/*获取设置支持的视频格式*/
VIDIOC_S_FMT/*设置捕获视频的格式*/
VIDIOC_REQBUFS/*向驱动提出申请内存的请求*/
VIDIOC_QUERYBUF/*向驱动查询申请到的内存*/
VIDIOC_QBUF/*将空闲的内存加入可捕获视频的队列*/
VIDIOC_DQBUF/*将已经捕获好视频的内存拉出已捕获视频的队列*/
VIDIOC_STREAMON/*打开视频流*/
VIDIOC_STREAMOFF/*关闭视频流*/
VIDIOC_QUERYCTRL/*查询驱动是否支持该命令*/
VIDIOC_G_CTRL/*获取当前命令值*/
VIDIOC_S_CTRL/*设置新的命令值*/
VIDIOC_G_TUNER/*获取调谐器信息*/
VIDIOC_S_TUNER/*设置调谐器信息*/
VIDIOC_G_FREQUENCY/*获取调谐器频率*/
VIDIOC_S_FREQUENCY/*设置调谐器频率*/
VIDIOC_CROPCAP/*视频裁剪和缩放功能信息*/
VIDIOC_G_CROP/*获取当前的裁剪矩形*/
VIDIOC_S_CROP/*设置当前的裁剪矩形*/
VIDIOC_ENUM_INPUT/*枚举视频输入*/
VIDIOC_G_INPUT/*查询当前视频输入设备是哪个*/
VIDIOC_S_INPUT/*选择当前视频输入设备是哪个*/
VIDIOC_G_PARM/*获取流参数*/
VIDIOC_S_PARM/*设置流参数*/
VIDIOC_QUERYCTRL/*枚举控制项目*/
VIDIOC_QUERYMENU/*枚举菜单控制项目*/
VIDIOC_G_FBUF/*获取帧缓冲区*/获取参数?
VIDIOC_S_FBUF/*覆盖帧缓冲区*/设置参数?
后边这两个暂时没看.
最终调用的函数是这样定义的.至于如何调用到这里,稍后专门讲驱动的时候再详细写.
drivers/media/video/samsung/fimc/fimc_v4l2.c
conststructv4l2_ioctl_opsfimc_v4l2_ops={
.vidioc_querycap=fimc_querycap,
.vidioc_reqbufs=fimc_reqbufs,
.vidioc_querybuf=fimc_querybuf,
.vidioc_g_ctrl=fimc_g_ctrl,
.vidioc_g_ext_ctrls=fimc_g_ext_ctrls,
.vidioc_s_ctrl=fimc_s_ctrl,
.vidioc_s_ext_ctrls=fimc_s_ext_ctrls,
.vidioc_cropcap=fimc_cropcap,
.vidioc_g_crop=fimc_g_crop,
.vidioc_s_crop=fimc_s_crop,
.vidioc_streamon=fimc_streamon,
.vidioc_streamoff=fimc_streamoff,
.vidioc_qbuf=fimc_qbuf,
.vidioc_dqbuf=fimc_dqbuf,
.vidioc_enum_fmt_vid_cap=fimc_enum_fmt_vid_capture,
.vidioc_g_fmt_vid_cap=fimc_g_fmt_vid_capture,
.vidioc_s_fmt_vid_cap=fimc_s_fmt_vid_capture,
.vidioc_s_fmt_type_private=fimc_s_fmt_vid_private,
.vidioc_try_fmt_vid_cap=fimc_try_fmt_vid_capture,
.vidioc_enum_input=fimc_enum_input,
.vidioc_g_input=fimc_g_input,
.vidioc_s_input=fimc_s_input,
.vidioc_g_parm=fimc_g_parm,
.vidioc_s_parm=fimc_s_parm,
.vidioc_queryctrl=fimc_queryctrl,
.vidioc_querymenu=fimc_querymenu,
.vidioc_g_fmt_vid_out=fimc_g_fmt_vid_out,
.vidioc_s_fmt_vid_out=fimc_s_fmt_vid_out,
.vidioc_try_fmt_vid_out=fimc_try_fmt_vid_out,
.vidioc_g_fbuf=fimc_g_fbuf,
.vidioc_s_fbuf=fimc_s_fbuf,
.vidioc_try_fmt_vid_overlay=fimc_try_fmt_overlay,
.vidioc_g_fmt_vid_overlay=fimc_g_fmt_vid_overlay,
.vidioc_s_fmt_vid_overlay=fimc_s_fmt_vid_overlay,
.vidioc_enum_framesizes=fimc_enum_framesizes,
.vidioc_enum_frameintervals=fimc_enum_frameintervals,
};
如下是HAL层调用的具体查询camera属性的实现.
staticintfimc_querycap(structfile*filp,void*fh,
structv4l2_capability*cap)
{
structfimc_control*ctrl=((structfimc_prv_data*)fh)->ctrl;
fimc_info1("%s:called\n",__func__);
strcpy(cap->driver,"SECFIMCDriver");
strlcpy(cap->card,ctrl->vd->name,sizeof(cap->card));//Step1-1-1-1-1:name是直接取值于具体的驱动
sprintf(cap->bus_info,"FIMCAHB-bus");
cap->version=0;
cap->capabilities=(V4L2_CAP_VIDEO_CAPTURE|V4L2_CAP_VIDEO_OUTPUT|
V4L2_CAP_VIDEO_OVERLAY|V4L2_CAP_STREAMING);//真把自己当神器了,什么属性都有....
return0;
}
最后再贴两个函数,解释name是如何获取到的
drivers/media/video/s5k4ea.c//摄像头的驱动文件
staticints5k4ea_probe(structi2c_client*client,
conststructi2c_device_id*id)
{
structs5k4ea_state*state;
structv4l2_subdev*sd;
state=kzalloc(sizeof(structs5k4ea_state),GFP_KERNEL);
if(state==NULL)
return-ENOMEM;
sd=&state->sd;
strcpy(sd->name,S5K4EA_DRIVER_NAME);//此处赋值成摄像头的具体名字
/*Registeringsubdev*/
v4l2_i2c_subdev_init(sd,client,&s5k4ea_ops);//此处注册摄像头设备,此函数中获得摄像头的名字
dev_info(&client->dev,"s5k4eahasbeenprobed\n");
return0;
}
v4l2_i2c_subdev_init函数将具体摄像头的驱动中获取的名字加工后搞到设备名里边供返回给上层应用(HAL)
drivers/media/video/v4l2-common.c
voidv4l2_i2c_subdev_init(structv4l2_subdev*sd,structi2c_client*client,
conststructv4l2_subdev_ops*ops)
{
v4l2_subdev_init(sd,ops);
sd->flags|=V4L2_SUBDEV_FL_IS_I2C;
/*theowneristhesameasthei2c_client'sdriverowner*/
sd->owner=client->driver->driver.owner;
/*i2c_clientandv4l2_subdevpointtooneanother*/
v4l2_set_subdevdata(sd,client);
i2c_set_clientdata(client,sd);
/*initializename*/
snprintf(sd->name,sizeof(sd->name),"%s%d-%04x",
client->driver->driver.name,i2c_adapter_id(client->adapter),
client->addr);//这里将摄像头名字更新到sd->name中,上层调用时返回sd->name(位置Step1-1-1-1-1).
}
至此,终于将整个流程连接到了一块儿.
很抱歉的是没有完全介绍初始化时整个camera模块每一步的具体实现.还是留到HAL层和驱动层分别介绍吧,明天写HAL,不知道一天时间能否写完.希望春节回家之前把camera模块都写完吧.
作者:joseph_lee
出处:joseh_lee2633的博客--http://www.cnblogs.com/joseph-linux
您的支持是对博主最大的鼓励,感谢您的认真阅读。
本文无所谓版权,欢迎转载。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理