现在的位置: 首页 > 综合 > 正文

Android 4.0 Camera架构分析之preview和takePicture

2014年02月16日 ⁄ 综合 ⁄ 共 10066字 ⁄ 字号 评论关闭

上篇文章介绍了,Camera初始化的过程,完成初始化之后就可以使用Camera提供的以下功能了

1.预览preview

2.视频录制

3.拍照和参数设置

打开Camera第一键事情就是预览取景preview的动作,我们先从Cameraapp分析起。所有拥有拍照功能的应用,它在预览时候都要实现SurfaceHolder.Callback接口,并实现其surfaceCreatedsurfaceChangedsurfaceDestroyed三个函数,同时声明一个用于预览的窗口SurfaceView,以下是系统自带ap的源代码

SurfaceViewpreview = (SurfaceView) findViewById(R.id.camera_preview);

SurfaceHolderholder = preview.getHolder();
holder.addCallback(this);

还要设置camera预览的surface缓存区,系统自带app实在surfaceChange()方法里面设置Camera的预览区,以供底层获取的preview数据不断投递到这个surface缓存区内。

publicvoid surfaceChanged(SurfaceHolder holder, int format, int w, int h) {

        mSurfaceHolder= holder;

       //The mCameraDevice will be null if it fails to connect to thecamera
        // hardware. Inthis case we will show a dialog and then finish the
       // activity, so it's OK to ignore it.

       if (mCameraDevice == null) return;

       // Sometimes surfaceChanged is called after onPause or beforeonResume.
        // Ignoreit.

       if (mPausing || isFinishing()) return;

       //Set preview display if the surface is being created. Previewwas
        // already started.Also restart the preview if display rotation has
       // changed. Sometimes this happens when the device is held inportrait
        // and cameraapp is opened. Rotation animation takes some time and
       // display rotation in onCreate may not be what we want.

       if (mCameraState == PREVIEW_STOPPED) {
           startPreview();
           startFaceDetection();
        }else {
           if (Util.getDisplayRotation(this) != mDisplayRotation){
               setDisplayOrientation();
           }
           if (holder.isCreating()) {
               
//Set preview display if the surface is being created andpreview
               // was already started. That means preview display was set tonull
               // and we need to set it now.

               
setPreviewDisplay(holder);
           }
        }

设置好以上参数后,就可以调用startPreview()进行取景预览

startPreview()也是一层层往下调用,最后到Camera的服务端CameraService,我们看下它的过程

Camera.java(应用)------------->Camera.java(框架)-------------->android_hardware_camera.cpp(JNI)-------------------->Camera.cpp(客户端)------------------->CameraService.cpp(服务端)--------------------->CameraHarwareInterface(HAL接口)

CameraService端将处理preview的请求并进入HAL

status_tCameraService::Client::startPreview() {

    enableMsgType(CAMERA_MSG_PREVIEW_METADATA);

     returnstartCameraMode(CAMERA_PREVIEW_MODE);
}

先是传递preview的消息到HAL层,然后执行preview

status_tCameraService::Client::startCameraMode(camera_mode mode) {
    switch(mode) {
        caseCAMERA_PREVIEW_MODE:
           if (mSurface == 0 && mPreviewWindow == 0) {
               LOG1("mSurface is not set yet.");
               // still able to start preview in this case.
           }
           
returnstartPreviewMode();
   }
}

status_tCameraService::Client::startPreviewMode() {
   LOG1("startPreviewMode");
    status_tresult = NO_ERROR;

    // if preview has beenenabled, nothing needs to be done
    if(mHardware->previewEnabled()) {
       return NO_ERROR;
    }

    if(mPreviewWindow != 0) {
       native_window_set_scaling_mode(mPreviewWindow.get(),
               NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
       native_window_set_buffers_transform(mPreviewWindow.get(),
               mOrientation);
    }
   mHardware->setPreviewWindow(mPreviewWindow);
   result = mHardware->startPreview();


   return result;
}

然后就近去HAL层调用,并通过回调函数源源不断的将数据投递到surfaceview的缓存去,因为preview的数据是比较大的,所以数据不会携带着传上上层,而是直接在两个缓存区之间copy,一个是底层采集数据的缓存区,另一个是用于显示的surfaceview缓存区

我们看看preview的回调函数是怎么处理的

首先在Camera客户端与服务端连接成功的时候就会设置一个回调函数dataCallBack

CameraService::Client::Client(constsp<CameraService>& cameraService,
       const sp<ICameraClient>& cameraClient,
       const sp<CameraHardwareInterface>& hardware,
       int cameraId, int cameraFacing, int clientPid) {
   ......
   
mHardware->setCallbacks(notifyCallback,
                           dataCallback,
                           dataCallbackTimestamp,
                           (void *)cameraId);

}

在上篇有介绍到,clientserver连接成功后就会new一个client返回,client的构造函数中,就对camera设置了notifyCallbackdataCallbackdataCallbackTimestamp三个回调函数,用于返回底层数据用于处理,看下它的处理方法

voidCameraService::Client::dataCallback(int32_t msgType,
       const sp<IMemory>& dataPtr, camera_frame_metadata_t*metadata, void* user) {
      switch (msgType& ~CAMERA_MSG_PREVIEW_METADATA) {
  
     caseCAMERA_MSG_PREVIEW_FRAME:
           client->handlePreviewData(msgType, dataPtr, metadata);

           break;
      .......

}

继续看handlePreviewData()

voidCameraService::Client::handlePreviewData(int32_tmsgType,
                                             const sp<IMemory>&mem,
                                             camera_frame_metadata_t *metadata) {
   
   sp<ICameraClient> c = mCameraClient;
   .......
     if (c != 0) {
       // Is the received frame copied out or not?
       if (flags & CAMERA_FRAME_CALLBACK_FLAG_COPY_OUT_MASK){
           LOG2("frame is copied");
           copyFrameAndPostCopiedFrame(msgType,c, heap, offset, size, metadata);
       } else {
           LOG2("frame is forwarded");
           mLock.unlock();
           c->dataCallback(msgType, mem, metadata);
       }
    } else {
       mLock.unlock();
    }
}

copyFrameAndPostCopiedFrame就是这个函数执行两个buffpreview数据的投递

voidCameraService::Client::copyFrameAndPostCopiedFrame(
       int32_t msgType, const sp<ICameraClient>& client,
       const sp<IMemoryHeap>& heap, size_t offset, size_tsize,
       camera_frame_metadata_t *metadata) {
    ......
   previewBuffer = mPreviewBuffer;

   memcpy(previewBuffer->base(), (uint8_t *)heap->base() + offset,size);

    sp<MemoryBase> frame = newMemoryBase(previewBuffer, 0, size);
    if (frame== 0) {
        LOGE("failedto allocate space for frame callback");
       mLock.unlock();
       return;
    }

   mLock.unlock();
    client->dataCallback(msgType,frame, metadata);
}

将数据处理成frame,继续调用客户端client的回调函数client->dataCallback(msgType,frame,
metadata);

//callback from camera service when frame or image is ready
voidCamera::dataCallback(int32_t msgType, const sp<IMemory>&dataPtr,
                         camera_frame_metadata_t *metadata)
{
   sp<CameraListener> listener;
    {
       Mutex::Autolock _l(mLock);
       listener = mListener;
    }
   if (listener != NULL) {
       
listener->postData(msgType,dataPtr, metadata);
   }
}

还记得初始化的时候,在jni里面有设置listener吗?

staticvoid android_hardware_Camera_native_setup(JNIEnv *env, jobjectthiz,
    jobject weak_this, jint cameraId)
{

    sp<JNICameraContext>context = new JNICameraContext(env, weak_this, clazz, camera);
   context->incStrong(thiz);
   camera->setListener(context);

}

继续listener->postData(msgType,dataPtr, metadata);

voidJNICameraContext::postData(int32_t msgType, const sp<IMemory>&dataPtr,
                               camera_frame_metadata_t *metadata)
{
   ......
     switch (dataMsgType) {
       case CAMERA_MSG_VIDEO_FRAME:
           // should never happen
           break;
         default:
           LOGV("dataCallback(%d, %p)", dataMsgType,dataPtr.get());
          
 copyAndPost(env,dataPtr, dataMsgType);
           break;
    }
}

继续copyAndPost(env,dataPtr, dataMsgType);

voidJNICameraContext::copyAndPost(JNIEnv* env, const sp<IMemory>&dataPtr, int msgType)
{
    jbyteArray obj =NULL;

    // allocate Java byte array and copydata
    if (dataPtr != NULL) {
   .......   
           } else {
               LOGV("Allocating callback buffer");
               obj= env->NewByteArray(size);
     .......
               
env->SetByteArrayRegion(obj,0, size, data);
           }
        } else {
           LOGE("image heap is NULL");
       }
    }

    // post imagedata to Java
   env->CallStaticVoidMethod(mCameraJClass,fields.post_event,
           mCameraJObjectWeak, msgType, 0, 0, obj);

   if (obj) {
       env->DeleteLocalRef(obj);
    }
}

解释一下标红的部分,先建立一个byte数组obj,将data缓存数据存储进obj数组,CallStaticVoidMethodC调用java函数,最后执行实在Camera.java(框架)的postEventFromNative()

    privatestatic void postEventFromNative(Objectcamera_ref,
                                           int what, int arg1, int arg2, Object obj)
   {
        Camera c =(Camera)((WeakReference)camera_ref).get();
       if (c == null)
           return;

        if(c.mEventHandler != null) {
           Message m = c.mEventHandler.obtainMessage(what, arg1, arg2,obj);
           c.mEventHandler.sendMessage(m);
       }
    }

还是handler处理地

 publicvoid handleMessage(Message msg) {
           switch(msg.what) {
                       case CAMERA_MSG_SHUTTER:
               if (mShutterCallback != null) {
                   mShutterCallback.onShutter();
               }
               return;

           case CAMERA_MSG_RAW_IMAGE:
               if (mRawImageCallback != null) {
                   mRawImageCallback.onPictureTaken((byte[])msg.obj,mCamera);
               }
               return;

           case CAMERA_MSG_COMPRESSED_IMAGE:
               if (mJpegCallback != null) {
                   mJpegCallback.onPictureTaken((byte[])msg.obj,mCamera);
               }
               return;
            case CAMERA_MSG_PREVIEW_FRAME:
               if (mPreviewCallback != null) {
                   PreviewCallback cb = mPreviewCallback;
                   if (mOneShot) {
                       // Clear the callback variable before thecallback
                       // in case the app calls setPreviewCallbackfrom
                       // the callback function
                       mPreviewCallback = null;
                   } else if (!mWithBuffer) {
                       // We're faking the camera preview mode toprevent
                       // the app from being flooded with previewframes.
                       // Set to oneshot mode again.
                       setHasPreviewCallback(true, false);
                   }
                   
cb.onPreviewFrame((byte[])msg.obj,mCamera);
               }
               return;
            }
        }
   }

上面可以看出,这里处理了所有的回调,快门回调mShutterCallback.onShutter()RawImageCallback.onPictureTaken()拍照数据回调,自动对焦回调等。。默认是没有previewcallback这个回调的,除非你的app设置了setPreviewCallback,可以看出preview的数据还是可以向上层回调,只是系统默认不回调,另数据采集区与显示区两个缓存区bufferpreview数据的投递,以完成preview实时显示是在HAL层完成的。

takePicture()处理过程跟preview差不多,只是增加了回调函数返回时候存储图像的动作

        publicJpegPictureCallback(Location loc) {
           mLocation = loc;
       }

        public voidonPictureTaken(
               final byte [] jpegData, final android.hardware.Camera camera){
             .........................
           if (!mIsImageCaptureIntent) {
               Size s = mParameters.getPictureSize();
               mImageSaver.addImage(jpegData,mLocation, s.width, s.height);
           } else {
               mJpegImageData = jpegData;
               if (!mQuickCapture) {
                   showPostCaptureAlert();
               } else {
                   doAttach();
               }
           }
           }
        }
   }

抱歉!评论已关闭.