Files
Vulkan-Samples/app/android/java/com/khronos/vulkan_samples/MainActivity.java
T
2025-09-20 23:13:56 +08:00

315 lines
11 KiB
Java

package com.khronos.vulkan_samples;
import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.view.WindowManager;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.camera.core.AspectRatio;
import androidx.camera.core.CameraSelector;
import androidx.camera.core.ImageAnalysis;
import androidx.camera.core.ImageProxy;
import androidx.camera.lifecycle.ProcessCameraProvider;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import com.google.androidgamesdk.GameActivity;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.mediapipe.tasks.components.containers.NormalizedLandmark;
import com.google.mediapipe.tasks.vision.core.RunningMode;
import com.google.mediapipe.tasks.vision.facelandmarker.FaceLandmarkerResult;
import org.jetbrains.annotations.NotNull;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class MainActivity extends GameActivity implements FaceLandmarkerHelper.LandmarkerListener, CameraDataFetcher.CameraDataCallback {
private ProcessCameraProvider cameraProvider;
private void initCamera(){
backgroundExecutor = Executors.newSingleThreadExecutor();
if (hasCameraPermission()) {
startCamera();
} else {
requestCameraPermission();
}
}
private void detectFace(ImageProxy imageProxy) {
faceLandmarkerHelper.detectLiveStream(
imageProxy,false
);
}
private Handler handler = new Handler(Looper.getMainLooper());
private Runnable cameraDataRunnable;
private static final long INTERVAL = 1000; // 5秒间隔
@Override
protected void onCreate(Bundle savedInstanceState) {
String native_lib_name = getResources().getString(R.string.native_lib_name);
try {
System.loadLibrary(native_lib_name);
} catch (UnsatisfiedLinkError e) {
Toast.makeText(getApplicationContext(), "Native code library failed to load.", Toast.LENGTH_SHORT).show();
}
String[] argArray = new String[2];
argArray[0] = "sample";
argArray[1] = "texture_loading";//"hello_triangle";
sendArgumentsToPlatform(argArray);
super.onCreate(savedInstanceState);
// 隐藏状态栏
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
initCamera();
backgroundExecutor.execute(new Runnable() {
@Override
public void run() {
faceLandmarkerHelper = new FaceLandmarkerHelper(
0.5f,
0.5f,
0.5f,
1,
0,
RunningMode.LIVE_STREAM,
MainActivity.this,
MainActivity.this
);
}
});
// 初始化Runnable
cameraDataRunnable = new Runnable() {
@Override
public void run() {
fetchCameraData();
handler.postDelayed(this, INTERVAL); // 再次调度
}
};
// 开始周期性调用
startPeriodicFetching();
Log.i("MainActivity", "onCreate: ");
}
boolean canFetch = true;
private void fetchCameraData() {
if(canFetch)
{
CameraDataFetcher.fetchCameraData("http://175.178.100.240:5000/data", MainActivity.this);
canFetch = false;
}
}
private void startPeriodicFetching() {
handler.postDelayed(cameraDataRunnable, INTERVAL);
}
private void stopPeriodicFetching() {
handler.removeCallbacks(cameraDataRunnable);
}
@Override
protected void onDestroy() {
super.onDestroy();
stopPeriodicFetching(); // 避免内存泄漏
}
private boolean hasCameraPermission() {
return ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
== PackageManager.PERMISSION_GRANTED;
}
private void requestCameraPermission() {
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.CAMERA},
REQUEST_CAMERA_PERMISSION);
}
private static final int REQUEST_CAMERA_PERMISSION = 1001;
ImageAnalysis imageAnalyzer;
ExecutorService backgroundExecutor;
FaceLandmarkerHelper faceLandmarkerHelper;
private void startCamera() {
ListenableFuture<ProcessCameraProvider> cameraProviderFuture =
ProcessCameraProvider.getInstance(this);
cameraProviderFuture.addListener(() -> {
try {
cameraProvider = cameraProviderFuture.get();
CameraSelector cameraSelector = new CameraSelector.Builder()
.requireLensFacing(CameraSelector.LENS_FACING_BACK)
.build();
imageAnalyzer = new ImageAnalysis.Builder()
.setTargetAspectRatio(AspectRatio.RATIO_4_3)
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
.setOutputImageFormat(ImageAnalysis.OUTPUT_IMAGE_FORMAT_RGBA_8888)
.build();
imageAnalyzer.setAnalyzer(backgroundExecutor, new ImageAnalysis.Analyzer() {
@Override
public void analyze(@NonNull ImageProxy image) {
try {
//Log.d("Camera", "Received image: " + image.getWidth() + "x" + image.getHeight());
processImageForVulkan(image);
detectFace(image);
} finally {
//image.close(); // 确保在这里关闭
//Log.d("Camera", "Image closed, ready for next frame");
}
}
});
cameraProvider.bindToLifecycle(this, cameraSelector, imageAnalyzer);
} catch (ExecutionException | InterruptedException e) {
Log.e("MainActivity", "Error starting camera: " + e.getMessage());
}
}, ContextCompat.getMainExecutor(this));
}
private void processImageForVulkan(ImageProxy image) {
// 获取图像信息
int width = image.getWidth();
int height = image.getHeight();
int format = image.getFormat();
// 获取图像数据平面
ImageProxy.PlaneProxy[] planes = image.getPlanes();
// 对于 RGBA_8888 格式,通常只有一个平面
if (planes.length > 0) {
ImageProxy.PlaneProxy plane = planes[0];
ByteBuffer buffer = plane.getBuffer();
int rowStride = plane.getRowStride();
int pixelStride = plane.getPixelStride();
// 调用 Native 方法处理图像
processImageNative(buffer, width, height, format,
rowStride, pixelStride, image.getImageInfo().getRotationDegrees());
}
}
// Native 方法
private native void processImageNative(ByteBuffer buffer, int width, int height,
int format, int rowStride, int pixelStride,
int rotation);
private static final int REQUEST_CODE_PERMISSIONS = 1001;
private static final String[] REQUIRED_PERMISSIONS = new String[]{Manifest.permission.CAMERA};
private static final String TAG = "MainActivity";
private boolean allPermissionsGranted() {
for (String permission : REQUIRED_PERMISSIONS) {
if (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {
return false;
}
}
return true;
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == REQUEST_CODE_PERMISSIONS) {
if (allPermissionsGranted()) {
startCamera();
} else {
Log.e(TAG, "Permissions not granted by the user.");
finish(); // Close the activity if permission is denied
}
}
}
private native void sendArgumentsToPlatform(String[] args);
@Override
public void onError(@NotNull String error, int errorCode) {
Log.e(TAG, "onError: Face Error");
}
private ByteBuffer nativeBuffer = null;
float[] points = new float[480 * 3];
@Override
public void onResults(FaceLandmarkerHelper.@NotNull ResultBundle resultBundle) {
FaceLandmarkerResult faceLandmarkerResult = resultBundle.getResult();
int height = resultBundle.getInputImageHeight();
int width = resultBundle.getInputImageWidth();
int bufferSize = 480 * 3 * 4;
if(nativeBuffer == null)
{
// 创建直接ByteBuffer(在native内存中)
nativeBuffer = ByteBuffer.allocateDirect(bufferSize);
nativeBuffer.order(ByteOrder.nativeOrder());
}
int index = 0;
// Iterate through each detected face
for (List<NormalizedLandmark> faceLandmarks : faceLandmarkerResult.faceLandmarks()) {
//should be 1
int arrayIndex = 0;
for (NormalizedLandmark p : faceLandmarks)
{
//Log.i(TAG, "Index" + index + " x:"+p.x() + " y:"+p.y() + " z:"+p.z());
points[arrayIndex++] = p.x();
points[arrayIndex++] = p.y();
points[arrayIndex++] = p.z() * z_rate;
index++;
}
}
// 转换为FloatBuffer并填充数据
FloatBuffer floatBuffer = nativeBuffer.asFloatBuffer();
floatBuffer.put(points);
floatBuffer.position(0);
passDataToNative(nativeBuffer, index, width, height);
}
public float z_rate = 1.0f;
public native void passDataToNative(ByteBuffer buffer, int pointCount, int width, int height);
public native void upDateCameraData(float fov, float x, float y, float z, float sx, float sy, float sz, float rotx, float roty, float rotz, float camx, float camy, float camz);
@Override
public void onSuccess(CameraDataFetcher.CameraData data) {
z_rate = data.z_rate;
upDateCameraData(data.fov, data.x, data.y, data.z, data.sx, data.sy, data.sz,
data.rotx, data.roty, data.rotz,
data.camx, data.camy, data.camz
);
canFetch = true;
Log.e(TAG, "GetCameraData " + data.toString());
}
@Override
public void onError(String errorMessage) {
Log.e(TAG, "GetCameraData Error");
canFetch = true;
}
}