Welcome 微信登录

首页 / 操作系统 / Linux / 【Android Camera】之 Preview

 Preview data ? Preview callback
  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的构造函数中,有相应的判断。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 的结构.:
123456struct overlay_control_context_t {struct overlay_control_device_t device;/* our private state goes below here */struct overlay_t* overlay_video1;//overlay1struct overlay_t* overlay_video2;//overlay2};
每个 overlay_t 代表一层 overlay, 每层 ovelay 有自己的 handle.可以使用自定义参数调用 overlay_control_device_t:: setParameter()来指明. Hal 层具体来实现,通过 Overlay object 来拿到 overlay1 overlay2 buffer 指针.  2 使用Surface显示     如果使用Surface,会调用函数registerPreviewBuffers()Surface注册buffersISurface::BufferHeap buffers(w, h, w, h,                                 PIXEL_FORMAT_YCbCr_420_SP,                                 transform,                                 0,                                 mHardware->getPreviewHeap());     status_t ret = mSurface->registerBuffers(buffers);其将mHardwarepreview heap传递给了Surface
关于Previewdata callback          上层Java 中 调用setPreviewCallback, 这个方法调用的是android_hardware_Camera_setHasPreviewCallback,最终实现落到 JNICameraContext::copyAndPost()身上。 
void JNICameraContext::copyAndPost(JNIEnv* env, const sp<IMemory>& dataPtr, int msgType){    jbyteArray obj = NULL;     // allocate Java byte array and copy data    if (dataPtr != NULL) {        ssize_t offset;        size_t size;        sp<IMemoryHeap> heap = dataPtr->getMemory(&offset, &size);        LOGV("postData: off=%d, size=%d", offset, size);        uint8_t *heapBase = (uint8_t*)heap->base();         if (heapBase != NULL) {            const jbyte* data = reinterpret_cast<const jbyte*>(heapBase + offset);            obj = env->NewByteArray(size);            if (obj == NULL) {                LOGE("Couldn"t allocate byte array for JPEG data");                env->ExceptionClear();            } else {                env->SetByteArrayRegion(obj, 0, size, data);            }        } else {            LOGE("image heap is NULL");        }    }     // post image data to Java    env->CallStaticVoidMethod(mCameraJClass, fields.post_event,            mCameraJObjectWeak, msgType, 0, 0, obj);    if (obj) {        env->DeleteLocalRef(obj);    }}
 
       其中 obj = env->NewByteArray(size); 每次都分配ByteArray .     按frame 480×320 pixels计算 ,意味着  230kb per call ,然后花费大量的时间去释放这些资源。 为了优化preview大量数据的回调,有人提出引入: 
static Mutex sPostDataLock; // A mutex that synchronizes calls to sCameraPreviewArrayGlobalstatic jbyteArray sCameraPreviewArrayGlobal; // Buffer that is reusedstatic size_t sCameraPreviewArraySize=0; // Size of the buffer (or 0 if the buffer is not yet used)
 
     为的是只申请一次空间,每帧重复使用ByteArray。 
void JNICameraContext::copyAndPost(JNIEnv* env, const sp<IMemory>& dataPtr, int msgType) {    if (dataPtr != NULL) {        ssize_t offset;        size_t size;        sp<IMemoryHeap> heap = dataPtr->getMemory(&offset, &size);        LOGV("postData: off=%d, size=%d", offset, size);        uint8_t *heapBase = (uint8_t*)heap->base();         if (heapBase != NULL) {            const jbyte* data = reinterpret_cast<const jbyte*>(heapBase + offset);            //HACK            if ((sCameraPreviewArraySize==0) || (sCameraPreviewArraySize!=size)) {                if(sCameraPreviewArraySize!=0) env->DeleteGlobalRef(sCameraPreviewArrayGlobal);                sCameraPreviewArraySize=size;                jbyteArray mCameraPreviewArray = env->NewByteArray(size);                sCameraPreviewArrayGlobal=(jbyteArray)env->NewGlobalRef(mCameraPreviewArray);                env->DeleteLocalRef(mCameraPreviewArray);            }            if (sCameraPreviewArrayGlobal == NULL) {                LOGE("Couldn"t allocate byte array for JPEG data");                env->ExceptionClear();            } else {                env->SetByteArrayRegion(sCameraPreviewArrayGlobal, 0, size, data);            }        } else {            LOGE("image heap is NULL");        }    }    // post image data to Java    env->CallStaticVoidMethod(mCameraJClass, fields.post_event, mCameraJObjectWeak, msgType, 0, 0, sCameraPreviewArrayGlobal);}
 我又看了Android 2.3是 如何处理的     让我感叹,长远性的策略思考,和实践中的优化完善多么重要。如果我们平时写程序,仅仅是为了实现某某功能,解决某某BUG,那么真的实属下等了…… 
  1. void JNICameraContext::copyAndPost(JNIEnv* env, const sp<IMemory>& dataPtr, int msgType)   
  2. {   
  3.     jbyteArray obj = NULL;   
  4.   
  5.     // allocate Java byte array and copy data   
  6.     if (dataPtr != NULL) {   
  7.         ssize_t offset;   
  8.         size_t size;   
  9.         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. }