chang to skin
This commit is contained in:
@@ -1,39 +0,0 @@
|
||||
|
||||
|
||||
let firstBackTime = 0
|
||||
|
||||
const __sfc__ = defineApp({
|
||||
onLaunch() {
|
||||
console.log('App Launch')
|
||||
},
|
||||
onShow() {
|
||||
console.log('App Show')
|
||||
},
|
||||
onHide() {
|
||||
console.log('App Hide')
|
||||
},
|
||||
|
||||
onLastPageBackPress() {
|
||||
console.log('App LastPageBackPress')
|
||||
if (firstBackTime == 0) {
|
||||
uni.showToast({
|
||||
title: '再按一次退出应用',
|
||||
position: 'bottom',
|
||||
})
|
||||
firstBackTime = Date.now()
|
||||
setTimeout(() => {
|
||||
firstBackTime = 0
|
||||
}, 2000)
|
||||
} else if (Date.now() - firstBackTime < 2000) {
|
||||
firstBackTime = Date.now()
|
||||
uni.exit()
|
||||
}
|
||||
},
|
||||
|
||||
onExit() {
|
||||
console.log('App Exit')
|
||||
},
|
||||
})
|
||||
|
||||
export default __sfc__
|
||||
const GenAppStyles = [_uM([["uni-row", _pS(_uM([["flexDirection", "row"]]))], ["uni-column", _pS(_uM([["flexDirection", "column"]]))]])]
|
||||
@@ -1,41 +0,0 @@
|
||||
import App from './App.uvue'
|
||||
|
||||
import { createSSRApp } from 'vue'
|
||||
export function createApp() {
|
||||
const app = createSSRApp(App)
|
||||
return {
|
||||
app
|
||||
}
|
||||
}
|
||||
export function main(app: IApp) {
|
||||
definePageRoutes();
|
||||
defineAppConfig();
|
||||
(createApp()['app'] as VueApp).mount(app, GenUniApp());
|
||||
}
|
||||
|
||||
export class UniAppConfig extends io.dcloud.uniapp.appframe.AppConfig {
|
||||
override name: string = "SkinCamera"
|
||||
override appid: string = "__UNI__644401D"
|
||||
override versionName: string = "1.0.0"
|
||||
override versionCode: string = "100"
|
||||
override uniCompilerVersion: string = "4.87"
|
||||
|
||||
constructor() { super() }
|
||||
}
|
||||
|
||||
import GenPagesIndexIndexClass from './pages/index/index.uvue'
|
||||
function definePageRoutes() {
|
||||
__uniRoutes.push({ path: "pages/index/index", component: GenPagesIndexIndexClass, meta: { isQuit: true } as UniPageMeta, style: _uM([["navigationBarTitleText","uni-app x"]]) } as UniPageRoute)
|
||||
}
|
||||
const __uniTabBar: Map<string, any | null> | null = null
|
||||
const __uniLaunchPage: Map<string, any | null> = _uM([["url","pages/index/index"],["style",_uM([["navigationBarTitleText","uni-app x"]])]])
|
||||
function defineAppConfig(){
|
||||
__uniConfig.entryPagePath = '/pages/index/index'
|
||||
__uniConfig.globalStyle = _uM([["navigationBarTextStyle","black"],["navigationBarTitleText","uni-app x"],["navigationBarBackgroundColor","#F8F8F8"],["backgroundColor","#F8F8F8"]])
|
||||
__uniConfig.getTabBarConfig = ():Map<string, any> | null => null
|
||||
__uniConfig.tabBar = __uniConfig.getTabBarConfig()
|
||||
__uniConfig.conditionUrl = ''
|
||||
__uniConfig.uniIdRouter = _uM()
|
||||
|
||||
__uniConfig.ready = true
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
require('vue');
|
||||
|
||||
uni;
|
||||
@@ -1,81 +0,0 @@
|
||||
|
||||
import {
|
||||
takePhoto,
|
||||
TakePhotoOption,
|
||||
TakePhotoSuccessCallbackResult,
|
||||
GeneralCallbackResult
|
||||
} from '@/uni_modules/Skin-cameraUTS'
|
||||
|
||||
|
||||
const __sfc__ = defineComponent({
|
||||
data() {
|
||||
return {
|
||||
yanse: "red",
|
||||
widthNum: 750,
|
||||
heightNum: 200,
|
||||
|
||||
flash: "on",
|
||||
device: "front",
|
||||
imagePath: "",
|
||||
videoPath: "https://qiniu-web-assets.dcloud.net.cn/unidoc/zh/2minute-demo.mp4"
|
||||
}
|
||||
},
|
||||
onLoad() {
|
||||
// let permissionNeed = ["android.permission.CAMERA", "android.permission.RECORD_AUDIO",
|
||||
// "android.permission.WRITE_EXTERNAL_STORAGE"]
|
||||
// // 请求权限
|
||||
// UTSAndroid.requestSystemPermission(UTSAndroid.getUniActivity()!, permissionNeed, function (allRight : boolean, _ : string[]) {
|
||||
// if (allRight) {
|
||||
// // 权限请求成功
|
||||
// } else {
|
||||
// //用户拒绝了部分权限
|
||||
// }
|
||||
// }, function (_ : boolean, _ : string[]) {
|
||||
// //用户拒绝了部分权限
|
||||
// })
|
||||
},
|
||||
methods: {
|
||||
onError(res : any) {
|
||||
console.log(res)
|
||||
},
|
||||
_takePhoto() {
|
||||
takePhoto({
|
||||
success: (res : TakePhotoSuccessCallbackResult) => {
|
||||
console.log('takePhoto', res)
|
||||
this.imagePath = res.tempImagePath;
|
||||
// _previewImage();
|
||||
},
|
||||
fail: (res : GeneralCallbackResult) => {
|
||||
console.log('takePhoto', res)
|
||||
}
|
||||
} as TakePhotoOption)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
export default __sfc__
|
||||
function GenPagesIndexIndexRender(this: InstanceType<typeof __sfc__>): any | null {
|
||||
const _ctx = this
|
||||
const _cache = this.$.renderCache
|
||||
return _cE("scroll-view", _uM({ class: "page-scroll-view" }), [
|
||||
_cE("view", null, [
|
||||
_cE("button", _uM({ onClick: _ctx._takePhoto }), "拍照", 8 /* PROPS */, ["onClick"]),
|
||||
_cE(XfCameraComponent.name, _uM({
|
||||
style: _nS(_uM({"height":"750rpx","background-color":"red"})),
|
||||
onError: _ctx.onError,
|
||||
flash: _ctx.flash,
|
||||
position: _ctx.device
|
||||
}), null, 44 /* STYLE, PROPS, NEED_HYDRATION */, ["onError", "flash", "position"]),
|
||||
isTrue(_ctx.imagePath)
|
||||
? _cE("image", _uM({
|
||||
key: 0,
|
||||
mode: "aspectFit",
|
||||
src: _ctx.imagePath
|
||||
}), null, 8 /* PROPS */, ["src"])
|
||||
: _cC("v-if", true)
|
||||
])
|
||||
])
|
||||
}
|
||||
const GenPagesIndexIndexStyles = [_uM([["video", _pS(_uM([["width", "100%"], ["height", 200]]))]])]
|
||||
|
||||
import { XfCameraComponent } from '@/uni_modules/Skin-cameraUTS?uts-proxy'
|
||||
-26
@@ -1,26 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
Vendored
-474
@@ -1,474 +0,0 @@
|
||||
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<string, Int>([
|
||||
['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<string, Int>([
|
||||
['back', CameraSelector.LENS_FACING_BACK],
|
||||
['front', CameraSelector.LENS_FACING_FRONT],
|
||||
])
|
||||
|
||||
|
||||
const RESOLUTION_MAP = new Map<string, Quality>([
|
||||
['low', Quality.LOWEST],
|
||||
['medium', Quality.HD],
|
||||
['high', Quality.HIGHEST],
|
||||
])
|
||||
|
||||
const QUALITY_MAP = new Map<string, Int>([
|
||||
["high", ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY],
|
||||
["low", ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY],
|
||||
])
|
||||
|
||||
const MIRROR_MODE = new Map<boolean, Int>([
|
||||
[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<Recorder> | null = null
|
||||
private recording : Recording | null = null
|
||||
private comp : UTSComponent<CameraView>;
|
||||
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<CameraView>, 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<string, any> = new Map();
|
||||
ret.set("errMsg", JSON.stringify(e))
|
||||
this.comp.$emit('error', ret)
|
||||
}
|
||||
}, ContextCompat.getMainExecutor(UTSAndroid.getUniActivity()!))
|
||||
// }).catch((err) => {
|
||||
// let ret : Map<string, any> = 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<string, any> = new Map();
|
||||
ret.set("maxZoom", maxZoom)
|
||||
this.comp.$emit('ready', ret)
|
||||
}
|
||||
|
||||
}
|
||||
unbindCamera() {
|
||||
this.cameraProvider?.unbindAll()
|
||||
this.cameraExecutor?.shutdown()
|
||||
}
|
||||
// checkSelfPermission() : Promise<boolean> {
|
||||
// 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
|
||||
// }
|
||||
Vendored
-252
@@ -1,252 +0,0 @@
|
||||
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<CameraView> {
|
||||
let cameraPreview = CameraManager.getInstance().getCameraView()
|
||||
if (cameraPreview != null) {
|
||||
return Promise.resolve<CameraView>(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<CameraView>((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)
|
||||
}
|
||||
Vendored
-151
@@ -1,151 +0,0 @@
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
import { CameraView } from './camera';
|
||||
import { CameraConfig } from '../interface'
|
||||
import { CameraManager } from './CameraManager';
|
||||
|
||||
//原生提供以下属性或方法的实现
|
||||
export default {
|
||||
/**
|
||||
* 组件名称,也就是开发者使用的标签
|
||||
*/
|
||||
name: "xf-camera",
|
||||
/**
|
||||
* 组件涉及的事件声明,只有声明过的事件,才能被正常发送
|
||||
*/
|
||||
emits: ['stop', 'error', 'ready'],
|
||||
/**
|
||||
* 属性声明,组件的使用者会传递这些属性值到组件
|
||||
*/
|
||||
props: {
|
||||
"mode": {
|
||||
type: String,
|
||||
default: "normal"
|
||||
},
|
||||
"resolution": {
|
||||
type: String,
|
||||
default: "medium" // low | high
|
||||
},
|
||||
"position": {
|
||||
type: String,
|
||||
default: "back" // front前置 |back 后置
|
||||
},
|
||||
"flash": {
|
||||
type: String,
|
||||
default: "auto" // auto, on, off, torch
|
||||
},
|
||||
"frameSize": {
|
||||
type: String,
|
||||
default: "medium" // small|large
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 组件内部变量声明
|
||||
*/
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
/**
|
||||
* 属性变化监听器实现
|
||||
*/
|
||||
watch: {
|
||||
"position": {
|
||||
/**
|
||||
* 这里监听属性变化,并进行组件内部更新
|
||||
*/
|
||||
handler(newValue : string, _ : string) {
|
||||
this.$el?.switchCamera(newValue)
|
||||
},
|
||||
immediate: false // 创建时是否通过此方法更新属性,默认值为false
|
||||
},
|
||||
"flash": {
|
||||
/**
|
||||
* 这里监听属性变化,并进行组件内部更新
|
||||
*/
|
||||
handler(newValue : string, _ : string) {
|
||||
this.$el?.setFlash(newValue)
|
||||
},
|
||||
immediate: false // 创建时是否通过此方法更新属性,默认值为false
|
||||
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 规则:如果没有配置expose,则methods中的方法均对外暴露,如果配置了expose,则以expose的配置为准向外暴露
|
||||
* ['publicMethod'] 含义为:只有 `publicMethod` 在实例上可用
|
||||
*/
|
||||
expose: [],
|
||||
methods: {
|
||||
|
||||
},
|
||||
/**
|
||||
* [可选实现] 组件被创建,组件第一个生命周期,
|
||||
* 在内存中被占用的时候被调用,开发者可以在这里执行一些需要提前执行的初始化逻辑
|
||||
*/
|
||||
created() {
|
||||
|
||||
},
|
||||
/**
|
||||
* [可选实现] 对应平台的view载体即将被创建,对应前端beforeMount
|
||||
*/
|
||||
NVBeforeLoad() {
|
||||
|
||||
},
|
||||
/**
|
||||
* [必须实现] 创建原生View,必须定义返回值类型
|
||||
* 开发者需要重点实现这个函数,声明原生组件被创建出来的过程,以及最终生成的原生组件类型
|
||||
* (Android需要明确知道View类型,需特殊校验)
|
||||
*/
|
||||
NVLoad() : CameraView {
|
||||
let previewView = new CameraView(this.$androidContext!, this)
|
||||
previewView.setTag("9527");
|
||||
CameraManager.getInstance().setCameraView(previewView);
|
||||
return previewView
|
||||
|
||||
},
|
||||
/**
|
||||
* [可选实现] 原生View已创建
|
||||
*/
|
||||
NVLoaded() {
|
||||
|
||||
},
|
||||
/**
|
||||
* [可选实现] 原生View布局完成
|
||||
*/
|
||||
NVLayouted() {
|
||||
this.$el?.setConfig({
|
||||
resolution: this.resolution,
|
||||
frameSize: this.frameSize,
|
||||
flash: this.flash
|
||||
} as CameraConfig)
|
||||
this.$el?.switchCamera(this.position)
|
||||
},
|
||||
/**
|
||||
* [可选实现] 原生View将释放
|
||||
*/
|
||||
NVBeforeUnload() {
|
||||
let ret : Map<string, any> = new Map();
|
||||
this.$emit('stop', ret)
|
||||
|
||||
this.$el?.unbindCamera()
|
||||
},
|
||||
/**
|
||||
* [可选实现] 原生View已释放,这里可以做释放View之后的操作
|
||||
*/
|
||||
NVUnloaded() {
|
||||
CameraManager.getInstance().dispose();
|
||||
},
|
||||
/**
|
||||
* [可选实现] 组件销毁
|
||||
*/
|
||||
unmounted() {
|
||||
|
||||
},
|
||||
}
|
||||
/*
|
||||
<view>
|
||||
</view>
|
||||
*/
|
||||
unpackage/dist/build/.tsc/app-android/uni_modules/Skin-cameraUTS/utssdk/app-ios/CameraManager.uts.ts
Vendored
-26
@@ -1,26 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
Vendored
-469
@@ -1,469 +0,0 @@
|
||||
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<string, AVCaptureSession.Preset>([
|
||||
['low', AVCaptureSession.Preset.low],
|
||||
['medium', AVCaptureSession.Preset.medium],
|
||||
['high', AVCaptureSession.Preset.high],
|
||||
])
|
||||
|
||||
const FLASH_MODE_MAP = new Map<string, AVCaptureDevice.TorchMode>([
|
||||
['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<UIView>;
|
||||
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<UIView>, 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<string, any> = new Map();
|
||||
ret.set("maxZoom", device.maxAvailableVideoZoomFactor)
|
||||
let detail : Map<string, any> = new Map();
|
||||
detail.set("detail", ret)
|
||||
this.comp?.$emit("ready", detail);
|
||||
} catch (e) {
|
||||
let ret : Map<string, any> = new Map();
|
||||
ret.set("errMsg", JSON.stringify(e))
|
||||
let detail : Map<string, any> = 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)
|
||||
}
|
||||
}
|
||||
Vendored
-207
@@ -1,207 +0,0 @@
|
||||
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)
|
||||
}
|
||||
Vendored
-166
@@ -1,166 +0,0 @@
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
import { UIView } from "UIKit"
|
||||
import { CameraView } from "./camera";
|
||||
import { CameraConfig } from '../interface'
|
||||
import { CameraManager } from "./CameraManager";
|
||||
|
||||
//原生提供以下属性或方法的实现
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
};
|
||||
},
|
||||
/**
|
||||
* 组件名称,也就是开发者使用的标签
|
||||
*/
|
||||
name: "xf-camera",
|
||||
/**
|
||||
* 组件涉及的事件声明,只有声明过的事件,才能被正常发送
|
||||
*/
|
||||
emits: ['stop', 'error', 'ready'],
|
||||
/**
|
||||
* 属性声明,组件的使用者会传递这些属性值到组件
|
||||
*/
|
||||
props: {
|
||||
"mode": {
|
||||
type: String,
|
||||
default: "normal"
|
||||
},
|
||||
"resolution": {
|
||||
type: String,
|
||||
default: "medium" // low | high
|
||||
},
|
||||
"position": {
|
||||
type: String,
|
||||
default: "back" // front前置 |back 后置
|
||||
},
|
||||
"flash": {
|
||||
type: String,
|
||||
default: "auto" // auto, on, off, torch
|
||||
},
|
||||
"frameSize": {
|
||||
type: String,
|
||||
default: "medium" // small|large
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 属性变化监听器实现
|
||||
*/
|
||||
watch: {
|
||||
"position": {
|
||||
/**
|
||||
* 这里监听属性变化,并进行组件内部更新
|
||||
*/
|
||||
handler(newValue : String, _oldValue : String) {
|
||||
CameraManager.getInstance().getCameraView()?.switchCamera(newValue)
|
||||
},
|
||||
/**
|
||||
* 创建时是否通过此方法更新属性,默认值为false
|
||||
*/
|
||||
immediate: false
|
||||
},
|
||||
"flash": {
|
||||
/**
|
||||
* 这里监听属性变化,并进行组件内部更新
|
||||
*/
|
||||
handler(newValue : String, _oldValue : String) {
|
||||
CameraManager.getInstance().getCameraView()?.setFlash(newValue)
|
||||
},
|
||||
/**
|
||||
* 创建时是否通过此方法更新属性,默认值为false
|
||||
*/
|
||||
immediate: false
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 规则:如果没有配置expose,则methods中的方法均对外暴露,如果配置了expose,则以expose的配置为准向外暴露
|
||||
* ['publicMethod'] 含义为:只有 `publicMethod` 在实例上可用
|
||||
*/
|
||||
expose: [],
|
||||
methods: {
|
||||
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* 组件被创建,组件第一个生命周期,
|
||||
* 在内存中被占用的时候被调用,开发者可以在这里执行一些需要提前执行的初始化逻辑
|
||||
* [可选实现]
|
||||
*/
|
||||
created() {
|
||||
|
||||
},
|
||||
/**
|
||||
* 对应平台的view载体即将被创建,对应前端beforeMount
|
||||
* [可选实现]
|
||||
*/
|
||||
NVBeforeLoad() {
|
||||
|
||||
},
|
||||
/**
|
||||
* 创建原生View,必须定义返回值类型
|
||||
* 开发者需要重点实现这个函数,声明原生组件被创建出来的过程,以及最终生成的原生组件类型
|
||||
* [必须实现]
|
||||
*/
|
||||
NVLoad() : UIView {
|
||||
let previewView = new UIView()
|
||||
previewView.tag = 9527
|
||||
return previewView
|
||||
},
|
||||
|
||||
/**
|
||||
* 原生View已创建
|
||||
* [可选实现]
|
||||
*/
|
||||
NVLoaded() {
|
||||
|
||||
},
|
||||
/**
|
||||
* 原生View布局完成
|
||||
* [可选实现]
|
||||
*/
|
||||
NVLayouted() {
|
||||
let previewView = this.$el as UIView;
|
||||
let cameraView = new CameraView(previewView, this, {
|
||||
resolution: this.resolution,
|
||||
frameSize: this.frameSize,
|
||||
devicePosition: this.position,
|
||||
flash: this.flash
|
||||
} as CameraConfig)
|
||||
CameraManager.getInstance().setCameraView(cameraView);
|
||||
},
|
||||
/**
|
||||
* 原生View将释放
|
||||
* [可选实现]
|
||||
*/
|
||||
NVBeforeUnload() {
|
||||
let ret : Map<string, any> = new Map();
|
||||
let detail : Map<string, any> = new Map();
|
||||
detail.set("detail", ret)
|
||||
this.$emit("stop", detail);
|
||||
|
||||
CameraManager.getInstance().getCameraView()?.unbindCamera()
|
||||
},
|
||||
/**
|
||||
* 原生View已释放,这里可以做释放View之后的操作
|
||||
* [可选实现]
|
||||
*/
|
||||
NVUnloaded() {
|
||||
CameraManager.getInstance().dispose()
|
||||
},
|
||||
/**
|
||||
* 组件销毁
|
||||
* [可选实现]
|
||||
*/
|
||||
unmounted() {
|
||||
|
||||
}
|
||||
}
|
||||
/*
|
||||
<view>
|
||||
</view>
|
||||
*/
|
||||
-214
@@ -1,214 +0,0 @@
|
||||
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
|
||||
unpackage/dist/build/.tsc/app-ios/uni_modules/Skin-cameraUTS/utssdk/app-android/CameraManager.uts.ts
Vendored
-26
@@ -1,26 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
Vendored
-474
@@ -1,474 +0,0 @@
|
||||
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<string, Int>([
|
||||
['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<string, Int>([
|
||||
['back', CameraSelector.LENS_FACING_BACK],
|
||||
['front', CameraSelector.LENS_FACING_FRONT],
|
||||
])
|
||||
|
||||
|
||||
const RESOLUTION_MAP = new Map<string, Quality>([
|
||||
['low', Quality.LOWEST],
|
||||
['medium', Quality.HD],
|
||||
['high', Quality.HIGHEST],
|
||||
])
|
||||
|
||||
const QUALITY_MAP = new Map<string, Int>([
|
||||
["high", ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY],
|
||||
["low", ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY],
|
||||
])
|
||||
|
||||
const MIRROR_MODE = new Map<boolean, Int>([
|
||||
[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<Recorder> | null = null
|
||||
private recording : Recording | null = null
|
||||
private comp : UTSComponent<CameraView>;
|
||||
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<CameraView>, 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<string, any> = new Map();
|
||||
ret.set("errMsg", JSON.stringify(e))
|
||||
this.comp.$emit('error', ret)
|
||||
}
|
||||
}, ContextCompat.getMainExecutor(UTSAndroid.getUniActivity()!))
|
||||
// }).catch((err) => {
|
||||
// let ret : Map<string, any> = 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<string, any> = new Map();
|
||||
ret.set("maxZoom", maxZoom)
|
||||
this.comp.$emit('ready', ret)
|
||||
}
|
||||
|
||||
}
|
||||
unbindCamera() {
|
||||
this.cameraProvider?.unbindAll()
|
||||
this.cameraExecutor?.shutdown()
|
||||
}
|
||||
// checkSelfPermission() : Promise<boolean> {
|
||||
// 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
|
||||
// }
|
||||
Vendored
-252
@@ -1,252 +0,0 @@
|
||||
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<CameraView> {
|
||||
let cameraPreview = CameraManager.getInstance().getCameraView()
|
||||
if (cameraPreview != null) {
|
||||
return Promise.resolve<CameraView>(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<CameraView>((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)
|
||||
}
|
||||
Vendored
-151
@@ -1,151 +0,0 @@
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
import { CameraView } from './camera';
|
||||
import { CameraConfig } from '../interface'
|
||||
import { CameraManager } from './CameraManager';
|
||||
|
||||
//原生提供以下属性或方法的实现
|
||||
export default {
|
||||
/**
|
||||
* 组件名称,也就是开发者使用的标签
|
||||
*/
|
||||
name: "xf-camera",
|
||||
/**
|
||||
* 组件涉及的事件声明,只有声明过的事件,才能被正常发送
|
||||
*/
|
||||
emits: ['stop', 'error', 'ready'],
|
||||
/**
|
||||
* 属性声明,组件的使用者会传递这些属性值到组件
|
||||
*/
|
||||
props: {
|
||||
"mode": {
|
||||
type: String,
|
||||
default: "normal"
|
||||
},
|
||||
"resolution": {
|
||||
type: String,
|
||||
default: "medium" // low | high
|
||||
},
|
||||
"position": {
|
||||
type: String,
|
||||
default: "back" // front前置 |back 后置
|
||||
},
|
||||
"flash": {
|
||||
type: String,
|
||||
default: "auto" // auto, on, off, torch
|
||||
},
|
||||
"frameSize": {
|
||||
type: String,
|
||||
default: "medium" // small|large
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 组件内部变量声明
|
||||
*/
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
/**
|
||||
* 属性变化监听器实现
|
||||
*/
|
||||
watch: {
|
||||
"position": {
|
||||
/**
|
||||
* 这里监听属性变化,并进行组件内部更新
|
||||
*/
|
||||
handler(newValue : string, _ : string) {
|
||||
this.$el?.switchCamera(newValue)
|
||||
},
|
||||
immediate: false // 创建时是否通过此方法更新属性,默认值为false
|
||||
},
|
||||
"flash": {
|
||||
/**
|
||||
* 这里监听属性变化,并进行组件内部更新
|
||||
*/
|
||||
handler(newValue : string, _ : string) {
|
||||
this.$el?.setFlash(newValue)
|
||||
},
|
||||
immediate: false // 创建时是否通过此方法更新属性,默认值为false
|
||||
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 规则:如果没有配置expose,则methods中的方法均对外暴露,如果配置了expose,则以expose的配置为准向外暴露
|
||||
* ['publicMethod'] 含义为:只有 `publicMethod` 在实例上可用
|
||||
*/
|
||||
expose: [],
|
||||
methods: {
|
||||
|
||||
},
|
||||
/**
|
||||
* [可选实现] 组件被创建,组件第一个生命周期,
|
||||
* 在内存中被占用的时候被调用,开发者可以在这里执行一些需要提前执行的初始化逻辑
|
||||
*/
|
||||
created() {
|
||||
|
||||
},
|
||||
/**
|
||||
* [可选实现] 对应平台的view载体即将被创建,对应前端beforeMount
|
||||
*/
|
||||
NVBeforeLoad() {
|
||||
|
||||
},
|
||||
/**
|
||||
* [必须实现] 创建原生View,必须定义返回值类型
|
||||
* 开发者需要重点实现这个函数,声明原生组件被创建出来的过程,以及最终生成的原生组件类型
|
||||
* (Android需要明确知道View类型,需特殊校验)
|
||||
*/
|
||||
NVLoad() : CameraView {
|
||||
let previewView = new CameraView(this.$androidContext!, this)
|
||||
previewView.setTag("9527");
|
||||
CameraManager.getInstance().setCameraView(previewView);
|
||||
return previewView
|
||||
|
||||
},
|
||||
/**
|
||||
* [可选实现] 原生View已创建
|
||||
*/
|
||||
NVLoaded() {
|
||||
|
||||
},
|
||||
/**
|
||||
* [可选实现] 原生View布局完成
|
||||
*/
|
||||
NVLayouted() {
|
||||
this.$el?.setConfig({
|
||||
resolution: this.resolution,
|
||||
frameSize: this.frameSize,
|
||||
flash: this.flash
|
||||
} as CameraConfig)
|
||||
this.$el?.switchCamera(this.position)
|
||||
},
|
||||
/**
|
||||
* [可选实现] 原生View将释放
|
||||
*/
|
||||
NVBeforeUnload() {
|
||||
let ret : Map<string, any> = new Map();
|
||||
this.$emit('stop', ret)
|
||||
|
||||
this.$el?.unbindCamera()
|
||||
},
|
||||
/**
|
||||
* [可选实现] 原生View已释放,这里可以做释放View之后的操作
|
||||
*/
|
||||
NVUnloaded() {
|
||||
CameraManager.getInstance().dispose();
|
||||
},
|
||||
/**
|
||||
* [可选实现] 组件销毁
|
||||
*/
|
||||
unmounted() {
|
||||
|
||||
},
|
||||
}
|
||||
/*
|
||||
<view>
|
||||
</view>
|
||||
*/
|
||||
Vendored
-26
@@ -1,26 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
-469
@@ -1,469 +0,0 @@
|
||||
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<string, AVCaptureSession.Preset>([
|
||||
['low', AVCaptureSession.Preset.low],
|
||||
['medium', AVCaptureSession.Preset.medium],
|
||||
['high', AVCaptureSession.Preset.high],
|
||||
])
|
||||
|
||||
const FLASH_MODE_MAP = new Map<string, AVCaptureDevice.TorchMode>([
|
||||
['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<UIView>;
|
||||
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<UIView>, 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<string, any> = new Map();
|
||||
ret.set("maxZoom", device.maxAvailableVideoZoomFactor)
|
||||
let detail : Map<string, any> = new Map();
|
||||
detail.set("detail", ret)
|
||||
this.comp?.$emit("ready", detail);
|
||||
} catch (e) {
|
||||
let ret : Map<string, any> = new Map();
|
||||
ret.set("errMsg", JSON.stringify(e))
|
||||
let detail : Map<string, any> = 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)
|
||||
}
|
||||
}
|
||||
-207
@@ -1,207 +0,0 @@
|
||||
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)
|
||||
}
|
||||
-166
@@ -1,166 +0,0 @@
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
import { UIView } from "UIKit"
|
||||
import { CameraView } from "./camera";
|
||||
import { CameraConfig } from '../interface'
|
||||
import { CameraManager } from "./CameraManager";
|
||||
|
||||
//原生提供以下属性或方法的实现
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
};
|
||||
},
|
||||
/**
|
||||
* 组件名称,也就是开发者使用的标签
|
||||
*/
|
||||
name: "xf-camera",
|
||||
/**
|
||||
* 组件涉及的事件声明,只有声明过的事件,才能被正常发送
|
||||
*/
|
||||
emits: ['stop', 'error', 'ready'],
|
||||
/**
|
||||
* 属性声明,组件的使用者会传递这些属性值到组件
|
||||
*/
|
||||
props: {
|
||||
"mode": {
|
||||
type: String,
|
||||
default: "normal"
|
||||
},
|
||||
"resolution": {
|
||||
type: String,
|
||||
default: "medium" // low | high
|
||||
},
|
||||
"position": {
|
||||
type: String,
|
||||
default: "back" // front前置 |back 后置
|
||||
},
|
||||
"flash": {
|
||||
type: String,
|
||||
default: "auto" // auto, on, off, torch
|
||||
},
|
||||
"frameSize": {
|
||||
type: String,
|
||||
default: "medium" // small|large
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 属性变化监听器实现
|
||||
*/
|
||||
watch: {
|
||||
"position": {
|
||||
/**
|
||||
* 这里监听属性变化,并进行组件内部更新
|
||||
*/
|
||||
handler(newValue : String, _oldValue : String) {
|
||||
CameraManager.getInstance().getCameraView()?.switchCamera(newValue)
|
||||
},
|
||||
/**
|
||||
* 创建时是否通过此方法更新属性,默认值为false
|
||||
*/
|
||||
immediate: false
|
||||
},
|
||||
"flash": {
|
||||
/**
|
||||
* 这里监听属性变化,并进行组件内部更新
|
||||
*/
|
||||
handler(newValue : String, _oldValue : String) {
|
||||
CameraManager.getInstance().getCameraView()?.setFlash(newValue)
|
||||
},
|
||||
/**
|
||||
* 创建时是否通过此方法更新属性,默认值为false
|
||||
*/
|
||||
immediate: false
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 规则:如果没有配置expose,则methods中的方法均对外暴露,如果配置了expose,则以expose的配置为准向外暴露
|
||||
* ['publicMethod'] 含义为:只有 `publicMethod` 在实例上可用
|
||||
*/
|
||||
expose: [],
|
||||
methods: {
|
||||
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* 组件被创建,组件第一个生命周期,
|
||||
* 在内存中被占用的时候被调用,开发者可以在这里执行一些需要提前执行的初始化逻辑
|
||||
* [可选实现]
|
||||
*/
|
||||
created() {
|
||||
|
||||
},
|
||||
/**
|
||||
* 对应平台的view载体即将被创建,对应前端beforeMount
|
||||
* [可选实现]
|
||||
*/
|
||||
NVBeforeLoad() {
|
||||
|
||||
},
|
||||
/**
|
||||
* 创建原生View,必须定义返回值类型
|
||||
* 开发者需要重点实现这个函数,声明原生组件被创建出来的过程,以及最终生成的原生组件类型
|
||||
* [必须实现]
|
||||
*/
|
||||
NVLoad() : UIView {
|
||||
let previewView = new UIView()
|
||||
previewView.tag = 9527
|
||||
return previewView
|
||||
},
|
||||
|
||||
/**
|
||||
* 原生View已创建
|
||||
* [可选实现]
|
||||
*/
|
||||
NVLoaded() {
|
||||
|
||||
},
|
||||
/**
|
||||
* 原生View布局完成
|
||||
* [可选实现]
|
||||
*/
|
||||
NVLayouted() {
|
||||
let previewView = this.$el as UIView;
|
||||
let cameraView = new CameraView(previewView, this, {
|
||||
resolution: this.resolution,
|
||||
frameSize: this.frameSize,
|
||||
devicePosition: this.position,
|
||||
flash: this.flash
|
||||
} as CameraConfig)
|
||||
CameraManager.getInstance().setCameraView(cameraView);
|
||||
},
|
||||
/**
|
||||
* 原生View将释放
|
||||
* [可选实现]
|
||||
*/
|
||||
NVBeforeUnload() {
|
||||
let ret : Map<string, any> = new Map();
|
||||
let detail : Map<string, any> = new Map();
|
||||
detail.set("detail", ret)
|
||||
this.$emit("stop", detail);
|
||||
|
||||
CameraManager.getInstance().getCameraView()?.unbindCamera()
|
||||
},
|
||||
/**
|
||||
* 原生View已释放,这里可以做释放View之后的操作
|
||||
* [可选实现]
|
||||
*/
|
||||
NVUnloaded() {
|
||||
CameraManager.getInstance().dispose()
|
||||
},
|
||||
/**
|
||||
* 组件销毁
|
||||
* [可选实现]
|
||||
*/
|
||||
unmounted() {
|
||||
|
||||
}
|
||||
}
|
||||
/*
|
||||
<view>
|
||||
</view>
|
||||
*/
|
||||
-214
@@ -1,214 +0,0 @@
|
||||
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
|
||||
-34
@@ -1,34 +0,0 @@
|
||||
let firstBackTime = 0;
|
||||
const __sfc__ = defineApp({
|
||||
onLaunch() {
|
||||
console.log('App Launch');
|
||||
},
|
||||
onShow() {
|
||||
console.log('App Show');
|
||||
},
|
||||
onHide() {
|
||||
console.log('App Hide');
|
||||
},
|
||||
onLastPageBackPress() {
|
||||
console.log('App LastPageBackPress');
|
||||
if (firstBackTime == 0) {
|
||||
uni.showToast({
|
||||
title: '再按一次退出应用',
|
||||
position: 'bottom',
|
||||
});
|
||||
firstBackTime = Date.now();
|
||||
setTimeout(() => {
|
||||
firstBackTime = 0;
|
||||
}, 2000);
|
||||
}
|
||||
else if (Date.now() - firstBackTime < 2000) {
|
||||
firstBackTime = Date.now();
|
||||
uni.exit();
|
||||
}
|
||||
},
|
||||
onExit() {
|
||||
console.log('App Exit');
|
||||
},
|
||||
});
|
||||
export default __sfc__;
|
||||
const GenAppStyles = [_uM([["uni-row", _pS(_uM([["flexDirection", "row"]]))], ["uni-column", _pS(_uM([["flexDirection", "column"]]))]])];
|
||||
-36
@@ -1,36 +0,0 @@
|
||||
import App from './App.uvue';
|
||||
import { createSSRApp } from 'vue';
|
||||
export function createApp(): UTSJSONObject {
|
||||
const app = createSSRApp(App);
|
||||
return {
|
||||
app
|
||||
};
|
||||
}
|
||||
export function main(app: IApp) {
|
||||
definePageRoutes();
|
||||
defineAppConfig();
|
||||
(createApp()['app'] as VueApp).mount(app, GenUniApp());
|
||||
}
|
||||
export class UniAppConfig extends io.dcloud.uniapp.appframe.AppConfig {
|
||||
override name: string = "SkinCamera";
|
||||
override appid: string = "__UNI__644401D";
|
||||
override versionName: string = "1.0.0";
|
||||
override versionCode: string = "100";
|
||||
override uniCompilerVersion: string = "4.87";
|
||||
constructor() { super(); }
|
||||
}
|
||||
import GenPagesIndexIndexClass from './pages/index/index.uvue';
|
||||
function definePageRoutes() {
|
||||
__uniRoutes.push({ path: "pages/index/index", component: GenPagesIndexIndexClass, meta: { isQuit: true } as UniPageMeta, style: _uM([["navigationBarTitleText", "uni-app x"]]) } as UniPageRoute);
|
||||
}
|
||||
const __uniTabBar: Map<string, any | null> | null = null;
|
||||
const __uniLaunchPage: Map<string, any | null> = _uM([["url", "pages/index/index"], ["style", _uM([["navigationBarTitleText", "uni-app x"]])]]);
|
||||
function defineAppConfig() {
|
||||
__uniConfig.entryPagePath = '/pages/index/index';
|
||||
__uniConfig.globalStyle = _uM([["navigationBarTextStyle", "black"], ["navigationBarTitleText", "uni-app x"], ["navigationBarBackgroundColor", "#F8F8F8"], ["backgroundColor", "#F8F8F8"]]);
|
||||
__uniConfig.getTabBarConfig = (): Map<string, any> | null => null;
|
||||
__uniConfig.tabBar = __uniConfig.getTabBarConfig();
|
||||
__uniConfig.conditionUrl = '';
|
||||
__uniConfig.uniIdRouter = _uM();
|
||||
__uniConfig.ready = true;
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
import { takePhoto, TakePhotoOption, TakePhotoSuccessCallbackResult, GeneralCallbackResult } from '@/uni_modules/Skin-cameraUTS';
|
||||
const __sfc__ = defineComponent({
|
||||
data() {
|
||||
return {
|
||||
yanse: "red",
|
||||
widthNum: 750,
|
||||
heightNum: 200,
|
||||
flash: "on",
|
||||
device: "front",
|
||||
imagePath: "",
|
||||
videoPath: "https://qiniu-web-assets.dcloud.net.cn/unidoc/zh/2minute-demo.mp4"
|
||||
};
|
||||
},
|
||||
onLoad() {
|
||||
// let permissionNeed = ["android.permission.CAMERA", "android.permission.RECORD_AUDIO",
|
||||
// "android.permission.WRITE_EXTERNAL_STORAGE"]
|
||||
// // 请求权限
|
||||
// UTSAndroid.requestSystemPermission(UTSAndroid.getUniActivity()!, permissionNeed, function (allRight : boolean, _ : string[]) {
|
||||
// if (allRight) {
|
||||
// // 权限请求成功
|
||||
// } else {
|
||||
// //用户拒绝了部分权限
|
||||
// }
|
||||
// }, function (_ : boolean, _ : string[]) {
|
||||
// //用户拒绝了部分权限
|
||||
// })
|
||||
},
|
||||
methods: {
|
||||
onError(res: any) {
|
||||
console.log(res);
|
||||
},
|
||||
_takePhoto() {
|
||||
takePhoto({
|
||||
success: (res: TakePhotoSuccessCallbackResult) => {
|
||||
console.log('takePhoto', res);
|
||||
this.imagePath = res.tempImagePath;
|
||||
// _previewImage();
|
||||
},
|
||||
fail: (res: GeneralCallbackResult) => {
|
||||
console.log('takePhoto', res);
|
||||
}
|
||||
} as TakePhotoOption);
|
||||
}
|
||||
}
|
||||
});
|
||||
export default __sfc__;
|
||||
function GenPagesIndexIndexRender(this: InstanceType<typeof __sfc__>): any | null {
|
||||
const _ctx = this;
|
||||
const _cache = this.$.renderCache;
|
||||
return _cE("scroll-view", _uM({ class: "page-scroll-view" }), [
|
||||
_cE("view", null, [
|
||||
_cE("button", _uM({ onClick: _ctx._takePhoto }), "拍照", 8 /* PROPS */, ["onClick"]),
|
||||
_cE(XfCameraComponent.name, _uM({
|
||||
style: _nS(_uM({ "height": "750rpx", "background-color": "red" })),
|
||||
onError: _ctx.onError,
|
||||
flash: _ctx.flash,
|
||||
position: _ctx.device
|
||||
}), null, 44 /* STYLE, PROPS, NEED_HYDRATION */, ["onError", "flash", "position"]),
|
||||
isTrue(_ctx.imagePath)
|
||||
? _cE("image", _uM({
|
||||
key: 0,
|
||||
mode: "aspectFit",
|
||||
src: _ctx.imagePath
|
||||
}), null, 8 /* PROPS */, ["src"])
|
||||
: _cC("v-if", true)
|
||||
])
|
||||
]);
|
||||
}
|
||||
const GenPagesIndexIndexStyles = [_uM([["video", _pS(_uM([["width", "100%"], ["height", 200]]))]])];
|
||||
import { XfCameraComponent } from '@/uni_modules/Skin-cameraUTS?uts-proxy';
|
||||
-20
@@ -1,20 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
Vendored
-414
@@ -1,414 +0,0 @@
|
||||
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<string, Int>([
|
||||
['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<string, Int>([
|
||||
['back', CameraSelector.LENS_FACING_BACK],
|
||||
['front', CameraSelector.LENS_FACING_FRONT],
|
||||
]);
|
||||
const RESOLUTION_MAP = new Map<string, Quality>([
|
||||
['low', Quality.LOWEST],
|
||||
['medium', Quality.HD],
|
||||
['high', Quality.HIGHEST],
|
||||
]);
|
||||
const QUALITY_MAP = new Map<string, Int>([
|
||||
["high", ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY],
|
||||
["low", ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY],
|
||||
]);
|
||||
const MIRROR_MODE = new Map<boolean, Int>([
|
||||
[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<Recorder> | null = null;
|
||||
private recording: Recording | null = null;
|
||||
private comp: UTSComponent<CameraView>;
|
||||
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<CameraView>, 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: any) {
|
||||
let ret: Map<string, any> = new Map();
|
||||
ret.set("errMsg", JSON.stringify(e));
|
||||
this.comp.$emit('error', ret);
|
||||
}
|
||||
}, ContextCompat.getMainExecutor(UTSAndroid.getUniActivity()!));
|
||||
// }).catch((err) => {
|
||||
// let ret : Map<string, any> = 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<string, any> = new Map();
|
||||
ret.set("maxZoom", maxZoom);
|
||||
this.comp.$emit('ready', ret);
|
||||
}
|
||||
}
|
||||
unbindCamera() {
|
||||
this.cameraProvider?.unbindAll();
|
||||
this.cameraExecutor?.shutdown();
|
||||
}
|
||||
// checkSelfPermission() : Promise<boolean> {
|
||||
// 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: any) {
|
||||
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
|
||||
// }
|
||||
Vendored
-214
@@ -1,214 +0,0 @@
|
||||
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<CameraView> {
|
||||
let cameraPreview = CameraManager.getInstance().getCameraView();
|
||||
if (cameraPreview != null) {
|
||||
return Promise.resolve<CameraView>(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<CameraView>((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);
|
||||
};
|
||||
Vendored
-140
@@ -1,140 +0,0 @@
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
import { CameraView } from './camera';
|
||||
import { CameraConfig } from '../interface';
|
||||
import { CameraManager } from './CameraManager';
|
||||
//原生提供以下属性或方法的实现
|
||||
export default {
|
||||
/**
|
||||
* 组件名称,也就是开发者使用的标签
|
||||
*/
|
||||
name: "xf-camera",
|
||||
/**
|
||||
* 组件涉及的事件声明,只有声明过的事件,才能被正常发送
|
||||
*/
|
||||
emits: ['stop', 'error', 'ready'],
|
||||
/**
|
||||
* 属性声明,组件的使用者会传递这些属性值到组件
|
||||
*/
|
||||
props: {
|
||||
"mode": {
|
||||
type: String,
|
||||
default: "normal"
|
||||
},
|
||||
"resolution": {
|
||||
type: String,
|
||||
default: "medium" // low | high
|
||||
},
|
||||
"position": {
|
||||
type: String,
|
||||
default: "back" // front前置 |back 后置
|
||||
},
|
||||
"flash": {
|
||||
type: String,
|
||||
default: "auto" // auto, on, off, torch
|
||||
},
|
||||
"frameSize": {
|
||||
type: String,
|
||||
default: "medium" // small|large
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 组件内部变量声明
|
||||
*/
|
||||
data(): UTSJSONObject {
|
||||
return {};
|
||||
},
|
||||
/**
|
||||
* 属性变化监听器实现
|
||||
*/
|
||||
watch: {
|
||||
"position": {
|
||||
/**
|
||||
* 这里监听属性变化,并进行组件内部更新
|
||||
*/
|
||||
handler(newValue: string, _: string) {
|
||||
this.$el?.switchCamera(newValue);
|
||||
},
|
||||
immediate: false // 创建时是否通过此方法更新属性,默认值为false
|
||||
},
|
||||
"flash": {
|
||||
/**
|
||||
* 这里监听属性变化,并进行组件内部更新
|
||||
*/
|
||||
handler(newValue: string, _: string) {
|
||||
this.$el?.setFlash(newValue);
|
||||
},
|
||||
immediate: false // 创建时是否通过此方法更新属性,默认值为false
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 规则:如果没有配置expose,则methods中的方法均对外暴露,如果配置了expose,则以expose的配置为准向外暴露
|
||||
* ['publicMethod'] 含义为:只有 `publicMethod` 在实例上可用
|
||||
*/
|
||||
expose: [],
|
||||
methods: {},
|
||||
/**
|
||||
* [可选实现] 组件被创建,组件第一个生命周期,
|
||||
* 在内存中被占用的时候被调用,开发者可以在这里执行一些需要提前执行的初始化逻辑
|
||||
*/
|
||||
created() {
|
||||
},
|
||||
/**
|
||||
* [可选实现] 对应平台的view载体即将被创建,对应前端beforeMount
|
||||
*/
|
||||
NVBeforeLoad() {
|
||||
},
|
||||
/**
|
||||
* [必须实现] 创建原生View,必须定义返回值类型
|
||||
* 开发者需要重点实现这个函数,声明原生组件被创建出来的过程,以及最终生成的原生组件类型
|
||||
* (Android需要明确知道View类型,需特殊校验)
|
||||
*/
|
||||
NVLoad(): CameraView {
|
||||
let previewView = new CameraView(this.$androidContext!, this);
|
||||
previewView.setTag("9527");
|
||||
CameraManager.getInstance().setCameraView(previewView);
|
||||
return previewView;
|
||||
},
|
||||
/**
|
||||
* [可选实现] 原生View已创建
|
||||
*/
|
||||
NVLoaded() {
|
||||
},
|
||||
/**
|
||||
* [可选实现] 原生View布局完成
|
||||
*/
|
||||
NVLayouted() {
|
||||
this.$el?.setConfig({
|
||||
resolution: this.resolution,
|
||||
frameSize: this.frameSize,
|
||||
flash: this.flash
|
||||
} as CameraConfig);
|
||||
this.$el?.switchCamera(this.position);
|
||||
},
|
||||
/**
|
||||
* [可选实现] 原生View将释放
|
||||
*/
|
||||
NVBeforeUnload() {
|
||||
let ret: Map<string, any> = new Map();
|
||||
this.$emit('stop', ret);
|
||||
this.$el?.unbindCamera();
|
||||
},
|
||||
/**
|
||||
* [可选实现] 原生View已释放,这里可以做释放View之后的操作
|
||||
*/
|
||||
NVUnloaded() {
|
||||
CameraManager.getInstance().dispose();
|
||||
},
|
||||
/**
|
||||
* [可选实现] 组件销毁
|
||||
*/
|
||||
unmounted() {
|
||||
},
|
||||
};
|
||||
/*
|
||||
<view>
|
||||
</view>
|
||||
*/
|
||||
-182
@@ -1,182 +0,0 @@
|
||||
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;
|
||||
};
|
||||
/** 接口调用结束的回调函数(调用成功、失败都会执行) */
|
||||
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;
|
||||
Vendored
-20
@@ -1,20 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
-421
@@ -1,421 +0,0 @@
|
||||
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<string, AVCaptureSession.Preset>([
|
||||
['low', AVCaptureSession.Preset.low],
|
||||
['medium', AVCaptureSession.Preset.medium],
|
||||
['high', AVCaptureSession.Preset.high],
|
||||
]);
|
||||
const FLASH_MODE_MAP = new Map<string, AVCaptureDevice.TorchMode>([
|
||||
['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<UIView>;
|
||||
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<UIView>, 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<string, any> = new Map();
|
||||
ret.set("maxZoom", device.maxAvailableVideoZoomFactor);
|
||||
let detail: Map<string, any> = new Map();
|
||||
detail.set("detail", ret);
|
||||
this.comp?.$emit("ready", detail);
|
||||
}
|
||||
catch (e) {
|
||||
let ret: Map<string, any> = new Map();
|
||||
ret.set("errMsg", JSON.stringify(e));
|
||||
let detail: Map<string, any> = 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);
|
||||
}
|
||||
}
|
||||
-164
@@ -1,164 +0,0 @@
|
||||
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);
|
||||
};
|
||||
-153
@@ -1,153 +0,0 @@
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
import { UIView } from "UIKit";
|
||||
import { CameraView } from "./camera";
|
||||
import { CameraConfig } from '../interface';
|
||||
import { CameraManager } from "./CameraManager";
|
||||
//原生提供以下属性或方法的实现
|
||||
export default {
|
||||
data(): UTSJSONObject {
|
||||
return {};
|
||||
},
|
||||
/**
|
||||
* 组件名称,也就是开发者使用的标签
|
||||
*/
|
||||
name: "xf-camera",
|
||||
/**
|
||||
* 组件涉及的事件声明,只有声明过的事件,才能被正常发送
|
||||
*/
|
||||
emits: ['stop', 'error', 'ready'],
|
||||
/**
|
||||
* 属性声明,组件的使用者会传递这些属性值到组件
|
||||
*/
|
||||
props: {
|
||||
"mode": {
|
||||
type: String,
|
||||
default: "normal"
|
||||
},
|
||||
"resolution": {
|
||||
type: String,
|
||||
default: "medium" // low | high
|
||||
},
|
||||
"position": {
|
||||
type: String,
|
||||
default: "back" // front前置 |back 后置
|
||||
},
|
||||
"flash": {
|
||||
type: String,
|
||||
default: "auto" // auto, on, off, torch
|
||||
},
|
||||
"frameSize": {
|
||||
type: String,
|
||||
default: "medium" // small|large
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 属性变化监听器实现
|
||||
*/
|
||||
watch: {
|
||||
"position": {
|
||||
/**
|
||||
* 这里监听属性变化,并进行组件内部更新
|
||||
*/
|
||||
handler(newValue: String, _oldValue: String) {
|
||||
CameraManager.getInstance().getCameraView()?.switchCamera(newValue);
|
||||
},
|
||||
/**
|
||||
* 创建时是否通过此方法更新属性,默认值为false
|
||||
*/
|
||||
immediate: false
|
||||
},
|
||||
"flash": {
|
||||
/**
|
||||
* 这里监听属性变化,并进行组件内部更新
|
||||
*/
|
||||
handler(newValue: String, _oldValue: String) {
|
||||
CameraManager.getInstance().getCameraView()?.setFlash(newValue);
|
||||
},
|
||||
/**
|
||||
* 创建时是否通过此方法更新属性,默认值为false
|
||||
*/
|
||||
immediate: false
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 规则:如果没有配置expose,则methods中的方法均对外暴露,如果配置了expose,则以expose的配置为准向外暴露
|
||||
* ['publicMethod'] 含义为:只有 `publicMethod` 在实例上可用
|
||||
*/
|
||||
expose: [],
|
||||
methods: {},
|
||||
/**
|
||||
* 组件被创建,组件第一个生命周期,
|
||||
* 在内存中被占用的时候被调用,开发者可以在这里执行一些需要提前执行的初始化逻辑
|
||||
* [可选实现]
|
||||
*/
|
||||
created() {
|
||||
},
|
||||
/**
|
||||
* 对应平台的view载体即将被创建,对应前端beforeMount
|
||||
* [可选实现]
|
||||
*/
|
||||
NVBeforeLoad() {
|
||||
},
|
||||
/**
|
||||
* 创建原生View,必须定义返回值类型
|
||||
* 开发者需要重点实现这个函数,声明原生组件被创建出来的过程,以及最终生成的原生组件类型
|
||||
* [必须实现]
|
||||
*/
|
||||
NVLoad(): UIView {
|
||||
let previewView = new UIView();
|
||||
previewView.tag = 9527;
|
||||
return previewView;
|
||||
},
|
||||
/**
|
||||
* 原生View已创建
|
||||
* [可选实现]
|
||||
*/
|
||||
NVLoaded() {
|
||||
},
|
||||
/**
|
||||
* 原生View布局完成
|
||||
* [可选实现]
|
||||
*/
|
||||
NVLayouted() {
|
||||
let previewView = this.$el as UIView;
|
||||
let cameraView = new CameraView(previewView, this, {
|
||||
resolution: this.resolution,
|
||||
frameSize: this.frameSize,
|
||||
devicePosition: this.position,
|
||||
flash: this.flash
|
||||
} as CameraConfig);
|
||||
CameraManager.getInstance().setCameraView(cameraView);
|
||||
},
|
||||
/**
|
||||
* 原生View将释放
|
||||
* [可选实现]
|
||||
*/
|
||||
NVBeforeUnload() {
|
||||
let ret: Map<string, any> = new Map();
|
||||
let detail: Map<string, any> = new Map();
|
||||
detail.set("detail", ret);
|
||||
this.$emit("stop", detail);
|
||||
CameraManager.getInstance().getCameraView()?.unbindCamera();
|
||||
},
|
||||
/**
|
||||
* 原生View已释放,这里可以做释放View之后的操作
|
||||
* [可选实现]
|
||||
*/
|
||||
NVUnloaded() {
|
||||
CameraManager.getInstance().dispose();
|
||||
},
|
||||
/**
|
||||
* 组件销毁
|
||||
* [可选实现]
|
||||
*/
|
||||
unmounted() {
|
||||
}
|
||||
};
|
||||
/*
|
||||
<view>
|
||||
</view>
|
||||
*/
|
||||
-182
@@ -1,182 +0,0 @@
|
||||
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;
|
||||
};
|
||||
/** 接口调用结束的回调函数(调用成功、失败都会执行) */
|
||||
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;
|
||||
@@ -1,125 +0,0 @@
|
||||
@file:Suppress("UNCHECKED_CAST", "USELESS_CAST", "INAPPLICABLE_JVM_NAME", "UNUSED_ANONYMOUS_PARAMETER", "NAME_SHADOWING", "UNNECESSARY_NOT_NULL_ASSERTION")
|
||||
package uni.UNI644401D
|
||||
import io.dcloud.uniapp.*
|
||||
import io.dcloud.uniapp.extapi.*
|
||||
import io.dcloud.uniapp.framework.*
|
||||
import io.dcloud.uniapp.runtime.*
|
||||
import io.dcloud.uniapp.vue.*
|
||||
import io.dcloud.uniapp.vue.shared.*
|
||||
import io.dcloud.uts.*
|
||||
import io.dcloud.uts.Map
|
||||
import io.dcloud.uts.Set
|
||||
import io.dcloud.uts.UTSAndroid
|
||||
import kotlin.properties.Delegates
|
||||
import io.dcloud.uniapp.extapi.exit as uni_exit
|
||||
import io.dcloud.uniapp.extapi.showToast as uni_showToast
|
||||
val runBlock1 = run {
|
||||
__uniConfig.getAppStyles = fun(): Map<String, Map<String, Map<String, Any>>> {
|
||||
return GenApp.styles
|
||||
}
|
||||
}
|
||||
var firstBackTime: Number = 0
|
||||
open class GenApp : BaseApp {
|
||||
constructor(__ins: ComponentInternalInstance) : super(__ins) {
|
||||
onLaunch(fun(_: OnLaunchOptions) {
|
||||
console.log("App Launch")
|
||||
}
|
||||
, __ins)
|
||||
onAppShow(fun(_: OnShowOptions) {
|
||||
console.log("App Show")
|
||||
}
|
||||
, __ins)
|
||||
onAppHide(fun() {
|
||||
console.log("App Hide")
|
||||
}
|
||||
, __ins)
|
||||
onLastPageBackPress(fun() {
|
||||
console.log("App LastPageBackPress")
|
||||
if (firstBackTime == 0) {
|
||||
uni_showToast(ShowToastOptions(title = "再按一次退出应用", position = "bottom"))
|
||||
firstBackTime = Date.now()
|
||||
setTimeout(fun(){
|
||||
firstBackTime = 0
|
||||
}, 2000)
|
||||
} else if (Date.now() - firstBackTime < 2000) {
|
||||
firstBackTime = Date.now()
|
||||
uni_exit(null)
|
||||
}
|
||||
}
|
||||
, __ins)
|
||||
onExit(fun() {
|
||||
console.log("App Exit")
|
||||
}
|
||||
, __ins)
|
||||
}
|
||||
companion object {
|
||||
val styles: Map<String, Map<String, Map<String, Any>>> by lazy {
|
||||
_nCS(_uA(
|
||||
styles0
|
||||
))
|
||||
}
|
||||
val styles0: Map<String, Map<String, Map<String, Any>>>
|
||||
get() {
|
||||
return _uM("uni-row" to _pS(_uM("flexDirection" to "row")), "uni-column" to _pS(_uM("flexDirection" to "column")))
|
||||
}
|
||||
}
|
||||
}
|
||||
val GenAppClass = CreateVueAppComponent(GenApp::class.java, fun(): VueComponentOptions {
|
||||
return VueComponentOptions(type = "app", name = "", inheritAttrs = true, inject = Map(), props = Map(), propsNeedCastKeys = _uA(), emits = Map(), components = Map(), styles = GenApp.styles)
|
||||
}
|
||||
, fun(instance): GenApp {
|
||||
return GenApp(instance)
|
||||
}
|
||||
)
|
||||
val GenPagesIndexIndexClass = CreateVueComponent(GenPagesIndexIndex::class.java, fun(): VueComponentOptions {
|
||||
return VueComponentOptions(type = "page", name = "", inheritAttrs = GenPagesIndexIndex.inheritAttrs, inject = GenPagesIndexIndex.inject, props = GenPagesIndexIndex.props, propsNeedCastKeys = GenPagesIndexIndex.propsNeedCastKeys, emits = GenPagesIndexIndex.emits, components = GenPagesIndexIndex.components, styles = GenPagesIndexIndex.styles)
|
||||
}
|
||||
, fun(instance, renderer): GenPagesIndexIndex {
|
||||
return GenPagesIndexIndex(instance, renderer)
|
||||
}
|
||||
)
|
||||
fun createApp(): UTSJSONObject {
|
||||
val app = createSSRApp(GenAppClass)
|
||||
return _uO("app" to app)
|
||||
}
|
||||
fun main(app: IApp) {
|
||||
definePageRoutes()
|
||||
defineAppConfig()
|
||||
(createApp()["app"] as VueApp).mount(app, GenUniApp())
|
||||
}
|
||||
open class UniAppConfig : io.dcloud.uniapp.appframe.AppConfig {
|
||||
override var name: String = "SkinCamera"
|
||||
override var appid: String = "__UNI__644401D"
|
||||
override var versionName: String = "1.0.0"
|
||||
override var versionCode: String = "100"
|
||||
override var uniCompilerVersion: String = "4.87"
|
||||
constructor() : super() {}
|
||||
}
|
||||
fun definePageRoutes() {
|
||||
__uniRoutes.push(UniPageRoute(path = "pages/index/index", component = GenPagesIndexIndexClass, meta = UniPageMeta(isQuit = true), style = _uM("navigationBarTitleText" to "uni-app x")))
|
||||
}
|
||||
val __uniLaunchPage: Map<String, Any?> = _uM("url" to "pages/index/index", "style" to _uM("navigationBarTitleText" to "uni-app x"))
|
||||
fun defineAppConfig() {
|
||||
__uniConfig.entryPagePath = "/pages/index/index"
|
||||
__uniConfig.globalStyle = _uM("navigationBarTextStyle" to "black", "navigationBarTitleText" to "uni-app x", "navigationBarBackgroundColor" to "#F8F8F8", "backgroundColor" to "#F8F8F8")
|
||||
__uniConfig.getTabBarConfig = fun(): Map<String, Any>? {
|
||||
return null
|
||||
}
|
||||
__uniConfig.tabBar = __uniConfig.getTabBarConfig()
|
||||
__uniConfig.conditionUrl = ""
|
||||
__uniConfig.uniIdRouter = _uM()
|
||||
__uniConfig.ready = true
|
||||
}
|
||||
open class GenUniApp : UniAppImpl() {
|
||||
open val vm: GenApp?
|
||||
get() {
|
||||
return getAppVm() as GenApp?
|
||||
}
|
||||
open val `$vm`: GenApp?
|
||||
get() {
|
||||
return getAppVm() as GenApp?
|
||||
}
|
||||
}
|
||||
fun getApp(): GenUniApp {
|
||||
return getUniApp() as GenUniApp
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
@file:Suppress("UNCHECKED_CAST", "USELESS_CAST", "INAPPLICABLE_JVM_NAME", "UNUSED_ANONYMOUS_PARAMETER", "NAME_SHADOWING", "UNNECESSARY_NOT_NULL_ASSERTION")
|
||||
package uni.UNI644401D
|
||||
import io.dcloud.uniapp.*
|
||||
import io.dcloud.uniapp.extapi.*
|
||||
import io.dcloud.uniapp.framework.*
|
||||
import io.dcloud.uniapp.runtime.*
|
||||
import io.dcloud.uniapp.vue.*
|
||||
import io.dcloud.uniapp.vue.shared.*
|
||||
import io.dcloud.uts.*
|
||||
import io.dcloud.uts.Map
|
||||
import io.dcloud.uts.Set
|
||||
import io.dcloud.uts.UTSAndroid
|
||||
import kotlin.properties.Delegates
|
||||
import uts.sdk.modules.SkinCameraUTS.XfCameraComponent
|
||||
import uts.sdk.modules.SkinCameraUTS.takePhoto
|
||||
import uts.sdk.modules.SkinCameraUTS.TakePhotoOption
|
||||
import uts.sdk.modules.SkinCameraUTS.TakePhotoSuccessCallbackResult
|
||||
import uts.sdk.modules.SkinCameraUTS.GeneralCallbackResult
|
||||
open class GenPagesIndexIndex : BasePage {
|
||||
constructor(__ins: ComponentInternalInstance, __renderer: String?) : super(__ins, __renderer) {
|
||||
onLoad(fun(_: OnLoadOptions) {}, __ins)
|
||||
}
|
||||
@Suppress("UNUSED_PARAMETER", "UNUSED_VARIABLE")
|
||||
override fun `$render`(): Any? {
|
||||
val _ctx = this
|
||||
val _cache = this.`$`.renderCache
|
||||
return _cE("scroll-view", _uM("class" to "page-scroll-view"), _uA(
|
||||
_cE("view", null, _uA(
|
||||
_cE("button", _uM("onClick" to _ctx._takePhoto), "拍照", 8, _uA(
|
||||
"onClick"
|
||||
)),
|
||||
_cE(XfCameraComponent.name, _uM("style" to _nS(_uM("height" to "750rpx", "background-color" to "red")), "onError" to _ctx.onError, "flash" to _ctx.flash, "position" to _ctx.device), null, 44, _uA(
|
||||
"onError",
|
||||
"flash",
|
||||
"position"
|
||||
)),
|
||||
if (isTrue(_ctx.imagePath)) {
|
||||
_cE("image", _uM("key" to 0, "mode" to "aspectFit", "src" to _ctx.imagePath), null, 8, _uA(
|
||||
"src"
|
||||
))
|
||||
} else {
|
||||
_cC("v-if", true)
|
||||
}
|
||||
))
|
||||
))
|
||||
}
|
||||
open var yanse: String by `$data`
|
||||
open var widthNum: Number by `$data`
|
||||
open var heightNum: Number by `$data`
|
||||
open var flash: String by `$data`
|
||||
open var device: String by `$data`
|
||||
open var imagePath: String by `$data`
|
||||
open var videoPath: String by `$data`
|
||||
@Suppress("USELESS_CAST")
|
||||
override fun data(): Map<String, Any?> {
|
||||
return _uM("yanse" to "red", "widthNum" to 750, "heightNum" to 200, "flash" to "on", "device" to "front", "imagePath" to "", "videoPath" to "https://qiniu-web-assets.dcloud.net.cn/unidoc/zh/2minute-demo.mp4")
|
||||
}
|
||||
open var onError = ::gen_onError_fn
|
||||
open fun gen_onError_fn(res: Any) {
|
||||
console.log(res)
|
||||
}
|
||||
open var _takePhoto = ::gen__takePhoto_fn
|
||||
open fun gen__takePhoto_fn() {
|
||||
takePhoto(TakePhotoOption(success = fun(res: TakePhotoSuccessCallbackResult){
|
||||
console.log("takePhoto", res)
|
||||
this.imagePath = res.tempImagePath
|
||||
}
|
||||
, fail = fun(res: GeneralCallbackResult){
|
||||
console.log("takePhoto", res)
|
||||
}
|
||||
))
|
||||
}
|
||||
companion object {
|
||||
val styles: Map<String, Map<String, Map<String, Any>>> by lazy {
|
||||
_nCS(_uA(
|
||||
styles0
|
||||
), _uA(
|
||||
GenApp.styles
|
||||
))
|
||||
}
|
||||
val styles0: Map<String, Map<String, Map<String, Any>>>
|
||||
get() {
|
||||
return _uM("video" to _pS(_uM("width" to "100%", "height" to 200)))
|
||||
}
|
||||
var inheritAttrs = true
|
||||
var inject: Map<String, Map<String, Any?>> = _uM()
|
||||
var emits: Map<String, Any?> = _uM()
|
||||
var props = _nP(_uM())
|
||||
var propsNeedCastKeys: UTSArray<String> = _uA()
|
||||
var components: Map<String, CreateVueComponent> = _uM()
|
||||
}
|
||||
}
|
||||
-29
@@ -1,29 +0,0 @@
|
||||
{
|
||||
"id": "__UNI__644401D",
|
||||
"name": "SkinCamera",
|
||||
"description": "",
|
||||
"version": {
|
||||
"name": "1.0.0",
|
||||
"code": "100"
|
||||
},
|
||||
"uni-app-x": {
|
||||
"compilerVersion": "4.87"
|
||||
},
|
||||
"app-android": {
|
||||
"distribute": {
|
||||
"modules": {
|
||||
"uni-exit": {},
|
||||
"uni-prompt": {}
|
||||
},
|
||||
"icons": {
|
||||
"hdpi": "",
|
||||
"xhdpi": "",
|
||||
"xxhdpi": "",
|
||||
"xxxhdpi": ""
|
||||
},
|
||||
"splashScreens": {
|
||||
"default": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 3.9 KiB |
Vendored
-14
@@ -1,14 +0,0 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="uts.sdk.modules.XFCameraUTS">
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-feature android:name="android.hardware.camera" android:required="true" />
|
||||
<uses-feature android:name="android.hardware.camera.any" />
|
||||
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false" />
|
||||
<uses-feature android:name="android.hardware.camera.flash" android:required="false" />
|
||||
<uses-feature android:name="android.hardware.microphone" android:required="true" />
|
||||
<application android:requestLegacyExternalStorage="true">
|
||||
<meta-data android:name="ScopedStorage" android:value="true" />
|
||||
</application>
|
||||
</manifest>
|
||||
-16
@@ -1,16 +0,0 @@
|
||||
{
|
||||
"minSdkVersion": "19",
|
||||
"dependencies": [
|
||||
"androidx.camera:camera-core:1.4.0-alpha04",
|
||||
"androidx.camera:camera-camera2:1.4.0-alpha04",
|
||||
"androidx.camera:camera-lifecycle:1.4.0-alpha04",
|
||||
"androidx.camera:camera-video:1.4.0-alpha04",
|
||||
"androidx.camera:camera-view:1.4.0-alpha04"
|
||||
],
|
||||
"components": [
|
||||
{
|
||||
"name": "xf-camera",
|
||||
"class": "uts.sdk.modules.SkinCameraUTS.XfCameraComponent"
|
||||
}
|
||||
]
|
||||
}
|
||||
Vendored
-745
@@ -1,745 +0,0 @@
|
||||
@file:Suppress("UNCHECKED_CAST", "USELESS_CAST", "INAPPLICABLE_JVM_NAME", "UNUSED_ANONYMOUS_PARAMETER", "NAME_SHADOWING", "UNNECESSARY_NOT_NULL_ASSERTION")
|
||||
package uts.sdk.modules.SkinCameraUTS
|
||||
import android.content.Context
|
||||
import android.graphics.ImageFormat
|
||||
import android.util.Size
|
||||
import android.view.ViewGroup
|
||||
import android.widget.FrameLayout
|
||||
import androidx.camera.core.CameraControl
|
||||
import androidx.camera.core.CameraSelector
|
||||
import androidx.camera.core.ImageAnalysis
|
||||
import androidx.camera.core.ImageCapture
|
||||
import androidx.camera.core.ImageCaptureException
|
||||
import androidx.camera.core.ImageProxy
|
||||
import androidx.camera.core.MirrorMode
|
||||
import androidx.camera.core.Preview
|
||||
import androidx.camera.core.UseCaseGroup
|
||||
import androidx.camera.lifecycle.ProcessCameraProvider
|
||||
import androidx.camera.video.FileOutputOptions
|
||||
import androidx.camera.video.Quality
|
||||
import androidx.camera.video.QualitySelector
|
||||
import androidx.camera.video.Recorder
|
||||
import androidx.camera.video.Recording
|
||||
import androidx.camera.video.VideoCapture
|
||||
import androidx.camera.video.VideoRecordEvent
|
||||
import androidx.camera.view.PreviewView
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import io.dcloud.uniapp.*
|
||||
import io.dcloud.uniapp.UniSDKEngine
|
||||
import io.dcloud.uniapp.appframe.PageProxy
|
||||
import io.dcloud.uniapp.dom.node.DomNode
|
||||
import io.dcloud.uniapp.dom.node.PageNode
|
||||
import io.dcloud.uniapp.extapi.*
|
||||
import io.dcloud.uniapp.framework.*
|
||||
import io.dcloud.uniapp.interfaces.INodeData
|
||||
import io.dcloud.uniapp.runtime.*
|
||||
import io.dcloud.uniapp.ui.component.IComponentData
|
||||
import io.dcloud.uniapp.vue.*
|
||||
import io.dcloud.uniapp.vue.shared.*
|
||||
import io.dcloud.uts.*
|
||||
import io.dcloud.uts.Map
|
||||
import io.dcloud.uts.Set
|
||||
import io.dcloud.uts.UTSAndroid
|
||||
import io.dcloud.uts.component.*
|
||||
import io.dcloud.uts.component.UTSComponent
|
||||
import java.io.File
|
||||
import java.util.concurrent.ExecutorService
|
||||
import java.util.concurrent.Executors
|
||||
import kotlin.properties.Delegates
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Deferred
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.async
|
||||
open class GeneralCallbackResult (
|
||||
@JsonNotNull
|
||||
open var errMsg: String,
|
||||
) : UTSObject()
|
||||
open class TakePhotoSuccessCallbackResult (
|
||||
@JsonNotNull
|
||||
open var tempImagePath: String,
|
||||
@JsonNotNull
|
||||
open var quality: String,
|
||||
@JsonNotNull
|
||||
open var errMsg: String,
|
||||
) : UTSObject()
|
||||
typealias TakePhotoFailCallback = (res: GeneralCallbackResult) -> Unit
|
||||
typealias TakePhotoSuccessCallback = (result: TakePhotoSuccessCallbackResult) -> Unit
|
||||
typealias TakePhotoCompleteCallback = (res: GeneralCallbackResult) -> Unit
|
||||
open class TakePhotoOption (
|
||||
open var complete: TakePhotoCompleteCallback? = null,
|
||||
open var fail: TakePhotoFailCallback? = null,
|
||||
open var quality: String? = null,
|
||||
open var selfieMirror: Boolean? = null,
|
||||
open var success: TakePhotoSuccessCallback? = null,
|
||||
) : UTSObject()
|
||||
open class SetZoomSuccessCallbackResult (
|
||||
@JsonNotNull
|
||||
open var zoom: Number,
|
||||
@JsonNotNull
|
||||
open var errMsg: String,
|
||||
) : UTSObject()
|
||||
typealias SetZoomCompleteCallback = (res: GeneralCallbackResult) -> Unit
|
||||
typealias SetZoomFailCallback = (res: GeneralCallbackResult) -> Unit
|
||||
typealias CameraContextSetZoomSuccessCallback = (result: SetZoomSuccessCallbackResult) -> Unit
|
||||
open class CameraContextSetZoomOption (
|
||||
@JsonNotNull
|
||||
open var zoom: Number,
|
||||
open var complete: SetZoomCompleteCallback? = null,
|
||||
open var fail: SetZoomFailCallback? = null,
|
||||
open var success: CameraContextSetZoomSuccessCallback? = null,
|
||||
) : UTSObject()
|
||||
open class StartRecordTimeoutCallbackResult (
|
||||
@JsonNotNull
|
||||
open var tempVideoPath: String,
|
||||
) : UTSObject()
|
||||
typealias StartRecordCompleteCallback = (res: GeneralCallbackResult) -> Unit
|
||||
typealias StartRecordFailCallback = (res: GeneralCallbackResult) -> Unit
|
||||
typealias StartRecordTimeoutCallback = (result: StartRecordTimeoutCallbackResult) -> Unit
|
||||
typealias CameraContextStartRecordSuccessCallback = (res: GeneralCallbackResult) -> Unit
|
||||
open class CameraContextStartRecordOption (
|
||||
open var complete: StartRecordCompleteCallback? = null,
|
||||
open var fail: StartRecordFailCallback? = null,
|
||||
open var selfieMirror: Boolean? = null,
|
||||
open var success: CameraContextStartRecordSuccessCallback? = null,
|
||||
open var timeout: Number? = null,
|
||||
open var timeoutCallback: StartRecordTimeoutCallback? = null,
|
||||
) : UTSObject()
|
||||
open class StopRecordSuccessCallbackResult (
|
||||
@JsonNotNull
|
||||
open var tempVideoPath: String,
|
||||
@JsonNotNull
|
||||
open var errMsg: String,
|
||||
) : UTSObject()
|
||||
typealias StopRecordCompleteCallback = (res: GeneralCallbackResult) -> Unit
|
||||
typealias StopRecordFailCallback = (res: GeneralCallbackResult) -> Unit
|
||||
typealias CameraContextStopRecordSuccessCallback = (result: StopRecordSuccessCallbackResult) -> Unit
|
||||
open class CameraContextStopRecordOption (
|
||||
open var complete: StopRecordCompleteCallback? = null,
|
||||
open var compressed: Boolean? = null,
|
||||
open var fail: StopRecordFailCallback? = null,
|
||||
open var success: CameraContextStopRecordSuccessCallback? = null,
|
||||
) : UTSObject()
|
||||
typealias OnCameraFrameCallback = (result: OnCameraFrameCallbackResult) -> Unit
|
||||
open class OnCameraFrameCallbackResult (
|
||||
@JsonNotNull
|
||||
open var data: Any,
|
||||
@JsonNotNull
|
||||
open var height: Number,
|
||||
@JsonNotNull
|
||||
open var width: Number,
|
||||
) : UTSObject()
|
||||
typealias StartSuccessCallback = (res: GeneralCallbackResult) -> Unit
|
||||
typealias StartFailCallback = (res: GeneralCallbackResult) -> Unit
|
||||
typealias StartCompleteCallback = (res: GeneralCallbackResult) -> Unit
|
||||
open class CameraFrameListenerStartOption (
|
||||
open var complete: StartCompleteCallback? = null,
|
||||
open var fail: StartFailCallback? = null,
|
||||
open var success: StartSuccessCallback? = null,
|
||||
) : UTSObject()
|
||||
typealias StopCompleteCallback = (res: GeneralCallbackResult) -> Unit
|
||||
typealias StopFailCallback = (res: GeneralCallbackResult) -> Unit
|
||||
typealias StopSuccessCallback = (res: GeneralCallbackResult) -> Unit
|
||||
open class StopOption (
|
||||
open var complete: StopCompleteCallback? = null,
|
||||
open var fail: StopFailCallback? = null,
|
||||
open var success: StopSuccessCallback? = null,
|
||||
) : UTSObject()
|
||||
typealias FlashMode = String
|
||||
typealias DevicePosition = String
|
||||
typealias Resolution = String
|
||||
typealias FrameSize = String
|
||||
typealias QualityType = String
|
||||
open class CameraConfig (
|
||||
open var flash: FlashMode? = null,
|
||||
open var devicePosition: DevicePosition? = null,
|
||||
open var resolution: Resolution? = null,
|
||||
open var frameSize: FrameSize? = null,
|
||||
) : UTSObject()
|
||||
typealias SwitchCompleteCallback = (res: GeneralCallbackResult) -> Unit
|
||||
typealias SwitchFailCallback = (res: GeneralCallbackResult) -> Unit
|
||||
typealias SwitchSuccessCallback = (res: GeneralCallbackResult) -> Unit
|
||||
open class SwitchOption (
|
||||
@JsonNotNull
|
||||
open var position: DevicePosition,
|
||||
open var complete: SwitchCompleteCallback? = null,
|
||||
open var fail: SwitchFailCallback? = null,
|
||||
open var success: SwitchSuccessCallback? = null,
|
||||
) : UTSObject()
|
||||
open class OnCameraFrameListenerOption (
|
||||
open var success: OnCameraFrameCallback? = null,
|
||||
) : UTSObject()
|
||||
typealias TakePhoto = (options: TakePhotoOption) -> Unit
|
||||
typealias SetZoom = (options: CameraContextSetZoomOption) -> Unit
|
||||
typealias StartRecord = (options: CameraContextStartRecordOption) -> Unit
|
||||
typealias StopRecord = (options: CameraContextStopRecordOption) -> Unit
|
||||
typealias StartCameraFrame = (options: CameraFrameListenerStartOption) -> Unit
|
||||
typealias StopCameraFrame = (options: StopOption) -> Unit
|
||||
typealias SwitchCamera = (options: SwitchOption) -> Unit
|
||||
val FLASH_MODE_MAP = Map<String, Int>(_uA(
|
||||
_uA(
|
||||
"auto",
|
||||
ImageCapture.FLASH_MODE_AUTO
|
||||
),
|
||||
_uA(
|
||||
"on",
|
||||
ImageCapture.FLASH_MODE_ON
|
||||
),
|
||||
_uA(
|
||||
"torch",
|
||||
ImageCapture.FLASH_MODE_ON
|
||||
),
|
||||
_uA(
|
||||
"off",
|
||||
ImageCapture.FLASH_MODE_OFF
|
||||
)
|
||||
))
|
||||
val DEVICE_POSITION_MAP = Map<String, Int>(_uA(
|
||||
_uA(
|
||||
"back",
|
||||
CameraSelector.LENS_FACING_BACK
|
||||
),
|
||||
_uA(
|
||||
"front",
|
||||
CameraSelector.LENS_FACING_FRONT
|
||||
)
|
||||
))
|
||||
val RESOLUTION_MAP = Map<String, Quality>(_uA(
|
||||
_uA(
|
||||
"low",
|
||||
Quality.LOWEST
|
||||
),
|
||||
_uA(
|
||||
"medium",
|
||||
Quality.HD
|
||||
),
|
||||
_uA(
|
||||
"high",
|
||||
Quality.HIGHEST
|
||||
)
|
||||
))
|
||||
val QUALITY_MAP = Map<String, Int>(_uA(
|
||||
_uA(
|
||||
"high",
|
||||
ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY
|
||||
),
|
||||
_uA(
|
||||
"low",
|
||||
ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY
|
||||
)
|
||||
))
|
||||
val MIRROR_MODE = Map<Boolean, Int>(_uA(
|
||||
_uA(
|
||||
true,
|
||||
MirrorMode.MIRROR_MODE_ON
|
||||
),
|
||||
_uA(
|
||||
false,
|
||||
MirrorMode.MIRROR_MODE_OFF
|
||||
)
|
||||
))
|
||||
open class CameraView : FrameLayout {
|
||||
private var previewView: PreviewView
|
||||
open var cameraProvider: ProcessCameraProvider? = null
|
||||
open var cameraControl: CameraControl? = null
|
||||
private var imageCapture: ImageCapture? = null
|
||||
private var videoCapture: VideoCapture<Recorder>? = null
|
||||
private var recording: Recording? = null
|
||||
private var comp: UTSComponent<CameraView>
|
||||
private var flashMode: FlashMode = "auto"
|
||||
private var devicePosition: DevicePosition = "back"
|
||||
private var resolution: Resolution = "medium"
|
||||
private var quality: QualityType = "normal"
|
||||
private var videoFile: File? = null
|
||||
open var maxZoom: Float = 1.toFloat()
|
||||
open var minZoom: Float = 1.toFloat()
|
||||
private var initialized: Boolean = false
|
||||
private var isListening: Boolean = false
|
||||
private var videoSelfieMirror: Boolean? = null
|
||||
private var cameraContextStopRecordOption: CameraContextStopRecordOption? = null
|
||||
private var onCameraFrameCallback: OnCameraFrameCallback? = null
|
||||
private var cameraExecutor: ExecutorService? = null
|
||||
private var onCameraFrameListenerOption: OnCameraFrameListenerOption? = null
|
||||
constructor(context: Context, comp: UTSComponent<CameraView>, config: CameraConfig = CameraConfig()) : super(context) {
|
||||
this.comp = comp
|
||||
this.flashMode = config.flash ?: "auto"
|
||||
this.devicePosition = config.devicePosition ?: "back"
|
||||
this.resolution = config.resolution ?: "medium"
|
||||
this.previewView = PreviewView(context)
|
||||
var layoutParam = FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
|
||||
this.addView(this.previewView, layoutParam)
|
||||
this.setupCamera()
|
||||
}
|
||||
open fun setupCamera() {
|
||||
var cameraProviderFuture = ProcessCameraProvider.getInstance(UTSAndroid.getUniActivity()!!)
|
||||
cameraProviderFuture.addListener(fun(){
|
||||
try {
|
||||
this.cameraProvider = cameraProviderFuture.get()
|
||||
this.bindCamera()
|
||||
}
|
||||
catch (e: Throwable) {
|
||||
var ret: Map<String, Any> = Map()
|
||||
ret.set("errMsg", JSON.stringify(e))
|
||||
this.comp.`$emit`("error", ret)
|
||||
}
|
||||
}
|
||||
, ContextCompat.getMainExecutor(UTSAndroid.getUniActivity()!!))
|
||||
}
|
||||
open fun bindCamera() {
|
||||
var targetResolution = RESOLUTION_MAP.get(this.resolution) ?: Quality.HD
|
||||
var captureMode = QUALITY_MAP.get(this.quality) ?: ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY
|
||||
var preview = Preview.Builder().build()
|
||||
preview.setSurfaceProvider(this.previewView.getSurfaceProvider())
|
||||
var imageCapture = ImageCapture.Builder().setFlashMode(FLASH_MODE_MAP.get(this.flashMode)!!).setCaptureMode(captureMode).build()
|
||||
var cameraSelector = CameraSelector.Builder().requireLensFacing(DEVICE_POSITION_MAP.get(this.devicePosition)!!).build()
|
||||
var recorder = Recorder.Builder().setQualitySelector(QualitySelector.from(targetResolution))
|
||||
var mirrorMode = if (this.videoSelfieMirror == null) {
|
||||
MirrorMode.MIRROR_MODE_ON_FRONT_ONLY
|
||||
} else {
|
||||
MIRROR_MODE.get(this.videoSelfieMirror!!)
|
||||
}
|
||||
var videoCapture = VideoCapture.Builder(recorder.build()).setMirrorMode(mirrorMode!!).build()
|
||||
this.videoCapture = videoCapture
|
||||
this.imageCapture = imageCapture
|
||||
this.cameraProvider!!.unbindAll()
|
||||
var imageAnalysis = ImageAnalysis.Builder().setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST).build()
|
||||
var viewPort = this.previewView.getViewPort()!!
|
||||
var useCaseGroup = UseCaseGroup.Builder().addUseCase(preview).addUseCase(imageAnalysis).addUseCase(imageCapture).addUseCase(videoCapture).setViewPort(viewPort).build()
|
||||
this.cameraExecutor = Executors.newSingleThreadExecutor()
|
||||
var _this = this
|
||||
open class Analyzer : ImageAnalysis.Analyzer {
|
||||
constructor(){}
|
||||
override fun analyze(image: ImageProxy) {
|
||||
if (_this.isListening) {
|
||||
var imageFormat = image.getFormat()
|
||||
var width = image.width
|
||||
var height = image.height
|
||||
if (imageFormat == ImageFormat.YUV_420_888) {
|
||||
_this.onCameraFrameCallback?.invoke(OnCameraFrameCallbackResult(width = width, height = height, data = image))
|
||||
_this.onCameraFrameListenerOption?.success?.invoke(OnCameraFrameCallbackResult(width = width, height = height, data = image))
|
||||
}
|
||||
}
|
||||
image.close()
|
||||
}
|
||||
override fun getDefaultTargetResolution(): Size {
|
||||
return Size(640, 480)
|
||||
}
|
||||
}
|
||||
imageAnalysis.setAnalyzer(this.cameraExecutor!!, Analyzer() as ImageAnalysis.Analyzer)
|
||||
var camera = this.cameraProvider!!.bindToLifecycle(UTSAndroid.getUniActivity()!! as LifecycleOwner, cameraSelector, useCaseGroup)
|
||||
this.cameraControl = camera.getCameraControl()
|
||||
if (!this.initialized) {
|
||||
this.initialized = true
|
||||
var zoomState = camera.getCameraInfo().getZoomState().getValue()
|
||||
this.maxZoom = zoomState?.getMaxZoomRatio() ?: 1.toFloat()
|
||||
this.minZoom = zoomState?.getMinZoomRatio() ?: 1.toFloat()
|
||||
var maxZoom = this.maxZoom
|
||||
var ret: Map<String, Any> = Map()
|
||||
ret.set("maxZoom", maxZoom)
|
||||
this.comp.`$emit`("ready", ret)
|
||||
}
|
||||
}
|
||||
open fun unbindCamera() {
|
||||
this.cameraProvider?.unbindAll()
|
||||
this.cameraExecutor?.shutdown()
|
||||
}
|
||||
open fun setConfig(config: CameraConfig = CameraConfig()) {
|
||||
this.flashMode = config.flash ?: "auto"
|
||||
this.devicePosition = config.devicePosition ?: "back"
|
||||
this.resolution = config.resolution ?: "medium"
|
||||
}
|
||||
open fun 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)!!)
|
||||
}
|
||||
open fun switchCamera(position: DevicePosition) {
|
||||
this.devicePosition = position
|
||||
if (DEVICE_POSITION_MAP.get(position) == null || this.cameraProvider == null) {
|
||||
return
|
||||
}
|
||||
this.unbindCamera()
|
||||
this.bindCamera()
|
||||
}
|
||||
open fun isResolutionSupported(context: Context, cameraSelector: CameraSelector, targetResolution: Size): Boolean {
|
||||
return true
|
||||
}
|
||||
open fun takePhoto(option: TakePhotoOption) {
|
||||
var quality = option.quality ?: "high"
|
||||
this.quality = quality
|
||||
if (quality == "low") {
|
||||
this.unbindCamera()
|
||||
this.bindCamera()
|
||||
}
|
||||
var privateDir = UTSAndroid.getAppCachePath()!!
|
||||
val tempFile = File(privateDir)
|
||||
if (!tempFile.exists()) {
|
||||
tempFile.mkdirs()
|
||||
}
|
||||
val photoFile = File(privateDir, Date.now() + ".png")
|
||||
var metadata = ImageCapture.Metadata()
|
||||
metadata.setReversedHorizontal(option.selfieMirror ?: DEVICE_POSITION_MAP.get(this.devicePosition)!! == CameraSelector.LENS_FACING_FRONT)
|
||||
val outputOptions = ImageCapture.OutputFileOptions.Builder(photoFile).setMetadata(metadata).build()
|
||||
open class OnImageSavedCallback : ImageCapture.OnImageSavedCallback {
|
||||
override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) {
|
||||
option.success?.invoke(TakePhotoSuccessCallbackResult(tempImagePath = photoFile.getAbsolutePath(), quality = quality, errMsg = "ok"))
|
||||
option.complete?.invoke(GeneralCallbackResult(errMsg = "ok"))
|
||||
}
|
||||
override fun onError(err: ImageCaptureException) {
|
||||
val result = GeneralCallbackResult(errMsg = err.toString())
|
||||
option.fail?.invoke(result)
|
||||
option.complete?.invoke(result)
|
||||
}
|
||||
override fun onCaptureStarted() {}
|
||||
}
|
||||
this.imageCapture!!.takePicture(outputOptions, ContextCompat.getMainExecutor(UTSAndroid.getUniActivity()!!), OnImageSavedCallback() as ImageCapture.OnImageSavedCallback)
|
||||
}
|
||||
open fun setZoom(option: CameraContextSetZoomOption) {
|
||||
if (this.cameraControl != null) {
|
||||
try {
|
||||
this.cameraControl!!.setZoomRatio(option.zoom.toFloat())
|
||||
option.success?.invoke(SetZoomSuccessCallbackResult(zoom = option.zoom, errMsg = "ok"))
|
||||
option.complete?.invoke(GeneralCallbackResult(errMsg = "ok"))
|
||||
} catch (e: Throwable) {
|
||||
val result = GeneralCallbackResult(errMsg = e.toString())
|
||||
option.complete?.invoke(result)
|
||||
option.fail?.invoke(result)
|
||||
}
|
||||
} else {
|
||||
val result = GeneralCallbackResult(errMsg = "相机不存在")
|
||||
option.complete?.invoke(result)
|
||||
option.fail?.invoke(result)
|
||||
}
|
||||
}
|
||||
open fun 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?.invoke(GeneralCallbackResult(errMsg = "已经在录制或相机不存在"))
|
||||
option.complete?.invoke(GeneralCallbackResult(errMsg = "已经在录制或相机不存在"))
|
||||
return
|
||||
}
|
||||
var privateDir = UTSAndroid.getAppCachePath()!!
|
||||
this.videoFile = File(privateDir, Date.now() + "video.mp4")
|
||||
var outputFileOptions = FileOutputOptions.Builder(this.videoFile!!).build()
|
||||
this.recording = this.videoCapture!!.output.prepareRecording(UTSAndroid.getUniActivity()!!, outputFileOptions).withAudioEnabled().start(ContextCompat.getMainExecutor(UTSAndroid.getUniActivity()!!), fun(recordEvent: VideoRecordEvent){
|
||||
if (recordEvent is VideoRecordEvent.Start) {
|
||||
option.success?.invoke(GeneralCallbackResult(errMsg = "ok"))
|
||||
if (option.timeout != null && option.timeout!! > 0) {
|
||||
setTimeout(fun(){
|
||||
this.recording?.stop()
|
||||
}
|
||||
, option.timeout!!)
|
||||
}
|
||||
} else if (recordEvent is VideoRecordEvent.Finalize) {
|
||||
this.recording?.close()
|
||||
this.recording = null
|
||||
var tempVideoPath = this.videoFile?.getAbsolutePath()!!
|
||||
this.cameraContextStopRecordOption?.success?.invoke(StopRecordSuccessCallbackResult(tempVideoPath = tempVideoPath, errMsg = "ok"))
|
||||
option.timeoutCallback?.invoke(StartRecordTimeoutCallbackResult(tempVideoPath = tempVideoPath))
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
open fun stopRecord(option: CameraContextStopRecordOption) {
|
||||
if (this.recording == null) {
|
||||
option.fail?.invoke(GeneralCallbackResult(errMsg = "未开始录制"))
|
||||
option.complete?.invoke(GeneralCallbackResult(errMsg = "未开始录制"))
|
||||
return
|
||||
}
|
||||
this.cameraContextStopRecordOption = option
|
||||
this.recording?.stop()
|
||||
}
|
||||
open fun onCameraFrame(callback: OnCameraFrameCallback? = null) {
|
||||
this.onCameraFrameCallback = callback
|
||||
}
|
||||
open fun onCameraFrameListener(option: OnCameraFrameListenerOption) {
|
||||
this.onCameraFrameListenerOption = option
|
||||
}
|
||||
open fun cameraFrameOnStart(option: CameraFrameListenerStartOption?) {
|
||||
this.isListening = true
|
||||
option?.success?.invoke(GeneralCallbackResult(errMsg = "ok"))
|
||||
}
|
||||
open fun cameraFrameOnStop(option: StopOption?) {
|
||||
this.isListening = false
|
||||
option?.success?.invoke(GeneralCallbackResult(errMsg = "ok"))
|
||||
}
|
||||
}
|
||||
open class CameraManager {
|
||||
private var cameraView: CameraView? = null
|
||||
public open fun setCameraView(cameraView: CameraView?): Unit {
|
||||
this.cameraView = cameraView!!
|
||||
}
|
||||
public open fun getCameraView(): CameraView? {
|
||||
return this.cameraView
|
||||
}
|
||||
public open fun dispose(): Unit {
|
||||
this.cameraView = null
|
||||
}
|
||||
companion object {
|
||||
private var instance: CameraManager? = null
|
||||
public fun getInstance(): CameraManager {
|
||||
if (CameraManager.instance == null) {
|
||||
CameraManager.instance = CameraManager()
|
||||
}
|
||||
return CameraManager.instance!!
|
||||
}
|
||||
}
|
||||
}
|
||||
open class XfCameraComponent : UTSComponent<CameraView> {
|
||||
constructor(proxy: PageProxy, componentData: IComponentData) : super(proxy, componentData)
|
||||
open var mode: String = "normal"
|
||||
open var resolution: String = "medium"
|
||||
open var position: String = "back"
|
||||
open var flash: String = "auto"
|
||||
open var frameSize: String = "medium"
|
||||
override fun created() {}
|
||||
override fun NVBeforeLoad() {}
|
||||
override fun NVLoad(): CameraView {
|
||||
var previewView = CameraView(this.`$androidContext`!!, this)
|
||||
previewView.setTag("9527")
|
||||
CameraManager.getInstance().setCameraView(previewView)
|
||||
return previewView
|
||||
}
|
||||
override fun NVLoaded() {}
|
||||
override fun NVLayouted() {
|
||||
this.`$el`?.setConfig(CameraConfig(resolution = this.resolution, frameSize = this.frameSize, flash = this.flash))
|
||||
this.`$el`?.switchCamera(this.position)
|
||||
}
|
||||
override fun NVBeforeUnload() {
|
||||
var ret: Map<String, Any> = Map()
|
||||
this.`$emit`("stop", ret)
|
||||
this.`$el`?.unbindCamera()
|
||||
}
|
||||
override fun NVUnloaded() {
|
||||
CameraManager.getInstance().dispose()
|
||||
}
|
||||
override fun unmounted() {}
|
||||
override fun `$init`() {
|
||||
this.`$watch`<String>("position", fun(newValue, _){
|
||||
this.`$el`?.switchCamera(newValue)
|
||||
}
|
||||
)
|
||||
this.`$watch`<String>("flash", fun(newValue, _){
|
||||
this.`$el`?.setFlash(newValue)
|
||||
}
|
||||
)
|
||||
}
|
||||
companion object {
|
||||
var name = "xf-camera"
|
||||
init {
|
||||
io.dcloud.uniapp.UniSDKEngine.registerUniComponent("xf-camera", XfCameraComponent::class.java, XfCameraElement::class.java)
|
||||
}
|
||||
}
|
||||
}
|
||||
open class XfCameraElement : DomNode {
|
||||
constructor(data: INodeData, pageNode: PageNode) : super(data, pageNode)
|
||||
}
|
||||
open class CameraFrameListener {
|
||||
open lateinit var context: CameraContext
|
||||
constructor(context: CameraContext){
|
||||
this.context = context
|
||||
}
|
||||
open fun start(): Unit {
|
||||
this.start(null)
|
||||
}
|
||||
open fun start(option: CameraFrameListenerStartOption?) {
|
||||
this.context.getCamera().then(fun(camera: CameraView){
|
||||
camera.cameraFrameOnStart(option)
|
||||
}
|
||||
)
|
||||
}
|
||||
open fun stop(): Unit {
|
||||
this.stop(null)
|
||||
}
|
||||
open fun stop(option: StopOption?) {
|
||||
this.context.getCamera().then(fun(camera: CameraView){
|
||||
camera.cameraFrameOnStop(option)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
open class CameraContext {
|
||||
private var mCameraFrameListener: CameraFrameListener? = null
|
||||
constructor(){}
|
||||
open fun getCamera(): UTSPromise<CameraView> {
|
||||
var cameraPreview = CameraManager.getInstance().getCameraView()
|
||||
if (cameraPreview != null) {
|
||||
return UTSPromise.resolve<CameraView>(cameraPreview)
|
||||
}
|
||||
var resolveFunc: ((res: CameraView) -> Unit)? = null
|
||||
var rejectFunc: ((res: String) -> Unit)? = null
|
||||
var maxAttempts: Number = 5
|
||||
var currentAttempt: Number = 0
|
||||
fun attemptGetCamera() {
|
||||
var camera = CameraManager.getInstance().getCameraView()
|
||||
if (camera != null) {
|
||||
resolveFunc?.invoke(camera)
|
||||
} else if (currentAttempt < maxAttempts) {
|
||||
currentAttempt++
|
||||
setTimeout(fun(){
|
||||
attemptGetCamera()
|
||||
}, 100)
|
||||
} else {
|
||||
rejectFunc?.invoke("Camera not found after multiple attempts")
|
||||
}
|
||||
}
|
||||
return UTSPromise<CameraView>(fun(resolve, reject){
|
||||
resolveFunc = resolve
|
||||
rejectFunc = reject
|
||||
attemptGetCamera()
|
||||
}
|
||||
)
|
||||
}
|
||||
open fun takePhoto(option: TakePhotoOption) {
|
||||
this.getCamera().then(fun(camera: CameraView){
|
||||
camera.takePhoto(option)
|
||||
}
|
||||
).`catch`(fun(){
|
||||
option.fail?.invoke(GeneralCallbackResult(errMsg = "未找到相机"))
|
||||
option.complete?.invoke(GeneralCallbackResult(errMsg = "未找到相机"))
|
||||
}
|
||||
)
|
||||
}
|
||||
open fun setZoom(option: CameraContextSetZoomOption) {
|
||||
this.getCamera().then(fun(camera: CameraView){
|
||||
camera.setZoom(option)
|
||||
}
|
||||
).`catch`(fun(){
|
||||
option.fail?.invoke(GeneralCallbackResult(errMsg = "未找到相机"))
|
||||
option.complete?.invoke(GeneralCallbackResult(errMsg = "未找到相机"))
|
||||
}
|
||||
)
|
||||
}
|
||||
open fun startRecord(option: CameraContextStartRecordOption) {
|
||||
this.getCamera().then(fun(camera: CameraView){
|
||||
camera.startRecord(option)
|
||||
}
|
||||
).`catch`(fun(){
|
||||
option.fail?.invoke(GeneralCallbackResult(errMsg = "未找到相机"))
|
||||
option.complete?.invoke(GeneralCallbackResult(errMsg = "未找到相机"))
|
||||
}
|
||||
)
|
||||
}
|
||||
open fun stopRecord(option: CameraContextStopRecordOption) {
|
||||
this.getCamera().then(fun(camera: CameraView){
|
||||
camera.stopRecord(option)
|
||||
}
|
||||
).`catch`(fun(){
|
||||
option.fail?.invoke(GeneralCallbackResult(errMsg = "未找到相机"))
|
||||
option.complete?.invoke(GeneralCallbackResult(errMsg = "未找到相机"))
|
||||
}
|
||||
)
|
||||
}
|
||||
open fun onCameraFrame(): CameraFrameListener {
|
||||
return this.onCameraFrame(null)
|
||||
}
|
||||
open fun onCameraFrame(callback: OnCameraFrameCallback? = null): CameraFrameListener {
|
||||
if (this.mCameraFrameListener == null) {
|
||||
this.mCameraFrameListener = CameraFrameListener(this)
|
||||
}
|
||||
this.getCamera().then(fun(camera: CameraView){
|
||||
camera.onCameraFrame(callback)
|
||||
}
|
||||
)
|
||||
return this.mCameraFrameListener!!
|
||||
}
|
||||
open fun onCameraFrameListener(option: OnCameraFrameListenerOption) {
|
||||
this.getCamera().then(fun(camera: CameraView){
|
||||
camera.onCameraFrameListener(option)
|
||||
}
|
||||
).`catch`(fun(){
|
||||
console.log("未找到相机")
|
||||
}
|
||||
)
|
||||
}
|
||||
open fun cameraFrameOnStart(option: CameraFrameListenerStartOption) {
|
||||
this.getCamera().then(fun(camera: CameraView){
|
||||
camera.cameraFrameOnStart(option)
|
||||
}
|
||||
).`catch`(fun(){
|
||||
option.fail?.invoke(GeneralCallbackResult(errMsg = "未找到相机"))
|
||||
option.complete?.invoke(GeneralCallbackResult(errMsg = "未找到相机"))
|
||||
}
|
||||
)
|
||||
}
|
||||
open fun cameraFrameOnStop(option: StopOption) {
|
||||
this.getCamera().then(fun(camera: CameraView){
|
||||
camera.cameraFrameOnStop(option)
|
||||
}
|
||||
).`catch`(fun(){
|
||||
option.fail?.invoke(GeneralCallbackResult(errMsg = "未找到相机"))
|
||||
option.complete?.invoke(GeneralCallbackResult(errMsg = "未找到相机"))
|
||||
}
|
||||
)
|
||||
}
|
||||
open fun switchCamera(option: SwitchOption) {
|
||||
this.getCamera().then(fun(camera: CameraView){
|
||||
camera.switchCamera(option.position)
|
||||
option.success?.invoke(GeneralCallbackResult(errMsg = "ok"))
|
||||
option.complete?.invoke(GeneralCallbackResult(errMsg = "ok"))
|
||||
}
|
||||
).`catch`(fun(){
|
||||
option.fail?.invoke(GeneralCallbackResult(errMsg = "未找到相机"))
|
||||
option.complete?.invoke(GeneralCallbackResult(errMsg = "未找到相机"))
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
var context: CameraContext = CameraContext()
|
||||
fun createCameraContext(): CameraContext {
|
||||
return context
|
||||
}
|
||||
val takePhoto: TakePhoto = fun(options: TakePhotoOption) {
|
||||
UTSAndroid.getDispatcher("main").async(fun(_) {
|
||||
context.takePhoto(options)
|
||||
}
|
||||
, null)
|
||||
}
|
||||
val setZoom: SetZoom = fun(options: CameraContextSetZoomOption) {
|
||||
UTSAndroid.getDispatcher("main").async(fun(_) {
|
||||
context.setZoom(options)
|
||||
}
|
||||
, null)
|
||||
}
|
||||
val startRecord: StartRecord = fun(options: CameraContextStartRecordOption) {
|
||||
UTSAndroid.getDispatcher("main").async(fun(_) {
|
||||
context.startRecord(options)
|
||||
}
|
||||
, null)
|
||||
}
|
||||
val stopRecord: StopRecord = fun(options: CameraContextStopRecordOption) {
|
||||
UTSAndroid.getDispatcher("main").async(fun(_) {
|
||||
context.stopRecord(options)
|
||||
}
|
||||
, null)
|
||||
}
|
||||
fun onCameraFrameListener(options: OnCameraFrameListenerOption) {
|
||||
UTSAndroid.getDispatcher("main").async(fun(_) {
|
||||
context.onCameraFrameListener(options)
|
||||
}
|
||||
, null)
|
||||
}
|
||||
val startCameraFrame: StartCameraFrame = fun(options: CameraFrameListenerStartOption) {
|
||||
UTSAndroid.getDispatcher("main").async(fun(_) {
|
||||
context.cameraFrameOnStart(options)
|
||||
}
|
||||
, null)
|
||||
}
|
||||
val stopCameraFrame: StopCameraFrame = fun(options: StopOption) {
|
||||
UTSAndroid.getDispatcher("main").async(fun(_) {
|
||||
context.cameraFrameOnStop(options)
|
||||
}
|
||||
, null)
|
||||
}
|
||||
val switchCamera: SwitchCamera = fun(options: SwitchOption) {
|
||||
UTSAndroid.getDispatcher("main").async(fun(_) {
|
||||
context.switchCamera(options)
|
||||
}
|
||||
, null)
|
||||
}
|
||||
-5
@@ -1,5 +0,0 @@
|
||||
const __uniConfig = {"pages":[],"globalStyle":{"navigationBarTextStyle":"black","navigationBarTitleText":"uni-app x","navigationBarBackgroundColor":"#F8F8F8","backgroundColor":"#F8F8F8"},"appname":"SkinCamera","compilerVersion":"4.87","entryPagePath":"pages/index/index","entryPageQuery":"","realEntryPagePath":"","themeConfig":{}};
|
||||
__uniConfig.getTabBarConfig = () => {return undefined};
|
||||
__uniConfig.tabBar = __uniConfig.getTabBarConfig();
|
||||
const __uniRoutes = [{"path":"pages/index/index","meta":{"isQuit":true,"isEntry":true,"navigationBarTitleText":"uni-app x"}}].map(uniRoute=>(uniRoute.meta.route=uniRoute.path,__uniConfig.pages.push(uniRoute.path),uniRoute.path='/'+uniRoute.path,uniRoute)).concat(typeof __uniSystemRoutes !== 'undefined' ? __uniSystemRoutes : []);
|
||||
|
||||
-103
@@ -1,103 +0,0 @@
|
||||
(function(vue) {
|
||||
"use strict";
|
||||
const { registerUTSInterface, initUTSProxyClass, initUTSProxyFunction, initUTSPackageName, initUTSIndexClassName, initUTSClassName } = uni;
|
||||
const name = "SkinCameraUTS";
|
||||
const moduleName = "UTS实现自定义相机拍照和录像功能";
|
||||
const moduleType = "";
|
||||
const errMsg = "";
|
||||
const is_uni_modules = true;
|
||||
const pkg = /* @__PURE__ */ initUTSPackageName(name, is_uni_modules);
|
||||
const cls = /* @__PURE__ */ initUTSIndexClassName(name, is_uni_modules);
|
||||
const XfCameraComponent = {};
|
||||
const takePhoto = /* @__PURE__ */ initUTSProxyFunction(false, { moduleName, moduleType, errMsg, main: true, package: pkg, class: cls, name: "takePhotoByJs", keepAlive: false, params: [{ "name": "options", "type": "UTSSDKModulesSkinCameraUTSTakePhotoOptionJSONObject" }], return: "" });
|
||||
function resolveEasycom(component, easycom) {
|
||||
return typeof component === "string" ? easycom : component;
|
||||
}
|
||||
const _sfc_main$1 = vue.defineComponent({
|
||||
data() {
|
||||
return {
|
||||
yanse: "red",
|
||||
widthNum: 750,
|
||||
heightNum: 200,
|
||||
flash: "on",
|
||||
device: "front",
|
||||
imagePath: "",
|
||||
videoPath: "https://qiniu-web-assets.dcloud.net.cn/unidoc/zh/2minute-demo.mp4"
|
||||
};
|
||||
},
|
||||
onLoad() {
|
||||
},
|
||||
methods: {
|
||||
onError(res = null) {
|
||||
uni.__log__("log", "at pages/index/index.uvue:54", res);
|
||||
},
|
||||
_takePhoto() {
|
||||
takePhoto({
|
||||
success: (res = null) => {
|
||||
uni.__log__("log", "at pages/index/index.uvue:59", "takePhoto", res);
|
||||
this.imagePath = res.tempImagePath;
|
||||
},
|
||||
fail: (res = null) => {
|
||||
uni.__log__("log", "at pages/index/index.uvue:64", "takePhoto", res);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
const _style_0$1 = { "video": { "": { "width": "100%", "height": 200 } } };
|
||||
const _export_sfc = (sfc, props) => {
|
||||
const target = sfc.__vccOpts || sfc;
|
||||
for (const [key, val] of props) {
|
||||
target[key] = val;
|
||||
}
|
||||
return target;
|
||||
};
|
||||
function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
|
||||
resolveEasycom(vue.resolveDynamicComponent("xf-camera"), XfCameraComponent);
|
||||
return vue.openBlock(), vue.createElementBlock("scroll-view", { class: "page-scroll-view" }, [
|
||||
vue.createElementVNode("view", null, [
|
||||
vue.createElementVNode("button", {
|
||||
onClick: _cache[0] || (_cache[0] = (...args) => $options._takePhoto && $options._takePhoto(...args))
|
||||
}, "拍照"),
|
||||
vue.createElementVNode("xf-camera", {
|
||||
style: { "height": "750rpx", "background-color": "red" },
|
||||
onError: _cache[1] || (_cache[1] = (...args) => $options.onError && $options.onError(...args)),
|
||||
flash: $data.flash,
|
||||
position: $data.device
|
||||
}, null, 40, ["flash", "position"]),
|
||||
$data.imagePath ? (vue.openBlock(), vue.createElementBlock("image", {
|
||||
key: 0,
|
||||
mode: "aspectFit",
|
||||
src: $data.imagePath
|
||||
}, null, 8, ["src"])) : vue.createCommentVNode("", true)
|
||||
])
|
||||
]);
|
||||
}
|
||||
const PagesIndexIndex = /* @__PURE__ */ _export_sfc(_sfc_main$1, [["render", _sfc_render], ["styles", [_style_0$1]]]);
|
||||
__definePage("pages/index/index", PagesIndexIndex);
|
||||
const _sfc_main = vue.defineComponent({
|
||||
onLaunch() {
|
||||
uni.__log__("log", "at App.uvue:7", "App Launch");
|
||||
},
|
||||
onShow() {
|
||||
uni.__log__("log", "at App.uvue:10", "App Show");
|
||||
},
|
||||
onHide() {
|
||||
uni.__log__("log", "at App.uvue:13", "App Hide");
|
||||
},
|
||||
onExit() {
|
||||
uni.__log__("log", "at App.uvue:34", "App Exit");
|
||||
}
|
||||
});
|
||||
const _style_0 = { "uni-row": { "": { "flexDirection": "row" } }, "uni-column": { "": { "flexDirection": "column" } } };
|
||||
const App = /* @__PURE__ */ _export_sfc(_sfc_main, [["styles", [_style_0]]]);
|
||||
const __global__ = typeof globalThis === "undefined" ? Function("return this")() : globalThis;
|
||||
__global__.__uniX = true;
|
||||
function createApp() {
|
||||
const app = vue.createSSRApp(App);
|
||||
return {
|
||||
app
|
||||
};
|
||||
}
|
||||
createApp().app.mount("#app");
|
||||
})(Vue);
|
||||
-28
@@ -1,28 +0,0 @@
|
||||
{
|
||||
"id": "__UNI__644401D",
|
||||
"name": "SkinCamera",
|
||||
"description": "",
|
||||
"version": {
|
||||
"name": "1.0.0",
|
||||
"code": "100"
|
||||
},
|
||||
"uni-app-x": {
|
||||
"compilerVersion": "4.87"
|
||||
},
|
||||
"app-ios": {
|
||||
"distribute": {
|
||||
"modules": {},
|
||||
"icons": {},
|
||||
"splashScreens": {},
|
||||
"privacyDescription": {
|
||||
"NSPhotoLibraryUsageDescription": "读取相册",
|
||||
"NSPhotoLibraryAddUsageDescription": "写入相册",
|
||||
"NSCameraUsageDescription": "要摄像头",
|
||||
"NSMicrophoneUsageDescription": "要麦克风",
|
||||
"NSLocationWhenInUseUsageDescription": "要访问",
|
||||
"NSLocationAlwaysUsageDescription": "要访问",
|
||||
"NSLocationAlwaysAndWhenInUseUsageDescription": "要访问"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 3.9 KiB |
-10
@@ -1,10 +0,0 @@
|
||||
{
|
||||
"deploymentTarget": "11",
|
||||
"components": [
|
||||
{
|
||||
"name": "xf-camera",
|
||||
"class": "UTSSDKModulesSkinCameraUTSXfCameraComponent",
|
||||
"delegateClass": "XfCameraComponentRegister"
|
||||
}
|
||||
]
|
||||
}
|
||||
-1407
File diff suppressed because it is too large
Load Diff
-39
@@ -1,39 +0,0 @@
|
||||
|
||||
|
||||
let firstBackTime = 0
|
||||
|
||||
const __sfc__ = defineApp({
|
||||
onLaunch() {
|
||||
console.log('App Launch', " at App.uvue:7")
|
||||
},
|
||||
onShow() {
|
||||
console.log('App Show', " at App.uvue:10")
|
||||
},
|
||||
onHide() {
|
||||
console.log('App Hide', " at App.uvue:13")
|
||||
},
|
||||
|
||||
onLastPageBackPress() {
|
||||
console.log('App LastPageBackPress', " at App.uvue:17")
|
||||
if (firstBackTime == 0) {
|
||||
uni.showToast({
|
||||
title: '再按一次退出应用',
|
||||
position: 'bottom',
|
||||
})
|
||||
firstBackTime = Date.now()
|
||||
setTimeout(() => {
|
||||
firstBackTime = 0
|
||||
}, 2000)
|
||||
} else if (Date.now() - firstBackTime < 2000) {
|
||||
firstBackTime = Date.now()
|
||||
uni.exit()
|
||||
}
|
||||
},
|
||||
|
||||
onExit() {
|
||||
console.log('App Exit', " at App.uvue:34")
|
||||
},
|
||||
})
|
||||
|
||||
export default __sfc__
|
||||
const GenAppStyles = [_uM([["uni-row", _pS(_uM([["flexDirection", "row"]]))], ["uni-column", _pS(_uM([["flexDirection", "column"]]))]])]
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"sources":["App.uvue"],"names":[],"mappings":";;CAEC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE;;CAEpB,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE;EACd,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE;GACV,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;EACzB,CAAC;EACD,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE;GACR,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;EACvB,CAAC;EACD,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE;GACR,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;EACvB,CAAC;;EAED,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE;GACrB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;GACnC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE;IACvB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;KACb,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;KACjB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACnB,CAAC;IACD,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACzB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE;KAChB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE;IACjB,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;GACR,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE;IAC7C,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACzB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;GACV;EACD,CAAC;;EAED,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE;GACR,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;EACvB,CAAC;CACF","file":"App.uvue","sourceRoot":"","sourcesContent":["<script lang=\"uts\">\r\n\r\n\tlet firstBackTime = 0\r\n\r\n\texport default {\r\n\t\tonLaunch() {\r\n\t\t\tconsole.log('App Launch')\r\n\t\t},\r\n\t\tonShow() {\r\n\t\t\tconsole.log('App Show')\r\n\t\t},\r\n\t\tonHide() {\r\n\t\t\tconsole.log('App Hide')\r\n\t\t},\r\n\r\n\t\tonLastPageBackPress() {\r\n\t\t\tconsole.log('App LastPageBackPress')\r\n\t\t\tif (firstBackTime == 0) {\r\n\t\t\t\tuni.showToast({\r\n\t\t\t\t\ttitle: '再按一次退出应用',\r\n\t\t\t\t\tposition: 'bottom',\r\n\t\t\t\t})\r\n\t\t\t\tfirstBackTime = Date.now()\r\n\t\t\t\tsetTimeout(() => {\r\n\t\t\t\t\tfirstBackTime = 0\r\n\t\t\t\t}, 2000)\r\n\t\t\t} else if (Date.now() - firstBackTime < 2000) {\r\n\t\t\t\tfirstBackTime = Date.now()\r\n\t\t\t\tuni.exit()\r\n\t\t\t}\r\n\t\t},\r\n\r\n\t\tonExit() {\r\n\t\t\tconsole.log('App Exit')\r\n\t\t},\r\n\t}\r\n</script>\r\n\r\n<style>\r\n\t/*每个页面公共css */\r\n\t.uni-row {\r\n\t\tflex-direction: row;\r\n\t}\r\n\r\n\t.uni-column {\r\n\t\tflex-direction: column;\r\n\t}\r\n</style>"]}
|
||||
-41
@@ -1,41 +0,0 @@
|
||||
import App from './App.uvue'
|
||||
|
||||
import { createSSRApp } from 'vue'
|
||||
export function createApp() {
|
||||
const app = createSSRApp(App)
|
||||
return {
|
||||
app
|
||||
}
|
||||
}
|
||||
export function main(app: IApp) {
|
||||
definePageRoutes();
|
||||
defineAppConfig();
|
||||
(createApp()['app'] as VueApp).mount(app, GenUniApp());
|
||||
}
|
||||
|
||||
export class UniAppConfig extends io.dcloud.uniapp.appframe.AppConfig {
|
||||
override name: string = "SkinCamera"
|
||||
override appid: string = "__UNI__644401D"
|
||||
override versionName: string = "1.0.0"
|
||||
override versionCode: string = "100"
|
||||
override uniCompilerVersion: string = "4.87"
|
||||
|
||||
constructor() { super() }
|
||||
}
|
||||
|
||||
import GenPagesIndexIndexClass from './pages/index/index.uvue'
|
||||
function definePageRoutes() {
|
||||
__uniRoutes.push({ path: "pages/index/index", component: GenPagesIndexIndexClass, meta: { isQuit: true } as UniPageMeta, style: _uM([["navigationBarTitleText","uni-app x"]]) } as UniPageRoute)
|
||||
}
|
||||
const __uniTabBar: Map<string, any | null> | null = null
|
||||
const __uniLaunchPage: Map<string, any | null> = _uM([["url","pages/index/index"],["style",_uM([["navigationBarTitleText","uni-app x"]])]])
|
||||
function defineAppConfig(){
|
||||
__uniConfig.entryPagePath = '/pages/index/index'
|
||||
__uniConfig.globalStyle = _uM([["navigationBarTextStyle","black"],["navigationBarTitleText","uni-app x"],["navigationBarBackgroundColor","#F8F8F8"],["backgroundColor","#F8F8F8"]])
|
||||
__uniConfig.getTabBarConfig = ():Map<string, any> | null => null
|
||||
__uniConfig.tabBar = __uniConfig.getTabBarConfig()
|
||||
__uniConfig.conditionUrl = ''
|
||||
__uniConfig.uniIdRouter = _uM()
|
||||
|
||||
__uniConfig.ready = true
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
require('vue');
|
||||
|
||||
uni;
|
||||
@@ -1,81 +0,0 @@
|
||||
|
||||
import {
|
||||
takePhoto,
|
||||
TakePhotoOption,
|
||||
TakePhotoSuccessCallbackResult,
|
||||
GeneralCallbackResult
|
||||
} from '@/uni_modules/Skin-cameraUTS'
|
||||
|
||||
|
||||
const __sfc__ = defineComponent({
|
||||
data() {
|
||||
return {
|
||||
yanse: "red",
|
||||
widthNum: 750,
|
||||
heightNum: 200,
|
||||
|
||||
flash: "on",
|
||||
device: "front",
|
||||
imagePath: "",
|
||||
videoPath: "https://qiniu-web-assets.dcloud.net.cn/unidoc/zh/2minute-demo.mp4"
|
||||
}
|
||||
},
|
||||
onLoad() {
|
||||
// let permissionNeed = ["android.permission.CAMERA", "android.permission.RECORD_AUDIO",
|
||||
// "android.permission.WRITE_EXTERNAL_STORAGE"]
|
||||
// // 请求权限
|
||||
// UTSAndroid.requestSystemPermission(UTSAndroid.getUniActivity()!, permissionNeed, function (allRight : boolean, _ : string[]) {
|
||||
// if (allRight) {
|
||||
// // 权限请求成功
|
||||
// } else {
|
||||
// //用户拒绝了部分权限
|
||||
// }
|
||||
// }, function (_ : boolean, _ : string[]) {
|
||||
// //用户拒绝了部分权限
|
||||
// })
|
||||
},
|
||||
methods: {
|
||||
onError(res : any) {
|
||||
console.log(res, " at pages/index/index.uvue:54")
|
||||
},
|
||||
_takePhoto() {
|
||||
takePhoto({
|
||||
success: (res : TakePhotoSuccessCallbackResult) => {
|
||||
console.log('takePhoto', res, " at pages/index/index.uvue:59")
|
||||
this.imagePath = res.tempImagePath;
|
||||
// _previewImage();
|
||||
},
|
||||
fail: (res : GeneralCallbackResult) => {
|
||||
console.log('takePhoto', res, " at pages/index/index.uvue:64")
|
||||
}
|
||||
} as TakePhotoOption)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
export default __sfc__
|
||||
function GenPagesIndexIndexRender(this: InstanceType<typeof __sfc__>): any | null {
|
||||
const _ctx = this
|
||||
const _cache = this.$.renderCache
|
||||
return _cE("scroll-view", _uM({ class: "page-scroll-view" }), [
|
||||
_cE("view", null, [
|
||||
_cE("button", _uM({ onClick: _ctx._takePhoto }), "拍照", 8 /* PROPS */, ["onClick"]),
|
||||
_cE(XfCameraComponent.name, _uM({
|
||||
style: _nS(_uM({"height":"750rpx","background-color":"red"})),
|
||||
onError: _ctx.onError,
|
||||
flash: _ctx.flash,
|
||||
position: _ctx.device
|
||||
}), null, 44 /* STYLE, PROPS, NEED_HYDRATION */, ["onError", "flash", "position"]),
|
||||
isTrue(_ctx.imagePath)
|
||||
? _cE("image", _uM({
|
||||
key: 0,
|
||||
mode: "aspectFit",
|
||||
src: _ctx.imagePath
|
||||
}), null, 8 /* PROPS */, ["src"])
|
||||
: _cC("v-if", true)
|
||||
])
|
||||
])
|
||||
}
|
||||
const GenPagesIndexIndexStyles = [_uM([["video", _pS(_uM([["width", "100%"], ["height", 200]]))]])]
|
||||
|
||||
import { XfCameraComponent } from '@/uni_modules/Skin-cameraUTS?uts-proxy'
|
||||
File diff suppressed because one or more lines are too long
-26
@@ -1,26 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
Vendored
-474
@@ -1,474 +0,0 @@
|
||||
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<string, Int>([
|
||||
['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<string, Int>([
|
||||
['back', CameraSelector.LENS_FACING_BACK],
|
||||
['front', CameraSelector.LENS_FACING_FRONT],
|
||||
])
|
||||
|
||||
|
||||
const RESOLUTION_MAP = new Map<string, Quality>([
|
||||
['low', Quality.LOWEST],
|
||||
['medium', Quality.HD],
|
||||
['high', Quality.HIGHEST],
|
||||
])
|
||||
|
||||
const QUALITY_MAP = new Map<string, Int>([
|
||||
["high", ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY],
|
||||
["low", ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY],
|
||||
])
|
||||
|
||||
const MIRROR_MODE = new Map<boolean, Int>([
|
||||
[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<Recorder> | null = null
|
||||
private recording : Recording | null = null
|
||||
private comp : UTSComponent<CameraView>;
|
||||
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<CameraView>, 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<string, any> = new Map();
|
||||
ret.set("errMsg", JSON.stringify(e))
|
||||
this.comp.$emit('error', ret)
|
||||
}
|
||||
}, ContextCompat.getMainExecutor(UTSAndroid.getUniActivity()!))
|
||||
// }).catch((err) => {
|
||||
// let ret : Map<string, any> = 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<string, any> = new Map();
|
||||
ret.set("maxZoom", maxZoom)
|
||||
this.comp.$emit('ready', ret)
|
||||
}
|
||||
|
||||
}
|
||||
unbindCamera() {
|
||||
this.cameraProvider?.unbindAll()
|
||||
this.cameraExecutor?.shutdown()
|
||||
}
|
||||
// checkSelfPermission() : Promise<boolean> {
|
||||
// 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
|
||||
// }
|
||||
Vendored
-252
@@ -1,252 +0,0 @@
|
||||
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<CameraView> {
|
||||
let cameraPreview = CameraManager.getInstance().getCameraView()
|
||||
if (cameraPreview != null) {
|
||||
return Promise.resolve<CameraView>(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<CameraView>((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(() => {
|
||||
__f__('log','at uni_modules/Skin-cameraUTS/utssdk/app-android/index.uts:145',"未找到相机")
|
||||
})
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
Vendored
-151
@@ -1,151 +0,0 @@
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
import { CameraView } from './camera';
|
||||
import { CameraConfig } from '../interface'
|
||||
import { CameraManager } from './CameraManager';
|
||||
|
||||
//原生提供以下属性或方法的实现
|
||||
export default {
|
||||
/**
|
||||
* 组件名称,也就是开发者使用的标签
|
||||
*/
|
||||
name: "xf-camera",
|
||||
/**
|
||||
* 组件涉及的事件声明,只有声明过的事件,才能被正常发送
|
||||
*/
|
||||
emits: ['stop', 'error', 'ready'],
|
||||
/**
|
||||
* 属性声明,组件的使用者会传递这些属性值到组件
|
||||
*/
|
||||
props: {
|
||||
"mode": {
|
||||
type: String,
|
||||
default: "normal"
|
||||
},
|
||||
"resolution": {
|
||||
type: String,
|
||||
default: "medium" // low | high
|
||||
},
|
||||
"position": {
|
||||
type: String,
|
||||
default: "back" // front前置 |back 后置
|
||||
},
|
||||
"flash": {
|
||||
type: String,
|
||||
default: "auto" // auto, on, off, torch
|
||||
},
|
||||
"frameSize": {
|
||||
type: String,
|
||||
default: "medium" // small|large
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 组件内部变量声明
|
||||
*/
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
/**
|
||||
* 属性变化监听器实现
|
||||
*/
|
||||
watch: {
|
||||
"position": {
|
||||
/**
|
||||
* 这里监听属性变化,并进行组件内部更新
|
||||
*/
|
||||
handler(newValue : string, _ : string) {
|
||||
this.$el?.switchCamera(newValue)
|
||||
},
|
||||
immediate: false // 创建时是否通过此方法更新属性,默认值为false
|
||||
},
|
||||
"flash": {
|
||||
/**
|
||||
* 这里监听属性变化,并进行组件内部更新
|
||||
*/
|
||||
handler(newValue : string, _ : string) {
|
||||
this.$el?.setFlash(newValue)
|
||||
},
|
||||
immediate: false // 创建时是否通过此方法更新属性,默认值为false
|
||||
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 规则:如果没有配置expose,则methods中的方法均对外暴露,如果配置了expose,则以expose的配置为准向外暴露
|
||||
* ['publicMethod'] 含义为:只有 `publicMethod` 在实例上可用
|
||||
*/
|
||||
expose: [],
|
||||
methods: {
|
||||
|
||||
},
|
||||
/**
|
||||
* [可选实现] 组件被创建,组件第一个生命周期,
|
||||
* 在内存中被占用的时候被调用,开发者可以在这里执行一些需要提前执行的初始化逻辑
|
||||
*/
|
||||
created() {
|
||||
|
||||
},
|
||||
/**
|
||||
* [可选实现] 对应平台的view载体即将被创建,对应前端beforeMount
|
||||
*/
|
||||
NVBeforeLoad() {
|
||||
|
||||
},
|
||||
/**
|
||||
* [必须实现] 创建原生View,必须定义返回值类型
|
||||
* 开发者需要重点实现这个函数,声明原生组件被创建出来的过程,以及最终生成的原生组件类型
|
||||
* (Android需要明确知道View类型,需特殊校验)
|
||||
*/
|
||||
NVLoad() : CameraView {
|
||||
let previewView = new CameraView(this.$androidContext!, this)
|
||||
previewView.setTag("9527");
|
||||
CameraManager.getInstance().setCameraView(previewView);
|
||||
return previewView
|
||||
|
||||
},
|
||||
/**
|
||||
* [可选实现] 原生View已创建
|
||||
*/
|
||||
NVLoaded() {
|
||||
|
||||
},
|
||||
/**
|
||||
* [可选实现] 原生View布局完成
|
||||
*/
|
||||
NVLayouted() {
|
||||
this.$el?.setConfig({
|
||||
resolution: this.resolution,
|
||||
frameSize: this.frameSize,
|
||||
flash: this.flash
|
||||
} as CameraConfig)
|
||||
this.$el?.switchCamera(this.position)
|
||||
},
|
||||
/**
|
||||
* [可选实现] 原生View将释放
|
||||
*/
|
||||
NVBeforeUnload() {
|
||||
let ret : Map<string, any> = new Map();
|
||||
this.$emit('stop', ret)
|
||||
|
||||
this.$el?.unbindCamera()
|
||||
},
|
||||
/**
|
||||
* [可选实现] 原生View已释放,这里可以做释放View之后的操作
|
||||
*/
|
||||
NVUnloaded() {
|
||||
CameraManager.getInstance().dispose();
|
||||
},
|
||||
/**
|
||||
* [可选实现] 组件销毁
|
||||
*/
|
||||
unmounted() {
|
||||
|
||||
},
|
||||
}
|
||||
/*
|
||||
<view>
|
||||
</view>
|
||||
*/
|
||||
Vendored
-26
@@ -1,26 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
Vendored
-469
@@ -1,469 +0,0 @@
|
||||
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<string, AVCaptureSession.Preset>([
|
||||
['low', AVCaptureSession.Preset.low],
|
||||
['medium', AVCaptureSession.Preset.medium],
|
||||
['high', AVCaptureSession.Preset.high],
|
||||
])
|
||||
|
||||
const FLASH_MODE_MAP = new Map<string, AVCaptureDevice.TorchMode>([
|
||||
['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<UIView>;
|
||||
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<UIView>, 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<string, any> = new Map();
|
||||
ret.set("maxZoom", device.maxAvailableVideoZoomFactor)
|
||||
let detail : Map<string, any> = new Map();
|
||||
detail.set("detail", ret)
|
||||
this.comp?.$emit("ready", detail);
|
||||
} catch (e) {
|
||||
let ret : Map<string, any> = new Map();
|
||||
ret.set("errMsg", JSON.stringify(e))
|
||||
let detail : Map<string, any> = 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) {
|
||||
__f__('log','at uni_modules/Skin-cameraUTS/utssdk/app-ios/camera.uts:345',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) {
|
||||
__f__('log','at uni_modules/Skin-cameraUTS/utssdk/app-ios/camera.uts:365',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)
|
||||
}
|
||||
}
|
||||
-207
@@ -1,207 +0,0 @@
|
||||
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)
|
||||
}
|
||||
-166
@@ -1,166 +0,0 @@
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
import { UIView } from "UIKit"
|
||||
import { CameraView } from "./camera";
|
||||
import { CameraConfig } from '../interface'
|
||||
import { CameraManager } from "./CameraManager";
|
||||
|
||||
//原生提供以下属性或方法的实现
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
};
|
||||
},
|
||||
/**
|
||||
* 组件名称,也就是开发者使用的标签
|
||||
*/
|
||||
name: "xf-camera",
|
||||
/**
|
||||
* 组件涉及的事件声明,只有声明过的事件,才能被正常发送
|
||||
*/
|
||||
emits: ['stop', 'error', 'ready'],
|
||||
/**
|
||||
* 属性声明,组件的使用者会传递这些属性值到组件
|
||||
*/
|
||||
props: {
|
||||
"mode": {
|
||||
type: String,
|
||||
default: "normal"
|
||||
},
|
||||
"resolution": {
|
||||
type: String,
|
||||
default: "medium" // low | high
|
||||
},
|
||||
"position": {
|
||||
type: String,
|
||||
default: "back" // front前置 |back 后置
|
||||
},
|
||||
"flash": {
|
||||
type: String,
|
||||
default: "auto" // auto, on, off, torch
|
||||
},
|
||||
"frameSize": {
|
||||
type: String,
|
||||
default: "medium" // small|large
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 属性变化监听器实现
|
||||
*/
|
||||
watch: {
|
||||
"position": {
|
||||
/**
|
||||
* 这里监听属性变化,并进行组件内部更新
|
||||
*/
|
||||
handler(newValue : String, _oldValue : String) {
|
||||
CameraManager.getInstance().getCameraView()?.switchCamera(newValue)
|
||||
},
|
||||
/**
|
||||
* 创建时是否通过此方法更新属性,默认值为false
|
||||
*/
|
||||
immediate: false
|
||||
},
|
||||
"flash": {
|
||||
/**
|
||||
* 这里监听属性变化,并进行组件内部更新
|
||||
*/
|
||||
handler(newValue : String, _oldValue : String) {
|
||||
CameraManager.getInstance().getCameraView()?.setFlash(newValue)
|
||||
},
|
||||
/**
|
||||
* 创建时是否通过此方法更新属性,默认值为false
|
||||
*/
|
||||
immediate: false
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 规则:如果没有配置expose,则methods中的方法均对外暴露,如果配置了expose,则以expose的配置为准向外暴露
|
||||
* ['publicMethod'] 含义为:只有 `publicMethod` 在实例上可用
|
||||
*/
|
||||
expose: [],
|
||||
methods: {
|
||||
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* 组件被创建,组件第一个生命周期,
|
||||
* 在内存中被占用的时候被调用,开发者可以在这里执行一些需要提前执行的初始化逻辑
|
||||
* [可选实现]
|
||||
*/
|
||||
created() {
|
||||
|
||||
},
|
||||
/**
|
||||
* 对应平台的view载体即将被创建,对应前端beforeMount
|
||||
* [可选实现]
|
||||
*/
|
||||
NVBeforeLoad() {
|
||||
|
||||
},
|
||||
/**
|
||||
* 创建原生View,必须定义返回值类型
|
||||
* 开发者需要重点实现这个函数,声明原生组件被创建出来的过程,以及最终生成的原生组件类型
|
||||
* [必须实现]
|
||||
*/
|
||||
NVLoad() : UIView {
|
||||
let previewView = new UIView()
|
||||
previewView.tag = 9527
|
||||
return previewView
|
||||
},
|
||||
|
||||
/**
|
||||
* 原生View已创建
|
||||
* [可选实现]
|
||||
*/
|
||||
NVLoaded() {
|
||||
|
||||
},
|
||||
/**
|
||||
* 原生View布局完成
|
||||
* [可选实现]
|
||||
*/
|
||||
NVLayouted() {
|
||||
let previewView = this.$el as UIView;
|
||||
let cameraView = new CameraView(previewView, this, {
|
||||
resolution: this.resolution,
|
||||
frameSize: this.frameSize,
|
||||
devicePosition: this.position,
|
||||
flash: this.flash
|
||||
} as CameraConfig)
|
||||
CameraManager.getInstance().setCameraView(cameraView);
|
||||
},
|
||||
/**
|
||||
* 原生View将释放
|
||||
* [可选实现]
|
||||
*/
|
||||
NVBeforeUnload() {
|
||||
let ret : Map<string, any> = new Map();
|
||||
let detail : Map<string, any> = new Map();
|
||||
detail.set("detail", ret)
|
||||
this.$emit("stop", detail);
|
||||
|
||||
CameraManager.getInstance().getCameraView()?.unbindCamera()
|
||||
},
|
||||
/**
|
||||
* 原生View已释放,这里可以做释放View之后的操作
|
||||
* [可选实现]
|
||||
*/
|
||||
NVUnloaded() {
|
||||
CameraManager.getInstance().dispose()
|
||||
},
|
||||
/**
|
||||
* 组件销毁
|
||||
* [可选实现]
|
||||
*/
|
||||
unmounted() {
|
||||
|
||||
}
|
||||
}
|
||||
/*
|
||||
<view>
|
||||
</view>
|
||||
*/
|
||||
-214
@@ -1,214 +0,0 @@
|
||||
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
|
||||
Vendored
-26
@@ -1,26 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
Vendored
-474
@@ -1,474 +0,0 @@
|
||||
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<string, Int>([
|
||||
['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<string, Int>([
|
||||
['back', CameraSelector.LENS_FACING_BACK],
|
||||
['front', CameraSelector.LENS_FACING_FRONT],
|
||||
])
|
||||
|
||||
|
||||
const RESOLUTION_MAP = new Map<string, Quality>([
|
||||
['low', Quality.LOWEST],
|
||||
['medium', Quality.HD],
|
||||
['high', Quality.HIGHEST],
|
||||
])
|
||||
|
||||
const QUALITY_MAP = new Map<string, Int>([
|
||||
["high", ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY],
|
||||
["low", ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY],
|
||||
])
|
||||
|
||||
const MIRROR_MODE = new Map<boolean, Int>([
|
||||
[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<Recorder> | null = null
|
||||
private recording : Recording | null = null
|
||||
private comp : UTSComponent<CameraView>;
|
||||
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<CameraView>, 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<string, any> = new Map();
|
||||
ret.set("errMsg", JSON.stringify(e))
|
||||
this.comp.$emit('error', ret)
|
||||
}
|
||||
}, ContextCompat.getMainExecutor(UTSAndroid.getUniActivity()!))
|
||||
// }).catch((err) => {
|
||||
// let ret : Map<string, any> = 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<string, any> = new Map();
|
||||
ret.set("maxZoom", maxZoom)
|
||||
this.comp.$emit('ready', ret)
|
||||
}
|
||||
|
||||
}
|
||||
unbindCamera() {
|
||||
this.cameraProvider?.unbindAll()
|
||||
this.cameraExecutor?.shutdown()
|
||||
}
|
||||
// checkSelfPermission() : Promise<boolean> {
|
||||
// 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
|
||||
// }
|
||||
-252
@@ -1,252 +0,0 @@
|
||||
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<CameraView> {
|
||||
let cameraPreview = CameraManager.getInstance().getCameraView()
|
||||
if (cameraPreview != null) {
|
||||
return Promise.resolve<CameraView>(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<CameraView>((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(() => {
|
||||
__f__('log','at uni_modules/Skin-cameraUTS/utssdk/app-android/index.uts:145',"未找到相机")
|
||||
})
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
-151
@@ -1,151 +0,0 @@
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
import { CameraView } from './camera';
|
||||
import { CameraConfig } from '../interface'
|
||||
import { CameraManager } from './CameraManager';
|
||||
|
||||
//原生提供以下属性或方法的实现
|
||||
export default {
|
||||
/**
|
||||
* 组件名称,也就是开发者使用的标签
|
||||
*/
|
||||
name: "xf-camera",
|
||||
/**
|
||||
* 组件涉及的事件声明,只有声明过的事件,才能被正常发送
|
||||
*/
|
||||
emits: ['stop', 'error', 'ready'],
|
||||
/**
|
||||
* 属性声明,组件的使用者会传递这些属性值到组件
|
||||
*/
|
||||
props: {
|
||||
"mode": {
|
||||
type: String,
|
||||
default: "normal"
|
||||
},
|
||||
"resolution": {
|
||||
type: String,
|
||||
default: "medium" // low | high
|
||||
},
|
||||
"position": {
|
||||
type: String,
|
||||
default: "back" // front前置 |back 后置
|
||||
},
|
||||
"flash": {
|
||||
type: String,
|
||||
default: "auto" // auto, on, off, torch
|
||||
},
|
||||
"frameSize": {
|
||||
type: String,
|
||||
default: "medium" // small|large
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 组件内部变量声明
|
||||
*/
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
/**
|
||||
* 属性变化监听器实现
|
||||
*/
|
||||
watch: {
|
||||
"position": {
|
||||
/**
|
||||
* 这里监听属性变化,并进行组件内部更新
|
||||
*/
|
||||
handler(newValue : string, _ : string) {
|
||||
this.$el?.switchCamera(newValue)
|
||||
},
|
||||
immediate: false // 创建时是否通过此方法更新属性,默认值为false
|
||||
},
|
||||
"flash": {
|
||||
/**
|
||||
* 这里监听属性变化,并进行组件内部更新
|
||||
*/
|
||||
handler(newValue : string, _ : string) {
|
||||
this.$el?.setFlash(newValue)
|
||||
},
|
||||
immediate: false // 创建时是否通过此方法更新属性,默认值为false
|
||||
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 规则:如果没有配置expose,则methods中的方法均对外暴露,如果配置了expose,则以expose的配置为准向外暴露
|
||||
* ['publicMethod'] 含义为:只有 `publicMethod` 在实例上可用
|
||||
*/
|
||||
expose: [],
|
||||
methods: {
|
||||
|
||||
},
|
||||
/**
|
||||
* [可选实现] 组件被创建,组件第一个生命周期,
|
||||
* 在内存中被占用的时候被调用,开发者可以在这里执行一些需要提前执行的初始化逻辑
|
||||
*/
|
||||
created() {
|
||||
|
||||
},
|
||||
/**
|
||||
* [可选实现] 对应平台的view载体即将被创建,对应前端beforeMount
|
||||
*/
|
||||
NVBeforeLoad() {
|
||||
|
||||
},
|
||||
/**
|
||||
* [必须实现] 创建原生View,必须定义返回值类型
|
||||
* 开发者需要重点实现这个函数,声明原生组件被创建出来的过程,以及最终生成的原生组件类型
|
||||
* (Android需要明确知道View类型,需特殊校验)
|
||||
*/
|
||||
NVLoad() : CameraView {
|
||||
let previewView = new CameraView(this.$androidContext!, this)
|
||||
previewView.setTag("9527");
|
||||
CameraManager.getInstance().setCameraView(previewView);
|
||||
return previewView
|
||||
|
||||
},
|
||||
/**
|
||||
* [可选实现] 原生View已创建
|
||||
*/
|
||||
NVLoaded() {
|
||||
|
||||
},
|
||||
/**
|
||||
* [可选实现] 原生View布局完成
|
||||
*/
|
||||
NVLayouted() {
|
||||
this.$el?.setConfig({
|
||||
resolution: this.resolution,
|
||||
frameSize: this.frameSize,
|
||||
flash: this.flash
|
||||
} as CameraConfig)
|
||||
this.$el?.switchCamera(this.position)
|
||||
},
|
||||
/**
|
||||
* [可选实现] 原生View将释放
|
||||
*/
|
||||
NVBeforeUnload() {
|
||||
let ret : Map<string, any> = new Map();
|
||||
this.$emit('stop', ret)
|
||||
|
||||
this.$el?.unbindCamera()
|
||||
},
|
||||
/**
|
||||
* [可选实现] 原生View已释放,这里可以做释放View之后的操作
|
||||
*/
|
||||
NVUnloaded() {
|
||||
CameraManager.getInstance().dispose();
|
||||
},
|
||||
/**
|
||||
* [可选实现] 组件销毁
|
||||
*/
|
||||
unmounted() {
|
||||
|
||||
},
|
||||
}
|
||||
/*
|
||||
<view>
|
||||
</view>
|
||||
*/
|
||||
Vendored
-26
@@ -1,26 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
-469
@@ -1,469 +0,0 @@
|
||||
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<string, AVCaptureSession.Preset>([
|
||||
['low', AVCaptureSession.Preset.low],
|
||||
['medium', AVCaptureSession.Preset.medium],
|
||||
['high', AVCaptureSession.Preset.high],
|
||||
])
|
||||
|
||||
const FLASH_MODE_MAP = new Map<string, AVCaptureDevice.TorchMode>([
|
||||
['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<UIView>;
|
||||
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<UIView>, 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<string, any> = new Map();
|
||||
ret.set("maxZoom", device.maxAvailableVideoZoomFactor)
|
||||
let detail : Map<string, any> = new Map();
|
||||
detail.set("detail", ret)
|
||||
this.comp?.$emit("ready", detail);
|
||||
} catch (e) {
|
||||
let ret : Map<string, any> = new Map();
|
||||
ret.set("errMsg", JSON.stringify(e))
|
||||
let detail : Map<string, any> = 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) {
|
||||
__f__('log','at uni_modules/Skin-cameraUTS/utssdk/app-ios/camera.uts:345',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) {
|
||||
__f__('log','at uni_modules/Skin-cameraUTS/utssdk/app-ios/camera.uts:365',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)
|
||||
}
|
||||
}
|
||||
-207
@@ -1,207 +0,0 @@
|
||||
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)
|
||||
}
|
||||
-166
@@ -1,166 +0,0 @@
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
import { UIView } from "UIKit"
|
||||
import { CameraView } from "./camera";
|
||||
import { CameraConfig } from '../interface'
|
||||
import { CameraManager } from "./CameraManager";
|
||||
|
||||
//原生提供以下属性或方法的实现
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
};
|
||||
},
|
||||
/**
|
||||
* 组件名称,也就是开发者使用的标签
|
||||
*/
|
||||
name: "xf-camera",
|
||||
/**
|
||||
* 组件涉及的事件声明,只有声明过的事件,才能被正常发送
|
||||
*/
|
||||
emits: ['stop', 'error', 'ready'],
|
||||
/**
|
||||
* 属性声明,组件的使用者会传递这些属性值到组件
|
||||
*/
|
||||
props: {
|
||||
"mode": {
|
||||
type: String,
|
||||
default: "normal"
|
||||
},
|
||||
"resolution": {
|
||||
type: String,
|
||||
default: "medium" // low | high
|
||||
},
|
||||
"position": {
|
||||
type: String,
|
||||
default: "back" // front前置 |back 后置
|
||||
},
|
||||
"flash": {
|
||||
type: String,
|
||||
default: "auto" // auto, on, off, torch
|
||||
},
|
||||
"frameSize": {
|
||||
type: String,
|
||||
default: "medium" // small|large
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 属性变化监听器实现
|
||||
*/
|
||||
watch: {
|
||||
"position": {
|
||||
/**
|
||||
* 这里监听属性变化,并进行组件内部更新
|
||||
*/
|
||||
handler(newValue : String, _oldValue : String) {
|
||||
CameraManager.getInstance().getCameraView()?.switchCamera(newValue)
|
||||
},
|
||||
/**
|
||||
* 创建时是否通过此方法更新属性,默认值为false
|
||||
*/
|
||||
immediate: false
|
||||
},
|
||||
"flash": {
|
||||
/**
|
||||
* 这里监听属性变化,并进行组件内部更新
|
||||
*/
|
||||
handler(newValue : String, _oldValue : String) {
|
||||
CameraManager.getInstance().getCameraView()?.setFlash(newValue)
|
||||
},
|
||||
/**
|
||||
* 创建时是否通过此方法更新属性,默认值为false
|
||||
*/
|
||||
immediate: false
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 规则:如果没有配置expose,则methods中的方法均对外暴露,如果配置了expose,则以expose的配置为准向外暴露
|
||||
* ['publicMethod'] 含义为:只有 `publicMethod` 在实例上可用
|
||||
*/
|
||||
expose: [],
|
||||
methods: {
|
||||
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* 组件被创建,组件第一个生命周期,
|
||||
* 在内存中被占用的时候被调用,开发者可以在这里执行一些需要提前执行的初始化逻辑
|
||||
* [可选实现]
|
||||
*/
|
||||
created() {
|
||||
|
||||
},
|
||||
/**
|
||||
* 对应平台的view载体即将被创建,对应前端beforeMount
|
||||
* [可选实现]
|
||||
*/
|
||||
NVBeforeLoad() {
|
||||
|
||||
},
|
||||
/**
|
||||
* 创建原生View,必须定义返回值类型
|
||||
* 开发者需要重点实现这个函数,声明原生组件被创建出来的过程,以及最终生成的原生组件类型
|
||||
* [必须实现]
|
||||
*/
|
||||
NVLoad() : UIView {
|
||||
let previewView = new UIView()
|
||||
previewView.tag = 9527
|
||||
return previewView
|
||||
},
|
||||
|
||||
/**
|
||||
* 原生View已创建
|
||||
* [可选实现]
|
||||
*/
|
||||
NVLoaded() {
|
||||
|
||||
},
|
||||
/**
|
||||
* 原生View布局完成
|
||||
* [可选实现]
|
||||
*/
|
||||
NVLayouted() {
|
||||
let previewView = this.$el as UIView;
|
||||
let cameraView = new CameraView(previewView, this, {
|
||||
resolution: this.resolution,
|
||||
frameSize: this.frameSize,
|
||||
devicePosition: this.position,
|
||||
flash: this.flash
|
||||
} as CameraConfig)
|
||||
CameraManager.getInstance().setCameraView(cameraView);
|
||||
},
|
||||
/**
|
||||
* 原生View将释放
|
||||
* [可选实现]
|
||||
*/
|
||||
NVBeforeUnload() {
|
||||
let ret : Map<string, any> = new Map();
|
||||
let detail : Map<string, any> = new Map();
|
||||
detail.set("detail", ret)
|
||||
this.$emit("stop", detail);
|
||||
|
||||
CameraManager.getInstance().getCameraView()?.unbindCamera()
|
||||
},
|
||||
/**
|
||||
* 原生View已释放,这里可以做释放View之后的操作
|
||||
* [可选实现]
|
||||
*/
|
||||
NVUnloaded() {
|
||||
CameraManager.getInstance().dispose()
|
||||
},
|
||||
/**
|
||||
* 组件销毁
|
||||
* [可选实现]
|
||||
*/
|
||||
unmounted() {
|
||||
|
||||
}
|
||||
}
|
||||
/*
|
||||
<view>
|
||||
</view>
|
||||
*/
|
||||
-214
@@ -1,214 +0,0 @@
|
||||
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
|
||||
-35
@@ -1,35 +0,0 @@
|
||||
let firstBackTime = 0;
|
||||
const __sfc__ = defineApp({
|
||||
onLaunch() {
|
||||
console.log('App Launch', " at App.uvue:7");
|
||||
},
|
||||
onShow() {
|
||||
console.log('App Show', " at App.uvue:10");
|
||||
},
|
||||
onHide() {
|
||||
console.log('App Hide', " at App.uvue:13");
|
||||
},
|
||||
onLastPageBackPress() {
|
||||
console.log('App LastPageBackPress', " at App.uvue:17");
|
||||
if (firstBackTime == 0) {
|
||||
uni.showToast({
|
||||
title: '再按一次退出应用',
|
||||
position: 'bottom',
|
||||
});
|
||||
firstBackTime = Date.now();
|
||||
setTimeout(() => {
|
||||
firstBackTime = 0;
|
||||
}, 2000);
|
||||
}
|
||||
else if (Date.now() - firstBackTime < 2000) {
|
||||
firstBackTime = Date.now();
|
||||
uni.exit();
|
||||
}
|
||||
},
|
||||
onExit() {
|
||||
console.log('App Exit', " at App.uvue:34");
|
||||
},
|
||||
});
|
||||
export default __sfc__;
|
||||
const GenAppStyles = [_uM([["uni-row", _pS(_uM([["flexDirection", "row"]]))], ["uni-column", _pS(_uM([["flexDirection", "column"]]))]])];
|
||||
//# sourceMappingURL=App.uvue.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"sources":["App.uvue"],"names":[],"mappings":"AAEC,IAAI,aAAY,GAAI,CAAA,CAAA;AAEpB,MAAK,OAAQ,GAAE,SAAA,CAAA;IACd,QAAQ;QACP,OAAO,CAAC,GAAG,CAAC,YAAY,EAAA,gBAAA,CAAA,CAAA;IACzB,CAAC;IACD,MAAM;QACL,OAAO,CAAC,GAAG,CAAC,UAAU,EAAA,iBAAA,CAAA,CAAA;IACvB,CAAC;IACD,MAAM;QACL,OAAO,CAAC,GAAG,CAAC,UAAU,EAAA,iBAAA,CAAA,CAAA;IACvB,CAAC;IAED,mBAAmB;QAClB,OAAO,CAAC,GAAG,CAAC,uBAAuB,EAAA,iBAAA,CAAA,CAAA;QACnC,IAAI,aAAY,IAAK,CAAC,EAAE;YACvB,GAAG,CAAC,SAAS,CAAC;gBACb,KAAK,EAAE,UAAU;gBACjB,QAAQ,EAAE,QAAQ;aAClB,CAAA,CAAA;YACD,aAAY,GAAI,IAAI,CAAC,GAAG,EAAC,CAAA;YACzB,UAAU,CAAC,GAAG,EAAC;gBACd,aAAY,GAAI,CAAA,CAAA;YACjB,CAAC,EAAE,IAAI,CAAA,CAAA;SACR;aAAO,IAAI,IAAI,CAAC,GAAG,EAAC,GAAI,aAAY,GAAI,IAAI,EAAE;YAC7C,aAAY,GAAI,IAAI,CAAC,GAAG,EAAC,CAAA;YACzB,GAAG,CAAC,IAAI,EAAC,CAAA;SACV;IACD,CAAC;IAED,MAAM;QACL,OAAO,CAAC,GAAG,CAAC,UAAU,EAAA,iBAAA,CAAA,CAAA;IACvB,CAAC;CACF,CAAA,CAAA","file":"App.uvue","sourceRoot":"","sourcesContent":["<script lang=\"uts\">\r\n\r\n\tlet firstBackTime = 0\r\n\r\n\texport default {\r\n\t\tonLaunch() {\r\n\t\t\tconsole.log('App Launch')\r\n\t\t},\r\n\t\tonShow() {\r\n\t\t\tconsole.log('App Show')\r\n\t\t},\r\n\t\tonHide() {\r\n\t\t\tconsole.log('App Hide')\r\n\t\t},\r\n\r\n\t\tonLastPageBackPress() {\r\n\t\t\tconsole.log('App LastPageBackPress')\r\n\t\t\tif (firstBackTime == 0) {\r\n\t\t\t\tuni.showToast({\r\n\t\t\t\t\ttitle: '再按一次退出应用',\r\n\t\t\t\t\tposition: 'bottom',\r\n\t\t\t\t})\r\n\t\t\t\tfirstBackTime = Date.now()\r\n\t\t\t\tsetTimeout(() => {\r\n\t\t\t\t\tfirstBackTime = 0\r\n\t\t\t\t}, 2000)\r\n\t\t\t} else if (Date.now() - firstBackTime < 2000) {\r\n\t\t\t\tfirstBackTime = Date.now()\r\n\t\t\t\tuni.exit()\r\n\t\t\t}\r\n\t\t},\r\n\r\n\t\tonExit() {\r\n\t\t\tconsole.log('App Exit')\r\n\t\t},\r\n\t}\r\n</script>\r\n\r\n<style>\r\n\t/*每个页面公共css */\r\n\t.uni-row {\r\n\t\tflex-direction: row;\r\n\t}\r\n\r\n\t.uni-column {\r\n\t\tflex-direction: column;\r\n\t}\r\n</style>"]}
|
||||
-37
@@ -1,37 +0,0 @@
|
||||
import App from './App.uvue';
|
||||
import { createSSRApp } from 'vue';
|
||||
export function createApp(): UTSJSONObject {
|
||||
const app = createSSRApp(App);
|
||||
return {
|
||||
app
|
||||
};
|
||||
}
|
||||
export function main(app: IApp) {
|
||||
definePageRoutes();
|
||||
defineAppConfig();
|
||||
(createApp()['app'] as VueApp).mount(app, GenUniApp());
|
||||
}
|
||||
export class UniAppConfig extends io.dcloud.uniapp.appframe.AppConfig {
|
||||
override name: string = "SkinCamera";
|
||||
override appid: string = "__UNI__644401D";
|
||||
override versionName: string = "1.0.0";
|
||||
override versionCode: string = "100";
|
||||
override uniCompilerVersion: string = "4.87";
|
||||
constructor() { super(); }
|
||||
}
|
||||
import GenPagesIndexIndexClass from './pages/index/index.uvue';
|
||||
function definePageRoutes() {
|
||||
__uniRoutes.push({ path: "pages/index/index", component: GenPagesIndexIndexClass, meta: { isQuit: true } as UniPageMeta, style: _uM([["navigationBarTitleText", "uni-app x"]]) } as UniPageRoute);
|
||||
}
|
||||
const __uniTabBar: Map<string, any | null> | null = null;
|
||||
const __uniLaunchPage: Map<string, any | null> = _uM([["url", "pages/index/index"], ["style", _uM([["navigationBarTitleText", "uni-app x"]])]]);
|
||||
function defineAppConfig() {
|
||||
__uniConfig.entryPagePath = '/pages/index/index';
|
||||
__uniConfig.globalStyle = _uM([["navigationBarTextStyle", "black"], ["navigationBarTitleText", "uni-app x"], ["navigationBarBackgroundColor", "#F8F8F8"], ["backgroundColor", "#F8F8F8"]]);
|
||||
__uniConfig.getTabBarConfig = (): Map<string, any> | null => null;
|
||||
__uniConfig.tabBar = __uniConfig.getTabBarConfig();
|
||||
__uniConfig.conditionUrl = '';
|
||||
__uniConfig.uniIdRouter = _uM();
|
||||
__uniConfig.ready = true;
|
||||
}
|
||||
//# sourceMappingURL=main.uts.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"main.uts","sourceRoot":"","sources":["main.uts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,YAAY,CAAA;AAE5B,OAAO,EAAE,YAAY,EAAE,MAAM,KAAK,CAAA;AAClC,MAAM,UAAU,SAAS;IACxB,MAAM,GAAG,GAAG,YAAY,CAAC,GAAG,CAAC,CAAA;IAC7B,OAAO;QACN,GAAG;KACH,CAAA;AACF,CAAC;AACD,MAAM,UAAU,IAAI,CAAC,GAAG,EAAE,IAAI;IAC1B,gBAAgB,EAAE,CAAC;IACnB,eAAe,EAAE,CAAC;IAClB,CAAC,SAAS,EAAE,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,SAAS,EAAE,CAAC,CAAC;AAC3D,CAAC;AAED,MAAM,OAAO,YAAa,SAAQ,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS;IACjE,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,CAAA;IACpC,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,gBAAgB,CAAA;IACzC,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAA;IACtC,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,KAAK,CAAA;IACpC,QAAQ,CAAC,kBAAkB,EAAE,MAAM,GAAG,MAAM,CAAA;IAE5C,gBAAgB,KAAK,EAAE,CAAA,CAAC,CAAC;CAC5B;AAED,OAAO,uBAAuB,MAAM,0BAA0B,CAAA;AAC9D,SAAS,gBAAgB;IACzB,WAAW,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,mBAAmB,EAAE,SAAS,EAAE,uBAAuB,EAAE,IAAI,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,WAAW,EAAE,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC,wBAAwB,EAAC,WAAW,CAAC,CAAC,CAAC,EAAE,IAAI,YAAY,CAAC,CAAA;AAChM,CAAC;AACD,MAAM,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,GAAG,IAAI,CAAA;AACxD,MAAM,eAAe,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,GAAG,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,KAAK,EAAC,mBAAmB,CAAC,EAAC,CAAC,OAAO,EAAC,GAAG,CAAC,CAAC,CAAC,wBAAwB,EAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;AAC3I,SAAS,eAAe;IACtB,WAAW,CAAC,aAAa,GAAG,oBAAoB,CAAA;IAChD,WAAW,CAAC,WAAW,GAAG,GAAG,CAAC,CAAC,CAAC,wBAAwB,EAAC,OAAO,CAAC,EAAC,CAAC,wBAAwB,EAAC,WAAW,CAAC,EAAC,CAAC,8BAA8B,EAAC,SAAS,CAAC,EAAC,CAAC,iBAAiB,EAAC,SAAS,CAAC,CAAC,CAAC,CAAA;IACnL,WAAW,CAAC,eAAe,GAAG,IAAG,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,CAAC,EAAE,CAAE,IAAI,CAAA;IACjE,WAAW,CAAC,MAAM,GAAG,WAAW,CAAC,eAAe,EAAE,CAAA;IAClD,WAAW,CAAC,YAAY,GAAG,EAAE,CAAA;IAC7B,WAAW,CAAC,WAAW,GAAG,GAAG,EAAE,CAAA;IAE/B,WAAW,CAAC,KAAK,GAAG,IAAI,CAAA;AAC1B,CAAC","sourcesContent":["import App from './App.uvue'\r\n\r\nimport { createSSRApp } from 'vue'\r\nexport function createApp() {\r\n\tconst app = createSSRApp(App)\r\n\treturn {\r\n\t\tapp\r\n\t}\r\n}\nexport function main(app: IApp) {\n definePageRoutes();\n defineAppConfig();\n (createApp()['app'] as VueApp).mount(app, GenUniApp());\n}\n\nexport class UniAppConfig extends io.dcloud.uniapp.appframe.AppConfig {\n override name: string = \"SkinCamera\"\n override appid: string = \"__UNI__644401D\"\n override versionName: string = \"1.0.0\"\n override versionCode: string = \"100\"\n override uniCompilerVersion: string = \"4.87\"\n \n constructor() { super() }\n}\n\nimport GenPagesIndexIndexClass from './pages/index/index.uvue'\nfunction definePageRoutes() {\n__uniRoutes.push({ path: \"pages/index/index\", component: GenPagesIndexIndexClass, meta: { isQuit: true } as UniPageMeta, style: _uM([[\"navigationBarTitleText\",\"uni-app x\"]]) } as UniPageRoute)\n}\nconst __uniTabBar: Map<string, any | null> | null = null\nconst __uniLaunchPage: Map<string, any | null> = _uM([[\"url\",\"pages/index/index\"],[\"style\",_uM([[\"navigationBarTitleText\",\"uni-app x\"]])]])\nfunction defineAppConfig(){\n __uniConfig.entryPagePath = '/pages/index/index'\n __uniConfig.globalStyle = _uM([[\"navigationBarTextStyle\",\"black\"],[\"navigationBarTitleText\",\"uni-app x\"],[\"navigationBarBackgroundColor\",\"#F8F8F8\"],[\"backgroundColor\",\"#F8F8F8\"]])\n __uniConfig.getTabBarConfig = ():Map<string, any> | null => null\n __uniConfig.tabBar = __uniConfig.getTabBarConfig()\n __uniConfig.conditionUrl = ''\n __uniConfig.uniIdRouter = _uM()\n \n __uniConfig.ready = true\n}\n"]}
|
||||
@@ -1,71 +0,0 @@
|
||||
import { takePhoto, TakePhotoOption, TakePhotoSuccessCallbackResult, GeneralCallbackResult } from '@/uni_modules/Skin-cameraUTS';
|
||||
const __sfc__ = defineComponent({
|
||||
data() {
|
||||
return {
|
||||
yanse: "red",
|
||||
widthNum: 750,
|
||||
heightNum: 200,
|
||||
flash: "on",
|
||||
device: "front",
|
||||
imagePath: "",
|
||||
videoPath: "https://qiniu-web-assets.dcloud.net.cn/unidoc/zh/2minute-demo.mp4"
|
||||
};
|
||||
},
|
||||
onLoad() {
|
||||
// let permissionNeed = ["android.permission.CAMERA", "android.permission.RECORD_AUDIO",
|
||||
// "android.permission.WRITE_EXTERNAL_STORAGE"]
|
||||
// // 请求权限
|
||||
// UTSAndroid.requestSystemPermission(UTSAndroid.getUniActivity()!, permissionNeed, function (allRight : boolean, _ : string[]) {
|
||||
// if (allRight) {
|
||||
// // 权限请求成功
|
||||
// } else {
|
||||
// //用户拒绝了部分权限
|
||||
// }
|
||||
// }, function (_ : boolean, _ : string[]) {
|
||||
// //用户拒绝了部分权限
|
||||
// })
|
||||
},
|
||||
methods: {
|
||||
onError(res: any) {
|
||||
console.log(res, " at pages/index/index.uvue:54");
|
||||
},
|
||||
_takePhoto() {
|
||||
takePhoto({
|
||||
success: (res: TakePhotoSuccessCallbackResult) => {
|
||||
console.log('takePhoto', res, " at pages/index/index.uvue:59");
|
||||
this.imagePath = res.tempImagePath;
|
||||
// _previewImage();
|
||||
},
|
||||
fail: (res: GeneralCallbackResult) => {
|
||||
console.log('takePhoto', res, " at pages/index/index.uvue:64");
|
||||
}
|
||||
} as TakePhotoOption);
|
||||
}
|
||||
}
|
||||
});
|
||||
export default __sfc__;
|
||||
function GenPagesIndexIndexRender(this: InstanceType<typeof __sfc__>): any | null {
|
||||
const _ctx = this;
|
||||
const _cache = this.$.renderCache;
|
||||
return _cE("scroll-view", _uM({ class: "page-scroll-view" }), [
|
||||
_cE("view", null, [
|
||||
_cE("button", _uM({ onClick: _ctx._takePhoto }), "拍照", 8 /* PROPS */, ["onClick"]),
|
||||
_cE(XfCameraComponent.name, _uM({
|
||||
style: _nS(_uM({ "height": "750rpx", "background-color": "red" })),
|
||||
onError: _ctx.onError,
|
||||
flash: _ctx.flash,
|
||||
position: _ctx.device
|
||||
}), null, 44 /* STYLE, PROPS, NEED_HYDRATION */, ["onError", "flash", "position"]),
|
||||
isTrue(_ctx.imagePath)
|
||||
? _cE("image", _uM({
|
||||
key: 0,
|
||||
mode: "aspectFit",
|
||||
src: _ctx.imagePath
|
||||
}), null, 8 /* PROPS */, ["src"])
|
||||
: _cC("v-if", true)
|
||||
])
|
||||
]);
|
||||
}
|
||||
const GenPagesIndexIndexStyles = [_uM([["video", _pS(_uM([["width", "100%"], ["height", 200]]))]])];
|
||||
import { XfCameraComponent } from '@/uni_modules/Skin-cameraUTS?uts-proxy';
|
||||
//# sourceMappingURL=index.uvue.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"sources":["pages/index/index.uvue"],"names":[],"mappings":"AAgBC,OAAO,EACN,SAAS,EACT,eAAe,EACf,8BAA8B,EAC9B,qBAAoB,EACrB,MAAO,8BAA6B,CAAA;AAGpC,MAAK,OAAQ,GAAE,eAAA,CAAA;IACd,IAAI;QACH,OAAO;YACN,KAAK,EAAE,KAAK;YACZ,QAAQ,EAAE,GAAG;YACb,SAAS,EAAE,GAAG;YAEd,KAAK,EAAE,IAAI;YACX,MAAM,EAAE,OAAO;YACf,SAAS,EAAE,EAAE;YACb,SAAS,EAAE,mEAAkE;SAC9E,CAAA;IACD,CAAC;IACD,MAAM;QACL,wFAAuF;QACvF,oDAAmD;QACnD,UAAS;QACT,iIAAgI;QAChI,mBAAkB;QAClB,cAAa;QACb,YAAW;QACX,gBAAe;QACf,KAAI;QACJ,4CAA2C;QAC3C,eAAc;QACd,KAAI;IACL,CAAC;IACD,OAAO,EAAE;QACR,OAAO,CAAC,GAAE,EAAI,GAAG;YAChB,OAAO,CAAC,GAAG,CAAC,GAAG,EAAA,+BAAA,CAAA,CAAA;QAChB,CAAC;QACD,UAAU;YACT,SAAS,CAAC;gBACT,OAAO,EAAE,CAAC,GAAE,EAAI,8BAA8B,EAAE,EAAC;oBAChD,OAAO,CAAC,GAAG,CAAC,WAAW,EAAE,GAAG,EAAA,+BAAA,CAAA,CAAA;oBAC5B,IAAI,CAAC,SAAQ,GAAI,GAAG,CAAC,aAAa,CAAA;oBAClC,mBAAkB;gBACnB,CAAC;gBACD,IAAI,EAAE,CAAC,GAAE,EAAI,qBAAqB,EAAE,EAAC;oBACpC,OAAO,CAAC,GAAG,CAAC,WAAW,EAAE,GAAG,EAAA,+BAAA,CAAA,CAAA;gBAC7B,CAAA;aACD,IAAK,eAAe,CAAA,CAAA;QACrB,CAAA;KACD;CACD,CAAA,CAAA;;;;;WAlEA,GAAA,CASc,aAAA,EAAA,GAAA,CAAA,EATD,KAAK,EAAC,kBAAkB,EAAA,CAAA,EAAA;QAEpC,GAAA,CAKO,MAAA,EAAA,IAAA,EAAA;YAJN,GAAA,CAAuC,QAAA,EAAA,GAAA,CAAA,EAA9B,OAAK,EAAE,IAAA,CAAA,UAAU,EAAA,CAAA,EAAE,IAAE,EAAA,CAAA,CAAA,WAAA,EAAA,CAAA,SAAA,CAAA,CAAA;YAC9B,GAAA,CAAA,iBAAA,CAAA,IACgC,EAAA,GAAA,CAAA;gBADrB,KAA6C,EAAA,GAAA,CAA7C,GAAA,CAAA,EAAA,QAAA,EAAA,QAAA,EAAA,kBAAA,EAAA,KAAA,EAAA,CAA6C,CAAA;gBAAE,OAAK,EAAE,IAAA,CAAA,OAAO;gBAAG,KAAK,EAAE,IAAA,CAAA,KAAK;gBACrF,QAAQ,EAAE,IAAA,CAAA,MAAM;;mBACL,IAAA,CAAA,SAAS,CAAA;kBAAtB,GAAA,CAAkE,OAAA,EAAA,GAAA,CAAA;;oBAA1C,IAAI,EAAC,WAAW;oBAAE,GAAG,EAAE,IAAA,CAAA,SAAS","file":"pages/index/index.uvue","sourcesContent":["<template>\n\n\t<scroll-view class=\"page-scroll-view\">\n\n\t\t<view>\n\t\t\t<button @click=\"_takePhoto\">拍照</button>\n\t\t\t<xf-camera style=\"height:750rpx; background-color: red;\" @error=\"onError\" :flash=\"flash\"\n\t\t\t\t:position=\"device\"></xf-camera>\n\t\t\t<image v-if=\"imagePath\" mode=\"aspectFit\" :src=\"imagePath\"></image>\n\t\t</view>\n\n\t</scroll-view>\n\n</template>\n\n<script>\n\timport {\n\t\ttakePhoto,\n\t\tTakePhotoOption,\n\t\tTakePhotoSuccessCallbackResult,\n\t\tGeneralCallbackResult\n\t} from '@/uni_modules/Skin-cameraUTS'\n\t\n\n\texport default {\n\t\tdata() {\n\t\t\treturn {\n\t\t\t\tyanse: \"red\",\n\t\t\t\twidthNum: 750,\n\t\t\t\theightNum: 200,\n\t\t\t\t\n\t\t\t\tflash: \"on\",\n\t\t\t\tdevice: \"front\",\n\t\t\t\timagePath: \"\",\n\t\t\t\tvideoPath: \"https://qiniu-web-assets.dcloud.net.cn/unidoc/zh/2minute-demo.mp4\"\n\t\t\t}\n\t\t},\n\t\tonLoad() {\n\t\t\t// let permissionNeed = [\"android.permission.CAMERA\", \"android.permission.RECORD_AUDIO\",\n\t\t\t// \t\t\t\t\t\"android.permission.WRITE_EXTERNAL_STORAGE\"]\n\t\t\t// // 请求权限\n\t\t\t// UTSAndroid.requestSystemPermission(UTSAndroid.getUniActivity()!, permissionNeed, function (allRight : boolean, _ : string[]) {\n\t\t\t// \tif (allRight) {\n\t\t\t// \t\t// 权限请求成功\n\t\t\t// \t} else {\n\t\t\t// \t\t//用户拒绝了部分权限\n\t\t\t// \t}\n\t\t\t// }, function (_ : boolean, _ : string[]) {\n\t\t\t// \t//用户拒绝了部分权限\n\t\t\t// })\n\t\t},\n\t\tmethods: {\n\t\t\tonError(res : any) {\n\t\t\t\tconsole.log(res)\n\t\t\t},\n\t\t\t_takePhoto() {\n\t\t\t\ttakePhoto({\n\t\t\t\t\tsuccess: (res : TakePhotoSuccessCallbackResult) => {\n\t\t\t\t\t\tconsole.log('takePhoto', res)\n\t\t\t\t\t\tthis.imagePath = res.tempImagePath;\n\t\t\t\t\t\t// _previewImage();\n\t\t\t\t\t},\n\t\t\t\t\tfail: (res : GeneralCallbackResult) => {\n\t\t\t\t\t\tconsole.log('takePhoto', res)\n\t\t\t\t\t}\n\t\t\t\t} as TakePhotoOption)\n\t\t\t}\n\t\t}\n\t}\n</script>\n\n<style>\n\t.video {\n\t\twidth: 100%;\n\t\theight: 200px;\n\t}\n</style>"]}
|
||||
unpackage/dist/dev/.uvue/app-android/uni_modules/Skin-cameraUTS/utssdk/app-android/CameraManager.uts
Vendored
-21
@@ -1,21 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=CameraManager.uts.map
|
||||
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"CameraManager.uts","sourceRoot":"","sources":["uni_modules/Skin-cameraUTS/utssdk/app-android/CameraManager.uts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAEtC,MAAM,OAAO,aAAa;IACzB,OAAO,CAAC,UAAU,EAAG,UAAU,GAAG,IAAI,GAAG,IAAI,CAAC;IAE9C,OAAO,CAAC,MAAM,CAAC,QAAQ,EAAG,aAAa,GAAG,IAAI,GAAG,IAAI,CAAC;IAEtD,MAAM,CAAC,MAAM,CAAC,WAAW,IAAK,aAAa;QAC1C,IAAI,aAAa,CAAC,QAAQ,IAAI,IAAI,EAAE;YACnC,aAAa,CAAC,QAAQ,GAAG,IAAI,aAAa,EAAE,CAAC;SAC7C;QACD,OAAO,aAAa,CAAC,QAAQ,CAAC,CAAC;IAChC,CAAC;IAED,MAAM,CAAC,aAAa,CAAC,UAAW,CAAC,EAAE,UAAU,GAAI,IAAI;QACpD,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC,CAAA;IAC9B,CAAC;IAED,MAAM,CAAC,aAAa,IAAK,UAAU,GAAG,IAAI;QACzC,OAAO,IAAI,CAAC,UAAU,CAAC;IACxB,CAAC;IAED,MAAM,CAAC,OAAO,IAAK,IAAI;QACtB,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;IACxB,CAAC;CACD","sourcesContent":["import { CameraView } from \"./camera\";\r\n\r\nexport class CameraManager {\r\n\tprivate cameraView : CameraView | null = null;\r\n\r\n\tprivate static instance : CameraManager | null = null;\r\n\r\n\tpublic static getInstance() : CameraManager {\r\n\t\tif (CameraManager.instance == null) {\r\n\t\t\tCameraManager.instance = new CameraManager();\r\n\t\t}\r\n\t\treturn CameraManager.instance!;\r\n\t}\r\n\r\n\tpublic setCameraView(cameraView ?: CameraView) : void {\r\n\t\tthis.cameraView = cameraView!\r\n\t}\r\n\r\n\tpublic getCameraView() : CameraView | null {\r\n\t\treturn this.cameraView;\r\n\t}\r\n\r\n\tpublic dispose() : void {\r\n\t\tthis.cameraView = null;\r\n\t}\r\n}"]}
|
||||
Vendored
-415
@@ -1,415 +0,0 @@
|
||||
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<string, Int>([
|
||||
['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<string, Int>([
|
||||
['back', CameraSelector.LENS_FACING_BACK],
|
||||
['front', CameraSelector.LENS_FACING_FRONT],
|
||||
]);
|
||||
const RESOLUTION_MAP = new Map<string, Quality>([
|
||||
['low', Quality.LOWEST],
|
||||
['medium', Quality.HD],
|
||||
['high', Quality.HIGHEST],
|
||||
]);
|
||||
const QUALITY_MAP = new Map<string, Int>([
|
||||
["high", ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY],
|
||||
["low", ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY],
|
||||
]);
|
||||
const MIRROR_MODE = new Map<boolean, Int>([
|
||||
[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<Recorder> | null = null;
|
||||
private recording: Recording | null = null;
|
||||
private comp: UTSComponent<CameraView>;
|
||||
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<CameraView>, 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: any) {
|
||||
let ret: Map<string, any> = new Map();
|
||||
ret.set("errMsg", JSON.stringify(e));
|
||||
this.comp.$emit('error', ret);
|
||||
}
|
||||
}, ContextCompat.getMainExecutor(UTSAndroid.getUniActivity()!));
|
||||
// }).catch((err) => {
|
||||
// let ret : Map<string, any> = 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<string, any> = new Map();
|
||||
ret.set("maxZoom", maxZoom);
|
||||
this.comp.$emit('ready', ret);
|
||||
}
|
||||
}
|
||||
unbindCamera() {
|
||||
this.cameraProvider?.unbindAll();
|
||||
this.cameraExecutor?.shutdown();
|
||||
}
|
||||
// checkSelfPermission() : Promise<boolean> {
|
||||
// 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: any) {
|
||||
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
|
||||
// }
|
||||
//# sourceMappingURL=camera.uts.map
|
||||
Vendored
-1
File diff suppressed because one or more lines are too long
Vendored
-215
@@ -1,215 +0,0 @@
|
||||
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<CameraView> {
|
||||
let cameraPreview = CameraManager.getInstance().getCameraView();
|
||||
if (cameraPreview != null) {
|
||||
return Promise.resolve<CameraView>(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<CameraView>((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(() => {
|
||||
__f__('log', 'at uni_modules/Skin-cameraUTS/utssdk/app-android/index.uts:145', "未找到相机");
|
||||
});
|
||||
}
|
||||
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);
|
||||
};
|
||||
//# sourceMappingURL=index.uts.map
|
||||
Vendored
-1
File diff suppressed because one or more lines are too long
Vendored
-141
@@ -1,141 +0,0 @@
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
import { CameraView } from './camera';
|
||||
import { CameraConfig } from '../interface';
|
||||
import { CameraManager } from './CameraManager';
|
||||
//原生提供以下属性或方法的实现
|
||||
export default {
|
||||
/**
|
||||
* 组件名称,也就是开发者使用的标签
|
||||
*/
|
||||
name: "xf-camera",
|
||||
/**
|
||||
* 组件涉及的事件声明,只有声明过的事件,才能被正常发送
|
||||
*/
|
||||
emits: ['stop', 'error', 'ready'],
|
||||
/**
|
||||
* 属性声明,组件的使用者会传递这些属性值到组件
|
||||
*/
|
||||
props: {
|
||||
"mode": {
|
||||
type: String,
|
||||
default: "normal"
|
||||
},
|
||||
"resolution": {
|
||||
type: String,
|
||||
default: "medium" // low | high
|
||||
},
|
||||
"position": {
|
||||
type: String,
|
||||
default: "back" // front前置 |back 后置
|
||||
},
|
||||
"flash": {
|
||||
type: String,
|
||||
default: "auto" // auto, on, off, torch
|
||||
},
|
||||
"frameSize": {
|
||||
type: String,
|
||||
default: "medium" // small|large
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 组件内部变量声明
|
||||
*/
|
||||
data(): UTSJSONObject {
|
||||
return {};
|
||||
},
|
||||
/**
|
||||
* 属性变化监听器实现
|
||||
*/
|
||||
watch: {
|
||||
"position": {
|
||||
/**
|
||||
* 这里监听属性变化,并进行组件内部更新
|
||||
*/
|
||||
handler(newValue: string, _: string) {
|
||||
this.$el?.switchCamera(newValue);
|
||||
},
|
||||
immediate: false // 创建时是否通过此方法更新属性,默认值为false
|
||||
},
|
||||
"flash": {
|
||||
/**
|
||||
* 这里监听属性变化,并进行组件内部更新
|
||||
*/
|
||||
handler(newValue: string, _: string) {
|
||||
this.$el?.setFlash(newValue);
|
||||
},
|
||||
immediate: false // 创建时是否通过此方法更新属性,默认值为false
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 规则:如果没有配置expose,则methods中的方法均对外暴露,如果配置了expose,则以expose的配置为准向外暴露
|
||||
* ['publicMethod'] 含义为:只有 `publicMethod` 在实例上可用
|
||||
*/
|
||||
expose: [],
|
||||
methods: {},
|
||||
/**
|
||||
* [可选实现] 组件被创建,组件第一个生命周期,
|
||||
* 在内存中被占用的时候被调用,开发者可以在这里执行一些需要提前执行的初始化逻辑
|
||||
*/
|
||||
created() {
|
||||
},
|
||||
/**
|
||||
* [可选实现] 对应平台的view载体即将被创建,对应前端beforeMount
|
||||
*/
|
||||
NVBeforeLoad() {
|
||||
},
|
||||
/**
|
||||
* [必须实现] 创建原生View,必须定义返回值类型
|
||||
* 开发者需要重点实现这个函数,声明原生组件被创建出来的过程,以及最终生成的原生组件类型
|
||||
* (Android需要明确知道View类型,需特殊校验)
|
||||
*/
|
||||
NVLoad(): CameraView {
|
||||
let previewView = new CameraView(this.$androidContext!, this);
|
||||
previewView.setTag("9527");
|
||||
CameraManager.getInstance().setCameraView(previewView);
|
||||
return previewView;
|
||||
},
|
||||
/**
|
||||
* [可选实现] 原生View已创建
|
||||
*/
|
||||
NVLoaded() {
|
||||
},
|
||||
/**
|
||||
* [可选实现] 原生View布局完成
|
||||
*/
|
||||
NVLayouted() {
|
||||
this.$el?.setConfig({
|
||||
resolution: this.resolution,
|
||||
frameSize: this.frameSize,
|
||||
flash: this.flash
|
||||
} as CameraConfig);
|
||||
this.$el?.switchCamera(this.position);
|
||||
},
|
||||
/**
|
||||
* [可选实现] 原生View将释放
|
||||
*/
|
||||
NVBeforeUnload() {
|
||||
let ret: Map<string, any> = new Map();
|
||||
this.$emit('stop', ret);
|
||||
this.$el?.unbindCamera();
|
||||
},
|
||||
/**
|
||||
* [可选实现] 原生View已释放,这里可以做释放View之后的操作
|
||||
*/
|
||||
NVUnloaded() {
|
||||
CameraManager.getInstance().dispose();
|
||||
},
|
||||
/**
|
||||
* [可选实现] 组件销毁
|
||||
*/
|
||||
unmounted() {
|
||||
},
|
||||
};
|
||||
/*
|
||||
<view>
|
||||
</view>
|
||||
*/
|
||||
//# sourceMappingURL=index.vue.map
|
||||
Vendored
-1
File diff suppressed because one or more lines are too long
-183
@@ -1,183 +0,0 @@
|
||||
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;
|
||||
};
|
||||
/** 接口调用结束的回调函数(调用成功、失败都会执行) */
|
||||
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;
|
||||
//# sourceMappingURL=interface.uts.map
|
||||
-1
File diff suppressed because one or more lines are too long
Vendored
-21
@@ -1,21 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=CameraManager.uts.map
|
||||
Vendored
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"CameraManager.uts","sourceRoot":"","sources":["uni_modules/Skin-cameraUTS/utssdk/app-ios/CameraManager.uts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAEtC,MAAM,OAAO,aAAa;IACzB,OAAO,CAAC,UAAU,EAAG,UAAU,GAAG,IAAI,GAAG,IAAI,CAAC;IAE9C,OAAO,CAAC,MAAM,CAAC,QAAQ,EAAG,aAAa,GAAG,IAAI,GAAG,IAAI,CAAC;IAEtD,MAAM,CAAC,MAAM,CAAC,WAAW,IAAK,aAAa;QAC1C,IAAI,aAAa,CAAC,QAAQ,IAAI,IAAI,EAAE;YACnC,aAAa,CAAC,QAAQ,GAAG,IAAI,aAAa,EAAE,CAAC;SAC7C;QACD,OAAO,aAAa,CAAC,QAAQ,CAAC,CAAC;IAChC,CAAC;IAED,MAAM,CAAC,aAAa,CAAC,UAAW,CAAC,EAAE,UAAU,GAAI,IAAI;QACpD,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC,CAAA;IAC9B,CAAC;IAED,MAAM,CAAC,aAAa,IAAK,UAAU,GAAG,IAAI;QACzC,OAAO,IAAI,CAAC,UAAU,CAAC;IACxB,CAAC;IAED,MAAM,CAAC,OAAO,IAAK,IAAI;QACtB,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;IACxB,CAAC;CACD","sourcesContent":["import { CameraView } from \"./camera\";\r\n\r\nexport class CameraManager {\r\n\tprivate cameraView : CameraView | null = null;\r\n\r\n\tprivate static instance : CameraManager | null = null;\r\n\r\n\tpublic static getInstance() : CameraManager {\r\n\t\tif (CameraManager.instance == null) {\r\n\t\t\tCameraManager.instance = new CameraManager();\r\n\t\t}\r\n\t\treturn CameraManager.instance!;\r\n\t}\r\n\r\n\tpublic setCameraView(cameraView ?: CameraView) : void {\r\n\t\tthis.cameraView = cameraView!\r\n\t}\r\n\r\n\tpublic getCameraView() : CameraView | null {\r\n\t\treturn this.cameraView;\r\n\t}\r\n\r\n\tpublic dispose() : void {\r\n\t\tthis.cameraView = null;\r\n\t}\r\n}"]}
|
||||
-422
@@ -1,422 +0,0 @@
|
||||
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<string, AVCaptureSession.Preset>([
|
||||
['low', AVCaptureSession.Preset.low],
|
||||
['medium', AVCaptureSession.Preset.medium],
|
||||
['high', AVCaptureSession.Preset.high],
|
||||
]);
|
||||
const FLASH_MODE_MAP = new Map<string, AVCaptureDevice.TorchMode>([
|
||||
['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<UIView>;
|
||||
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<UIView>, 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<string, any> = new Map();
|
||||
ret.set("maxZoom", device.maxAvailableVideoZoomFactor);
|
||||
let detail: Map<string, any> = new Map();
|
||||
detail.set("detail", ret);
|
||||
this.comp?.$emit("ready", detail);
|
||||
}
|
||||
catch (e) {
|
||||
let ret: Map<string, any> = new Map();
|
||||
ret.set("errMsg", JSON.stringify(e));
|
||||
let detail: Map<string, any> = 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) {
|
||||
__f__('log', 'at uni_modules/Skin-cameraUTS/utssdk/app-ios/camera.uts:345', 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) {
|
||||
__f__('log', 'at uni_modules/Skin-cameraUTS/utssdk/app-ios/camera.uts:365', 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);
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=camera.uts.map
|
||||
-1
File diff suppressed because one or more lines are too long
-165
@@ -1,165 +0,0 @@
|
||||
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);
|
||||
};
|
||||
//# sourceMappingURL=index.uts.map
|
||||
-1
File diff suppressed because one or more lines are too long
-154
@@ -1,154 +0,0 @@
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
import { UIView } from "UIKit";
|
||||
import { CameraView } from "./camera";
|
||||
import { CameraConfig } from '../interface';
|
||||
import { CameraManager } from "./CameraManager";
|
||||
//原生提供以下属性或方法的实现
|
||||
export default {
|
||||
data(): UTSJSONObject {
|
||||
return {};
|
||||
},
|
||||
/**
|
||||
* 组件名称,也就是开发者使用的标签
|
||||
*/
|
||||
name: "xf-camera",
|
||||
/**
|
||||
* 组件涉及的事件声明,只有声明过的事件,才能被正常发送
|
||||
*/
|
||||
emits: ['stop', 'error', 'ready'],
|
||||
/**
|
||||
* 属性声明,组件的使用者会传递这些属性值到组件
|
||||
*/
|
||||
props: {
|
||||
"mode": {
|
||||
type: String,
|
||||
default: "normal"
|
||||
},
|
||||
"resolution": {
|
||||
type: String,
|
||||
default: "medium" // low | high
|
||||
},
|
||||
"position": {
|
||||
type: String,
|
||||
default: "back" // front前置 |back 后置
|
||||
},
|
||||
"flash": {
|
||||
type: String,
|
||||
default: "auto" // auto, on, off, torch
|
||||
},
|
||||
"frameSize": {
|
||||
type: String,
|
||||
default: "medium" // small|large
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 属性变化监听器实现
|
||||
*/
|
||||
watch: {
|
||||
"position": {
|
||||
/**
|
||||
* 这里监听属性变化,并进行组件内部更新
|
||||
*/
|
||||
handler(newValue: String, _oldValue: String) {
|
||||
CameraManager.getInstance().getCameraView()?.switchCamera(newValue);
|
||||
},
|
||||
/**
|
||||
* 创建时是否通过此方法更新属性,默认值为false
|
||||
*/
|
||||
immediate: false
|
||||
},
|
||||
"flash": {
|
||||
/**
|
||||
* 这里监听属性变化,并进行组件内部更新
|
||||
*/
|
||||
handler(newValue: String, _oldValue: String) {
|
||||
CameraManager.getInstance().getCameraView()?.setFlash(newValue);
|
||||
},
|
||||
/**
|
||||
* 创建时是否通过此方法更新属性,默认值为false
|
||||
*/
|
||||
immediate: false
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 规则:如果没有配置expose,则methods中的方法均对外暴露,如果配置了expose,则以expose的配置为准向外暴露
|
||||
* ['publicMethod'] 含义为:只有 `publicMethod` 在实例上可用
|
||||
*/
|
||||
expose: [],
|
||||
methods: {},
|
||||
/**
|
||||
* 组件被创建,组件第一个生命周期,
|
||||
* 在内存中被占用的时候被调用,开发者可以在这里执行一些需要提前执行的初始化逻辑
|
||||
* [可选实现]
|
||||
*/
|
||||
created() {
|
||||
},
|
||||
/**
|
||||
* 对应平台的view载体即将被创建,对应前端beforeMount
|
||||
* [可选实现]
|
||||
*/
|
||||
NVBeforeLoad() {
|
||||
},
|
||||
/**
|
||||
* 创建原生View,必须定义返回值类型
|
||||
* 开发者需要重点实现这个函数,声明原生组件被创建出来的过程,以及最终生成的原生组件类型
|
||||
* [必须实现]
|
||||
*/
|
||||
NVLoad(): UIView {
|
||||
let previewView = new UIView();
|
||||
previewView.tag = 9527;
|
||||
return previewView;
|
||||
},
|
||||
/**
|
||||
* 原生View已创建
|
||||
* [可选实现]
|
||||
*/
|
||||
NVLoaded() {
|
||||
},
|
||||
/**
|
||||
* 原生View布局完成
|
||||
* [可选实现]
|
||||
*/
|
||||
NVLayouted() {
|
||||
let previewView = this.$el as UIView;
|
||||
let cameraView = new CameraView(previewView, this, {
|
||||
resolution: this.resolution,
|
||||
frameSize: this.frameSize,
|
||||
devicePosition: this.position,
|
||||
flash: this.flash
|
||||
} as CameraConfig);
|
||||
CameraManager.getInstance().setCameraView(cameraView);
|
||||
},
|
||||
/**
|
||||
* 原生View将释放
|
||||
* [可选实现]
|
||||
*/
|
||||
NVBeforeUnload() {
|
||||
let ret: Map<string, any> = new Map();
|
||||
let detail: Map<string, any> = new Map();
|
||||
detail.set("detail", ret);
|
||||
this.$emit("stop", detail);
|
||||
CameraManager.getInstance().getCameraView()?.unbindCamera();
|
||||
},
|
||||
/**
|
||||
* 原生View已释放,这里可以做释放View之后的操作
|
||||
* [可选实现]
|
||||
*/
|
||||
NVUnloaded() {
|
||||
CameraManager.getInstance().dispose();
|
||||
},
|
||||
/**
|
||||
* 组件销毁
|
||||
* [可选实现]
|
||||
*/
|
||||
unmounted() {
|
||||
}
|
||||
};
|
||||
/*
|
||||
<view>
|
||||
</view>
|
||||
*/
|
||||
//# sourceMappingURL=index.vue.map
|
||||
-1
File diff suppressed because one or more lines are too long
-183
@@ -1,183 +0,0 @@
|
||||
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;
|
||||
};
|
||||
/** 接口调用结束的回调函数(调用成功、失败都会执行) */
|
||||
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;
|
||||
//# sourceMappingURL=interface.uts.map
|
||||
-1
File diff suppressed because one or more lines are too long
Binary file not shown.
-26
@@ -1,26 +0,0 @@
|
||||
{
|
||||
"id": "__UNI__644401D",
|
||||
"name": "SkinCamera",
|
||||
"description": "",
|
||||
"version": {
|
||||
"name": "1.0.0",
|
||||
"code": "100"
|
||||
},
|
||||
"uni-app-x": {
|
||||
"compilerVersion": "4.87"
|
||||
},
|
||||
"app-android": {
|
||||
"distribute": {
|
||||
"modules": {},
|
||||
"icons": {
|
||||
"hdpi": "",
|
||||
"xhdpi": "",
|
||||
"xxhdpi": "",
|
||||
"xxxhdpi": ""
|
||||
},
|
||||
"splashScreens": {
|
||||
"default": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user