《Android 美顏類相機(jī)開發(fā)匯總》第一章 Android OpenGLES 相機(jī)預(yù)覽

SurfaceView + OpenGLES 預(yù)覽相機(jī)

使用OpenGLES 預(yù)覽相機(jī),我們可以通過GLSurfaceView 來預(yù)覽相機(jī)。GLSurfaceView封裝了EGLContext。關(guān)于GLSurfaceView的源碼里面,GLThread作為單獨(dú)的線程處理OpenGL的繪制操作,但是這里有個(gè)問題,我們可以看看GLThread里面的循環(huán):

while (true) {
    synchronized (sGLThreadManager) {
        while (true) {
            ...   // 處理是否需要刷新
            sGLThreadManager.wait();
        }
    } // end of synchronized(sGLThreadManager)
    if (event != null) {
        event.run();
        event = null;
        continue;
    }
    ...
}

內(nèi)循環(huán)是用來判斷是否需要走繪制循環(huán)。當(dāng)使用RENDERMODE_WHEN_DIRTY而非RENDERMODE_CONTINUOUSLY時(shí),如果我們不主動(dòng)調(diào)用requestRender繪制的話,它會(huì)一直在內(nèi)部等待。然后另外一點(diǎn)就是,當(dāng)我們調(diào)用queueEvent方法過多的時(shí)候,會(huì)導(dǎo)致event事件過多,然后需要不斷地循環(huán)處理event事件,最終并沒有走到刷新畫面的流程。也就是說,為了保證得到更高的fps,我們需要解決這個(gè)問題。還有另外一個(gè)問題就是GLSurfaceView 中的EGL環(huán)境有可能會(huì)丟失重建的情況,對(duì)后續(xù)利用SharedContext做錄制處理有影響。
因此,我沒有使用GLSurfaceView來做繪制操作,用另外一個(gè)Looper線程單獨(dú)處理OpenGLES 的紋理資源加載、渲染等操作。放棄使用GLSurfaceView 的另外一大原因是,為了利用SharedContext實(shí)現(xiàn)無丟幀錄制視頻的功能,GLSurfaceView 有可能會(huì)在中途釋放并重新創(chuàng)建EGLContext,導(dǎo)致SharedContext失效,錄制失敗的情況。關(guān)于這個(gè)的話,可以參考grafika,里面有issue討論過這個(gè)問題。
關(guān)于SurfaceView + OpenGLES 預(yù)覽相機(jī),可以參考本人的文章:
Android Camera SurfaceView OpenGLES 預(yù)覽
這篇文章是很久之前寫的,現(xiàn)在CainCamera開源項(xiàng)目已經(jīng)發(fā)生了比較大的改變。這里還是重新介紹一遍吧。不過這次應(yīng)該是最后一次大改動(dòng)了,相機(jī)部分的功能基本已經(jīng)完成,只剩一些小功能沒有實(shí)現(xiàn)而已,而且暫時(shí)也不會(huì)再更新相機(jī)部分的功能了。

  • 渲染線程 —— HandlerThread
    通過HandlerThread 創(chuàng)建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;       // 是否預(yù)覽狀態(tài)
    private boolean isRecording = false;        // 是否錄制狀態(tài)
    private boolean isRecordingPause = false;   // 是否處于暫停錄制狀態(tài)

    // EGL共享上下文
    private EglCore mEglCore;
    // 預(yù)覽用的EGLSurface
    private WindowSurface mDisplaySurface;

    private int mInputTexture;
    private int mCurrentTexture;
    private SurfaceTexture mSurfaceTexture;

    // 矩陣
    private final float[] mMatrix = new float[16];

    // 預(yù)覽回調(diào)
    private byte[] mPreviewBuffer;
    // 輸入圖像大小
    private int mTextureWidth, mTextureHeight;

    // 可用幀
    private int mFrameNum = 0;

    // 渲染Handler回調(diào)
    private RenderHandler mRenderHandler;

    // 計(jì)算幀率
    private FrameRateMeter mFrameRateMeter;

    // 上下文
    private Context mContext;

    // 正在拍照
    private volatile boolean mTakingPicture;
    // 預(yù)覽參數(shù)
    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();
    }

    /**
     * 設(shè)置預(yù)覽Handler回調(diào)
     * @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);
        }
        // 計(jì)算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();
        }
    }

    /**
     * 預(yù)覽回調(diào)
     * @param data
     */
    void onPreviewCallback(byte[] data) {
        if (mCameraParam.cameraCallback != null) {
            mCameraParam.cameraCallback.onPreviewCallback(data);
        }
    }

    /**
     * Surface創(chuàng)建
     * @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);

        // 打開相機(jī)
        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);

        // 是否繪制人臉關(guān)鍵點(diǎn)
        mRenderManager.drawFacePoint(mCurrentTexture);

        // 顯示到屏幕
        mDisplaySurface.swapBuffers();

        // 執(zhí)行拍照
        if (mCameraParam.isTakePicture && !mTakingPicture) {
            synchronized (mSyncFence) {
                mTakingPicture = true;
                mRenderHandler.sendEmptyMessage(RenderHandler.MSG_TAKE_PICTURE);
            }
        }

        // 是否處于錄制狀態(tài)
        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;
        }
    }

    /**
     * 計(jì)算fps
     */
    void calculateFps() {
        // 幀率回調(diào)
        if ((mCameraParam).fpsCallback != null) {
            mFrameRateMeter.drawFrameCount();
            (mCameraParam).fpsCallback.onFpsCallback(mFrameRateMeter.getFPS());
        }
    }

    /**
     * 計(jì)算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);
        }
    }

    /**
     * 切換動(dòng)態(tài)濾鏡
     * @param color
     */
    void changeDynamicFilter(DynamicColor color) {
        synchronized (mSynOperation) {
            mRenderManager.changeDynamicFilter(color);
        }
    }

    /**
     * 切換動(dòng)態(tài)彩妝
     * @param makeup
     */
    void changeDynamicMakeup(DynamicMakeup makeup) {
        synchronized (mSynOperation) {
            mRenderManager.changeDynamicMakeup(makeup);
        }
    }

    /**
     * 切換動(dòng)態(tài)資源
     * @param color
     */
    void changeDynamicResource(DynamicColor color) {
        synchronized (mSynOperation) {
            mRenderManager.changeDynamicResource(color);
        }
    }

    /**
     * 切換動(dòng)態(tài)資源
     * @param sticker
     */
    void changeDynamicResource(DynamicSticker sticker) {
        synchronized (mSynOperation) {
            mRenderManager.changeDynamicResource(sticker);
        }
    }

    /**
     * 開始錄制
     */
    void startRecording() {
        if (mEglCore != null) {
            // 設(shè)置渲染Texture 的寬高
            HardcodeEncoder.getInstance().setTextureSize(mTextureWidth, mTextureHeight);
            // 這里將EGLContext傳遞到錄制線程共享。
            // 由于EGLContext是當(dāng)前線程手動(dòng)創(chuàng)建,也就是OpenGLES的main thread
            // 這里需要傳自己手動(dòng)創(chuàng)建的EglContext
            HardcodeEncoder.getInstance().startRecording(mContext, mEglCore.getEGLContext());
        }
        isRecording = true;
    }

    /**
     * 停止錄制
     */
    void stopRecording() {
        HardcodeEncoder.getInstance().stopRecording();
        isRecording = false;
    }

    /**
     * 請(qǐng)求刷新
     */
    public void requestRender() {
        synchronized (mSyncFrameNum) {
            if (isPreviewing) {
                ++mFrameNum;
                if (mRenderHandler != null) {
                    mRenderHandler.removeMessages(RenderHandler.MSG_RENDER);
                    mRenderHandler.sendMessage(mRenderHandler
                            .obtainMessage(RenderHandler.MSG_RENDER));
                }
            }
        }
    }


    // --------------------------------- 相機(jī)操作邏輯 ----------------------------------------------
    /**
     * 打開相機(jī)
     */
    void openCamera() {
        releaseCamera();
        CameraEngine.getInstance().openCamera(mContext);
        CameraEngine.getInstance().setPreviewSurface(mSurfaceTexture);
        calculateImageSize();
        mPreviewBuffer = new byte[mTextureWidth * mTextureHeight * 3/ 2];
        CameraEngine.getInstance().setPreviewCallbackWithBuffer(this, mPreviewBuffer);
        // 相機(jī)打開回調(diào)
        if (mCameraParam.cameraCallback != null) {
            mCameraParam.cameraCallback.onCameraOpened();
        }
    }

    /**
     * 切換相機(jī)
     */
    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();
    }

    /**
     * 開始預(yù)覽
     */
    private void startPreview() {
        CameraEngine.getInstance().startPreview();
        isPreviewing = true;
    }

    /**
     * 釋放相機(jī)
     */
    private void releaseCamera() {
        isPreviewing = false;
        CameraEngine.getInstance().releaseCamera();
    }
}

相機(jī)的操作也放在該線程。這樣相機(jī)打開關(guān)閉操作、渲染前處理等操作都不會(huì)影響到UI的響應(yīng)了。CameraEngine則是相機(jī)引擎單例,用于控制相機(jī)操作的。RenderManager 則是渲染操作的單例,如果你不想用SurfaceView,也可以單獨(dú)將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>();

    // 坐標(biāo)緩沖
    private ScaleType mScaleType = ScaleType.CENTER_CROP;
    private FloatBuffer mVertexBuffer;
    private FloatBuffer mTextureBuffer;
    // 用于顯示裁剪的紋理頂點(diǎn)緩沖
    private FloatBuffer mDisplayVertexBuffer;
    private FloatBuffer mDisplayTextureBuffer;

    // 視圖寬高
    private int mViewWidth, mViewHeight;
    // 輸入圖像大小
    private int mTextureWidth, mTextureHeight;

    // 相機(jī)參數(shù)
    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();
    }

    /**
     * 釋放緩沖區(qū)
     */
    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;
        }
    }

    /**
     * 初始化緩沖區(qū)
     */
    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();
        // 相機(jī)輸入濾鏡
        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));
        // 人臉關(guān)鍵點(diǎn)調(diào)試
        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);
        }
    }

    /**
     * 切換動(dòng)態(tài)濾鏡
     * @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);
    }

    /**
     * 切換動(dòng)態(tài)濾鏡
     * @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);
        }
    }

    /**
     * 切換動(dòng)態(tài)資源
     * @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);
    }

    /**
     * 切換動(dòng)態(tài)資源
     * @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);
        // 設(shè)置輸入輸入大小,初始化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);
        // 如果處于對(duì)比狀態(tài),不做處理
        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);
            }
        }

        // 顯示輸出,需要調(diào)整視口大小
        mFilterArrays.get(RenderIndex.DisplayIndex).drawFrame(currentTexture, mDisplayVertexBuffer, mDisplayTextureBuffer);

        return currentTexture;
    }

    /**
     * 繪制調(diào)試用的人臉關(guān)鍵點(diǎn)
     * @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);
            }
        }
    }

    /**
     * 設(shè)置輸入紋理大小
     * @param width
     * @param height
     */
    public void setTextureSize(int width, int height) {
        mTextureWidth = width;
        mTextureHeight = height;
    }

    /**
     * 設(shè)置紋理顯示大小
     * @param width
     * @param height
     */
    public void setDisplaySize(int width, int height) {
        mViewWidth = width;
        mViewHeight = height;
        adjustCoordinateSize();
        onFilterChanged();
    }

    /**
     * 調(diào)整濾鏡
     */
    private void onFilterChanged() {
        for (int i = 0; i < mFilterArrays.size(); i++) {
            if (mFilterArrays.get(i) != null) {
                mFilterArrays.get(i).onInputSizeChanged(mTextureWidth, mTextureHeight);
                // 到顯示之前都需要?jiǎng)?chuàng)建FBO,這里限定是防止創(chuàng)建多余的FBO,節(jié)省GPU資源
                if (i < RenderIndex.DisplayIndex) {
                    mFilterArrays.get(i).initFrameBuffer(mTextureWidth, mTextureHeight);
                }
                mFilterArrays.get(i).onDisplaySizeChanged(mViewWidth, mViewHeight);
            }
        }
    }

    /**
     * 調(diào)整由于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);
    }

    /**
     * 計(jì)算距離
     * @param coordinate
     * @param distance
     * @return
     */
    private float addDistance(float coordinate, float distance) {
        return coordinate == 0.0f ? distance : 1 - distance;
    }
}

這里由于渲染層數(shù)是有限并且是固定的,因此使用SparseArray來存儲(chǔ)渲染的濾鏡列表,這比用Hashmap的效率要高一點(diǎn)。其中CameraParam是存儲(chǔ)相機(jī)參數(shù)的單例,UI層可以通過改變CameraParam的數(shù)據(jù)調(diào)節(jié)濾鏡渲染的流程。

  • 預(yù)覽渲染器 —— PreviewRenderer
    為了方便使用,我們將RenderThread封裝到預(yù)覽渲染器單例中:
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;
    }

    // 相機(jī)渲染參數(shù)
    private CameraParam mCameraParam;

    // 渲染Handler
    private RenderHandler mRenderHandler;
    // 渲染線程
    private RenderThread mPreviewRenderThread;
    // 操作鎖
    private final Object mSynOperation = new Object();

    private WeakReference<SurfaceView> mWeakSurfaceView;

    /**
     * 設(shè)置相機(jī)回調(diào)
     * @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回調(diào)
     */
    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大小發(fā)生變化
     * @param width
     * @param height
     */
    public void surfaceSizeChanged(int width, int height) {
        if (mRenderHandler != null) {
            mRenderHandler.sendMessage(mRenderHandler
                    .obtainMessage(RenderHandler.MSG_SURFACE_CHANGED, width, height));
        }
    }

    /**
     * 請(qǐng)求渲染
     */
    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));
        }
    }

    /**
     * 切換動(dòng)態(tài)資源
     * @param color
     */
    public void changeDynamicResource(DynamicColor color) {
        if (mRenderHandler == null) {
            return;
        }
        synchronized (mSynOperation) {
            mRenderHandler.sendMessage(mRenderHandler
                    .obtainMessage(RenderHandler.MSG_CHANGE_DYNAMIC_RESOURCE, color));
        }
    }

    /**
     * 切換動(dòng)態(tài)資源
     * @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;
            }
        }
    }

    /**
     * 切換相機(jī)
     */
    public void switchCamera() {
        if (mRenderHandler == null) {
            return;
        }
        synchronized (mSynOperation) {
            mRenderHandler.sendEmptyMessage(RenderHandler.MSG_SWITCH_CAMERA);
        }
    }

    /**
     * 重新打開相機(jī)
     */
    public void reopenCamera() {
        if (mRenderHandler == null) {
            return;
        }
        synchronized (mSynOperation) {
            mRenderHandler.sendEmptyMessage(RenderHandler.MSG_REOPEN_CAMERA);
        }
    }

    /**
     * 是否需要進(jìn)行對(duì)比
     * @param enable
     */
    public void enableCompare(boolean enable) {
        synchronized (mSynOperation) {
            mCameraParam.showCompare = enable;
        }
    }
}

這樣,我們就可以在預(yù)覽頁面里面,通過PreviewRenderer 來控制OpenGLES 渲染參數(shù)、相機(jī)操作邏輯、以及切換濾鏡、貼紙等操作邏輯了。

  • 預(yù)覽頁面Builder封裝
    為了方便進(jìn)入預(yù)覽頁面時(shí),傳入預(yù)覽的寬高比、是否顯示調(diào)試關(guān)鍵點(diǎn)等參數(shù),我們通過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);
    }

    /**
     * 是否顯示人臉關(guān)鍵點(diǎn)
     * @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;
    }

    /**
     * 期望預(yù)覽幀率
     * @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;
    }

    /**
     * 對(duì)焦權(quán)重
     * @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;
    }

    /**
     * 錄制時(shí)間
     * @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;
    }

    /**
     * 延時(shí)拍攝
     * @param takeDelay
     * @return
     */
    public PreviewBuilder takeDelay(boolean takeDelay) {
        mCameraParam.takeDelay = takeDelay;
        return this;
    }

    /**
     * 是否開啟夜光補(bǔ)償
     * @param luminousEnhancement
     * @return
     */
    public PreviewBuilder luminousEnhancement(boolean luminousEnhancement) {
        mCameraParam.luminousEnhancement = luminousEnhancement;
        return this;
    }

    /**
     * 設(shè)置拍照監(jiān)聽器
     * @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;
    }

    /**
     * 打開預(yù)覽
     * @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);
        }
    }

    /**
     * 打開預(yù)覽
     */
    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);
        }
    }
}

然后我們暴露一個(gè)預(yù)覽引擎對(duì)象:

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

    /**
     * 設(shè)置長寬比
     * @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();
    }
}
  • 打開預(yù)覽頁面的調(diào)用方式
    這樣,我們就可以在使用的時(shí)候打開預(yù)覽頁面時(shí)傳入不同的預(yù)覽參數(shù)配置,使用如下所示:
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();

預(yù)覽整體流程,這里就介紹完了。
詳細(xì)實(shí)現(xiàn)過程請(qǐng)參照本人的項(xiàng)目:
CainCamera

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容