SurfaceView + OpenGLES 預覽相機
使用OpenGLES 預覽相機廊敌,我們可以通過GLSurfaceView 來預覽相機。GLSurfaceView封裝了EGLContext剧罩。關于GLSurfaceView的源碼里面茬贵,GLThread作為單獨的線程處理OpenGL的繪制操作槐沼,但是這里有個問題阶冈,我們可以看看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)。當使用RENDERMODE_WHEN_DIRTY而非RENDERMODE_CONTINUOUSLY時葫慎,如果我們不主動調(diào)用requestRender繪制的話衔彻,它會一直在內(nèi)部等待。然后另外一點就是偷办,當我們調(diào)用queueEvent方法過多的時候艰额,會導致event事件過多,然后需要不斷地循環(huán)處理event事件椒涯,最終并沒有走到刷新畫面的流程柄沮。也就是說,為了保證得到更高的fps废岂,我們需要解決這個問題铡溪。還有另外一個問題就是GLSurfaceView 中的EGL環(huán)境有可能會丟失重建的情況,對后續(xù)利用SharedContext做錄制處理有影響泪喊。
因此棕硫,我沒有使用GLSurfaceView來做繪制操作,用另外一個Looper線程單獨處理OpenGLES 的紋理資源加載袒啼、渲染等操作哈扮。放棄使用GLSurfaceView 的另外一大原因是,為了利用SharedContext實現(xiàn)無丟幀錄制視頻的功能蚓再,GLSurfaceView 有可能會在中途釋放并重新創(chuàng)建EGLContext滑肉,導致SharedContext失效,錄制失敗的情況摘仅。關于這個的話靶庙,可以參考grafika,里面有issue討論過這個問題娃属。
關于SurfaceView + OpenGLES 預覽相機六荒,可以參考本人的文章:
Android Camera SurfaceView OpenGLES 預覽
這篇文章是很久之前寫的护姆,現(xiàn)在CainCamera開源項目已經(jīng)發(fā)生了比較大的改變。這里還是重新介紹一遍吧掏击。不過這次應該是最后一次大改動了卵皂,相機部分的功能基本已經(jīng)完成,只剩一些小功能沒有實現(xiàn)而已砚亭,而且暫時也不會再更新相機部分的功能了灯变。
- 渲染線程 —— 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; // 是否預覽狀態(tài)
private boolean isRecording = false; // 是否錄制狀態(tài)
private boolean isRecordingPause = false; // 是否處于暫停錄制狀態(tài)
// EGL共享上下文
private EglCore mEglCore;
// 預覽用的EGLSurface
private WindowSurface mDisplaySurface;
private int mInputTexture;
private int mCurrentTexture;
private SurfaceTexture mSurfaceTexture;
// 矩陣
private final float[] mMatrix = new float[16];
// 預覽回調(diào)
private byte[] mPreviewBuffer;
// 輸入圖像大小
private int mTextureWidth, mTextureHeight;
// 可用幀
private int mFrameNum = 0;
// 渲染Handler回調(diào)
private RenderHandler mRenderHandler;
// 計算幀率
private FrameRateMeter mFrameRateMeter;
// 上下文
private Context mContext;
// 正在拍照
private volatile boolean mTakingPicture;
// 預覽參數(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();
}
/**
* 設置預覽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);
}
// 計算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();
}
}
/**
* 預覽回調(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);
// 打開相機
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();
// 執(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;
}
}
/**
* 計算fps
*/
void calculateFps() {
// 幀率回調(diào)
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);
}
}
/**
* 切換動態(tài)濾鏡
* @param color
*/
void changeDynamicFilter(DynamicColor color) {
synchronized (mSynOperation) {
mRenderManager.changeDynamicFilter(color);
}
}
/**
* 切換動態(tài)彩妝
* @param makeup
*/
void changeDynamicMakeup(DynamicMakeup makeup) {
synchronized (mSynOperation) {
mRenderManager.changeDynamicMakeup(makeup);
}
}
/**
* 切換動態(tài)資源
* @param color
*/
void changeDynamicResource(DynamicColor color) {
synchronized (mSynOperation) {
mRenderManager.changeDynamicResource(color);
}
}
/**
* 切換動態(tài)資源
* @param sticker
*/
void changeDynamicResource(DynamicSticker sticker) {
synchronized (mSynOperation) {
mRenderManager.changeDynamicResource(sticker);
}
}
/**
* 開始錄制
*/
void startRecording() {
if (mEglCore != null) {
// 設置渲染Texture 的寬高
HardcodeEncoder.getInstance().setTextureSize(mTextureWidth, mTextureHeight);
// 這里將EGLContext傳遞到錄制線程共享添祸。
// 由于EGLContext是當前線程手動創(chuàng)建,也就是OpenGLES的main thread
// 這里需要傳自己手動創(chuàng)建的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);
// 相機打開回調(diào)
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;
// 相機參數(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();
// 相機輸入濾鏡
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));
// 人臉關鍵點調(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);
}
}
/**
* 切換動態(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);
}
/**
* 切換動態(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);
}
}
/**
* 切換動態(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);
}
/**
* 切換動態(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);
// 設置輸入輸入大小混坞,初始化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);
// 如果處于對比狀態(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)試用的人臉關鍵點
* @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();
}
/**
* 調(diào)整濾鏡
*/
private void onFilterChanged() {
for (int i = 0; i < mFilterArrays.size(); i++) {
if (mFilterArrays.get(i) != null) {
mFilterArrays.get(i).onInputSizeChanged(mTextureWidth, mTextureHeight);
// 到顯示之前都需要創(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);
}
/**
* 計算距離
* @param coordinate
* @param distance
* @return
*/
private float addDistance(float coordinate, float distance) {
return coordinate == 0.0f ? distance : 1 - distance;
}
}
這里由于渲染層數(shù)是有限并且是固定的镶殷,因此使用SparseArray來存儲渲染的濾鏡列表,這比用Hashmap的效率要高一點微酬。其中CameraParam是存儲相機參數(shù)的單例绘趋,UI層可以通過改變CameraParam的數(shù)據(jù)調(diào)節(jié)濾鏡渲染的流程。
- 預覽渲染器 —— 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;
}
// 相機渲染參數(shù)
private CameraParam mCameraParam;
// 渲染Handler
private RenderHandler mRenderHandler;
// 渲染線程
private RenderThread mPreviewRenderThread;
// 操作鎖
private final Object mSynOperation = new Object();
private WeakReference<SurfaceView> mWeakSurfaceView;
/**
* 設置相機回調(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));
}
}
/**
* 請求渲染
*/
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));
}
}
/**
* 切換動態(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));
}
}
/**
* 切換動態(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;
}
}
}
/**
* 切換相機
*/
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 渲染參數(shù)垦江、相機操作邏輯帽馋、以及切換濾鏡、貼紙等操作邏輯了。
- 預覽頁面Builder封裝
為了方便進入預覽頁面時绽族,傳入預覽的寬高比姨涡、是否顯示調(diào)試關鍵點等參數(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);
}
/**
* 是否顯示人臉關鍵點
* @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;
}
/**
* 設置拍照監(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;
}
/**
* 打開預覽
* @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();
}
}
- 打開預覽頁面的調(diào)用方式
這樣项秉,我們就可以在使用的時候打開預覽頁面時傳入不同的預覽參數(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();
預覽整體流程慷彤,這里就介紹完了娄蔼。
詳細實現(xiàn)過程請參照本人的項目:
CainCamera