1# 录像实现方案(ArkTS) 2 3在开发相机应用时,需要先申请相机相关权限[开发准备](camera-preparation.md)。 4当前示例提供完整的录像流程介绍,方便开发者了解完整的接口调用顺序。 5 6在参考以下示例前,建议开发者查看[相机开发指导(ArkTS)](camera-preparation.md)的具体章节,了解[设备输入](camera-device-input.md)、[会话管理](camera-session-management.md)、[录像](camera-recording.md)等单个流程。 7 8## 开发流程 9 10在获取到相机支持的输出流能力后,开始创建录像流,开发流程如下。 11 12 13 14 15## 完整示例 16Context获取方式请参考:[获取UIAbility的上下文信息](../../application-models/uiability-usage.md#获取uiability的上下文信息)。 17 18```ts 19import { camera } from '@kit.CameraKit'; 20import { BusinessError } from '@kit.BasicServicesKit'; 21import { media } from '@kit.MediaKit'; 22import { common } from '@kit.AbilityKit'; 23import { photoAccessHelper } from '@kit.MediaLibraryKit'; 24import { fileIo as fs } from '@kit.CoreFileKit'; 25 26async function videoRecording(context: common.Context, surfaceId: string): Promise<void> { 27 // 创建CameraManager对象 28 let cameraManager: camera.CameraManager = camera.getCameraManager(context); 29 if (!cameraManager) { 30 console.error("camera.getCameraManager error"); 31 return; 32 } 33 34 // 监听相机状态变化 35 cameraManager.on('cameraStatus', (err: BusinessError, cameraStatusInfo: camera.CameraStatusInfo) => { 36 if (err !== undefined && err.code !== 0) { 37 console.error('cameraStatus with errorCode = ' + err.code); 38 return; 39 } 40 console.info(`camera : ${cameraStatusInfo.camera.cameraId}`); 41 console.info(`status: ${cameraStatusInfo.status}`); 42 }); 43 44 // 获取相机列表 45 let cameraArray: Array<camera.CameraDevice> = []; 46 try { 47 cameraArray = cameraManager.getSupportedCameras(); 48 } catch (error) { 49 let err = error as BusinessError; 50 console.error(`getSupportedCameras call failed. error code: ${err.code}`); 51 } 52 53 if (cameraArray.length <= 0) { 54 console.error("cameraManager.getSupportedCameras error"); 55 return; 56 } 57 58 // 获取支持的模式类型 59 let sceneModes: Array<camera.SceneMode> = cameraManager.getSupportedSceneModes(cameraArray[0]); 60 let isSupportVideoMode: boolean = sceneModes.indexOf(camera.SceneMode.NORMAL_VIDEO) >= 0; 61 if (!isSupportVideoMode) { 62 console.error('video mode not support'); 63 return; 64 } 65 66 // 获取相机设备支持的输出流能力 67 let cameraOutputCap: camera.CameraOutputCapability = cameraManager.getSupportedOutputCapability(cameraArray[0], camera.SceneMode.NORMAL_VIDEO); 68 if (!cameraOutputCap) { 69 console.error("cameraManager.getSupportedOutputCapability error") 70 return; 71 } 72 console.info("outputCapability: " + JSON.stringify(cameraOutputCap)); 73 74 let previewProfilesArray: Array<camera.Profile> = cameraOutputCap.previewProfiles; 75 if (!previewProfilesArray) { 76 console.error("createOutput previewProfilesArray == null || undefined"); 77 } 78 79 let photoProfilesArray: Array<camera.Profile> = cameraOutputCap.photoProfiles; 80 if (!photoProfilesArray) { 81 console.error("createOutput photoProfilesArray == null || undefined"); 82 } 83 84 let videoProfilesArray: Array<camera.VideoProfile> = cameraOutputCap.videoProfiles; 85 if (!videoProfilesArray) { 86 console.error("createOutput videoProfilesArray == null || undefined"); 87 } 88 // videoProfile的宽高需要与AVRecorderProfile的宽高保持一致,并且需要使用AVRecorderProfile锁支持的宽高 89 let videoSize: camera.Size = { 90 width: 640, 91 height: 480 92 } 93 let videoProfile: undefined | camera.VideoProfile = videoProfilesArray.find((profile: camera.VideoProfile) => { 94 return profile.size.width === videoSize.width && profile.size.height === videoSize.height; 95 }); 96 if (!videoProfile) { 97 console.error('videoProfile is not found'); 98 return; 99 } 100 // 配置参数以实际硬件设备支持的范围为准 101 let aVRecorderProfile: media.AVRecorderProfile = { 102 audioBitrate: 48000, 103 audioChannels: 2, 104 audioCodec: media.CodecMimeType.AUDIO_AAC, 105 audioSampleRate: 48000, 106 fileFormat: media.ContainerFormatType.CFT_MPEG_4, 107 videoBitrate: 2000000, 108 videoCodec: media.CodecMimeType.VIDEO_AVC, 109 videoFrameWidth: videoSize.width, 110 videoFrameHeight: videoSize.height, 111 videoFrameRate: 30 112 }; 113 let options: photoAccessHelper.CreateOptions = { 114 title: Date.now().toString() 115 }; 116 let accessHelper: photoAccessHelper.PhotoAccessHelper = photoAccessHelper.getPhotoAccessHelper(context); 117 let videoUri: string = await accessHelper.createAsset(photoAccessHelper.PhotoType.VIDEO, 'mp4', options); 118 let file: fs.File = fs.openSync(videoUri, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE); 119 let aVRecorderConfig: media.AVRecorderConfig = { 120 audioSourceType: media.AudioSourceType.AUDIO_SOURCE_TYPE_MIC, 121 videoSourceType: media.VideoSourceType.VIDEO_SOURCE_TYPE_SURFACE_YUV, 122 profile: aVRecorderProfile, 123 url: `fd://${file.fd.toString()}`, // 文件需先由调用者创建,赋予读写权限,将文件fd传给此参数,eg.fd://45--file:///data/media/01.mp4 124 rotation: 0, // 合理值0、90、180、270,非合理值prepare接口将报错 125 location: { latitude: 30, longitude: 130 } 126 }; 127 128 let avRecorder: media.AVRecorder | undefined = undefined; 129 try { 130 avRecorder = await media.createAVRecorder(); 131 } catch (error) { 132 let err = error as BusinessError; 133 console.error(`createAVRecorder call failed. error code: ${err.code}`); 134 } 135 136 if (avRecorder === undefined) { 137 return; 138 } 139 140 try { 141 await avRecorder.prepare(aVRecorderConfig); 142 } catch (error) { 143 let err = error as BusinessError; 144 console.error(`prepare call failed. error code: ${err.code}`); 145 } 146 147 let videoSurfaceId: string | undefined = undefined; // 该surfaceID用于传递给相机接口创造videoOutput 148 try { 149 videoSurfaceId = await avRecorder.getInputSurface(); 150 } catch (error) { 151 let err = error as BusinessError; 152 console.error(`getInputSurface call failed. error code: ${err.code}`); 153 } 154 if (videoSurfaceId === undefined) { 155 return; 156 } 157 // 创建VideoOutput对象 158 let videoOutput: camera.VideoOutput | undefined = undefined; 159 try { 160 videoOutput = cameraManager.createVideoOutput(videoProfile, videoSurfaceId); 161 } catch (error) { 162 let err = error as BusinessError; 163 console.error(`Failed to create the videoOutput instance. error: ${JSON.stringify(err)}`); 164 } 165 if (videoOutput === undefined) { 166 return; 167 } 168 // 监听视频输出错误信息 169 videoOutput.on('error', (error: BusinessError) => { 170 console.error(`Preview output error code: ${error.code}`); 171 }); 172 173 //创建会话 174 let videoSession: camera.VideoSession | undefined = undefined; 175 try { 176 videoSession = cameraManager.createSession(camera.SceneMode.NORMAL_VIDEO) as camera.VideoSession; 177 } catch (error) { 178 let err = error as BusinessError; 179 console.error(`Failed to create the session instance. error: ${JSON.stringify(err)}`); 180 } 181 if (videoSession === undefined) { 182 return; 183 } 184 // 监听session错误信息 185 videoSession.on('error', (error: BusinessError) => { 186 console.error(`Video session error code: ${error.code}`); 187 }); 188 189 // 开始配置会话 190 try { 191 videoSession.beginConfig(); 192 } catch (error) { 193 let err = error as BusinessError; 194 console.error(`Failed to beginConfig. error: ${JSON.stringify(err)}`); 195 } 196 197 // 创建相机输入流 198 let cameraInput: camera.CameraInput | undefined = undefined; 199 try { 200 cameraInput = cameraManager.createCameraInput(cameraArray[0]); 201 } catch (error) { 202 let err = error as BusinessError; 203 console.error(`Failed to createCameraInput. error: ${JSON.stringify(err)}`); 204 } 205 if (cameraInput === undefined) { 206 return; 207 } 208 // 监听cameraInput错误信息 209 let cameraDevice: camera.CameraDevice = cameraArray[0]; 210 cameraInput.on('error', cameraDevice, (error: BusinessError) => { 211 console.error(`Camera input error code: ${error.code}`); 212 }); 213 214 // 打开相机 215 try { 216 await cameraInput.open(); 217 } catch (error) { 218 let err = error as BusinessError; 219 console.error(`Failed to open cameraInput. error: ${JSON.stringify(err)}`); 220 } 221 222 // 向会话中添加相机输入流 223 try { 224 videoSession.addInput(cameraInput); 225 } catch (error) { 226 let err = error as BusinessError; 227 console.error(`Failed to add cameraInput. error: ${JSON.stringify(err)}`); 228 } 229 230 // 创建预览输出流,其中参数 surfaceId 参考下面 XComponent 组件,预览流为XComponent组件提供的surface 231 let previewOutput: camera.PreviewOutput | undefined = undefined; 232 try { 233 previewOutput = cameraManager.createPreviewOutput(previewProfilesArray[0], surfaceId); 234 } catch (error) { 235 let err = error as BusinessError; 236 console.error(`Failed to create the PreviewOutput instance. error: ${JSON.stringify(err)}`); 237 } 238 239 if (previewOutput === undefined) { 240 return; 241 } 242 // 向会话中添加预览输出流 243 try { 244 videoSession.addOutput(previewOutput); 245 } catch (error) { 246 let err = error as BusinessError; 247 console.error(`Failed to add previewOutput. error: ${JSON.stringify(err)}`); 248 } 249 250 // 向会话中添加录像输出流 251 try { 252 videoSession.addOutput(videoOutput); 253 } catch (error) { 254 let err = error as BusinessError; 255 console.error(`Failed to add videoOutput. error: ${JSON.stringify(err)}`); 256 } 257 258 // 提交会话配置 259 try { 260 await videoSession.commitConfig(); 261 } catch (error) { 262 let err = error as BusinessError; 263 console.error(`videoSession commitConfig error: ${JSON.stringify(err)}`); 264 } 265 266 // 启动会话 267 try { 268 await videoSession.start(); 269 } catch (error) { 270 let err = error as BusinessError; 271 console.error(`videoSession start error: ${JSON.stringify(err)}`); 272 } 273 274 // 启动录像输出流 275 videoOutput.start((err: BusinessError) => { 276 if (err) { 277 console.error(`Failed to start the video output. error: ${JSON.stringify(err)}`); 278 return; 279 } 280 console.info('Callback invoked to indicate the video output start success.'); 281 }); 282 283 // 开始录像 284 try { 285 await avRecorder.start(); 286 } catch (error) { 287 let err = error as BusinessError; 288 console.error(`avRecorder start error: ${JSON.stringify(err)}`); 289 } 290 291 // 停止录像输出流 292 videoOutput.stop((err: BusinessError) => { 293 if (err) { 294 console.error(`Failed to stop the video output. error: ${JSON.stringify(err)}`); 295 return; 296 } 297 console.info('Callback invoked to indicate the video output stop success.'); 298 }); 299 300 // 停止录像 301 try { 302 await avRecorder.stop(); 303 } catch (error) { 304 let err = error as BusinessError; 305 console.error(`avRecorder stop error: ${JSON.stringify(err)}`); 306 } 307 308 // 停止当前会话 309 await videoSession.stop(); 310 311 // 关闭文件 312 fs.closeSync(file); 313 314 // 释放相机输入流 315 await cameraInput.close(); 316 317 // 释放预览输出流 318 await previewOutput.release(); 319 320 // 释放录像输出流 321 await videoOutput.release(); 322 323 // 释放会话 324 await videoSession.release(); 325 326 // 会话置空 327 videoSession = undefined; 328} 329``` 330