SurfaceView + OpenGLES 预览相机
使用OpenGLES 预览相机,我们可以通过GLSurfaceView 来预览相机。GLSurfaceView封装了EGLContext。关于GLSurfaceView的源码里面,GLThread作为单独的线程处理OpenGL的绘制操作,但是这里有个问题,我们可以看看GLThread里面的循环:
while (true) {
synchronized (sGLThreadManager) {
while (true) {
... // 处理是否需要刷新
sGLThreadManager.wait();
}
} // end of synchronized(sGLThreadManager)
if (event != null) {
event.run();
event = null;
continue;
}
...
}
内循环是用来判断是否需要走绘制循环。当使用RENDERMODE_WHEN_DIRTY而非RENDERMODE_CONTINUOUSLY时,如果我们不主动调用requestRender绘制的话,它会一直在内部等待。然后另外一点就是,当我们调用queueEvent方法过多的时候,会导致event事件过多,然后需要不断地循环处理event事件,最终并没有走到刷新画面的流程。也就是说,为了保证得到更高的fps,我们需要解决这个问题。还有另外一个问题就是GLSurfaceView 中的EGL环境有可能会丢失重建的情况,对后续利用SharedContext做录制处理有影响。
因此,我没有使用GLSurfaceView来做绘制操作,用另外一个Looper线程单独处理OpenGLES 的纹理资源加载、渲染等操作。放弃使用GLSurfaceView 的另外一大原因是,为了利用SharedContext实现无丢帧录制视频的功能,GLSurfaceView 有可能会在中途释放并重新创建EGLContext,导致SharedContext失效,录制失败的情况。关于这个的话,可以参考grafika,里面有issue讨论过这个问题。
关于SurfaceView + OpenGLES 预览相机,可以参考本人的文章:
Android Camera SurfaceView OpenGLES 预览
这篇文章是很久之前写的,现在CainCamera开源项目已经发生了比较大的改变。这里还是重新介绍一遍吧。不过这次应该是最后一次大改动了,相机部分的功能基本已经完成,只剩一些小功能没有实现而已,而且暂时也不会再更新相机部分的功能了。
渲染线程 —— HandlerThread
通过HandlerThread 创建EGLContext绑定的渲染线程,如下:
class RenderThread extends HandlerThread implements SurfaceTexture.OnFrameAvailableListener,
Camera.PreviewCallback {
private static final String TAG = "RenderThread";
private static final boolean VERBOSE = false;
// 操作锁
private final Object mSynOperation = new Object();
// 更新帧的锁
private final Object mSyncFrameNum = new Object();
private final Object mSyncFence = new Object();
private boolean isPreviewing = false; // 是否预览状态
private boolean isRecording = false; // 是否录制状态
private boolean isRecordingPause = false; // 是否处于暂停录制状态
// EGL共享上下文
private EglCore mEglCore;
// 预览用的EGLSurface
private WindowSurface mDisplaySurface;
private int mInputTexture;
private int mCurrentTexture;
private SurfaceTexture mSurfaceTexture;
// 矩阵
private final float[] mMatrix = new float[16];
// 预览回调
private byte[] mPreviewBuffer;
// 输入图像大小
private int mTextureWidth, mTextureHeight;
// 可用帧
private int mFrameNum = 0;
// 渲染Handler回调
private RenderHandler mRenderHandler;
// 计算帧率
private FrameRateMeter mFrameRateMeter;
// 上下文
private Context mContext;
// 正在拍照
private volatile boolean mTakingPicture;
// 预览参数
private CameraParam mCameraParam;
// 渲染管理器
private RenderManager mRenderManager;
public RenderThread(Context context, String name) {
super(name);
mContext = context;
mCameraParam = CameraParam.getInstance();
mRenderManager = RenderManager.getInstance();
mFrameRateMeter = new FrameRateMeter();
}
/**
* 设置预览Handler回调
* @param handler
*/
public void setRenderHandler(RenderHandler handler) {
mRenderHandler = handler;
}
@Override
public void onFrameAvailable(SurfaceTexture surfaceTexture) {
}
private long time = 0;
@Override
public void onPreviewFrame(byte[] data, Camera camera) {
synchronized (mSynOperation) {
if (isPreviewing || isRecording) {
mRenderHandler.sendMessage(mRenderHandler
.obtainMessage(RenderHandler.MSG_PREVIEW_CALLBACK, data));
}
}
if (mPreviewBuffer != null) {
camera.addCallbackBuffer(mPreviewBuffer);
}
// 计算fps
if (mRenderHandler != null && mCameraParam.showFps) {
mRenderHandler.sendEmptyMessage(RenderHandler.MSG_CALCULATE_FPS);
}
if (VERBOSE) {
Log.d("onPreviewFrame", "update time = " + (System.currentTimeMillis() - time));
time = System.currentTimeMillis();
}
}
/**
* 预览回调
* @param data
*/
void onPreviewCallback(byte[] data) {
if (mCameraParam.cameraCallback != null) {
mCameraParam.cameraCallback.onPreviewCallback(data);
}
}
/**
* Surface创建
* @param holder
*/
void surfaceCreated(SurfaceHolder holder) {
mEglCore = new EglCore(null, EglCore.FLAG_RECORDABLE);
mDisplaySurface = new WindowSurface(mEglCore, holder.getSurface(), false);
mDisplaySurface.makeCurrent();
GLES30.glDisable(GLES30.GL_DEPTH_TEST);
GLES30.glDisable(GLES30.GL_CULL_FACE);
// 渲染器初始化
mRenderManager.init(mContext);
mInputTexture = OpenGLUtils.createOESTexture();
mSurfaceTexture = new SurfaceTexture(mInputTexture);
mSurfaceTexture.setOnFrameAvailableListener(this);
// 打开相机
openCamera();
}
/**
* Surface改变
* @param width
* @param height
*/
void surfaceChanged(int width, int height) {
mRenderManager.setDisplaySize(width, height);
startPreview();
}
/**
* Surface销毁
*/
void surfaceDestroyed() {
mTakingPicture = false;
mRenderManager.release();
releaseCamera();
if (mSurfaceTexture != null) {
mSurfaceTexture.release();
mSurfaceTexture = null;
}
if (mDisplaySurface != null) {
mDisplaySurface.release();
mDisplaySurface = null;
}
if (mEglCore != null) {
mEglCore.release();
mEglCore = null;
}
}
/**
* 绘制帧
*/
void drawFrame() {
// 如果存在新的帧,则更新帧
synchronized (mSyncFrameNum) {
synchronized (mSyncFence) {
if (mSurfaceTexture != null) {
while (mFrameNum != 0) {
mSurfaceTexture.updateTexImage();
--mFrameNum;
}
} else {
return;
}
}
}
// 切换渲染上下文
mDisplaySurface.makeCurrent();
mSurfaceTexture.getTransformMatrix(mMatrix);
// 绘制渲染
mCurrentTexture = mRenderManager.drawFrame(mInputTexture, mMatrix);
// 是否绘制人脸关键点
mRenderManager.drawFacePoint(mCurrentTexture);
// 显示到屏幕
mDisplaySurface.swapBuffers();
// 执行拍照
if (mCameraParam.isTakePicture && !mTakingPicture) {
synchronized (mSyncFence) {
mTakingPicture = true;
mRenderHandler.sendEmptyMessage(RenderHandler.MSG_TAKE_PICTURE);
}
}
// 是否处于录制状态
if (isRecording && !isRecordingPause) {
HardcodeEncoder.getInstance().frameAvailable();
HardcodeEncoder.getInstance()
.drawRecorderFrame(mCurrentTexture, mSurfaceTexture.getTimestamp());
}
}
/**
* 拍照
*/
void takePicture() {
synchronized (mSyncFence) {
ByteBuffer buffer = mDisplaySurface.getCurrentFrame();
mCameraParam.captureCallback.onCapture(buffer,
mDisplaySurface.getWidth(), mDisplaySurface.getHeight());
mTakingPicture = false;
mCameraParam.isTakePicture = false;
}
}
/**
* 计算fps
*/
void calculateFps() {
// 帧率回调
if ((mCameraParam).fpsCallback != null) {
mFrameRateMeter.drawFrameCount();
(mCameraParam).fpsCallback.onFpsCallback(mFrameRateMeter.getFPS());
}
}
/**
* 计算imageView 的宽高
*/
private void calculateImageSize() {
if (mCameraParam.orientation == 90 || mCameraParam.orientation == 270) {
mTextureWidth = mCameraParam.previewHeight;
mTextureHeight = mCameraParam.previewWidth;
} else {
mTextureWidth = mCameraParam.previewWidth;
mTextureHeight = mCameraParam.previewHeight;
}
mRenderManager.setTextureSize(mTextureWidth, mTextureHeight);
}
/**
* 切换边框模糊
* @param en