【Android Camera】之 Preview

转自: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层获得previewbuffer,将其设置给Surface去显示preview的结果。


Preview数据可以通过OverlaySurface两种介质去显示

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;
3     /* our private state goes below here */
4     struct overlay_t* overlay_video1;//overlay1
5     struct overlay_t* overlay_video2;//overlay2
6 };<span></span>

每个 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);

其将mHardwarepreview heap传递给了Surface

关于Previewdata callback:

     上层Java 中 调用setPreviewCallback, 这个方法调用的是android_hardware_Camera_setHasPreviewCallback,最终实现落到 JNICameraContext::copyAndPost()身上。

01 void JNICameraContext::copyAndPost(JNIEnv* env, const sp<IMemory>& dataPtr, int msgType)
02 {
03     jbyteArray obj = NULL;
04   
05     // allocate Java byte array and copy data
06     if (dataPtr != NULL) {
07         ssize_t offset;
08         size_t size;
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();
12   
13         if (heapBase != NULL) {
14             const jbyte* data = reinterpret_cast<const jbyte*>(heapBase + offset);
15             obj = env->NewByteArray(size);
16             if (obj == NULL) {
17                 LOGE("Couldn't allocate byte array for JPEG data");
18                 env->ExceptionClear();
19             else {
20                 env->SetByteArrayRegion(obj, 0, size, data);
21             }
22         else {
23             LOGE("image heap is NULL");
24         }
25     }
26   
27     // post image data to Java
28     env->CallStaticVoidMethod(mCameraJClass, fields.post_event,
29             mCameraJObjectWeak, msgType, 0, 0, obj);
30     if (obj) {
31         env->DeleteLocalRef(obj);
32     }
33 }


其中 obj = env->NewByteArray(size); 每次都分配ByteArray .

    按frame 480×320 pixels计算 ,意味着  230kb per call ,然后花费大量的时间去释放这些资源。 为了优化preview大量数据的回调,有人提出引入:

1 static Mutex sPostDataLock; // A mutex that synchronizes calls to sCameraPreviewArrayGlobal
2 static jbyteArray sCameraPreviewArrayGlobal; // Buffer that is reused
3 static size_t sCameraPreviewArraySize=0; // Size of the buffer (or 0 if the buffer is not yet used)

为的是只申请一次空间,每帧重复使用ByteArray。

01 void JNICameraContext::copyAndPost(JNIEnv* env, const sp<IMemory>& dataPtr, int msgType) {
02     if (dataPtr != NULL) {
03         ssize_t offset;
04         size_t size;
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();
08   
09         if (heapBase != NULL) {
10             const jbyte* data = reinterpret_cast<const jbyte*>(heapBase + offset);
11             //HACK
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);
19             }
20             if (sCameraPreviewArrayGlobal == NULL) {
21                 LOGE("Couldn't allocate byte array for JPEG data");
22                 env->ExceptionClear();
23             else {
24                 env->SetByteArrayRegion(sCameraPreviewArrayGlobal, 0, size, data);
25             }
26         else {
27             LOGE("image heap is NULL");
28         }
29     }
30     // post image data to Java
31     env->CallStaticVoidMethod(mCameraJClass, fields.post_event, mCameraJObjectWeak, msgType, 0, 0, sCameraPreviewArrayGlobal);
32 }


我又看了Android 2.3是如何处理的

让我感叹,长远性的策略思考,和实践中的优化完善多么重要。如果我们平时写程序,仅仅是为了实现某某功能,解决某某BUG,那么真的实属下等了……

01 void JNICameraContext::copyAndPost(JNIEnv* env, const sp<IMemory>& dataPtr, int msgType)  
02 {  
03     jbyteArray obj = NULL;  
04    
05     // allocate Java byte array and copy data  
06     if (dataPtr != NULL) {  
07         ssize_t offset;  
08         size_t size;  
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();  
12    
13         if (heapBase != NULL) {  
14             const jbyte* data = reinterpret_cast<const jbyte*>(heapBase + offset);  
15    
16             if (!mManualBufferMode) {  
17                 LOGV("Allocating callback buffer");  
18                 obj = env->NewByteArray(size);  
19             else {  
20                 // Vector access should be protected by lock in postData()  
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);  
25    
26                     obj = (jbyteArray)env->NewLocalRef(globalBuffer);  
27                     env->DeleteGlobalRef(globalBuffer);  
28    
29                     if (obj != NULL) {  
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!",  
33                                  size, bufferLength);  
34                             env->DeleteLocalRef(obj);  
35                             return;  
36                         }  
37                     }  
38                 }  
39    
40                 if(mCallbackBuffers.isEmpty()) {  
41                     LOGV("Out of buffers, clearing callback!");  
42                     mCamera->setPreviewCallbackFlags(FRAME_CALLBACK_FLAG_NOOP);  
43                     mManualCameraCallbackSet = false;  
44    
45                     if (obj == NULL) {  
46                         return;  
47                     }  
48                 }  
49             }  
50    
51             if (obj == NULL) {  
52                 LOGE("Couldn't allocate byte array for JPEG data");  
53                 env->ExceptionClear();  
54             else {  
55                 env->SetByteArrayRegion(obj, 0, size, data);  
56             }  
57         else {  
58             LOGE("image heap is NULL");  
59         }  
60     }  
61    
62     // post image data to Java  
63     env->CallStaticVoidMethod(mCameraJClass, fields.post_event,  
64             mCameraJObjectWeak, msgType, 0, 0, obj);  
65     if (obj) {  
66         env->DeleteLocalRef(obj);  
67     }  
68 }


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值