1# 音视频录制开发指导 2 3## 简介 4 5音视频录制的主要工作是捕获音频信号,接收视频信号,完成音视频编码并保存到文件中,帮助开发者轻松实现音视频录制功能,包括开始录制、暂停录制、恢复录制、停止录制、释放资源等功能控制。它允许调用者指定录制的编码格式、封装格式、文件路径等参数。 6 7## 运作机制 8 9该模块提供了音视频录制状态变化示意图和音视频录制外部模块交互图。 10 11**图1** 音视频录制状态变化示意图 12 13 14 15**图2** 视频录制外部模块交互图--待修改 16 17 18 19**说明**:音频录制时,框架层会通过Native Framework的媒体服务,调用音频子系统通过音频HDI捕获音频数据,通过软件编码封装后保存至文件中,实现音频录制功能。视频录制时,由相机子系统通过视频HDI捕获图像数据,媒体服务将图像数据通过视频编码HDI编码,再将编码后的图像数据封装至文件中,实现视频录制功能。通过音视频录制组合,可分别实现纯音频录制、纯视频录制,音视频录制。 20 21## 约束与限制 22 23涉及音频录制时,需要先对所开发的应用配置麦克风权限(ohos.permission.MICROPHONE),权限配置相关内容可参考:[访问控制权限申请指导](../security/accesstoken-guidelines.md)。 24 25涉及相机视频录制时,需要与相机模块配合,相机模块接口开放状态以及使用详情见[相机管理](../reference/apis/js-apis-camera.md)。 26 27## 开发指导 28 29详细API含义可参考:[媒体服务API文档AVRecorder](../reference/apis/js-apis-media.md#avrecorder9) 30 31媒体库相关流程含义可参考:[媒体库管理](../reference/apis/js-apis-medialibrary.md) 32 33### 音视频录制全流程场景 34 35音视频录制全流程场景包含:创建实例、设置录制参数、获取输入surface、开始录制、暂停录制、恢复录制、停止录制、释放资源等流程。 36 37音频录制相关配置参数范围,受到设备编解码性能,音频子系统性能等综合限制。 38 39视频录制相关配置参数范围,受到设备编解码性能,相机子系统性能等综合限制。 40 41``` 42import media from '@ohos.multimedia.media' 43import camera from '@ohos.multimedia.camera' 44import mediaLibrary from '@ohos.multimedia.mediaLibrary' 45 46export class AVRecorderDemo { 47 private testFdNumber; // 用于保存fd地址 48 49 // 获取录制的音频文件fileName对应的fd,需借助媒体库能力。使用mediaLibrary需要添加以下权限, ohos.permission.MEDIA_LOCATION、ohos.permission.WRITE_MEDIA、ohos.permission.READ_MEDIA. 50 async getFd(fileName) { 51 // 实现方式参考媒体库资料文档。 52 this.testFdNumber = "fd://" + fdNumber.toString(); // e.g. fd://54 53 } 54 55 // 当promise接口发生错误上上报的错误回调接口 56 failureCallback(error) { 57 console.info('error happened, error message is ' + error.message); 58 } 59 60 // 当promise接口发生异常时,系统调用的错误回调接口 61 catchCallback(error) { 62 console.info('catch error happened, error message is ' + error.message); 63 } 64 65 async AVRecorderDemo() { 66 let AVRecorder; // AVRecorder空对象在createAVRecorder成功后赋值 67 let surfaceID; // 从getInputSurface获取surfaceID,传递给相机的videoOutput 68 await this.getFd('01.mp4'); 69 70 // 音视频录制相关参数配置,配置参数以实际硬件设备支持的范围为准 71 let avProfile = { 72 audioBitrate : 48000, 73 audioChannels : 2, 74 audioCodec : media.CodecMimeType.AUDIO_AAC, 75 audioSampleRate : 48000, 76 fileFormat : media.ContainerFormatType.CFT_MPEG_4, 77 videoBitrate : 2000000, 78 videoCodec : media.CodecMimeType.VIDEO_AVC, 79 videoFrameWidth : 640, 80 videoFrameHeight : 480, 81 videoFrameRate : 30 82 } 83 let avConfig = { 84 audioSourceType : media.AudioSourceType.AUDIO_SOURCE_TYPE_MIC, 85 videoSourceType : media.VideoSourceType.VIDEO_SOURCE_TYPE_SURFACE_YUV, 86 profile : avProfile, 87 url : 'fd://', 88 rotation : 0, 89 location : { latitude : 30, longitude : 130 } 90 } 91 92 // 创建AVRecorder对象 93 await media.createAVRecorder().then((recorder) => { 94 console.info('case createAVRecorder called'); 95 if (typeof (recorder) != 'undefined') { 96 AVRecorder = recorder; 97 console.info('createAVRecorder success'); 98 } else { 99 console.info('createAVRecorder failed'); 100 } 101 }, this.failureCallback).catch(this.catchCallback); 102 103 // 对象创建成功后创建on('stateChange')和on('error')监听回调用于监听状态机变化和错误上报 104 AVRecorder.on('stateChange', async (state, reason) => { 105 console.info('case state has changed, new state is :' + state); 106 switch (state) { 107 // 用户可以根据需求在不同状态设置自己想要进行的行为 108 case 'idle': 109 // 调用rest接口后触发idle状态;create后也在idle状态 110 break; 111 case 'prepared': 112 // 调用prepare接口后触发prepared状态; 113 break; 114 case 'started': 115 // 调用start接口后触发started状态; 116 break; 117 case 'paused': 118 // 调用pause接口后触发paused状态; 119 break; 120 case 'stopped': 121 // 调用stop接口后触发stopped状态; 122 break; 123 case 'released': 124 // 调用release接口后触发released状态; 125 break; 126 case 'error': 127 // error状态说明底层出错,用户需排查错误,重新创建avRecorder; 128 break; 129 default: 130 console.info('case state is unknown'); 131 } 132 }); 133 AVRecorder.on('error', (err) => { 134 // 监听非接口类错误上报 135 console.info('case avRecorder.on(error) called, errMessage is ' + err.message); 136 }); 137 138 // 调用prepare完成音频录制前的准备工作;底层实际是根据prepare的入参来判断是音频录制、视频录制还是音视频录制 139 await AVRecorder.prepare(avConfig).then(() => { 140 console.info('prepare success'); 141 }, this.failureCallback).catch(this.catchCallback); 142 143 // 包含视频的录制需要调用getInputSurface接口,并将返回值surfaceID传递给camera相关接口 144 await AVRecorder.getInputSurface().then((surface) => { 145 console.info('getInputSurface success'); 146 surfaceID = surface; // surfaceID给camera的createVideoOutput()作为其中的一个入参 147 }, this.failureCallback).catch(this.catchCallback); 148 149 // 视频录制依赖相机相关接口,以下需要先调用相机起流接口后才能继续执行,具体的相机接口调用请参考sample用例 150 // 视频录制启动接口 151 await AVRecorder.start().then(() => { 152 console.info('start success'); 153 }, this.failureCallback).catch(this.catchCallback); 154 155 // 调用pause接口时需要暂停camera出流 156 await AVRecorder.pause().then(() => { 157 console.info('pause success'); 158 }, this.failureCallback).catch(this.catchCallback); 159 160 // 调用resume接口时需要恢复camera出流 161 await AVRecorder.resume().then(() => { 162 console.info('resume success'); 163 }, this.failureCallback).catch(this.catchCallback); 164 165 // 停止camera出流后,停止视频录制 166 await AVRecorder.stop().then(() => { 167 console.info('stop success'); 168 }, this.failureCallback).catch(this.catchCallback); 169 170 // 重置录制相关配置 171 await AVRecorder.reset().then(() => { 172 console.info('reset success'); 173 }, this.failureCallback).catch(this.catchCallback); 174 175 // 关闭监听回调,如果用户不自行调用off接口,在调用release后,设置的回调接口也会无效 176 AVRecorder.off('stateChange'); 177 AVRecorder.off('error'); 178 179 // 释放视频录制相关资源并释放camera对象相关资源 180 await AVRecorder.release().then(() => { 181 console.info('release success'); 182 }, this.failureCallback).catch(this.catchCallback); 183 184 // 相关对象置null 185 AVRecorder = undefined; 186 surfaceID = undefined; 187 } 188} 189``` 190 191### 纯音频录制全流程场景 192 193纯音频录制全流程场景包含:创建实例、设置录制参数、开始录制、暂停录制、恢复录制、停止录制、释放资源等流程。 194 195音频录制相关配置参数范围,受到设备编解码性能,音频子系统性能等综合限制。 196 197``` 198import media from '@ohos.multimedia.media' 199import mediaLibrary from '@ohos.multimedia.mediaLibrary' 200 201export class AudioRecorderDemo { 202 private testFdNumber; // 用于保存fd地址 203 204 // 获取录制的音频文件fileName对应的fd,需借助媒体库能力。使用mediaLibrary需要添加以下权限, ohos.permission.MEDIA_LOCATION、ohos.permission.WRITE_MEDIA、ohos.permission.READ_MEDIA 205 async getFd(fileName) { 206 // 实现方式参考媒体库资料文档。 207 this.testFdNumber = "fd://" + fdNumber.toString(); // e.g. fd://54 208 } 209 210 // 当promise接口发生错误上报的错误回调接口 211 failureCallback(error) { 212 console.info('error happened, error message is ' + error.message); 213 } 214 215 // 当promise接口发生异常时,系统调用的错误回调接口 216 catchCallback(error) { 217 console.info('catch error happened, error message is ' + error.message); 218 } 219 220 async audioRecorderDemo() { 221 let audioRecorder; // audioRecorder空对象在createAVRecorder成功后赋值 222 await this.getFd('01.m4a'); 223 // 音频录制相关参数配置 224 let audioProfile = { 225 audioBitrate : 48000, 226 audioChannels : 2, 227 audioCodec : media.CodecMimeType.AUDIO_AAC, 228 audioSampleRate : 48000, 229 fileFormat : media.ContainerFormatType.CFT_MPEG_4, 230 } 231 let audioConfig = { 232 audioSourceType : media.AudioSourceType.AUDIO_SOURCE_TYPE_MIC, 233 profile : audioProfile, 234 url : this.testFdNumber, 235 rotation : 0, 236 location : { latitude : 30, longitude : 130 } 237 } 238 239 // 创建audioRecorder对象 240 await media.createAVRecorder().then((recorder) => { 241 console.info('case createAVRecorder called'); 242 if (typeof (recorder) != 'undefined') { 243 audioRecorder = recorder; 244 console.info('createAudioRecorder success'); 245 } else { 246 console.info('createAudioRecorder failed'); 247 } 248 }, this.failureCallback).catch(this.catchCallback); 249 250 // 对象创建成功后创建on('stateChange')和on('error')监听回调用于监听状态机变化和错误上报 251 audioRecorder.on('stateChange', async (state, reason) => { 252 console.info('case state has changed, new state is :' + state); 253 switch (state) { 254 // 用户可以根据需求在不同状态设置自己想要进行的行为 255 case 'idle': 256 // 调用rest接口后触发idle状态;create后也在idle状态 257 break; 258 case 'prepared': 259 // 调用prepare接口后触发prepared状态; 260 break; 261 case 'started': 262 // 调用start接口后触发started状态; 263 break; 264 case 'paused': 265 // 调用pause接口后触发paused状态; 266 break; 267 case 'stopped': 268 // 调用stop接口后触发stopped状态; 269 break; 270 case 'released': 271 // 调用release接口后触发released状态; 272 break; 273 case 'error': 274 // error状态说明底层出错,用户需排查错误,重新创建avRecorder; 275 break; 276 default: 277 console.info('case state is unknown'); 278 } 279 }); 280 audioRecorder.on('error', (err) => { 281 // 监听非接口类错误上报 282 console.info('case avRecorder.on(error) called, errMessage is ' + err.message); 283 }); 284 285 // 调用prepare完成音频录制前的准备工作;底层实际是根据prepare的入参来判断是音频录制、视频录制还是音视频录制 286 await audioRecorder.prepare(audioConfig).then(() => { 287 console.info('prepare success'); 288 }, this.failureCallback).catch(this.catchCallback); 289 290 // 调用start接口启动音频录制 291 await audioRecorder.start().then(() => { 292 console.info('start success'); 293 }, this.failureCallback).catch(this.catchCallback); 294 295 // 调用pause接口暂停音频录制 296 await audioRecorder.pause().then(() => { 297 console.info('pause success'); 298 }, this.failureCallback).catch(this.catchCallback); 299 300 // 调用resume接口恢复音频录制 301 await audioRecorder.resume().then(() => { 302 console.info('resume success'); 303 }, this.failureCallback).catch(this.catchCallback); 304 305 // 调用stop接口停止音频录制 306 await audioRecorder.stop().then(() => { 307 console.info('stop success'); 308 }, this.failureCallback).catch(this.catchCallback); 309 310 // 调用reset接口重置录制相关配置 311 await audioRecorder.reset().then(() => { 312 console.info('reset success'); 313 }, this.failureCallback).catch(this.catchCallback); 314 315 // 关闭监听回调,如果用户不自行调用off接口,在调用release后,设置的回调接口也会无效 316 avRecorder.off('stateChange'); 317 avRecorder.off('error'); 318 319 // 调用release接口释放音频录制相关资源 320 await audioRecorder.release().then(() => { 321 console.info('release success'); 322 }, this.failureCallback).catch(this.catchCallback); 323 324 // 相关对象置null 325 audioRecorder = undefined; 326 } 327} 328 329``` 330 331### 纯视频录制全流程场景 332 333纯视频录制全流程场景包含:创建实例、设置录制参数、获取输入surface、开始录制、暂停录制、恢复录制、停止录制、释放资源等流程。 334 335视频录制相关配置参数范围,受到设备编解码性能,相机子系统性能等综合限制。 336 337``` 338import media from '@ohos.multimedia.media' 339import camera from '@ohos.multimedia.camera' 340import mediaLibrary from '@ohos.multimedia.mediaLibrary' 341 342export class VideoRecorderDemo { 343 private testFdNumber; // 用于保存fd地址 344 345 // 获取录制的音频文件fileName对应的fd,需借助媒体库能力。使用mediaLibrary需要添加以下权限, ohos.permission.MEDIA_LOCATION、ohos.permission.WRITE_MEDIA、ohos.permission.READ_MEDIA. 346 async getFd(fileName) { 347 // 实现方式参考媒体库资料文档。 348 this.testFdNumber = "fd://" + fdNumber.toString(); // e.g. fd://54 349 } 350 351 // 当promise接口发生错误上上报的错误回调接口 352 failureCallback(error) { 353 console.info('error happened, error message is ' + error.message); 354 } 355 356 // 当promise接口发生异常时,系统调用的错误回调接口 357 catchCallback(error) { 358 console.info('catch error happened, error message is ' + error.message); 359 } 360 361 async videoRecorderDemo() { 362 let videoRecorder; // videoRecorder空对象在createAVRecorder成功后赋值 363 let surfaceID; // 从getInputSurface获取surfaceID,传递给相机的videoOutput 364 await this.getFd('01.mp4'); 365 366 // 纯视频录制相关参数配置,配置参数以实际硬件设备支持的范围为准 367 let videoProfile = { 368 fileFormat : media.ContainerFormatType.CFT_MPEG_4, 369 videoBitrate : 2000000, 370 videoCodec : media.CodecMimeType.VIDEO_AVC, 371 videoFrameWidth : 640, 372 videoFrameHeight : 480, 373 videoFrameRate : 30 374 } 375 let videoConfig = { 376 videoSourceType : media.VideoSourceType.VIDEO_SOURCE_TYPE_SURFACE_YUV, 377 profile : videoProfile, 378 url : 'fd://', 379 rotation : 0, 380 location : { latitude : 30, longitude : 130 } 381 } 382 383 // 创建videoRecorder对象 384 await media.createAVRecorder().then((recorder) => { 385 console.info('case createVideoRecorder called'); 386 if (typeof (recorder) != 'undefined') { 387 videoRecorder = recorder; 388 console.info('createVideoRecorder success'); 389 } else { 390 console.info('createVideoRecorder failed'); 391 } 392 }, this.failureCallback).catch(this.catchCallback); 393 394 // 对象创建成功后创建on('stateChange')和on('error')监听回调用于监听状态机变化和错误上报 395 videoRecorder.on('stateChange', async (state, reason) => { 396 console.info('case state has changed, new state is :' + state); 397 switch (state) { 398 // 用户可以根据需求在不同状态设置自己想要进行的行为 399 case 'idle': 400 // 调用rest接口后触发idle状态;create后也在idle状态 401 break; 402 case 'prepared': 403 // 调用prepare接口后触发prepared状态; 404 break; 405 case 'started': 406 // 调用start接口后触发started状态; 407 break; 408 case 'paused': 409 // 调用pause接口后触发paused状态; 410 break; 411 case 'stopped': 412 // 调用stop接口后触发stopped状态; 413 break; 414 case 'released': 415 // 调用release接口后触发released状态; 416 break; 417 case 'error': 418 // error状态说明底层出错,用户需排查错误,重新创建avRecorder; 419 break; 420 default: 421 console.info('case state is unknown'); 422 } 423 }); 424 videoRecorder.on('error', (err) => { 425 // 监听非接口类错误上报 426 console.info('case avRecorder.on(error) called, errMessage is ' + err.message); 427 }); 428 429 // 调用prepare完成音频录制前的准备工作;底层实际是根据prepare的入参来判断是音频录制、视频录制还是音视频录制 430 await videoRecorder.prepare(videoConfig).then(() => { 431 console.info('prepare success'); 432 }, this.failureCallback).catch(this.catchCallback); 433 434 // 包含视频的录制需要调用getInputSurface接口,并将返回值surfaceID传递给camera相关接口 435 await videoRecorder.getInputSurface().then((surface) => { 436 console.info('getInputSurface success'); 437 surfaceID = surface; // surfaceID给camera的createVideoOutput()作为其中的一个入参 438 }, this.failureCallback).catch(this.catchCallback); 439 440 // 视频录制依赖相机相关接口,以下需要先调用相机起流接口后才能继续执行,具体的相机接口调用请参考sample用例 441 // 视频录制启动接口 442 await videoRecorder.start().then(() => { 443 console.info('start success'); 444 }, this.failureCallback).catch(this.catchCallback); 445 446 // 调用pause接口时需要暂停camera出流 447 await videoRecorder.pause().then(() => { 448 console.info('pause success'); 449 }, this.failureCallback).catch(this.catchCallback); 450 451 // 调用resume接口时需要恢复camera出流 452 await videoRecorder.resume().then(() => { 453 console.info('resume success'); 454 }, this.failureCallback).catch(this.catchCallback); 455 456 // 停止camera出流后,停止视频录制 457 await videoRecorder.stop().then(() => { 458 console.info('stop success'); 459 }, this.failureCallback).catch(this.catchCallback); 460 461 // 重置录制相关配置 462 await videoRecorder.reset().then(() => { 463 console.info('reset success'); 464 }, this.failureCallback).catch(this.catchCallback); 465 466 // 关闭监听回调,如果用户不自行调用off接口,在调用release后,设置的回调接口也会无效 467 videoRecorder.off('stateChange'); 468 videoRecorder.off('error'); 469 470 // 释放视频录制相关资源并释放camera对象相关资源 471 await videoRecorder.release().then(() => { 472 console.info('release success'); 473 }, this.failureCallback).catch(this.catchCallback); 474 475 // 相关对象置null 476 videoRecorder = undefined; 477 surfaceID = undefined; 478 } 479} 480``` 481 482 483 484### 音视频录制APP 485 486音视频录制APP案例包含:创建实例、设置录制参数、获取输入surface、开始录制、暂停录制、恢复录制、停止录制、释放资源等流程。 487 488详细代码可参考:[AVRecorderDemo]([multimedia_player_framework: Implementation of media playback and recording | 媒体播放和录制功能实现 - Gitee.com](https://gitee.com/openharmony/multimedia_player_framework/tree/master/test/appdemo/AVRecorderDemo)) 489