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

Android Camera数据流分析全程记录(overlay方式)

2013年03月06日 ⁄ 综合 ⁄ 共 7429字 ⁄ 字号 评论关闭
 Android
Camera数据流分析全程记录(overlay方式)



这里为什么要研究overlay方式呢?android camera需要driver和app层需要有大量数据需要传输,如果使用非overlay方式进行数据从driver到app层的传输,使系统性能受到很到影响,使系统速度变慢,同时会影响功耗等,而在camera preview module时,通常我们是不必要将采集的数据保存下来的,而不像录像module下,需要将数据保存下来,所以overlay方式就是不经过数据回传,直接显示从driver的数据方式,采用这种方式app从无法获取到数据,所以这种方式应用在preview方式下

这里我是针对android4.0版本的,相对android2.x版本的overlay已经发生了很大的变化,想要研究这方面的可以自己去了解一下,这里不再多说了


开始部分我就直接在这里带过了,系统初始打开camera时,调用到app的onCreate方法,这里主要做了一下工作:

1.开始一个openCamera线程打开camera

2.实例化很多的对象,用于camera工作使用
3.实例化surfaceview和surfaceholder,并且填充了其中的surfacechanged,surfacedestoryed和surfacecreated这三个方式
4.开始一个preview线程用于preview过程

这其中3.4是我们这里要关注的重点,上面实例化了这个surfaceview将决定了我们到底是否使用overlay方式

在这里第三遍完成之后,系统会自动执行surfacechanged这个方式,每次显示区域发生改变都会自动调用这个方法,刚开始打开camera时,显示区域从无到有,因此必要这里会想调用到surfacechanged方法

我们就还是看看在这里都做了些什么事情

  1. public void surfaceChanged(SurfaceHolder
    holder, int format, int w, int h) {
  2.         // Make sure we have a surface in the
    holder before proceeding.
  3.         if (holder.getSurface() == null) {
  4.             Log.d(TAG, "holder.getSurface()
    == null"
    );
  5.             return;
  6.         }

  7.         Log.v(TAG, "surfaceChanged.
    w="
     + w + ". h=" + h);

  8.         // We need to save
    the holder for later use, even when the mCameraDevice
  9.         // is null. This
    could happen if onResume() is invoked
    after this
  10.         // function.
  11.         mSurfaceHolder = holder;

  12.         // The mCameraDevice will be null if it
    fails to connect to the camera
  13.         // hardware. In this case we
    will show a dialog and then finish the
  14.         // activity, so
    it's OK to ignore it.
  15.         if (mCameraDevice == null) return;

  16.         // Sometimes surfaceChanged is called
    after onPause or before onResume.
  17.         // Ignore it.
  18.         if (mPausing || isFinishing()) return;

  19.         setSurfaceLayout();

  20.         // Set preview
    display if the surface is being created. Preview was
  21.         // already started. Also
    restart the preview if display rotation has
  22.         // changed. Sometimes
    this happens when the device is held in portrait
  23.         // and camera
    app is opened. Rotation animation takes some time and
  24.         // display rotation in onCreate
    may not be what we want.
  25.         if (mCameraState == PREVIEW_STOPPED) {//这里表示第一次打开camera时,那么调用startpreview
  26.             startPreview(true);
  27.             startFaceDetection();
  28.         } else {//这里则表示camera已经打开过程中发生的显示变化,比如横屏竖频转换,所以zheli只需要重新设置previewdisplay
  29.             if (Util.getDisplayRotation(this) != mDisplayRotation) {
  30.                 setDisplayOrientation();
  31.             }
  32.             if (holder.isCreating()) {
  33.                 // Set preview
    display if the surface is being created and preview
  34.                 // was already started. That
    means preview display was set to null
  35.                 // and we
    need to set it now.
  36.                 setPreviewDisplay(holder);
  37.             }
  38.         }

  39.         // If first time initialization is not finished, send
    a message to do
  40.         // it later. We
    want to finish surfaceChanged as soon as possible to let
  41.         // user see preview first.
  42.         if (!mFirstTimeInitialized) {
  43.             mHandler.sendEmptyMessage(FIRST_TIME_INIT);
  44.         } else {
  45.             initializeSecondTime();
  46.         }

  47.         SurfaceView preview = (SurfaceView) findViewById(R.id.camera_preview);
  48.         CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId];
  49.         boolean mirror = (info.facing == CameraInfo.CAMERA_FACING_FRONT);
  50.         int displayRotation = Util.getDisplayRotation(this);
  51.         int displayOrientation = Util.getDisplayOrientation(displayRotation, mCameraId);

  52.         mTouchManager.initialize(preview.getHeight() / 3, preview.getHeight() / 3,
  53.                preview, this, mirror, displayOrientation);

  54.     }

从上面代码我们必须知道,在surface发生变化时必须调用setPreviewDisplay,根据之后的学习,在startpreview方式中真正startpreview之前同样要调用setPreviewDisplay,在setPreviewDisplay的方法中完成了很多初始化,也是在这里决定是否使用overlay方式的,我们就先看看startpreview这个方法吧

  1. private void startPreview(boolean
    updateAll) {
  2.         if (mPausing || isFinishing()) return;

  3.         mFocusManager.resetTouchFocus();

  4.         mCameraDevice.setErrorCallback(mErrorCallback);

  5.         // If we're
    previewing already, stop the preview first (this will blank
  6.         // the screen).
  7.         if (mCameraState != PREVIEW_STOPPED) stopPreview();

  8.         setPreviewDisplay(mSurfaceHolder);
  9.         setDisplayOrientation();

  10.         if (!mSnapshotOnIdle) {
  11.             // If the
    focus mode is continuous autofocus, call cancelAutoFocus to
  12.             // resume it
    because it may have been paused by autoFocus call.
  13.             if (Parameters.FOCUS_MODE_CONTINUOUS_PICTURE.equals(mFocusManager.getFocusMode())) {
  14.                 mCameraDevice.cancelAutoFocus();
  15.             }
  16.             mFocusManager.setAeAwbLock(false); // Unlock
    AE and AWB.
  17.         }

  18.         if ( updateAll ) {
  19.             Log.v(TAG, "Updating
    all parameters!"
    );
  20.             setCameraParameters(UPDATE_PARAM_INITIALIZE | UPDATE_PARAM_ZOOM | UPDATE_PARAM_PREFERENCE);
  21.         } else {
  22.             setCameraParameters(UPDATE_PARAM_MODE);
  23.         }

  24.         //setCameraParameters(UPDATE_PARAM_ALL);

  25.         // Inform the mainthread to go on the
    UI initialization.
  26.         if (mCameraPreviewThread != null) {
  27.             synchronized (mCameraPreviewThread) {
  28.                 mCameraPreviewThread.notify();
  29.             }
  30.         }

  31.         try {
  32.             Log.v(TAG, "startPreview");
  33.             mCameraDevice.startPreview();
  34.         } catch (Throwable ex) {
  35.             closeCamera();
  36.             throw new RuntimeException("startPreview failed", ex);
  37.         }

  38.         mZoomState = ZOOM_STOPPED;
  39.         setCameraState(IDLE);
  40.         mFocusManager.onPreviewStarted();
  41.         if ( mTempBracketingEnabled ) {
  42.             mFocusManager.setTempBracketingState(FocusManager.TempBracketingStates.ACTIVE);
  43.         }

  44.         if (mSnapshotOnIdle) {
  45.             mHandler.post(mDoSnapRunnable);
  46.         }
  47.     }

上面大家看到了,先调用了setPreviewDisplay,最后调用mCameraDevice.startPreview()开始preview

这里过程如下:app-->frameworks-->JNI-->camera client-->camera service-->hardware interface-->HAL

1.setPreviewDisplay方法调用时在app层最初的传入的参数是surfaceholder结构

2.到了JNI层setPreviewDisplay方法传入的参数已经是surface结构了

3.到了camera service层

    sp<IBinder> binder(surface != 0 ? surface->asBinder() : 0);

    sp<ANativeWindow> window(surface);

    return setPreviewWindow(binder, window);

    通过上面的转换调用同名不同参数的另外一个方法,到这里调用的参数已经转变为IBinder和ANativeWindow

4.调用hardware interface的setPreviewWindow(window),这里只有一个ANativeWindow类型的参数

5.到了camerahal_module中转站时又发生了变化
,看看下面的定义,参数变为preview_stream_ops 这个类型的结构

    int camera_set_preview_window(struct camera_device * device, struct preview_stream_ops *window)

上面过程参数类型一直在变化,不过从app层一直传到这里,其实是对同一个内存地址的传输,就像张三换了身衣服,但是他还是张三一样

现在我们就直接看看HAL层的实现

  1. /**
  2.    @brief Sets ANativeWindow object.

  3.    Preview buffers provided to CameraHal via this object. DisplayAdapter will be
    interfacing with it
  4.    to render buffers to display.

  5.    @param[in] window The
    ANativeWindow object created by Surface flinger
  6.    @return NO_ERROR If the ANativeWindow object passes validation criteria
  7.    @todo Define validation criteria for ANativeWindow object. Define error codes for scenarios

  8.  */
  9. status_t CameraHal::setPreviewWindow(struct
    preview_stream_ops *window)
  10. {
  11.     status_t ret = NO_ERROR;
  12.     CameraAdapter::BuffersDescriptor desc;

  13.     LOG_FUNCTION_NAME;
  14.     mSetPreviewWindowCalled = true;

  15.    ///If the
    Camera service passes a null window, we destroy existing window and free
    the DisplayAdapter
  16.     if(!window)//这种情况下,window是null,表示不采用overlay方式,则不需要新建displayadapter
  17.     {
  18.         if(mDisplayAdapter.get() != NULL)
  19.         {
  20.             ///NULL window passed, destroy
    the display adapter if present
  21.             CAMHAL_LOGD("NULL window passed, destroying display adapter");
  22.             mDisplayAdapter.clear();
  23.             ///@remarks If there
    was a window previously existing, we usually expe

抱歉!评论已关闭.