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

Android Camera数据流分析全程记录

2013年10月25日 ⁄ 综合 ⁄ 共 5315字 ⁄ 字号 评论关闭
Android Camera数据流分析全程记录


花了不少时间在这个数据流的分析上面,自己毕竟没怎么做过android,这里记录一下自己的见解,任何理解错误还望高人指教,以后还需慢慢纠正
整个分析过程从app的onCreate开始:packages/apps/OMAPCamera/src/com/ti/omap4/android/camera/Camera.java
在onCreate中做了很多的初始化,我们真正关注的是一下几条语句:

  1. // don'set mSurfaceHolder
    here
    . We have it set ONLY
    within

  2. // surfaceChanged / surfaceDestroyed, other
    parts of the code

  3. // assume
    that when it 
    is set, the
    surface 
    is also set.
  4. SurfaceView preview = (SurfaceView) findViewById(R.id.camera_preview);
  5. SurfaceHolder holder = preview.getHolder();
  6. holder.addCallback(this);

在这里我们实例化了一个SurfaceView对象,通过这个对象获取SurfaceHolder对象,实现这个addCallback方法,
其中SurfaceView的定义在以下路径:frameworks/base/core/java/android/view/SurfaceView.java
其中SurfaceHolder的定义在以下路径:frameworks/base/core/java/android/view/SurfaceHolder.java

这里看看这个文章的解释,写的很是不错:http://blog.chinaunix.net/uid-9863638-id-1996383.html

SurfaceFlinger 是Android multimedia 的一个部分,在Android 的实现中它是一个service ,提供系统范围内的surface composer 功能,它能够将各种应用程序的2D,3D surface 进行组合。
在具体讲SurfaceFlinger 之前,我们先来看一下有关显示方面的一些基础知识 。

每个应用程序可能对应着一个或者多个图形界面,而每个界面我们就称之为一个surface ,或者说是window ,在上面的图中我们能看到4 个surface ,一个是home 界面,还有就是红、绿、蓝分别代表的3 个surface ,而两个button 实际是home surface 里面的内容。在这里我们能看到我们进行图形显示所需要解决
的问题:

a 、首先每个surface 在屏幕上有它的位置,以及大小,然后每个surface 里面还有要显示的内容,内容,大小,位置 这些元素 在我们改变应用程序的时候都可能会改变,改变时应该如何处理 
b 、然后就各个surface 之间可能有重叠,比如说在上面的简略图中,绿色覆盖了蓝色,而红色又覆盖了绿色和蓝色以及下面的home ,而且还具有一定透明度。这种层之间的关系应该如何描述      
我们首先来看第二个问题,我们可以想象在屏幕平面的垂直方向还有一个Z 轴,所有的surface 根据在Z 轴上的坐标来确定前后,这样就可以描述各个surface 之间的上下覆盖关系了,而这个在Z 轴上的顺序,图形上有个专业术语叫Z-order 。  
对于第一个问题,我们需要一个结构来记录应用程序界面的位置,大小,以及一个buffer 来记录需要显示的内容,所以这就是我们surface 的概念,surface 实际我们可以把它理解成一个容器,这个容器记录着应用程序界面的控制信息,比如说大小啊,位置啊,而它还有buffer 来专门存储需要显示的内容。
在这里还存在一个问题,那就是当存在图形重合的时候应该如何处理呢,而且可能有些surface 还带有透明信息,这里就是我们SurfaceFlinger 需要解决问题,它要把各个surface 组合(compose/merge) 成一个main Surface ,最后将Main Surface 的内容发送给FB/V4l2
Output ,这样屏幕上就能看到我们想要的效果。

在实际中对这些Surface 进行merge 可以采用两种方式,一种就是采用软件的形式来merge ,还一种就是采用硬件的方式,软件的方式就是我们的SurfaceFlinger ,而硬件的方式就是Overlay 。

首先继承SurfaceView并实现SurfaceHolder.Callback接口
使用接口的原因:因为使用SurfaceView 有一个原则,所有的绘图工作必须得在Surface 被创建之后才能开始(Surface—表面,基本上我们可以把它当作显存的一个映射,写入到Surface 的内容可以被直接复制到显存从而显示出来,这使得显示速度会非常快),而在Surface 被销毁之前必须结束。所以Callback
中的surfaceCreated 和surfaceDestroyed 就成了绘图处理代码的边界。

需要重写的方法
 (1)public void surfaceChanged(SurfaceHolder holder,int format,int width,int height){}//在surface的大小发生改变时激发
 (2)public void surfaceCreated(SurfaceHolder holder){}//在创建时激发,一般在这里调用画图的线程。
 (3)public void surfaceDestroyed(SurfaceHolder holder) {} //销毁时激发,一般在这里将画图的线程停止、释放。
这几个方法在在app中都已经重新实现了,重点分析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) {//这里check摄像头是否已经启动,第一次启动摄像头和摄像头已经打开从新进入摄像头实现方法不同
  26.             startPreview(true);
  27.             startFaceDetection();
  28.         } else {
  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.     }

以上标注部分是关键,现在直接分心startPreview方法,这是第一次打开摄像头的处理函数,进行了一些初始换,而已经处于摄像头打开状态时不必使用startPreview方法,而是用上面的另外一个分支,重新开始显示即可

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

  3.         mFocusManager.

抱歉!评论已关闭.