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数据的显示过程:
     
    
| 
          
          
          
          
          | 
          
          
          
          | 
     
     
    
| 
          
          
          
          | 
          
 
          
 
          
 
          
          | 
     
      Preview
     
     数据可以通过Overlay和Surface两种介质去显示
    
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 的结构.如:
| 
          
          
          
          
          
          | 
          
          
          
          
          
          | 
每个 overlay_t 代表一层 overlay, 每层 ovelay 有自己的 handle.可以使用自定义参数调用 overlay_control_device_t:: setParameter()来指明. Hal 层具体来实现,通过Overlay object 来拿到 overlay1 与 overlay2 的 buffer 指针.
2、 使用Surface显示
如果使用Surface,会调用函数registerPreviewBuffers()向Surface注册buffers。
ISurface::BufferHeap buffers(w, h, w, h,
PIXEL_FORMAT_YCbCr_420_SP,
transform,
0,
mHardware->getPreviewHeap());
status_t ret = mSurface->registerBuffers(buffers);
其将mHardware的preview 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; 
          
         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”); } } 
          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; 
         static jbyteArray sCameraPreviewArrayGlobal; 
         static size_t sCameraPreviewArraySize=0; | 
     
      为的是只申请一次空间,每帧重复使用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); 
          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”); } } 
          env->CallStaticVoidMethod(mCameraJClass, fields.post_event, mCameraJObjectWeak, msgType, 0, 0, sCameraPreviewArrayGlobal); } | 
我又看了Android 2.3是 如何处理的
     
      让我感叹,长远性的策略思考,和实践中的优化完善多么重要。如果我们平时写程序,仅仅是为了实现某某功能,解决某某BUG,那么真的实属下等了……
     
    
     
     
    
- 
       
 
 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);
- 
       
 
- 
       
 
 if
 
 (!mManualBufferMode) {
 
- 
       
 LOGV(
 
 “Allocating callback buffer”
 
 );
 
- 
       
 obj = env->NewByteArray(size);
 
- 
       
 }
 
 else
 
 {
 
- 
       
 
 // Vector access should be protected by lock in postData()
 
 
- 
       
 
 if
 
 (!mCallbackBuffers.isEmpty()) {
 
- 
       
 LOGV(
 
 “Using callback buffer from queue of length %d”
 
 , mCallbackBuffers.size());
 
- 
       
 jbyteArray globalBuffer = mCallbackBuffers.itemAt(0);
 
- 
       
 mCallbackBuffers.removeAt(0);
 
- 
       
 
- 
       
 obj = (jbyteArray)env->NewLocalRef(globalBuffer);
 
- 
       
 env->DeleteGlobalRef(globalBuffer);
 
- 
       
 
- 
       
 
 if
 
 (obj != NULL) {
 
- 
       
 jsize bufferLength = env->GetArrayLength(obj);
 
- 
       
 
 if
 
 ((
 
 
 int
 
 )bufferLength < (
 
 int
 
 )size) {
- 
       
 LOGE(
 
 “Manually set buffer was too small! Expected %d bytes, but got %d!”
 
 ,
 
- 
       
 size, bufferLength);
 
- 
       
 env->DeleteLocalRef(obj);
 
- 
       
 
 return
 
 ;
 
- 
       
 }
 
- 
       
 }
 
- 
       
 }
 
- 
       
 
- 
       
 
 if
 
 (mCallbackBuffers.isEmpty()) {
 
- 
       
 LOGV(
 
 “Out of buffers, clearing callback!”
 
 );
 
- 
       
 mCamera->setPreviewCallbackFlags(FRAME_CALLBACK_FLAG_NOOP);
 
- 
       
 mManualCameraCallbackSet =
 
 false
 
 ;
 
- 
       
 
- 
       
 
 if
 
 (obj == NULL) {
 
- 
       
 
 return
 
 ;
 
- 
       
 }
 
- 
       
 }
 
- 
       
 }
 
- 
       
 
- 
       
 
 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);
 
- 
       
 }
 
- 
       
 }
 
 
