本章我们来分析一下之前我们提过的相机采集的数据究竟是如何绘制到屏幕上的,这里需要几个必要的知识点:OpenGL、Android的SurfaceTexture、TextureView。网上可以搜到比较全面的有关于这些知识的文章,因此本文将不会花大篇幅介绍这些知识。
既然要将相机的采集,那么我们还是得从开启预览说起,之前文章(https://zsyyblog.com/88b52125.html )有详细分析过startPreview
的具体细节,但是对startPreview
这个方法的参数没有做太多的分析,这里我们将从底层倒过来分析这个startPreview
参数的来龙去脉。我们先看com.serenegiant.usb.UVCCamera
的代码片段:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public synchronized void setPreviewDisplay (final SurfaceHolder holder) { nativeSetPreviewDisplay(mNativePtr, holder.getSurface()); } public synchronized void setPreviewTexture (final SurfaceTexture texture) { final Surface surface = new Surface (texture); nativeSetPreviewDisplay(mNativePtr, surface); } public synchronized void setPreviewDisplay (final Surface surface) { nativeSetPreviewDisplay(mNativePtr, surface); }
直接说结论,相机相当于一个生产者,它会不断地产生图像数据,与其对应的自然会需要有一个消费者来接收相机送过来的数据,而这个消费者的代表就是Surface。最终这个Surface将会通过Android封装好的SurfaceView或者TextureView绘制到屏幕上。接下来我们再看这里传过来的Surface(或者是Surface的包装如SurfaceTexture等)是怎么来的。
1 2 3 4 5 6 7 8 9 10 11 12 13 public void handleStartPreview (final Object surface) { ... if (surface instanceof SurfaceHolder) { mUVCCamera.setPreviewDisplay((SurfaceHolder) surface); } if (surface instanceof Surface) { mUVCCamera.setPreviewDisplay((Surface) surface); } else { mUVCCamera.setPreviewTexture((SurfaceTexture) surface); } mUVCCamera.startPreview(); ... }
我们继续跟踪代码发现UVCCamera的这几个方法调用者是AbstractUVCCameraHandler
中CameraThread的handleStartPreview
方法。而这个方法又是AbstractUVCCameraHandler
本身的startPreview
方法所调用的,重点来了,这个startPreview
中的Object surface
究竟是从何而来呢。答案就是这个surface正是由最终渲染承载的TextureView
所创建出来的。在UVCCamera中已经帮我们封装好了一个TextureView——UVCCameraTextureView
,UVCCameraTextureView
实现了一个自定义的方便调用的接口——CameraViewInterface
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public interface CameraViewInterface extends IAspectRatioView { interface Callback { void onSurfaceCreated (CameraViewInterface view, Surface surface) ; void onSurfaceChanged (CameraViewInterface view, Surface surface, int width, int height) ; void onSurfaceDestroy (CameraViewInterface view, Surface surface) ; } void onPause () ; void onResume () ; void setCallback (Callback callback) ; SurfaceTexture getSurfaceTexture () ; Surface getSurface () ; boolean hasSurface () ; void setVideoEncoder (final IVideoEncoder encoder) ; Bitmap captureStillImage (int width, int height) ; }
而这个接口中正有一个获取Surface的方法——getSurfaceTexture
。我们继续看UVCCameraTextureView
中对这个方法的具体实现:
1 2 3 4 @Override public SurfaceTexture getSurfaceTexture () { return mRenderHandler != null ? mRenderHandler.getPreviewTexture() : null ; }
在UVCCameraTextureView中定义了一个渲染线程——RenderThread
,RenderHandler
则是这个线程中创建的Handler。我们再看这个RenderHandler
的getPreviewTexture
方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public final SurfaceTexture getPreviewTexture () { if (DEBUG) Log.v(TAG, "getPreviewTexture:" ); if (mIsActive) { synchronized (mThread.mSync) { sendEmptyMessage(MSG_CREATE_SURFACE); try { mThread.mSync.wait(); } catch (final InterruptedException e) { } return mThread.mPreviewSurface; } } else { return null ; } }
可以看到最终这个SurfaceTexture
是在RenderThread
中的,我们发现在return之前这个Handler还发了一个create_surface
的消息。
1 2 3 4 5 6 7 8 9 10 11 @Override public final void handleMessage (final Message msg) { if (mThread == null ) return ; switch (msg.what) { ··· case MSG_CREATE_SURFACE: mThread.updatePreviewSurface(); break ; ··· } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public final void updatePreviewSurface () { if (DEBUG) Log.i(TAG, "RenderThread#updatePreviewSurface:" ); synchronized (mSync) { if (mPreviewSurface != null ) { if (DEBUG) Log.d(TAG, "updatePreviewSurface:release mPreviewSurface" ); mPreviewSurface.setOnFrameAvailableListener(null ); mPreviewSurface.release(); mPreviewSurface = null ; } mEglSurface.makeCurrent(); if (mTexId >= 0 ) { mDrawer.deleteTex(mTexId); } mTexId = mDrawer.initTex(); if (DEBUG) Log.v(TAG, "updatePreviewSurface:tex_id=" + mTexId); mPreviewSurface = new SurfaceTexture (mTexId); mPreviewSurface.setDefaultBufferSize(mViewWidth, mViewHeight); mPreviewSurface.setOnFrameAvailableListener(mHandler); mSync.notifyAll(); } }
可以看到最终这个SurefaceTexture
是在这里创建的。我们大概分析一下这段代码,首先当mPreviewSurface
不为空时候先将其release。然后调用EGLBase.IEglSurface
的makeCurrent
方法,这个EGLBase.IEglSurface
也是UVCCamera中定义的,它主要封装了EGL相关的东西,并且它是根据UVCCameraTextureView
的Surface所创建的:
1 2 3 4 5 6 7 8 9 private final void init () { if (DEBUG) Log.v(TAG, "RenderThread#init:" ); mEgl = EGLBase.createFrom(null , false , false ); mEglSurface = mEgl.createFromSurface(mSurface); mEglSurface.makeCurrent(); mDrawer = new GLDrawer2D (true ); }
这里的mSurface
就是UVCCameraTextureView
中onSurfaceTextureAvailable
回调所拿到的。
我们继续回到mEglSurface.makeCurrent()
,Android里(好像是Api 17以后)用到的EGL是1.4版本,而makeCurrent
最终调用的也是EGL14的makeCurrent
。这个方法是用来切换EGL的上下文,只有在该方法调用之后,我们才可以调用OpenGL的方法。
接下来是初始化纹理——mDrawer.initTex()
,这个mDrawer也是UVCCamera中定义的类——GLDrawer2D
,它是一个对着色器、纹理操作相关的封装,有兴趣的同学可以仔细阅读一下这个类。最终这个方法会返回一个纹理id。接下来的代码就是根据这个纹理id创建一块SurfaceTexture
并设置默认框以及对纹理变化的监听。
相关内容
参考链接:https://www.jianshu.com/p/3b7f3ff6ab45