美文网首页Android多媒体技术Android音视频系列音视频
《Android 美颜类相机开发汇总》第一章 Android O

《Android 美颜类相机开发汇总》第一章 Android O

作者: cain_huang | 来源:发表于2018-11-19 00:42 被阅读392次

    SurfaceView + OpenGLES 预览相机

    使用OpenGLES 预览相机,我们可以通过GLSurfaceView 来预览相机。GLSurfaceView封装了EGLContext。但是GLSurfaceView 是在UI线程所在的Looper线程里面创建的EGLContext。也就是说,我们如果在GLSurfaceView里面加载资源,其实还是在Main Looper 里面处理的。对于美颜类相机来说,由于很多资源需要加载以及很多关键点等的计算,如果所有的操作都在Main Looper 里面处理,由于MessageQueue通常是线性处理任务的,Looper线程不断查询消息队列中的任务,逐个取出来处理。这会导致UI的操作一直要等待一些关键点计算、纹理资源加载操作处理完再响应,这里很可能会造成比较UI无响应、预览画面卡顿等问题。因此,我们这里放弃使用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 enableEdgeBlur
         */
        void changeEdgeBlurFilter(boolean enableEdgeBlur) {
            synchronized (mSynOperation) {
                mRenderManager.changeEdgeBlurFilter(enableEdgeBlur);
            }
        }
    
        /**
         * 切换动态滤镜
         * @param color
         */
        void changeDynamicFilter(DynamicColor color) {
            synchronized (mSynOperation) {
                mRenderManager.changeDynamicFilter(color);
            }
        }
    
        /**
         * 切换动态彩妆
         * @param makeup
         */
        void changeDynamicMakeup(DynamicMakeup makeup) {
            synchronized (mSynOperation) {
                mRenderManager.changeDynamicMakeup(makeup);
            }
        }
    
        /**
         * 切换动态资源
         * @param color
         */
        void changeDynamicResource(DynamicColor color) {
            synchronized (mSynOperation) {
                mRenderManager.changeDynamicResource(color);
            }
        }
    
        /**
         * 切换动态资源
         * @param sticker
         */
        void changeDynamicResource(DynamicSticker sticker) {
            synchronized (mSynOperation) {
                mRenderManager.changeDynamicResource(sticker);
            }
        }
    
        /**
         * 开始录制
         */
        void startRecording() {
            if (mEglCore != null) {
                // 设置渲染Texture 的宽高
                HardcodeEncoder.getInstance().setTextureSize(mTextureWidth, mTextureHeight);
                // 这里将EGLContext传递到录制线程共享。
                // 由于EGLContext是当前线程手动创建,也就是OpenGLES的main thread
                // 这里需要传自己手动创建的EglContext
                HardcodeEncoder.getInstance().startRecording(mContext, mEglCore.getEGLContext());
            }
            isRecording = true;
        }
    
        /**
         * 停止录制
         */
        void stopRecording() {
            HardcodeEncoder.getInstance().stopRecording();
            isRecording = false;
        }
    
        /**
         * 请求刷新
         */
        public void requestRender() {
            synchronized (mSyncFrameNum) {
                if (isPreviewing) {
                    ++mFrameNum;
                    if (mRenderHandler != null) {
                        mRenderHandler.removeMessages(RenderHandler.MSG_RENDER);
                        mRenderHandler.sendMessage(mRenderHandler
                                .obtainMessage(RenderHandler.MSG_RENDER));
                    }
                }
            }
        }
    
    
        // --------------------------------- 相机操作逻辑 ----------------------------------------------
        /**
         * 打开相机
         */
        void openCamera() {
            releaseCamera();
            CameraEngine.getInstance().openCamera(mContext);
            CameraEngine.getInstance().setPreviewSurface(mSurfaceTexture);
            calculateImageSize();
            mPreviewBuffer = new byte[mTextureWidth * mTextureHeight * 3/ 2];
            CameraEngine.getInstance().setPreviewCallbackWithBuffer(this, mPreviewBuffer);
            // 相机打开回调
            if (mCameraParam.cameraCallback != null) {
                mCameraParam.cameraCallback.onCameraOpened();
            }
        }
    
        /**
         * 切换相机
         */
        void switchCamera() {
            mCameraParam.backCamera = !mCameraParam.backCamera;
            if (mCameraParam.backCamera) {
                mCameraParam.cameraId = Camera.CameraInfo.CAMERA_FACING_BACK;
            } else {
                mCameraParam.cameraId = Camera.CameraInfo.CAMERA_FACING_FRONT;
            }
            openCamera();
            startPreview();
        }
    
        /**
         * 开始预览
         */
        private void startPreview() {
            CameraEngine.getInstance().startPreview();
            isPreviewing = true;
        }
    
        /**
         * 释放相机
         */
        private void releaseCamera() {
            isPreviewing = false;
            CameraEngine.getInstance().releaseCamera();
        }
    }
    

    相机的操作也放在该线程。这样相机打开关闭操作、渲染前处理等操作都不会影响到UI的响应了。CameraEngine则是相机引擎单例,用于控制相机操作的。RenderManager 则是渲染操作的单例,如果你不想用SurfaceView,也可以单独将RenderManager提取出来,放到GLSurfaceView中。

    • 渲染管理器 —— RenderManager
      RenderManager 的代码如下:
    public final class RenderManager {
    
        private static class RenderManagerHolder {
            public static RenderManager instance = new RenderManager();
        }
    
        private RenderManager() {
            mCameraParam = CameraParam.getInstance();
        }
    
        public static RenderManager getInstance() {
            return RenderManagerHolder.instance;
        }
    
        // 滤镜列表
        private SparseArray<GLImageFilter> mFilterArrays = new SparseArray<GLImageFilter>();
    
        // 坐标缓冲
        private ScaleType mScaleType = ScaleType.CENTER_CROP;
        private FloatBuffer mVertexBuffer;
        private FloatBuffer mTextureBuffer;
        // 用于显示裁剪的纹理顶点缓冲
        private FloatBuffer mDisplayVertexBuffer;
        private FloatBuffer mDisplayTextureBuffer;
    
        // 视图宽高
        private int mViewWidth, mViewHeight;
        // 输入图像大小
        private int mTextureWidth, mTextureHeight;
    
        // 相机参数
        private CameraParam mCameraParam;
        // 上下文
        private Context mContext;
    
        /**
         * 初始化
         */
        public void init(Context context) {
            initBuffers();
            initFilters(context);
            mContext = context;
        }
    
        /**
         * 释放资源
         */
        public void release() {
            releaseBuffers();
            releaseFilters();
            mContext = null;
        }
    
        /**
         * 释放滤镜
         */
        private void releaseFilters() {
            for (int i = 0; i < mFilterArrays.size(); i++) {
                if (mFilterArrays.get(i) != null) {
                    mFilterArrays.get(i).release();
                }
            }
            mFilterArrays.clear();
        }
    
        /**
         * 释放缓冲区
         */
        private void releaseBuffers() {
            if (mVertexBuffer != null) {
                mVertexBuffer.clear();
                mVertexBuffer = null;
            }
            if (mTextureBuffer != null) {
                mTextureBuffer.clear();
                mTextureBuffer = null;
            }
            if (mDisplayVertexBuffer != null) {
                mDisplayVertexBuffer.clear();
                mDisplayVertexBuffer = null;
            }
            if (mDisplayTextureBuffer != null) {
                mDisplayTextureBuffer.clear();
                mDisplayTextureBuffer = null;
            }
        }
    
        /**
         * 初始化缓冲区
         */
        private void initBuffers() {
            releaseBuffers();
            mDisplayVertexBuffer = OpenGLUtils.createFloatBuffer(TextureRotationUtils.CubeVertices);
            mDisplayTextureBuffer = OpenGLUtils.createFloatBuffer(TextureRotationUtils.TextureVertices);
            mVertexBuffer = OpenGLUtils.createFloatBuffer(TextureRotationUtils.CubeVertices);
            mTextureBuffer = OpenGLUtils.createFloatBuffer(TextureRotationUtils.TextureVertices);
        }
    
        /**
         * 初始化滤镜
         * @param context
         */
        private void initFilters(Context context) {
            releaseFilters();
            // 相机输入滤镜
            mFilterArrays.put(RenderIndex.CameraIndex, new GLImageOESInputFilter(context));
            // 美颜滤镜
            mFilterArrays.put(RenderIndex.BeautyIndex, new GLImageBeautyFilter(context));
            // 彩妆滤镜
            mFilterArrays.put(RenderIndex.MakeupIndex, new GLImageMakeupFilter(context, null));
            // 美型滤镜
            mFilterArrays.put(RenderIndex.FaceAdjustIndex, new GLImageFaceReshapeFilter(context));
            // LUT/颜色滤镜
            mFilterArrays.put(RenderIndex.FilterIndex, null);
            // 贴纸资源滤镜
            mFilterArrays.put(RenderIndex.ResourceIndex, null);
            // 景深滤镜
            mFilterArrays.put(RenderIndex.DepthBlurIndex, new GLImageDepthBlurFilter(context));
            // 暗角滤镜
            mFilterArrays.put(RenderIndex.VignetteIndex, new GLImageVignetteFilter(context));
            // 显示输出
            mFilterArrays.put(RenderIndex.DisplayIndex, new GLImageFilter(context));
            // 人脸关键点调试
            mFilterArrays.put(RenderIndex.FacePointIndex, new GLImageFacePointsFilter(context));
        }
    
        /**
         * 是否切换边框模糊
         * @param enableEdgeBlur
         */
        public synchronized void changeEdgeBlurFilter(boolean enableEdgeBlur) {
            if (enableEdgeBlur) {
                mFilterArrays.get(RenderIndex.DisplayIndex).release();
                GLImageFrameEdgeBlurFilter filter = new GLImageFrameEdgeBlurFilter(mContext);
                filter.onInputSizeChanged(mTextureWidth, mTextureHeight);
                filter.onDisplaySizeChanged(mViewWidth, mViewHeight);
                mFilterArrays.put(RenderIndex.DisplayIndex, filter);
            } else {
                mFilterArrays.get(RenderIndex.DisplayIndex).release();
                GLImageFilter filter = new GLImageFilter(mContext);
                filter.onInputSizeChanged(mTextureWidth, mTextureHeight);
                filter.onDisplaySizeChanged(mViewWidth, mViewHeight);
                mFilterArrays.put(RenderIndex.DisplayIndex, filter);
            }
        }
    
        /**
         * 切换动态滤镜
         * @param color
         */
        public synchronized void changeDynamicFilter(DynamicColor color) {
            if (mFilterArrays.get(RenderIndex.FilterIndex) != null) {
                mFilterArrays.get(RenderIndex.FilterIndex).release();
                mFilterArrays.put(RenderIndex.FilterIndex, null);
            }
            if (color == null) {
                return;
            }
            GLImageDynamicColorFilter filter = new GLImageDynamicColorFilter(mContext, color);
            filter.onInputSizeChanged(mTextureWidth, mTextureHeight);
            filter.initFrameBuffer(mTextureWidth, mTextureHeight);
            filter.onDisplaySizeChanged(mViewWidth, mViewHeight);
            mFilterArrays.put(RenderIndex.FilterIndex, filter);
        }
    
        /**
         * 切换动态滤镜
         * @param dynamicMakeup
         */
        public synchronized void changeDynamicMakeup(DynamicMakeup dynamicMakeup) {
            if (mFilterArrays.get(RenderIndex.MakeupIndex) != null) {
                ((GLImageMakeupFilter)mFilterArrays.get(RenderIndex.MakeupIndex)).changeMakeupData(dynamicMakeup);
            } else {
                GLImageMakeupFilter filter = new GLImageMakeupFilter(mContext, dynamicMakeup);
                filter.onInputSizeChanged(mTextureWidth, mTextureHeight);
                filter.initFrameBuffer(mTextureWidth, mTextureHeight);
                filter.onDisplaySizeChanged(mViewWidth, mViewHeight);
                mFilterArrays.put(RenderIndex.MakeupIndex, filter);
            }
        }
    
        /**
         * 切换动态资源
         * @param color
         */
        public synchronized void changeDynamicResource(DynamicColor color) {
            if (mFilterArrays.get(RenderIndex.ResourceIndex) != null) {
                mFilterArrays.get(RenderIndex.ResourceIndex).release();
                mFilterArrays.put(RenderIndex.ResourceIndex, null);
            }
            if (color == null) {
                return;
            }
            GLImageDynamicColorFilter filter = new GLImageDynamicColorFilter(mContext, color);
            filter.onInputSizeChanged(mTextureWidth, mTextureHeight);
            filter.initFrameBuffer(mTextureWidth, mTextureHeight);
            filter.onDisplaySizeChanged(mViewWidth, mViewHeight);
            mFilterArrays.put(RenderIndex.ResourceIndex, filter);
        }
    
        /**
         * 切换动态资源
         * @param sticker
         */
        public synchronized void changeDynamicResource(DynamicSticker sticker) {
            // 释放旧滤镜
            if (mFilterArrays.get(RenderIndex.ResourceIndex) != null) {
                mFilterArrays.get(RenderIndex.ResourceIndex).release();
                mFilterArrays.put(RenderIndex.ResourceIndex, null);
            }
            if (sticker == null) {
                return;
            }
            GLImageDynamicStickerFilter filter = new GLImageDynamicStickerFilter(mContext, sticker);
            // 设置输入输入大小,初始化fbo等
            filter.onInputSizeChanged(mTextureWidth, mTextureHeight);
            filter.initFrameBuffer(mTextureWidth, mTextureHeight);
            filter.onDisplaySizeChanged(mViewWidth, mViewHeight);
            mFilterArrays.put(RenderIndex.ResourceIndex, filter);
        }
    
        /**
         * 绘制纹理
         * @param inputTexture
         * @param mMatrix
         * @return
         */
        public int drawFrame(int inputTexture, float[] mMatrix) {
            int currentTexture = inputTexture;
            if (mFilterArrays.get(RenderIndex.CameraIndex) == null
                    || mFilterArrays.get(RenderIndex.DisplayIndex) == null) {
                return currentTexture;
            }
            if (mFilterArrays.get(RenderIndex.CameraIndex) instanceof GLImageOESInputFilter) {
                ((GLImageOESInputFilter)mFilterArrays.get(RenderIndex.CameraIndex)).setTextureTransformMatrix(mMatrix);
            }
            currentTexture = mFilterArrays.get(RenderIndex.CameraIndex)
                    .drawFrameBuffer(currentTexture, mVertexBuffer, mTextureBuffer);
            // 如果处于对比状态,不做处理
            if (!mCameraParam.showCompare) {
                // 美颜滤镜
                if (mFilterArrays.get(RenderIndex.BeautyIndex) != null) {
                    if (mFilterArrays.get(RenderIndex.BeautyIndex) instanceof IBeautify
                            && mCameraParam.beauty != null) {
                        ((IBeautify) mFilterArrays.get(RenderIndex.BeautyIndex)).onBeauty(mCameraParam.beauty);
                    }
                    currentTexture = mFilterArrays.get(RenderIndex.BeautyIndex).drawFrameBuffer(currentTexture, mVertexBuffer, mTextureBuffer);
                }
    
                // 彩妆滤镜
                if (mFilterArrays.get(RenderIndex.MakeupIndex) != null) {
                    currentTexture = mFilterArrays.get(RenderIndex.MakeupIndex).drawFrameBuffer(currentTexture, mVertexBuffer, mTextureBuffer);
                }
    
                // 美型滤镜
                if (mFilterArrays.get(RenderIndex.FaceAdjustIndex) != null) {
                    if (mFilterArrays.get(RenderIndex.FaceAdjustIndex) instanceof IBeautify) {
                        ((IBeautify) mFilterArrays.get(RenderIndex.FaceAdjustIndex)).onBeauty(mCameraParam.beauty);
                    }
                    currentTexture = mFilterArrays.get(RenderIndex.FaceAdjustIndex).drawFrameBuffer(currentTexture, mVertexBuffer, mTextureBuffer);
                }
    
                // 绘制颜色滤镜
                if (mFilterArrays.get(RenderIndex.FilterIndex) != null) {
                    currentTexture = mFilterArrays.get(RenderIndex.FilterIndex).drawFrameBuffer(currentTexture, mVertexBuffer, mTextureBuffer);
                }
    
                // 资源滤镜,可以是贴纸、滤镜甚至是彩妆类型
                if (mFilterArrays.get(RenderIndex.ResourceIndex) != null) {
                    currentTexture = mFilterArrays.get(RenderIndex.ResourceIndex).drawFrameBuffer(currentTexture, mVertexBuffer, mTextureBuffer);
                }
    
                // 景深
                if (mFilterArrays.get(RenderIndex.DepthBlurIndex) != null) {
                    mFilterArrays.get(RenderIndex.DepthBlurIndex).setFilterEnable(mCameraParam.enableDepthBlur);
                    currentTexture = mFilterArrays.get(RenderIndex.DepthBlurIndex).drawFrameBuffer(currentTexture, mVertexBuffer, mTextureBuffer);
                }
    
                // 暗角
                if (mFilterArrays.get(RenderIndex.VignetteIndex) != null) {
                    mFilterArrays.get(RenderIndex.VignetteIndex).setFilterEnable(mCameraParam.enableVignette);
                    currentTexture = mFilterArrays.get(RenderIndex.VignetteIndex).drawFrameBuffer(currentTexture, mVertexBuffer, mTextureBuffer);
                }
            }
    
            // 显示输出,需要调整视口大小
            mFilterArrays.get(RenderIndex.DisplayIndex).drawFrame(currentTexture, mDisplayVertexBuffer, mDisplayTextureBuffer);
    
            return currentTexture;
        }
    
        /**
         * 绘制调试用的人脸关键点
         * @param mCurrentTexture
         */
        public void drawFacePoint(int mCurrentTexture) {
            if (mFilterArrays.get(RenderIndex.FacePointIndex) != null) {
                if (mCameraParam.drawFacePoints && LandmarkEngine.getInstance().hasFace()) {
                    mFilterArrays.get(RenderIndex.FacePointIndex).drawFrame(mCurrentTexture, mDisplayVertexBuffer, mDisplayTextureBuffer);
                }
            }
        }
    
        /**
         * 设置输入纹理大小
         * @param width
         * @param height
         */
        public void setTextureSize(int width, int height) {
            mTextureWidth = width;
            mTextureHeight = height;
        }
    
        /**
         * 设置纹理显示大小
         * @param width
         * @param height
         */
        public void setDisplaySize(int width, int height) {
            mViewWidth = width;
            mViewHeight = height;
            adjustCoordinateSize();
            onFilterChanged();
        }
    
        /**
         * 调整滤镜
         */
        private void onFilterChanged() {
            for (int i = 0; i < mFilterArrays.size(); i++) {
                if (mFilterArrays.get(i) != null) {
                    mFilterArrays.get(i).onInputSizeChanged(mTextureWidth, mTextureHeight);
                    // 到显示之前都需要创建FBO,这里限定是防止创建多余的FBO,节省GPU资源
                    if (i < RenderIndex.DisplayIndex) {
                        mFilterArrays.get(i).initFrameBuffer(mTextureWidth, mTextureHeight);
                    }
                    mFilterArrays.get(i).onDisplaySizeChanged(mViewWidth, mViewHeight);
                }
            }
        }
    
        /**
         * 调整由于surface的大小与SurfaceView大小不一致带来的显示问题
         */
        private void adjustCoordinateSize() {
            float[] textureCoord = null;
            float[] vertexCoord = null;
            float[] textureVertices = TextureRotationUtils.TextureVertices;
            float[] vertexVertices = TextureRotationUtils.CubeVertices;
            float ratioMax = Math.max((float) mViewWidth / mTextureWidth,
                    (float) mViewHeight / mTextureHeight);
            // 新的宽高
            int imageWidth = Math.round(mTextureWidth * ratioMax);
            int imageHeight = Math.round(mTextureHeight * ratioMax);
            // 获取视图跟texture的宽高比
            float ratioWidth = (float) imageWidth / (float) mViewWidth;
            float ratioHeight = (float) imageHeight / (float) mViewHeight;
            if (mScaleType == ScaleType.CENTER_INSIDE) {
                vertexCoord = new float[] {
                        vertexVertices[0] / ratioHeight, vertexVertices[1] / ratioWidth, vertexVertices[2],
                        vertexVertices[3] / ratioHeight, vertexVertices[4] / ratioWidth, vertexVertices[5],
                        vertexVertices[6] / ratioHeight, vertexVertices[7] / ratioWidth, vertexVertices[8],
                        vertexVertices[9] / ratioHeight, vertexVertices[10] / ratioWidth, vertexVertices[11],
                };
            } else if (mScaleType == ScaleType.CENTER_CROP) {
                float distHorizontal = (1 - 1 / ratioWidth) / 2;
                float distVertical = (1 - 1 / ratioHeight) / 2;
                textureCoord = new float[] {
                        addDistance(textureVertices[0], distVertical), addDistance(textureVertices[1], distHorizontal),
                        addDistance(textureVertices[2], distVertical), addDistance(textureVertices[3], distHorizontal),
                        addDistance(textureVertices[4], distVertical), addDistance(textureVertices[5], distHorizontal),
                        addDistance(textureVertices[6], distVertical), addDistance(textureVertices[7], distHorizontal),
                };
            }
            if (vertexCoord == null) {
                vertexCoord = vertexVertices;
            }
            if (textureCoord == null) {
                textureCoord = textureVertices;
            }
            // 更新VertexBuffer 和 TextureBuffer
            mDisplayVertexBuffer.clear();
            mDisplayVertexBuffer.put(vertexCoord).position(0);
            mDisplayTextureBuffer.clear();
            mDisplayTextureBuffer.put(textureCoord).position(0);
        }
    
        /**
         * 计算距离
         * @param coordinate
         * @param distance
         * @return
         */
        private float addDistance(float coordinate, float distance) {
            return coordinate == 0.0f ? distance : 1 - distance;
        }
    }
    

    这里由于渲染层数是有限并且是固定的,因此使用SparseArray来存储渲染的滤镜列表,这比用Hashmap的效率要高一点。其中CameraParam是存储相机参数的单例,UI层可以通过改变CameraParam的数据调节滤镜渲染的流程。

    • 预览渲染器 —— PreviewRenderer
      为了方便使用,我们将RenderThread封装到预览渲染器单例中:
    public final class PreviewRenderer {
    
        private PreviewRenderer() {
            mCameraParam = CameraParam.getInstance();
        }
    
        private static class RenderHolder {
            private static PreviewRenderer instance = new PreviewRenderer();
        }
    
        public static PreviewRenderer getInstance() {
            return RenderHolder.instance;
        }
    
        // 相机渲染参数
        private CameraParam mCameraParam;
    
        // 渲染Handler
        private RenderHandler mRenderHandler;
        // 渲染线程
        private RenderThread mPreviewRenderThread;
        // 操作锁
        private final Object mSynOperation = new Object();
    
        private WeakReference<SurfaceView> mWeakSurfaceView;
    
        /**
         * 设置相机回调
         * @param callback
         * @return
         */
        public RenderBuilder setCameraCallback(OnCameraCallback callback) {
            return new RenderBuilder(this, callback);
        }
    
        /**
         * 初始化渲染器
         */
        void initRenderer(Context context) {
            synchronized (mSynOperation) {
                mPreviewRenderThread = new RenderThread(context, "RenderThread");
                mPreviewRenderThread.start();
                mRenderHandler = new RenderHandler(mPreviewRenderThread);
                // 绑定Handler
                mPreviewRenderThread.setRenderHandler(mRenderHandler);
            }
        }
    
        /**
         * 销毁渲染器
         */
        public void destroyRenderer() {
            synchronized (mSynOperation) {
                if (mWeakSurfaceView != null) {
                    mWeakSurfaceView.clear();
                    mWeakSurfaceView = null;
                }
                if (mRenderHandler != null) {
                    mRenderHandler.removeCallbacksAndMessages(null);
                    mRenderHandler = null;
                }
                if (mPreviewRenderThread != null) {
                    mPreviewRenderThread.quitSafely();
                    try {
                        mPreviewRenderThread.join();
                    } catch (InterruptedException e) {
    
                    }
                    mPreviewRenderThread = null;
                }
            }
        }
    
        /**
         * 绑定需要渲染的SurfaceView
         * @param surfaceView
         */
        public void setSurfaceView(SurfaceView surfaceView) {
            mWeakSurfaceView = new WeakReference<>(surfaceView);
            surfaceView.getHolder().addCallback(mSurfaceCallback);
        }
    
        /**
         * Surface回调
         */
        private SurfaceHolder.Callback mSurfaceCallback = new SurfaceHolder.Callback() {
            @Override
            public void surfaceCreated(SurfaceHolder holder) {
                if (mRenderHandler != null) {
                    mRenderHandler.sendMessage(mRenderHandler
                            .obtainMessage(RenderHandler.MSG_SURFACE_CREATED, holder));
                }
            }
    
            @Override
            public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
                surfaceSizeChanged(width, height);
            }
    
            @Override
            public void surfaceDestroyed(SurfaceHolder holder) {
                if (mRenderHandler != null) {
                    mRenderHandler.sendMessage(mRenderHandler
                            .obtainMessage(RenderHandler.MSG_SURFACE_DESTROYED));
                }
            }
        };
    
        /**
         * Surface大小发生变化
         * @param width
         * @param height
         */
        public void surfaceSizeChanged(int width, int height) {
            if (mRenderHandler != null) {
                mRenderHandler.sendMessage(mRenderHandler
                        .obtainMessage(RenderHandler.MSG_SURFACE_CHANGED, width, height));
            }
        }
    
        /**
         * 请求渲染
         */
        public void requestRender() {
            if (mPreviewRenderThread != null) {
                mPreviewRenderThread.requestRender();
            }
        }
    
        /**
         * 切换边框模糊功能
         * @param enableEdgeBlur
         */
        public void changeEdgeBlurFilter(boolean enableEdgeBlur) {
            if (mRenderHandler == null) {
                return;
            }
            synchronized (mSynOperation) {
                mRenderHandler.sendMessage(mRenderHandler
                        .obtainMessage(RenderHandler.MSG_CHANGE_EDGE_BLUR, enableEdgeBlur));
            }
        }
    
        /**
         * 切换滤镜
         * @param color
         */
        public void changeDynamicFilter(DynamicColor color) {
            if (mRenderHandler == null) {
                return;
            }
            synchronized (mSynOperation) {
                mRenderHandler.sendMessage(mRenderHandler
                        .obtainMessage(RenderHandler.MSG_CHANGE_DYNAMIC_COLOR, color));
            }
        }
    
        /**
         * 切换彩妆
         * @param makeup
         */
        public void changeDynamicMakeup(DynamicMakeup makeup) {
            if (mRenderHandler == null) {
                return;
            }
            synchronized (mSynOperation) {
                mRenderHandler.sendMessage(mRenderHandler
                        .obtainMessage(RenderHandler.MSG_CHANGE_DYNAMIC_MAKEUP, makeup));
            }
        }
    
        /**
         * 切换动态资源
         * @param color
         */
        public void changeDynamicResource(DynamicColor color) {
            if (mRenderHandler == null) {
                return;
            }
            synchronized (mSynOperation) {
                mRenderHandler.sendMessage(mRenderHandler
                        .obtainMessage(RenderHandler.MSG_CHANGE_DYNAMIC_RESOURCE, color));
            }
        }
    
        /**
         * 切换动态资源
         * @param sticker
         */
        public void changeDynamicResource(DynamicSticker sticker) {
            if (mRenderHandler == null) {
                return;
            }
            synchronized (mSynOperation) {
                mRenderHandler.sendMessage(mRenderHandler
                        .obtainMessage(RenderHandler.MSG_CHANGE_DYNAMIC_RESOURCE, sticker));
            }
        }
    
        /**
         * 开始录制
         */
        public void startRecording() {
            if (mRenderHandler == null) {
                return;
            }
            synchronized (mSynOperation) {
                mRenderHandler.sendMessage(mRenderHandler
                        .obtainMessage(RenderHandler.MSG_START_RECORDING));
            }
        }
    
        /**
         * 停止录制
         */
        public void stopRecording() {
            if (mRenderHandler == null) {
                return;
            }
            synchronized (mSynOperation) {
                mRenderHandler.sendEmptyMessage(RenderHandler.MSG_STOP_RECORDING);
            }
        }
    
        /**
         * 拍照
         */
        public void takePicture() {
            synchronized (mSynOperation) {
                if (!mCameraParam.isTakePicture) {
                    mCameraParam.isTakePicture = true;
                }
            }
        }
    
        /**
         * 切换相机
         */
        public void switchCamera() {
            if (mRenderHandler == null) {
                return;
            }
            synchronized (mSynOperation) {
                mRenderHandler.sendEmptyMessage(RenderHandler.MSG_SWITCH_CAMERA);
            }
        }
    
        /**
         * 重新打开相机
         */
        public void reopenCamera() {
            if (mRenderHandler == null) {
                return;
            }
            synchronized (mSynOperation) {
                mRenderHandler.sendEmptyMessage(RenderHandler.MSG_REOPEN_CAMERA);
            }
        }
    
        /**
         * 是否需要进行对比
         * @param enable
         */
        public void enableCompare(boolean enable) {
            synchronized (mSynOperation) {
                mCameraParam.showCompare = enable;
            }
        }
    }
    

    这样,我们就可以在预览页面里面,通过PreviewRenderer 来控制OpenGLES 渲染参数、相机操作逻辑、以及切换滤镜、贴纸等操作逻辑了。

    • 预览页面Builder封装
      为了方便进入预览页面时,传入预览的宽高比、是否显示调试关键点等参数,我们通过Builder 模式来控制:
    public final class PreviewBuilder {
    
        private PreviewEngine mPreviewEngine;
        private CameraParam mCameraParam;
    
        public PreviewBuilder(PreviewEngine engine, AspectRatio ratio) {
            mPreviewEngine = engine;
            mCameraParam = CameraParam.getInstance();
            mCameraParam.setAspectRatio(ratio);
        }
    
        /**
         * 是否显示人脸关键点
         * @param show
         * @return
         */
        public PreviewBuilder showFacePoints(boolean show) {
            mCameraParam.drawFacePoints = show;
            return this;
        }
    
        /**
         * 是否显示fps
         * @param show
         * @return
         */
        public PreviewBuilder showFps(boolean show) {
            mCameraParam.showFps = show;
            return this;
        }
    
        /**
         * 期望预览帧率
         * @param fps
         * @return
         */
        public PreviewBuilder expectFps(int fps) {
            mCameraParam.expectFps = fps;
            return this;
        }
    
        /**
         * 期望宽度
         * @param width
         * @return
         */
        public PreviewBuilder expectWidth(int width) {
            mCameraParam.expectWidth = width;
            return this;
        }
    
        /**
         * 期望高度
         * @param height
         * @return
         */
        public PreviewBuilder expectHeight(int height) {
            mCameraParam.expectHeight = height;
            return this;
        }
    
        /**
         * 是否高清拍照
         * @param highDefinition
         * @return
         */
        public PreviewBuilder highDefinition(boolean highDefinition) {
            mCameraParam.highDefinition = highDefinition;
            return this;
        }
    
        /**
         * 是否打开后置摄像头
         * @param backCamera
         * @return
         */
        public PreviewBuilder backCamera(boolean backCamera) {
            mCameraParam.backCamera = backCamera;
            if (mCameraParam.backCamera) {
                mCameraParam.cameraId = Camera.CameraInfo.CAMERA_FACING_BACK;
            }
            return this;
        }
    
        /**
         * 对焦权重
         * @param weight
         * @return
         */
        public PreviewBuilder focusWeight(int weight) {
            mCameraParam.setFocusWeight(weight);
            return this;
        }
    
        /**
         * 是否允许录制
         * @param recordable
         * @return
         */
        public PreviewBuilder recordable(boolean recordable) {
            mCameraParam.recordable = recordable;
            return this;
        }
    
        /**
         * 录制时间
         * @param recordTime
         * @return
         */
        public PreviewBuilder recordTime(int recordTime) {
            mCameraParam.recordTime = recordTime;
            return this;
        }
    
        /**
         * 是否录制音频
         * @param recordAudio
         * @return
         */
        public PreviewBuilder recordAudio(boolean recordAudio) {
            mCameraParam.recordAudio = recordAudio;
            return this;
        }
    
        /**
         * 延时拍摄
         * @param takeDelay
         * @return
         */
        public PreviewBuilder takeDelay(boolean takeDelay) {
            mCameraParam.takeDelay = takeDelay;
            return this;
        }
    
        /**
         * 是否开启夜光补偿
         * @param luminousEnhancement
         * @return
         */
        public PreviewBuilder luminousEnhancement(boolean luminousEnhancement) {
            mCameraParam.luminousEnhancement = luminousEnhancement;
            return this;
        }
    
        /**
         * 设置拍照监听器
         * @param listener
         * @return
         */
        public PreviewBuilder setPreviewCaptureListener(OnPreviewCaptureListener listener) {
            mCameraParam.captureListener = listener;
            return this;
        }
    
        /**
         *
         * @param listener
         * @return
         */
        public PreviewBuilder setGalleryListener(OnGallerySelectedListener listener) {
            mCameraParam.gallerySelectedListener = listener;
            return this;
        }
    
        /**
         * 打开预览
         * @param requestCode
         */
        public void startPreviewForResult(int requestCode) {
            Activity activity = mPreviewEngine.getActivity();
            if (activity == null) {
                return;
            }
            Intent intent = new Intent(activity, CameraActivity.class);
            Fragment fragment = mPreviewEngine.getFragment();
            if (fragment != null) {
                fragment.startActivityForResult(intent, requestCode);
            } else {
                activity.startActivityForResult(intent, requestCode);
            }
        }
    
        /**
         * 打开预览
         */
        public void startPreview() {
            Activity activity = mPreviewEngine.getActivity();
            if (activity == null) {
                return;
            }
            Intent intent = new Intent(activity, CameraActivity.class);
            Fragment fragment = mPreviewEngine.getFragment();
            if (fragment != null) {
                fragment.startActivity(intent);
            } else {
                activity.startActivity(intent);
            }
        }
    }
    

    然后我们暴露一个预览引擎对象:

    public final class PreviewEngine {
    
        private WeakReference<Activity> mWeakActivity;
        private WeakReference<Fragment> mWeakFragment;
    
        private PreviewEngine(Activity activity) {
            this(activity, null);
        }
    
        private PreviewEngine(Fragment fragment) {
            this(fragment.getActivity(), fragment);
        }
    
        private PreviewEngine(Activity activity, Fragment fragment) {
            mWeakActivity = new WeakReference<>(activity);
            mWeakFragment = new WeakReference<>(fragment);
        }
    
        public static PreviewEngine from(Activity activity) {
            return new PreviewEngine(activity);
        }
    
        public static PreviewEngine from(Fragment fragment) {
            return new PreviewEngine(fragment);
        }
    
        /**
         * 设置长宽比
         * @param ratio
         * @return
         */
        public PreviewBuilder setCameraRatio(AspectRatio ratio) {
            return new PreviewBuilder(this, ratio);
        }
    
        public Activity getActivity() {
            return mWeakActivity.get();
        }
    
        public Fragment getFragment() {
            return mWeakFragment.get();
        }
    }
    
    • 打开预览页面的调用方式
      这样,我们就可以在使用的时候打开预览页面时传入不同的预览参数配置,使用如下所示:
    PreviewEngine.from(this)
                    .setCameraRatio(AspectRatio.Ratio_16_9)
                    .showFacePoints(false)
                    .showFps(true)
                    .setGalleryListener(new OnGallerySelectedListener() {
                        @Override
                        public void onGalleryClickListener(GalleryType type) {
                            scanMedia(type == GalleryType.ALL);
                        }
                    })
                    .setPreviewCaptureListener(new OnPreviewCaptureListener() {
                        @Override
                        public void onMediaSelectedListener(String path, GalleryType type) {
                            if (type == GalleryType.PICTURE) {
                                Intent intent = new Intent(MainActivity.this, ImageEditActivity.class);
                                intent.putExtra(ImageEditActivity.PATH, path);
                                startActivity(intent);
                            } else if (type == GalleryType.VIDEO) {
                                Intent intent = new Intent(MainActivity.this, VideoEditActivity.class);
                                intent.putExtra(VideoEditActivity.PATH, path);
                                startActivity(intent);
                            }
                        }
                    })
                    .startPreview();
    

    预览整体流程,这里就介绍完了。
    详细实现过程请参照本人的项目:
    CainCamera

    相关文章

      网友评论

      • 叫我大王1992:还要个问题就是拍照后图片保存不成功,视频也是,请问下原因是什么
        叫我大王1992:@cain_huang 谢谢大佬,图片保存这边发现一个问题 华为的nova3 出现保存图片全黑的现象 ,换朋友的小米6 正常
        cain_huang:我已经提交了图片保存的代码。视频部分,本身已经合成mp4了,在Android/data/com.cgfay.caincamera/cache 目录下。由于短视频编辑合成功能还没做完,所以我还没把视频合成输出到DCIM目录下。这个你自己改一下就好。
        cain_huang:@叫我大王1992 图片我没加保存功能啊,图片编辑功能我还没做完呢。目前在搞短视频编辑功能,你可以用glReadPixels将图像数据从GPU中读出来然后保存起来,可以看我Github上那个没关闭的issue,里面有贴了代码。
      • 叫我大王1992:您好,最近在学习这块,刚还看到你的项目,有个问题,图库选择图片后到编辑页面,为什么图片会黑屏或者花屏出现?
        cain_huang:@叫我大王1992 这个问题实际上我没遇到,不知道具体设备和实际情况。不过黑屏、花屏的话,基本是加载数据源有问题,比如像素对齐,绘制只做了一半,或者是某些设备的GPU存在问题,这个你可以用SnapDragon Profiler调试看看
      • iLovT:您好,最近遇到了个问题,我使用活体识别,需要开启双摄像头,但是始终开启失败,请问怎么实现两个摄像头都打开,红外摄像头作为数据源,可见光摄像头作为预览呢,期待您的回复!
        cain_huang:@iLovT 这个需要底层支持的。厂商ISP不支持的话,应用层是无法做到的。

      本文标题:《Android 美颜类相机开发汇总》第一章 Android O

      本文链接:https://www.haomeiwen.com/subject/ztvyvftx.html