diff --git a/pages/index/index.uvue b/pages/index/index.uvue
index 54b66f4..9609ed2 100644
--- a/pages/index/index.uvue
+++ b/pages/index/index.uvue
@@ -1,24 +1,188 @@
-
-
-
- {{title}}
-
-
-
-
-
-
\ No newline at end of file
diff --git a/uni_modules/XF-cameraUTS/changelog.md b/uni_modules/XF-cameraUTS/changelog.md
new file mode 100644
index 0000000..5e5871e
--- /dev/null
+++ b/uni_modules/XF-cameraUTS/changelog.md
@@ -0,0 +1,24 @@
+## 1.1.1(2025-10-15)
+ 修复监听持续返回事件问题。
+## 1.1.0(2024-10-13)
+修改AndroidManifest.xml下的包名。
+## 1.0.9(2024-10-12)
+删除takePhoto下的takeMode参数逻辑
+## 1.0.8(2024-10-10)
+兼容处理uniapp无法切换相机和拍照的问题。
+## 1.0.7(2024-10-09)
+修复uniapp环境下不能正常使用插件问题。
+## 1.0.6(2024-09-13)
+去掉插件内部权限申请逻辑。
+## 1.0.5(2024-08-10)
+修复Android不能正常录像的问题。
+## 1.0.4(2024-08-08)
+修复iOS默认相机设置不生效的问题
+## 1.0.3(2024-08-06)
+修改iOS拍照接口默认质量为原图。
+## 1.0.2(2024-07-22)
+修复iOS编译问题。
+## 1.0.1(2024-07-04)
+修复一些bug。
+## 1.0.0(2024-07-04)
+初始化发布
diff --git a/uni_modules/XF-cameraUTS/encrypt b/uni_modules/XF-cameraUTS/encrypt
new file mode 100644
index 0000000..708c6d5
Binary files /dev/null and b/uni_modules/XF-cameraUTS/encrypt differ
diff --git a/uni_modules/XF-cameraUTS/package.json b/uni_modules/XF-cameraUTS/package.json
new file mode 100644
index 0000000..0603086
--- /dev/null
+++ b/uni_modules/XF-cameraUTS/package.json
@@ -0,0 +1,114 @@
+{
+ "id": "XF-cameraUTS",
+ "displayName": "UTS实现自定义相机拍照和录像功能",
+ "version": "1.1.1",
+ "description": "XF-cameraUTS封装了Android和iOS自定义相机预览的功能。",
+ "keywords": [
+ "相机",
+ "录像",
+ "拍照"
+],
+ "repository": "",
+ "engines": {
+ "HBuilderX": "^3.7.0",
+ "uni-app": "^4.74",
+ "uni-app-x": "^4.74"
+ },
+ "dcloudext": {
+ "type": "component-uts",
+ "sale": {
+ "regular": {
+ "price": "168.00"
+ },
+ "sourcecode": {
+ "price": "0.00"
+ }
+ },
+ "contact": {
+ "qq": ""
+ },
+ "declaration": {
+ "ads": "无",
+ "data": "无",
+ "permissions": "android.permission.CAMERA\nandroid.permission.RECORD_AUDIO\nandroid.permission.WRITE_EXTERNAL_STORAGE\nandroid.permission.READ_EXTERNAL_STORAGE\nPrivacy - Camera Usage Description"
+ },
+ "npmurl": "",
+ "darkmode": "x",
+ "i18n": "x",
+ "widescreen": "x"
+ },
+ "uni_modules": {
+ "dependencies": [],
+ "encrypt": [],
+ "platforms": {
+ "cloud": {
+ "tcb": "√",
+ "aliyun": "√",
+ "alipay": "√"
+ },
+ "client": {
+ "uni-app": {
+ "vue": {
+ "vue2": "x",
+ "vue3": "x"
+ },
+ "web": {
+ "safari": "x",
+ "chrome": "x"
+ },
+ "app": {
+ "vue": "x",
+ "nvue": {
+ "extVersion": "1.0.1",
+ "minVersion": ""
+ },
+ "android": {
+ "extVersion": "1.0.1",
+ "minVersion": "21"
+ },
+ "ios": {
+ "extVersion": "1.0.1",
+ "minVersion": "12"
+ },
+ "harmony": "x"
+ },
+ "mp": {
+ "weixin": "x",
+ "alipay": "x",
+ "toutiao": "x",
+ "baidu": "x",
+ "kuaishou": "x",
+ "jd": "x",
+ "harmony": "x",
+ "qq": "x",
+ "lark": "x"
+ },
+ "quickapp": {
+ "huawei": "x",
+ "union": "x"
+ }
+ },
+ "uni-app-x": {
+ "web": {
+ "safari": "x",
+ "chrome": "x"
+ },
+ "app": {
+ "android": {
+ "extVersion": "1.0.1",
+ "minVersion": "21"
+ },
+ "ios": {
+ "extVersion": "1.0.1",
+ "minVersion": "12"
+ },
+ "harmony": "x"
+ },
+ "mp": {
+ "weixin": "x"
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/XF-cameraUTS/readme.md b/uni_modules/XF-cameraUTS/readme.md
new file mode 100644
index 0000000..9871e04
--- /dev/null
+++ b/uni_modules/XF-cameraUTS/readme.md
@@ -0,0 +1,183 @@
+# **概述**
+
+XF-cameraUTS封装了Android和iOS自定义相机预览的功能。
+
+## **模块调用**
+
+```js
+
+
+```
+
+## 参数说明:
+
+position:
+
+- 类型:字符串型
+- 描述:(可选项)前置或者后置相机。(front前置 |back 后置)
+
+flash:
+
+- 类型:字符串型
+- 描述:(可选项)是否开启闪光灯。( auto, on, off, torch)
+
+
+## **插件接口**
+
+
+
+# **takePhoto**
+
+拍照
+
+## uni-app项目中(nvue)调用示例:
+
+```js
+import { takePhoto } from "@/uni_modules/XF-cameraUTS"
+
+takePhoto({
+ success: (res) => {
+ console.log(res)
+ }
+});
+```
+
+## uni-app x项目(uvue)中调用示例:
+
+```js
+import { takePhoto } from "@/uni_modules/XF-cameraUTS";
+import { TakePhotoOption, TakePhotoSuccessCallbackResult } from "@/uni_modules/XF-cameraUTS/utssdk/interface.uts";
+
+let options = {
+ success: (res : TakePhotoSuccessCallbackResult) => {
+ console.log(res)
+ }
+} as TakePhotoOption;
+takePhoto(options);
+```
+
+## 可用性
+
+iOS、Android系统
+
+可提供的1.0.0及更高版本
+
+
+
+
+# **startRecord**
+
+开始录像
+
+## uni-app项目中(nvue)调用示例:
+
+```js
+import { startRecord } from "@/uni_modules/XF-cameraUTS"
+
+startRecord({
+ success: (res) => {
+ console.log(res)
+ }
+});
+```
+
+## uni-app x项目(uvue)中调用示例:
+
+```js
+import { startRecord } from "@/uni_modules/XF-cameraUTS";
+import { CameraContextStartRecordOption, GeneralCallbackResult } from "@/uni_modules/XF-cameraUTS/utssdk/interface.uts";
+
+let options = {
+ success: (res : GeneralCallbackResult) => {
+ console.log(res)
+ }
+} as CameraContextStartRecordOption;
+startRecord(options);
+```
+
+## 可用性
+
+iOS、Android系统
+
+可提供的1.0.0及更高版本
+
+
+
+
+
+# **stopRecord**
+
+停止录像
+
+## uni-app项目中(nvue)调用示例:
+
+```js
+import { stopRecord } from "@/uni_modules/XF-cameraUTS"
+
+stopRecord({
+ success: (res) => {
+ console.log(res)
+ }
+});
+```
+
+## uni-app x项目(uvue)中调用示例:
+
+```js
+import { stopRecord } from "@/uni_modules/XF-cameraUTS";
+import { CameraContextStopRecordOption, StopRecordSuccessCallbackResult } from "@/uni_modules/XF-cameraUTS/utssdk/interface.uts";
+
+let options = {
+ success: (res : StopRecordSuccessCallbackResult) => {
+ console.log(res)
+ }
+} as CameraContextStopRecordOption;
+stopRecord(options);
+```
+
+## 可用性
+
+iOS、Android系统
+
+可提供的1.0.0及更高版本
+
+
+
+
+# **switchCamera**
+
+切换相机
+
+## uni-app项目中(nvue)调用示例:
+
+```js
+import { switchCamera } from "@/uni_modules/XF-cameraUTS"
+
+switchCamera({
+ position: 'back',
+ success: (res) => {
+ console.log(res)
+ }
+});
+```
+
+## uni-app x项目(uvue)中调用示例:
+
+```js
+import { switchCamera } from "@/uni_modules/XF-cameraUTS";
+import { SwitchOption, GeneralCallbackResult } from "@/uni_modules/XF-cameraUTS/utssdk/interface.uts";
+
+let options = {
+ position: 'back',
+ success: (res : GeneralCallbackResult) => {
+ console.log(res)
+ }
+} as SwitchOption;
+switchCamera(options);
+```
+
+## 可用性
+
+iOS、Android系统
+
+可提供的1.0.8及更高版本
\ No newline at end of file
diff --git a/uni_modules/XF-cameraUTS/utssdk/app-android/AndroidManifest.xml b/uni_modules/XF-cameraUTS/utssdk/app-android/AndroidManifest.xml
new file mode 100644
index 0000000..86f5b64
--- /dev/null
+++ b/uni_modules/XF-cameraUTS/utssdk/app-android/AndroidManifest.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/uni_modules/XF-cameraUTS/utssdk/app-android/CameraManager.uts b/uni_modules/XF-cameraUTS/utssdk/app-android/CameraManager.uts
new file mode 100644
index 0000000..fb0aafc
--- /dev/null
+++ b/uni_modules/XF-cameraUTS/utssdk/app-android/CameraManager.uts
@@ -0,0 +1,26 @@
+import { CameraView } from "./camera";
+
+export class CameraManager {
+ private cameraView : CameraView | null = null;
+
+ private static instance : CameraManager | null = null;
+
+ public static getInstance() : CameraManager {
+ if (CameraManager.instance == null) {
+ CameraManager.instance = new CameraManager();
+ }
+ return CameraManager.instance!;
+ }
+
+ public setCameraView(cameraView ?: CameraView) : void {
+ this.cameraView = cameraView!
+ }
+
+ public getCameraView() : CameraView | null {
+ return this.cameraView;
+ }
+
+ public dispose() : void {
+ this.cameraView = null;
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/XF-cameraUTS/utssdk/app-android/camera.uts b/uni_modules/XF-cameraUTS/utssdk/app-android/camera.uts
new file mode 100644
index 0000000..e223355
--- /dev/null
+++ b/uni_modules/XF-cameraUTS/utssdk/app-android/camera.uts
@@ -0,0 +1,474 @@
+import ProcessCameraProvider from 'androidx.camera.lifecycle.ProcessCameraProvider'
+import Preview from 'androidx.camera.core.Preview'
+import MirrorMode from 'androidx.camera.core.MirrorMode'
+import PreviewView from 'androidx.camera.view.PreviewView'
+import Context from 'android.content.Context'
+import FrameLayout from 'android.widget.FrameLayout'
+import ViewGroup from 'android.view.ViewGroup'
+import CameraSelector from 'androidx.camera.core.CameraSelector'
+import ImageAnalysis from 'androidx.camera.core.ImageAnalysis'
+import UseCaseGroup from 'androidx.camera.core.UseCaseGroup'
+import ContextCompat from 'androidx.core.content.ContextCompat'
+
+import LifecycleOwner from 'androidx.lifecycle.LifecycleOwner'
+import ImageCapture from 'androidx.camera.core.ImageCapture'
+import Size from 'android.util.Size'
+import ImageCaptureException from 'androidx.camera.core.ImageCaptureException'
+import File from 'java.io.File'
+
+import ImageProxy from 'androidx.camera.core.ImageProxy';
+import CameraControl from 'androidx.camera.core.CameraControl';
+
+import VideoCapture from 'androidx.camera.video.VideoCapture'
+import Recorder from 'androidx.camera.video.Recorder'
+import Recording from 'androidx.camera.video.Recording'
+import FileOutputOptions from 'androidx.camera.video.FileOutputOptions'
+import Quality from 'androidx.camera.video.Quality'
+import QualitySelector from 'androidx.camera.video.QualitySelector'
+import VideoRecordEvent from 'androidx.camera.video.VideoRecordEvent'
+
+// import Bitmap from 'android.graphics.Bitmap';
+// import FileOutputStream from 'java.io.FileOutputStream';
+// import MediaMetadataRetriever from 'android.media.MediaMetadataRetriever';
+
+import ExecutorService from 'java.util.concurrent.ExecutorService'
+import Executors from 'java.util.concurrent.Executors'
+import ImageFormat from 'android.graphics.ImageFormat';
+
+import {
+ TakePhotoOption,
+ GeneralCallbackResult,
+ TakePhotoSuccessCallbackResult,
+ CameraContextSetZoomOption,
+ SetZoomSuccessCallbackResult,
+ CameraContextStopRecordOption,
+ CameraContextStartRecordOption,
+ StartRecordTimeoutCallbackResult,
+ StopRecordSuccessCallbackResult,
+ OnCameraFrameCallback,
+ OnCameraFrameCallbackResult,
+ CameraFrameListenerStartOption,
+ StopOption,
+
+ CameraConfig,
+ FlashMode,
+ DevicePosition,
+ Resolution,
+ //FrameSize,
+ QualityType,
+ OnCameraFrameListenerOption
+} from '../interface'
+import Bitmap from "android.graphics.Bitmap";
+import FileOutputStream from "java.io.FileOutputStream";
+
+const FLASH_MODE_MAP = new Map([
+ ['auto', ImageCapture.FLASH_MODE_AUTO],
+ ['on', ImageCapture.FLASH_MODE_ON],
+ ['torch', ImageCapture.FLASH_MODE_ON],
+ ['off', ImageCapture.FLASH_MODE_OFF],
+])
+const DEVICE_POSITION_MAP = new Map([
+ ['back', CameraSelector.LENS_FACING_BACK],
+ ['front', CameraSelector.LENS_FACING_FRONT],
+])
+
+
+const RESOLUTION_MAP = new Map([
+ ['low', Quality.LOWEST],
+ ['medium', Quality.HD],
+ ['high', Quality.HIGHEST],
+])
+
+const QUALITY_MAP = new Map([
+ ["high", ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY],
+ ["low", ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY],
+])
+
+const MIRROR_MODE = new Map([
+ [true, MirrorMode.MIRROR_MODE_ON],
+ [false, MirrorMode.MIRROR_MODE_OFF],
+])
+
+export class CameraView extends FrameLayout {
+ private previewView : PreviewView;
+ public cameraProvider : ProcessCameraProvider | null = null;
+ public cameraControl : CameraControl | null = null;
+ private imageCapture : ImageCapture | null = null
+ private videoCapture : VideoCapture | null = null
+ private recording : Recording | null = null
+ private comp : UTSComponent;
+ private flashMode : FlashMode = 'auto'
+ private devicePosition : DevicePosition = 'back'
+ private resolution : Resolution = 'medium'
+ // private frameSize : FrameSize = 'medium'
+ private quality : QualityType = 'normal'
+ private videoFile : File | null = null
+ maxZoom : Float = (1).toFloat()
+ minZoom : Float = (1).toFloat()
+ private initialized : boolean = false
+ private isListening : boolean = false
+ private videoSelfieMirror : boolean | null = null
+ private cameraContextStopRecordOption : CameraContextStopRecordOption | null = null
+ private onCameraFrameCallback : OnCameraFrameCallback | null = null
+ private cameraExecutor : ExecutorService | null = null
+ private onCameraFrameListenerOption : OnCameraFrameListenerOption | null = null;
+
+ constructor(context : Context, comp : UTSComponent, config : CameraConfig = {} as CameraConfig) {
+ super(context);
+ this.comp = comp
+ this.flashMode = config.flash ?? 'auto'
+ this.devicePosition = config.devicePosition ?? 'back'
+ this.resolution = config.resolution ?? 'medium'
+ // this.frameSize = config.frameSize ?? 'medium'
+
+ this.previewView = new PreviewView(context);
+ let layoutParam = new FrameLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT);
+ this.addView(this.previewView, layoutParam);
+ this.setupCamera()
+ }
+ setupCamera() {
+ // this.checkSelfPermission().then(() => {
+ let cameraProviderFuture = ProcessCameraProvider.getInstance(UTSAndroid.getUniActivity()!)
+ cameraProviderFuture.addListener(() => {
+ try {
+ this.cameraProvider = cameraProviderFuture.get()
+ this.bindCamera();
+ } catch (e) {
+ let ret : Map = new Map();
+ ret.set("errMsg", JSON.stringify(e))
+ this.comp.$emit('error', ret)
+ }
+ }, ContextCompat.getMainExecutor(UTSAndroid.getUniActivity()!))
+ // }).catch((err) => {
+ // let ret : Map = new Map();
+ // ret.set("errMsg", err ?? '权限不足')
+ // this.comp.$emit('error', ret)
+ // })
+ }
+ bindCamera() {
+
+ let targetResolution = RESOLUTION_MAP.get(this.resolution) ?? Quality.HD;
+ let captureMode = QUALITY_MAP.get(this.quality) ?? ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY;
+
+ // 创建一个Preview对象,用于显示相机预览
+ let preview = new Preview.Builder()
+ .build();
+ preview.setSurfaceProvider(this.previewView.getSurfaceProvider());
+
+ // 创建一个ImageCapture对象,用于拍照
+ let imageCapture = new ImageCapture.Builder()
+ .setFlashMode(FLASH_MODE_MAP.get(this.flashMode)!)
+ .setCaptureMode(captureMode)
+ .build();
+ // 选择要使用的相机(后置或前置)
+ let cameraSelector = new CameraSelector.Builder()
+ .requireLensFacing(DEVICE_POSITION_MAP.get(this.devicePosition)!)
+ .build();
+
+ let recorder = Recorder.Builder()
+ .setQualitySelector(QualitySelector.from(targetResolution))
+ let mirrorMode = this.videoSelfieMirror == null ? MirrorMode.MIRROR_MODE_ON_FRONT_ONLY : MIRROR_MODE.get(this.videoSelfieMirror!);
+ let videoCapture = new VideoCapture.Builder(recorder.build())
+ .setMirrorMode(mirrorMode!)
+ .build();
+
+ this.videoCapture = videoCapture
+
+ // 将参数应用到 VideoCapture
+ this.imageCapture = imageCapture;
+ this.cameraProvider!.unbindAll();
+ let imageAnalysis = ImageAnalysis.Builder()
+ // .setTargetResolution(new Size(1280, 720))
+ .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
+ .build()
+
+ let viewPort = this.previewView.getViewPort()!
+ let useCaseGroup = new UseCaseGroup.Builder()
+ .addUseCase(preview)
+ .addUseCase(imageAnalysis)
+ .addUseCase(imageCapture)
+ .addUseCase(videoCapture)
+ .setViewPort(viewPort)
+ .build();
+
+ this.cameraExecutor = Executors.newSingleThreadExecutor()
+ let _this = this
+ class Analyzer implements ImageAnalysis.Analyzer {
+ constructor() {
+
+ }
+ override analyze(image : ImageProxy) {
+ if (_this.isListening) {
+ let imageFormat = image.getFormat();
+ // 获取图像的宽度和高度
+ let width = image.width
+ let height = image.height
+
+ if (imageFormat == ImageFormat.YUV_420_888) {
+ _this.onCameraFrameCallback?.({
+ width,
+ height,
+ data: image
+ } as OnCameraFrameCallbackResult)
+
+ _this.onCameraFrameListenerOption?.success?.({
+ width,
+ height,
+ data: image
+ } as OnCameraFrameCallbackResult)
+ }
+ }
+ image.close();
+ }
+ override getDefaultTargetResolution() : Size {
+ // 返回默认的目标分辨率
+ return Size(640, 480)
+ }
+ }
+ imageAnalysis.setAnalyzer(this.cameraExecutor!, new Analyzer() as ImageAnalysis.Analyzer)
+
+ let camera = this.cameraProvider!.bindToLifecycle(
+ UTSAndroid.getUniActivity()! as LifecycleOwner,
+ cameraSelector,
+ useCaseGroup
+ // preview,
+ // imageCapture,
+ // videoCapture
+ );
+ this.cameraControl = camera.getCameraControl();
+ if (!this.initialized) {
+ this.initialized = true
+ let zoomState = camera.getCameraInfo().getZoomState().getValue();
+ this.maxZoom = zoomState?.getMaxZoomRatio() ?? (1).toFloat();
+ this.minZoom = zoomState?.getMinZoomRatio() ?? (1).toFloat();
+ let maxZoom = this.maxZoom
+
+ let ret : Map = new Map();
+ ret.set("maxZoom", maxZoom)
+ this.comp.$emit('ready', ret)
+ }
+
+ }
+ unbindCamera() {
+ this.cameraProvider?.unbindAll()
+ this.cameraExecutor?.shutdown()
+ }
+ // checkSelfPermission() : Promise {
+ // let permissionNeed = ["android.permission.CAMERA", "android.permission.RECORD_AUDIO", "android.permission.READ_EXTERNAL_STORAGE"]
+ // return new Promise((resoleve, _) => {
+ // resoleve(true);
+ // UTSAndroid.requestSystemPermission(UTSAndroid.getUniActivity()!, permissionNeed, function (allRight : boolean, list : string[]) {
+ // if (allRight) {
+ // resoleve(true);
+ // } else {
+ // reject(list);
+ // }
+ // }, function (_ : boolean, grantedList : string[]) {
+ // //grantedList
+ // reject(grantedList);
+ // })
+ // })
+ // }
+ setConfig(config : CameraConfig = {} as CameraConfig) {
+ this.flashMode = config.flash ?? 'auto'
+ this.devicePosition = config.devicePosition ?? 'back'
+ this.resolution = config.resolution ?? 'medium'
+ // this.frameSize = config.frameSize ?? 'medium'
+ }
+ setFlash(mode : FlashMode) {
+ this.flashMode = mode
+ if (FLASH_MODE_MAP.get(mode) == null || this.imageCapture == null) return
+ this.imageCapture!.setFlashMode(FLASH_MODE_MAP.get(mode)!)
+ }
+ switchCamera(position : DevicePosition) {
+ this.devicePosition = position
+ if (DEVICE_POSITION_MAP.get(position) == null || this.cameraProvider == null) return
+ this.unbindCamera()
+ this.bindCamera()
+ }
+ isResolutionSupported(context : Context, cameraSelector : CameraSelector, targetResolution : Size) : boolean {
+ return true
+ }
+ takePhoto(option : TakePhotoOption) {
+ let quality = option.quality ?? "high";
+ this.quality = quality;
+ if (quality == 'low') {
+ this.unbindCamera()
+ this.bindCamera()
+ }
+ // 设置输出目录
+ let privateDir = UTSAndroid.getAppCachePath()!;
+ const tempFile = new File(privateDir);
+ if (!tempFile.exists()) {
+ tempFile.mkdirs()
+ }
+ // 设置文件名
+ const photoFile = new File(privateDir, Date.now() + '.png');
+
+ // 处理镜像
+ let metadata = new ImageCapture.Metadata();
+ metadata.setReversedHorizontal(option.selfieMirror ?? DEVICE_POSITION_MAP.get(this.devicePosition)! == CameraSelector.LENS_FACING_FRONT);
+
+ const outputOptions = ImageCapture.OutputFileOptions.Builder(photoFile).setMetadata(metadata).build()
+ class OnImageSavedCallback implements ImageCapture.OnImageSavedCallback {
+ override onImageSaved(outputFileResults : ImageCapture.OutputFileResults) {
+ option.success?.({
+ tempImagePath: photoFile.getAbsolutePath(),
+ quality: quality,
+ errMsg: 'ok'
+ } as TakePhotoSuccessCallbackResult)
+ option.complete?.({
+ errMsg: 'ok'
+ } as GeneralCallbackResult)
+ }
+ override onError(err : ImageCaptureException) {
+ const result : GeneralCallbackResult = {
+ errMsg: err.toString()
+ }
+ option.fail?.(result)
+ option.complete?.(result)
+ }
+ override onCaptureStarted() {
+
+ }
+ }
+ this.imageCapture!.takePicture(outputOptions, ContextCompat.getMainExecutor(UTSAndroid.getUniActivity()!), new OnImageSavedCallback() as ImageCapture.OnImageSavedCallback)
+
+ }
+ setZoom(option : CameraContextSetZoomOption) {
+ if (this.cameraControl != null) {
+ try {
+ this.cameraControl!.setZoomRatio(option.zoom.toFloat())
+ option.success?.({
+ zoom: option.zoom,
+ errMsg: 'ok'
+ } as SetZoomSuccessCallbackResult)
+ option.complete?.({
+ errMsg: 'ok'
+ } as GeneralCallbackResult)
+ } catch (e) {
+ const result : GeneralCallbackResult = {
+ errMsg: e.toString()
+ }
+ option.complete?.(result)
+ option.fail?.(result)
+ }
+ } else {
+ const result : GeneralCallbackResult = {
+ errMsg: '相机不存在'
+ }
+ option.complete?.(result)
+ option.fail?.(result)
+ }
+ }
+ startRecord(option : CameraContextStartRecordOption) {
+ if (option.selfieMirror != null && this.videoSelfieMirror != option.selfieMirror) {
+ this.videoSelfieMirror = option.selfieMirror
+ this.unbindCamera()
+ this.bindCamera()
+ }
+ if (this.recording != null || this.videoCapture == null) {
+ option.fail?.({
+ errMsg: '已经在录制或相机不存在'
+ } as GeneralCallbackResult)
+ option.complete?.({
+ errMsg: '已经在录制或相机不存在'
+ } as GeneralCallbackResult)
+ return
+ }
+ // 设置输出目录
+ let privateDir = UTSAndroid.getAppCachePath()!;
+ // 设置文件名
+ this.videoFile = new File(privateDir, Date.now() + 'video.mp4');
+ let outputFileOptions = new FileOutputOptions.Builder(this.videoFile!).build();
+
+
+ this.recording = this.videoCapture!.output
+ .prepareRecording(UTSAndroid.getUniActivity()!, outputFileOptions)
+ .withAudioEnabled()
+ // .apply(()=>{
+ // })
+ .start(ContextCompat.getMainExecutor(UTSAndroid.getUniActivity()!), (recordEvent : VideoRecordEvent) => {
+ if (recordEvent instanceof VideoRecordEvent.Start) {
+ option.success?.({
+ errMsg: 'ok'
+ } as GeneralCallbackResult)
+
+ if (option.timeout != null && option.timeout! > 0) {
+ setTimeout(() => {
+ this.recording?.stop()
+ }, option.timeout!)
+ }
+ } else if (recordEvent instanceof VideoRecordEvent.Finalize) {
+ this.recording?.close()
+ this.recording = null
+ let tempVideoPath = this.videoFile?.getAbsolutePath()!
+ //let tempThumbPath = extractThumbnail(tempVideoPath) ?? ''
+ this.cameraContextStopRecordOption?.success?.({
+ tempVideoPath,
+ errMsg: 'ok'
+ } as StopRecordSuccessCallbackResult)
+
+ option.timeoutCallback?.({
+ tempVideoPath,
+ } as StartRecordTimeoutCallbackResult)
+ }
+ })
+ }
+ stopRecord(option : CameraContextStopRecordOption) {
+ if (this.recording == null) {
+ option.fail?.({
+ errMsg: '未开始录制'
+ } as GeneralCallbackResult)
+ option.complete?.({
+ errMsg: '未开始录制'
+ } as GeneralCallbackResult)
+ return
+ }
+ this.cameraContextStopRecordOption = option
+ this.recording?.stop()
+ }
+ onCameraFrame(callback : OnCameraFrameCallback | null = null) {
+ this.onCameraFrameCallback = callback
+ }
+
+ onCameraFrameListener(option : OnCameraFrameListenerOption) {
+ this.onCameraFrameListenerOption = option
+ }
+
+ cameraFrameOnStart(option ?: CameraFrameListenerStartOption) {
+ this.isListening = true
+ option?.success?.({
+ errMsg: 'ok'
+ } as GeneralCallbackResult)
+ }
+ cameraFrameOnStop(option ?: StopOption) {
+ this.isListening = false
+ option?.success?.({
+ errMsg: 'ok'
+ } as GeneralCallbackResult)
+ }
+
+}
+
+
+// // 从视频文件中提取封面的函数
+// function extractThumbnail(videoUri : string) : string | null {
+// let retriever = new MediaMetadataRetriever()
+// retriever.setDataSource(videoUri)
+// // 提取视频的某一帧作为封面
+// let thumbnail = retriever.frameAtTime
+// // 保存封面到文件
+// if (thumbnail != null) {
+// let privateDir = UTSAndroid.getAppCachePath();
+// const thumbnailFile = new File(privateDir, Date.now() + '_thumbnail.jpg');
+// let outputStream = FileOutputStream(thumbnailFile)
+// thumbnail.compress(Bitmap.CompressFormat.JPEG, 90, outputStream)
+// outputStream.close()
+// return thumbnailFile.getAbsolutePath()
+// }
+// retriever.release()
+// return null
+// }
\ No newline at end of file
diff --git a/uni_modules/XF-cameraUTS/utssdk/app-android/config.json b/uni_modules/XF-cameraUTS/utssdk/app-android/config.json
new file mode 100644
index 0000000..39e876f
--- /dev/null
+++ b/uni_modules/XF-cameraUTS/utssdk/app-android/config.json
@@ -0,0 +1,20 @@
+{
+ "minSdkVersion": "19",
+ "dependencies": [
+ // 使用camera2实现的CameraX核心库
+ // 定义camerax_version为"1.4.0-alpha04"
+ // 下面的行是可选的,因为core库通过camera-camera2间接包含
+ "androidx.camera:camera-core:1.4.0-alpha04"
+ "androidx.camera:camera-camera2:1.4.0-alpha04"
+ // 如果你想要额外使用CameraX生命周期库
+ "androidx.camera:camera-lifecycle:1.4.0-alpha04"
+ // 如果你想要额外使用CameraX视频捕获库
+ "androidx.camera:camera-video:1.4.0-alpha04"
+ // 如果你想要额外使用CameraX视图类
+ "androidx.camera:camera-view:1.4.0-alpha04"
+ // 如果你想要额外添加CameraX ML Kit Vision集成
+ // "androidx.camera:camera-mlkit-vision:1.4.0-alpha04"
+ // 如果你想要额外使用CameraX扩展库
+ // "androidx.camera:camera-extensions:1.4.0-alpha04"
+ ]
+}
\ No newline at end of file
diff --git a/uni_modules/XF-cameraUTS/utssdk/app-android/index.uts b/uni_modules/XF-cameraUTS/utssdk/app-android/index.uts
new file mode 100644
index 0000000..c44f570
--- /dev/null
+++ b/uni_modules/XF-cameraUTS/utssdk/app-android/index.uts
@@ -0,0 +1,252 @@
+import { CameraView } from './camera'
+import {
+ TakePhotoOption,
+ GeneralCallbackResult,
+ CameraContextSetZoomOption,
+ CameraContextStartRecordOption,
+ CameraContextStopRecordOption,
+ // CameraFrameListener,
+ OnCameraFrameCallback,
+ // OnCameraFrameCallbackResult,
+ CameraFrameListenerStartOption,
+ StopOption,
+ TakePhoto,
+ SetZoom,
+ StartRecord,
+ StopRecord,
+ StartCameraFrame,
+ StopCameraFrame,
+ OnCameraFrameListenerOption,
+ SwitchOption,
+ SwitchCamera
+} from '../interface'
+import { CameraManager } from "./CameraManager";
+
+
+class CameraFrameListener {
+ context : CameraContext
+ constructor(context : CameraContext) {
+ this.context = context
+ }
+ start() : void
+ start(option ?: CameraFrameListenerStartOption) {
+ this.context.getCamera().then((camera : CameraView) => {
+ camera.cameraFrameOnStart(option)
+ })
+ }
+ stop() : void
+ stop(option ?: StopOption) {
+ this.context.getCamera().then((camera : CameraView) => {
+ camera.cameraFrameOnStop(option)
+ })
+ }
+}
+
+class CameraContext {
+ private mCameraFrameListener : CameraFrameListener | null = null
+ constructor() {
+ }
+ getCamera() : Promise {
+ let cameraPreview = CameraManager.getInstance().getCameraView()
+ if (cameraPreview != null) {
+ return Promise.resolve(cameraPreview);
+ }
+ let resolveFunc : ((res : CameraView) => void) | null = null
+ let rejectFunc : ((res : string) => void) | null = null
+
+ let maxAttempts : number = 5
+ let currentAttempt : number = 0
+
+ function attemptGetCamera() {
+ let camera = CameraManager.getInstance().getCameraView()
+ if (camera !== null) {
+ resolveFunc?.(camera)
+ } else if (currentAttempt < maxAttempts) {
+ currentAttempt++
+ setTimeout(() => {
+ attemptGetCamera()
+ }, 100)
+ } else {
+ rejectFunc?.('Camera not found after multiple attempts')
+ }
+ }
+ return new Promise((resolve, reject) => {
+ resolveFunc = resolve
+ rejectFunc = reject
+ attemptGetCamera()
+ })
+ }
+
+
+ takePhoto(option : TakePhotoOption) {
+ this.getCamera().then((camera : CameraView) => {
+ camera.takePhoto(option)
+ }).catch(() => {
+ option.fail?.({
+ errMsg: '未找到相机'
+ } as GeneralCallbackResult)
+ option.complete?.({
+ errMsg: '未找到相机'
+ } as GeneralCallbackResult)
+ })
+
+ }
+ setZoom(option : CameraContextSetZoomOption) {
+ this.getCamera().then((camera : CameraView) => {
+ camera.setZoom(option)
+ }).catch(() => {
+ option.fail?.({
+ errMsg: '未找到相机'
+ } as GeneralCallbackResult)
+ option.complete?.({
+ errMsg: '未找到相机'
+ } as GeneralCallbackResult)
+ })
+ }
+ startRecord(option : CameraContextStartRecordOption) {
+ this.getCamera().then((camera : CameraView) => {
+ camera.startRecord(option)
+ }).catch(() => {
+ option.fail?.({
+ errMsg: '未找到相机'
+ } as GeneralCallbackResult)
+ option.complete?.({
+ errMsg: '未找到相机'
+ } as GeneralCallbackResult)
+ })
+ }
+ stopRecord(option : CameraContextStopRecordOption) {
+ this.getCamera().then((camera : CameraView) => {
+ camera.stopRecord(option)
+ }).catch(() => {
+ option.fail?.({
+ errMsg: '未找到相机'
+ } as GeneralCallbackResult)
+ option.complete?.({
+ errMsg: '未找到相机'
+ } as GeneralCallbackResult)
+ })
+ }
+ onCameraFrame() : CameraFrameListener
+ onCameraFrame(callback : OnCameraFrameCallback | null = null) : CameraFrameListener {
+ if (this.mCameraFrameListener == null) {
+ this.mCameraFrameListener = new CameraFrameListener(this)
+ }
+ this.getCamera().then((camera : CameraView) => {
+ camera.onCameraFrame(callback)
+ })
+ return this.mCameraFrameListener!
+ }
+ //////////////////////////////////////////////////////
+ onCameraFrameListener(option : OnCameraFrameListenerOption) {
+ this.getCamera().then((camera : CameraView) => {
+ camera.onCameraFrameListener(option)
+ }).catch(() => {
+ console.log("未找到相机")
+ })
+ }
+
+ cameraFrameOnStart(option : CameraFrameListenerStartOption) {
+ this.getCamera().then((camera : CameraView) => {
+ camera.cameraFrameOnStart(option)
+ }).catch(() => {
+ option.fail?.({
+ errMsg: '未找到相机'
+ } as GeneralCallbackResult)
+ option.complete?.({
+ errMsg: '未找到相机'
+ } as GeneralCallbackResult)
+ })
+ }
+
+ cameraFrameOnStop(option : StopOption) {
+ this.getCamera().then((camera : CameraView) => {
+ camera.cameraFrameOnStop(option)
+ }).catch(() => {
+ option.fail?.({
+ errMsg: '未找到相机'
+ } as GeneralCallbackResult)
+ option.complete?.({
+ errMsg: '未找到相机'
+ } as GeneralCallbackResult)
+ })
+ }
+
+ switchCamera(option : SwitchOption) {
+ this.getCamera().then((camera : CameraView) => {
+ camera.switchCamera(option.position)
+
+ option.success?.({
+ errMsg: 'ok'
+ } as GeneralCallbackResult)
+ option.complete?.({
+ errMsg: 'ok'
+ } as GeneralCallbackResult)
+ }).catch(() => {
+ option.fail?.({
+ errMsg: '未找到相机'
+ } as GeneralCallbackResult)
+ option.complete?.({
+ errMsg: '未找到相机'
+ } as GeneralCallbackResult)
+ })
+ }
+}
+
+let context : CameraContext = new CameraContext();
+
+export function createCameraContext() : CameraContext {
+ return context;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+
+export const takePhoto : TakePhoto = function (options : TakePhotoOption) {
+ UTSAndroid.getDispatcher("main").async(function (_) {
+ context.takePhoto(options)
+ }, null)
+}
+
+export const setZoom : SetZoom = function (options : CameraContextSetZoomOption) {
+ UTSAndroid.getDispatcher("main").async(function (_) {
+ context.setZoom(options)
+ }, null)
+}
+
+export const startRecord : StartRecord = function (options : CameraContextStartRecordOption) {
+ UTSAndroid.getDispatcher("main").async(function (_) {
+ context.startRecord(options)
+ }, null)
+}
+
+export const stopRecord : StopRecord = function (options : CameraContextStopRecordOption) {
+ UTSAndroid.getDispatcher("main").async(function (_) {
+ context.stopRecord(options)
+ }, null)
+}
+
+@UTSJS.keepAlive
+export function onCameraFrameListener(options : OnCameraFrameListenerOption) {
+ UTSAndroid.getDispatcher("main").async(function (_) {
+ context.onCameraFrameListener(options)
+ }, null)
+}
+
+export const startCameraFrame : StartCameraFrame = function (options : CameraFrameListenerStartOption) {
+ UTSAndroid.getDispatcher("main").async(function (_) {
+ context.cameraFrameOnStart(options)
+ }, null)
+}
+
+export const stopCameraFrame : StopCameraFrame = function (options : StopOption) {
+ UTSAndroid.getDispatcher("main").async(function (_) {
+ context.cameraFrameOnStop(options)
+ }, null)
+}
+
+
+export const switchCamera : SwitchCamera = function (options : SwitchOption) {
+ UTSAndroid.getDispatcher("main").async(function (_) {
+ context.switchCamera(options)
+ }, null)
+}
\ No newline at end of file
diff --git a/uni_modules/XF-cameraUTS/utssdk/app-android/index.vue b/uni_modules/XF-cameraUTS/utssdk/app-android/index.vue
new file mode 100644
index 0000000..c37a8c4
--- /dev/null
+++ b/uni_modules/XF-cameraUTS/utssdk/app-android/index.vue
@@ -0,0 +1,152 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/uni_modules/XF-cameraUTS/utssdk/app-ios/CameraManager.uts b/uni_modules/XF-cameraUTS/utssdk/app-ios/CameraManager.uts
new file mode 100644
index 0000000..fb0aafc
--- /dev/null
+++ b/uni_modules/XF-cameraUTS/utssdk/app-ios/CameraManager.uts
@@ -0,0 +1,26 @@
+import { CameraView } from "./camera";
+
+export class CameraManager {
+ private cameraView : CameraView | null = null;
+
+ private static instance : CameraManager | null = null;
+
+ public static getInstance() : CameraManager {
+ if (CameraManager.instance == null) {
+ CameraManager.instance = new CameraManager();
+ }
+ return CameraManager.instance!;
+ }
+
+ public setCameraView(cameraView ?: CameraView) : void {
+ this.cameraView = cameraView!
+ }
+
+ public getCameraView() : CameraView | null {
+ return this.cameraView;
+ }
+
+ public dispose() : void {
+ this.cameraView = null;
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/XF-cameraUTS/utssdk/app-ios/camera.uts b/uni_modules/XF-cameraUTS/utssdk/app-ios/camera.uts
new file mode 100644
index 0000000..270f31f
--- /dev/null
+++ b/uni_modules/XF-cameraUTS/utssdk/app-ios/camera.uts
@@ -0,0 +1,469 @@
+import { UIImage, UIView } from 'UIKit';
+import { AVCaptureDevice, AVCaptureDeviceInput, AVCaptureSession, AVCaptureVideoPreviewLayer, AVMediaType, AVLayerVideoGravity, AVCaptureVideoDataOutput, AVCaptureVideoDataOutputSampleBufferDelegate, AVCaptureOutput, AVCaptureConnection, AVCapturePhotoOutput, AVCapturePhotoSettings, AVCapturePhotoCaptureDelegate, AVCaptureMovieFileOutput, AVCaptureFileOutputRecordingDelegate, AVCaptureFileOutput } from "AVFoundation";
+import { CMSampleBuffer, CMSampleBufferGetImageBuffer } from 'CoreMedia';
+import { DispatchQueue } from 'Dispatch';
+import { CVPixelBufferGetWidth, CVPixelBufferGetHeight } from "CoreVideo";
+import { NSError, URL } from "Foundation";
+
+import {
+ TakePhotoOption,
+ GeneralCallbackResult,
+ TakePhotoSuccessCallbackResult,
+ CameraContextSetZoomOption,
+ SetZoomSuccessCallbackResult,
+ CameraContextStopRecordOption,
+ CameraContextStartRecordOption,
+ StartRecordTimeoutCallbackResult,
+ StopRecordSuccessCallbackResult,
+ OnCameraFrameCallback,
+ OnCameraFrameCallbackResult,
+ OnCameraFrameListenerOption,
+ CameraFrameListenerStartOption,
+ StopOption,
+
+ CameraConfig,
+ FlashMode,
+ DevicePosition,
+ Resolution
+} from '../interface'
+import { AVCapturePhoto } from 'AVFoundation';
+
+
+
+const RESOLUTION_MAP = new Map([
+ ['low', AVCaptureSession.Preset.low],
+ ['medium', AVCaptureSession.Preset.medium],
+ ['high', AVCaptureSession.Preset.high],
+])
+
+const FLASH_MODE_MAP = new Map([
+ ['auto', AVCaptureDevice.TorchMode.auto],
+ ['on', AVCaptureDevice.TorchMode.on],
+ ['torch', AVCaptureDevice.TorchMode.on],
+ ['off', AVCaptureDevice.TorchMode.off],
+])
+
+
+let fileOutputRecordingDelegate : MyAVCaptureFileOutputRecordingDelegate | null = null;
+let capturePhotoCaptureDelegate : MyAVCapturePhotoCaptureDelegate | null = null;
+let onCameraFrameListenerOption : OnCameraFrameListenerOption | null = null;
+let cameraContextStopRecordOption : CameraContextStopRecordOption | null = null;
+
+class MyAVCaptureFileOutputRecordingDelegate implements AVCaptureFileOutputRecordingDelegate {
+ private option : CameraContextStartRecordOption;
+
+ constructor(option : CameraContextStartRecordOption) {
+ this.option = option;
+ }
+
+ fileOutput(output : AVCaptureFileOutput, @argumentLabel("didStartRecordingTo") fileURL : URL, @argumentLabel("from") connections : [AVCaptureConnection]) : void {
+ this.option.success?.({
+ errMsg: 'ok'
+ } as GeneralCallbackResult)
+ }
+
+ fileOutput(output : AVCaptureFileOutput, @argumentLabel("didFinishRecordingTo") outputFileURL : URL, @argumentLabel("from") connections : [AVCaptureConnection], @argumentLabel("error") error ?: NSError) : void {
+ if (error == null) {
+ cameraContextStopRecordOption?.success?.({
+ tempVideoPath: outputFileURL.path + "",
+ errMsg: 'ok'
+ } as StopRecordSuccessCallbackResult)
+
+ this.option.timeoutCallback?.({
+ tempVideoPath: outputFileURL.path + ""
+ } as StartRecordTimeoutCallbackResult)
+ } else {
+ cameraContextStopRecordOption?.fail?.({
+ errMsg: '录制失败'
+ } as GeneralCallbackResult)
+ cameraContextStopRecordOption?.complete?.({
+ errMsg: '录制失败'
+ } as GeneralCallbackResult)
+ }
+
+ }
+
+}
+
+class MyAVCapturePhotoCaptureDelegate implements AVCapturePhotoCaptureDelegate {
+ private option : TakePhotoOption;
+
+ constructor(option : TakePhotoOption) {
+ this.option = option;
+ }
+
+ captureOutput(output : AVCapturePhotoOutput, @argumentLabel("didFinishProcessingPhoto") photo : AVCapturePhoto, @argumentLabel("error") error : Error) : void {
+ let imageData = photo.fileDataRepresentation()
+ if (imageData == null) {
+ const result : GeneralCallbackResult = {
+ errMsg: "拍照失败"
+ }
+ this.option.fail?.(result)
+ this.option.complete?.(result)
+ return;
+ }
+ let path = UTSiOS.getDataPath() + (new Date().getTime()).toString() + ".jpg";
+ let url = new URL(fileURLWithPath = path);
+
+ let quality = this.option.quality ?? "original"
+ if (quality == 'original') {
+ try {
+ UTSiOS.try(imageData!.write(to = url, options = NSData.WritingOptions.atomic))
+
+ this.option.success?.({
+ tempImagePath: path,
+ quality: quality,
+ errMsg: 'ok'
+ } as TakePhotoSuccessCallbackResult)
+ this.option.complete?.({
+ errMsg: 'ok'
+ } as GeneralCallbackResult)
+ } catch (e) {
+ const result : GeneralCallbackResult = {
+ errMsg: JSON.stringify(e)
+ }
+ this.option.fail?.(result)
+ this.option.complete?.(result)
+ }
+ } else {
+ let image = UIImage(data = imageData!)
+ if (image != null) {
+ let _quality : number = 0.5;
+ if (quality == 'high') {
+ _quality = 1.0;
+ } else if (quality == 'normal') {
+ _quality = 0.8;
+ } else if (quality == 'low') {
+ _quality = 0.5;
+ }
+ let data = image!.jpegData(compressionQuality = _quality.toDouble())!
+
+ try {
+ UTSiOS.try(data.write(to = url, options = NSData.WritingOptions.atomic))
+
+ this.option.success?.({
+ tempImagePath: path,
+ quality: quality,
+ errMsg: 'ok'
+ } as TakePhotoSuccessCallbackResult)
+ this.option.complete?.({
+ errMsg: 'ok'
+ } as GeneralCallbackResult)
+ } catch (e) {
+ const result : GeneralCallbackResult = {
+ errMsg: JSON.stringify(e)
+ }
+ this.option.fail?.(result)
+ this.option.complete?.(result)
+ }
+ } else {
+ const result : GeneralCallbackResult = {
+ errMsg: "拍照失败"
+ }
+ this.option.fail?.(result)
+ this.option.complete?.(result)
+ }
+ }
+ }
+
+}
+
+export class CameraView implements AVCaptureVideoDataOutputSampleBufferDelegate {
+
+ private captureSession ?: AVCaptureSession;
+ private videoDeviceInput ?: AVCaptureDeviceInput;
+ private photoDeviceOutput ?: AVCapturePhotoOutput;
+ private movieDeviceOutput ?: AVCaptureMovieFileOutput;
+ private previewLayer ?: AVCaptureVideoPreviewLayer;
+ private previewView ?: UIView;
+ private comp ?: UTSComponent;
+ private flashMode : FlashMode = 'auto'
+ private devicePosition : DevicePosition = 'back'
+ private resolution : Resolution = 'medium'
+ private isListening : boolean = false
+ private onCameraFrameCallback : OnCameraFrameCallback | null = null;
+
+ constructor(previewView : UIView, comp : UTSComponent, config : CameraConfig = {} as CameraConfig) {
+ super.init();
+ this.previewView = previewView
+ this.comp = comp
+ this.flashMode = config.flash ?? 'auto'
+ this.devicePosition = config.devicePosition ?? 'back'
+ this.resolution = config.resolution ?? 'medium'
+ this.setupCamera();
+ }
+
+ setupCamera() {
+ this.captureSession = new AVCaptureSession();
+ let targetResolution = RESOLUTION_MAP.get(this.resolution) ?? AVCaptureSession.Preset.high;
+ this.captureSession!.sessionPreset = targetResolution;
+ let device = this.getCameraDevice();
+ try {
+ //相机
+ let deviceInput = UTSiOS.try(new AVCaptureDeviceInput(device = device))
+ if (this.captureSession!.canAddInput(deviceInput)) {
+ this.captureSession!.addInput(deviceInput);
+ }
+ //实时流输出
+ let output = new AVCaptureVideoDataOutput();
+ output.setSampleBufferDelegate(this, queue = DispatchQueue.main);
+ if (this.captureSession!.canAddOutput(output)) {
+ this.captureSession!.addOutput(output);
+ }
+
+ let photoOutput = new AVCapturePhotoOutput();
+ // let photoSettings = new AVCapturePhotoSettings(format = [AVVideoCodecKey = AVVideoCodecType.jpeg]);
+ // photoOutput.setPreparedPhotoSettingsArray([photoSettings], completionHandler = null);
+ if (this.captureSession!.canAddOutput(photoOutput)) {
+ this.captureSession!.addOutput(photoOutput);
+ }
+
+ let movieOutput = new AVCaptureMovieFileOutput();
+ if (this.captureSession!.canAddOutput(movieOutput)) {
+ this.captureSession!.addOutput(movieOutput);
+ }
+
+ this.videoDeviceInput = deviceInput;
+ this.photoDeviceOutput = photoOutput;
+ this.movieDeviceOutput = movieOutput;
+ this.previewLayer = new AVCaptureVideoPreviewLayer(session = this.captureSession!)
+ this.previewLayer!.videoGravity = AVLayerVideoGravity.resizeAspectFill;
+ this.previewLayer!.frame = this.previewView!.layer.bounds;
+ this.previewView!.layer.addSublayer(this.previewLayer!)
+ this.captureSession!.startRunning();
+
+ let ret : Map = new Map();
+ ret.set("maxZoom", device.maxAvailableVideoZoomFactor)
+ let detail : Map = new Map();
+ detail.set("detail", ret)
+ this.comp?.$emit("ready", detail);
+ } catch (e) {
+ let ret : Map = new Map();
+ ret.set("errMsg", JSON.stringify(e))
+ let detail : Map = new Map();
+ detail.set("detail", ret)
+ this.comp?.$emit("error", detail);
+ }
+ }
+
+ getCameraDevice() : AVCaptureDevice {
+ let cameras = AVCaptureDevice.devices(for = AVMediaType.video)
+ let frontCamera : AVCaptureDevice | null = null;
+ let backCamera : AVCaptureDevice | null = null;
+
+ let index = 0;
+ while (index < cameras.length) {
+ if (index == cameras.length) {
+ break;
+ }
+ let camera = cameras[index.toInt()] as AVCaptureDevice;
+ if (camera.position == AVCaptureDevice.Position.back) {
+ backCamera = camera;
+ } else {
+ frontCamera = camera;
+ }
+ index++;
+ }
+
+ if (this.devicePosition == 'back') {
+ return backCamera!;
+ } else {
+ return frontCamera!;
+ }
+ }
+
+ captureOutput(output : AVCaptureOutput, @argumentLabel("didOutput") sampleBuffer : CMSampleBuffer, @argumentLabel("from") connection : AVCaptureConnection) : void {
+
+ if (this.isListening) {
+ let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer)
+ let width = CVPixelBufferGetWidth(imageBuffer!)
+ let height = CVPixelBufferGetHeight(imageBuffer!);
+
+ const res : OnCameraFrameCallbackResult = {
+ width: width as Int,
+ height: height as Int,
+ data: imageBuffer!
+ };
+ this.onCameraFrameCallback?.(res)
+ onCameraFrameListenerOption?.success?.({
+ width: width as Int,
+ height: height as Int,
+ data: imageBuffer!
+ } as OnCameraFrameCallbackResult)
+
+ // this.onCameraFrameListenerOption?.success({
+ // width: width as Int,
+ // height: height as Int,
+ // data: imageBuffer!
+ // } as OnCameraFrameCallbackResult)
+
+
+ // if (imageBuffer != null) {
+ // let ciImage = new CIImage(cvPixelBuffer = imageBuffer!)
+ // let temporaryContext = new CIContext(options = null)
+ // let width = CVPixelBufferGetWidth(imageBuffer!)
+ // let height = CVPixelBufferGetHeight(imageBuffer!);
+ // let videoImage = temporaryContext.createCGImage(ciImage, from = new CGRect(x = 0, y = 0, width = width, height = height))
+ // let image = new UIImage(cgImage = videoImage!)
+
+ // this.onCameraFrameCallback?.({
+ // width: width as Int,
+ // height: height as Int,
+ // data: image
+ // } as OnCameraFrameCallbackResult)
+ // }
+ }
+ }
+
+ bindCamera() {
+ this.captureSession!.startRunning();
+ }
+
+
+ unbindCamera() {
+ this.captureSession!.stopRunning();
+ }
+
+
+ setConfig(config : CameraConfig = {} as CameraConfig) {
+ this.flashMode = config.flash ?? 'auto'
+ this.devicePosition = config.devicePosition ?? 'back'
+ this.resolution = config.resolution ?? 'medium'
+ }
+
+ setFlash(mode : FlashMode) {
+ this.flashMode = mode
+ let targetFlash = FLASH_MODE_MAP.get(this.flashMode) ?? AVCaptureDevice.TorchMode.auto;
+ try {
+ let device = this.getCameraDevice();
+ if (device.hasTorch && device.isTorchAvailable) {
+ UTSiOS.try(device.lockForConfiguration());
+ device.torchMode = targetFlash;
+ device.unlockForConfiguration()
+ }
+ } catch (e) {
+ console.log(e)
+ }
+ }
+
+
+ switchCamera(position : DevicePosition) {
+ if (!this.captureSession!.isRunning) {
+ return;
+ }
+ this.devicePosition = position;
+ try {
+ this.captureSession!.beginConfiguration();
+ this.captureSession!.removeInput(this.videoDeviceInput!);
+ let device = this.getCameraDevice();
+
+ let deviceInput = UTSiOS.try(new AVCaptureDeviceInput(device = device))
+ this.captureSession!.addInput(deviceInput);
+ this.videoDeviceInput = deviceInput;
+ this.captureSession!.commitConfiguration();
+ } catch (e) {
+ console.log(e)
+ }
+ }
+
+ takePhoto(option : TakePhotoOption) {
+ capturePhotoCaptureDelegate = new MyAVCapturePhotoCaptureDelegate(option);
+ let settings = new AVCapturePhotoSettings()
+ this.photoDeviceOutput?.capturePhoto(with = settings, delegate = capturePhotoCaptureDelegate!);
+ }
+
+
+ setZoom(option : CameraContextSetZoomOption) {
+ try {
+ let device = this.getCameraDevice();
+ // if (device.isRampingVideoZoom) {
+ UTSiOS.try(device.lockForConfiguration());
+ device.videoZoomFactor = option.zoom.toDouble();
+ device.unlockForConfiguration()
+
+ option.success?.({
+ zoom: option.zoom,
+ errMsg: 'ok'
+ } as SetZoomSuccessCallbackResult)
+ option.complete?.({
+ errMsg: 'ok'
+ } as GeneralCallbackResult)
+ // } else {
+ // const result : GeneralCallbackResult = {
+ // errMsg: '相机不支持缩放'
+ // }
+ // option.complete?.(result)
+ // option.fail?.(result)
+ // }
+ } catch (e) {
+ const result : GeneralCallbackResult = {
+ errMsg: JSON.stringify(e)
+ }
+ option.complete?.(result)
+ option.fail?.(result)
+ }
+ }
+
+ startRecord(option : CameraContextStartRecordOption) {
+ let isRecording = this.movieDeviceOutput?.isRecording ?? false;
+ if (isRecording) {
+ option.fail?.({
+ errMsg: '已经在录制或相机不存在'
+ } as GeneralCallbackResult)
+ option.complete?.({
+ errMsg: '已经在录制或相机不存在'
+ } as GeneralCallbackResult)
+ return
+ }
+
+ fileOutputRecordingDelegate = new MyAVCaptureFileOutputRecordingDelegate(option);
+ let path = UTSiOS.getDataPath() + (new Date().getTime()).toString() + ".mp4";
+ let url = new URL(fileURLWithPath = path);
+ this.movieDeviceOutput?.startRecording(to = url, recordingDelegate = fileOutputRecordingDelegate!);
+
+ if (option.timeout != null && option.timeout! > 0) {
+ setTimeout(() => {
+ this.movieDeviceOutput?.stopRecording();
+ }, (option.timeout!).toInt())
+ }
+ }
+
+
+ stopRecord(option : CameraContextStopRecordOption) {
+ let isRecording = this.movieDeviceOutput?.isRecording ?? false;
+ if (!isRecording) {
+ option.fail?.({
+ errMsg: '未开始录制'
+ } as GeneralCallbackResult)
+ option.complete?.({
+ errMsg: '未开始录制'
+ } as GeneralCallbackResult)
+ return
+ }
+ cameraContextStopRecordOption = option;
+ this.movieDeviceOutput?.stopRecording();
+ }
+
+
+ onCameraFrame(callback : OnCameraFrameCallback | null = null) {
+ this.onCameraFrameCallback = callback
+ }
+
+ onCameraFrameListener(option : OnCameraFrameListenerOption) {
+ onCameraFrameListenerOption = option
+ }
+
+ cameraFrameOnStart(option ?: CameraFrameListenerStartOption) {
+ this.isListening = true
+ option?.success?.({
+ errMsg: 'ok'
+ } as GeneralCallbackResult)
+ }
+
+ cameraFrameOnStop(option ?: StopOption) {
+ this.isListening = false
+ option?.success?.({
+ errMsg: 'ok'
+ } as GeneralCallbackResult)
+ }
+}
\ No newline at end of file
diff --git a/uni_modules/XF-cameraUTS/utssdk/app-ios/config.json b/uni_modules/XF-cameraUTS/utssdk/app-ios/config.json
new file mode 100644
index 0000000..dfb9a96
--- /dev/null
+++ b/uni_modules/XF-cameraUTS/utssdk/app-ios/config.json
@@ -0,0 +1,3 @@
+{
+ "deploymentTarget": "11"
+}
\ No newline at end of file
diff --git a/uni_modules/XF-cameraUTS/utssdk/app-ios/index.uts b/uni_modules/XF-cameraUTS/utssdk/app-ios/index.uts
new file mode 100644
index 0000000..16422bc
--- /dev/null
+++ b/uni_modules/XF-cameraUTS/utssdk/app-ios/index.uts
@@ -0,0 +1,207 @@
+import {
+ TakePhotoOption,
+ GeneralCallbackResult,
+ CameraContextSetZoomOption,
+ CameraContextStartRecordOption,
+ CameraContextStopRecordOption,
+ OnCameraFrameCallback,
+ CameraFrameListenerStartOption,
+ StopOption,
+ TakePhoto,
+ SetZoom,
+ StartRecord,
+ StopRecord,
+ StartCameraFrame,
+ StopCameraFrame,
+ OnCameraFrameListenerOption,
+ SwitchCamera,
+ SwitchOption
+} from '../interface'
+import { CameraManager } from './CameraManager';
+
+class CameraFrameListener {
+ context : CameraContext
+ constructor(context : CameraContext) {
+ this.context = context
+ }
+ start() : void
+ start(option ?: CameraFrameListenerStartOption) {
+ CameraManager.getInstance().getCameraView()?.cameraFrameOnStart(option)
+ }
+ stop() : void
+ stop(option ?: StopOption) {
+ CameraManager.getInstance().getCameraView()?.cameraFrameOnStop(option)
+ }
+}
+
+class CameraContext {
+ private mCameraFrameListener : CameraFrameListener | null = null
+
+ constructor() {
+
+ }
+
+ takePhoto(option : TakePhotoOption) {
+ let camera = CameraManager.getInstance().getCameraView()
+ if (camera == null) {
+ option.fail?.({
+ errMsg: '未找到相机'
+ } as GeneralCallbackResult)
+ option.complete?.({
+ errMsg: '未找到相机'
+ } as GeneralCallbackResult)
+ return;
+ }
+ camera?.takePhoto(option)
+ }
+
+ setZoom(option : CameraContextSetZoomOption) {
+ let camera = CameraManager.getInstance().getCameraView()
+ if (camera == null) {
+ option.fail?.({
+ errMsg: '未找到相机'
+ } as GeneralCallbackResult)
+ option.complete?.({
+ errMsg: '未找到相机'
+ } as GeneralCallbackResult)
+ return;
+ }
+
+ camera?.setZoom(option)
+ }
+
+ startRecord(option : CameraContextStartRecordOption) {
+ let camera = CameraManager.getInstance().getCameraView()
+ if (camera == null) {
+ option.fail?.({
+ errMsg: '未找到相机'
+ } as GeneralCallbackResult)
+ option.complete?.({
+ errMsg: '未找到相机'
+ } as GeneralCallbackResult)
+ return;
+ }
+ camera?.startRecord(option)
+ }
+
+ stopRecord(option : CameraContextStopRecordOption) {
+ let camera = CameraManager.getInstance().getCameraView()
+ if (camera == null) {
+ option.fail?.({
+ errMsg: '未找到相机'
+ } as GeneralCallbackResult)
+ option.complete?.({
+ errMsg: '未找到相机'
+ } as GeneralCallbackResult)
+ return;
+ }
+ camera?.stopRecord(option)
+ }
+
+ onCameraFrame() : CameraFrameListener
+ onCameraFrame(callback : OnCameraFrameCallback | null = null) : CameraFrameListener {
+ if (this.mCameraFrameListener == null) {
+ this.mCameraFrameListener = new CameraFrameListener(this)
+ }
+ CameraManager.getInstance().getCameraView()?.onCameraFrame(callback)
+ return this.mCameraFrameListener!
+ }
+ //////////////////////////////////////////////////////
+ onCameraFrameListener(option : OnCameraFrameListenerOption) {
+ let camera = CameraManager.getInstance().getCameraView()
+ if (camera == null) {
+ return;
+ }
+ camera?.onCameraFrameListener(option)
+ }
+
+ cameraFrameOnStart(option : CameraFrameListenerStartOption) {
+ let camera = CameraManager.getInstance().getCameraView()
+ if (camera == null) {
+ option.fail?.({
+ errMsg: '未找到相机'
+ } as GeneralCallbackResult)
+ option.complete?.({
+ errMsg: '未找到相机'
+ } as GeneralCallbackResult)
+ return;
+ }
+ camera?.cameraFrameOnStart(option)
+ }
+
+ cameraFrameOnStop(option : StopOption) {
+ let camera = CameraManager.getInstance().getCameraView()
+ if (camera == null) {
+ option.fail?.({
+ errMsg: '未找到相机'
+ } as GeneralCallbackResult)
+ option.complete?.({
+ errMsg: '未找到相机'
+ } as GeneralCallbackResult)
+ return;
+ }
+ camera?.cameraFrameOnStop(option)
+ }
+
+ switchCamera(option : SwitchOption) {
+ let camera = CameraManager.getInstance().getCameraView()
+ if (camera == null) {
+ option.fail?.({
+ errMsg: '未找到相机'
+ } as GeneralCallbackResult)
+ option.complete?.({
+ errMsg: '未找到相机'
+ } as GeneralCallbackResult)
+ return;
+ }
+ camera?.switchCamera(option.position)
+
+ option.success?.({
+ errMsg: 'ok'
+ } as GeneralCallbackResult)
+ option.complete?.({
+ errMsg: 'ok'
+ } as GeneralCallbackResult)
+ }
+}
+
+let context : CameraContext = new CameraContext();
+
+export function createCameraContext() : CameraContext {
+ return context;
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+export const takePhoto : TakePhoto = function (options : TakePhotoOption) {
+ context.takePhoto(options)
+}
+
+export const setZoom : SetZoom = function (options : CameraContextSetZoomOption) {
+ context.setZoom(options)
+}
+
+export const startRecord : StartRecord = function (options : CameraContextStartRecordOption) {
+ context.startRecord(options)
+}
+
+export const stopRecord : StopRecord = function (options : CameraContextStopRecordOption) {
+ context.stopRecord(options)
+}
+
+@UTSJS.keepAlive
+export function onCameraFrameListener(options : OnCameraFrameListenerOption) {
+ context.onCameraFrameListener(options)
+}
+
+export const startCameraFrame : StartCameraFrame = function (options : CameraFrameListenerStartOption) {
+ context.cameraFrameOnStart(options)
+}
+
+export const stopCameraFrame : StopCameraFrame = function (options : StopOption) {
+ context.cameraFrameOnStop(options)
+}
+
+export const switchCamera : SwitchCamera = function (options : SwitchOption) {
+ context.switchCamera(options)
+}
\ No newline at end of file
diff --git a/uni_modules/XF-cameraUTS/utssdk/app-ios/index.vue b/uni_modules/XF-cameraUTS/utssdk/app-ios/index.vue
new file mode 100644
index 0000000..fed3abc
--- /dev/null
+++ b/uni_modules/XF-cameraUTS/utssdk/app-ios/index.vue
@@ -0,0 +1,167 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/uni_modules/XF-cameraUTS/utssdk/interface.uts b/uni_modules/XF-cameraUTS/utssdk/interface.uts
new file mode 100644
index 0000000..ae3b66f
--- /dev/null
+++ b/uni_modules/XF-cameraUTS/utssdk/interface.uts
@@ -0,0 +1,214 @@
+export type GeneralCallbackResult = {
+ /** 错误信息 */
+ errMsg : string
+}
+export type TakePhotoSuccessCallbackResult = {
+ /** 照片文件的临时路径 (本地路径),安卓是jpg图片格式,ios是png */
+ tempImagePath : string
+ quality : string
+ errMsg : string
+}
+/** 接口调用失败的回调函数 */
+export type TakePhotoFailCallback = (res : GeneralCallbackResult) => void
+/** 接口调用成功的回调函数 */
+export type TakePhotoSuccessCallback = (
+ result : TakePhotoSuccessCallbackResult
+) => void
+
+/** 接口调用结束的回调函数(调用成功、失败都会执行) */
+export type TakePhotoCompleteCallback = (res : GeneralCallbackResult) => void
+
+export type TakePhotoOption = {
+ /** 接口调用结束的回调函数(调用成功、失败都会执行) */
+ complete ?: TakePhotoCompleteCallback
+ /** 接口调用失败的回调函数 */
+ fail ?: TakePhotoFailCallback
+ /** 成像质量
+ *
+ * 可选值:
+ * - 'high': 高质量;
+ * - 'normal': 普通质量;
+ * - 'low': 低质量;
+ * - 'original': 原图; */
+ quality ?: 'high' | 'normal' | 'low' | 'original'
+ /**
+ * 是否开启镜像 */
+ selfieMirror ?: boolean
+ /** 接口调用成功的回调函数 */
+ success ?: TakePhotoSuccessCallback
+}
+
+
+export type SetZoomSuccessCallbackResult = {
+ /** 实际设置的缩放级别。由于系统限制,某些机型可能无法设置成指定值,会改用最接近的可设值。 */
+ zoom : number
+ errMsg : string
+}
+/** 接口调用结束的回调函数(调用成功、失败都会执行) */
+export type SetZoomCompleteCallback = (res : GeneralCallbackResult) => void
+/** 接口调用失败的回调函数 */
+export type SetZoomFailCallback = (res : GeneralCallbackResult) => void
+/** 接口调用成功的回调函数 */
+export type CameraContextSetZoomSuccessCallback = (
+ result : SetZoomSuccessCallbackResult
+) => void
+
+export type CameraContextSetZoomOption = {
+ /** 缩放级别,范围[1, maxZoom]。zoom 可取小数,精确到小数后一位。maxZoom 可在 bindinitdone 返回值中获取。 */
+ zoom : number
+ /** 接口调用结束的回调函数(调用成功、失败都会执行) */
+ complete ?: SetZoomCompleteCallback
+ /** 接口调用失败的回调函数 */
+ fail ?: SetZoomFailCallback
+ /** 接口调用成功的回调函数 */
+ success ?: CameraContextSetZoomSuccessCallback
+}
+
+export type StartRecordTimeoutCallbackResult = {
+ /** 视频的文件的临时路径 (本地路径) */
+ tempVideoPath : string
+}
+
+/** 接口调用结束的回调函数(调用成功、失败都会执行) */
+export type StartRecordCompleteCallback = (res : GeneralCallbackResult) => void
+/** 接口调用失败的回调函数 */
+export type StartRecordFailCallback = (res : GeneralCallbackResult) => void
+/** 超过录制时长上限时会结束录像并触发此回调,录像异常退出时也会触发此回调 */
+export type StartRecordTimeoutCallback = (
+ result : StartRecordTimeoutCallbackResult
+) => void
+/** 接口调用成功的回调函数 */
+export type CameraContextStartRecordSuccessCallback = (
+ res : GeneralCallbackResult
+) => void
+export type CameraContextStartRecordOption = {
+ /** 接口调用结束的回调函数(调用成功、失败都会执行) */
+ complete ?: StartRecordCompleteCallback
+ /** 接口调用失败的回调函数 */
+ fail ?: StartRecordFailCallback
+ /**
+ * 是否开启镜像 */
+ selfieMirror ?: boolean
+ /** 接口调用成功的回调函数 */
+ success ?: CameraContextStartRecordSuccessCallback
+ /**
+ * 录制时长上限,单位为秒,最长不能超过 5 分钟 */
+ timeout ?: number
+ /** 超过录制时长上限时会结束录像并触发此回调,录像异常退出时也会触发此回调 */
+ timeoutCallback ?: StartRecordTimeoutCallback
+}
+
+export type StopRecordSuccessCallbackResult = {
+ /** 视频的文件的临时路径 (本地路径) */
+ tempVideoPath : string
+ errMsg : string
+}
+/** 接口调用结束的回调函数(调用成功、失败都会执行) */
+export type StopRecordCompleteCallback = (res : GeneralCallbackResult) => void
+/** 接口调用失败的回调函数 */
+export type StopRecordFailCallback = (res : GeneralCallbackResult) => void
+/** 接口调用成功的回调函数 */
+export type CameraContextStopRecordSuccessCallback = (
+ result : StopRecordSuccessCallbackResult
+) => void
+export type CameraContextStopRecordOption = {
+ /** 接口调用结束的回调函数(调用成功、失败都会执行) */
+ complete ?: StopRecordCompleteCallback
+ /** 启动视频压缩,压缩效果同`chooseVideo` */
+ compressed ?: boolean
+ /** 接口调用失败的回调函数 */
+ fail ?: StopRecordFailCallback
+ /** 接口调用成功的回调函数 */
+ success ?: CameraContextStopRecordSuccessCallback
+}
+
+/** 回调函数 */
+export type OnCameraFrameCallback = (result : OnCameraFrameCallbackResult) => void
+export type OnCameraFrameCallbackResult = {
+ /** 图像像素点数据,一维数组,每四项表示一个像素点的 rgba */
+ data : any
+ /** 图像数据矩形的高度 */
+ height : number
+ /** 图像数据矩形的宽度 */
+ width : number
+}
+/** 接口调用成功的回调函数 */
+export type StartSuccessCallback = (res : GeneralCallbackResult) => void
+/** 接口调用失败的回调函数 */
+type StartFailCallback = (res : GeneralCallbackResult) => void
+/** 接口调用结束的回调函数(调用成功、失败都会执行) */
+type StartCompleteCallback = (res : GeneralCallbackResult) => void
+export type CameraFrameListenerStartOption = {
+ /** 接口调用结束的回调函数(调用成功、失败都会执行) */
+ complete ?: StartCompleteCallback
+ /** 接口调用失败的回调函数 */
+ fail ?: StartFailCallback
+ /** 接口调用成功的回调函数 */
+ success ?: StartSuccessCallback
+ /** [Worker](https://developers.weixin.qq.com/miniprogram/dev/api/worker/Worker.html)
+ *
+ * 可选参数。如果需要在 iOS ExperimentalWorker 内监听摄像头帧数据,则需要传入对应 Worker 对象。详情 [Worker.getCameraFrameData](https://developers.weixin.qq.com/miniprogram/dev/api/worker/Worker.getCameraFrameData.html) */
+ // worker ?: Worker
+}
+
+
+/** 接口调用结束的回调函数(调用成功、失败都会执行) */
+export type StopCompleteCallback = (res : GeneralCallbackResult) => void
+/** 接口调用失败的回调函数 */
+export type StopFailCallback = (res : GeneralCallbackResult) => void
+/** 接口调用成功的回调函数 */
+export type StopSuccessCallback = (res : GeneralCallbackResult) => void
+export type StopOption = {
+ /** 接口调用结束的回调函数(调用成功、失败都会执行) */
+ complete ?: StopCompleteCallback
+ /** 接口调用失败的回调函数 */
+ fail ?: StopFailCallback
+ /** 接口调用成功的回调函数 */
+ success ?: StopSuccessCallback
+}
+
+export type FlashMode = 'auto' | 'on' | 'off' | 'torch'
+export type DevicePosition = 'back' | 'front'
+export type Resolution = 'low' | 'medium' | 'high'
+export type FrameSize = 'medium' | 'small' | 'large'
+export type QualityType = 'high' | 'normal' | 'low' | 'original'
+
+
+export type CameraConfig = {
+ flash ?: FlashMode
+ devicePosition ?: DevicePosition
+ resolution ?: Resolution
+ frameSize ?: FrameSize
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+
+/** 接口调用结束的回调函数(调用成功、失败都会执行) */
+export type SwitchCompleteCallback = (res : GeneralCallbackResult) => void
+/** 接口调用失败的回调函数 */
+export type SwitchFailCallback = (res : GeneralCallbackResult) => void
+/** 接口调用成功的回调函数 */
+export type SwitchSuccessCallback = (res : GeneralCallbackResult) => void
+export type SwitchOption = {
+ position : DevicePosition
+ /** 接口调用结束的回调函数(调用成功、失败都会执行) */
+ complete ?: SwitchCompleteCallback
+ /** 接口调用失败的回调函数 */
+ fail ?: SwitchFailCallback
+ /** 接口调用成功的回调函数 */
+ success ?: SwitchSuccessCallback
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+export type OnCameraFrameListenerOption = {
+ success ?: OnCameraFrameCallback
+}
+
+export type TakePhoto = (options : TakePhotoOption) => void
+export type SetZoom = (options : CameraContextSetZoomOption) => void
+export type StartRecord = (options : CameraContextStartRecordOption) => void
+export type StopRecord = (options : CameraContextStopRecordOption) => void
+export type OnCameraFrameListener = (options : OnCameraFrameListenerOption) => void
+export type StartCameraFrame = (options : CameraFrameListenerStartOption) => void
+export type StopCameraFrame = (options : StopOption) => void
+export type SwitchCamera = (options : SwitchOption) => void
\ No newline at end of file