• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 音视频录制开发指导
2
3## 简介
4
5音视频录制的主要工作是捕获音频信号,接收视频信号,完成音视频编码并保存到文件中,帮助开发者轻松实现音视频录制功能,包括开始录制、暂停录制、恢复录制、停止录制、释放资源等功能控制。它允许调用者指定录制的编码格式、封装格式、文件路径等参数。
6
7## 运作机制
8
9该模块提供了音视频录制状态变化示意图和音视频录制外部模块交互图。
10
11**图1** 音视频录制状态变化示意图
12
13![zh-ch_image_video_recorder_state_machine](figures/zh-ch_image_avrecorder_state_machine.png)
14
15**图2** 视频录制外部模块交互图--待修改
16
17![zh-ch_image_video_recorder_zero](figures/zh-ch_image_avrecorder_module_interaction.png)
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_LOCATIONohos.permission.WRITE_MEDIAohos.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_LOCATIONohos.permission.WRITE_MEDIAohos.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_LOCATIONohos.permission.WRITE_MEDIAohos.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