From d29319da510e38edcd9d09ccd10466543cbe29e9 Mon Sep 17 00:00:00 2001 From: benyq <30992974+benyq@users.noreply.github.com> Date: 2021年12月30日 09:40:18 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E5=8D=87=E7=BA=A7=20v8.0.2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../framework/PreprocessorFaceUnity.java | 58 +- .../java/io/agora/profile/CPUInfoUtil.java | 172 ++++++ .../main/java/io/agora/profile/CSVUtils.java | 186 ++++++ .../main/java/io/agora/profile/Constant.java | 49 ++ .../main/java/io/agora/profile/FPSUtil.java | 63 ++ .../java/io/agora/profile/MemoryInfoUtil.java | 89 +++ .../io/agora/rtcwithfu/MyApplication.java | 4 + .../rtcwithfu/RtcEngineEventHandlerProxy.java | 56 ++ .../rtcwithfu/activities/FUChatActivity.java | 55 +- .../rtcwithfu/activities/MainActivity.java | 21 +- .../faceunity/build.gradle | 9 +- .../src/main/java/com/faceunity/FUConfig.java | 8 + .../java/com/faceunity/nama/FURenderer.java | 12 +- .../nama/data/FaceBeautyDataFactory.java | 31 +- .../faceunity/nama/repo/FaceBeautySource.java | 46 +- .../faceunity/nama/utils/FuDeviceUtils.java | 583 ++++++++++++++++++ ...beauty_shape_face_short_close_selector.xml | 5 + ..._beauty_shape_face_short_open_selector.xml | 5 + ...on_beauty_box_face_short_close_checked.png | Bin 0 -> 9780 bytes ...con_beauty_box_face_short_close_normal.png | Bin 0 -> 6913 bytes ...con_beauty_box_face_short_open_checked.png | Bin 0 -> 10062 bytes ...icon_beauty_box_face_short_open_normal.png | Bin 0 -> 7187 bytes .../src/main/res/values-zh-rCN/strings.xml | 1 + .../faceunity/src/main/res/values/strings.xml | 1 + 24 files changed, 1410 insertions(+), 44 deletions(-) create mode 100644 Agora-Video-With-FaceUnity-Android/app/src/main/java/io/agora/profile/CPUInfoUtil.java create mode 100644 Agora-Video-With-FaceUnity-Android/app/src/main/java/io/agora/profile/CSVUtils.java create mode 100644 Agora-Video-With-FaceUnity-Android/app/src/main/java/io/agora/profile/Constant.java create mode 100644 Agora-Video-With-FaceUnity-Android/app/src/main/java/io/agora/profile/FPSUtil.java create mode 100644 Agora-Video-With-FaceUnity-Android/app/src/main/java/io/agora/profile/MemoryInfoUtil.java create mode 100644 Agora-Video-With-FaceUnity-Android/faceunity/src/main/java/com/faceunity/FUConfig.java create mode 100644 Agora-Video-With-FaceUnity-Android/faceunity/src/main/java/com/faceunity/nama/utils/FuDeviceUtils.java create mode 100644 Agora-Video-With-FaceUnity-Android/faceunity/src/main/res/drawable/icon_beauty_shape_face_short_close_selector.xml create mode 100644 Agora-Video-With-FaceUnity-Android/faceunity/src/main/res/drawable/icon_beauty_shape_face_short_open_selector.xml create mode 100644 Agora-Video-With-FaceUnity-Android/faceunity/src/main/res/mipmap-xxhdpi/icon_beauty_box_face_short_close_checked.png create mode 100644 Agora-Video-With-FaceUnity-Android/faceunity/src/main/res/mipmap-xxhdpi/icon_beauty_box_face_short_close_normal.png create mode 100644 Agora-Video-With-FaceUnity-Android/faceunity/src/main/res/mipmap-xxhdpi/icon_beauty_box_face_short_open_checked.png create mode 100644 Agora-Video-With-FaceUnity-Android/faceunity/src/main/res/mipmap-xxhdpi/icon_beauty_box_face_short_open_normal.png diff --git a/Agora-Video-With-FaceUnity-Android/app/src/main/java/io/agora/framework/PreprocessorFaceUnity.java b/Agora-Video-With-FaceUnity-Android/app/src/main/java/io/agora/framework/PreprocessorFaceUnity.java index 77a93d59..f0b7bfea 100644 --- a/Agora-Video-With-FaceUnity-Android/app/src/main/java/io/agora/framework/PreprocessorFaceUnity.java +++ b/Agora-Video-With-FaceUnity-Android/app/src/main/java/io/agora/framework/PreprocessorFaceUnity.java @@ -6,24 +6,34 @@ import android.os.Looper; import android.util.Log; +import com.faceunity.FUConfig; +import com.faceunity.core.faceunity.FUAIKit; +import com.faceunity.core.faceunity.FURenderKit; +import com.faceunity.core.model.facebeauty.FaceBeautyBlurTypeEnum; import com.faceunity.nama.FURenderer; +import com.faceunity.nama.utils.FuDeviceUtils; import io.agora.capture.framework.modules.channels.VideoChannel; import io.agora.capture.framework.modules.processors.IPreprocessor; import io.agora.capture.video.camera.VideoCaptureFrame; +import io.agora.profile.CSVUtils; public class PreprocessorFaceUnity implements IPreprocessor { private final static String TAG = PreprocessorFaceUnity.class.getSimpleName(); private FURenderer mFURenderer = FURenderer.getInstance(); - private Context mContext; private boolean renderSwitch; private int skipFrame = 0; private Handler mGLHandler; + public void setCSVUtils(CSVUtils cSVUtils) { + this.mCSVUtils = cSVUtils; + } + + private CSVUtils mCSVUtils; + public PreprocessorFaceUnity(Context context) { - mContext = context; } @Override @@ -40,10 +50,20 @@ public VideoCaptureFrame onPreProcessFrame(VideoCaptureFrame outFrame, VideoChan return outFrame; } mFURenderer.setInputOrientation(outFrame.rotation); + + if (FUConfig.DEVICE_LEVEL> FuDeviceUtils.DEVICE_LEVEL_MID)//高性能设备 + cheekFaceNum(); + + long start = System.nanoTime(); int texId = mFURenderer.onDrawFrameDualInput(outFrame.image, outFrame.textureId, outFrame.format.getWidth(), outFrame.format.getHeight()); + long renderTime = System.nanoTime() - start; + if (mCSVUtils != null) { + mCSVUtils.writeCsv(null, renderTime); + } + // The texture is transformed to texture2D by beauty module. if (skipFrame <= 0) { outFrame.textureId = texId; @@ -77,6 +97,7 @@ public void releasePreprocessor(VideoChannel.ChannelContext context) { private void startGLThread() { if (mGLHandler == null) { mGLHandler = new Handler(Looper.myLooper()); + mGLHandler.post(() -> {if (mSurfaceViewListener !=null ) mSurfaceViewListener.onSurfaceCreated();}); } } @@ -98,9 +119,40 @@ public void skipFrame() { public void releaseFURender() { renderSwitch = false; mGLHandler.removeCallbacksAndMessages(0); - mGLHandler.post(() -> FURenderer.getInstance().release()); + mGLHandler.post(() -> { + if (mSurfaceViewListener !=null ) mSurfaceViewListener.onSurfaceDestroyed(); + }); mGLHandler = null; } + private SurfaceViewListener mSurfaceViewListener; + public interface SurfaceViewListener{ + void onSurfaceCreated(); + void onSurfaceDestroyed(); + } + + public void setSurfaceListener(SurfaceViewListener surfaceViewListener) { + this.mSurfaceViewListener = surfaceViewListener; + } + + /** + * 检查当前人脸数量 + */ + private void cheekFaceNum() { + //根据有无人脸 + 设备性能 判断开启的磨皮类型 + float faceProcessorGetConfidenceScore = FUAIKit.getInstance().getFaceProcessorGetConfidenceScore(0); + if (faceProcessorGetConfidenceScore>= 0.95) { + //高端手机并且检测到人脸开启均匀磨皮,人脸点位质 + if (FURenderKit.getInstance() != null && FURenderKit.getInstance().getFaceBeauty() != null && FURenderKit.getInstance().getFaceBeauty().getBlurType() != FaceBeautyBlurTypeEnum.EquallySkin) { + FURenderKit.getInstance().getFaceBeauty().setBlurType(FaceBeautyBlurTypeEnum.EquallySkin); + FURenderKit.getInstance().getFaceBeauty().setEnableBlurUseMask(true); + } + } else { + if (FURenderKit.getInstance() != null && FURenderKit.getInstance().getFaceBeauty() != null && FURenderKit.getInstance().getFaceBeauty().getBlurType() != FaceBeautyBlurTypeEnum.FineSkin) { + FURenderKit.getInstance().getFaceBeauty().setBlurType(FaceBeautyBlurTypeEnum.FineSkin); + FURenderKit.getInstance().getFaceBeauty().setEnableBlurUseMask(false); + } + } + } } diff --git a/Agora-Video-With-FaceUnity-Android/app/src/main/java/io/agora/profile/CPUInfoUtil.java b/Agora-Video-With-FaceUnity-Android/app/src/main/java/io/agora/profile/CPUInfoUtil.java new file mode 100644 index 00000000..cdd578e6 --- /dev/null +++ b/Agora-Video-With-FaceUnity-Android/app/src/main/java/io/agora/profile/CPUInfoUtil.java @@ -0,0 +1,172 @@ +package io.agora.profile; + +import android.content.Context; +import android.os.Build; + +import java.io.BufferedReader; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.RandomAccessFile; + +/** + * cpu使用率获取工具类 + * Created by lirui on 2017年8月2日. + */ + +public class CPUInfoUtil { + private long lastTotalCpu = 0; + private long lastProcessCpu = 0; + + private final String PackageName; + + private volatile boolean isRunningCPU = false; + private volatile double cpuRate = 0; + + public CPUInfoUtil(Context context) { + if (Build.VERSION.SDK_INT>= 26) { + final String pn = context.getPackageName(); + if (pn.length() <= 16) { + PackageName = pn; + } else { + PackageName = pn.substring(0, 15) + "+"; + } +// Log.e(TAG, "CSVUtils PackageName " + PackageName); + isRunningCPU = true; + CPUInfoThread cpuinfothread = new CPUInfoThread(); + cpuinfothread.start(); + } else { + PackageName = null; + } + } + + public double getProcessCpuUsed() { + if (Build.VERSION.SDK_INT>= 26) { + return cpuRate; + } else { + double pcpu = 0; + double tmp = 1.0; + long nowTotalCpu = getTotalCpu(); + long nowProcessCpu = getMyProcessCpu(); + if (nowTotalCpu != 0 && (nowTotalCpu - lastTotalCpu) != 0) { +// Log.e(TAG, "cpu used nowProcessCpu " + nowProcessCpu + " lastProcessCpu " + lastProcessCpu + " nowTotalCpu " + nowTotalCpu + " lastTotalCpu " + lastTotalCpu); + pcpu = 100 * (tmp * (nowProcessCpu - lastProcessCpu) / (nowTotalCpu - lastTotalCpu)); + } + lastProcessCpu = nowProcessCpu; + lastTotalCpu = nowTotalCpu; + return pcpu < 0 ? 0 : pcpu; + } + } + + public void close() { + if (Build.VERSION.SDK_INT>= 26) { + isRunningCPU = false; + } + } + + private long getTotalCpu() { + String[] cpuInfos = null; + try { + RandomAccessFile reader = null; + reader = new RandomAccessFile("/proc/stat", "r"); + String load = reader.readLine(); + reader.close(); + cpuInfos = load.split(" "); + } catch (IOException e) { + e.printStackTrace(); + return 0; + } + long totalCpu = 0; + try { + totalCpu = Long.parseLong(cpuInfos[2]) + + Long.parseLong(cpuInfos[3]) + Long.parseLong(cpuInfos[4]) + + Long.parseLong(cpuInfos[6]) + Long.parseLong(cpuInfos[5]) + + Long.parseLong(cpuInfos[7]) + Long.parseLong(cpuInfos[8]); + } catch (ArrayIndexOutOfBoundsException e) { + e.printStackTrace(); + return 0; + } + return totalCpu; + } + + private long getMyProcessCpu() { + String[] cpuInfos = null; + try { + int pid = android.os.Process.myPid(); + BufferedReader reader = new BufferedReader(new InputStreamReader( + new FileInputStream("/proc/" + pid + "/stat")), 1000); + String load = reader.readLine(); + reader.close(); + cpuInfos = load.split(" "); + } catch (IOException e) { + e.printStackTrace(); + return 0; + } + long appCpuTime = 0; + try { + appCpuTime = Long.parseLong(cpuInfos[13]) + + Long.parseLong(cpuInfos[14]) + Long.parseLong(cpuInfos[15]) + + Long.parseLong(cpuInfos[16]); + } catch (ArrayIndexOutOfBoundsException e) { + e.printStackTrace(); + return 0; + } + return appCpuTime; + } + + class CPUInfoThread extends Thread { + + private double allCPU = 0; + + @Override + public void run() { + String line = null; + InputStream is = null; + try { + + Runtime runtime = Runtime.getRuntime(); + Process proc = runtime.exec("top -d 1"); + is = proc.getInputStream(); + + // 换成BufferedReader + BufferedReader buf = new BufferedReader(new InputStreamReader(is)); + do { + line = buf.readLine(); + if (allCPU == 0 && line.contains("user") && line.contains("nice") && line.contains("sys") && line.contains("idle") && line.contains("iow") && line.contains("irq") && line.contains("sirq") && line.contains("host")) { + if (line.indexOf("%cpu ")> 0) + allCPU = Double.parseDouble(line.split("%cpu ")[0]); + if (allCPU == 0) { + String[] s = line.split("%,"); + for (String st : s) { + String[] sts = st.split(" "); + if (sts.length> 0) + allCPU += Double.parseDouble(sts[sts.length - 1]); + } + } + } + // 读取到相应pkgName跳出循环(或者未找到) + if (line == null || line.endsWith(PackageName)) { +// Log.e(TAG, "cpu line : " + line); + String str[] = line.split(" "); + int t = 0; + for (int i = str.length - 1; i> 0; i--) { + if (!str[i].isEmpty() && ++t == 4) { +// Log.e(TAG, "cpu : " + str[i] + " allCPU " + allCPU); + cpuRate = 100 * Double.parseDouble(str[i]) / allCPU; + } + } + continue; + } + } while (isRunningCPU); + + if (is != null) { + buf.close(); + is.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + } +} diff --git a/Agora-Video-With-FaceUnity-Android/app/src/main/java/io/agora/profile/CSVUtils.java b/Agora-Video-With-FaceUnity-Android/app/src/main/java/io/agora/profile/CSVUtils.java new file mode 100644 index 00000000..d4f521a3 --- /dev/null +++ b/Agora-Video-With-FaceUnity-Android/app/src/main/java/io/agora/profile/CSVUtils.java @@ -0,0 +1,186 @@ +package io.agora.profile; + +import android.app.ActivityManager; +import android.content.Context; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Process; +import android.util.Log; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Locale; + +import io.agora.rtcwithfu.RtcEngineEventHandlerProxy; + +/** + * Created by tujh on 2017年11月2日. + */ +public class CSVUtils { + public static final String TAG = CSVUtils.class.getSimpleName(); + /* 每 100 帧统计一次 */ + public static final int FRAME_STEP = 100; + + public static final String COMMA = ","; + + private OutputStreamWriter mStreamWriter; + + private ActivityManager mActivityManager; + + private Handler mHandler; + + private CPUInfoUtil mCPUInfoUtil; + + private int mFrameRate; + private volatile double mCpuUsed; + private volatile double mAverageFps; + private volatile double mAverageRenderTime; + private volatile double mMemory; + private long mSumRenderTimeInNano; + private volatile long mTimestamp; + + private RtcEngineEventHandlerProxy mRtcEngineEventHandler; + + public void setRtcEngineEventHandler(RtcEngineEventHandlerProxy rtcEngineEventHandlerProxy) { + mRtcEngineEventHandler = rtcEngineEventHandlerProxy; + } + + public CSVUtils(Context context) { + mActivityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); + mCPUInfoUtil = new CPUInfoUtil(context); + + HandlerThread handlerThread = new HandlerThread(TAG, Process.THREAD_PRIORITY_BACKGROUND); + handlerThread.start(); + mHandler = new Handler(handlerThread.getLooper()); + } + + public void initHeader(String folderName, StringBuilder headerInfo) { + Log.d(TAG, "initHeader() called with: folderName = [" + folderName + "], headerInfo = [" + headerInfo + "]"); + StringBuilder stringBuilder = new StringBuilder().append("时间").append(COMMA) + .append("帧率").append(COMMA) + .append("渲染耗时").append(COMMA) + .append("CPU").append(COMMA) + .append("内存").append(COMMA) + .append("远端分辨率").append(COMMA) + .append("远端渲染帧率").append(COMMA) + .append("远端解码帧率").append(COMMA) + .append("远端接收码率").append(COMMA); + if (headerInfo != null) { + stringBuilder.append(headerInfo); + } + stringBuilder.append("\n"); + + File file = new File(folderName); + File parentFile = file.getParentFile(); + if (!parentFile.exists()) { + parentFile.mkdirs(); + } + try { + if (!file.exists()) { + file.createNewFile(); + } + mStreamWriter = new OutputStreamWriter(new FileOutputStream(file, false), "GBK"); + } catch (IOException e) { + Log.e(TAG, "CSVUtils: ", e); + } + flush(stringBuilder); + mTimestamp = System.currentTimeMillis(); + } + + public void writeCsv(final StringBuilder extraInfo, long renderTimeInNano) { + if (mStreamWriter == null) { + return; + } + + mSumRenderTimeInNano += renderTimeInNano; + if (mFrameRate % FRAME_STEP == FRAME_STEP - 1) { + mTimestamp = System.currentTimeMillis(); + mAverageFps = FPSUtil.fpsAVG(FRAME_STEP); + mAverageRenderTime = (double) mSumRenderTimeInNano / FRAME_STEP / 1_000_000; + mSumRenderTimeInNano = 0; + mHandler.post(new Runnable() { + @Override + public void run() { + mCpuUsed = mCPUInfoUtil.getProcessCpuUsed(); + mMemory = MemoryInfoUtil.getMemory(mActivityManager.getProcessMemoryInfo(new int[]{Process.myPid()})); + String strCPU = String.format(Locale.getDefault(), "%.2f", mCpuUsed); + String strMemory = String.format(Locale.getDefault(), "%.2f", mMemory); + + StringBuilder stringBuilder = new StringBuilder(); + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS", Locale.getDefault()); + RtcEngineEventHandlerProxy.StatsInfo statsInfo = mRtcEngineEventHandler.retrieveStatsInfo(); + stringBuilder.append(dateFormat.format(new Date(mTimestamp))).append(COMMA) + .append(String.format(Locale.getDefault(), "%.2f", mAverageFps)).append(COMMA) + .append(String.format(Locale.getDefault(), "%.2f", mAverageRenderTime)).append(COMMA) + .append(strCPU).append(COMMA) + .append(strMemory).append(COMMA) + .append(statsInfo.width).append("x") + .append(statsInfo.height).append(COMMA) + .append(statsInfo.renderFps).append(COMMA) + .append(statsInfo.decoderFps).append(COMMA) + .append(statsInfo.receivedBitrate).append(COMMA); + Log.d(TAG, "Fps:" + String.format(Locale.getDefault(), "%.2f", mAverageFps) + + " Render Time:" + String.format(Locale.getDefault(), "%.2f", mAverageRenderTime)); + if (extraInfo != null) { + stringBuilder.append(extraInfo); + } + stringBuilder.append("\n"); + flush(stringBuilder); + } + }); + } + mFrameRate++; + } + + private void flush(StringBuilder stringBuilder) { + if (mStreamWriter == null) { + return; + } + try { + mStreamWriter.write(stringBuilder.toString()); + mStreamWriter.flush(); + } catch (IOException e) { + Log.e(TAG, "flush: ", e); + } + } + + public void close() { + Log.d(TAG, "close: "); + mHandler.post(new Runnable() { + @Override + public void run() { + if (mStreamWriter != null) { + try { + mStreamWriter.close(); + mStreamWriter = null; + } catch (IOException e) { + Log.e(TAG, "close: ", e); + } + } + } + }); + mHandler.getLooper().quitSafely(); + mHandler = null; + mCPUInfoUtil.close(); + } + + public double getCpuUsed() { + return mCpuUsed; + } + + public double getMemory() { + return mMemory; + } + + public double getAverageRenderTime() { + return mAverageRenderTime; + } + + public double getAverageFps() { + return mAverageFps; + } +} diff --git a/Agora-Video-With-FaceUnity-Android/app/src/main/java/io/agora/profile/Constant.java b/Agora-Video-With-FaceUnity-Android/app/src/main/java/io/agora/profile/Constant.java new file mode 100644 index 00000000..8b0d070c --- /dev/null +++ b/Agora-Video-With-FaceUnity-Android/app/src/main/java/io/agora/profile/Constant.java @@ -0,0 +1,49 @@ +package io.agora.profile; + +import android.os.Build; +import android.os.Environment; + +import java.io.File; +import java.util.regex.Pattern; + +/** + * Created by tujh on 2018年2月7日. + */ + +public class Constant { + public static final String APP_NAME = "AgoraVideo"; + public static final String filePath = Environment.getExternalStoragePublicDirectory("") + + File.separator + "FaceUnity" + File.separator + APP_NAME + File.separator; + + public static final String DICMFilePath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).getPath(); + public static final String photoFilePath; + public static final String cameraFilePath; + + static { + if (Build.FINGERPRINT.contains("Flyme") + || Pattern.compile("Flyme", Pattern.CASE_INSENSITIVE).matcher(Build.DISPLAY).find() + || Build.MANUFACTURER.contains("Meizu") + || Build.MANUFACTURER.contains("MeiZu")) { + photoFilePath = DICMFilePath + File.separator + "Camera" + File.separator; + cameraFilePath = DICMFilePath + File.separator + "Video" + File.separator; + } else if (Build.FINGERPRINT.contains("vivo") + || Pattern.compile("vivo", Pattern.CASE_INSENSITIVE).matcher(Build.DISPLAY).find() + || Build.MANUFACTURER.contains("vivo") + || Build.MANUFACTURER.contains("vivo")) { + photoFilePath = cameraFilePath = Environment.getExternalStoragePublicDirectory("") + File.separator + "相机" + File.separator; + } else { + cameraFilePath = photoFilePath = DICMFilePath + File.separator + "Camera" + File.separator; + } + createFile(filePath); + createFile(cameraFilePath); + createFile(photoFilePath); + } + + public static void createFile(String path) { + File dir = new File(path); + if (!dir.exists()) { + dir.mkdirs(); + } + } + +} diff --git a/Agora-Video-With-FaceUnity-Android/app/src/main/java/io/agora/profile/FPSUtil.java b/Agora-Video-With-FaceUnity-Android/app/src/main/java/io/agora/profile/FPSUtil.java new file mode 100644 index 00000000..861f8875 --- /dev/null +++ b/Agora-Video-With-FaceUnity-Android/app/src/main/java/io/agora/profile/FPSUtil.java @@ -0,0 +1,63 @@ +package io.agora.profile; + +/** + * FPS工具类 + * Created by tujh on 2018年5月24日. + */ +public class FPSUtil { + private static final int NANO_IN_ONE_MILLI_SECOND = 1000000; + private static final int NANO_IN_ONE_SECOND = 1000 * NANO_IN_ONE_MILLI_SECOND; + + private static long sLastFrameTimeStamp = 0; + + /** + * 每帧都计算 + * + * @return + */ + public static double fps() { + long tmp = System.nanoTime(); + double fps = ((double) NANO_IN_ONE_SECOND) / (tmp - sLastFrameTimeStamp); + sLastFrameTimeStamp = tmp; +// Log.e(TAG, "FPS : " + fps); + return fps; + } + + private static long mStartTime = 0; + + /** + * 平均值 + * + * @return + */ + public static double fpsAVG(int time) { + long tmp = System.nanoTime(); + double fps = ((double) NANO_IN_ONE_SECOND) * time / (tmp - mStartTime); + mStartTime = tmp; +// Log.e(TAG, "FPS : " + fps); + return fps; + } + + private long mLimitMinTime = 33333333; + private long mLimitStartTime; + private int mLimitFrameRate; + + public void setLimitMinTime(long limitMinTime) { + mLimitMinTime = limitMinTime; + } + + public void limit() { + try { + if (mLimitFrameRate == 0 || mLimitFrameRate> 600000) { + mLimitStartTime = System.nanoTime(); + mLimitFrameRate = 0; + } + long sleepTime = mLimitMinTime * mLimitFrameRate++ - (System.nanoTime() - mLimitStartTime); + if (sleepTime> 0) { + Thread.sleep(sleepTime / NANO_IN_ONE_MILLI_SECOND, (int) (sleepTime % NANO_IN_ONE_MILLI_SECOND)); + } + } catch (InterruptedException e) { + e.printStackTrace(); + } + } +} diff --git a/Agora-Video-With-FaceUnity-Android/app/src/main/java/io/agora/profile/MemoryInfoUtil.java b/Agora-Video-With-FaceUnity-Android/app/src/main/java/io/agora/profile/MemoryInfoUtil.java new file mode 100644 index 00000000..c9376b72 --- /dev/null +++ b/Agora-Video-With-FaceUnity-Android/app/src/main/java/io/agora/profile/MemoryInfoUtil.java @@ -0,0 +1,89 @@ +package io.agora.profile; + +import android.os.Build; +import android.os.Debug; +import android.support.annotation.RequiresApi; + +import java.lang.reflect.Field; + +/** + * 内存使用率获取工具类 + * Created by tujh on 2018年5月24日. + */ +public abstract class MemoryInfoUtil { + + @RequiresApi(api = Build.VERSION_CODES.M) + public static double getMemory(Debug.MemoryInfo[] memoryInfos) { + try { + if (Build.VERSION.SDK_INT>= 27) { + return compute(memoryInfos); + } + + Field otherStats_Field = memoryInfos[0].getClass().getDeclaredField("otherStats"); + otherStats_Field.setAccessible(true); + int[] otherStats = (int[]) otherStats_Field.get(memoryInfos[0]); + + Field NUM_CATEGORIES_Field = memoryInfos[0].getClass().getDeclaredField("NUM_CATEGORIES"); + Field offsetPrivateClean_Field = memoryInfos[0].getClass().getDeclaredField("offsetPrivateClean"); + Field offsetPrivateDirty_Field = memoryInfos[0].getClass().getDeclaredField("offsetPrivateDirty"); + final int NUM_CATEGORIES = (int) NUM_CATEGORIES_Field.get(memoryInfos[0]); + final int offsetPrivateClean = (int) offsetPrivateClean_Field.get(memoryInfos[0]); + final int offsetPrivateDirty = (int) offsetPrivateDirty_Field.get(memoryInfos[0]); + + int javaHeap = memoryInfos[0].dalvikPrivateDirty + + otherStats[12 * NUM_CATEGORIES + offsetPrivateClean] + otherStats[12 * NUM_CATEGORIES + offsetPrivateDirty]; + int nativeHeap = memoryInfos[0].nativePrivateDirty; + int code = otherStats[6 * NUM_CATEGORIES + offsetPrivateClean] + otherStats[6 * NUM_CATEGORIES + offsetPrivateDirty] + + otherStats[7 * NUM_CATEGORIES + offsetPrivateClean] + otherStats[7 * NUM_CATEGORIES + offsetPrivateDirty] + + otherStats[8 * NUM_CATEGORIES + offsetPrivateClean] + otherStats[8 * NUM_CATEGORIES + offsetPrivateDirty] + + otherStats[9 * NUM_CATEGORIES + offsetPrivateClean] + otherStats[9 * NUM_CATEGORIES + offsetPrivateDirty] + + otherStats[10 * NUM_CATEGORIES + offsetPrivateClean] + otherStats[10 * NUM_CATEGORIES + offsetPrivateDirty] + + otherStats[11 * NUM_CATEGORIES + offsetPrivateClean] + otherStats[11 * NUM_CATEGORIES + offsetPrivateDirty]; + int stack = otherStats[NUM_CATEGORIES + offsetPrivateDirty]; + int graphics = otherStats[4 * NUM_CATEGORIES + offsetPrivateClean] + otherStats[4 * NUM_CATEGORIES + offsetPrivateDirty] + + otherStats[14 * NUM_CATEGORIES + offsetPrivateClean] + otherStats[14 * NUM_CATEGORIES + offsetPrivateDirty] + + otherStats[15 * NUM_CATEGORIES + offsetPrivateClean] + otherStats[15 * NUM_CATEGORIES + offsetPrivateDirty]; + int other = otherStats[0 * NUM_CATEGORIES + offsetPrivateClean] + otherStats[0 * NUM_CATEGORIES + offsetPrivateDirty] + + otherStats[2 * NUM_CATEGORIES + offsetPrivateClean] + otherStats[2 * NUM_CATEGORIES + offsetPrivateDirty] + + otherStats[3 * NUM_CATEGORIES + offsetPrivateClean] + otherStats[3 * NUM_CATEGORIES + offsetPrivateDirty] + + otherStats[5 * NUM_CATEGORIES + offsetPrivateClean] + otherStats[5 * NUM_CATEGORIES + offsetPrivateDirty] + + otherStats[13 * NUM_CATEGORIES + offsetPrivateClean] + otherStats[13 * NUM_CATEGORIES + offsetPrivateDirty] + + otherStats[16 * NUM_CATEGORIES + offsetPrivateClean] + otherStats[16 * NUM_CATEGORIES + offsetPrivateDirty]; + int all = javaHeap + nativeHeap + code + stack + graphics + other; +// Log.d("MemoryAll", "javaHeap=" + javaHeap +// + "\nnativeHeap=" + nativeHeap + "\ncode=" + code + "\nstack=" + stack +// + "\ngraphics=" + graphics + "\nother=" + other); +// Log.e(TAG, "memory " + memoryStr +// + " java-heap " + String.format("%.2f", ((double) javaHeap) / 1024) +// + " native-heap " + String.format("%.2f", ((double) nativeHeap) / 1024) +// + " code " + String.format("%.2f", ((double) code) / 1024) +// + " stack " + String.format("%.2f", ((double) stack) / 1024) +// + " graphics " + String.format("%.2f", ((double) graphics) / 1024) +// + " other " + String.format("%.2f", ((double) other) / 1024) +// + " pps " + String.format("%.2f", ((double) memoryInfos[0].getTotalPss()) / 1024) +// ); + return ((double) all) / 1024; + } catch (Exception e) { + e.printStackTrace(); + } + return 0; + } + + + @RequiresApi(api = Build.VERSION_CODES.M) + private static double compute(Debug.MemoryInfo[] memInfo) { + String java_mem = memInfo[0].getMemoryStat("summary.java-heap"); + String native_mem = memInfo[0].getMemoryStat("summary.native-heap"); + String graphics_mem = memInfo[0].getMemoryStat("summary.graphics"); + String stack_mem = memInfo[0].getMemoryStat("summary.stack"); + String code_mem = memInfo[0].getMemoryStat("summary.code"); + String others_mem = memInfo[0].getMemoryStat("summary.system"); + int all = Integer.parseInt(java_mem) + + Integer.parseInt(native_mem) + + Integer.parseInt(graphics_mem) + + Integer.parseInt(stack_mem) + + Integer.parseInt(code_mem) + + Integer.parseInt(others_mem); + return ((double) all) / 1024; + } +} diff --git a/Agora-Video-With-FaceUnity-Android/app/src/main/java/io/agora/rtcwithfu/MyApplication.java b/Agora-Video-With-FaceUnity-Android/app/src/main/java/io/agora/rtcwithfu/MyApplication.java index fa1b1773..dc1a7edd 100644 --- a/Agora-Video-With-FaceUnity-Android/app/src/main/java/io/agora/rtcwithfu/MyApplication.java +++ b/Agora-Video-With-FaceUnity-Android/app/src/main/java/io/agora/rtcwithfu/MyApplication.java @@ -59,6 +59,10 @@ public void removeRtcHandler(RtcEngineEventHandler handler) { mRtcEventHandler.removeEventHandler(handler); } + public RtcEngineEventHandlerProxy getRtcEventHandler() { + return mRtcEventHandler; + } + public CameraVideoManager videoManager() { return mVideoManager; } diff --git a/Agora-Video-With-FaceUnity-Android/app/src/main/java/io/agora/rtcwithfu/RtcEngineEventHandlerProxy.java b/Agora-Video-With-FaceUnity-Android/app/src/main/java/io/agora/rtcwithfu/RtcEngineEventHandlerProxy.java index f50209e8..b91dba78 100644 --- a/Agora-Video-With-FaceUnity-Android/app/src/main/java/io/agora/rtcwithfu/RtcEngineEventHandlerProxy.java +++ b/Agora-Video-With-FaceUnity-Android/app/src/main/java/io/agora/rtcwithfu/RtcEngineEventHandlerProxy.java @@ -5,6 +5,7 @@ import io.agora.rtc.IRtcEngineEventHandler; public class RtcEngineEventHandlerProxy extends IRtcEngineEventHandler { + private static final String TAG = "RtcEngineEventHandlerPr"; private ArrayList mEventHandlers = new ArrayList(); public void addEventHandler(RtcEngineEventHandler handler) { @@ -42,4 +43,59 @@ public void onRemoteVideoStateChanged(int uid, int state, int reason, int elapse handler.onRemoteVideoStateChanged(uid, state, reason, elapsed); } } + + private int mCallbackCount; + private int mWidth; + private int mHeight; + private int mSumRenderFps; + private int mSumDecoderFps; + private int mSumReceivedBitrate; + + public static class StatsInfo { + public int width; + public int height; + public int renderFps; + public int decoderFps; + + public int receivedBitrate; + } + + public StatsInfo retrieveStatsInfo() { + StatsInfo statsInfo = new StatsInfo(); + synchronized (this) { + statsInfo.width = mWidth; + statsInfo.height = mHeight; + int count = mCallbackCount; + if (count> 0) { + statsInfo.renderFps = mSumRenderFps / count; + statsInfo.decoderFps = mSumDecoderFps / count; + statsInfo.receivedBitrate = mSumReceivedBitrate / count; + } + mCallbackCount = 0; + mSumRenderFps = 0; + mSumDecoderFps = 0; + mSumReceivedBitrate = 0; + } + return statsInfo; + } + + @Override + public void onRemoteVideoStats(RemoteVideoStats remoteVideoStats) { + super.onRemoteVideoStats(remoteVideoStats); + // Log.d(TAG, String.format("onRemoteVideoStats. width %d, height %d, " + +// "delay %d, receivedBitrate %d, decoderOutputFrameRate %d, rendererOutputFrameRate %d, " + +// "packetLossRate %d, totalFrozenTime %d, totalActiveTime %d, rxStreamType %d", +// remoteVideoStats.width, remoteVideoStats.height, remoteVideoStats.delay, remoteVideoStats.receivedBitrate, +// remoteVideoStats.decoderOutputFrameRate, remoteVideoStats.rendererOutputFrameRate, +// remoteVideoStats.packetLossRate, remoteVideoStats.totalFrozenTime, remoteVideoStats.totalActiveTime, +// remoteVideoStats.rxStreamType)); + synchronized (this) { + mWidth = remoteVideoStats.width; + mHeight = remoteVideoStats.height; + mSumRenderFps += remoteVideoStats.rendererOutputFrameRate; + mSumDecoderFps += remoteVideoStats.decoderOutputFrameRate; + mSumReceivedBitrate += remoteVideoStats.receivedBitrate; + ++mCallbackCount; + } + } } diff --git a/Agora-Video-With-FaceUnity-Android/app/src/main/java/io/agora/rtcwithfu/activities/FUChatActivity.java b/Agora-Video-With-FaceUnity-Android/app/src/main/java/io/agora/rtcwithfu/activities/FUChatActivity.java index f71c3882..90cfa75c 100644 --- a/Agora-Video-With-FaceUnity-Android/app/src/main/java/io/agora/rtcwithfu/activities/FUChatActivity.java +++ b/Agora-Video-With-FaceUnity-Android/app/src/main/java/io/agora/rtcwithfu/activities/FUChatActivity.java @@ -11,6 +11,7 @@ import android.view.SurfaceView; import android.view.TextureView; import android.view.View; +import android.view.WindowManager; import android.widget.FrameLayout; import android.widget.RelativeLayout; import android.widget.TextView; @@ -18,20 +19,24 @@ import com.faceunity.core.enumeration.FUAIProcessorEnum; import com.faceunity.nama.FURenderer; import com.faceunity.nama.data.FaceUnityDataFactory; -import com.faceunity.nama.dialog.ToastHelper; import com.faceunity.nama.listener.FURendererListener; import com.faceunity.nama.ui.FaceUnityView; -import java.util.concurrent.CountDownLatch; +import java.io.File; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Locale; import io.agora.capture.video.camera.CameraVideoManager; import io.agora.capture.video.camera.Constant; import io.agora.capture.video.camera.VideoCapture; import io.agora.framework.PreprocessorFaceUnity; import io.agora.framework.RtcVideoConsumer; +import io.agora.profile.CSVUtils; import io.agora.rtc.RtcEngine; import io.agora.rtc.video.VideoCanvas; import io.agora.rtc.video.VideoEncoderConfiguration; +import io.agora.rtcwithfu.MyApplication; import io.agora.rtcwithfu.R; import io.agora.rtcwithfu.RtcEngineEventHandler; import io.agora.rtcwithfu.utils.Constants; @@ -64,6 +69,7 @@ public class FUChatActivity extends RtcBasedActivity implements RtcEngineEventHa @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); setContentView(R.layout.activity_base); initUI(); initRoom(); @@ -71,6 +77,10 @@ protected void onCreate(Bundle savedInstanceState) { mFURenderer.bindListener(mFURendererListener); String sdkVersion = RtcEngine.getSdkVersion(); Log.i(TAG, "onCreate: agora sdk version " + sdkVersion); + initCsvUtil(this); + if (preprocessor !=null) { + preprocessor.setCSVUtils(mCSVUtils); + } } private void initUI() { @@ -131,6 +141,17 @@ public void onCameraClosed() { TextureView localVideo = findViewById(R.id.local_video_view); mVideoManager.setLocalPreview(localVideo); + preprocessor.setSurfaceListener(new PreprocessorFaceUnity.SurfaceViewListener() { + @Override + public void onSurfaceCreated() { + mFaceUnityDataFactory.bindCurrentRenderer(); + } + + @Override + public void onSurfaceDestroyed() { + mFURenderer.release(); + } + }); } private void joinChannel() { @@ -186,7 +207,6 @@ protected void onResume() { super.onResume(); Sensor sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); mSensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_NORMAL); - mFaceUnityDataFactory.bindCurrentRenderer(); preprocessor.setRenderEnable(true); mVideoManager.startCapture(); } @@ -267,4 +287,33 @@ public void onSensorChanged(SensorEvent event) { public void onAccuracyChanged(Sensor sensor, int accuracy) { } + + private static final int ENCODE_FRAME_WIDTH = 960; + private static final int ENCODE_FRAME_HEIGHT = 540; + private static final int ENCODE_FRAME_BITRATE = 1000; + private static final int ENCODE_FRAME_FPS = 30; + + private CSVUtils mCSVUtils; + private void initCsvUtil(Context context) { + mCSVUtils = new CSVUtils(context); + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss", Locale.getDefault()); + String dateStrDir = format.format(new Date(System.currentTimeMillis())); + dateStrDir = dateStrDir.replaceAll("-", "").replaceAll("_", ""); + SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmmssSSS", Locale.getDefault()); + String dateStrFile = df.format(new Date()); + String filePath = io.agora.profile.Constant.filePath + dateStrDir + File.separator + "excel-" + dateStrFile + ".csv"; + Log.d(TAG, "initLog: CSV file path:" + filePath); + StringBuilder headerInfo = new StringBuilder(); + headerInfo.append("version:").append(FURenderer.getInstance().getVersion()).append(CSVUtils.COMMA) + .append("机型:").append(android.os.Build.MANUFACTURER).append(android.os.Build.MODEL).append(CSVUtils.COMMA) + .append("处理方式:双输入纹理输出").append(CSVUtils.COMMA) + .append("编码方式:硬件编码").append(CSVUtils.COMMA) + .append("编码分辨率:").append(ENCODE_FRAME_WIDTH).append("x").append(ENCODE_FRAME_HEIGHT).append(CSVUtils.COMMA) + .append("编码帧率:").append(ENCODE_FRAME_FPS).append(CSVUtils.COMMA) + .append("编码码率:").append(ENCODE_FRAME_BITRATE).append(CSVUtils.COMMA) + .append("预览分辨率:").append(CAPTURE_WIDTH).append("x").append(CAPTURE_HEIGHT).append(CSVUtils.COMMA) + .append("预览帧率:").append(CAPTURE_FRAME_RATE).append(CSVUtils.COMMA); + mCSVUtils.initHeader(filePath, headerInfo); + mCSVUtils.setRtcEngineEventHandler(((MyApplication) getApplication()).getRtcEventHandler()); + } } diff --git a/Agora-Video-With-FaceUnity-Android/app/src/main/java/io/agora/rtcwithfu/activities/MainActivity.java b/Agora-Video-With-FaceUnity-Android/app/src/main/java/io/agora/rtcwithfu/activities/MainActivity.java index 4d724259..c0e399be 100644 --- a/Agora-Video-With-FaceUnity-Android/app/src/main/java/io/agora/rtcwithfu/activities/MainActivity.java +++ b/Agora-Video-With-FaceUnity-Android/app/src/main/java/io/agora/rtcwithfu/activities/MainActivity.java @@ -12,8 +12,11 @@ import android.widget.EditText; import android.widget.Toast; -import io.agora.rtcwithfu.utils.Constants; +import com.faceunity.FUConfig; +import com.faceunity.nama.utils.FuDeviceUtils; + import io.agora.rtcwithfu.R; +import io.agora.rtcwithfu.utils.Constants; public class MainActivity extends Activity { private static final int REQUEST_CODE_ALL_PERMISSIONS = 999; @@ -25,6 +28,7 @@ protected void onCreate(Bundle savedInstanceState) { setContentView(R.layout.activity_navigation); mChannelName = findViewById(R.id.edt_channel); checkPermissions(); + FUConfig.DEVICE_LEVEL = FuDeviceUtils.judgeDeviceLevel(this); } private void checkPermissions() { @@ -68,6 +72,7 @@ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permis } public void onStartBroadcastClick(View view) { + if (!isFastClick()) {return;} String name = mChannelName.getText().toString(); if (name.isEmpty()) { Toast.makeText(this, R.string.empty_room_name_toast, Toast.LENGTH_SHORT).show(); @@ -77,4 +82,18 @@ public void onStartBroadcastClick(View view) { startActivity(intent); } } + + // 两次点击按钮之间的点击间隔不能少于1000毫秒 + private static final int MIN_CLICK_DELAY_TIME = 1000; + private static long lastClickTime; + + public static boolean isFastClick() { + boolean flag = false; + long curClickTime = System.currentTimeMillis(); + if ((curClickTime - lastClickTime)>= MIN_CLICK_DELAY_TIME) { + flag = true; + } + lastClickTime = curClickTime; + return flag; + } } diff --git a/Agora-Video-With-FaceUnity-Android/faceunity/build.gradle b/Agora-Video-With-FaceUnity-Android/faceunity/build.gradle index 079b72b3..9eb276eb 100644 --- a/Agora-Video-With-FaceUnity-Android/faceunity/build.gradle +++ b/Agora-Video-With-FaceUnity-Android/faceunity/build.gradle @@ -6,9 +6,8 @@ android { defaultConfig { minSdkVersion 19 targetSdkVersion 28 - versionCode 741 - versionName "7.4.1.0" - // 7.3.0_phy_505b959b_bf80f59 + versionCode 802 + versionName "8.0.2" } buildTypes { release { @@ -28,7 +27,7 @@ dependencies { resolutionStrategy.cacheDynamicVersionsFor 0,'seconds' } implementation 'com.android.support:appcompat-v7:28.0.0' - api 'com.faceunity:core:7.4.1.0' - implementation 'com.faceunity:model:7.4.1.0' + api 'com.faceunity:core:8.0.2' + implementation 'com.faceunity:model:8.0.2' implementation 'com.android.support:recyclerview-v7:28.0.0' } \ No newline at end of file diff --git a/Agora-Video-With-FaceUnity-Android/faceunity/src/main/java/com/faceunity/FUConfig.java b/Agora-Video-With-FaceUnity-Android/faceunity/src/main/java/com/faceunity/FUConfig.java new file mode 100644 index 00000000..7e797594 --- /dev/null +++ b/Agora-Video-With-FaceUnity-Android/faceunity/src/main/java/com/faceunity/FUConfig.java @@ -0,0 +1,8 @@ +package com.faceunity; + +import com.faceunity.nama.utils.FuDeviceUtils; + +public class FUConfig { + //设备等级默认为中级 + public static int DEVICE_LEVEL = FuDeviceUtils.DEVICE_LEVEL_MID; +} diff --git a/Agora-Video-With-FaceUnity-Android/faceunity/src/main/java/com/faceunity/nama/FURenderer.java b/Agora-Video-With-FaceUnity-Android/faceunity/src/main/java/com/faceunity/nama/FURenderer.java index fcd9a0e7..f7ac5e02 100644 --- a/Agora-Video-With-FaceUnity-Android/faceunity/src/main/java/com/faceunity/nama/FURenderer.java +++ b/Agora-Video-With-FaceUnity-Android/faceunity/src/main/java/com/faceunity/nama/FURenderer.java @@ -69,6 +69,14 @@ public static FURenderer getInstance() { /*检测标识*/ private int aIProcessTrackStatus = -1; + /** + * + * @return version + */ + public String getVersion() { + return mFURenderKit.getVersion(); + } + /** * 初始化鉴权 * @@ -122,8 +130,8 @@ public int onDrawFrameDualInput(byte[] img, int texId, int width, int height) { prepareDrawFrame(); FURenderInputData inputData = new FURenderInputData(width, height); /*注释掉Buffer配置,启用单纹理模式,防止Buffer跟纹理存在不对齐造成,美妆偏移*/ - //inputData.setImageBuffer(new FURenderInputData.FUImageBuffer(inputBufferType, img));//设置为单Buffer输入 - inputData.setTexture(new FURenderInputData.FUTexture(inputTextureType, texId)); +// inputData.setImageBuffer(new FURenderInputData.FUImageBuffer(inputBufferType, img));//设置为单Buffer输入 + inputData.setTexture(new FURenderInputData.FUTexture(inputTextureType, texId)); FURenderInputData.FURenderConfig config = inputData.getRenderConfig(); config.setExternalInputType(externalInputType); config.setInputOrientation(inputOrientation); diff --git a/Agora-Video-With-FaceUnity-Android/faceunity/src/main/java/com/faceunity/nama/data/FaceBeautyDataFactory.java b/Agora-Video-With-FaceUnity-Android/faceunity/src/main/java/com/faceunity/nama/data/FaceBeautyDataFactory.java index 6e2628f0..a4f4d63a 100644 --- a/Agora-Video-With-FaceUnity-Android/faceunity/src/main/java/com/faceunity/nama/data/FaceBeautyDataFactory.java +++ b/Agora-Video-With-FaceUnity-Android/faceunity/src/main/java/com/faceunity/nama/data/FaceBeautyDataFactory.java @@ -1,8 +1,12 @@ package com.faceunity.nama.data; + + import android.support.annotation.NonNull; +import com.faceunity.FUConfig; import com.faceunity.core.controller.facebeauty.FaceBeautyParam; +import com.faceunity.core.faceunity.FUAIKit; import com.faceunity.core.faceunity.FURenderKit; import com.faceunity.core.model.facebeauty.FaceBeauty; import com.faceunity.nama.entity.FaceBeautyBean; @@ -201,16 +205,17 @@ public void updateFilterIntensity(double intensity) { put(FaceBeautyParam.FACE_SHAPE_INTENSITY, defaultFaceBeauty::setSharpenIntensity); put(FaceBeautyParam.CHEEK_THINNING_INTENSITY, defaultFaceBeauty::setCheekThinningIntensity); put(FaceBeautyParam.CHEEK_V_INTENSITY, defaultFaceBeauty::setCheekVIntensity); - put(FaceBeautyParam.CHEEK_NARROW_INTENSITY, defaultFaceBeauty::setCheekNarrowIntensity); - put(FaceBeautyParam.CHEEK_SMALL_INTENSITY, defaultFaceBeauty::setCheekSmallIntensity); + put(FaceBeautyParam.CHEEK_NARROW_INTENSITY_V2, defaultFaceBeauty::setCheekNarrowIntensityV2); + put(FaceBeautyParam.CHEEK_SHORT_INTENSITY, defaultFaceBeauty::setCheekShortIntensity); + put(FaceBeautyParam.CHEEK_SMALL_INTENSITY_V2, defaultFaceBeauty::setCheekSmallIntensityV2); put(FaceBeautyParam.INTENSITY_CHEEKBONES_INTENSITY, defaultFaceBeauty::setCheekBonesIntensity); put(FaceBeautyParam.INTENSITY_LOW_JAW_INTENSITY, defaultFaceBeauty::setLowerJawIntensity); - put(FaceBeautyParam.EYE_ENLARGING_INTENSITY, defaultFaceBeauty::setEyeEnlargingIntensity); + put(FaceBeautyParam.EYE_ENLARGING_INTENSITY_V2, defaultFaceBeauty::setEyeEnlargingIntensityV2); put(FaceBeautyParam.EYE_CIRCLE_INTENSITY, defaultFaceBeauty::setEyeCircleIntensity); put(FaceBeautyParam.CHIN_INTENSITY, defaultFaceBeauty::setChinIntensity); - put(FaceBeautyParam.FOREHEAD_INTENSITY, defaultFaceBeauty::setForHeadIntensity); - put(FaceBeautyParam.NOSE_INTENSITY, defaultFaceBeauty::setNoseIntensity); - put(FaceBeautyParam.MOUTH_INTENSITY, defaultFaceBeauty::setMouthIntensity); + put(FaceBeautyParam.FOREHEAD_INTENSITY_V2, defaultFaceBeauty::setForHeadIntensityV2); + put(FaceBeautyParam.NOSE_INTENSITY_V2, defaultFaceBeauty::setNoseIntensityV2); + put(FaceBeautyParam.MOUTH_INTENSITY_V2, defaultFaceBeauty::setMouthIntensityV2); put(FaceBeautyParam.CANTHUS_INTENSITY, defaultFaceBeauty::setCanthusIntensity); put(FaceBeautyParam.EYE_SPACE_INTENSITY, defaultFaceBeauty::setEyeSpaceIntensity); put(FaceBeautyParam.EYE_ROTATE_INTENSITY, defaultFaceBeauty::setEyeRotateIntensity); @@ -234,16 +239,17 @@ public void updateFilterIntensity(double intensity) { put(FaceBeautyParam.FACE_SHAPE_INTENSITY, defaultFaceBeauty::getSharpenIntensity); put(FaceBeautyParam.CHEEK_THINNING_INTENSITY, defaultFaceBeauty::getCheekThinningIntensity); put(FaceBeautyParam.CHEEK_V_INTENSITY, defaultFaceBeauty::getCheekVIntensity); - put(FaceBeautyParam.CHEEK_NARROW_INTENSITY, defaultFaceBeauty::getCheekNarrowIntensity); - put(FaceBeautyParam.CHEEK_SMALL_INTENSITY, defaultFaceBeauty::getCheekSmallIntensity); + put(FaceBeautyParam.CHEEK_NARROW_INTENSITY_V2, defaultFaceBeauty::getCheekNarrowIntensityV2); + put(FaceBeautyParam.CHEEK_SHORT_INTENSITY, defaultFaceBeauty::getCheekShortIntensity); + put(FaceBeautyParam.CHEEK_SMALL_INTENSITY_V2, defaultFaceBeauty::getCheekSmallIntensityV2); put(FaceBeautyParam.INTENSITY_CHEEKBONES_INTENSITY, defaultFaceBeauty::getCheekBonesIntensity); put(FaceBeautyParam.INTENSITY_LOW_JAW_INTENSITY, defaultFaceBeauty::getLowerJawIntensity); - put(FaceBeautyParam.EYE_ENLARGING_INTENSITY, defaultFaceBeauty::getEyeEnlargingIntensity); + put(FaceBeautyParam.EYE_ENLARGING_INTENSITY_V2, defaultFaceBeauty::getEyeEnlargingIntensityV2); put(FaceBeautyParam.EYE_CIRCLE_INTENSITY, defaultFaceBeauty::getEyeCircleIntensity); put(FaceBeautyParam.CHIN_INTENSITY, defaultFaceBeauty::getChinIntensity); - put(FaceBeautyParam.FOREHEAD_INTENSITY, defaultFaceBeauty::getForHeadIntensity); - put(FaceBeautyParam.NOSE_INTENSITY, defaultFaceBeauty::getNoseIntensity); - put(FaceBeautyParam.MOUTH_INTENSITY, defaultFaceBeauty::getMouthIntensity); + put(FaceBeautyParam.FOREHEAD_INTENSITY_V2, defaultFaceBeauty::getForHeadIntensityV2); + put(FaceBeautyParam.NOSE_INTENSITY_V2, defaultFaceBeauty::getNoseIntensityV2); + put(FaceBeautyParam.MOUTH_INTENSITY_V2, defaultFaceBeauty::getMouthIntensityV2); put(FaceBeautyParam.CANTHUS_INTENSITY, defaultFaceBeauty::getCanthusIntensity); put(FaceBeautyParam.EYE_SPACE_INTENSITY, defaultFaceBeauty::getEyeSpaceIntensity); put(FaceBeautyParam.EYE_ROTATE_INTENSITY, defaultFaceBeauty::getEyeRotateIntensity); @@ -259,6 +265,7 @@ public void updateFilterIntensity(double intensity) { * FURenderKit加载当前特效 */ public void bindCurrentRenderer() { + FUAIKit.getInstance().faceProcessorSetFaceLandmarkQuality(FUConfig.DEVICE_LEVEL); mFURenderKit.setFaceBeauty(defaultFaceBeauty); } } diff --git a/Agora-Video-With-FaceUnity-Android/faceunity/src/main/java/com/faceunity/nama/repo/FaceBeautySource.java b/Agora-Video-With-FaceUnity-Android/faceunity/src/main/java/com/faceunity/nama/repo/FaceBeautySource.java index eb9cdf03..d38f6257 100644 --- a/Agora-Video-With-FaceUnity-Android/faceunity/src/main/java/com/faceunity/nama/repo/FaceBeautySource.java +++ b/Agora-Video-With-FaceUnity-Android/faceunity/src/main/java/com/faceunity/nama/repo/FaceBeautySource.java @@ -35,17 +35,18 @@ public static FaceBeauty getDefaultFaceBeauty() { recommendFaceBeauty.setFilterIntensity(0.4); /*美肤*/ recommendFaceBeauty.setBlurType(FaceBeautyBlurTypeEnum.FineSkin); - recommendFaceBeauty.setBlurIntensity(4.2); + recommendFaceBeauty.setSharpenIntensity(0.2); recommendFaceBeauty.setColorIntensity(0.3); recommendFaceBeauty.setRedIntensity(0.3); - recommendFaceBeauty.setSharpenIntensity(0.2); + recommendFaceBeauty.setBlurIntensity(4.2); /*美型*/ + recommendFaceBeauty.setFaceShapeIntensity(1.0); + recommendFaceBeauty.setEyeEnlargingIntensityV2(0.4); recommendFaceBeauty.setCheekVIntensity(0.5); - recommendFaceBeauty.setEyeEnlargingIntensity(0.4); + recommendFaceBeauty.setNoseIntensityV2(0.5); + recommendFaceBeauty.setForHeadIntensityV2(0.3); + recommendFaceBeauty.setMouthIntensityV2(0.4); recommendFaceBeauty.setChinIntensity(0.3); - recommendFaceBeauty.setForHeadIntensity(0.3); - recommendFaceBeauty.setNoseIntensity(0.5); - recommendFaceBeauty.setMouthIntensity(0.4); return recommendFaceBeauty; } @@ -128,13 +129,19 @@ public static ArrayList buildShapeParams() { ); params.add( new FaceBeautyBean( - FaceBeautyParam.CHEEK_NARROW_INTENSITY, R.string.beauty_box_cheek_narrow, + FaceBeautyParam.CHEEK_NARROW_INTENSITY_V2, R.string.beauty_box_cheek_narrow, R.drawable.icon_beauty_shape_face_narrow_close_selector, R.drawable.icon_beauty_shape_face_narrow_open_selector ) ); params.add( new FaceBeautyBean( - FaceBeautyParam.CHEEK_SMALL_INTENSITY, R.string.beauty_box_cheek_small, + FaceBeautyParam.CHEEK_SHORT_INTENSITY, R.string.beauty_box_cheek_short, + R.drawable.icon_beauty_shape_face_short_close_selector, R.drawable.icon_beauty_shape_face_short_open_selector + ) + ); + params.add( + new FaceBeautyBean( + FaceBeautyParam.CHEEK_SMALL_INTENSITY_V2, R.string.beauty_box_cheek_small, R.drawable.icon_beauty_shape_face_little_close_selector, R.drawable.icon_beauty_shape_face_little_open_selector ) ); @@ -152,7 +159,7 @@ public static ArrayList buildShapeParams() { ); params.add( new FaceBeautyBean( - FaceBeautyParam.EYE_ENLARGING_INTENSITY, R.string.beauty_box_eye_enlarge, + FaceBeautyParam.EYE_ENLARGING_INTENSITY_V2, R.string.beauty_box_eye_enlarge, R.drawable.icon_beauty_shape_enlarge_eye_close_selector, R.drawable.icon_beauty_shape_enlarge_eye_open_selector ) ); @@ -170,19 +177,19 @@ public static ArrayList buildShapeParams() { ); params.add( new FaceBeautyBean( - FaceBeautyParam.FOREHEAD_INTENSITY, R.string.beauty_box_intensity_forehead, + FaceBeautyParam.FOREHEAD_INTENSITY_V2, R.string.beauty_box_intensity_forehead, R.drawable.icon_beauty_shape_forehead_close_selector, R.drawable.icon_beauty_shape_forehead_open_selector ) ); params.add( new FaceBeautyBean( - FaceBeautyParam.NOSE_INTENSITY, R.string.beauty_box_intensity_nose, + FaceBeautyParam.NOSE_INTENSITY_V2, R.string.beauty_box_intensity_nose, R.drawable.icon_beauty_shape_thin_nose_close_selector, R.drawable.icon_beauty_shape_thin_nose_open_selector ) ); params.add( new FaceBeautyBean( - FaceBeautyParam.MOUTH_INTENSITY, R.string.beauty_box_intensity_mouth, + FaceBeautyParam.MOUTH_INTENSITY_V2, R.string.beauty_box_intensity_mouth, R.drawable.icon_beauty_shape_mouth_close_selector, R.drawable.icon_beauty_shape_mouth_open_selector ) ); @@ -244,17 +251,20 @@ public static HashMap buildModelAttributeRange() { /*美型*/ params.put(FaceBeautyParam.FACE_SHAPE_INTENSITY, new ModelAttributeData(1.0, 0.0, 0.0, 1.0)); params.put(FaceBeautyParam.CHEEK_THINNING_INTENSITY, new ModelAttributeData(0.0, 0.0, 0.0, 1.0)); + params.put(FaceBeautyParam.CHEEK_LONG_INTENSITY, new ModelAttributeData(0.0, 0.0, 0.0, 1.0)); + params.put(FaceBeautyParam.CHEEK_CIRCLE_INTENSITY, new ModelAttributeData(0.0, 0.0, 0.0, 1.0)); params.put(FaceBeautyParam.CHEEK_V_INTENSITY, new ModelAttributeData(0.5, 0.0, 0.0, 1.0)); - params.put(FaceBeautyParam.CHEEK_NARROW_INTENSITY, new ModelAttributeData(0.0, 0.0, 0.0, 0.5)); - params.put(FaceBeautyParam.CHEEK_SMALL_INTENSITY, new ModelAttributeData(0.0, 0.0, 0.0, 0.5)); + params.put(FaceBeautyParam.CHEEK_NARROW_INTENSITY_V2, new ModelAttributeData(0.0, 0.0, 0.0, 1.0)); + params.put(FaceBeautyParam.CHEEK_SHORT_INTENSITY, new ModelAttributeData(0.0, 0.0, 0.0, 1.0)); + params.put(FaceBeautyParam.CHEEK_SMALL_INTENSITY_V2, new ModelAttributeData(0.0, 0.0, 0.0, 1.0)); params.put(FaceBeautyParam.INTENSITY_CHEEKBONES_INTENSITY, new ModelAttributeData(0.0, 0.0, 0.0, 1.0)); params.put(FaceBeautyParam.INTENSITY_LOW_JAW_INTENSITY, new ModelAttributeData(0.0, 0.0, 0.0, 1.0)); - params.put(FaceBeautyParam.EYE_ENLARGING_INTENSITY, new ModelAttributeData(0.4, 0.0, 0.0, 1.0)); + params.put(FaceBeautyParam.EYE_ENLARGING_INTENSITY_V2, new ModelAttributeData(0.4, 0.0, 0.0, 1.0)); params.put(FaceBeautyParam.EYE_CIRCLE_INTENSITY, new ModelAttributeData(0.0, 0.0, 0.0, 1.0)); params.put(FaceBeautyParam.CHIN_INTENSITY, new ModelAttributeData(0.3, 0.5, 0.0, 1.0)); - params.put(FaceBeautyParam.FOREHEAD_INTENSITY, new ModelAttributeData(0.3, 0.5, 0.0, 1.0)); - params.put(FaceBeautyParam.NOSE_INTENSITY, new ModelAttributeData(0.5, 0.0, 0.0, 1.0)); - params.put(FaceBeautyParam.MOUTH_INTENSITY, new ModelAttributeData(0.4, 0.5, 0.0, 1.0)); + params.put(FaceBeautyParam.FOREHEAD_INTENSITY_V2, new ModelAttributeData(0.3, 0.5, 0.0, 1.0)); + params.put(FaceBeautyParam.NOSE_INTENSITY_V2, new ModelAttributeData(0.5, 0.0, 0.0, 1.0)); + params.put(FaceBeautyParam.MOUTH_INTENSITY_V2, new ModelAttributeData(0.4, 0.5, 0.0, 1.0)); params.put(FaceBeautyParam.CANTHUS_INTENSITY, new ModelAttributeData(0.0, 0.0, 0.0, 1.0)); params.put(FaceBeautyParam.EYE_SPACE_INTENSITY, new ModelAttributeData(0.5, 0.5, 0.0, 1.0)); params.put(FaceBeautyParam.EYE_ROTATE_INTENSITY, new ModelAttributeData(0.5, 0.5, 0.0, 1.0)); diff --git a/Agora-Video-With-FaceUnity-Android/faceunity/src/main/java/com/faceunity/nama/utils/FuDeviceUtils.java b/Agora-Video-With-FaceUnity-Android/faceunity/src/main/java/com/faceunity/nama/utils/FuDeviceUtils.java new file mode 100644 index 00000000..85de3301 --- /dev/null +++ b/Agora-Video-With-FaceUnity-Android/faceunity/src/main/java/com/faceunity/nama/utils/FuDeviceUtils.java @@ -0,0 +1,583 @@ +package com.faceunity.nama.utils; + +import android.annotation.TargetApi; +import android.app.ActivityManager; +import android.content.Context; +import android.os.Build; +import android.text.TextUtils; +import android.util.Log; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileFilter; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; + +public class FuDeviceUtils { + + public static final String TAG = "FuDeviceUtils"; + + public static final int DEVICE_LEVEL_HIGH = 2; + public static final int DEVICE_LEVEL_MID = 1; + public static final int DEVICE_LEVEL_LOW = 0; + + /** + * The default return value of any method in this class when an + * error occurs or when processing fails (Currently set to -1). Use this to check if + * the information about the device in question was successfully obtained. + */ + public static final int DEVICEINFO_UNKNOWN = -1; + + private static final FileFilter CPU_FILTER = new FileFilter() { + @Override + public boolean accept(File pathname) { + String path = pathname.getName(); + //regex is slow, so checking char by char. + if (path.startsWith("cpu")) { + for (int i = 3; i < path.length(); i++) { + if (!Character.isDigit(path.charAt(i))) { + return false; + } + } + return true; + } + return false; + } + }; + + + /** + * Calculates the total RAM of the device through Android API or /proc/meminfo. + * + * @param c - Context object for current running activity. + * @return Total RAM that the device has, or DEVICEINFO_UNKNOWN = -1 in the event of an error. + */ + @TargetApi(Build.VERSION_CODES.JELLY_BEAN) + public static long getTotalMemory(Context c) { + // memInfo.totalMem not supported in pre-Jelly Bean APIs. + if (Build.VERSION.SDK_INT>= Build.VERSION_CODES.JELLY_BEAN) { + ActivityManager.MemoryInfo memInfo = new ActivityManager.MemoryInfo(); + ActivityManager am = (ActivityManager) c.getSystemService(Context.ACTIVITY_SERVICE); + am.getMemoryInfo(memInfo); + if (memInfo != null) { + return memInfo.totalMem; + } else { + return DEVICEINFO_UNKNOWN; + } + } else { + long totalMem = DEVICEINFO_UNKNOWN; + try { + FileInputStream stream = new FileInputStream("/proc/meminfo"); + try { + totalMem = parseFileForValue("MemTotal", stream); + totalMem *= 1024; + } finally { + stream.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + return totalMem; + } + } + + /** + * Method for reading the clock speed of a CPU core on the device. Will read from either + * {@code /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq} or {@code /proc/cpuinfo}. + * + * @return Clock speed of a core on the device, or -1 in the event of an error. + */ + public static int getCPUMaxFreqKHz() { + int maxFreq = DEVICEINFO_UNKNOWN; + try { + for (int i = 0; i < getNumberOfCPUCores(); i++) { + String filename = + "/sys/devices/system/cpu/cpu" + i + "/cpufreq/cpuinfo_max_freq"; + File cpuInfoMaxFreqFile = new File(filename); + if (cpuInfoMaxFreqFile.exists() && cpuInfoMaxFreqFile.canRead()) { + byte[] buffer = new byte[128]; + FileInputStream stream = new FileInputStream(cpuInfoMaxFreqFile); + try { + stream.read(buffer); + int endIndex = 0; + //Trim the first number out of the byte buffer. + while (Character.isDigit(buffer[endIndex]) && endIndex < buffer.length) { + endIndex++; + } + String str = new String(buffer, 0, endIndex); + Integer freqBound = Integer.parseInt(str); + if (freqBound> maxFreq) { + maxFreq = freqBound; + } + } catch (NumberFormatException e) { + //Fall through and use /proc/cpuinfo. + } finally { + stream.close(); + } + } + } + if (maxFreq == DEVICEINFO_UNKNOWN) { + FileInputStream stream = new FileInputStream("/proc/cpuinfo"); + try { + int freqBound = parseFileForValue("cpu MHz", stream); + freqBound *= 1024; //MHz -> kHz + if (freqBound> maxFreq) maxFreq = freqBound; + } finally { + stream.close(); + } + } + } catch (IOException e) { + maxFreq = DEVICEINFO_UNKNOWN; //Fall through and return unknown. + } + return maxFreq; + } + + /** + * Reads the number of CPU cores from the first available information from + * {@code /sys/devices/system/cpu/possible}, {@code /sys/devices/system/cpu/present}, + * then {@code /sys/devices/system/cpu/}. + * + * @return Number of CPU cores in the phone, or DEVICEINFO_UKNOWN = -1 in the event of an error. + */ + public static int getNumberOfCPUCores() { + if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.GINGERBREAD_MR1) { + // Gingerbread doesn't support giving a single application access to both cores, but a + // handful of devices (Atrix 4G and Droid X2 for example) were released with a dual-core + // chipset and Gingerbread; that can let an app in the background run without impacting + // the foreground application. But for our purposes, it makes them single core. + return 1; + } + int cores; + try { + cores = getCoresFromFileInfo("/sys/devices/system/cpu/possible"); + if (cores == DEVICEINFO_UNKNOWN) { + cores = getCoresFromFileInfo("/sys/devices/system/cpu/present"); + } + if (cores == DEVICEINFO_UNKNOWN) { + cores = new File("/sys/devices/system/cpu/").listFiles(CPU_FILTER).length; + } + } catch (SecurityException e) { + cores = DEVICEINFO_UNKNOWN; + } catch (NullPointerException e) { + cores = DEVICEINFO_UNKNOWN; + } + return cores; + } + + /** + * Tries to read file contents from the file location to determine the number of cores on device. + * + * @param fileLocation The location of the file with CPU information + * @return Number of CPU cores in the phone, or DEVICEINFO_UKNOWN = -1 in the event of an error. + */ + private static int getCoresFromFileInfo(String fileLocation) { + InputStream is = null; + try { + is = new FileInputStream(fileLocation); + BufferedReader buf = new BufferedReader(new InputStreamReader(is)); + String fileContents = buf.readLine(); + buf.close(); + return getCoresFromFileString(fileContents); + } catch (IOException e) { + return DEVICEINFO_UNKNOWN; + } finally { + if (is != null) { + try { + is.close(); + } catch (IOException e) { + // Do nothing. + } + } + } + } + + /** + * Converts from a CPU core information format to number of cores. + * + * @param str The CPU core information string, in the format of "0-N" + * @return The number of cores represented by this string + */ + private static int getCoresFromFileString(String str) { + if (str == null || !str.matches("0-[\\d]+$")) { + return DEVICEINFO_UNKNOWN; + } + return Integer.valueOf(str.substring(2)) + 1; + } + + /** + * Helper method for reading values from system files, using a minimised buffer. + * + * @param textToMatch - Text in the system files to read for. + * @param stream - FileInputStream of the system file being read from. + * @return A numerical value following textToMatch in specified the system file. + * -1 in the event of a failure. + */ + private static int parseFileForValue(String textToMatch, FileInputStream stream) { + byte[] buffer = new byte[1024]; + try { + int length = stream.read(buffer); + for (int i = 0; i < length; i++) { + if (buffer[i] == '\n' || i == 0) { + if (buffer[i] == '\n') i++; + for (int j = i; j < length; j++) { + int textIndex = j - i; + //Text doesn't match query at some point. + if (buffer[j] != textToMatch.charAt(textIndex)) { + break; + } + //Text matches query here. + if (textIndex == textToMatch.length() - 1) { + return extractValue(buffer, j); + } + } + } + } + } catch (IOException e) { + //Ignore any exceptions and fall through to return unknown value. + } catch (NumberFormatException e) { + } + return DEVICEINFO_UNKNOWN; + } + + /** + * Helper method used by {@link #parseFileForValue(String, FileInputStream) parseFileForValue}. Parses + * the next available number after the match in the file being read and returns it as an integer. + * + * @param index - The index in the buffer array to begin looking. + * @return The next number on that line in the buffer, returned as an int. Returns + * DEVICEINFO_UNKNOWN = -1 in the event that no more numbers exist on the same line. + */ + private static int extractValue(byte[] buffer, int index) { + while (index < buffer.length && buffer[index] != '\n') { + if (Character.isDigit(buffer[index])) { + int start = index; + index++; + while (index < buffer.length && Character.isDigit(buffer[index])) { + index++; + } + String str = new String(buffer, 0, start, index - start); + return Integer.parseInt(str); + } + index++; + } + return DEVICEINFO_UNKNOWN; + } + + /** + * 获取当前剩余内存(ram) + * + * @param context + * @return + */ + public static long getAvailMemory(Context context) { + ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); + ActivityManager.MemoryInfo mi = new ActivityManager.MemoryInfo(); + am.getMemoryInfo(mi); + return mi.availMem; + } + + /** + * 获取厂商信息 + * + * @return + */ + public static String getBrand() { + return Build.BRAND; + } + + /** + * 获取手机机型 + * + * @return + */ + public static String getModel() { + return Build.MODEL; + } + + /** + * 获取硬件信息(cpu型号) + * + * @return + */ + public static String getHardWare() { + try { + FileReader fr = new FileReader("/proc/cpuinfo"); + BufferedReader br = new BufferedReader(fr); + String text; + String last = ""; + while ((text = br.readLine()) != null) { + last = text; + } + //一般机型的cpu型号都会在cpuinfo文件的最后一行 + if (last.contains("Hardware")) { + String[] hardWare = last.split(":\\s+", 2); + return hardWare[1]; + } + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + return Build.HARDWARE; + } + + + /** + * Level judgement based on current memory and CPU. + * + * @param context - Context object. + * @return + */ + public static int judgeDeviceLevel(Context context) { + int level; + //有一些设备不符合下述的判断规则,则走一个机型判断模式 + int specialDevice = judgeDeviceLevelInDeviceName(); + if (specialDevice>= 0) return specialDevice; + + int ramLevel = judgeMemory(context); + int cpuLevel = judgeCPU(); + if (ramLevel == 0 || ramLevel == 1 || cpuLevel == 0) { + level = DEVICE_LEVEL_LOW; + } else { + if (cpuLevel> 1) { + level = DEVICE_LEVEL_HIGH; + } else { + level = DEVICE_LEVEL_MID; + } + } + Log.d(TAG,"DeviceLevel: " + level); + return level; + } + + /** + * -1 不是特定的高低端机型 + * @return + */ + private static int judgeDeviceLevelInDeviceName() { + String currentDeviceName = getDeviceName(); + for (String deviceName:upscaleDevice) { + if (deviceName.equals(currentDeviceName)) { + return DEVICE_LEVEL_HIGH; + } + } + + for (String deviceName:middleDevice) { + if (deviceName.equals(currentDeviceName)) { + return DEVICE_LEVEL_MID; + } + } + + for (String deviceName:lowDevice) { + if (deviceName.equals(currentDeviceName)) { + return DEVICE_LEVEL_LOW; + } + } + return -1; + } + + public static final String[] upscaleDevice = {"vivo X6S A","MHA-AL00","VKY-AL00","V1838A"}; + public static final String[] lowDevice = {}; + public static final String[] middleDevice = {"OPPO R11s","PAR-AL00","MI 8 Lite","ONEPLUS A6000","PRO 6","PRO 7 Plus"}; + + /** + * 评定内存的等级. + * + * @return + */ + private static int judgeMemory(Context context) { + long ramMB = getTotalMemory(context) / (1024 * 1024); + int level = -1; + if (ramMB <= 2000) { //2G或以下的最低档 + level = 0; + } else if (ramMB <= 3000) { //2-3G + level = 1; + } else if (ramMB <= 4000) { //4G档 2018主流中端机 + level = 2; + } else if (ramMB <= 6000) { //6G档 高端机 + level = 3; + } else { //6G以上 旗舰机配置 + level = 4; + } + return level; + } + + /** + * 评定CPU等级.(按频率和厂商型号综合判断) + * + * @return + */ + private static int judgeCPU() { + int level = 0; + String cpuName = getHardWare(); + int freqMHz = getCPUMaxFreqKHz() / 1024; + + //一个不符合下述规律的高级白名单 + //如果可以获取到CPU型号名称 -> 根据不同的名称走不同判定策略 + if (!TextUtils.isEmpty(cpuName)) { + if (cpuName.contains("qcom") || cpuName.contains("Qualcomm")) { //高通骁龙 + return judgeQualcommCPU(cpuName, freqMHz); + } else if (cpuName.contains("hi") || cpuName.contains("kirin")) { //海思麒麟 + return judgeSkinCPU(cpuName, freqMHz); + } else if (cpuName.contains("MT")) {//联发科 + return judgeMTCPU(cpuName, freqMHz); + } + } + + //cpu型号无法获取的普通规则 + if (freqMHz <= 1600) { //1.5G 低端 + level = 0; + } else if (freqMHz <= 1950) { //2GHz 低中端 + level = 1; + } else if (freqMHz <= 2500) { //2.2 2.3g 中高端 + level = 2; + } else { //高端 + level = 3; + } + return level; + } + + /** + * 联发科芯片等级判定 + * + * @return + */ + private static int judgeMTCPU(String cpuName, int freqMHz) { + //P60之前的全是低端机 MT6771V/C + int level = 0; + int mtCPUVersion = getMTCPUVersion(cpuName); + if (mtCPUVersion == -1) { + //读取不出version 按照一个比较严格的方式来筛选出高端机 + if (freqMHz <= 1600) { //1.5G 低端 + level = 0; + } else if (freqMHz <= 2200) { //2GHz 低中端 + level = 1; + } else if (freqMHz <= 2700) { //2.2 2.3g 中高端 + level = 2; + } else { //高端 + level = 3; + } + } else if (mtCPUVersion < 6771) { + //均为中低端机 + if (freqMHz <= 1600) { //1.5G 低端 + level = 0; + } else { //2GHz 中端 + level = 1; + } + } else { + if (freqMHz <= 1600) { //1.5G 低端 + level = 0; + } else if (freqMHz <= 1900) { //2GHz 低中端 + level = 1; + } else if (freqMHz <= 2500) { //2.2 2.3g 中高端 + level = 2; + } else { //高端 + level = 3; + } + } + + return level; + } + + /** + * 通过联发科CPU型号定义 -> 获取cpu version + * + * @param cpuName + * @return + */ + private static int getMTCPUVersion(String cpuName) { + //截取MT后面的四位数字 + int cpuVersion = -1; + if (cpuName.length()> 5) { + String cpuVersionStr = cpuName.substring(2, 6); + try { + cpuVersion = Integer.valueOf(cpuVersionStr); + } catch (NumberFormatException exception) { + exception.printStackTrace(); + } + } + + return cpuVersion; + } + + /** + * 高通骁龙芯片等级判定 + * + * @return + */ + private static int judgeQualcommCPU(String cpuName, int freqMHz) { + int level = 0; + //xxxx inc MSM8937 比较老的芯片 + //7 8 xxx inc SDM710 + if (cpuName.contains("MSM")) { + //老芯片 + if (freqMHz <= 1600) { //1.5G 低端 + level = 0; + } else { //2GHz 低中端 + level = 1; + } + } else { + //新的芯片 + if (freqMHz <= 1600) { //1.5G 低端 + level = 0; + } else if (freqMHz <= 2000) { //2GHz 低中端 + level = 1; + } else if (freqMHz <= 2500) { //2.2 2.3g 中高端 + level = 2; + } else { //高端 + level = 3; + } + } + + return level; + } + + /** + * 麒麟芯片等级判定 + * + * @param freqMHz + * @return + */ + private static int judgeSkinCPU(String cpuName, int freqMHz) { + //型号 -> kirin710之后 & 最高核心频率 + int level = 0; + if (cpuName.startsWith("hi")) { + //这个是海思的芯片中低端 + if (freqMHz <= 1600) { //1.5G 低端 + level = 0; + } else if (freqMHz <= 2000) { //2GHz 低中端 + level = 1; + } + } else { + //这个是海思麒麟的芯片 + if (freqMHz <= 1600) { //1.5G 低端 + level = 0; + } else if (freqMHz <= 2000) { //2GHz 低中端 + level = 1; + } else if (freqMHz <= 2500) { //2.2 2.3g 中高端 + level = 2; + } else { //高端 + level = 3; + } + } + + return level; + } + + public static final String Nexus_6P = "Nexus 6P"; + + /** + * 获取设备名 + * + * @return + */ + public static String getDeviceName() { + String deviceName = ""; + if (Build.MODEL != null) deviceName = Build.MODEL; + Log.e(TAG,"deviceName: " + deviceName); + return deviceName; + } +} diff --git a/Agora-Video-With-FaceUnity-Android/faceunity/src/main/res/drawable/icon_beauty_shape_face_short_close_selector.xml b/Agora-Video-With-FaceUnity-Android/faceunity/src/main/res/drawable/icon_beauty_shape_face_short_close_selector.xml new file mode 100644 index 00000000..c0a88e6a --- /dev/null +++ b/Agora-Video-With-FaceUnity-Android/faceunity/src/main/res/drawable/icon_beauty_shape_face_short_close_selector.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/Agora-Video-With-FaceUnity-Android/faceunity/src/main/res/drawable/icon_beauty_shape_face_short_open_selector.xml b/Agora-Video-With-FaceUnity-Android/faceunity/src/main/res/drawable/icon_beauty_shape_face_short_open_selector.xml new file mode 100644 index 00000000..fcb3174e --- /dev/null +++ b/Agora-Video-With-FaceUnity-Android/faceunity/src/main/res/drawable/icon_beauty_shape_face_short_open_selector.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/Agora-Video-With-FaceUnity-Android/faceunity/src/main/res/mipmap-xxhdpi/icon_beauty_box_face_short_close_checked.png b/Agora-Video-With-FaceUnity-Android/faceunity/src/main/res/mipmap-xxhdpi/icon_beauty_box_face_short_close_checked.png new file mode 100644 index 0000000000000000000000000000000000000000..c45df41410d93ead895e3c1313e24d663e05ddcf GIT binary patch literal 9780 zcmV-4Cd=80P)7}Ja;Kq!T><96jqugc@|pxv=*=y$mh;5 z91D!v8vp>RoL0J4*#IHD7NjmNK4v3s3>fHC=_FmY{78=6qgok`Q;J7%DnMqG672YZXARv&c>LLUh0F0FY zA!jawwZZ2GjI zhA)S=iUNcSr*H|tg!cywBX{_!EAxaH>lujROuDQX0D!|^BaR1zOznplJ_JBWkB^R@ zzcPd2A#Vf_qTw;R2n?Bb9r+q@8;f@d2Swcp-pV=AR(MpHFOwRRn#WC&EdD zaOZ(O{hpXCh`B)sNL2xdg{{O~z_Ui1hxi=O3YJcK1#kBz79d!lP=_F5t_K(<&)492 zvyTKY&-0MG4s@AOVqCY20SL&+om*poYXgR_Yiy;QS}WQG2{T>;VXtlRJlE1_YLGUC&3CM*@&1 zcgzMsrzn@7MR@^%fI{6E5p(_L3S@b<(+*=fo<1&@2ndn4gj7`zf{ih^qoo}-)e*=- zHt`5t23qaavXU!1AgBcrvSDSN`1v^TB*0_#5YVT`mt}^s0Yas6M?7c`AXJrQ(&uAD zmH@^&o`pxsa`#adK(MS**`An*4?w6a<3*onbmk=a31nfss*xw=ciu60y4k#;qkan{ z8N-%V&+xosg-HNGe8H&IQWLk;%{D+#=N1;XeeTa-wR|hKBLrsN3{L|?R;;y**a8R_ zq)>GP5UQ;u{=8o$kN_cCUZ*(-0LZe28*2dpIfY9z3`xW1Zg19zwNmvV41w27x~xcR zESXjU!r2YUao6V#v6X70t!D;fJ&(Xqpw-e)!V;S`fPk!%OSekzDvuEGXc+3^R=B#&uOSYuA-HyLm$ht&aaBgz%=E z>|||n+>P{@s2j=u?Tg8}b5o;DO)!`=>E*f)5Mx8_L6bM(bHJ#*RuZj}0ijl_k&*8N zAau3|L*=32*{qXWo$#SS>VSp;ifZ0|5@%N*2C+05h+P^X#B9vYh}WgvkIRmWzJ2H5 zmZIUc0jC z+uis6{qufd)OiHAb!j_!$r}=t+LN+EgEVyy?%zH+?}JU5w=QaEU!`xLyvvNWBV}$L zK6&gCV8}|1Ke1r!`t+zXLI94;#hvRmf9vb=YTk7Kgpl^h)nuleT{>lL(&_#7_ps)9 zEZlYENxSR0|AIg}nJ$m+Haodm)LWm?&_9iJ3PM zf{hrPO1ドル}3NvlaSYSPpAT%DdbnR0nkt`V{v%W@JalE|eJrHh+HC$zCx?jTAY>#E zAqj-ANIwor`5ドル#HJ(t%mI*=53I1QLgr03E@Z&a(-H2m=;-9EVK=aaKX9io2^pSS%L z&*q)_Ki=p5*?%`3{Pfiw^zR*~t?pKYRBzHDOrlUd8S@DtG3I0^>(i3%h+_+|Njizid>CD8WMpSslPHsH;| z4nqWEbKK!AtB=oqM*vOe*twzg`n~r7goQHU5Wpw=w{P>IPY3R_ewS_PNmz#bm#Yy1 zi=Sx40Rkbps;{H-GkCS{>Ct9<#je(e^*wgcx?c~*{-_edprzlv{`8)m38(ko&b}l? z)h69G03O>Vw}PUkuguw}zS1i+%+I%d@6Jx1wSpmpk9dp|LQJ~Ugd5j3eE)|!u(7Ag ztF{+}01^UlM%?v#*VaW`)TJf}cL!4Un$qCcK5nA+Yv9X3zz_mRRzhsTwT%&x+JxIe z78%aPjSC(!G1#qc6Mu$*i1x`zx|bZadGUp83U09)CON!bL!VMlD9=R$LZWJbI2bAx zKTps_91MQ#<3>S)$G^?W6dsn(2>^ufW$CaZ-Y+s08jo!U+PIjmNPK7$bVP;tb( zS)Xuv|CXqA^G~E*KbKwZ#g~(SIv`lpc*OtO`n2mk6hbJ(n$V{gHW_p79N4_})cp64 zm2(Qm569Td6Y~jRFwp4og7!EdAg6E+#SqjNokN5`^HH-~)a(1^+lZ4EINF$V^;xm| zH>^4H!<5rhqzgei8zu-ef}yfjz>h{Dxxx(sdQ23M!AEU~h=}}k<|txsgd8epuc5fl z(I#E?xx#lGj{`!A6sqoyxY4ZLZJ<}nvgd1dro*6=aha$o{@rx1yp(qmjzjs><&ugm zgAoEjgFbA~aNxKRQfJo!zRkJ%^vl1^U2=8fw>0Lm>NG+!^KU{6j-ZbM!pX@K0n|F&iNUyWRudE4@=j zV)t&a+T*#?7r%G+?a*fg9e;Dz>Lq6uI`~ULy{fd%+W?3cY$Fc=Ayd~z2zL-kegauW zp+e!`{5ja8QOg$q$TJn|Qxl_ij}M=(OUX0nRHfz`7^3X@^`8^vZq@5r_|_!cKD+&m z=HFYpjxMt|_V#Z5!Vud3_MWvXPtN~gd54Lw?sK-m8+O1T34~`=^tm)U_LS%=?|-q>D$lADlYmFKeurUBBMk$E#KNAOR42 z)+|3Yf9&znKxFsN^ZYLVp$SOx0HIWRARcrC45P67WEO&mgM+umE?^bP>cZ23H|wu& zS+?xLiq9{IHm8FK`-KnmZZ~P^bcW>w`Q@Z0UOV#Th$ZRQ&X(%f+I8-qeY{969930ドル z&zfZ?=Z~fN9vs^+&gBs}1hm=*d4O2w_29P9RA|a8Ylr0AG_@3FIJxKu} zVV#`A2mtP-F<;it>%THOpy@l)6ドルIbQO*&1|roO(D%Sx{@j-w~2PG79~RN~|;IQ3B9 zi7kWczGp~JrR9~LXzooqy4Gmk(W`dHejQzE)o)Kbq+UJscl_A{7vm0WzT@auDFh(Z zoRfW+0z!`D%3fkBl&wCDXcN$D_?s4`D=qrM{@ovRU0oSBw#+;s4!*J`a=f2?(Qn2 zH}(ct2eZjJ*+xs{Cc=DUk z1!m!b#Uci_owTHbU)KSzBbIxl8bEG##=SG&O;~*Q@L!1z77lg5-3=8Ag;%bKi9e{r zZ1}=$Lp&O_?v?*2CFfR5n;E@%2~FtzRJD%NR)^Q>((et+qEarO`g`Amo*RlD3w7xVVa4>0QDQ}F@MKMb)BG2 zbPnER(w>?(dgh&jkGi&tHNPY2HXZd<)1x1)keo)76&hpc=i9b96~}|3dm#<#h-ehc zT&sQ?>Vuzj{@|7)UyRsUQFl5B@Rw-T**0QkkdwOCl=N7z<2`4ekq2r~s)y@7e9owe z;!phj)SNIZ8{S~>NA+5~`^D#$-!=E+&Tk*lB%&lxdIYsnx0?9fKz(Xb%F(ai{l}D5 zv=ZtFU_*x7hOAQEi4a_H9JKeuc}AP)-|jzgPCf(tu!y6wbW>1O>hc_GY>WK*S!K z_13pfBOn?K8dtZ)sF~9(GR#Sei~MVNlZEz{sc7^FNQX$J?h7y$Etr=^Vudf*@uAbh zC&W_=h$e4N4+$PNvEaTY`)S6UO_*Uc5Gh?~NSfWEs2}H%>JZ@zw~uqG zUaPU7Lu6;fADu&Why(TyX0^9~Y@|oyO9FchA7dGoe)Hn~U1K^|9RnPMxez%yQ{ToX zVi|3eMX}wbZq+3{7Vdn<*|%o>;gpf9OF#bgs5$Y+cP80uj>q z{;56d4^A6Q{S2x~<3$gqb(`z4vegcd^_mim(ljxc)piv{v)w4i+v8ncnb{wipmgi& ztKZJTZ`7vm7e=m}PIEg|mBx$y%AgOQZu7SE_iWzjWlL~N)W(Pfkw1QUv8qVP8a`=e zo4%|5nonO1{XHr2$ku-+ zJte*n`GTkJ^zEiBox&+pc^+XVqjqE8TJH<-7fdgqn~orhpazf_arl*fygvb@b$w#} zE_Dy$qqYtW{-&zP$r?VX*}CB?qen5EL#hln9 zxV1}{S(~OxlrF+NG%)Ay_c~0-j&p2Kig;^8pv&wHy}eq653qP&$Di1_=J534d&Ha5 z{*)nr$)cOC1-20f`~Gs~LzTCG3(GQY?q0L}%z_H=Ft)!Ofz++0r2E1h?>hSiwx<3k z)H&wdvj5GB-m=0fS9}pzDicY;bj>+Cx52#l<&omR_xkcAZx z&sZj5l{APx0mJ5Ona;|c1pUE~l^T2OtGB*QzF4))^N=C2qF;Yo1nFES$f@_zLyIh4 zQ@l~1ePZqhv+f?=;%G+@y91)d`*WJt??3t@dWxbRPiZ$U>^uDFzzv29ytypPB8BU3 zu^nUzGuHd3Q}3y3HxR-F3Bl&O9ドルAqe_T>ngUA0QL{u`q|lUMVkdB6obOYGkDznrRq zuS1SE9(9Ndon^P{y)w!_bj&x?Ew1X?#JKYZrw{#6)C5~1L#_TRqXI%dnD>Qc8fu9> z`sEw5?w{HBz^bVc2R3ドルNb*G*vWk#Fz@efGH@r>%(H zT|pMy0w-GsxZxMyQQoP9;?d6(PeXHwqd$ag4>j zLenE3{%qjn1S_3?{JRhJZ8Pz^DGVnO>@)hz)R@E521cY^zu@q^TKR7HM;sEUQ+ShW zb$+>Kn~94ja89ZM+ebwPsFW3eoWg|-U$A4iYrV!{!cmh3FUMXz_V>H(R4ドル>Tvq?wS zj?;ggAa$wcU(lZroO0##uA^TJ|I^V5Y2iQ}hlKJK7Ep55>Y)AA7?DKZ*7NciPVx@8yI-l8~OFOspvxt~~evK<_eomo6vrqz*sgm1bpfkwm zWeiP`aCOt-B^Q7B>QZTR?D+X3p?oyhw=TlCg_yy8(g~$SpWk=*vq4rJHEB+%E?=(gQ@wG!9t_LL zXuj<{ibb7i%+0y7a!1q7@adj#q?ojr1pef6va@em(g9s@py3ww(1;xo_ynoobr>re z9yWP`MHqapKJ&ljQy0bT_@&f{>B7XQN0vu$TX)Ud@Cq=I-ef zyJ!7vo3hs+4tDzd=WyQ+eT6hYf;dgN*^#SeeS3Y=Vw+=49*OPTv1-Yg?;ISlMHhgv zF+UwZibE5I6y7ZAI1@g1+eo#4gZ2gKg)$$+9H05t_sJKJTD7s1Uf*BOjZ^sqgwo{X z7gvA1Jo?Y2C1$asfkW-4Ea~FgvF~8WAZhZYW805>IdZ$T*=QRLnNUAu_~g;7Oi>UE zn-qC;>%X7$-&#^_jpBe>IQqN7bg3s2hEQ(hs&+ z4EEjKIu&cUez=a-cIwnpo4_fAuP{ghQGJ*YC6 z8F%gY!D&N()g|63HH(elA3FE1_nm48?tpnYvN9gr-2Fk9xi&pz3N0*b&gOnKE%_n- zS-tf1q7NOO4uI$JJz&rZp_PS_j=A_FM1&}0N48F1{MN&#eV>OZgE2P6pZsUTu{opm zV}q{LnpoWy?C9^=q(ff_g^~t1`|gd?XP5nNQ%dB~G+ZbXE`3dSwe3}-_51V37w9f# zbMCGE>wiAIU|gx2K;Qcvr>*MgV@an7A>nfk+Ka1ZF1q%|4^IU2S}Vh@-)!#d6&gN} z_OGafA-ox1ドルi4wllhxsA2_F{}eS~*kv~!4iLm^03_}0;E^LGwx-e8USSLqw5=r(7| z`%X1{8(Z!YaTV?&H-mKgZPW-K{Mx$GuFIsan;WQg;9I`uca#@SAJ2dPon<|!+cx3 zH|t%gv+Kh%J>4?1*+4MnA%i%E*5Akn9DS0c6ドルP*iboQGYwfAClC4ZX{XgP6F+ko&P zuOrFBoZPyUgs6R!`+Qg4JPwa!q1*HOic|B)IW!9$gqYR{7Ye#O_JjZcC=@kOUL?3^ zsfCIYe#2MC28N9IWHjfb^0f59X0tYGL&TCRYv)B3e*70j!`ZK)BJArw#>%VJs9Qo^ zF3cN^y0qwX2ae2sYg=w=V##_6LZJT3?*%q}^YihnOztW?82}(AZC2vGDKF0|OB&bIqk8V(ves&L&5nw%_l z|As%T36Yn%xpP57J`SngxP3Dx51%@mLg@+^#1XSG*QCo%&WgW%E*;%>)${Ic5k;J9@)~`G{cZ}l_mlBY{ z>2ドル(7p_t_705F`~ITSOXo;8aRqff|)PZ|XcnJ@-PWCbdtuhrbxU3z$r$ zDw+y+Z>fu4T_xx2CV_}!h$$EAGm`abF;_I$iuB1ecSF0$%es2E>GcXLl?j$e+Gx>~ zE1SMsa{0HpSITn4EJ|LjCwqh zI{S2ZX$WNUD(DEMp3G+LwLh1vi2QMeUBd?y)qQ2{r~dphuhZ%deA<6|**ujywl^q4 zlfYnj#ME9XN-RCT6o%tq^HH-x>%8#VU{3K+r=V)0HYx7Xtv%~^M{S6>Zm(~tFhAm8 z!~X9D1oj@;OXU;T(vtg0&>1wD@zL9UTzBc$*$(^m+PJ;8celF45+yBBHbNZJ-1b@Q zI07LM&~tdLW^d1USL*EcNXZ83gpzpS(5};qK008n0josL5CR>hEbr{q?!|tbTv5$p zIi+KaI&IRWRbMT?xpUR+(iB@eu3mIfJm4>ZoSY{v(26@vut=`#2N=d-cN@h?pmx_+ zy_%1i^NvjIQRne`n);07=*w#tt-E_BW96V@z!}@*S8Ac1i7v?8V=`-m~I&Esk z?JMV^xBqlJ{>ZjRb66n+{KJR%2EH(~jf-ESCY;jQgF);Q$=-9b)9*yBU$i=A@47fs zR(g?1M=M$u<(fb5s7dcfiu~e~wpqz@;2r?z1xw})_{sh|somi4pa0g+qhyjanfs#i z6POd_%R3W5D z;4ub<%;a3{{abr)j5jkxt?2wud3_jqegk&34~*iif}i{{6u0duk!{q*sf#0tax#tc zaW`~@m0?(2Ga+xym?iuq@4T!*PE!@OGe z=#4n3r2swFr>Df7`u3xrEs4@-X5QLi{re+{LInV!rJjjVo1SoX+0S0^3L?e z^?JWK6v>>_78g5nPIhwS@87JB$b)=QD2S8?KW^sc`H&Hvf?6dXD1Jvu;Go-a!z>6b7Yc%Q9%@R3PDhXOpK07lp z>EfY%w|D&>nR5BR48d}VIQZ(uYcphO_s1&>;i!!fi!3P&US4-)l2d*ugaTcLrX=3A zcIBCcc@=S9`sw5-mx7`y`Mn?{_fG6uf8_HayXaqfe79$)M^Nh*ALlpu%E_JkKkBuy z)OFa{frt14&^}q-xiFd10RaGHot%3RyYWla000H=NklQ+(L>)qyU>+ccLu6x0W zh7g)tGwtM}4g@1={Y=lo#ttZMw$=fRBzjvWIb;vV)f8?SF&y7J;rcJ`?xAdS~$@6O}4 z_+||tXoVFwB*$GV!!gtvbUFJqaH`R?Lv`1HkQ!2#YARi7Vn#;HrR2nOhf;JYC6{xe z*YWlGy&WJ^dkU`%0x*7X^>otx(|aB~)DIA-+vlx1P98ogKp+H!C+RmYCEq>1Gr6#S zQ0K*we$L+hYD=Gt*z2kHj&4iDCPRVw-9LPYkBeXZY5*cgol7s~7mZqb(A!cvRC;R;8A~v=$JQ0X0&nDxZRB zi)fz3zL@0jdJQcuTdX2!s}Aw7zfA3iSlm|ZVz1BQzKODC>J4Rvr-32MYEO1so)DC0 znm(YBnA!715zI466ajS6n3F*-_E)K3m{aM@z3b?IB?lKPaq5L zo3&b6;m@Xu$z|J6mC7CQpgn+4NU-(1RWb=s0~8C70FB1xY{6v%1YN(}%@;A|g0hR1 zOeD`VJ3Pl61(}(p#+Vi*q_P8oQd24$BAzq_3=%Ch@-w}&wk{si7){A#pcV9nGPUvn zf~Kies2d|qm*CSy z0rL2+*`TGR9jyw1EN=Do00gDYuu5fTLKv@#i0OE`w$|p=5D54rb50I5K9%Z`UK(|K z0fNpWWtHkSgkWzmYk-!vpl9rJLX(Y}wV7vW#%O!2-JXD;{3uwZvLzwn)e*PHqMsHT z5b%lSoE#dcY!@(4x!4;Jd9M$dsvUvY_xb5VB>`qAo;|V;PZLq)RUnHJo>+j1ドルBFsq50MIv?|8}}!$TAav>DXBuFOZv zl0+gVAoAvwyZInsYy}9BKfn7ZBRYAW00{NGls#8g3#7F(iw_9OiAv?p5)$-W*|c_B z9_syv7Jr8FvRP3Z;=(Ch0x;qItBT`p zc?E6tc#QQ7L~$lv7ENSkmD`)uw5{k^QGlSVF+izwX9;Rb01+zDJoAmZ$}{*J&}!4H zaflT|UKI%l%X%P?C>tOIXqsSIWeOVNYPA}e zOumR9UjP9OFs?kjiGvIR2*~6i9}hS#uB7lfai%YtYGo5TGUGZV>gmj z0T7nOQV__2T<*#urege@ti`^{atufklgvds3+vofw3kh>of>RKN>u{HV{7w~1DU2K z0N5Kbpc){A0}NpSKomHRS{IG`#sEVeKxpJK$p8e!gIv{p0OBk4lb|@DI7)D~0@c?3 z5};JN0EThl%ur1*?*tGdCm1UL!^rID!G!_F2FNfpQ;Z%mD96N-EP!zq&}yj>sa*by zwk}$X{!a@a3d@E*A!x>W8XCX?XJ<}kg%9(7tnwqc0fhoe5d6ik1&hd3{{j6!z4gq$ z&j@&Ih7g*7@Q*h_jJ1g8GYt}n24rW`yi+uankt3#pIseP#Moq8X#O9q+f1>Kl*bML O0000%?7B;>x*aX6vgen3FM?wOGn{b41g(H~L903~}n{X5%fNhQ# zbA+Qf0u(p}fdCaM*s)Uv8{z|52HTP)Tef9c(n{WA-b|LHhZQp#_Aswl)E)SHYSYSZUl>uP;758LFUbdQ#|AsgJ_`3D~m= z_84QLDkf07ドル()XH&kD5RI{Md-!P>|HVBlH};%V4326qAb#b$eVU|E4IplmQ%3zMU;cLFB1!o(TI%#=OoLf${HfUwpE_rv-TI7;$>gusO?bO4@} zkUCR`oy%c|F-Ah^z_oz`gapKWaQGM;3ycjM#fq8G%rtCU3HJ?}tT8ZvxWn4z8{n`R zINZSUAz}-?LL_A;yY7Tt#~7pAO5fW+0AioD>qlXOKM@9EP8IHqZYt9=uw$h$J1QKv zq0mJConFx0~DBvVb^_d zcR$!j<$&00?gx|fhws=thdjx?rlw%qygdxla`4ikp=;!rshot;4yr<_o2x$hd~@3m za=6PFBl}9}tr9?3YgbOgF##N2`id6e014bT2HTA>`^sKk>43P;+O;d-ghTWOQPlo5 zZX%Pgb&WB*ih60Q_ml<*e~vzb!2`=&z@hf;n!aofg_l4;f9i^i7bfaj5m@gnkczm7 zmAT%e4m*}P7E-ueQ8*xUGEBn*131!~qRS-Cq5AG5fqTc`mZDFJLIEKk1rLTL;jsd! zTG%}bzbn!nQWzjS7)}ge?IB=?s9?Ku4Pg`5JqEX$g3RiL0K&70Qvx__2@JV5vdB&; zC^i?tJ{AlJ36RrpLICTET;JH1S?AU}%*0YWFkBy1dkqn8B8?l!Q5-s$aQ z+d`ag77B><5jE*prV5SBVGP|~YbPWhEM4r5Kqv&<7dts&f4;jryxaul+sb1v%v-apw zXRKOk15SY&Y&FK*(akiw0)(7fR>6r&{tVraxPN^WFtiVDTV>2Wd1sLu5ORB)fm6Ik zbl&$b_3J+crfaat7-ndtQDynuM{pu5T&Fvhv)ZSemp26Q}t(F7!EPX#aq?2h41 z3wK+WrFBR3NpheE?giZ6sd&~<6ywfws9m?}lgjut%jm0`$ zae+NmqyrFZxyQma34A?qevGp%fFnXU%ffMhNiQ%1H8FIRa+^Cwlq`G`A*j zp}d{+GDWqIz*~U_d*h(qvpsfwdbUT*10L&b9oyUWX$uHNVb8#W4Xh{#ow;9QATV&4 zfoBBpLSS{9hb03{E6m=&wEzEI26iMaQHM5GzZaX4JRvZxvo4NQ>uRJP@~oy^@)14% zjTpXb;V#J?+GqpMX)&h{MsS&aUxQZyuWIg-z-MCkSKsH^z}s8&(Rd%vO|ULHTG+o8 zHlJeT>3DE!3y2A8*NwSjxucxX#e6z;6#xSVgl7R8TRfnzXd7@-0>1^eTDZf40%iBk zcf_afk^m||7+7yy+JTM6*=9~N?nG>ri$Y)3Wifm|gueh@+e90I@B9e<%ww8gfoj^z z1w;vaG=}e~PY6#4;DV;{#CrZEf-iR@R&O2b3~Xr^Sln{6Wv__fVOqI9xko8*jN#qb&9SlRI@ zI}kV%xKbHoJUh^d^hbc~J`FhoxUCLX=qpWGMgWfq4zLXU`t~|p?$?)R>b>;QHS|}l z{3cogV!yQ;YHqQWG<~rfjth=#&~ctn5m8eaw($|ba7`r>a-a3-0ldbz%@TXb(n>PG z*>$+CoiJI|JbN9d4!18i<{(+(av4um(<$!gek-7>ciuI4D)4&o_e@902UjQX?EtQd z`fwH04IzL>1@K(pF-?lOi3C2Czz-zD^tv&9d~Rwp+}ND+>L5T^YY&f{WL!y3ryBef z@VX|!QAesygdfR_+ee}`h88OsaiD!ehK=a-65IMj1pm?RIrcEVysPBGKPNPB4j{bE z;&^Yf=wXcfk9aom_yFF8bKOtdt^_`wz)uHFlNdU-b~Y{|Ev21=z_-`os-onX{8U_6 zPjBPclC^O(1438Lhg88X0yrUr_X8(omURp8u?T*#u%(vsvGIx{oK{scZ8 z!;ecFxZoED@X!$c30UPpt53ドルiNP*SOP}0PP?&FxM!)r>uZXPtX1a^%ZvpN04EFct$ z2RyuNZzx^oJgbmzMWZF~#RNX5e9_$yv!8wSRgb`nfLCN}J@>_MW&*#q@UuGnL*B7Mcu4?%@B3d6!4L92TU0-P+i1AiWdR{y$x_4dyv6d&V`ejPq0X*Un?2DQ%!oMYf=etx=!%6RqpsEtWHyN(+Cjn~5n5a-AJ(P_x< z@;bTfH%^FqtX;Lnv5V4jMoKy#fq%mplhhJ_12-LI%tQka-m)p%MQQaXYR*`cHR*1v!{4Tco)Q*W z*(;^D8F4z5KlFo6#qjzBI8V8t+kcY%#8xXxc81K&3+NWivnljZkW^)_&^N*zG%?k7ZSo- z5Y|=Xfhm>$H6(f={*@*sH!}HI+YzN=vHA4@8c-KP`Q-Ee4oUbeFaE7@roX zlsR};^LOAJQ`Ppy@S0}V_EH9~_@yB{E^r2~wEZY@f@aI65)mQ1%fOSp&*i0c_;SB& zIt>^7DQlHmvNV;ofpKZ_fIsn6xif}0E~Xnsy4zA?2k@E9?*BXjeGF}zkMVe20^1Y# zYCqlal*vvf#3@mUChumYjSp6NTS zx8^*SKLk8VRIx6e;vEUxJrq>xVv+%z9L|lWxHfXu-JxiWfZ%CsA6jN~Rx==gO) z;61pjmD=4gydie{FRVFty3{Tq1>D6kNF6;aEvW(!BO$yw6D>U*!z;V-I~*2j z*4*i!fSyK6;H(&~7?$~K%B8am5hH|G1@Q9pBue0IG5pUE5vt=K38!d)s z583s)!7@q}Dn*zY{3R~9Mfn-j7L6}Ny7>Vd7di)^)FW(99iBF5(P#4^y9QIGUpqY@ z1UltGP#rF{t0Q=0$IotvI(W6U!u%Rxi54!ew@3yu$V+NmJEyHxmY7nDv3vEbj67+I z0U2?g6+y=|d4o*A?A*LNy*!^99#e`>jo}-^GJg-^y|QJTTBf&@2J*fF(pEk`B~SwI zi{XdEM6h;qmAxi}_Zaw7@81x4i*Cm>fP=LDhU9ドルC+LPqHQ})Da@aJTdG#0M%0XiMC ztCl^tg}z@Wp!d3N#G6>ylnkLZHZ^XQYW_}%ZD92Xe47KVdb_-5@|J~eNWY&CL1_&> zm}Zi*@a-5b>Zj$h7n(H8-@C0nY`IHYTzad)xvBpkpafhO#_+Au2QGPl8hi@)eLswR zC_fYpAv;VRWJ-6TOZOvyhv=@J3SN@LeF(iWu?%IaIlaT@GLx4kR{jmS>Qwmj0!QgI z=_QpwfkbmIbX7RnX4u9`Z|IW!L`}Di_eAi&1um?zS9xXBxn7uTj4O6gpX)U>?|eMF zc^ja}@eBH>!JT!oRo0kN-S9JjP&nUFd~ZvkT$FKCN-uUxC73tHWoj7D#Kb>u0LbRaGcomO@QA5%i}mG03g1 zZ4Ty+6t1{K0H^s3GU?ZSm;btFv3ドル?Bk_VFld|%Ka+n*}wofE-T6)x#OC|`r`S-UQB zTEx^}j%lJ|qzAy}>2_y)HaFIEXBTG4^z7@m8dA@A#g0D0H&+{4m z)3Oe_p-8}fUj)Cb5D-ZXZZit@^?+FAv&WPcz16(98ax`uIvT{VaIeh83S;Yr5OTmV zuJqF$DOU14D`C*U@zRYbLc5#nH9pAZxoH|8Kc0C0JEI^83A)Fgm)Mh*O)r!NT_;u0=cWm)!-k2C-|}g=O=Lee8loe&y<*4&acz7 z_>yv5ZHAj<{;oo5^w zI=r`CkZg;SMqgB;goF3!R*IswQhERzblm&2M*1LrC^xq}S!}#J;Q8JiFa_~-b$C-Z zr%ZMY+58awN(Uf;()dht*&I_x`tr>MpGNb)E`;X?E=6IQN_i%MPb$ZCp2XrIygG16 zHzfZkKlV>zSIA^eO)bwRbi)w<(;lpdplwnsym3i&{x&h$`v|shdz8gc>ToK}M}ejc zGJQ!HG7YyVS%~IYd0N6|;`rFlGkuV3`qe4h{Ka|Hze-~eI$eM=)aZF_lu8xIBTMx( zr2INeDG9u_HTB8RROAGEVW-nbU<%+lfnzhn{zsmnb!o?{wjc#w(ufdvatm$ z6{hf3L{S~_z-?+Hn8!B>iOR~>(I>Iy(j&_`<+rn6ixupuot~*5euru<3on1kvo0wh z3YktvVhB(1k=ogu+*d38;6n2_G-aWyJn5L&>$ovDWn{Yp| zfYiXMXp^?R$|0S&f@t%WR5Y+^1jOG}p|4$CF1QZ5w?dAUg}StexxO|lW_BzOh){^B|mSq3n4t(XPci5KK7OIJ{{)>k(bMV0FO2B zIA8cgo2b)4Rc;ha`|AL%sKX7NBCNg4@WjBSQB2v4z_2-n4LY`46Mjz~w&AF|5nqR5I*E*dPUNBN!PZe%U*@P5d zs?4*y6A=9KDxC7nz!g%<=h8ohnrjof2`92ドル$frmny1{>YslC=7 zG46`5RZ>8u89ZL`Bc3fM-0Wt^f8r~N14U`Rm|lx2FLB)_Qbk>(Sc*?9T4?xyqT>UX zcx)n?8#%s^?<&u;@f6&$+l%vk$d)pme%x%_od#a5@(j?4r1wb@ocbzt&cn5}abvm& zpmvn<^1>OR%eE^;S1N!C465Uy$~jl(q4bdS$yZ`Yi`j4OF}1nY$jaB~H%RFc&o-{Q zaJJp;3#@*p)0HvL#QmxCbg2ドルl1~MDkczX=z$xkv5yUH`(AoQujw&lhglooQnGL4F* zsLZy}j!3G!qiTI#NNi?fs$lbpI^5dn#?b2h5<*p$f?9nc)cdf>s*;R1cvX7k{s|?6 zCh}z(4=PVRaD`mkY}{HEb`3z4aI=afb=M8z^2Gh9?o~&3Jn228^YFJ3MWr{#rAPVHArJgG**Ek>5|sNSziP^|*wMLv;Dl`YPO27uT8E~WtN3`mbEVx zbbUE#RSKT14(#QfiL5Z}s|nAtW)DL3nO70lBM{|CyWO;q9opV9vIE^=?eZg?K&Sis zz%(BT_Milw6u{$rh+r?E713`4u883$QZ}iMsD+nE^XV6?(?_aZ<&sj6isjuo23~cr zl$TVWTVwcenr^Y?5~Q4I;YdF*_1PZ7M^c{;)$IygVfi%iy!yz}3Si;=V@;D-X2K;tXNg zCg7YE!Ik=D4gNV(3L)*Ml*F>VG>W>`oB}H?sxsRLxF~|Jd0@$?r@CFugMB%6u^xps zWyf7%%)H?N9RZ;kO;pT{i~t%qa`MNZ>LHH?}gb z`&<@szogj2`aahmth~5lbethe3l$&zkv3icq;h;_aw1c8rb>mx?~mc^G>dNtkCpwV z={Bc%s`gf)K9w@!jTtkq^N{A93lP@Y6_JbX>iab`4}b(-8OaQ64BWAPPYm0v`_ZkW zZS(Sh3|bAAk+_61c>!UjKqa6{Ewa*SvP*yOi2?^as*UMrf5`<1 zO~VVAlGf8KFg?v~>7mLg&{2@g&L}q^^pFf+t3c%(D{>P`1-%49fJ9|Y38LGSaSZhOHqf5eC*MRD7w58rXQb1SW-HjSr#&lIayQdAc z)~@s(+5@Gq>XCSh@iE;hHXCC)`inL%z9&HFC3~zreA&Tf>BUIgp<9!`v$n}v$-9w2 z0igk`wMYAEJf)>Z&byGMehmsJMsu6#pp7h4hRD85k>iuFam4vxE`g%Is7&gxW0?a) z&xWxDoe=(%kdP@SmeMoX^_Sj*J$eH7jw!!`F$>agE*KCV6lMk<ZRs39oMNGr(-8i=tF~`1ah_!av6s!wfj_09p+%1CGIo~l8!>;?_?)l;;a=xqU zZhZkns;}^&bQ>d=+~bgMJ^{HR&Gr?oU&u?`z5yaT!Tr{5sKJIq^d{0^4~h!w6hxDE zQy0>2AON8S$g6t;95w@oE33*OAYaHRH8~2q?u1?aU=OW;GcbTiCwH^8V*?6F0!b83kGZq%<^l${za8bcbj%%2=e+oalrnp3dcnckcds@a0& zf_~(~7}sF`KG?klb`SbV&<-e75w2uhcgcfnjf*j&jk`)c%mfackluz98&^bsc^qs7 zDVP~|`Lq>LK3SD|xhvvy*?Ca_BD-dJg8P#qJu$)w7+(d;reS={oh0E5gi2rRPnMMR z=qrzBARcw$_&WOIF_@Zw$yF{lwtk=d43Z~6JS?mrFE0KcH8gA`I7{XL00000NkvXX Hu0mjfwdE`s literal 0 HcmV?d00001 diff --git a/Agora-Video-With-FaceUnity-Android/faceunity/src/main/res/mipmap-xxhdpi/icon_beauty_box_face_short_open_checked.png b/Agora-Video-With-FaceUnity-Android/faceunity/src/main/res/mipmap-xxhdpi/icon_beauty_box_face_short_open_checked.png new file mode 100644 index 0000000000000000000000000000000000000000..edd4be9e91f0f99dbf2911c5b4e6894fc38221c5 GIT binary patch literal 10062 zcmV-UC$ZRxP)Xy&<+3h8ix27}jaqSWr;}QII12j=SUTcIN-h!XYYUf!hPr`942{n|<@%o8qg8nrx|*c$b*o00ico z!cTX1R%I|~tPJByct%4IBLjqRgb*1(3LO4MU5Ly6)X24?$q(33FKn9y3#yS?` zbc0Hz11Tw1000jFz6#{(Eit$ZgFr)zhEP1aOyANe=f>i+!N}y19aE#0o15h16;9|#DfW7tk zC_MrQ$Y2P#Zz1L+U@|2G0E|)(S|0k984w7_G<6xr0|3ejfmi0i5wyae#n=owlg(rk z3^40&005|QTIpJ41BCEekh{3}Sk1UGV4zc_)0jON!q~{lP2Rql^MDu|8Hi#nhRi4cfWu!Sjt7K7>xURV1VBi)kB%R| zGK1j(Zv+sc<1xajoc{3^`5jnlgs^grccsky(@j}wrn24+ffwik$*kak%sbqyoi}r| zLZfkK2?-*E@s(VnCvX4=al946%Rr|~kyH35i)t)vv-X`{_Ud=BV^lTLaCZ}%n@AXuQ%h9F|C2N)*T*Wg*R zj|MQ$^N_m^3>gtpT(?UB2*@d&TVsH01BR)bw~_Xlnu?=>u@wS%7ドル>{{50U>AA+BSq>?<#axz5jh0aq1u~6kbyD3_l_c>#fdO4}F_YyD>iWO=634$nT=eOxXND9Q&2xuzasNYiIqOFK-cJxE|| z;SsnD^!lr1C0BMp&=W|=hL&~UXU|Ee0Uoo5fH5tuEF+W+5E_j;;z4@=p{6W@K6^%D z4Pb2GS$L!@cOPW|1k0+`?TMB60EEi&yu{jgMjr4yfh>%(>h;vfq&}OXTd5~huF=%D zV%Spj2+ulJmnMKN(Tt)+`{6v&-@ug>$hS%LSW^s@H8-G#)xI4G=OkH zDosZKp;j#MXZobSgN;T2qnZek|BXAVx^)!^Q*d_)LkacqDR_R^k5dt0!Te!H@Z|>n# z!^eeFsO5}YDd$)@XXSY-HfQHpO-8dZEkS?(MtE{c_=9ドルEF;4ドルGdr6Zb#pe*fWNUWj zzLFevO9lizjjTjoPn^buRl3!bYyIkJYBcRy+qG`vx|*7`>&l$nydi|5<39-@yd^s; zNuLyZBP}Z8M$&)#qB3va)ag^=O%`2Rx$Xm`m{5Dr1Xti2o<~soh zo$bL;d1!bx?c`P`Y*>&spkaWjnzx_K+0}U` z8!QeG5I8vnBOZiQWO$uVhrXUc!zT7{_6cmv%9UEkvT`VKJO{?SnKv2JbO|xnu59{t z_uYU0yjKu)9>Hy0+D=;Xx=f?>q^!^&O~d_r(I@A=zbWI^MIG&{_6<~anympm!p*}c zmt6u(nJIB6=8s*U7I8)hz)`rkbKT}`eN9=-yAFU5(mpx5jO4RRKUtf2dcXZWta%;_ zcOBW;SBiRd6bA@7QaN`65V+apD_9C545whK1%_xs-uS#r_2BlskxW)Z6ga`e%3BD* zW{j;Cg0b16*QJ_uX{mgU!N^-IIlLvu3|Wq4Ihh>ElyaHc#Z9JhafgUgLd0?qGBSve z3_@6_9|xs}VYw4latJQlh>`{@T4{rMTRq{AylT)JRb82Au(rXB^gr_Z)YdQ-qqcUNyxZ;{eJ4TGb!mYSM`7w;f#zB zP<#2sjq}sg=2|dhy2d-eq(eq)sli&qphn&4fdek*i5_yegfg9w#oxs<*3lz={oy96 zUah(h1cc=4kFF{=cl@&azr~ZN_Y!FP*-u^SbQ}0aL5CrNu{HMamet2+y)A&I#n?G5 z>h*v3eFzI=!Xbc<|8l*sl!s-ws&)ii7e@l851tm9$+k3ilgs{i-;cj~w$ne6q9fn) zE#bRIScd$UqZ0y)A8SPc0wJZQpQH0Lc(w24(PsRjcNE&{bs)>q>~Q(~Q6tcpa_@TF z={*PIPw$P+x)Y_>Cq|n9kL{9MK~>XN;p|gi?bRaG&$oS_&Q6}Sf+2(td5jZ6EQXZ$ z8`m~`|A#iPv8TqXwiko|5(02~?Df0X)-AedNQoEj4&?4N<-xcf)i{soz?xr5aq0@j z_?Y->8yAJ^V?US8&Hz{KC;tN?6+}HJyjQLQ+YE7bctyzA0!C2~eTWsSk z_yRCLe$P=DAe2bu9GaK3qa?a^`MmW(8Bbg3e? zf^>&Kf$ASRu!obUZ!m-pVob2bk{x?&`=+zsPdXwsZ!fNo_|(b6J6Mdejj4&(whU?T zb%}jxFtI_m_tBhJj{`!XsD&Wg#!)$)+D-mBv{r|{gAw<@akyat7=l>ImWXxpPL#O( zl_+`nJ7@!fHH}C7udPqJKEoh{LaYg0y|Bfcefz-XwWsF2cdY!Q6is2+?v&*W%Zf^` zM0l%VT~GISOXLS*D^IK^fXPIo%k$f#fPkFJxdn!xeu)Ij+W{faY}Bmg_4>W>7UJaj zjyBdDV`j|4ドルQtN)@X2Xv(U*4^dp;jDVp5~vK_8AnN|hS~bekw3gOAv-Xi@mDGcMW7 zo_Un#SN5c?fC#C?$bxy^xV~q#QDXWs1Vma4S?3DgaXbnLIZ|o5JK{#OcDF%ZEyvCq zE7NH6Bf`zPjJRuCman<;`!}v!!xtmguhtvdf(c!kpy8l#bjndm`fvy*ja@sdo3rhv z#&2oNrQMq(uM7dqBqQ%8G=B^FFd&?qJP|im%I0fwg+qwTKJ-i#*j5V`9^WLS^ zvsXrLU4F~%8nq`u#6bUUL%my#n>UG7Itexp&F-O3ymRr$^pPtwZd?!!rb^SOt%5Bk zQq&65eEj@Y4f?%3R*X>@y>H`>C+ECNV>HAx%wx+DU^M3YUp@qgykMK!Vknil568i- z)7JF%YSVL=jRv+_jkor!{pHM}52(vmm1y+`jSX!2*0hfV+lLTHZHm}KTUH&P`6l%} z5Z&lWZ2M1_e};LhY0t!-GqR$uKXzcG_6<~p&d}qm$-|qv9~6fo1^_ykkh30k`p9kz z*UpOaeV|N2i0@eS`Xe@m3jm_F0;$tpl-e7r{>!5So*VhaI9BOY&BjF1#betJe=%&U z#h?>$sw7(1l6Iu`7@ep6-n(YAE-ylarCKqkMsCAF@z{&>Nwb@w5(->toCSDc#rzNA~&!ua8N5(`W^0f@F0 zLWWe%-K9Q^>NI^rFW+{(UZn%mcW!2M#L6yxLYMl3D}g|=ckjSw6z;{%>A9< z#EVC_ADle&FEOW>UBBMk*Q-_7U;z+&)+|3YZ|w1sKxFsNbNw#=p$SNG0ijlVARcrC z3?pikd+lh0YN>gn`21m>14?+ zb!Ft{`IX1scC8cK+4gwi`6FBZ{iNTPGLoxYk0B0row5Ffnk_;Hz`Qg{3Fi)OKJe*F z+e%_Ibk3IkHJgVDfVlC`s-Z8GZK7GJ+mV%hRpL604%Few=_v4ドルTbru78eAjBypQM10u};pR1ORubG+FvyNW1JNEDBQmcM@ z+9BoYslVgS9=I5LVDoK9ze*thDc0<)!xruoq*v8o(x7bh!j;++y@$vhgrceefymp_ ze%I9%F>#WOi8%Pun(#>~5AP782ドル>H%{Hw@6m(nEW4>eBX#&(~-<8^i7q0gvel+s1- z>hD!c&n%kY*i&2TfNRw6gTUQggN)|BQr=&5p0S~~ck3QQAR|=F1GoO&@YAW;qp4rt z6V-bCuzz^91}%Epgo@t3;m4ETjLtt6&KDLju=<8ivhmid1r@uBk4XjYWj0QgU5el z8#6OD^2E-!S}!Y#2UYc2^3ZSoc+0s~{WjDGKk@v*El0i_v9qG?bP(V#(X6v=q|6{E zcdseyF@MLq&OU)c&TFeNJLUN7QQyR!`1^_3Vb~_T!H^H@HGk*JFKoZ-?!}(pKBCE@ z;y~#U)JofG;&+3LDT&EPzkcT*OJ?Cls3U+484?XywYC!>IDb26?}2jnYPCasR>uN4rw^Mlw>NbJ57R019`)w z4CJL}5Z&i*`#|O49YiHHWv1LdHuKFzNf(aLinmWx3+Vl7tu`Mmne_k=iPyIdX^{VW zWfztP1Vf~9?hPQw_f4=j(}e&c=Ge?PzkLz`(O~eny3I$;m}=8uc4BP!U&EU$u(wVn zqenm*L>g^BfU#u3yh4!4{2d=SJy;>0SU@y+V`@n7s}u9@YqIV{o!s$ei=~oHpuGt* zj1D5T3k^xLTNL%9JW?AXY{B+%PStBQ7Ho*D^thw5sSRZkAOR0+@yUX3G%X%!>`L?rfy|%(GBgc?-{OeJ( z-3^6xFuraqWR%Jes!^`Xh{s8G_y^=RsT&g$ImOjEOLt>+n z^40Fs<(+gh9rzke_cbg73_r5<-;bxvux+-#q+n7ypdg`^q)znn!ypr}#(utl6xgqz zN)wDqiMO3p7ePZOHVAob()&oR6mnb|?#D%J9TxmeRne0eKIyS_!=xqHp> zGxICJ!`S|M1ah~UvhE9ZyyNT}*qZt`Q0JI)%l%J>jn0YhhRnaV1i1pC31nG$ns`kUV- zU94Ki^N=Ail3#z@bnILw$f?iLLkn$QQ@q)jbz;u@Gw&SU;%G;ZGERHZk2Qa9cC-2e zMt?|mQPkrp^~QyLhd&#%!Bl}amt_%Bxc-*fL8c&MeSSLiuC{gqAzY9UY`t@A=elEG zjiA|8t8^Q%G4gX|HBTCdE1a{$>|OuMsVew7l6>hP37i&C#&C_b&A1WL+t!0ドルDXK%Ij8*XsOY%{CJkPvD1ドル`DY&8ドルK6>D-Et}y zHf;Wm;jZ->hYDLw8oV5H_1NF0L*^9R8=H z4bp;vIt~ftE2yM`IT7gJ&Dpwp7ドルjF2;NGb{CDwBgqijd?3GDk;?dGFre2ドル|1E_6Jr z$&h+(>F0~0{wXn&EZ|fc6gvCQLDd?z=w%B{u<=y&bwxfjc*(_ireav|9c>3r6v{_~ zed{8On@bt&TTnRaZT!N@<1;vq?kyje)fxj(ve%txsghmnnu%jz0b<}wya;%qs_xa$ zmQE-&^8CKTpAQzb)ucJ4x_q^^Z}rCQdN3@fp!v3Q^?=R1IVbzdhDFOFexK{0B&~F0 zA=vjnkd<}qmjxnluoiq>f<~l7xycke1qz)6x@x4|e2y{wzvyt`m(y~e#e9vfd{k`q zkKnfMx_h_vDVL9@i{Y8xOxEhq&r9L%=@hePeRQd^*B}md`r_v>-wyqRG(dtnEjd}? zt7d+CebeGn$C^A8+qq-ak~7~qIAV(mfU!9*9YC@}6NVI=Eb2HDHfP&Nt$%~|`RRo+ z?nfP;@#gnQ7mtdXSW2(oujj^Td;(g~=Q?LJx3#kXU>A&^1x zq)W%PANgwJcCp!L8x5UMKVI?@~@w9X~i_*sq3!+a+eP5&T2v{Pmtw4Z5ドルy z$OdOjzkhT0`(5UgYLzJFI?ZI6NHy&*Qtmq!&Uf3nU$L@kfXV zQOJ&Loxc37hfn*y4^jqWY>7Mh&xT{ONA1TZLy0x9y3OA)z_Uq*eh>;J4RF?-8>i1M z``@PI@S~}?Kqg%Jn(%7dyGHBx=8ey{U98reTl?4le0u)45+{Ma_d8Bm)yv10P7y-F z=a}>tSIt;>?T;TG3+NTA!>-?K?&sAaY!K~VQ3*qMD?ABuGKH1Z;b{pURTN#qyD!{1 z)V-k)BrAODXtw#=2R3gIbNowlICh)828JDBaFeAImlmTmt3fZ^uO2O3j>|gs=RvM4v?ep0D~ge|P3Pa%a~EdwM!$R;!6%%tHoo46VQMAn7Mg zi@JR1=&Uz3mRyAfftC{&whagy`Wliw$jNO;j*r+ksqc5?&ExP;7P>vZuQ)YtoI|tF zL5OLMaG{{fBP#>|K&7gQawEYdOD9ドルsC>p*zHZWwwr=vM1ji;>-wp#TO8x}3OvUYAn z!R5a&8qR(VRiR)1F;-cvM%`lSa$(+VHl#+LJ8)#yo7-|y5{kD|5CZjId^hm9H@+Cp zDwM9m$^Zbd=ra@cee&YWvZOJ7l=m)Q|Ix?0RrlOHm3!B&IO*85gd{Ju@!i4!0f57j zpBFZ%jbHyfsNvwTqYBo|pvlQ%_HXz@Oo+U~&7BJx`cX*r#_gLrdHB@fRBBhiAdXng zITk}!Qf6HAje95ehTq(|D#~KWC^l;@wHaEB{<@`q_au#c@&{lrxqsu5ubtdoh*czp z%!r2)m+o7?^5mQ`V)ImsqF-z0ドル@8Uy7F`{RNPZ3g!zrCxU-G2OXP4J4 zm~-iu8MI=aLmGp@4U17IkC6Z6D6D{rAA)RaM-@kk*T^Y1qJ6(lP#85moW zFQ3?X_S=cOvhLm}x%ylgRZ34$Ta2FB*{8z`Lm`t}K}RU{WVPzA{kddC_>a@<8a|+^ z?yG1&`R5mTgWho9vjN-7$ad);%PpXC5|~U66>X~&C6*qQhT%BaY}Cvab$Y%sgi}4R zDX5vKPmH~EYtQ=K5gQg=x7W8+70ドル%4;edAo0{e{Yt?>zLY0Ld2*bKUixX5ikuDkT> zEQfu2OFzS@RvY# z_G1@lMeQb7q*V6@4CAo7jiSV_-SuU!W@Bc*tf*{!_sg#Ez}Uze^)#uf$TE$z z?qmfOaxF}7ZM`O%=5I~w<{sqo)mom9`q4dpx_h_ictf~za!_t2%r({n5*j`c2Y>;MuUcf z2aj)9zu()h(n9bB`N=i_%&+6 zshvF-#6FhnJtr&ecEtLHtE2X=i?w8?6`FLkqIFTO@pHGD^xmS-FHUg>P_!6)W5k1? ziYx~IsPLTDjqUWsZ~Z+Qw(5#xLfBlPC5Ljd((h!&--<9k#wkdk>(hQOlKTHos4HhB+(thM}M_OpCC{l+GMra_N|jS(bDf(I!PjLY^l) z01ドル{EheBgci9ドルUnYPHDBcLESfmSLt$Xh5ドルZ_rmM7{Xz$}*7`MQ%Bi(AAI%6Wk}fvu zGVW#Gz8)UEZ{4Y=f7Zr!n7lI7t96e)&rPqV000LGNkl`Dv5$A=UPL6WPFRGH) z3qo@D#IE&6z8JcT{-wuvdxm)gwSN9lev_`8+`0e5-WyAlqm&(Zh%W&B+Tk|`Mw z06^Bsxd#Ctzb1Dn`EX2kT2u3tj~e=S9UQ9is1e8@PQf6S1CU{9idY&zLolDCONfbx z-nZ`XjqR&6ドル?VPeZSJ-K9wF_z=kI6;p}93vPcD3aQQXnL1)t9j(|+sj)27z|2pJ^Y z`VirUHtXz?N#DinSuflRw3_f;n>sy)j(`xcwm$T|baD0UA8ejgjol>0;iCZrLO^(ucJorwo#Q)`3fc#Co*(Jw?Cr0$^+}Jpo^to-wghZ3TFk*exnZT3ohBL$b6s#Do3{tsi1>Td9k^ zK8^DxiOkd+$_h^dQ>JK7_R_2nlxLbgpplf>^CeNtQ%V#8bkUrhPA}v{=ea5^AgF3N zR;}(pti%f-RF>zT(&=%a*gQ`l3-4L=dRpPHR27rUHlZ4gJK{lm0HKgz>shO08lVSI zEIa~qx>9EgE*l`I{7N@p#8~soE><#?jk{**9ch+8wiw{fb32<~2lv5kt!{{t@*h50 zWT}zG3i7O^11q0-+149N) zzEb9Z>GA@C3L;~bnij;tVSwe=;FCoG^7xL`q^G4FMTI~XCA~cWL1{CrTHTot#;YP? zI-ablEwwZR0zT22olOs)O7uuCiMqW2LC2A^YHb@ru(y;oKuZ$nDf^tzWTRGn##x#% z+8(mo6A+Xi6{}XaBt*J8;`R{wNudD&pJ2_-rh&?K0Rz>Gy#bN?`cP=v5r}=C?LH)e z9_D#SV7%3&r@j~EeoYkQQVKv&`YcdsLlCjn19`zN5~=3tBl}=C5m8^fDq-gyN@!WljjM5(8$Z# zb7eh&6g#r?fS{acH0~@RLC=g$vD5N!KL~NW6~fD9xtl2Mh>ih7E?07yS{=v(Cghnv zg~(}wz={aEYR*oNEbXnO$Ppgv=14%~4z6(VM+|NPAf!Hvjvv3QnsV%O1Yly|2@?)X znYT;h&rn`AD{4YqIF(BPCcJ-DaojDhqD4QCv5|o&)?&z{iOfW~y@{qRqGLq?g0jW{ zwc4E}=s5z2P>JTLZ`4(u!RLTppDM;7RtkMpBp__^Kp;~$KnTz@!HUWhHYkO{AERm8 zK_Czb^!lrgd_5E&~4bq5@bt-|3F#4#W{o4y((IcY2}U@9{pY_chVLgB$NGEWSMXI>6(D)mZG6i^(cxY7c(wEbm3t#$znT zhH(M}$p9fTLI?{G!_of(`hVIWq=ugv@Yo6=v;g5BZ-yA_5zl9sWHKGd%A$FvXcRRy k3h6()I;co7$)%zBf3-Z+v8jYAcK`qY07*qoM6N<$g57ai2><{9 literal 0 HcmV?d00001 diff --git a/Agora-Video-With-FaceUnity-Android/faceunity/src/main/res/mipmap-xxhdpi/icon_beauty_box_face_short_open_normal.png b/Agora-Video-With-FaceUnity-Android/faceunity/src/main/res/mipmap-xxhdpi/icon_beauty_box_face_short_open_normal.png new file mode 100644 index 0000000000000000000000000000000000000000..10907c06acfd658c91d804996d586588ebdeb301 GIT binary patch literal 7187 zcmV+u9PHzXP)NklK@EkRgo%OkLO5iFU2g~q-0dgGImp%d$xoD@1$Dub|V1hy}N?Z%j} z69dS*kG>%Y&kD5RI{FV7f~BD&fSxO5h{j?25Nrl^iOuv2Yw3A{xWn4P6JX&OEDm6? zfq6NyfTD@XS{NOK9V0NZ9!5?yW}@g$XVQMp5`?ujcpMHGfc+&8NC=$CM0?;_38@nm zcyvB&GR82LnSrw_^DWXkr{TEQGDSCTsKzK|E;f{1vcl0`@U5 zzmM2LyCjmZlPwRymV=DZZKdm_CkSGvwFeBs3V$N>#F#p`GrFmaPr#;y#%$_fzjeFn z+T#0LgHD1&Ls-)7h3JL;EsPAp{lMehx|MW=Ags0X#$b5~_QeT|b7l7mOhm9{D?Ho{ zHqvo|*kSFy5*YjJ`q*iL6&o2FgAI#~d90J2cZ?u(jobyx7r@G1RVa0G;WPFXg|Klx zY&OQozEXJU2tinD7mmY00qj%wjAmg6F>D=zjmDUrMNhAAg4k;9(gkq%UV4MbYyYY@ zkx^K`#F#C4HCnHo!U)3OB9@16M3EEN+uAO>TZmXlA!heN2txA0C>$~X2Y3tQ-nMT= z&Nr^Wrg@Hq4bCx$uUu>+)P zVcQ`5F;9C)ZUo_p;m81%?ge&;4s3U(E^G|jhTuMvlUY3%g79qOm;jc|5ksbp%(GHL z6l?QfA2TKh36SG(cmT`uoaJ0wt*t-|k9wJ6Mt&AEA_$!fqp)%S4xAGpTid__YA3gk z4Ks1RnJGaW5W=BzSIYvnx4kB#GKe4^YVUCuztC3k8pz|?dU)=kLRVlE*0!tBY>Oad zn+V}(V7Snk%{AWc3QUwiLT1ahkCq9-3z0|9-6-1KtByRUn}-ZyQe3qZJk&Bl?6mg4 zL1(O*D?PpfWms>Fd9amXwnPweZdn9J&iOO6vc%o1tAL@MaQ`A>9?3e2%mg8~w+T4L zdqih#f39BlS75vhtBsL{pn1te5Yjh{!J#FmZ^-ma^RDM=bZ7-^1e=B(`)F!1&$PML z+65szVa_48LtB`CH@bD)V~iPT)^t{aIKJ0izls4J4qz|_N!nupi~-vs zc*w%T)?qyC6{QS2j4^jLn`~2p@If>^=fC7E;Dk6e= ztrR+NR|Gd(*l3|4-}MMIT0@29)%9M=t4>anoOl#gPZ#eoEkRgo4-VmwZVHkCtPJ4g z22Q~R_H+UcK&<5v3%a7z?b!j-pl=@nex|b!4)f^pqpe>QU+|}lB=3a-a1L;!Uq2De zeKD+(x04!ERQmwD7dYM<2i4bt$kl1v9x)b^sa0jt*r-ljf>0Fp1RQT*K~B_}xfuh2 zfn^3>5Wp*e#SI>o3^1-RdjsSC|IG$A#SW-L8>{cdW+YFDn3h`?N2+;Mp@%%HX#_r^ z=f4xd4=rq#+@Xy&@RB-X`gjP}==(Cf8F))=ofy6t!N2)BF9zOUr;hsjZ0+~=?P9^T z8#sCGh<)j3wra07=9lcv3%spnZA5$*h49~gvtIywu*NMQjN#J}{77|5@T>qXuIW## z=U+qkdV20Z*vPTJ-prXJ6ZX&Q?1^1CU~WEj1((10VAeKSW;+4v8U+^D-E7$_LU?kE zLRyCHp1w)|2bbW(z!U4laR|rhRs%nb;BE^1ドルlN;=0J02B3O55w49N5A`8XD(lO9vv!aBc-|ZzN3SInFEAaO$mF z?I$vv*7k&@=EAd&2!=cI`fp^ryXAzo(H@`{5@4s^1)3p zd_RC2!!BF}bwdc?sR6tcczTUuZX|}!8ドルfD=m}VO=li%Ncvt6~T!qc*}F~4kXS#>i1 zn4#|2TDYqg^J))*u-5JqI?1>rm`-K*d*JOgf}@U9od`db88^e^3auhmGT?~zohde= z(@SjYb0Pd^kpp<^z}?ek?v>Dw7|hPAp22f6)RlLYT==Ji22LReZ?ibe+blA)*?twz zCQc3DLpay{q-=@dvoZX#*EESG$JWlpMWiKbCn50t6}T}kd8T$%&-e{Lms3)f)i%cL zy6P03ou#g~ajZ+*IBE$(SIsAOf?Wh~cnK~6j!F&wUf?q!+?-`Xov!1F_$h(Y9q3k) zlt*GXFM`{O7*jzkLsIVJgU%8<$?|fmvsyq%s6wxr(lzred&va4u-w6fnep)uv8z{q z>1`%Qmf+L=q)^jRI^7V##ff67mHxaoGj4RktXj=!zu#u{@-*8Qd)yN9v2%{+#~J&V zqDroIlWciP2V>Ui$|Y@JVZ{@Jluc))I@uM&CnNZIVIAk(Z~#v#!9N4y?OI30TYgFk ztX8^`Cbr~Wj0WNa0>d5y3To4A>WE>iQ%g;d`WoGtt@6Y>*}o@fL8%;O4)k0MsQXP zf6O|z61>c_Ag#ms5&W#)vpFiJrZg|FsypGf3>G$p8 z1*0LyVleXtSaaxnu08EQZfNCXNcZ7y8&x;EG(pH$ve0lmZ?XIWaF8F+NCap1y4*0` z=9b{e1}^t#{HnU$&*|N3X2qH^+?bIp5W5$?!CJDkDvI@Ull%1jK z-G0!z$N#tjpOLiN&P00UnE^P@!0CQGx`xVl_2saENxbIme4W?Bh#)SC3f5^fSTp%u z-Y%E@>Iw0PwTqTGc2O8+q@?o!_;;K!NkxL>1rc1=D;wMHI06Tda-~e(zKvTVI8Opw zvo=fc`oI~=R4xcw?JlS~Mx8ドルe{QrgM1_AU4R%g%5m zD`HZ<6vezhchrrj{plxjk>iPjh4ji0zS3+sC3s^1Z&khNZW+zmnc?s9%-%cqHR3 zcaVv;Fc={DI1GS<$;n$x1ttx7u&e{@wop`l00)|)a!a`t_rw&sc3st+xzq*q4zea^ z5qlewZNm$cvTkP5mAA8dUiWZm%9{B==&qi#ruZAwWdd{elTxyj=UV5nwMPkDVi*Cf zNErQVe?oi{xFLqsoo1yLb`DBwDTP$%JeLa-D9cohE%F{pi0*r#Q~l1G;@O5|B%KsV zc}rMF6)(?m?{6s0s=%4Jt0wgrE{)(n z+HSB%A1uMyatKZh?~;%`?F!yVc6tD3d*_?(bz&iu@ z2S33$g15zRdxs}ncz0#zcq>oqq=f%rgj&=u_YgK;eYvU=-lr5 zqLh^-&{|t)AaB`uEX!l~sah55;we55!^3^CN-YdBfTK%O<0)@`0R z;iElG1yct%CW1J*PJ~i!bMR_w8GaOXr1HlI&WT;<-k;7lcwugyrap2z1>``}FC`Q~ zNV8VgPcM&I7#JFWkKnFWDi25S&KMrZqy1iLqwL)261#*HaF<6qb#%x!xdy01p=fcq zqNT?pcylX$hyAjeWp_F#pr=}5I46SZ`(^wZa^WmPi&28N1n|b>B#PmE5&Ul-EmYH? z%05wYLBI-Bu9gU1+-KM8D6ドルKTsMNxg;qP4ドルEy8C|nKix;$>s-eNXaB_3hhD{y+R zMW0Pa*<~0j{mzxwlprgv1m)puxhahqhgs>+sDf8pE6lGNmT2L+N*yqeUY=6*+Bt5m zP+|&A#_rX#GV&x412W>gD1@dkdA+=V>9KitdU-xoJ*E_&8o_t^W&ECqcgU7;Y?0nl z>d5;FNLzVovOqCh7{O2aX~7zeRrZ<^e8j+cdh;sctxdvk0qs=ste%fuymbumpsfd^ z!{3ln(pb3B2k10KS1o#MGi_fdp!d42#+z7J9rvL&HZg7$YW_|Z+rZ)h_Gty_5Ujz+6C^nc-;dz3ZdxweS(B>ydz-b(<~!kp!b=&>Py7b~0dTE~;CqF4 zTyO_v_&o5Zz8m>aej@Bcc9=NG6z)Qo>_-4k)LlIhyd;VH5PBi8^kpeKy~CGMgO?^& z{tcP(RCsuS{dJnO10|5-qB43ドルPB_`7*v3L{=#u?JMYoNQgz%djC)Tm1yfW&1FHBbZ z6}zZR^_rS?Jf7XW4^ZUz#a-0kPCD5NHKtHE{1ibbobT1Rz?3AQ<#(zt!0o$ihlbe@ zWOh3k>2~potlG)4!}1a~M1sk2N924(I*zNAcEgup>*%n+K%MkX2QKf1EPzS;f`0B= zfJ(bm=%EedH7$VE1c&n4(1r9hExOs2;FBKKIH|8d;cr#os!j`}W!Um7=~EC|`Po zYA_M>)4B-cR@V@Nx#bY^e9zkDq0=H37VMjzoV<+(;h#-qlt|>`D)5zlNKGI?umqQx-56QInEGA>muGpi zy$ilz0AA)1{gbo~x}iwGzA%K}c90E(*aEML8!aeTwD;1XLXC&g?E)?6a z$*DBm3e+ITbOkVE9PSlZh{oA*Si;)H@v)z0`5@WktrOb(t40lXw|Y$nm4h;Xlr;k#McO+CUWLmFeM z;}+nCu!I_HCrsh>7Dai)1GlNwU>;v2Bnp+SsZV0r!6VB#<+#-iixpfdchswi%gp&; z5S)IyrUF+;A=B(gEWvYqq;?vU`zFB;&J@R?28FKUpkrdM!^W&h$#%P!!&h%$u8 z%jG|SQw%)QCqB_8>U2<+8wjze7qpotxu*s=wu-(ggh@`837ziytc~e(n5ly8`PI+PA5~-!Jx1=;uB)fbhqe0-yX0#fNuW|oJznu6o-HTb^k&F^;v2Ceio$#`y%tqo;<`;fin?md6b~$# zYxsbo;{yjgHWE&a9G}sPmEqxN4DMZQOf_X>T^T}Ob|b}79imYB1o>YoRZv={2<3wb z;M*0rwH-xJqlNPF!ign^wkt(fB7lk*l*dDvb8gD8(EU;+TZ$#kW|y@Gm8V)GJ3dLj zL4r#>*SPG$>GH5ou=;~$SH>&@_oveHy*@)4$aHAq0})&xKglfYDocO8P^SVL<{psi zEaY@)8Wl^?G22F?MN;M+W$W`oVl5j}2Aj`S;J#)zhI;Kw2vu6flG86FE^tXzrDVL* ztI|8}pHMK-gg#B<9_gtyun5_q$he=7emdoi0f((gtyofz-5{=u-a8$^n!4jj?;)ls zzl|s=y*4gII*{*rZK<@8v#gs6$w^nkyn@n^+_7|x)2+ie2m(0c3!cxyude_gq^_gr z{Evm7)JB(k7Iaj}#ZD-}s$AylfwiuQ;M1~;WHDv;8>RTGrYxJSrF!;6aY*Q56+5n| znnw6j0(IFg1m`EsVIwGmdt&%)0CMveq_VW2noB*O4=Jp+dWF@urK(QX^M7OaV+5Z{d_I)7D{zVB6X1E(k&#K6 z32!Z6!6zPt)dv~VFh5DtMcirafrD^(0fU=yliq^4+`wN5f5Pm})YGi(s`}Z>2+GC% zh!j?y(5}K6!qP>Ab5001=*u$vODYvYQcsY?vb|K3x|W>+D@m#{SqHc*gl~Dml2K22 zyJ|c8dgNj~axFr~U0}?#;Q>tvLL(Zt_Jq_Mlj|V!YRq39C1bo0I5Fj#rej?<2jy@> z3pdAbjfK1Fnb$p*gEE46F>? zvHnN|8?5{2R?@I|`9KD(D9ドルsnPR{if9!$#zx(177NPzKmHd=TfEguNC(!hRR$dMzf zgx+;&`5*x00G(tRkvpw)jo#x*m}Tw_>L}*o9=XUhYa=T`m@!Za=t7gMG#l*P?`^HX zZjWkXn%ZA75rl@}1x!in2?|VGqnmrE<15fqkw7y#gepjf8nl>Q(m8g-UBBZ?H_JMd zH`Gp-ODnU}a!UlE_38yX1ドルK~o!lu*6UeQE`YHd8$+LF)FC1I^)f@-a|x!P^6fUdyX zsyVcbX(@enTN`SvUFbcudrD!|=Hku98ドル@V$Ym8~?FIqeKwg^IvY`1owd3&0rXXE1b z-JJXtbBg0j)=9KY5bD5Md!Wz8Qy4UI)``s3tFC}zG&h(g+Q?jGh;(C93ドルO?11I`C? zjwrfIm2m|&&2t3Nwqa~WCxkyGBxK5orEny>?yh%l_a4JzL&A43W=0y854ドルvj3Nryu z@BpZ}wAtIs-&3QLYJJBbtTq`mk2_j)1;~;cg#Z=4*n32ドル#z29TSWDda} z@?aC`A>>XFo+yG*IAp+CN9XJw?QMFar;^)*T;XOUK-TM^5Q0dE0yw~9SLM4TdvR-J zmlJ%`Os3} z4DBvLuRrJI1+j>hZ4<4|ts?~cs%~}(73p2noigdjzivc3c_fujv51zemf3ppvst9l zBI$a>+Wm)I0I&QrJKlf03)SK0sw`q09%#k6C66(7WkamB^TuF#$zeSAh2w4>i=DBJ zi3qlAg@>n$pUBu&%hkF<5q)crhtjpk9k|c{upxt0bf%q@tzxexxlqtn^z<8ryp^y$ z_R^b3l|3jbtXU9E)=iyBy`CTlO+a4VD`43K>?2f_y+FQ@QEGG$wmbw|y1^dm5l+t# zL^8O0tQ`_z47#wq*(zUz6R>fCvfwp{!k)=nK9gsAjv$iBSZk&JUNPYOET%+b&t$?~ zd{%1aumX?HgNKSxJLfUCo+XH6a>Bei0TzzI;s6#46s(WfNx~KcVlr|kjGUOGWik%} zsBNR?2_ki3XskLZ7L0>*83I}DNyC{GO5?C&93ENhkaELr-5OdYn!X{3+KKs-L!cc? zLx%(-;*hyrbL!N*bVJZbK8#TrcI||1>tI{2p9GBvrBe&nDxtPi-&#BHD9jsnnR@0M z7)(AnUuCOTM4`M4!o(O%3_Cn+MU;(H{-L*a(a+a@g4VKK>QN&w{95SV300_瘦脸 V脸 窄脸 + 短脸 小脸 下巴 额头 diff --git a/Agora-Video-With-FaceUnity-Android/faceunity/src/main/res/values/strings.xml b/Agora-Video-With-FaceUnity-Android/faceunity/src/main/res/values/strings.xml index 2f8c5246..8692ac78 100644 --- a/Agora-Video-With-FaceUnity-Android/faceunity/src/main/res/values/strings.xml +++ b/Agora-Video-With-FaceUnity-Android/faceunity/src/main/res/values/strings.xml @@ -38,6 +38,7 @@ Cheekbone Jawbone CheekNarrow + Cheek short Cheek small Chin Forehead From 2e31a774db69dcafcc1b8d254952d6be29aaa56e Mon Sep 17 00:00:00 2001 From: benyq <30992974+benyq@users.noreply.github.com> Date: 2021年12月30日 10:49:11 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E5=88=A0=E9=99=A4=E5=A4=9A=E4=BD=99?= =?UTF-8?q?=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../framework/PreprocessorFaceUnity.java | 13 -- .../java/io/agora/profile/CPUInfoUtil.java | 172 ---------------- .../main/java/io/agora/profile/CSVUtils.java | 186 ------------------ .../main/java/io/agora/profile/Constant.java | 49 ----- .../main/java/io/agora/profile/FPSUtil.java | 63 ------ .../java/io/agora/profile/MemoryInfoUtil.java | 89 --------- .../rtcwithfu/activities/FUChatActivity.java | 34 ---- 7 files changed, 606 deletions(-) delete mode 100644 Agora-Video-With-FaceUnity-Android/app/src/main/java/io/agora/profile/CPUInfoUtil.java delete mode 100644 Agora-Video-With-FaceUnity-Android/app/src/main/java/io/agora/profile/CSVUtils.java delete mode 100644 Agora-Video-With-FaceUnity-Android/app/src/main/java/io/agora/profile/Constant.java delete mode 100644 Agora-Video-With-FaceUnity-Android/app/src/main/java/io/agora/profile/FPSUtil.java delete mode 100644 Agora-Video-With-FaceUnity-Android/app/src/main/java/io/agora/profile/MemoryInfoUtil.java diff --git a/Agora-Video-With-FaceUnity-Android/app/src/main/java/io/agora/framework/PreprocessorFaceUnity.java b/Agora-Video-With-FaceUnity-Android/app/src/main/java/io/agora/framework/PreprocessorFaceUnity.java index f0b7bfea..79a6adf4 100644 --- a/Agora-Video-With-FaceUnity-Android/app/src/main/java/io/agora/framework/PreprocessorFaceUnity.java +++ b/Agora-Video-With-FaceUnity-Android/app/src/main/java/io/agora/framework/PreprocessorFaceUnity.java @@ -16,7 +16,6 @@ import io.agora.capture.framework.modules.channels.VideoChannel; import io.agora.capture.framework.modules.processors.IPreprocessor; import io.agora.capture.video.camera.VideoCaptureFrame; -import io.agora.profile.CSVUtils; public class PreprocessorFaceUnity implements IPreprocessor { private final static String TAG = PreprocessorFaceUnity.class.getSimpleName(); @@ -27,12 +26,6 @@ public class PreprocessorFaceUnity implements IPreprocessor { private Handler mGLHandler; - public void setCSVUtils(CSVUtils cSVUtils) { - this.mCSVUtils = cSVUtils; - } - - private CSVUtils mCSVUtils; - public PreprocessorFaceUnity(Context context) { } @@ -54,16 +47,10 @@ public VideoCaptureFrame onPreProcessFrame(VideoCaptureFrame outFrame, VideoChan if (FUConfig.DEVICE_LEVEL> FuDeviceUtils.DEVICE_LEVEL_MID)//高性能设备 cheekFaceNum(); - long start = System.nanoTime(); int texId = mFURenderer.onDrawFrameDualInput(outFrame.image, outFrame.textureId, outFrame.format.getWidth(), outFrame.format.getHeight()); - long renderTime = System.nanoTime() - start; - if (mCSVUtils != null) { - mCSVUtils.writeCsv(null, renderTime); - } - // The texture is transformed to texture2D by beauty module. if (skipFrame <= 0) { outFrame.textureId = texId; diff --git a/Agora-Video-With-FaceUnity-Android/app/src/main/java/io/agora/profile/CPUInfoUtil.java b/Agora-Video-With-FaceUnity-Android/app/src/main/java/io/agora/profile/CPUInfoUtil.java deleted file mode 100644 index cdd578e6..00000000 --- a/Agora-Video-With-FaceUnity-Android/app/src/main/java/io/agora/profile/CPUInfoUtil.java +++ /dev/null @@ -1,172 +0,0 @@ -package io.agora.profile; - -import android.content.Context; -import android.os.Build; - -import java.io.BufferedReader; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.RandomAccessFile; - -/** - * cpu使用率获取工具类 - * Created by lirui on 2017/8/2. - */ - -public class CPUInfoUtil { - private long lastTotalCpu = 0; - private long lastProcessCpu = 0; - - private final String PackageName; - - private volatile boolean isRunningCPU = false; - private volatile double cpuRate = 0; - - public CPUInfoUtil(Context context) { - if (Build.VERSION.SDK_INT>= 26) { - final String pn = context.getPackageName(); - if (pn.length() <= 16) { - PackageName = pn; - } else { - PackageName = pn.substring(0, 15) + "+"; - } -// Log.e(TAG, "CSVUtils PackageName " + PackageName); - isRunningCPU = true; - CPUInfoThread cpuinfothread = new CPUInfoThread(); - cpuinfothread.start(); - } else { - PackageName = null; - } - } - - public double getProcessCpuUsed() { - if (Build.VERSION.SDK_INT>= 26) { - return cpuRate; - } else { - double pcpu = 0; - double tmp = 1.0; - long nowTotalCpu = getTotalCpu(); - long nowProcessCpu = getMyProcessCpu(); - if (nowTotalCpu != 0 && (nowTotalCpu - lastTotalCpu) != 0) { -// Log.e(TAG, "cpu used nowProcessCpu " + nowProcessCpu + " lastProcessCpu " + lastProcessCpu + " nowTotalCpu " + nowTotalCpu + " lastTotalCpu " + lastTotalCpu); - pcpu = 100 * (tmp * (nowProcessCpu - lastProcessCpu) / (nowTotalCpu - lastTotalCpu)); - } - lastProcessCpu = nowProcessCpu; - lastTotalCpu = nowTotalCpu; - return pcpu < 0 ? 0 : pcpu; - } - } - - public void close() { - if (Build.VERSION.SDK_INT>= 26) { - isRunningCPU = false; - } - } - - private long getTotalCpu() { - String[] cpuInfos = null; - try { - RandomAccessFile reader = null; - reader = new RandomAccessFile("/proc/stat", "r"); - String load = reader.readLine(); - reader.close(); - cpuInfos = load.split(" "); - } catch (IOException e) { - e.printStackTrace(); - return 0; - } - long totalCpu = 0; - try { - totalCpu = Long.parseLong(cpuInfos[2]) - + Long.parseLong(cpuInfos[3]) + Long.parseLong(cpuInfos[4]) - + Long.parseLong(cpuInfos[6]) + Long.parseLong(cpuInfos[5]) - + Long.parseLong(cpuInfos[7]) + Long.parseLong(cpuInfos[8]); - } catch (ArrayIndexOutOfBoundsException e) { - e.printStackTrace(); - return 0; - } - return totalCpu; - } - - private long getMyProcessCpu() { - String[] cpuInfos = null; - try { - int pid = android.os.Process.myPid(); - BufferedReader reader = new BufferedReader(new InputStreamReader( - new FileInputStream("/proc/" + pid + "/stat")), 1000); - String load = reader.readLine(); - reader.close(); - cpuInfos = load.split(" "); - } catch (IOException e) { - e.printStackTrace(); - return 0; - } - long appCpuTime = 0; - try { - appCpuTime = Long.parseLong(cpuInfos[13]) - + Long.parseLong(cpuInfos[14]) + Long.parseLong(cpuInfos[15]) - + Long.parseLong(cpuInfos[16]); - } catch (ArrayIndexOutOfBoundsException e) { - e.printStackTrace(); - return 0; - } - return appCpuTime; - } - - class CPUInfoThread extends Thread { - - private double allCPU = 0; - - @Override - public void run() { - String line = null; - InputStream is = null; - try { - - Runtime runtime = Runtime.getRuntime(); - Process proc = runtime.exec("top -d 1"); - is = proc.getInputStream(); - - // 换成BufferedReader - BufferedReader buf = new BufferedReader(new InputStreamReader(is)); - do { - line = buf.readLine(); - if (allCPU == 0 && line.contains("user") && line.contains("nice") && line.contains("sys") && line.contains("idle") && line.contains("iow") && line.contains("irq") && line.contains("sirq") && line.contains("host")) { - if (line.indexOf("%cpu ")> 0) - allCPU = Double.parseDouble(line.split("%cpu ")[0]); - if (allCPU == 0) { - String[] s = line.split("%,"); - for (String st : s) { - String[] sts = st.split(" "); - if (sts.length> 0) - allCPU += Double.parseDouble(sts[sts.length - 1]); - } - } - } - // 读取到相应pkgName跳出循环(或者未找到) - if (line == null || line.endsWith(PackageName)) { -// Log.e(TAG, "cpu line : " + line); - String str[] = line.split(" "); - int t = 0; - for (int i = str.length - 1; i> 0; i--) { - if (!str[i].isEmpty() && ++t == 4) { -// Log.e(TAG, "cpu : " + str[i] + " allCPU " + allCPU); - cpuRate = 100 * Double.parseDouble(str[i]) / allCPU; - } - } - continue; - } - } while (isRunningCPU); - - if (is != null) { - buf.close(); - is.close(); - } - } catch (IOException e) { - e.printStackTrace(); - } - } - } -} diff --git a/Agora-Video-With-FaceUnity-Android/app/src/main/java/io/agora/profile/CSVUtils.java b/Agora-Video-With-FaceUnity-Android/app/src/main/java/io/agora/profile/CSVUtils.java deleted file mode 100644 index d4f521a3..00000000 --- a/Agora-Video-With-FaceUnity-Android/app/src/main/java/io/agora/profile/CSVUtils.java +++ /dev/null @@ -1,186 +0,0 @@ -package io.agora.profile; - -import android.app.ActivityManager; -import android.content.Context; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.Process; -import android.util.Log; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.OutputStreamWriter; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.Locale; - -import io.agora.rtcwithfu.RtcEngineEventHandlerProxy; - -/** - * Created by tujh on 2017年11月2日. - */ -public class CSVUtils { - public static final String TAG = CSVUtils.class.getSimpleName(); - /* 每 100 帧统计一次 */ - public static final int FRAME_STEP = 100; - - public static final String COMMA = ","; - - private OutputStreamWriter mStreamWriter; - - private ActivityManager mActivityManager; - - private Handler mHandler; - - private CPUInfoUtil mCPUInfoUtil; - - private int mFrameRate; - private volatile double mCpuUsed; - private volatile double mAverageFps; - private volatile double mAverageRenderTime; - private volatile double mMemory; - private long mSumRenderTimeInNano; - private volatile long mTimestamp; - - private RtcEngineEventHandlerProxy mRtcEngineEventHandler; - - public void setRtcEngineEventHandler(RtcEngineEventHandlerProxy rtcEngineEventHandlerProxy) { - mRtcEngineEventHandler = rtcEngineEventHandlerProxy; - } - - public CSVUtils(Context context) { - mActivityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); - mCPUInfoUtil = new CPUInfoUtil(context); - - HandlerThread handlerThread = new HandlerThread(TAG, Process.THREAD_PRIORITY_BACKGROUND); - handlerThread.start(); - mHandler = new Handler(handlerThread.getLooper()); - } - - public void initHeader(String folderName, StringBuilder headerInfo) { - Log.d(TAG, "initHeader() called with: folderName = [" + folderName + "], headerInfo = [" + headerInfo + "]"); - StringBuilder stringBuilder = new StringBuilder().append("时间").append(COMMA) - .append("帧率").append(COMMA) - .append("渲染耗时").append(COMMA) - .append("CPU").append(COMMA) - .append("内存").append(COMMA) - .append("远端分辨率").append(COMMA) - .append("远端渲染帧率").append(COMMA) - .append("远端解码帧率").append(COMMA) - .append("远端接收码率").append(COMMA); - if (headerInfo != null) { - stringBuilder.append(headerInfo); - } - stringBuilder.append("\n"); - - File file = new File(folderName); - File parentFile = file.getParentFile(); - if (!parentFile.exists()) { - parentFile.mkdirs(); - } - try { - if (!file.exists()) { - file.createNewFile(); - } - mStreamWriter = new OutputStreamWriter(new FileOutputStream(file, false), "GBK"); - } catch (IOException e) { - Log.e(TAG, "CSVUtils: ", e); - } - flush(stringBuilder); - mTimestamp = System.currentTimeMillis(); - } - - public void writeCsv(final StringBuilder extraInfo, long renderTimeInNano) { - if (mStreamWriter == null) { - return; - } - - mSumRenderTimeInNano += renderTimeInNano; - if (mFrameRate % FRAME_STEP == FRAME_STEP - 1) { - mTimestamp = System.currentTimeMillis(); - mAverageFps = FPSUtil.fpsAVG(FRAME_STEP); - mAverageRenderTime = (double) mSumRenderTimeInNano / FRAME_STEP / 1_000_000; - mSumRenderTimeInNano = 0; - mHandler.post(new Runnable() { - @Override - public void run() { - mCpuUsed = mCPUInfoUtil.getProcessCpuUsed(); - mMemory = MemoryInfoUtil.getMemory(mActivityManager.getProcessMemoryInfo(new int[]{Process.myPid()})); - String strCPU = String.format(Locale.getDefault(), "%.2f", mCpuUsed); - String strMemory = String.format(Locale.getDefault(), "%.2f", mMemory); - - StringBuilder stringBuilder = new StringBuilder(); - SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS", Locale.getDefault()); - RtcEngineEventHandlerProxy.StatsInfo statsInfo = mRtcEngineEventHandler.retrieveStatsInfo(); - stringBuilder.append(dateFormat.format(new Date(mTimestamp))).append(COMMA) - .append(String.format(Locale.getDefault(), "%.2f", mAverageFps)).append(COMMA) - .append(String.format(Locale.getDefault(), "%.2f", mAverageRenderTime)).append(COMMA) - .append(strCPU).append(COMMA) - .append(strMemory).append(COMMA) - .append(statsInfo.width).append("x") - .append(statsInfo.height).append(COMMA) - .append(statsInfo.renderFps).append(COMMA) - .append(statsInfo.decoderFps).append(COMMA) - .append(statsInfo.receivedBitrate).append(COMMA); - Log.d(TAG, "Fps:" + String.format(Locale.getDefault(), "%.2f", mAverageFps) - + " Render Time:" + String.format(Locale.getDefault(), "%.2f", mAverageRenderTime)); - if (extraInfo != null) { - stringBuilder.append(extraInfo); - } - stringBuilder.append("\n"); - flush(stringBuilder); - } - }); - } - mFrameRate++; - } - - private void flush(StringBuilder stringBuilder) { - if (mStreamWriter == null) { - return; - } - try { - mStreamWriter.write(stringBuilder.toString()); - mStreamWriter.flush(); - } catch (IOException e) { - Log.e(TAG, "flush: ", e); - } - } - - public void close() { - Log.d(TAG, "close: "); - mHandler.post(new Runnable() { - @Override - public void run() { - if (mStreamWriter != null) { - try { - mStreamWriter.close(); - mStreamWriter = null; - } catch (IOException e) { - Log.e(TAG, "close: ", e); - } - } - } - }); - mHandler.getLooper().quitSafely(); - mHandler = null; - mCPUInfoUtil.close(); - } - - public double getCpuUsed() { - return mCpuUsed; - } - - public double getMemory() { - return mMemory; - } - - public double getAverageRenderTime() { - return mAverageRenderTime; - } - - public double getAverageFps() { - return mAverageFps; - } -} diff --git a/Agora-Video-With-FaceUnity-Android/app/src/main/java/io/agora/profile/Constant.java b/Agora-Video-With-FaceUnity-Android/app/src/main/java/io/agora/profile/Constant.java deleted file mode 100644 index 8b0d070c..00000000 --- a/Agora-Video-With-FaceUnity-Android/app/src/main/java/io/agora/profile/Constant.java +++ /dev/null @@ -1,49 +0,0 @@ -package io.agora.profile; - -import android.os.Build; -import android.os.Environment; - -import java.io.File; -import java.util.regex.Pattern; - -/** - * Created by tujh on 2018年2月7日. - */ - -public class Constant { - public static final String APP_NAME = "AgoraVideo"; - public static final String filePath = Environment.getExternalStoragePublicDirectory("") - + File.separator + "FaceUnity" + File.separator + APP_NAME + File.separator; - - public static final String DICMFilePath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).getPath(); - public static final String photoFilePath; - public static final String cameraFilePath; - - static { - if (Build.FINGERPRINT.contains("Flyme") - || Pattern.compile("Flyme", Pattern.CASE_INSENSITIVE).matcher(Build.DISPLAY).find() - || Build.MANUFACTURER.contains("Meizu") - || Build.MANUFACTURER.contains("MeiZu")) { - photoFilePath = DICMFilePath + File.separator + "Camera" + File.separator; - cameraFilePath = DICMFilePath + File.separator + "Video" + File.separator; - } else if (Build.FINGERPRINT.contains("vivo") - || Pattern.compile("vivo", Pattern.CASE_INSENSITIVE).matcher(Build.DISPLAY).find() - || Build.MANUFACTURER.contains("vivo") - || Build.MANUFACTURER.contains("vivo")) { - photoFilePath = cameraFilePath = Environment.getExternalStoragePublicDirectory("") + File.separator + "相机" + File.separator; - } else { - cameraFilePath = photoFilePath = DICMFilePath + File.separator + "Camera" + File.separator; - } - createFile(filePath); - createFile(cameraFilePath); - createFile(photoFilePath); - } - - public static void createFile(String path) { - File dir = new File(path); - if (!dir.exists()) { - dir.mkdirs(); - } - } - -} diff --git a/Agora-Video-With-FaceUnity-Android/app/src/main/java/io/agora/profile/FPSUtil.java b/Agora-Video-With-FaceUnity-Android/app/src/main/java/io/agora/profile/FPSUtil.java deleted file mode 100644 index 861f8875..00000000 --- a/Agora-Video-With-FaceUnity-Android/app/src/main/java/io/agora/profile/FPSUtil.java +++ /dev/null @@ -1,63 +0,0 @@ -package io.agora.profile; - -/** - * FPS工具类 - * Created by tujh on 2018年5月24日. - */ -public class FPSUtil { - private static final int NANO_IN_ONE_MILLI_SECOND = 1000000; - private static final int NANO_IN_ONE_SECOND = 1000 * NANO_IN_ONE_MILLI_SECOND; - - private static long sLastFrameTimeStamp = 0; - - /** - * 每帧都计算 - * - * @return - */ - public static double fps() { - long tmp = System.nanoTime(); - double fps = ((double) NANO_IN_ONE_SECOND) / (tmp - sLastFrameTimeStamp); - sLastFrameTimeStamp = tmp; -// Log.e(TAG, "FPS : " + fps); - return fps; - } - - private static long mStartTime = 0; - - /** - * 平均值 - * - * @return - */ - public static double fpsAVG(int time) { - long tmp = System.nanoTime(); - double fps = ((double) NANO_IN_ONE_SECOND) * time / (tmp - mStartTime); - mStartTime = tmp; -// Log.e(TAG, "FPS : " + fps); - return fps; - } - - private long mLimitMinTime = 33333333; - private long mLimitStartTime; - private int mLimitFrameRate; - - public void setLimitMinTime(long limitMinTime) { - mLimitMinTime = limitMinTime; - } - - public void limit() { - try { - if (mLimitFrameRate == 0 || mLimitFrameRate> 600000) { - mLimitStartTime = System.nanoTime(); - mLimitFrameRate = 0; - } - long sleepTime = mLimitMinTime * mLimitFrameRate++ - (System.nanoTime() - mLimitStartTime); - if (sleepTime> 0) { - Thread.sleep(sleepTime / NANO_IN_ONE_MILLI_SECOND, (int) (sleepTime % NANO_IN_ONE_MILLI_SECOND)); - } - } catch (InterruptedException e) { - e.printStackTrace(); - } - } -} diff --git a/Agora-Video-With-FaceUnity-Android/app/src/main/java/io/agora/profile/MemoryInfoUtil.java b/Agora-Video-With-FaceUnity-Android/app/src/main/java/io/agora/profile/MemoryInfoUtil.java deleted file mode 100644 index c9376b72..00000000 --- a/Agora-Video-With-FaceUnity-Android/app/src/main/java/io/agora/profile/MemoryInfoUtil.java +++ /dev/null @@ -1,89 +0,0 @@ -package io.agora.profile; - -import android.os.Build; -import android.os.Debug; -import android.support.annotation.RequiresApi; - -import java.lang.reflect.Field; - -/** - * 内存使用率获取工具类 - * Created by tujh on 2018年5月24日. - */ -public abstract class MemoryInfoUtil { - - @RequiresApi(api = Build.VERSION_CODES.M) - public static double getMemory(Debug.MemoryInfo[] memoryInfos) { - try { - if (Build.VERSION.SDK_INT>= 27) { - return compute(memoryInfos); - } - - Field otherStats_Field = memoryInfos[0].getClass().getDeclaredField("otherStats"); - otherStats_Field.setAccessible(true); - int[] otherStats = (int[]) otherStats_Field.get(memoryInfos[0]); - - Field NUM_CATEGORIES_Field = memoryInfos[0].getClass().getDeclaredField("NUM_CATEGORIES"); - Field offsetPrivateClean_Field = memoryInfos[0].getClass().getDeclaredField("offsetPrivateClean"); - Field offsetPrivateDirty_Field = memoryInfos[0].getClass().getDeclaredField("offsetPrivateDirty"); - final int NUM_CATEGORIES = (int) NUM_CATEGORIES_Field.get(memoryInfos[0]); - final int offsetPrivateClean = (int) offsetPrivateClean_Field.get(memoryInfos[0]); - final int offsetPrivateDirty = (int) offsetPrivateDirty_Field.get(memoryInfos[0]); - - int javaHeap = memoryInfos[0].dalvikPrivateDirty - + otherStats[12 * NUM_CATEGORIES + offsetPrivateClean] + otherStats[12 * NUM_CATEGORIES + offsetPrivateDirty]; - int nativeHeap = memoryInfos[0].nativePrivateDirty; - int code = otherStats[6 * NUM_CATEGORIES + offsetPrivateClean] + otherStats[6 * NUM_CATEGORIES + offsetPrivateDirty] - + otherStats[7 * NUM_CATEGORIES + offsetPrivateClean] + otherStats[7 * NUM_CATEGORIES + offsetPrivateDirty] - + otherStats[8 * NUM_CATEGORIES + offsetPrivateClean] + otherStats[8 * NUM_CATEGORIES + offsetPrivateDirty] - + otherStats[9 * NUM_CATEGORIES + offsetPrivateClean] + otherStats[9 * NUM_CATEGORIES + offsetPrivateDirty] - + otherStats[10 * NUM_CATEGORIES + offsetPrivateClean] + otherStats[10 * NUM_CATEGORIES + offsetPrivateDirty] - + otherStats[11 * NUM_CATEGORIES + offsetPrivateClean] + otherStats[11 * NUM_CATEGORIES + offsetPrivateDirty]; - int stack = otherStats[NUM_CATEGORIES + offsetPrivateDirty]; - int graphics = otherStats[4 * NUM_CATEGORIES + offsetPrivateClean] + otherStats[4 * NUM_CATEGORIES + offsetPrivateDirty] - + otherStats[14 * NUM_CATEGORIES + offsetPrivateClean] + otherStats[14 * NUM_CATEGORIES + offsetPrivateDirty] - + otherStats[15 * NUM_CATEGORIES + offsetPrivateClean] + otherStats[15 * NUM_CATEGORIES + offsetPrivateDirty]; - int other = otherStats[0 * NUM_CATEGORIES + offsetPrivateClean] + otherStats[0 * NUM_CATEGORIES + offsetPrivateDirty] - + otherStats[2 * NUM_CATEGORIES + offsetPrivateClean] + otherStats[2 * NUM_CATEGORIES + offsetPrivateDirty] - + otherStats[3 * NUM_CATEGORIES + offsetPrivateClean] + otherStats[3 * NUM_CATEGORIES + offsetPrivateDirty] - + otherStats[5 * NUM_CATEGORIES + offsetPrivateClean] + otherStats[5 * NUM_CATEGORIES + offsetPrivateDirty] - + otherStats[13 * NUM_CATEGORIES + offsetPrivateClean] + otherStats[13 * NUM_CATEGORIES + offsetPrivateDirty] - + otherStats[16 * NUM_CATEGORIES + offsetPrivateClean] + otherStats[16 * NUM_CATEGORIES + offsetPrivateDirty]; - int all = javaHeap + nativeHeap + code + stack + graphics + other; -// Log.d("MemoryAll", "javaHeap=" + javaHeap -// + "\nnativeHeap=" + nativeHeap + "\ncode=" + code + "\nstack=" + stack -// + "\ngraphics=" + graphics + "\nother=" + other); -// Log.e(TAG, "memory " + memoryStr -// + " java-heap " + String.format("%.2f", ((double) javaHeap) / 1024) -// + " native-heap " + String.format("%.2f", ((double) nativeHeap) / 1024) -// + " code " + String.format("%.2f", ((double) code) / 1024) -// + " stack " + String.format("%.2f", ((double) stack) / 1024) -// + " graphics " + String.format("%.2f", ((double) graphics) / 1024) -// + " other " + String.format("%.2f", ((double) other) / 1024) -// + " pps " + String.format("%.2f", ((double) memoryInfos[0].getTotalPss()) / 1024) -// ); - return ((double) all) / 1024; - } catch (Exception e) { - e.printStackTrace(); - } - return 0; - } - - - @RequiresApi(api = Build.VERSION_CODES.M) - private static double compute(Debug.MemoryInfo[] memInfo) { - String java_mem = memInfo[0].getMemoryStat("summary.java-heap"); - String native_mem = memInfo[0].getMemoryStat("summary.native-heap"); - String graphics_mem = memInfo[0].getMemoryStat("summary.graphics"); - String stack_mem = memInfo[0].getMemoryStat("summary.stack"); - String code_mem = memInfo[0].getMemoryStat("summary.code"); - String others_mem = memInfo[0].getMemoryStat("summary.system"); - int all = Integer.parseInt(java_mem) - + Integer.parseInt(native_mem) - + Integer.parseInt(graphics_mem) - + Integer.parseInt(stack_mem) - + Integer.parseInt(code_mem) - + Integer.parseInt(others_mem); - return ((double) all) / 1024; - } -} diff --git a/Agora-Video-With-FaceUnity-Android/app/src/main/java/io/agora/rtcwithfu/activities/FUChatActivity.java b/Agora-Video-With-FaceUnity-Android/app/src/main/java/io/agora/rtcwithfu/activities/FUChatActivity.java index 90cfa75c..f23a7077 100644 --- a/Agora-Video-With-FaceUnity-Android/app/src/main/java/io/agora/rtcwithfu/activities/FUChatActivity.java +++ b/Agora-Video-With-FaceUnity-Android/app/src/main/java/io/agora/rtcwithfu/activities/FUChatActivity.java @@ -32,7 +32,6 @@ import io.agora.capture.video.camera.VideoCapture; import io.agora.framework.PreprocessorFaceUnity; import io.agora.framework.RtcVideoConsumer; -import io.agora.profile.CSVUtils; import io.agora.rtc.RtcEngine; import io.agora.rtc.video.VideoCanvas; import io.agora.rtc.video.VideoEncoderConfiguration; @@ -77,10 +76,6 @@ protected void onCreate(Bundle savedInstanceState) { mFURenderer.bindListener(mFURendererListener); String sdkVersion = RtcEngine.getSdkVersion(); Log.i(TAG, "onCreate: agora sdk version " + sdkVersion); - initCsvUtil(this); - if (preprocessor !=null) { - preprocessor.setCSVUtils(mCSVUtils); - } } private void initUI() { @@ -287,33 +282,4 @@ public void onSensorChanged(SensorEvent event) { public void onAccuracyChanged(Sensor sensor, int accuracy) { } - - private static final int ENCODE_FRAME_WIDTH = 960; - private static final int ENCODE_FRAME_HEIGHT = 540; - private static final int ENCODE_FRAME_BITRATE = 1000; - private static final int ENCODE_FRAME_FPS = 30; - - private CSVUtils mCSVUtils; - private void initCsvUtil(Context context) { - mCSVUtils = new CSVUtils(context); - SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss", Locale.getDefault()); - String dateStrDir = format.format(new Date(System.currentTimeMillis())); - dateStrDir = dateStrDir.replaceAll("-", "").replaceAll("_", ""); - SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmmssSSS", Locale.getDefault()); - String dateStrFile = df.format(new Date()); - String filePath = io.agora.profile.Constant.filePath + dateStrDir + File.separator + "excel-" + dateStrFile + ".csv"; - Log.d(TAG, "initLog: CSV file path:" + filePath); - StringBuilder headerInfo = new StringBuilder(); - headerInfo.append("version:").append(FURenderer.getInstance().getVersion()).append(CSVUtils.COMMA) - .append("机型:").append(android.os.Build.MANUFACTURER).append(android.os.Build.MODEL).append(CSVUtils.COMMA) - .append("处理方式:双输入纹理输出").append(CSVUtils.COMMA) - .append("编码方式:硬件编码").append(CSVUtils.COMMA) - .append("编码分辨率:").append(ENCODE_FRAME_WIDTH).append("x").append(ENCODE_FRAME_HEIGHT).append(CSVUtils.COMMA) - .append("编码帧率:").append(ENCODE_FRAME_FPS).append(CSVUtils.COMMA) - .append("编码码率:").append(ENCODE_FRAME_BITRATE).append(CSVUtils.COMMA) - .append("预览分辨率:").append(CAPTURE_WIDTH).append("x").append(CAPTURE_HEIGHT).append(CSVUtils.COMMA) - .append("预览帧率:").append(CAPTURE_FRAME_RATE).append(CSVUtils.COMMA); - mCSVUtils.initHeader(filePath, headerInfo); - mCSVUtils.setRtcEngineEventHandler(((MyApplication) getApplication()).getRtcEventHandler()); - } }

AltStyle によって変換されたページ (->オリジナル) /