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如需要将视频保存到媒体库中可参考[保存媒体库资源](../medialibrary/photoAccessHelper-savebutton.md#保存媒体库资源)。 10## 开发流程 11 12在获取到相机支持的输出流能力后,开始创建录像流,开发流程如下。 13 14 15 16 17## 完整示例 18Context获取方式请参考:[获取UIAbility的上下文信息](../../application-models/uiability-usage.md#获取uiability的上下文信息)。 19 20```ts 21import { camera } from '@kit.CameraKit'; 22import { BusinessError } from '@kit.BasicServicesKit'; 23import { media } from '@kit.MediaKit'; 24import { common } from '@kit.AbilityKit'; 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 || videoProfilesArray.length === 0) { 87 console.error("createOutput videoProfilesArray == null || undefined"); 88 } 89 90 // videoProfile的宽高需要与AVRecorderProfile的宽高保持一致,并且需要使用AVRecorderProfile所支持的宽高。 91 // 示例代码默认选择第一个videoProfile,实际开发需根据所需筛选videoProfile。 92 let videoProfile: camera.VideoProfile = videoProfilesArray[0]; 93 let isHdr = videoProfile.format === camera.CameraFormat.CAMERA_FORMAT_YCBCR_P010 || videoProfile.format === camera.CameraFormat.CAMERA_FORMAT_YCRCB_P010; 94 // 配置参数以实际硬件设备支持的范围为准。 95 let aVRecorderProfile: media.AVRecorderProfile = { 96 audioBitrate: 48000, 97 audioChannels: 2, 98 audioCodec: media.CodecMimeType.AUDIO_AAC, 99 audioSampleRate: 48000, 100 fileFormat: media.ContainerFormatType.CFT_MPEG_4, 101 videoBitrate: 2000000, 102 videoCodec: isHdr ? media.CodecMimeType.VIDEO_HEVC : media.CodecMimeType.VIDEO_AVC, 103 videoFrameWidth: videoProfile.size.width, 104 videoFrameHeight: videoProfile.size.height, 105 videoFrameRate: 30, 106 isHdr: isHdr 107 }; 108 let videoUri: string = `file://${context.filesDir}/${Date.now()}.mp4`; // 本地沙箱路径。 109 let file: fs.File = fs.openSync(videoUri, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE); 110 let aVRecorderConfig: media.AVRecorderConfig = { 111 audioSourceType: media.AudioSourceType.AUDIO_SOURCE_TYPE_MIC, 112 videoSourceType: media.VideoSourceType.VIDEO_SOURCE_TYPE_SURFACE_YUV, 113 profile: aVRecorderProfile, 114 url: `fd://${file.fd.toString()}`, // 文件需先由调用者创建,赋予读写权限,将文件fd传给此参数,eg.fd://45--file:///data/media/01.mp4 115 rotation: 0, // 合理值0、90、180、270,非合理值prepare接口将报错。 116 location: { latitude: 30, longitude: 130 } 117 }; 118 119 let avRecorder: media.AVRecorder | undefined = undefined; 120 try { 121 avRecorder = await media.createAVRecorder(); 122 } catch (error) { 123 let err = error as BusinessError; 124 console.error(`createAVRecorder call failed. error code: ${err.code}`); 125 } 126 127 if (avRecorder === undefined) { 128 return; 129 } 130 131 try { 132 await avRecorder.prepare(aVRecorderConfig); 133 } catch (error) { 134 let err = error as BusinessError; 135 console.error(`prepare call failed. error code: ${err.code}`); 136 } 137 138 let videoSurfaceId: string | undefined = undefined; // 该surfaceID用于传递给相机接口创造videoOutput。 139 try { 140 videoSurfaceId = await avRecorder.getInputSurface(); 141 } catch (error) { 142 let err = error as BusinessError; 143 console.error(`getInputSurface call failed. error code: ${err.code}`); 144 } 145 if (videoSurfaceId === undefined) { 146 return; 147 } 148 // 创建VideoOutput对象。 149 let videoOutput: camera.VideoOutput | undefined = undefined; 150 try { 151 videoOutput = cameraManager.createVideoOutput(videoProfile, videoSurfaceId); 152 } catch (error) { 153 let err = error as BusinessError; 154 console.error(`Failed to create the videoOutput instance. error: ${err}`); 155 } 156 if (videoOutput === undefined) { 157 return; 158 } 159 // 监听视频输出错误信息。 160 videoOutput.on('error', (error: BusinessError) => { 161 console.error(`Preview output error code: ${error.code}`); 162 }); 163 164 //创建会话。 165 let videoSession: camera.VideoSession | undefined = undefined; 166 try { 167 videoSession = cameraManager.createSession(camera.SceneMode.NORMAL_VIDEO) as camera.VideoSession; 168 } catch (error) { 169 let err = error as BusinessError; 170 console.error(`Failed to create the session instance. error: ${err}`); 171 } 172 if (videoSession === undefined) { 173 return; 174 } 175 // 监听session错误信息。 176 videoSession.on('error', (error: BusinessError) => { 177 console.error(`Video session error code: ${error.code}`); 178 }); 179 180 // 开始配置会话。 181 try { 182 videoSession.beginConfig(); 183 } catch (error) { 184 let err = error as BusinessError; 185 console.error(`Failed to beginConfig. error: ${err}`); 186 } 187 188 // 创建相机输入流。 189 let cameraInput: camera.CameraInput | undefined = undefined; 190 try { 191 cameraInput = cameraManager.createCameraInput(cameraArray[0]); 192 } catch (error) { 193 let err = error as BusinessError; 194 console.error(`Failed to createCameraInput. error: ${err}`); 195 } 196 if (cameraInput === undefined) { 197 return; 198 } 199 // 监听cameraInput错误信息。 200 let cameraDevice: camera.CameraDevice = cameraArray[0]; 201 cameraInput.on('error', cameraDevice, (error: BusinessError) => { 202 console.error(`Camera input error code: ${error.code}`); 203 }); 204 205 // 打开相机。 206 try { 207 await cameraInput.open(); 208 } catch (error) { 209 let err = error as BusinessError; 210 console.error(`Failed to open cameraInput. error: ${err}`); 211 } 212 213 // 向会话中添加相机输入流。 214 try { 215 videoSession.addInput(cameraInput); 216 } catch (error) { 217 let err = error as BusinessError; 218 console.error(`Failed to add cameraInput. error: ${err}`); 219 } 220 221 // 创建预览输出流,其中参数 surfaceId 参考下面 XComponent 组件,预览流为XComponent组件提供的surface。 222 let previewOutput: camera.PreviewOutput | undefined = undefined; 223 let previewProfile = previewProfilesArray.find((previewProfile: camera.Profile) => { 224 return Math.abs((previewProfile.size.width / previewProfile.size.height) - (videoProfile.size.width / videoProfile.size.height)) < Number.EPSILON; 225 }); // 筛选与录像分辨率宽高比一致的预览分辨率。 226 if (previewProfile === undefined) { 227 return; 228 } 229 try { 230 previewOutput = cameraManager.createPreviewOutput(previewProfile, surfaceId); 231 } catch (error) { 232 let err = error as BusinessError; 233 console.error(`Failed to create the PreviewOutput instance. error: ${err}`); 234 } 235 if (previewOutput === undefined) { 236 return; 237 } 238 239 // 向会话中添加预览输出流。 240 try { 241 videoSession.addOutput(previewOutput); 242 } catch (error) { 243 let err = error as BusinessError; 244 console.error(`Failed to add previewOutput. error: ${err}`); 245 } 246 247 // 向会话中添加录像输出流。 248 try { 249 videoSession.addOutput(videoOutput); 250 } catch (error) { 251 let err = error as BusinessError; 252 console.error(`Failed to add videoOutput. error: ${err}`); 253 } 254 255 // 提交会话配置。 256 try { 257 await videoSession.commitConfig(); 258 } catch (error) { 259 let err = error as BusinessError; 260 console.error(`videoSession commitConfig error: ${err}`); 261 } 262 263 // 启动会话。 264 try { 265 await videoSession.start(); 266 } catch (error) { 267 let err = error as BusinessError; 268 console.error(`videoSession start error: ${err}`); 269 } 270 271 // 启动录像输出流。 272 videoOutput.start((err: BusinessError) => { 273 if (err) { 274 console.error(`Failed to start the video output. error: ${err}`); 275 return; 276 } 277 console.info('Callback invoked to indicate the video output start success.'); 278 }); 279 280 // 开始录像。 281 try { 282 await avRecorder.start(); 283 } catch (error) { 284 let err = error as BusinessError; 285 console.error(`avRecorder start error: ${err}`); 286 } 287 288 // 停止录像输出流。 289 videoOutput.stop((err: BusinessError) => { 290 if (err) { 291 console.error(`Failed to stop the video output. error: ${err}`); 292 return; 293 } 294 console.info('Callback invoked to indicate the video output stop success.'); 295 }); 296 297 // 停止录像。 298 try { 299 await avRecorder.stop(); 300 } catch (error) { 301 let err = error as BusinessError; 302 console.error(`avRecorder stop error: ${err}`); 303 } 304 305 // 停止当前会话。 306 await videoSession.stop(); 307 308 // 关闭文件。 309 fs.closeSync(file); 310 311 // 释放相机输入流。 312 await cameraInput.close(); 313 314 // 释放预览输出流。 315 await previewOutput.release(); 316 317 // 释放录像输出流。 318 await videoOutput.release(); 319 320 // 释放会话。 321 await videoSession.release(); 322 323 // 会话置空。 324 videoSession = undefined; 325} 326``` 327