分类: Java
2011-11-30 12:03:42
俗话说:知己知彼,百战不殆,想要移植camera硬件,需要了解android中camera的框架思想和调用流程,才能事半功倍。
(1) Android中camera编程思路:
Camera类介绍:
常用接口,这些接口是camera编程时作为框架层回调时候使用:
常用方法,这些方法作为camera应用编程时使用的API
在应用层的编程上常用流程为:
预览:
mCamera = Camera.open();
Camera.Parameters p = mCamera.getParameters();
p.setPreviewSize(w, h);
mCamera.setParameters(p);
SurfaceView mSurfaceView = (SurfaceView)
findViewById(R.id.surface_camera);
SurfaceHolder holder =
mSurfaceView.getHolder();
holder.addCallback(this);
mCamera.setPreviewDisplay(holder);
mCamera.startPreview();
当我们打开摄像头应用的时候首先是预览的功能,预览就是将摄像头拍摄下的景物实时的呈现出来,在android应用中能够显示给用户交互的类都是继承自 view类,我们需要给camera预览显示时提供一个画布一样的东西,这里我们使用surfaceview类,为什么不直接使用view类呢?关于 view显示的框架我将在以后的博文中给大家分析,这里使用surfaceview类作为camera的显示,可以使camera框架层得到一个可以控制 显示框架的代理,使用这个代理可以将camera获取的帧数据发送到显示框架去合成显示,如果我们设置了预览回调:
也可以在回调中将每一帧的数据保存起来形成录像的功能
拍照:
mCamera.(
shutter, raw, jpeg)
拍照本身调用就需要设置回调函数,回调函数中可以设置几种回调方式,这几种都是拍照时框架层提供给应用层数据,有原始数据,有jpeg数据,这几种都是拍摄下来的数据的不同储存方式,供我们在应用层处理使用。
小结:在应用层上我们可以了解到什么?
预览和拍照都可以通过回调的方式获取框架层的数据,当然预览也可以不需要把框架层的数据获取,直接发给显示框架去显示。
(2) 现在我们要开始分析camera类代码了解调用细节:
Camera.java (frameworks\base\core\java\android\hardware)
public static Camera open() {
return new Camera(); //构造camera
}
Camera() {
mShutterCallback = null;
mRawImageCallback = null;
mJpegCallback = null;
mPreviewCallback = null;
mPostviewCallback = null;
mZoomCallback = null;
Looper looper;
if ((looper = Looper.myLooper()) != null) { //创建Handler,处理本地的通知事件
mEventHandler = new EventHandler(this, looper);
} else if ((looper = Looper.getMainLooper()) != null) {
mEventHandler = new EventHandler(this, looper);
} else {
mEventHandler = null;
}
native_setup(new
WeakReference
}
private native final void native_setup(Object camera_this);
WeakReference 类的作用:如果想观察一个类什么时候被垃圾回收清除,我们使用WeakReference类来定义,当Camera类的引用为null是,可以通过WeakReference的get方法获取此类的信息,了解此类是否被垃圾回收
EventHandler调用流程;
(1) private native final void native_setup(Object camera_this);
(2) private native final void native_setParameters(String params);
(3) private native final String native_getParameters();
(4) private native final void setPreviewDisplay(Surface surface);
(5) public native final void startPreview();
在camera中我们发现应用层代码调用中都调用了本地(native)方法
Camera在应用层做了重要的两件是,初始化一个EventHandler,等待本地消息通知,调用本地方法进入jni层代码空间,在这里jni的调用方向有两种:
1):java调用C/C++代码,将用户功能性的代码放到jni层处理
2):C/C++代码调用java层代码,将框架层的消息传递给应用层处理
(2) Camera jni层代码:
android_hardware_Camera.cpp (frameworks\base\core\jni)
int register_android_hardware_Camera(JNIEnv *env)
{
field fields_to_find[] = {
{ "android/hardware/Camera", "mNativeContext", "I", &fields.context },
{ "android/view/Surface", "mSurface", "I", &fields.surface }
};
//初始化fields_t结构体context,surface
if (find_fields(env, fields_to_find, NELEM(fields_to_find)) < 0) return -1;
jclass clazz = env->FindClass("android/hardware/Camera");
//初始化fields_t结构体post_event
fields.post_event = env->GetStaticMethodID(clazz, "postEventFromNative",
"(Ljava/lang/Object;IIILjava/lang/Object;)V");
if (fields.post_event == NULL) {
LOGE("Can't find android/hardware/Camera.postEventFromNative");
return -1;
}
// Register native functions 注册本地函数列表
return AndroidRuntime::registerNativeMethods(env, "android/hardware/Camera",
camMethods, NELEM(camMethods));
}
int register_android_hardware_Camera方法是首先调用的方法,作用是将java层的变量域和方法域存在jni层,jni层调用java层的成员变量和成员方法都要依赖域:
struct fields_t {
jfieldID context;// mNativeContext
jfieldID surface;// mSurface
jmethodID post_event;// postEventFromNative
};
AndroidRuntime::registerNativeMethods 方法主要是将本地函数注册给虚拟机,java层可以通过虚拟机调用这些方法
static JNINativeMethod camMethods[] = {
{ "native_setup",
"(Ljava/lang/Object;)V",
(void*)android_hardware_Camera_native_setup },
{ "native_release",
"()V",
(void*)android_hardware_Camera_release },
{ "setPreviewDisplay",
"(Landroid/view/Surface;)V",
(void *)android_hardware_Camera_setPreviewDisplay },
{ "startPreview",
"()V",
(void *)android_hardware_Camera_startPreview },
{ "stopPreview",
"()V",
(void *)android_hardware_Camera_stopPreview },
{ "previewEnabled",
"()Z",
(void *)android_hardware_Camera_previewEnabled },
{ "setHasPreviewCallback",
"(ZZ)V",
(void *)android_hardware_Camera_setHasPreviewCallback },
{ "addCallbackBuffer",
"([B)V",
(void *)android_hardware_Camera_addCallbackBuffer },
{ "native_autoFocus",
"()V",
(void *)android_hardware_Camera_autoFocus },
{ "native_cancelAutoFocus",
"()V",
(void *)android_hardware_Camera_cancelAutoFocus },
{ "native_takePicture",
"()V",
(void *)android_hardware_Camera_takePicture },
{ "native_setParameters",
"(Ljava/lang/String;)V",
(void *)android_hardware_Camera_setParameters },
{ "native_getParameters",
"()Ljava/lang/String;",
(void *)android_hardware_Camera_getParameters },
{ "reconnect",
"()V",
(void*)android_hardware_Camera_reconnect },
{ "lock",
"()V",
(void*)android_hardware_Camera_lock },
{ "unlock",
"()V",
(void*)android_hardware_Camera_unlock },
{ "startSmoothZoom",
"(I)V",
(void *)android_hardware_Camera_startSmoothZoom },
{ "stopSmoothZoom",
"()V",
(void *)android_hardware_Camera_stopSmoothZoom },
};
JNINativeMethod列出java层方法和c/c++层方法的映射表:
1)android_hardware_Camera_native_setup方法
这个方法主要作用是建立如图的联系:
sp
sp
context->incStrong(thiz);
camera->setListener(context);//初始化mListener
env->SetIntField(thiz, fields.context, (int)context.get());
随后产生server和HAL之间的联系:
void CameraService::Client::dataCallback(int32_t msgType, const
sp
{
LOGV("dataCallback(%d)", msgType);
sp
if (client == 0) {
return;
}
sp
if (dataPtr == NULL) {
LOGE("Null data returned in data callback");
if (c != NULL) {
c->notifyCallback(CAMERA_MSG_ERROR, UNKNOWN_ERROR, 0);
c->dataCallback(msgType, NULL);
}
return;
}
CAMERA_MSG_SHUTTER
switch (msgType) {
case CAMERA_MSG_PREVIEW_FRAME:
client->handlePreviewData(dataPtr);//处理预览数据函数
break;
case CAMERA_MSG_POSTVIEW_FRAME:
client->handlePostview(dataPtr);
break;
case CAMERA_MSG_RAW_IMAGE: //处理未压缩照片函数
client->handleRawPicture(dataPtr);
break;
case CAMERA_MSG_COMPRESSED_IMAGE: //处理压缩处理的照片函数
client->handleCompressedPicture(dataPtr);
break;
default:
if (c != NULL) {
c->dataCallback(msgType, dataPtr);
}
break;
}
#if DEBUG_CLIENT_REFERENCES
if (client->getStrongCount() == 1) {
LOGE("++++++++++++++++ (DATA CALLBACK) THIS WILL CAUSE A LOCKUP!");
client->printRefs();
}
#endif
}
Linux中使用V4L2最为摄像头驱动,V4L2在用户空间通过各种ioctl调用进行控制,并且可以使用mmap进行内存映射
ioctl函数命令参数如下:
.vidioc_querycap =
vidioc_querycap, //查询驱动功能
.vidioc_enum_fmt_vid_cap =
vidioc_enum_fmt_vid_cap,
//获取当前驱动支持的视频格式
.vidioc_g_fmt_vid_cap =
vidioc_g_fmt_vid_cap,
//读取当前驱动的频捕获格式
.vidioc_s_fmt_vid_cap =
vidioc_s_fmt_vid_cap, //设置当前驱动的频捕获格式
.vidioc_try_fmt_vid_cap =
vidioc_try_fmt_vid_cap, //验证当前驱动的显示格式
.vidioc_reqbufs =
vidioc_reqbufs,
//分配内存
.vidioc_querybuf =
vidioc_querybuf,
//把VIDIOC_REQBUFS中分配的数据缓存转换成物理地址
.vidioc_qbuf =
vidioc_qbuf,
//把数据从缓存中读取出来
.vidioc_dqbuf =
vidioc_dqbuf, //把数据放回缓存队列
.vidioc_streamon =
vidioc_streamon,
//开始视频显示函数
.vidioc_streamoff =
vidioc_streamoff, //结束视频显示函数
.vidioc_cropcap =
vidioc_cropcap, //查询驱动的修剪能力
.vidioc_g_crop =
vidioc_g_crop,
//读取视频信号的矩形边框
.vidioc_s_crop =
vidioc_s_crop,
//设置视频信号的矩形边框 .vidioc_querystd =
vidioc_querystd, //检查当前视频设备支持的标准,例如PAL或NTSC。
.vidioc_default =
vidioc_default,
HAL首先:初始化的时候进行camera基础参数的设置,然后调用mmap系统调用将camera驱动层的数据队列映射到用户空间,
其次:当预览方法被调用的时候启动预览线程,循环的检测队列中是否有帧数据,如果帧数据存在,读取帧数据,由于读取的数据为YUV格式的数据,所有要将YUV数据转换成RGB的送给显示框架显示,也可以将转换过的数据送给视频编码模块,编码成功后储存变成录像的功能,
最后:当用户使用拍照的功能的时候,拍照线程被调用(非循环),检测队列中的帧数据,将帧数据从队列中取出,拍照的数据一定需要传到JAVA层,所有可以将数据转换成JPEG格式再上传,也可以转换成RGB的数据上传给java层
所有上传的数据处理都要经过dataCallback,除非实现了overlay