这段时间有点忙,一直没时间写第三篇教程,其实代码很早之前就写好了。本系列教程会有三篇文章讲解Android平台滤镜的实现方式,希望在阅读本文之前先阅读前面两篇文档。
下面进入正题:第三篇
本文使用GLSurfaceView作为布局界面,并且仍将使用SurfaceTexture来获取预览图像,但是Camera V2的API跟Camera V1的差别还挺大的,所以需要熟悉一下,可以参考我之前的博客Android Camera2教程之打开相机、开启预览、实现PreviewCallback、拍照。这里我也再讲一下,下面开始讲解。
public void startCameraThread() {
mCameraThread = new HandlerThread("CameraThread");
mCameraThread.start();
mCameraHandler = new Handler(mCameraThread.getLooper());
}
public String setupCamera(int width, int height) {
//获取摄像头管理者,它主要用来查询和打开可用的摄像头
CameraManager cameraManager = (CameraManager) mActivity.getSystemService(Context.CAMERA_SERVICE);
try {
//遍历所有摄像头
for (String id : cameraManager.getCameraIdList()) {
//获取此ID对应摄像头的参数
CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics(id);
//默认打开后置摄像头
if (characteristics.get(CameraCharacteristics.LENS_FACING) == CameraCharacteristics.LENS_FACING_FRONT) {
continue;
}
//获取StreamConfigurationMap,它是管理摄像头支持的所有输出格式和尺寸
StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
//根据屏幕尺寸(通过参数传进来)匹配最合适的预览尺寸
mPreviewSize = getOptimalSize(map.getOutputSizes(SurfaceTexture.class), width, height);
mCameraId = id;
}
} catch (CameraAccessException e) {
e.printStackTrace();
}
return mCameraId;
}
public boolean openCamera() {
CameraManager cameraManager = (CameraManager) mActivity.getSystemService(Context.CAMERA_SERVICE);
try {
//开启相机,第一个参数指示打开哪个摄像头,第二个参数mStateCallback为相机的状态回调接口,第三个参数用来确定Callback在哪个线程执行,为null的话就在当前线程执行
cameraManager.openCamera(mCameraId, mStateCallback, mCameraHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
return false;
}
return true;
}
//当相机成功打开后会回调onOpened方法,这里可以拿到CameraDevice对象,也就是具体的摄像头设备
public CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() {
@Override
public void onOpened(@NonNull CameraDevice camera) {
mCameraDevice = camera;
}
}
现在Camera2已结打开了,下面开始初始化GLSurfaceView,伪代码如下,这个不细讲了,可以参考我之前的博客 Android初始化OpenGL ES,并且分析Renderer子线程原理
//实例化一个GLSurfaceView
mGLSurfaceView = new GLSurfaceView(this);
//配置OpenGL ES,主要是版本设置和设置Renderer,Renderer用于执行OpenGL的绘制
mGLSurfaceView.setEGLContextClientVersion(2);
mGLSurfaceView.setRenderer(new GLSurfaceView.Renderer());
//在屏幕上显示GLSurfaceView
我们在Renderer的onSurfaceCreated方法中创建一个OES纹理
public static int createOESTextureObject() {
int[] tex = new int[1];
GLES20.glGenTextures(1, tex, 0);
GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, tex[0]);
GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);
GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
GL10.GL_TEXTURE_WRAP_S, GL10.GL_CLAMP_TO_EDGE);
GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
GL10.GL_TEXTURE_WRAP_T, GL10.GL_CLAMP_TO_EDGE);
GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 0);
return tex[0];
}
之后根据OES纹理Id创建SurfaceTexture,用来接收Camera2的预览数据。
public boolean initSurfaceTexture() {
//根据OES纹理ID实例化SurfaceTexture
mSurfaceTexture = new SurfaceTexture(mOESTextureId);
//当SurfaceTexture接收到一帧数据时,请求OpenGL ES进行渲染
mSurfaceTexture.setOnFrameAvailableListener(new SurfaceTexture.OnFrameAvailableListener() {
@Override
public void onFrameAvailable(SurfaceTexture surfaceTexture) {
mCameraV2GLSurfaceView.requestRender();
}
});
return true;
}
最后初始化OpenGL ES环境,主要有shader编写和编译,链接到program。这一套流程在第一篇文章中Android Camera使用OpenGL ES 2.0和GLSurfaceView对预览进行实时处理(黑白滤镜)讲过了,这里就不讲了。
public void startPreview() {
//设置SurfaceTexture的默认尺寸
mSurfaceTexture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
//根据mSurfaceTexture创建Surface
Surface surface = new Surface(mSurfaceTexture);
try {
//创建preview捕获请求
mCaptureRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
//将此请求输出目标设为我们创建的Surface对象,这个Surface对象也必须添加给createCaptureSession才行
mCaptureRequestBuilder.addTarget(surface);
//创建捕获会话,第一个参数是捕获数据的输出Surface列表,第二个参数是CameraCaptureSession的状态回调接口,当它创建好后会回调onConfigured方法,第三个参数用来确定Callback在哪个线程执行,为null的话就在当前线程执行
mCameraDevice.createCaptureSession(Arrays.asList(surface), new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(@NonNull CameraCaptureSession session) {
try {
//创建捕获请求
mCaptureRequest = mCaptureRequestBuilder.build();
mCameraCaptureSession = session;
//设置重复捕获数据的请求,之后surface绑定的SurfaceTexture中就会一直有数据到达,然后就会回调SurfaceTexture.OnFrameAvailableListener接口
mCameraCaptureSession.setRepeatingRequest(mCaptureRequest, null, mCameraHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
@Override
public void onConfigureFailed(@NonNull CameraCaptureSession session) {
}
}, mCameraHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
在Renderer的onDrawFrame方法中,首先更新纹理数据
if (mSurfaceTexture != null) {
//更新数据,其实也是消耗数据,将上一帧的数据处理或者抛弃掉,要不然SurfaceTexture是接收不到最新数据
mSurfaceTexture.updateTexImage();
mSurfaceTexture.getTransformMatrix(transformMatrix);
}
然后OpenGL ES将此OES纹理数据绘制到屏幕上,这根前两篇讲的一样,就不复述了。
至此运行此程序Camera预览就会显示为黑白滤镜,如下图所示。
总结一下:首先使用Camera2 API开启相机,然后添加GLSurfaceView作为布局界面,之后创建一个外部纹理,根据此纹理ID创建一个SurfaceTexture。开启预览,Camera2将预览数据输出至此SurfaceTexture上。有数据到达时,会回调SurfaceTexture的OnFrameAvailableListener接口,在此接口中requestRender,通过OpenGL ES将此OES纹理绘制到屏幕上。
代码地址(顺手给个Star啊):点击查看源码
作者:lb377463323
出处:http://blog.csdn.net/lb377463323
原文链接:http://blog.csdn.net/lb377463323/article/details/78054892
转载请注明出处!