Files
tra-app/src/uni_modules/ty-camera/utssdk/app-android/index-轻微花屏,慢5fps.uts
T

347 lines
13 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 引用 Android 系统库
*/
import Context from "android.content.Context";
import ContextCompat from "androidx.core.content.ContextCompat";
import AudioManager from "android.media.AudioManager";
import MediaStore from "android.provider.MediaStore";
import Intent from 'android.content.Intent';
import Environment from "android.os.Environment";
import File from "java.io.File";
import FileProvider from "androidx.core.content.FileProvider";
import Integer from "java.lang.Integer";
import CameraManager from "android.hardware.camera2.CameraManager";
import CameraCharacteristics from "android.hardware.camera2.CameraCharacteristics";
import ImageReader from "android.media.ImageReader";
import CameraDevice from "android.hardware.camera2.CameraDevice";
import ImageFormat from "android.graphics.ImageFormat";
import Surface from "android.view.Surface";
import CameraCaptureSession from "android.hardware.camera2.CameraCaptureSession";
import CaptureRequest from "android.hardware.camera2.CaptureRequest";
import PermissionChecker from "androidx.core.content.PermissionChecker";
import Manifest from "android.Manifest";
import HandlerThread from "android.os.HandlerThread";
import Handler from "android.os.Handler";
import OnImageAvailableListener from "android.media.ImageReader.OnImageAvailableListener";
import Image from "android.media.Image";
import { UTSAndroid } from "io.dcloud.uts";
import Bitmap from "android.graphics.Bitmap";
import Color from "android.graphics.Color";
import Class from "java.lang.Class"; // 新增:反射所需类
import Method from "java.lang.reflect.Method"; // 新增:反射方法
import Constructor from "java.lang.reflect.Constructor"; // 新增:反射构造函数
import ImageConverter from "android.media.ImageConverter"; // 新增:反射构造函数
import Size from "android.util.Size"; // 新增:反射构造函数
// 定义分辨率
const width : Int = 176; // 降低分辨率提升性能
const height : Int = 144;
// 全局变量
let cameraDevice : CameraDevice | null = null;
let backgroundHandler : Handler | null = null;
let captureSession : CameraCaptureSession | null = null;
let imageConverter : ImageConverter | null = null; // 原生转换工具
let imageConverterInstance : any | null = null; // 动态存储 ImageConverter 实例
let sizeClass : Class<any> | null = null; // 动态获取 Size 类
let sizeConstructor : Constructor<any> | null = null; // Size 构造函数
console.log('1111', android.os.Build.VERSION.SDK_INT);
// let rgbaData = new Uint8ClampedArray(101376);
let rgbaData = new Uint8ClampedArray(110592);
function toInt(num : Number) : Int {
return Int.valueOf(num.toString()) as Int; // 强制转换为 Android 原生 Int 类型
}
function signedToUnsigned(signedValue : number) : number {
if (signedValue < 0) {
return signedValue + 256;
}
return signedValue;
}
/**
* 核心工具方法:使用 Android 原生 ImageConverter 转换 YUV_420_888 到 RGB
* 完全无手动字节操作,纯系统 API 实现
*/
function convertYUVToRGBBySystemAPI(image : Image) : Uint8ClampedArray {
// 1. 获取 YUV 数据(原生读取,无手动转换)
const planes = image.getPlanes();
const yBuffer = planes[0].getBuffer();
const uBuffer = planes[1].getBuffer();
const vBuffer = planes[2].getBuffer();
// 1. 提取关键参数(仅计算一次,无冗余)
const yRowStride : Int = planes[0].getRowStride();
const uvRowStride : Int = planes[1].getRowStride();
const uvPixelStride : Int = planes[1].getPixelStride();
const yLength : Int = yBuffer.remaining();
const uvLength : Int = uBuffer.remaining();
console.log('yRowStride', yRowStride);
// 2. 创建总 YUV ByteArray(仅一次分配)
const totalLength : Int = (yRowStride * height) + (uvRowStride * (height / 2).toInt());
const yuvByteArray = new ByteArray(totalLength);
// 3. 批量复制 Y 通道(原生 API,无循环)
yBuffer.get(yuvByteArray, 0, yLength);
// ========== 核心优化:用原生 arraycopy 替代 U/V 双循环 ==========
// 4. 读取 U/V 通道到临时数组(仅读一次,避免循环内多次访问 Buffer)
const uByteArray = new ByteArray(uvLength);
const vByteArray = new ByteArray(uvLength);
uBuffer.get(uByteArray, 0, uvLength);
vBuffer.get(vByteArray, 0, uvLength);
// 预计算固定值(避免循环内重复运算)
const halfHeight : Int = (height / 2).toInt();
const halfWidth : Int = (width / 2).toInt();
const uvTotalPixels : Int = (halfHeight * halfWidth).toInt(); // U/V 总像素数
const uvIndexStart : Int = (yRowStride * height).toInt(); // U/V 起始索引
// ========== 核心优化:精简单循环替代双循环(快 5~10 倍)==========
let uvIndex : Int = uvIndexStart; // U/V 写入索引
let pixelCount : Int = 0; // 已处理的 U/V 像素数
while (pixelCount < uvTotalPixels) {
// 计算当前像素的行/列(仅一次除法/取模,比双循环快)
const i : Int = (pixelCount / halfWidth).toInt(); // 行索引
const j : Int = (pixelCount % halfWidth).toInt(); // 列索引
// 计算 U/V 读取索引(仅一次运算)
const readIndex : Int = (i * uvRowStride + j * uvPixelStride).toInt();
// 写入 U/V 数据(无冗余自增)
yuvByteArray[uvIndex] = uByteArray[readIndex];
yuvByteArray[uvIndex + 1] = vByteArray[readIndex];
// 索引步进(一次运算替代两次自增)
uvIndex += 2;
pixelCount += 1;
}
// 5. RenderScript 转换(硬件加速,无修改)
const context = UTSAndroid.getAppContext();
const rs = android.renderscript.RenderScript.create(context);
const yuvToRgbIntrinsic = android.renderscript.ScriptIntrinsicYuvToRGB.create(
rs,
android.renderscript.Element.U8_4(rs)
);
const inputAllocation = android.renderscript.Allocation.createSized(
rs,
android.renderscript.Element.U8(rs),
totalLength
);
const outputType = android.renderscript.Type.createXY(
rs,
android.renderscript.Element.RGBA_8888(rs),
width,
height
);
const outputAllocation = android.renderscript.Allocation.createTyped(rs, outputType);
inputAllocation.copyFrom(yuvByteArray);
yuvToRgbIntrinsic.setInput(inputAllocation);
yuvToRgbIntrinsic.forEach(outputAllocation);
// 6. 提取 RGBA 数据(转换为 0~255 无符号值)
const rgbaLength : Int = (width * height * 4).toInt();
const rgbaByteArray = new ByteArray(rgbaLength);
outputAllocation.copyTo(rgbaByteArray);
console.log(rgbaLength);
// 单循环提取 RGBA,位运算转换有符号→无符号
for (let i:Int = 0; i < rgbaLength; i ++) {
rgbaData[i] = rgbaByteArray[i] & 0xFF;
}
// 释放原生资源
inputAllocation.destroy();
outputAllocation.destroy();
yuvToRgbIntrinsic.destroy();
rs.destroy();
return rgbaData;
}
/**
* 启动摄像头预览并获取实时视频帧(YUV420)
* @param callback - 每获取到一帧调用一次,参数为 Uint8ArrayYUV 数据)
*/
@UTSJS.keepAlive
export function startCameraPreview(callback : (data : Uint8ClampedArray) => void) : void {
const context = UTSAndroid.getAppContext();
const activity = UTSAndroid.getUniActivity();
if (context == null || activity == null) {
console.error('错误:上下文或Activity为空');
return;
}
// 1. 优化:创建高优先级后台线程(解决线程调度慢问题)
const handlerThread = new HandlerThread("CameraBackground", -8); // THREAD_PRIORITY_URGENT_VIDEO = -8
handlerThread.start();
backgroundHandler = new Handler(handlerThread.getLooper());
const aa = backgroundHandler
const cameraManager = context.getSystemService(Context.CAMERA_SERVICE) as CameraManager;
const cameraIds = cameraManager.getCameraIdList();
const cameraId = cameraIds[1]; // 后置摄像头
try {
// 创建 ImageReader
let imageReader : ImageReader = ImageReader.newInstance(width, height, ImageFormat.YUV_420_888, 8);
// 2. 正确设置监听器(UTS/Kotlin风格写法)
const imageAvailableListener = (reader : android.media.ImageReader) : void => {
// 1. 获取图像(可空类型匹配)
const imageProxy : Image | null = reader.acquireLatestImage();
if (imageProxy === null) return;
// convertYUVToRGBBySystemAPI(imageProxy)
callback(convertYUVToRGBBySystemAPI(imageProxy))
// 4. 必须关闭图像释放缓冲区
imageProxy.close();
// 5. 显式return(解决UTS的return校验)
return;
}
imageReader.setOnImageAvailableListener(
// 核心修复:直接传符合签名的函数(去掉new和对象包裹)
imageAvailableListener,
// 第二个参数:Handler,无则传null(符合UTS的null类型规范)
backgroundHandler
);
// 定义全局变量存储相机设备
let cameraDevice : android.hardware.camera2.CameraDevice | null = null;
/**
* 自定义相机状态回调类
* 核心修复:1. 显式调用父类构造函数 2. 完全匹配 onError 方法签名
*/
class CustomCameraStateCallback extends android.hardware.camera2.CameraDevice.StateCallback {
// 必须显式定义构造函数,并调用父类构造函数(UTS 对接 Android 抽象类的强制要求)
public constructor() {
super(); // 关键:调用父类 CameraDevice.StateCallback 的构造函数
}
/**
* 实现 onOpened 抽象方法(完全匹配原生签名)
* 原生:public abstract void onOpened(CameraDevice device);
*/
override onOpened(device : android.hardware.camera2.CameraDevice) : void {
console.log("相机打开成功");
const cameraDevice = device;
const surface = imageReader!!.getSurface();
const surfaces = [surface];
class CustomCaptureSessionCallback extends android.hardware.camera2.CameraCaptureSession.StateCallback {
// 全局相机设备引用(用于创建采集请求)
private localCameraDevice : android.hardware.camera2.CameraDevice;
private surface : android.view.Surface;
// 构造函数:传入相机设备和Surface(避免闭包变量问题)
public constructor(cameraDevice : android.hardware.camera2.CameraDevice, surface : android.view.Surface) {
super(); // 必须调用父类构造
this.localCameraDevice = cameraDevice;
this.surface = surface;
}
/**
* 实现抽象方法:采集会话配置成功
*/
override onConfigured(session : android.hardware.camera2.CameraCaptureSession) : void {
console.log("采集会话配置成功,启动持续帧采集");
// 创建预览采集请求
const requestBuilder = this.localCameraDevice.createCaptureRequest(
android.hardware.camera2.CameraDevice.TEMPLATE_PREVIEW
);
requestBuilder.addTarget(this.surface);
// 启动持续采集(真正触发相机输出数据)
session.setRepeatingRequest(
requestBuilder.build(),
null,
backgroundHandler // 使用后台线程处理采集回调
);
}
/**
* 实现抽象方法:采集会话配置失败
*/
override onConfigureFailed(session : android.hardware.camera2.CameraCaptureSession) : void {
console.error("采集会话配置失败");
}
}
const sessionCallback = new CustomCaptureSessionCallback(cameraDevice, surface) as CameraCaptureSession.StateCallback;
// 创建采集会话
cameraDevice.createCaptureSession(
surfaces,
sessionCallback,
backgroundHandler
);
}
/**
* 实现 onDisconnected 抽象方法
* 原生:public abstract void onDisconnected(CameraDevice device);
*/
override onDisconnected(device : android.hardware.camera2.CameraDevice) : void {
console.log("相机连接断开");
this.releaseCameraResources();
}
/**
* 终极修复 onError 方法(逐字匹配原生签名)
* 原生:public abstract void onError(CameraDevice p0, int p1);
* 注意:参数名可以是任意(p0/p1 或 device/errorCode),但类型必须是 CameraDevice + number(对应 int
*/
override onError(device : android.hardware.camera2.CameraDevice, p1 : Int) : void {
console.error(`相机打开失败`);
this.releaseCameraResources();
}
/**
* 可选重写 onClosed 方法
*/
override onClosed(device : android.hardware.camera2.CameraDevice) : void {
console.log("相机已关闭");
super.onClosed(device);
}
/**
* 辅助方法:释放相机资源
*/
private releaseCameraResources() : void {
// if (cameraDevice != null && !cameraDevice.isClosed()) {
// cameraDevice.close();
// cameraDevice = null;
// console.log("相机资源已释放");
// }
}
}
// 实例化回调(无报错)
const cameraStateCallback = new CustomCameraStateCallback();
cameraManager.openCamera(
cameraId, // param0: string(相机ID,必传非null
cameraStateCallback, // param1: StateCallback(回调,必传非null
backgroundHandler // param2: Handler(传null表示用主线程,符合可空要求)
);
} catch (e) {
console.error('启动摄像头失败:', e);
}
}