【Android CameraX】CameraX源码分析和最佳实践 (二)——Surface、SurfaceView、TextureView、SurfaceTexture和GLSurfaceView

一、简介

本片文章旨在把CameraX框架里相机界面预览相关的SurfaceSurfaceViewTextureViewSurfaceTextureGLSurfaceView组件讲解清楚。
文章包括如下部分:

  1. 相机预览界面实现思路和常见的实现方案。
  2. SurfaceSurfaceViewTextureViewSurfaceTextureGLSurfaceView组件
  3. 相关代码最佳实践

相关文章推荐:

  1. Android Camera系列文章目录索引汇总
  2. Android CameraX 综述
  3. CameraXBasic —— 官方CameraX实例源码分析
  4. Camera1初始化销毁流程(二) —— 初始化基本框架和CameraView几种实现方式及其伪代码
  5. CameraX初始化、预览、销毁源码分析和最佳实践 (一) —— 介绍和CameraX相关配置
  6. Android Camera理论协议和规范

关注我,随时获取CameraX更多相关资讯。

二、相机预览界面逻辑实现

相机预览界面,相当于把物理硬件摄像头捕获到的真实3D画面显示在2D的手机界面上。如下图,具体的详细流程,可参看文章开头相关文章推荐里面第七篇。
在这里插入图片描述
因此,相机画面预览界面简单理解就是提供一个容器给到系统层面,使现实世界的画面通过硬件摄像头处理后,绘制在容器上面。 这里就不得不引出Android操作系统里Surface的概念,没错Surface就类似这样的容器功能。

接下来的内容,涉及到通用的View细节,我们这里不在细说,感兴趣的可参看相关文件推荐里面第四篇,或者自行网上搜索 本文后续内容会重点关注

  1. SurfaceView、TextureView、SurfaceTexture和GLSurfaceView如何提供并管理Surface的。
  2. 为什么有这么多的组件,以及什么样的场景下怎么选择这些组件。

2.1 Surface

在Android生态中,无论开发者用什么渲染 API,一切内容都会渲染到 Surface 上。我们从该类的头部可以看到如下的定义:

/**
 * Handle onto a raw buffer that is being managed by the screen compositor.
 *
 * A Surface is generally created by or from a consumer of image buffers such as a 
 * {android.graphics.SurfaceTexture}, 
 * {android.media.MediaRecorder},
 * {android.renderscript.Allocation}
 * 
 * and is handed to some kind of producer such as
 * {android.opengl.EGL14#eglCreateWindowSurface(android.opengl.EGLDisplay,android.opengl.EGLConfig,java.lang.Object,int[],int) OpenGL},
{android.media.MediaPlayer#setSurface MediaPlayer}
{android.hardware.camera2.CameraDevice#createCaptureSession CameraDevice} to draw
 * into.

可以得到如下几个信息

  1. a raw buffer
  2. 生产方:OpenGL ES、Canvas 2D 和 mediaserver
  3. 消费方:SurfaceTexture、相机预览图像流,ImageReader

也即由消费方创建Surface发送给生产方,生产方在上面进行绘制。到此我们即可得出,我们需要做的是在UI层面创建一个CameraDisplayView。并提供相应的Surface,传给生产方,这里也就是相机的底层。

Surface通常有2种通用的使用方式来获取:

  1. 通过【2.3】节组件里提供的公用方法获取Surface
  2. 通过【2.2】节SurfaceTexture创建
public Surface(SurfaceTexture surfaceTexture) {
    
    
    if (surfaceTexture == null) {
    
    
        throw new IllegalArgumentException("surfaceTexture must not be null");
    }
    mIsSingleBuffered = surfaceTexture.isSingleBuffered();
    synchronized (mLock) {
    
    
        mName = surfaceTexture.toString();
        setNativeObjectLocked(nativeCreateFromSurfaceTexture(surfaceTexture));
    }
}

2.2 SurfaceTexture

2.2.1 SurfaceTexture的概念解析

定义 : Captures frames from an image stream as an OpenGL ES texture.

可以看出SurfaceTexture来源一个图像流。接着阅读如下:

The image stream may come from either camera preview or video decode.

这个图像流,可以来源于camera preview or video decode。也就是说,如果要在App上实现预览摄像头的画面,参考【二】,我们只需要通过UI组件生成SurfaceTexture然后传递给CameraCamera获取摄像头捕捉到的图像流在SurfaceTexture上绘制,从而通过UI组件显示在用户面前。

  1. A Surface created from a SurfaceTexture can be used as an output destination for the camera2, MediaCodec, MediaPlayer, and etc.
  2. A SurfaceTexture may also be used in place of a SurfaceHolder when specifying the output destination of the older android.hardware.Camera API.

可以看到SurfaceTexture一可以用来创建Surface;二可以用于SurfaceHolder

其余还有2个比较重要的方法:updateTexImage getTransformMatrix(float[])

  1. updateTexImage 用于刷新内容为图像流的最新一帧
  2. getTransformMatrix(float[])用于获取纹理的转换矩阵

2.2.2 SurfaceTexture的源码分析

SurfaceTexture有三个构造方法,最终都会调用到Native层的nativeInit方法。

private native void nativeInit(boolean isDetached, int texName,
            boolean singleBufferMode, WeakReference<SurfaceTexture> weakSelf)
            throws Surface.OutOfResourcesException;

**构造函数**
public SurfaceTexture(int texName) {
    
    
    this(texName, false);
}

public SurfaceTexture(int texName, boolean singleBufferMode) {
    
    
    mCreatorLooper = Looper.myLooper();
    mIsSingleBuffered = singleBufferMode;
    nativeInit(false, texName, singleBufferMode, new WeakReference<SurfaceTexture>(this));
}

public SurfaceTexture(boolean singleBufferMode) {
    
    
    mCreatorLooper = Looper.myLooper();
    mIsSingleBuffered = singleBufferMode;
    nativeInit(true, 0, singleBufferMode, new WeakReference<SurfaceTexture>(this));
}

这里面有两个重要的参数texNamesingleBufferMode

  1. texNameOpenGL ES的纹理Id通过调用glGenTextures该方法会生成一个纹理Buffer,最后返回一个Id。具体的细节这里不再阐述,感兴趣的可浏览OpenGL ES目录下文章内容
  2. singleBufferMode从命名可知,该字段用于标记是否使用单缓冲还是双缓冲。在单缓冲模式下,应用只能串行的获取图像内容缓冲。

其他常见的调用方法如下,都比较简单,自己浏览即可,具体的实现最终会调用到Native

//Register a callback to be invoked when a new image frame becomes available to the SurfaceTexture.
public void setOnFrameAvailableListener(@Nullable final OnFrameAvailableListener listener,
        @Nullable Handler handler) {
    
    
    if (listener != null) {
    
    
        Looper looper = handler != null ? handler.getLooper() :
                mCreatorLooper != null ? mCreatorLooper : Looper.getMainLooper();
        mOnFrameAvailableHandler = new Handler(looper, null, true /*async*/) {
    
    
            @Override
            public void handleMessage(Message msg) {
    
    
                listener.onFrameAvailable(SurfaceTexture.this);
            }
        };
    } else {
    
    
        mOnFrameAvailableHandler = null;
    }
}

public void setDefaultBufferSize(int width, int height) {
    
    
    nativeSetDefaultBufferSize(width, height);
}


/**
 * Update the texture image to the most recent frame from the image stream.  This may only be
 * called while the OpenGL ES context that owns the texture is current on the calling thread.
 * It will implicitly bind its texture to the GL_TEXTURE_EXTERNAL_OES texture target.
 */
public void updateTexImage() {
    
    
    nativeUpdateTexImage();
}


/**
 * Retrieve the 4x4 texture coordinate transform matrix associated with the texture image set by
 * the most recent call to updateTexImage.
 *
 * This transform matrix maps 2D homogeneous texture coordinates of the form (s, t, 0, 1) with s
 * and t in the inclusive range [0, 1] to the texture coordinate that should be used to sample
 * that location from the texture.  Sampling the texture outside of the range of this transform
 * is undefined.
 *
 * The matrix is stored in column-major order so that it may be passed directly to OpenGL ES via
 * the glLoadMatrixf or glUniformMatrix4fv functions.
 *
 * @param mtx the array into which the 4x4 matrix will be stored.  The array must have exactly
 *     16 elements.
 */
public void getTransformMatrix(float[] mtx) {
    
    
    // Note we intentionally don't check mtx for null, so this will result in a
    // NullPointerException. But it's safe because it happens before the call to native.
    if (mtx.length != 16) {
    
    
        throw new IllegalArgumentException();
    }
    nativeGetTransformMatrix(mtx);
}

2.2.3 SurfaceTexture Demo算法

伪代码,这里的createOPGLESTexture可自行百度搜索模版代码。

private void setCameraImplSurfaceTexture(int wUI, int hUI) {
    
    
    int texId = createOPGLESTexture();
    SurfaceTexture surfaceTexture = new SurfaceTexture(texId);
    surfaceTexture.setOnFrameAvailableListener(this);
    getCameraImpl().setPreviewSurfaceTexture(mSurfaceTexture, wUI, hUI);
}

也可参考TextureView里面的Demo

if (createNewSurface) {
    
    
    // Create a new SurfaceTexture for the layer.
    mSurface = new SurfaceTexture(false);
    nCreateNativeWindow(mSurface);
}
mLayer.setSurfaceTexture(mSurface);
mSurface.setDefaultBufferSize(getWidth(), getHeight());
mSurface.setOnFrameAvailableListener(mUpdateListener, mAttachInfo.mHandler);

核心步骤为:

  1. 构建new SurfaceTexture
  2. 设置setDefaultBufferSize
  3. 监听surfaceTexture.setOnFrameAvailableListener(this)
  4. 丢给Camera实例【具体内容,将在**CameraX源码分析和最佳实践 (二)**阐述】

2.3 SurfaceView

SurfaceView 是Android发布第一个版本自带的API,看其源码可知,主要功能是通过SurfaceHolder来对Surface进行管理,SurfaceView源码如下:

public class SurfaceView extends MockView {
    
    
    public SurfaceView(Context context, AttributeSet attrs, int defStyle) {
    
    
        super(context, attrs, defStyle);
    }

    public SurfaceHolder getHolder() {
    
    
        return mSurfaceHolder;
    }

    private SurfaceHolder mSurfaceHolder = new SurfaceHolder() {
    
    

        @Override
        public boolean isCreating() {
    
    
            return false;
        }

        @Override
        public void addCallback(Callback callback) {
    
    
        }

        @Override
        public void removeCallback(Callback callback) {
    
    
        }

        @Override
        public void setFixedSize(int width, int height) {
    
    
        }

        @Override
        public void setSizeFromLayout() {
    
    
        }

        @Override
        public void setFormat(int format) {
    
    
        }

        @Override
        public void setType(int type) {
    
    
        }

        @Override
        public void setKeepScreenOn(boolean screenOn) {
    
    
        }

        @Override
        public Canvas lockCanvas() {
    
    
            return null;
        }

        @Override
        public Canvas lockCanvas(Rect dirty) {
    
    
            return null;
        }

        @Override
        public void unlockCanvasAndPost(Canvas canvas) {
    
    
        }

        @Override
        public Surface getSurface() {
    
    
            return null;
        }

        @Override
        public Rect getSurfaceFrame() {
    
    
            return null;
        }
    };
}

接下来我们来看SurfaceHolder,它定义了一系列的接口用于展示、监听和控制Surface,到这里我们发现了Surface的踪迹。

Abstract interface to someone holding a display surface. Allows you to control the surface size and format, edit the pixels in the surface, and monitor changes to the surface.

继续浏览源码,具体到对Surface展示、监听和控制。SurfaceHolder首先提供了一个CallBack用于监听Surface的创建、更新和销毁。与此同时,也提供了获取Surface的方法,public Surface getSurface(),详细的CallBack代码如下:


    /**
     * Add a Callback interface for this holder.  There can several Callback
     * interfaces associated with a holder.
     *
     * @param callback The new Callback interface.
     */
    public void addCallback(Callback callback);

    /**
     * Removes a previously added Callback interface from this holder.
     *
     * @param callback The Callback interface to remove.
     */
    public void removeCallback(Callback callback);



    /**
     * A client may implement this interface to receive information about
     * changes to the surface.  When used with a {@link SurfaceView}, the
     * Surface being held is only available between calls to
     * {@link #surfaceCreated(SurfaceHolder)} and
     * {@link #surfaceDestroyed(SurfaceHolder)}.  The Callback is set with
     * {@link SurfaceHolder#addCallback SurfaceHolder.addCallback} method.
     */
    public interface Callback {
    
    
        /**
         * This is called immediately after the surface is first created.
         * Implementations of this should start up whatever rendering code
         * they desire.  Note that only one thread can ever draw into
         * a {@link Surface}, so you should not draw into the Surface here
         * if your normal rendering will be in another thread.
         *
         * @param holder The SurfaceHolder whose surface is being created.
         */
        public void surfaceCreated(SurfaceHolder holder);

        /**
         * This is called immediately after any structural changes (format or
         * size) have been made to the surface.  You should at this point update
         * the imagery in the surface.  This method is always called at least
         * once, after {@link #surfaceCreated}.
         *
         * @param holder The SurfaceHolder whose surface has changed.
         * @param format The new PixelFormat of the surface.
         * @param width The new width of the surface.
         * @param height The new height of the surface.
         */
        public void surfaceChanged(SurfaceHolder holder, int format, int width,
                int height);

        /**
         * This is called immediately before a surface is being destroyed. After
         * returning from this call, you should no longer try to access this
         * surface.  If you have a rendering thread that directly accesses
         * the surface, you must ensure that thread is no longer touching the
         * Surface before returning from this function.
         *
         * @param holder The SurfaceHolder whose surface is being destroyed.
         */
        public void surfaceDestroyed(SurfaceHolder holder);
    }

在三个方法里,我们都可以通过返回的参数获取到Surface然后最终传递给相机实例用于绘制

2.4 TextureView

2.4.1 TextureView的出现

从官网我们会发现Android在 【Api Level1】 就有了SurfaceView,那随之就有一个问题:既然有了SurfaceView,为什么又引入了TextureView【API Level 14】呢?。我们已经获知SurfaceView提供了一个可以绘制的Surface。并通过SurfaceHolder可以监听Surface的创建,更新和销毁。但具体到更新,这里不包括对普通View缩放,动画,裁剪等操作。而且SurfaceView是和Window关联起来的,我们来看如下描述:

The surface is Z ordered so that it is behind the window holding its SurfaceView; the SurfaceView punches a hole in its window to allow its surface to be displayed.

基于此,在Api Level14的时候官方又引入了TextureView,根据其描述。我们发现相比较SurfaceViewTextureView没有创建一个分离的window,而是表现的更像是一个正常的View。支持透明、旋转和裁剪

Unlike SurfaceView, TextureView does not create a separate window but behaves as a regular View. This key difference allows a TextureView to have translucency, arbitrary rotations, and complex clipping. For example, you can make a TextureView semi-translucent by calling myView.setAlpha(0.5f).

但这种支持,却又牺牲了性能。

One implication of this integration of TextureView into the view hierarchy is that it may have slower performance than SurfaceView. TextureView contents must be copied, internally, from the underlying surface into the view displaying those contents.

进而我们可以得出在TextureViewSurfaceView之间的差异

  1. TextureView支持透明、旋转和裁剪,表现和正常View的行为是一致的。
  2. TextureView性能比SurfaceView要差。

官网也对其性能做了进一步的描述:

For that reason(Performance), SurfaceView is recommended as a more general solution to problems requiring rendering to surfaces.

2.4.2 TextureView源码分析

这里我们对TextureView重要的组件和方法进行简要分析,TextureView有3个重要的组件:

  1. TextureLayer mLayer
  2. SurfaceTexture mSurface
  3. SurfaceTextureListener mListener

2.4.2.1 TextureLayer

TextureLayer represents a SurfaceTexture that will be composited by RenderThread into the frame when drawn in a HW accelerated Canvas.

TextureViewSurfaceTexture的操作最终都会调用到TextureLayer里,并传递到HardwareRenderer渲染,或者调用Native方法。

//native方法***
private static native boolean nPrepare(long layerUpdater, int width, int height,
        boolean isOpaque);
private static native void nSetLayerPaint(long layerUpdater, long paint);
private static native void nSetTransform(long layerUpdater, long matrix);
private static native void nSetSurfaceTexture(long layerUpdater, SurfaceTexture surface);
private static native void nUpdateSurfaceTexture(long layerUpdater);

//调用Render方法
public void setSurfaceTexture(SurfaceTexture surface) {
    
    
    nSetSurfaceTexture(mFinalizer.get(), surface);
    mRenderer.pushLayerUpdate(this);
}

public void updateSurfaceTexture() {
    
    
    nUpdateSurfaceTexture(mFinalizer.get());
    mRenderer.pushLayerUpdate(this);
}

public boolean copyInto(Bitmap bitmap) {
    
    
    return mRenderer.copyLayerInto(this, bitmap);
}

public boolean prepare(int width, int height, boolean isOpaque) {
    
    
    return nPrepare(mFinalizer.get(), width, height, isOpaque);
}

public void setTransform(Matrix matrix) {
    
    
    nSetTransform(mFinalizer.get(), matrix.native_instance);
    mRenderer.pushLayerUpdate(this);
}

/**
 * Indicates that this layer has lost its texture.
 */
public void detachSurfaceTexture() {
    
    
    mRenderer.detachSurfaceTexture(mFinalizer.get());
}

2.4.2.2 SurfaceTexture

见【2.2】

2.4.2.3 SurfaceTextureListener

类似SurfaceViewCallBack方法,用于监听SurfaceTexture的创建,更新和销毁


/**
 * This listener can be used to be notified when the surface texture
 * associated with this texture view is available.
 */
public static interface SurfaceTextureListener {
    
    
    /**
     * Invoked when a {@link TextureView}'s SurfaceTexture is ready for use.
     *
     * @param surface The surface returned by
     *                {@link android.view.TextureView#getSurfaceTexture()}
     * @param width The width of the surface
     * @param height The height of the surface
     */
    public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height);

    /**
     * Invoked when the {@link SurfaceTexture}'s buffers size changed.
     *
     * @param surface The surface returned by
     *                {@link android.view.TextureView#getSurfaceTexture()}
     * @param width The new width of the surface
     * @param height The new height of the surface
     */
    public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height);

    /**
     * Invoked when the specified {@link SurfaceTexture} is about to be destroyed.
     * If returns true, no rendering should happen inside the surface texture after this method
     * is invoked. If returns false, the client needs to call {@link SurfaceTexture#release()}.
     * Most applications should return true.
     *
     * @param surface The surface about to be destroyed
     */
    public boolean onSurfaceTextureDestroyed(SurfaceTexture surface);

    /**
     * Invoked when the specified {@link SurfaceTexture} is updated through
     * {@link SurfaceTexture#updateTexImage()}.
     *
     * @param surface The surface just updated
     */
    public void onSurfaceTextureUpdated(SurfaceTexture surface);
}

2.4.2.4 运转流程

抛开一些参数设置、创建销毁、attach和detach之外,TextureView的核心代码流程再draw()函数里。

@Override
public final void draw(Canvas canvas) {
    
    
    // NOTE: Maintain this carefully (see View#draw)
    mPrivateFlags = (mPrivateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;

    if (canvas.isHardwareAccelerated()) {
    
    
        RecordingCanvas recordingCanvas = (RecordingCanvas) canvas;

        TextureLayer layer = getTextureLayer();
        if (layer != null) {
    
    
            applyUpdate();
            applyTransformMatrix();

            mLayer.setLayerPaint(mLayerPaint); // ensure layer paint is up to date
            recordingCanvas.drawTextureLayer(layer);
        }
    }
}

这里提醒一下TextureView只能在支持硬件加速的设备上使用。也即如下注释:

TextureView can only be used in a hardware accelerated window. When rendered in software, TextureView will draw nothing.

draw流程中可以看到有如下几个步骤:

  1. 生成操控SurfaceTextureTextureLayer
  2. 更新SurfaceTexture内容
  3. 获取transformMatrix
  4. 设置画笔参数
  5. 绘制drawTextureLayer

接下来我们分别来看各个步骤,并伴随着代码注释说明:

****************************************
1. 参考2.2 首先new SurfaceTexture
2. 传递到下层,并设置Size和监听
3. 回调
4. 查最后传递给CameraSurfaceView由UI层操作
****************************************
TextureLayer getTextureLayer() {
    
    
		mLayer = mAttachInfo.mThreadedRenderer.createTextureLayer();
        boolean createNewSurface = (mSurface == null);
        if (createNewSurface) {
    
    
            // Create a new SurfaceTexture for the layer.
            mSurface = new SurfaceTexture(false);
            nCreateNativeWindow(mSurface);
        }

        mLayer.setSurfaceTexture(mSurface);
        mSurface.setDefaultBufferSize(getWidth(), getHeight());
        mSurface.setOnFrameAvailableListener(mUpdateListener, mAttachInfo.mHandler);
        if (mListener != null && createNewSurface) {
    
    
            mListener.onSurfaceTextureAvailable(mSurface, getWidth(), getHeight());
        }
        mLayer.setLayerPaint(mLayerPaint);
    }

    if (mUpdateSurface) {
    
    
        mUpdateSurface = false;

        updateLayer();
        mMatrixChanged = true;

        mLayer.setSurfaceTexture(mSurface);
        mSurface.setDefaultBufferSize(getWidth(), getHeight());
    }

    return mLayer;
}

****************************************
1. 设置新参数
2. updateSurfaceTexture更新
3. 最总调用到TextureLayer里,从而调用Native方法
****************************************
private void applyUpdate() {
    
    
    if (mLayer == null) {
    
    
        return;
    }

    synchronized (mLock) {
    
    
        if (mUpdateLayer) {
    
    
            mUpdateLayer = false;
        } else {
    
    
            return;
        }
    }

    mLayer.prepare(getWidth(), getHeight(), mOpaque);
    mLayer.updateSurfaceTexture();

    if (mListener != null) {
    
    
        mListener.onSurfaceTextureUpdated(mSurface);
    }
}

private void applyTransformMatrix() {
    
    
    if (mMatrixChanged && mLayer != null) {
    
    
        mLayer.setTransform(mMatrix);
        mMatrixChanged = false;
    }
}

2.4.3 TextureView的实践Demo

这里提供2个简单的实践Demo以供参考:

Demo1:Camera

可以看到这里TextureView相比较【2.2】只执行了第四步,在onSurfaceTextureAvailable的回调中把SurfaceTexture传递给Camera

public class LiveCameraActivity extends Activity implements TextureView.SurfaceTextureListener {
    
    
    private Camera mCamera;
    private TextureView mTextureView;

    protected void onCreate(Bundle savedInstanceState) {
    
    
        super.onCreate(savedInstanceState);

        mTextureView = new TextureView(this);
        mTextureView.setSurfaceTextureListener(this);

        setContentView(mTextureView);
    }

    public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
    
    
        mCamera = Camera.open();

        try {
    
    
            mCamera.setPreviewTexture(surface);
            mCamera.startPreview();
        } catch (IOException ioe) {
    
    
            // Something bad happened
        }
    }

    public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
    
    
        // Ignored, Camera does all the work for us
    }

    public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
    
    
        mCamera.stopPreview();
        mCamera.release();
        return true;
    }

    public void onSurfaceTextureUpdated(SurfaceTexture surface) {
    
    
        // Invoked every time there's a new Camera preview frame
    }
}

Demo2:MediaPlayer

同样在onSurfaceTextureAvailable的回调中把SurfaceTexture传递给MediaPlayer.

public class MyActivity extends Activity implements TextureView.SurfaceTextureListener {
    
    
	 private MediaPlayer mMediaPlayer;
	 private TextureView mTextureView;
	
	 protected void onCreate(Bundle savedInstanceState) {
    
    
	     super.onCreate(savedInstanceState);
	
	     mMediaPlayer = new MediaPlayer();
	
	     mTextureView = new TextureView(this);
	     mTextureView.setSurfaceTextureListener(this);
	     setContentView(mTextureView);
	 }
	
	 public void onSurfaceTextureAvailable(@NonNull SurfaceTexture surfaceTexture,
	                                       int width, int height) {
    
    
	     AssetFileDescriptor fileDescriptor = // get file descriptor
	     mMediaPlayer.setDataSource(fileDescriptor);
	     mMediaPlayer.setSurface(new Surface(surfaceTexture));
	     mMediaPlayer.prepareAsync();
	     mMediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
    
    
	         @Override
	         public void onPrepared(MediaPlayer mp) {
    
    
	             mMediaPlayer.start();
	         }
	     });
	    } catch (IOException e) {
    
    
	        e.printStackTrace();
	    }
	 }
	
	@Override
	public void onSurfaceTextureSizeChanged(@NonNull SurfaceTexture surfaceTexture,
	                                        int width, int height) {
    
    
	    // Handle size change depending on media needs
	}
	
	@Override
	public boolean onSurfaceTextureDestroyed(@NonNull SurfaceTexture surfaceTexture) {
    
    
	    // Release unneeded resources
	    mMediaPlayer.stop();
	    mMediaPlayer.release();
	    return true;
	}
	
	@Override
	public void onSurfaceTextureUpdated(@NonNull SurfaceTexture surfaceTexture) {
    
    
	     // Invoked every time there's a new video frame
	}

}

2.5 GLSurfaceView

GLSurfaceView涉及到OpenGL ES相关的开发,需要有相关OpenGL ES的基础知识,详细的流程可参考官方文档和Develop Guide。这里就不做赘述。

  1. GLSurfaceView
  2. OpenGL ES

我们这里主要来说说在SurfaceViewTextureView存在的前提下GLSurfaceView的使用场景。
如果去看官方的Demo和开源项目,会发现基本上都使用SurfaceView或者TextureView【根据不同场景具体区分】,如果你只是简单的开发一个相机APP用于拍照,选择SurfaceView完全足够。 但如果你有如下CLSurfaceView官网上说明提供的支持的诉求。则可以选择CLSurfaceView

  1. Manages a surface, which is a special piece of memory that can be composited into the Android view system.
  2. Manages an EGL display, which enables OpenGL to render into a surface.
  3. Accepts a user-provided Renderer object that does the actual rendering.
  4. Renders on a dedicated thread to decouple rendering performance from the UI thread.
  5. Supports both on-demand and continuous rendering.
  6. Optionally wraps, traces, and/or error-checks the renderer’s OpenGL calls.

SurfaceViewTextureView获取自带的Surface或者SurfaceTexture,传递给Camera绘制,如果你想要在上面额外绘制一些特效,则不是一个比较好的UI组件。TextureView里注释也有提到如下:

It is important to note that only one producer can use the TextureView. if you use a TextureView to display the camera preview, you cannot use {@link #lockCanvas()} to draw onto the TextureView at the same time.

GLSurfaceView则提供了OpenGL ES的环境。可以在SurfaceTexture上绘制自己想要的特效纹理。再结合相机预览的图像流进行更多的扩展。具体的简单源码分析可参看如下文章: Android GLSurfaceView详解。感兴趣的同学可自行学习OpenGL ES相关的开发知识,如果只局限在相机层面的功能。 SurfaceViewTextureView则足够使用了。

三、本篇小结

  1. 相机预览界面,相当于把物理硬件摄像头捕获到的真实3D画面显示在2D的手机界面上。需要创建Surface或者SurfaceTexture并传递给Camera实例
  2. SurfaceTexture一可以用来创建Surface;二可以用于SurfaceHolder
  3. 相比较SurfaceViewTextureView支持透明、旋转和裁剪,表现和正常View的行为是一致的。但 TextureView性能比SurfaceView要差。
  4. 只是简单的开发一个相机APP用于拍照,选择SurfaceView完全足够。如果需要额外在相机预览图像流上做额外绘制,则GLSurfaceView是比较好的选择。但需要OpenGL ES相关的基础知识。

猜你喜欢

转载自blog.csdn.net/Scott_S/article/details/127319448