327 lines
11 KiB
Java
Raw Normal View History

2022-09-17 16:54:58 +08:00
package com.yunbao.faceunity.utils;
import android.content.Context;
import android.hardware.Camera;
import com.faceunity.core.callback.OperateCallback;
import com.faceunity.core.entity.FURenderInputData;
import com.faceunity.core.entity.FURenderOutputData;
import com.faceunity.core.enumeration.CameraFacingEnum;
import com.faceunity.core.enumeration.FUAIProcessorEnum;
import com.faceunity.core.enumeration.FUAITypeEnum;
import com.faceunity.core.faceunity.FURenderConfig;
import com.faceunity.core.faceunity.FURenderKit;
import com.faceunity.core.faceunity.FURenderManager;
import com.faceunity.core.utils.CameraUtils;
import com.faceunity.core.utils.FULogger;
import com.yunbao.faceunity.listener.FURendererListener;
import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
/**
* DESC
* Created on 2021/4/26
*/
public class FURenderer extends IFURenderer {
private static final String TAG = "FURenderer";
public volatile static FURenderer INSTANCE;
public static FURenderer getInstance() {
if (INSTANCE == null) {
synchronized (FURenderer.class) {
if (INSTANCE == null) {
INSTANCE = new FURenderer();
INSTANCE.mFURenderKit = FURenderKit.getInstance();
}
}
}
return INSTANCE;
}
/**
* 状态回调监听
*/
private FURendererListener mFURendererListener;
/* 特效FURenderKit*/
private FURenderKit mFURenderKit;
/* AI道具*/
private String BUNDLE_AI_FACE = "model" + File.separator + "ai_face_processor.bundle";
private String BUNDLE_AI_HUMAN = "model" + File.separator + "ai_human_processor.bundle";
/* GL 线程 ID */
private Long mGlThreadId = 0L;
/* 任务队列 */
private ArrayList<Runnable> mEventQueue = new ArrayList<>(16);
private final Object queueLock = new Object();
/* 渲染开关标识 */
private volatile boolean mRendererSwitch = false;
/* 清除队列标识 */
private volatile boolean mClearQueue = false;
/* 相机角度-朝向映射 */
private HashMap<Integer, CameraFacingEnum> cameraOrientationMap = new HashMap<>();
/*检测类型*/
private FUAIProcessorEnum aIProcess = FUAIProcessorEnum.FACE_PROCESSOR;
/*检测标识*/
private int aIProcessTrackStatus = -1;
public String getVersion() {
return mFURenderKit.getVersion();
}
/**
* 初始化鉴权
*
* @param context
*/
@Override
public void setup(Context context) {
2022-09-22 15:25:48 +08:00
FURenderManager.setKitDebug(FULogger.LogLevel.OFF);
FURenderManager.setCoreDebug(FULogger.LogLevel.OFF);
2022-09-17 16:54:58 +08:00
FURenderManager.registerFURender(context, Authpack.A(), new OperateCallback() {
@Override
public void onSuccess(int i, @NotNull String s) {
if (i == FURenderConfig.OPERATE_SUCCESS_AUTH) {
mFURenderKit.getFUAIController().loadAIProcessor(BUNDLE_AI_FACE, FUAITypeEnum.FUAITYPE_FACEPROCESSOR);
mFURenderKit.getFUAIController().loadAIProcessor(BUNDLE_AI_HUMAN, FUAITypeEnum.FUAITYPE_HUMAN_PROCESSOR);
mFURenderKit.setReadBackSync(true);
int cameraFrontOrientation = CameraUtils.INSTANCE.getCameraOrientation(Camera.CameraInfo.CAMERA_FACING_FRONT);
int cameraBackOrientation = CameraUtils.INSTANCE.getCameraOrientation(Camera.CameraInfo.CAMERA_FACING_BACK);
cameraOrientationMap.put(cameraFrontOrientation, CameraFacingEnum.CAMERA_FRONT);
cameraOrientationMap.put(cameraBackOrientation, CameraFacingEnum.CAMERA_BACK);
}
}
@Override
public void onFail(int i, @NotNull String s) {
}
});
}
/**
* 开启合成状态
*/
@Override
public void prepareRenderer(FURendererListener mFURendererListener) {
this.mFURendererListener = mFURendererListener;
mRendererSwitch = true;
mClearQueue = false;
queueEvent(() -> mGlThreadId = Thread.currentThread().getId());
mFURendererListener.onPrepare();
}
/**
* 双输入接口输入 buffer texture必须在具有 GL 环境的线程调用
* 由于省去数据拷贝性能相对最优优先推荐使用
* 缺点是无法保证 buffer 和纹理对齐可能出现点位和效果对不上的情况
*
* @param img NV21 buffer
* @param texId 纹理 ID
* @param width
* @param height
* @return
*/
@Override
public int onDrawFrameDualInput(byte[] img, int texId, int width, int height) {
prepareDrawFrame();
if (!mRendererSwitch) {
return texId;
}
FURenderInputData inputData = new FURenderInputData(width, height);
if (img != null) {
inputData.setImageBuffer(new FURenderInputData.FUImageBuffer(inputBufferType, img));
}
if (texId != -1) {
inputData.setTexture(new FURenderInputData.FUTexture(inputTextureType, texId));
}
FURenderInputData.FURenderConfig config = inputData.getRenderConfig();
config.setExternalInputType(externalInputType);
config.setInputOrientation(inputOrientation);
config.setDeviceOrientation(deviceOrientation);
config.setInputBufferMatrix(inputBufferMatrix);
config.setInputTextureMatrix(inputTextureMatrix);
config.setOutputMatrix(outputMatrix);
config.setCameraFacing(cameraFacing);
FURenderOutputData outputData = mFURenderKit.renderWithInput(inputData);
if (outputData.getTexture() != null && outputData.getTexture().getTexId() > 0) {
return outputData.getTexture().getTexId();
}
return texId;
}
public FURenderOutputData onDrawFrameInputWithReturn(byte[] img, int width, int height) {
prepareDrawFrame();
if (!mRendererSwitch) {
return null ;
}
FURenderInputData inputData = new FURenderInputData(width, height);
if (img != null) {
inputData.setImageBuffer(new FURenderInputData.FUImageBuffer(inputBufferType, img));
}
FURenderInputData.FURenderConfig config = inputData.getRenderConfig();
config.setExternalInputType(externalInputType);
config.setInputOrientation(inputOrientation);
config.setDeviceOrientation(deviceOrientation);
config.setInputBufferMatrix(inputBufferMatrix);
config.setInputTextureMatrix(inputTextureMatrix);
config.setCameraFacing(cameraFacing);
config.setNeedBufferReturn(true);
config.setOutputMatrix(outputMatrix);
return mFURenderKit.renderWithInput(inputData);
}
/**
* 类似 GLSurfaceView queueEvent 机制把任务抛到 GL 线程执行
*
* @param runnable
*/
@Override
public void queueEvent(Runnable runnable) {
if (runnable == null) {
return;
}
if (mGlThreadId == Thread.currentThread().getId()) {
runnable.run();
} else {
synchronized (queueLock) {
mEventQueue.add(runnable);
}
}
}
/**
* 释放资源
*/
@Override
public void release() {
mRendererSwitch = false;
mClearQueue = true;
mGlThreadId = 0L;
synchronized (queueLock) {
mEventQueue.clear();
mClearQueue = false;
mFURenderKit.release();
aIProcessTrackStatus = -1;
if (mFURendererListener != null) {
mFURendererListener.onRelease();
mFURendererListener = null;
}
}
}
/**
* 渲染前置执行
*
* @return
*/
private void prepareDrawFrame() {
benchmarkFPS();
// 执行任务队列中的任务
synchronized (queueLock) {
while (!mEventQueue.isEmpty() && !mClearQueue) {
mEventQueue.remove(0).run();
}
}
// AI检测
trackStatus();
}
/**
* 设置检测类型
*
* @param type
*/
@Override
public void setAIProcessTrackType(FUAIProcessorEnum type) {
aIProcess = type;
aIProcessTrackStatus = -1;
}
/**
* 设置FPS检测
*
* @param enable
*/
@Override
public void setMarkFPSEnable(boolean enable) {
mIsRunBenchmark = enable;
}
@Override
public void setInputOrientation(int inputOrientation) {
if (cameraOrientationMap.containsKey(inputOrientation)) {
setCameraFacing(cameraOrientationMap.get(inputOrientation));
}
super.setInputOrientation(inputOrientation);
}
/**
* AI识别数目检测
*/
private void trackStatus() {
int trackCount;
if (aIProcess == FUAIProcessorEnum.HAND_GESTURE_PROCESSOR) {
trackCount = mFURenderKit.getFUAIController().handProcessorGetNumResults();
} else if (aIProcess == FUAIProcessorEnum.HUMAN_PROCESSOR) {
trackCount = mFURenderKit.getFUAIController().humanProcessorGetNumResults();
} else {
trackCount = mFURenderKit.getFUAIController().isTracking();
}
if (trackCount != aIProcessTrackStatus) {
aIProcessTrackStatus = trackCount;
} else {
return;
}
if (mFURendererListener != null) {
mFURendererListener.onTrackStatusChanged(aIProcess, trackCount);
}
}
//endregion AI识别
//------------------------------FPS 渲染时长回调相关定义------------------------------------
private static final int NANO_IN_ONE_MILLI_SECOND = 1_000_000;
private static final int NANO_IN_ONE_SECOND = 1_000_000_000;
private static final int FRAME_COUNT = 20;
private boolean mIsRunBenchmark = false;
private int mCurrentFrameCount;
private long mLastFrameTimestamp;
private long mSumCallTime;
private long mCallStartTime;
private void benchmarkFPS() {
if (!mIsRunBenchmark) {
return;
}
if (++mCurrentFrameCount == FRAME_COUNT) {
long tmp = System.nanoTime();
double fps = (double) NANO_IN_ONE_SECOND / ((double) (tmp - mLastFrameTimestamp) / FRAME_COUNT);
double renderTime = (double) mSumCallTime / FRAME_COUNT / NANO_IN_ONE_MILLI_SECOND;
mLastFrameTimestamp = tmp;
mSumCallTime = 0;
mCurrentFrameCount = 0;
if (mFURendererListener != null) {
mFURendererListener.onFpsChanged(fps, renderTime);
}
}
}
}