俗話說(shuō):知己知彼,百戰(zhàn)不殆,想要移植camera硬件,需要了解android中camera的框架思想和調(diào)用流程,才能事半功倍。
(1) Android中camera編程思路:
Camera類介紹:
常用接口,這些接口是camera編程時(shí)作為框架層回調(diào)時(shí)候使用:

常用方法,這些方法作為camera應(yīng)用編程時(shí)使用的API

在應(yīng)用層的編程上常用流程為:
預(yù)覽:
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();
當(dāng)我們打開攝像頭應(yīng)用的時(shí)候首先是預(yù)覽的功能,預(yù)覽就是將攝像頭拍攝下的景物實(shí)時(shí)的呈現(xiàn)出來(lái),在android應(yīng)用中能夠顯示給用戶交互的類都是繼承自view類,我們需要給camera預(yù)覽顯示時(shí)提供一個(gè)畫布一樣的東西,這里我們使用surfaceview類,為什么不直接使用view類呢?關(guān)于view顯示的框架我將在以后的博文中給大家分析,這里使用surfaceview類作為camera的顯示,可以使camera框架層得到一個(gè)可以控制顯示框架的代理,使用這個(gè)代理可以將camera獲取的幀數(shù)據(jù)發(fā)送到顯示框架去合成顯示,如果我們?cè)O(shè)置了預(yù)覽回調(diào):

也可以在回調(diào)中將每一幀的數(shù)據(jù)保存起來(lái)形成錄像的功能
拍照:
mCamera.takePicture(Camera.ShutterCallback shutter, Camera.PictureCallback raw, Camera.PictureCallback jpeg)
拍照本身調(diào)用就需要設(shè)置回調(diào)函數(shù),回調(diào)函數(shù)中可以設(shè)置幾種回調(diào)方式,這幾種都是拍照時(shí)框架層提供給應(yīng)用層數(shù)據(jù),有原始數(shù)據(jù),有jpeg數(shù)據(jù),這幾種都是拍攝下來(lái)的數(shù)據(jù)的不同儲(chǔ)存方式,供我們?cè)趹?yīng)用層處理使用。
小結(jié):在應(yīng)用層上我們可以了解到什么?
預(yù)覽和拍照都可以通過(guò)回調(diào)的方式獲取框架層的數(shù)據(jù),當(dāng)然預(yù)覽也可以不需要把框架層的數(shù)據(jù)獲取,直接發(fā)給顯示框架去顯示。
(2) 現(xiàn)在我們要開始分析camera類代碼了解調(diào)用細(xì)節(jié):
Camera.java (frameworks\base\core\java\android\hardware)
public static Camera open() {
return new Camera(); //構(gòu)造camera
}
Camera() {
mShutterCallback = null;
mRawImageCallback = null;
mJpegCallback = null;
mPreviewCallback = null;
mPostviewCallback = null;
mZoomCallback = null;
Looper looper;
if ((looper = Looper.myLooper()) != null) { //創(chuàng)建Handler,處理本地的通知事件
mEventHandler = new EventHandler(this, looper);
} else if ((looper = Looper.getMainLooper()) != null) {
mEventHandler = new EventHandler(this, looper);
} else {
mEventHandler = null;
}
native_setup(new WeakReference<Camera>(this)); //調(diào)用本地方法,完成初始化
}
private native final void native_setup(Object camera_this);
WeakReference 類的作用:如果想觀察一個(gè)類什么時(shí)候被垃圾回收清除,我們使用WeakReference類來(lái)定義,當(dāng)Camera類的引用為null是,可以通過(guò)WeakReference的get方法獲取此類的信息,了解此類是否被垃圾回收
EventHandler調(diào)用流程;

(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中我們發(fā)現(xiàn)應(yīng)用層代碼調(diào)用中都調(diào)用了本地(native)方法

Camera在應(yīng)用層做了重要的兩件是,初始化一個(gè)EventHandler,等待本地消息通知,調(diào)用本地方法進(jìn)入jni層代碼空間,在這里jni的調(diào)用方向有兩種:
1):java調(diào)用C/C++代碼,將用戶功能性的代碼放到jni層處理
2):C/C++代碼調(diào)用java層代碼,將框架層的消息傳遞給應(yīng)用層處理
(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結(jié)構(gòu)體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結(jié)構(gòu)體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 注冊(cè)本地函數(shù)列表
return AndroidRuntime::registerNativeMethods(env, "android/hardware/Camera",
camMethods, NELEM(camMethods));
}
int register_android_hardware_Camera方法是首先調(diào)用的方法,作用是將java層的變量域和方法域存在jni層,jni層調(diào)用java層的成員變量和成員方法都要依賴域:
struct fields_t {
jfieldID context;// mNativeContext
jfieldID surface;// mSurface
jmethodID post_event;// postEventFromNative
};
AndroidRuntime::registerNativeMethods 方法主要是將本地函數(shù)注冊(cè)給虛擬機(jī),java層可以通過(guò)虛擬機(jī)調(diào)用這些方法
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方法
這個(gè)方法主要作用是建立如圖的聯(lián)系:

sp<Camera> camera = Camera::connect();//初始化三組proxy/stub聯(lián)系
sp<JNICameraContext> context = new JNICameraContext(env, weak_this, clazz, camera);
context->incStrong(thiz);
camera->setListener(context);//初始化mListener
env->SetIntField(thiz, fields.context, (int)context.get());
隨后產(chǎn)生server和HAL之間的聯(lián)系:

void CameraService::Client::dataCallback(int32_t msgType, const sp<IMemory>& dataPtr, void* user)
{
LOGV("dataCallback(%d)", msgType);
sp<Client> client = getClientFromCookie(user);
if (client == 0) {
return;
}
sp<ICameraClient> c = client->mCameraClient;
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);//處理預(yù)覽數(shù)據(jù)函數(shù)
break;
case CAMERA_MSG_POSTVIEW_FRAME:
client->handlePostview(dataPtr);
break;
case CAMERA_MSG_RAW_IMAGE: //處理未壓縮照片函數(shù)
client->handleRawPicture(dataPtr);
break;
case CAMERA_MSG_COMPRESSED_IMAGE: //處理壓縮處理的照片函數(shù)
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最為攝像頭驅(qū)動(dòng),V4L2在用戶空間通過(guò)各種ioctl調(diào)用進(jìn)行控制,并且可以使用mmap進(jìn)行內(nèi)存映射

ioctl函數(shù)命令參數(shù)如下:
.vidioc_querycap = vidioc_querycap, //查詢驅(qū)動(dòng)功能
.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, //獲取當(dāng)前驅(qū)動(dòng)支持的視頻格式
.vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, //讀取當(dāng)前驅(qū)動(dòng)的頻捕獲格式
.vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, //設(shè)置當(dāng)前驅(qū)動(dòng)的頻捕獲格式
.vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, //驗(yàn)證當(dāng)前驅(qū)動(dòng)的顯示格式
.vidioc_reqbufs = vidioc_reqbufs, //分配內(nèi)存
.vidioc_querybuf = vidioc_querybuf, //把VIDIOC_REQBUFS中分配的數(shù)據(jù)緩存轉(zhuǎn)換成物理地址
.vidioc_qbuf = vidioc_qbuf, //把數(shù)據(jù)從緩存中讀取出來(lái)
.vidioc_dqbuf = vidioc_dqbuf, //把數(shù)據(jù)放回緩存隊(duì)列
.vidioc_streamon = vidioc_streamon, //開始視頻顯示函數(shù)
.vidioc_streamoff = vidioc_streamoff, //結(jié)束視頻顯示函數(shù)
.vidioc_cropcap = vidioc_cropcap, //查詢驅(qū)動(dòng)的修剪能力
.vidioc_g_crop = vidioc_g_crop, //讀取視頻信號(hào)的矩形邊框
.vidioc_s_crop = vidioc_s_crop, //設(shè)置視頻信號(hào)的矩形邊框 .vidioc_querystd = vidioc_querystd, //檢查當(dāng)前視頻設(shè)備支持的標(biāo)準(zhǔn),例如PAL或NTSC。
.vidioc_default = vidioc_default,

HAL首先:初始化的時(shí)候進(jìn)行camera基礎(chǔ)參數(shù)的設(shè)置,然后調(diào)用mmap系統(tǒng)調(diào)用將camera驅(qū)動(dòng)層的數(shù)據(jù)隊(duì)列映射到用戶空間,
其次:當(dāng)預(yù)覽方法被調(diào)用的時(shí)候啟動(dòng)預(yù)覽線程,循環(huán)的檢測(cè)隊(duì)列中是否有幀數(shù)據(jù),如果幀數(shù)據(jù)存在,讀取幀數(shù)據(jù),由于讀取的數(shù)據(jù)為YUV格式的數(shù)據(jù),所有要將YUV數(shù)據(jù)轉(zhuǎn)換成RGB的送給顯示框架顯示,也可以將轉(zhuǎn)換過(guò)的數(shù)據(jù)送給視頻編碼模塊,編碼成功后儲(chǔ)存變成錄像的功能,
最后:當(dāng)用戶使用拍照的功能的時(shí)候,拍照線程被調(diào)用(非循環(huán)),檢測(cè)隊(duì)列中的幀數(shù)據(jù),將幀數(shù)據(jù)從隊(duì)列中取出,拍照的數(shù)據(jù)一定需要傳到JAVA層,所有可以將數(shù)據(jù)轉(zhuǎn)換成JPEG格式再上傳,也可以轉(zhuǎn)換成RGB的數(shù)據(jù)上傳給java層
所有上傳的數(shù)據(jù)處理都要經(jīng)過(guò)dataCallback,除非實(shí)現(xiàn)了overlay