Android Camera API 2使用OpenGL ES 2.0和GLSurfaceView对预览进行实时二次处理(黑白滤镜)

这段时间有点忙,一直没时间写第三篇教程,其实代码很早之前就写好了。本系列教程会有三篇文章讲解Android平台滤镜的实现方式,希望在阅读本文之前先阅读前面两篇文档。

下面进入正题:第三篇

本文使用GLSurfaceView作为布局界面,并且仍将使用SurfaceTexture来获取预览图像,但是Camera V2的API跟Camera V1的差别还挺大的,所以需要熟悉一下,可以参考我之前的博客Android Camera2教程之打开相机、开启预览、实现PreviewCallback、拍照。这里我也再讲一下,下面开始讲解。

1, 使用Camera API 2开启相机

  • 在onCreate方法中创建并启动Camera子线程,后面Camera开启、预览、拍照都放在这个子线程中。
    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;
        }
    }

2,添加GLSurfaceView作为布局界面、创建SurfaceTexture

现在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对预览进行实时处理(黑白滤镜)讲过了,这里就不讲了。

3, 使用Camera2 API开启预览

    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();
        }
    }

4,绘制预览数据到屏幕上

在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
转载请注明出处!

猜你喜欢

转载自blog.csdn.net/lb377463323/article/details/78054892