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