转自:http://my.oschina.net/jerikc/blog/112786

Android Camera小系统:

嗯……直接看Camera HAL层,它实现是主要的工作, 它一般通过ioctl调用V4L2 command ①从linux kernel中的camera driver①得到preview数据. 然后交给surface(或overlay)显示或者保存为文件.在HAL层需要打开对应的设备文件,并通过ioctrl访问camera driver. Android通过这个HAL层来保证底层硬件(驱动)改变,只需修改对应的HAL层代码,FrameWork层与JAVA Ap的都不用改变.
注释:①V4L2(video 4 linux 2)
备注:①这个驱动并不是camera本身而是控制camera的主设备,这个camera控制器在linux里被抽象成为v4l2层通用,最后由(*attach)连接到具体每个不同的camera设备驱动里。camera=camera控制器+外接的camera sensor,控制器集成在cpu里,linux下的设备结点就是/dev/video0.
preview数据的显示过程:
简 单 概 括 为 | Java app 呼叫 ② Jni ,Jni调用各种.so : libandroid_runtime.so ---> libcamera_client.so ---> Binder IPC---> libcameraservice.so ---> libcamera.so 注释:②请原谅我用【呼叫】这个动词,实在想不出更加形象的词汇了。 |
详 细 说 明 | 1.打开linux kernel中的camera driver的设备文件,调用CameraHardwareInterface.h 中定义的openCameraHardware(),打开camera driver的设备文件(例如/dev/video0). 2.CameraHardwareInterface.h 中定义的 setParameters()函数,传参告诉camera HAL使用哪一个硬件摄像头,以及它工作的参数(size, format等等),并在HAL层分配存储preview数据的buffers(如果buffers是在linux kernel中的camera driver中分配的,并拿到这些buffers mmap后的地址指针). 3.如果不使用overlay那设置显示目标就在libcameraservice.so 中,不会进Camera HAL动态库.并将上一步拿到的preview数据buffers地址注册到surface中. 如果使用overlay那在libcameraservice.so 中会通过传进来的Isurface创建Overlay类的实例,然后调用CameraHardwareInterface.h 中定义的 setOverlay()设置到Camera HAL动态库中. 4.开始preview,调用到CameraHardwareInterface.h 中定义的 startPreview()函数.startPreviewMode会处理preview的显示介质,如果使用Overlay显示,会设置相应的Overlay,同时调用mHardware->startPreview()以启动preview;否则先调用mHardware->startPreview()启动preview,然后设置buffer:调用函数registerPreviewBuffers(),它会调用mHardware->getPreviewHeap(),从HAL层获得preview的buffer,将其设置给Surface去显示preview的结果。 |
Preview数据可以通过Overlay和Surface两种介质去显示
1.使用Overlay显示
overlay 一般用在 camera preview, 视频播放等需要高帧率的地方, 还有可能 UI 界面设计的需求,如 map 地图查看软件需两层显示信息. overlay需要硬件与驱动的支持.Overlay 没有 java 层的 code, 也就没有 JNI 调用. 一般都在 native 中使用.
如果要使用Overlay,底层硬件必须支持Overlay。在CameraService::Client的构造函数中,有相应的判断。
1 | CameraService::Client::Client( const sp<CameraService>& cameraService, const sp<ICameraClient>& cameraClient, pid_t clientPid){} |
若mUseOverlay = mHardware->useOverlay();返回值为true,则表示硬件支持Overlay;否则只能使用Surface显示。
Android系统中提供了Overlay的接口,其具体实现需要自己做.
关于多层 overlay:例如需要同时支持 overlay1 与 overlay2.需在overlay hal 的 overlay_control_device_t 中要添加 overlay1 与 overlay2 的结构.如:
1 | struct overlay_control_context_t { |
2 | struct overlay_control_device_t device; |
4 | struct overlay_t* overlay_video1; |
5 | struct overlay_t* overlay_video2; |
每个 overlay_t 代表一层 overlay, 每层 ovelay 有自己的 handle.可以使用自定义参数调用 overlay_control_device_t:: setParameter()来指明. Hal 层具体来实现,通过 Overlay object 来拿到 overlay1 与 overlay2 的 buffer 指针.
2、 使用Surface显示
如果使用Surface,会调用函数registerPreviewBuffers()向Surface注册buffers。
1 | ISurface::BufferHeap buffers(w, h, w, h,PIXEL_FORMAT_YCbCr_420_SP,transform,0,mHardware->getPreviewHeap()); |
2 | status_t ret = mSurface->registerBuffers(buffers); |
其将mHardware的preview heap传递给了Surface。
关于Previewdata callback:
上层Java 中 调用setPreviewCallback, 这个方法调用的是android_hardware_Camera_setHasPreviewCallback,最终实现落到 JNICameraContext::copyAndPost()身上。
01 | void JNICameraContext::copyAndPost(JNIEnv* env, const sp<IMemory>& dataPtr, int msgType) |
03 | jbyteArray obj = NULL; |
06 | if (dataPtr != NULL) { |
09 | sp<IMemoryHeap> heap = dataPtr->getMemory(&offset, &size); |
10 | LOGV( "postData: off=%d, size=%d" , offset, size); |
11 | uint8_t *heapBase = (uint8_t*)heap->base(); |
13 | if (heapBase != NULL) { |
14 | const jbyte* data = reinterpret_cast < const jbyte*>(heapBase + offset); |
15 | obj = env->NewByteArray(size); |
17 | LOGE( "Couldn't allocate byte array for JPEG data" ); |
18 | env->ExceptionClear(); |
20 | env->SetByteArrayRegion(obj, 0, size, data); |
23 | LOGE( "image heap is NULL" ); |
28 | env->CallStaticVoidMethod(mCameraJClass, fields.post_event, |
29 | mCameraJObjectWeak, msgType, 0, 0, obj); |
31 | env->DeleteLocalRef(obj); |
其中 obj = env->NewByteArray(size); 每次都分配ByteArray .
按frame 480×320 pixels计算 ,意味着 230kb per call ,然后花费大量的时间去释放这些资源。 为了优化preview大量数据的回调,有人提出引入:
1 | static Mutex sPostDataLock; |
2 | static jbyteArray sCameraPreviewArrayGlobal; |
3 | static size_t sCameraPreviewArraySize=0; |
为的是只申请一次空间,每帧重复使用ByteArray。
01 | void JNICameraContext::copyAndPost(JNIEnv* env, const sp<IMemory>& dataPtr, int msgType) { |
02 | if (dataPtr != NULL) { |
05 | sp<IMemoryHeap> heap = dataPtr->getMemory(&offset, &size); |
06 | LOGV( "postData: off=%d, size=%d" , offset, size); |
07 | uint8_t *heapBase = (uint8_t*)heap->base(); |
09 | if (heapBase != NULL) { |
10 | const jbyte* data = reinterpret_cast < const jbyte*>(heapBase + offset); |
12 | if ((sCameraPreviewArraySize==0) |
13 | || (sCameraPreviewArraySize!=size)) { |
14 | if (sCameraPreviewArraySize!=0) env->DeleteGlobalRef(sCameraPreviewArrayGlobal); |
15 | sCameraPreviewArraySize=size; |
16 | jbyteArray mCameraPreviewArray = env->NewByteArray(size); |
17 | sCameraPreviewArrayGlobal=(jbyteArray)env->NewGlobalRef(mCameraPreviewArray); |
18 | env->DeleteLocalRef(mCameraPreviewArray); |
20 | if (sCameraPreviewArrayGlobal == NULL) { |
21 | LOGE( "Couldn't allocate byte array for JPEG data" ); |
22 | env->ExceptionClear(); |
24 | env->SetByteArrayRegion(sCameraPreviewArrayGlobal, 0, size, data); |
27 | LOGE( "image heap is NULL" ); |
31 | env->CallStaticVoidMethod(mCameraJClass, fields.post_event, mCameraJObjectWeak, msgType, 0, 0, sCameraPreviewArrayGlobal); |
我又看了Android 2.3是如何处理的
让我感叹,长远性的策略思考,和实践中的优化完善多么重要。如果我们平时写程序,仅仅是为了实现某某功能,解决某某BUG,那么真的实属下等了……
01 | void JNICameraContext::copyAndPost(JNIEnv* env, const sp<IMemory>& dataPtr, int msgType) |
03 | jbyteArray obj = NULL; |
06 | if (dataPtr != NULL) { |
09 | sp<IMemoryHeap> heap = dataPtr->getMemory(&offset, &size); |
10 | LOGV( "postData: off=%d, size=%d" , offset, size); |
11 | uint8_t *heapBase = (uint8_t*)heap->base(); |
13 | if (heapBase != NULL) { |
14 | const jbyte* data = reinterpret_cast < const jbyte*>(heapBase + offset); |
16 | if (!mManualBufferMode) { |
17 | LOGV( "Allocating callback buffer" ); |
18 | obj = env->NewByteArray(size); |
21 | if (!mCallbackBuffers.isEmpty()) { |
22 | LOGV( "Using callback buffer from queue of length %d" , mCallbackBuffers.size()); |
23 | jbyteArray globalBuffer = mCallbackBuffers.itemAt(0); |
24 | mCallbackBuffers.removeAt(0); |
26 | obj = (jbyteArray)env->NewLocalRef(globalBuffer); |
27 | env->DeleteGlobalRef(globalBuffer); |
30 | jsize bufferLength = env->GetArrayLength(obj); |
31 | if (( int )bufferLength < ( int )size) { |
32 | LOGE( "Manually set buffer was too small! Expected %d bytes, but got %d!" , |
34 | env->DeleteLocalRef(obj); |
40 | if (mCallbackBuffers.isEmpty()) { |
41 | LOGV( "Out of buffers, clearing callback!" ); |
42 | mCamera->setPreviewCallbackFlags(FRAME_CALLBACK_FLAG_NOOP); |
43 | mManualCameraCallbackSet = false ; |
52 | LOGE( "Couldn't allocate byte array for JPEG data" ); |
53 | env->ExceptionClear(); |
55 | env->SetByteArrayRegion(obj, 0, size, data); |
58 | LOGE( "image heap is NULL" ); |
63 | env->CallStaticVoidMethod(mCameraJClass, fields.post_event, |
64 | mCameraJObjectWeak, msgType, 0, 0, obj); |
66 | env->DeleteLocalRef(obj); |