import { abilityAccessCtrl, Permissions } from '@kit.AbilityKit'; import { camera } from '@kit.CameraKit'; import { BusinessError } from '@kit.BasicServicesKit'; import { image } from '@kit.ImageKit'; import fs from '@ohos.file.fs'; @Component struct CameraViewComponent { @Prop mode: string = 'photo' @Prop isFront: boolean = false @Prop isFlash: boolean = false @Prop isTorch: boolean = false @Prop correctOrientation: boolean = false @Prop @Watch('onCommandChange') command: string = '' // 指令 onTake?: (path: string) => void private xComponentCtl: XComponentController = new XComponentController(); private xComponentSurfaceId: string = ''; @State imageWidth: number = 1920; @State imageHeight: number = 1080; private cameraManager: camera.CameraManager | undefined = undefined; private cameras: Array | Array = []; private cameraPosition: number = 0 private isFrontCamera = false private cameraInput: camera.CameraInput | undefined = undefined; private previewOutput: camera.PreviewOutput | undefined = undefined; private photoOutput: camera.PhotoOutput | undefined = undefined; private videoSession: camera.VideoSession | undefined = undefined; private photoSession: camera.PhotoSession | undefined = undefined; private uiContext: UIContext = this.getUIContext(); private context: Context | undefined = this.uiContext.getHostContext(); private cameraPermission: Permissions = 'ohos.permission.CAMERA'; private isTorchOn: boolean = false private isFlashOn: boolean = false @State isShow: boolean = false; onCommandChange() { let command = this.command.split('-')[0] if (command == 'open') { this.initCamera() } else if (command == 'close') { this.releaseCamera(); } else if (command == 'take') { this.takePhoto() } else if (command == 'switch') { this.isFrontCamera = this.isFront this.switchCamera() } else if (command == 'flash') { this.isFlashOn = this.isFlash this.onFlash() } else if (command == 'torch') { this.isTorchOn = this.isTorch this.onTorch() } } async requestPermissionsFn(): Promise { let atManager = abilityAccessCtrl.createAtManager(); if (this.context) { let res = await atManager.requestPermissionsFromUser(this.context, [this.cameraPermission]); for (let i = 0; i < res.permissions.length; i++) { if (this.cameraPermission.toString() === res.permissions[i] && res.authResults[i] === 0) { this.isShow = true; } } } } async aboutToAppear() { await this.requestPermissionsFn(); } aboutToDisappear(): void { this.releaseCamera(); } // 初始化相机。 async initCamera(): Promise { // console.info(`initCamera previewOutput xComponentSurfaceId:${this.xComponentSurfaceId}`); try { // 获取相机管理器实例。 this.cameraManager = camera.getCameraManager(this.context); if (!this.cameraManager) { console.error('initCamera getCameraManager'); } // 获取当前设备支持的相机device列表。 this.cameras = this.cameraManager.getSupportedCameras(); if (!this.cameras) { console.error('initCamera getSupportedCameras'); } // 选择一个相机device,创建cameraInput输出对象。 this.cameraInput = this.cameraManager.createCameraInput(this.cameras[this.cameraPosition]); if (!this.cameraInput) { console.error('initCamera createCameraInput'); } // 打开相机。 await this.cameraInput.open().catch((err: BusinessError) => { console.error(`initCamera open fail: ${err}`); }) // 获取相机device支持的profile。 let capability: camera.CameraOutputCapability = this.cameraManager.getSupportedOutputCapability(this.cameras[this.cameraPosition], camera.SceneMode.NORMAL_PHOTO); if (!capability) { console.error('initCamera getSupportedOutputCapability'); } let previewProfilesArray: Array = capability.previewProfiles; if (!previewProfilesArray) { console.error("createOutput previewProfilesArray == null || undefined"); } let photoProfilesArray: Array = capability.photoProfiles; if (!photoProfilesArray) { console.error("createOutput photoProfilesArray == null || undefined"); } // 预览 let minRatioDiff: number = 0.1; let surfaceRatio: number = this.imageWidth / this.imageHeight; // 最接近16:9宽高比。 let previewProfile: camera.Profile = previewProfilesArray[0]; // // 应用开发者根据实际业务需求选择一个支持的预览流previewProfile。 // for (let index = 0; index < previewProfilesArray.length; index++) { // const tempProfile = previewProfilesArray[index]; // let tempRatio = tempProfile.size.width >= tempProfile.size.height ? // tempProfile.size.width / tempProfile.size.height : tempProfile.size.height / tempProfile.size.width; // let currentRatio = Math.abs(tempRatio - surfaceRatio); // if (currentRatio <= minRatioDiff && tempProfile.format == camera.CameraFormat.CAMERA_FORMAT_JPEG) { // previewProfile = tempProfile; // break; // } // } // this.imageWidth = previewProfile.size.width; // 更新xComponent组件的宽。 // this.imageHeight = previewProfile.size.height; // 更新xComponent组件的高。 // console.info(`initCamera imageWidth:${this.imageWidth} imageHeight:${this.imageHeight}`); // 使用xComponentSurfaceId创建预览。 this.previewOutput = this.cameraManager.createPreviewOutput(previewProfile, this.xComponentSurfaceId); if (!this.previewOutput) { console.error('initCamera createPreviewOutput'); } // 拍照 this.photoOutput = this.cameraManager.createPhotoOutput(photoProfilesArray[0]); this.setPhotoOutputCb(this.photoOutput); // 创建录像模式相机会话。 if(this.mode == 'video') { this.videoSession = this.cameraManager.createSession(camera.SceneMode.NORMAL_VIDEO) as camera.VideoSession; if (!this.videoSession) { console.error('initCamera createSession'); } // 开始配置会话。 this.videoSession.beginConfig(); // 添加相机设备输入。 this.videoSession.addInput(this.cameraInput); // 添加预览流输出。 this.videoSession.addOutput(this.previewOutput); // 提交会话配置。 await this.videoSession.commitConfig(); // 开始启动已配置的输入输出流。 await this.videoSession.start(); } else if(this.mode == 'photo') { this.photoSession = this.cameraManager.createSession(camera.SceneMode.NORMAL_PHOTO) as camera.PhotoSession; if (!this.photoSession) { console.error('initCamera createSession'); } // 开始配置会话。 this.photoSession.beginConfig(); // 添加相机设备输入。 this.photoSession.addInput(this.cameraInput); // 添加预览流输出。 this.photoSession.addOutput(this.previewOutput); // 添加拍照输出流 this.photoSession.addOutput(this.photoOutput); // 提交会话配置。 await this.photoSession.commitConfig(); // 开始启动已配置的输入输出流。 await this.photoSession.start(); } } catch (error) { console.error(`initCamera fail: ${error}`); } } // 释放相机。 async releaseCamera(): Promise { try { if(this.mode == 'video') { // 停止当前会话。 await this.videoSession?.stop(); // 释放相机输入流。 await this.cameraInput?.close(); // 释放预览输出流。 await this.previewOutput?.release(); // 释放会话。 await this.videoSession?.release(); } else if(this.mode == 'photo') { // 停止当前会话。 await this.photoSession?.stop(); // 释放相机输入流。 await this.cameraInput?.close(); // 释放预览输出流。 await this.previewOutput?.release(); // 释放拍照输出流。 await this.photoOutput?.release(); // 释放会话。 await this.photoSession?.release(); } } catch (error) { console.error(`initCamera fail: ${error}`); } } setPhotoOutputCb(photoOutput: camera.PhotoOutput): void { //设置回调之后,调用photoOutput的capture方法,就会将拍照的buffer回传到回调中。 photoOutput.on('photoAvailable', (errCode: BusinessError, photo: camera.Photo): void => { if (errCode || photo === undefined) { console.error('getPhoto failed'); return; } let imageObj = photo.main; imageObj.getComponent(image.ComponentType.JPEG, async (errCode: BusinessError, component: image.Component) => { if (errCode || component === undefined) { console.error('getComponent failed'); return; } let buffer: ArrayBuffer; if (component.byteBuffer) { buffer = component.byteBuffer; } else { console.error('byteBuffer is null'); return; } // 生成照片 try { const tempDir = this.context?.cacheDir; const timestamp = new Date().getTime(); const fileName = `photo_${timestamp}.jpg`; const filePath = `${tempDir}/${fileName}`; const file = await fs.open(filePath, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE); await fs.write(file.fd, buffer); await fs.close(file.fd); this.onTake?.(filePath); } catch (error) { console.error('保存临时照片失败:', error); } finally { imageObj.release(); } }); }); } takePhoto() { let photoCaptureSetting: camera.PhotoCaptureSetting = { quality: camera.QualityLevel.QUALITY_LEVEL_HIGH, // 设置图片质量高。 rotation: camera.ImageRotation.ROTATION_0 // 设置图片旋转角度0。 } // 使用当前拍照设置进行拍照。 this.photoOutput?.capture(photoCaptureSetting, (err: BusinessError) => { if (err) { console.error(`Failed to capture the photo ${err.message}`); return; } // console.info('Callback invoked to indicate the photo capture request success.'); }); } // 切换摄像头 async switchCamera() { if (this.cameras.length < 2) { console.error('Only one camera available'); return; } // 切换摄像头位置 this.cameraPosition = (this.isFrontCamera ? camera.CameraPosition.CAMERA_POSITION_FRONT : camera.CameraPosition.CAMERA_POSITION_BACK) - 1; await this.releaseCamera(); await this.initCamera(); } // 手电筒 onTorch() { try { let torchSupport = this.cameraManager?.isTorchSupported() ?? false if(torchSupport) { if(this.cameraPosition == camera.CameraPosition.CAMERA_POSITION_BACK - 1) { console.error('back camera not support'); return } this.cameraManager?.setTorchMode(this.isTorchOn ? camera.TorchMode.ON : camera.TorchMode.OFF); } } catch (error) { console.error(error); } } // 闪光灯 onFlash() { try { let torchSupport = this.cameraManager?.isTorchSupported() ?? false if(torchSupport) { if(this.cameraPosition == camera.CameraPosition.CAMERA_POSITION_FRONT - 1) { console.error('front camera not support'); return } this.cameraManager?.setTorchMode(this.isFlashOn ? camera.TorchMode.AUTO : camera.TorchMode.OFF); } } catch (error) { console.error(error); } } build() { Column() { if (this.isShow) { XComponent({ id: 'componentId', type: XComponentType.SURFACE, controller: this.xComponentCtl }) .onLoad(async () => { // 获取组件surfaceId。 this.xComponentSurfaceId = this.xComponentCtl.getXComponentSurfaceId(); // 初始化相机,组件实时渲染每帧预览流数据。 this.initCamera() }) .width(this.uiContext.px2vp(this.imageHeight)) .height(this.uiContext.px2vp(this.imageWidth)) } } .justifyContent(FlexAlign.Center) .backgroundColor(Color.Black) .height('100%') .width('100%') } } @Builder export function CameraView(params: ESObject) { Row() { CameraViewComponent({ mode: 'photo', isFront: params.isFront, isFlash: params.isFlash, isTorch: params.isTorch, command: params.command, correctOrientation: params.correctOrientation, onTake: (path: string) => { let fun = params.onTake as (path: string) => void fun(path) } }) .width('100%') .height('100%') } .width('100%') .height('100%') .attributeModifier(params.attributeUpdater) }