1/* 2 * Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16import camera from '@ohos.multimedia.camera' 17import image from '@ohos.multimedia.image' 18import photoAccessHelper from '@ohos.file.photoAccessHelper' 19import media from '@ohos.multimedia.media'; 20import deviceInfo from '@ohos.deviceInfo'; 21import type common from '@ohos.app.ability.common' 22import MediaModel from './MediaModel' 23import Logger from '../utils/Logger' 24 25const TAG: string = '[CameraModel]' 26 27const CAMERASIZE = { 28 WIDTH: 1920, 29 HEIGHT: 1080 30} 31 32/** 33 * 相机服务 34 */ 35export default class CameraModel { 36 private cameraInput: camera.CameraInput; 37 private previewOutput: camera.PreviewOutput; 38 private photoAsset: photoAccessHelper.PhotoAsset; 39 private photoUri: string = ''; 40 private mediaModel: MediaModel; 41 private fd: number = -1; 42 private receiver: image.ImageReceiver; 43 private context: any; 44 private cameraManager: camera.CameraManager; 45 private cameras: Array<camera.CameraDevice>; 46 private photoOutPut: camera.PhotoOutput; 47 private captureSession: camera.CaptureSession; 48 private cameraOutputCapability: camera.CameraOutputCapability; 49 private videoOutput: camera.VideoOutput; 50 private avRecorder: media.AVRecorder; 51 52 private avRecorderProfile: media.VideoRecorderProfile = { 53 audioBitrate: 48000, 54 audioChannels: 2, 55 audioCodec: media.CodecMimeType.AUDIO_AAC, 56 audioSampleRate: 48000, 57 fileFormat: media.ContainerFormatType.CFT_MPEG_4, 58 videoBitrate: 2000000, 59 videoCodec: media.CodecMimeType.VIDEO_MPEG4, 60 videoFrameWidth: 640, 61 videoFrameHeight: 480, 62 videoFrameRate: 30 63 } 64 65 private videoSourceType = 0; 66 67 constructor(context: common.Context) { 68 this.context = context 69 this.mediaModel = MediaModel.getMediaInstance(context) 70 // 服务端代码,创建ImageReceiver 71 this.receiver = image.createImageReceiver(CAMERASIZE.WIDTH, CAMERASIZE.HEIGHT, 4, 8) // 4表示生成的图像格式,8表示用户希望同时访问的最大图像数 72 Logger.info(TAG, `createImageReceiver`) 73 // 获取Surface ID 74 this.receiver.getReceivingSurfaceId((surfaceId) => { 75 Logger.info(TAG, `getReceivingSurfaceId surfaceId is ${surfaceId}`) 76 }) 77 // 注册Surface的监听,在surface的buffer准备好后触发 78 this.receiver.on('imageArrival', () => { 79 Logger.info(TAG, `imageArrival`) 80 // 去获取Surface中最新的buffer 81 this.receiver.readNextImage((err, image) => { 82 Logger.info(TAG, `readNextImage`) 83 if (err || image === undefined) { 84 Logger.error(TAG, `failed to get valid image`) 85 return 86 } 87 image.getComponent(4, (errMsg, img) => { 88 // 4表示图像类型为JPEG 89 // 消费component.byteBuffer,例如:将buffer内容保存成图片。 90 Logger.info(TAG, `getComponent`) 91 if (errMsg || img === undefined) { 92 Logger.info(TAG, `failed to get valid buffer`) 93 return 94 } 95 let buffer = new ArrayBuffer(4096) 96 if (img.byteBuffer) { 97 buffer = img.byteBuffer 98 } else { 99 Logger.error(TAG, `img.byteBuffer is undefined`) 100 } 101 }) 102 }) 103 }) 104 } 105 106 /** 107 * 创建相机 108 */ 109 async createCamera(surfaceId: string): Promise<void> { 110 Logger.info(TAG, `initCamera surfaceId:${surfaceId}`); 111 // await this.releaseCamera(); 112 Logger.info(TAG, `deviceInfo.deviceType = ${deviceInfo.deviceType}`); 113 if (deviceInfo.deviceType === 'default') { 114 Logger.info(TAG, `deviceInfo.deviceType default 1 = ${deviceInfo.deviceType}`); 115 this.videoSourceType = 1; 116 Logger.info(TAG, `deviceInfo.deviceType default 2 = ${deviceInfo.deviceType}`); 117 } else { 118 Logger.info(TAG, `deviceInfo.deviceType other 1 = ${deviceInfo.deviceType}`); 119 this.videoSourceType = 0; 120 Logger.info(TAG, `deviceInfo.deviceType other 2 = ${deviceInfo.deviceType}`); 121 } 122 Logger.info(TAG, `getCameraManager begin`); 123 try { 124 Logger.info(TAG, `getCameraManager try begin`); 125 this.cameraManager = camera.getCameraManager(this.context); 126 Logger.info(TAG, `getCameraManager try end`); 127 } catch (e) { 128 Logger.info(TAG, `getCameraManager catch e:${JSON.stringify(e)}`); 129 } 130 Logger.info(TAG, `getCameraManager end`); 131 Logger.info(TAG, `getCameraManager ${JSON.stringify(this.cameraManager)}`); 132 this.cameras = this.cameraManager.getSupportedCameras(); 133 Logger.info(TAG, `createCamera get cameras ${this.cameras.length}`); 134 if (this.cameras.length === 0) { 135 Logger.info(TAG, 'cannot get cameras'); 136 return; 137 } 138 139 Logger.info(TAG, `createCamera cameras=${this.cameras}`); 140 let mCamera = this.cameras[0]; 141 Logger.info(TAG, `createCamera mCamera=${mCamera}`); 142 this.cameraInput = this.cameraManager.createCameraInput(mCamera); 143 Logger.info(TAG, `createCamera cameraInput=${this.cameraInput}`); 144 this.cameraInput.open(); 145 Logger.info(TAG, 'createCameraInput'); 146 this.cameraOutputCapability = this.cameraManager.getSupportedOutputCapability(mCamera); 147 let previewProfile = this.cameraOutputCapability.previewProfiles[0]; 148 this.previewOutput = this.cameraManager.createPreviewOutput( 149 previewProfile, 150 surfaceId 151 ); 152 Logger.info(TAG, 'createPreviewOutput'); 153 let rSurfaceId = await this.receiver.getReceivingSurfaceId(); 154 let photoProfile = this.cameraOutputCapability.photoProfiles[0]; 155 this.photoOutPut = this.cameraManager.createPhotoOutput( 156 photoProfile, 157 rSurfaceId 158 ); 159 this.captureSession = this.cameraManager.createCaptureSession(); 160 Logger.info(TAG, 'createCaptureSession'); 161 this.captureSession.beginConfig(); 162 Logger.info(TAG, 'beginConfig'); 163 this.captureSession.addInput(this.cameraInput); 164 this.captureSession.addOutput(this.previewOutput); 165 this.captureSession.addOutput(this.photoOutPut); 166 await this.captureSession.commitConfig(); 167 await this.captureSession.start(); 168 Logger.info(TAG, 'captureSession start'); 169 } 170 171 /** 172 * 开始录像 173 */ 174 async startVideo(): Promise<void> { 175 Logger.info(TAG, 'startVideo begin'); 176 Logger.info(TAG, 'startVideo 1'); 177 await this.captureSession.stop(); 178 Logger.info(TAG, 'startVideo 2'); 179 this.captureSession.beginConfig(); 180 Logger.info(TAG, 'startVideo 3'); 181 if (this.videoOutput) { 182 try { 183 Logger.info(TAG, 'startVideo 4'); 184 this.captureSession.removeOutput(this.videoOutput); 185 } catch (e) { 186 Logger.info(TAG, `startVideo catch e:${e}`); 187 } 188 } 189 if (this.videoOutput) { 190 try { 191 Logger.info(TAG, 'startVideo 5'); 192 this.captureSession.removeOutput(this.videoOutput); 193 Logger.info(TAG, 'startVideo 6'); 194 } catch (e) { 195 Logger.info(TAG, `startVideo catch e:${JSON.stringify(e)}`); 196 } 197 try { 198 Logger.info(TAG, 'startVideo release'); 199 await this.videoOutput.release(); 200 } catch (e) { 201 Logger.info(TAG, `startVideo catch e:${JSON.stringify(e)}`); 202 } 203 } 204 Logger.info(TAG, 'startVideo 7'); 205 this.photoAsset = await this.mediaModel.createAndGetUri(photoAccessHelper.PhotoType.VIDEO); 206 Logger.info(TAG, `startVideo photoAsset:${this.photoAsset}`); 207 this.fd = await this.mediaModel.getFdPath(this.photoAsset); 208 Logger.info(TAG, `startVideo fd:${this.fd}`); 209 this.avRecorder = await media.createAVRecorder(); 210 211 Logger.info(TAG, `startVideo into createAVRecorder:${this.avRecorder}`); 212 if (this.avRecorder != null) { 213 Logger.info(TAG, `startVideo createAVRecorder success:${this.avRecorder}`); 214 215 let videoConfig: media.VideoRecorderConfig = { 216 audioSourceType: 1, 217 videoSourceType: this.videoSourceType, 218 profile: this.avRecorderProfile, 219 url: `fd://${this.fd}`, 220 rotation: 0 221 } 222 223 Logger.info(TAG, `startVideo videoConfig:${JSON.stringify(videoConfig)}`); 224 try { 225 await this.avRecorder.prepare(videoConfig); 226 } catch (err) { 227 Logger.info(TAG, `startVideo err:${err}`); 228 } 229 230 Logger.info(TAG, `startVideo prepare`); 231 let videoId = await this.avRecorder.getInputSurface(); 232 let videoProfile = this.cameraOutputCapability.videoProfiles[0]; 233 Logger.info(TAG, `startVideo capability.videoProfiles[]=: ${JSON.stringify(this.cameraOutputCapability.videoProfiles)}`); 234 Logger.info(TAG, `startVideo videoProfile:${JSON.stringify(videoProfile)}`); 235 this.videoOutput = this.cameraManager.createVideoOutput(videoProfile, videoId); 236 Logger.info(TAG, `startVideo videoOutput:${this.videoOutput}`); 237 this.captureSession.addOutput(this.videoOutput); 238 Logger.info(TAG, `startVideo addOutput`); 239 await this.captureSession.commitConfig(); 240 Logger.info(TAG, `startVideo commitConfig`); 241 await this.captureSession.start(); 242 Logger.info(TAG, `startVideo commitConfig captureSession`); 243 await this.videoOutput.start(); 244 Logger.info(TAG, `startVideo commitConfig videoOutput`); 245 await this.avRecorder.start(); 246 Logger.info(TAG, 'startVideo end'); 247 248 } else { 249 Logger.info(TAG, `startVideo createAVRecorder fail, error null`); 250 } 251 } 252 253 /** 254 * 停止录像 255 */ 256 async stopVideo(): Promise<string> { 257 Logger.info(TAG, 'stopVideo called'); 258 await this.avRecorder.stop(); 259 await this.avRecorder.release(); 260 await this.videoOutput.stop(); 261 // 停止结束时复制录制的视频至沙箱cache目录中 262 let fileName = await this.copyVideo(); 263 await this.photoAsset.close(this.fd); 264 return fileName; 265 } 266 267 /** 268 * 复制视频至video 269 */ 270 async copyVideo(): Promise<string> { 271 let url = this.photoAsset.uri; 272 Logger.info(TAG, `stopVideo uri:${url}`); 273 let fileName = await this.mediaModel.copyVideo(this.fd); 274 Logger.info(TAG, `stopVideo fileName:${fileName}`); 275 return fileName; 276 } 277 278 /** 279 * 关闭相机 280 */ 281 async releaseCamera(): Promise<void> { 282 Logger.info(TAG, `releaseCamera start`); 283 if (this.cameraInput) { 284 await this.cameraInput.close(); 285 } 286 if (this.previewOutput) { 287 await this.previewOutput.release(); 288 } 289 if (this.photoOutPut) { 290 await this.photoOutPut.release(); 291 } 292 if (this.videoOutput) { 293 await this.videoOutput.release(); 294 } 295 if (this.captureSession) { 296 await this.captureSession.release(); 297 } 298 Logger.info(TAG, `releaseCamera end`); 299 } 300} 301 302