• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 音频编码
2
3<!--Kit: AVCodec Kit-->
4<!--Subsystem: Multimedia-->
5<!--Owner: @mr-chencxy-->
6<!--Designer: @dpy2650--->
7<!--Tester: @baotianhao-->
8<!--Adviser: @zengyawen-->
9
10开发者可以调用本模块的Native API接口,完成音频编码,即将音频PCM编码压缩成不同的格式。
11
12接口不限制PCM数据的来源,开发者可以调用麦克风录制获取、也可以导入编辑后的PCM数据,通过音频编码,输出对应格式的码流,最后封装为目标格式文件。
13
14当前支持的编码能力请参考[AVCodec支持的格式](avcodec-support-formats.md#音频编码)。
15
16**适用场景**
17
18- 音频录制
19
20  通过录制传入PCM,然后编码出对应格式的码流,最后[封装](audio-video-muxer.md)成想要的格式。
21- 音频编辑
22
23  编辑PCM后导出音频文件的场景,需要编码成对应音频格式后再[封装](audio-video-muxer.md)成文件。
24> **说明:**
25>
26> - AAC编码器默认采用的VBR可变码率模式,与配置的预期参数可能存在偏差。
27> - AAC编码器默认输出携带ADTS头部,帧数据的前7字节为ADTS头部。
28
29## 开发指导
30
31详细的API说明请参考[API文档](../../reference/apis-avcodec-kit/_audio_codec.md)。
32
33参考以下示例代码,完成音频编码的全流程,包括:创建编码器、设置编码参数(采样率/码率/声道数等)、开始、刷新、重置、销毁资源。
34
35在应用开发过程中,开发者应按一定顺序调用方法,执行对应操作,否则系统可能会抛出异常或生成其他未定义的行为。具体顺序可参考下列开发步骤及对应说明。
36
37如下为音频编码调用关系图:
38
39- 虚线表示可选。
40
41- 实线表示必选。
42
43![Invoking relationship of audio encode stream](figures/audio-codec.png)
44
45### 在 CMake 脚本中链接动态库
46
47```cmake
48target_link_libraries(sample PUBLIC libnative_media_codecbase.so)
49target_link_libraries(sample PUBLIC libnative_media_core.so)
50target_link_libraries(sample PUBLIC libnative_media_acodec.so)
51```
52> **说明:**
53>
54> 上述'sample'字样仅为示例,此处由开发者根据实际工程目录自定义。
55
56### 开发步骤
57
581. 添加头文件。
59
60    ```cpp
61    #include <multimedia/player_framework/native_avcodec_audiocodec.h>
62    #include <multimedia/native_audio_channel_layout.h>
63    #include <multimedia/player_framework/native_avcapability.h>
64    #include <multimedia/player_framework/native_avcodec_base.h>
65    #include <multimedia/player_framework/native_avformat.h>
66    #include <multimedia/player_framework/native_avbuffer.h>
67    ```
68
692. 创建编码器实例对象,OH_AVCodec *为编码器实例指针。
70
71   应用可以通过媒体类型或编解码器名称创建编码器。
72
73   方法一:通过 Mimetype 创建编码器。
74    ```cpp
75    // 设置判定是否为编码;设置true表示当前是编码。
76    bool isEncoder = true;
77    // 通过媒体类型创建编码器。
78    OH_AVCodec *audioEnc_ = OH_AudioCodec_CreateByMime(OH_AVCODEC_MIMETYPE_AUDIO_AAC, isEncoder);
79    ```
80   方法二:通过 codec name 创建编码器。
81    ```cpp
82    // 通过 codecname 创建编码器。
83    OH_AVCapability *capability = OH_AVCodec_GetCapability(OH_AVCODEC_MIMETYPE_AUDIO_AAC, true);
84    const char *name = OH_AVCapability_GetName(capability);
85    OH_AVCodec *audioEnc_ = OH_AudioCodec_CreateByName(name);
86    ```
87   添加头文件和命名空间:
88    ```cpp
89    #include <mutex>
90    #include <queue>
91    // c++标准库命名空间。
92    using namespace std;
93    ```
94   示例代码:
95    ```cpp
96    // 初始化队列。
97    class AEncBufferSignal {
98    public:
99        std::mutex inMutex_;
100        std::mutex outMutex_;
101        std::mutex startMutex_;
102        std::condition_variable inCond_;
103        std::condition_variable outCond_;
104        std::condition_variable startCond_;
105        std::queue<uint32_t> inQueue_;
106        std::queue<uint32_t> outQueue_;
107        std::queue<OH_AVBuffer *> inBufferQueue_;
108        std::queue<OH_AVBuffer *> outBufferQueue_;
109    };
110    AEncBufferSignal *signal_;
111    ```
112
1133. 调用OH_AudioCodec_RegisterCallback()注册回调函数。
114
115   注册回调函数指针集合OH_AVCodecCallback,包括:
116
117   - OH_AVCodecOnError:编码器运行错误。
118   - OH_AVCodecOnStreamChanged:音频编码器暂未支持此回调。
119   - OH_AVCodecOnNeedInputBuffer:运行过程中需要新的输入数据,即编码器已准备好,可以输入PCM数据。
120   - OH_AVCodecOnNewOutputBuffer:运行过程中产生了新的输出数据,即编码完成。
121
122   开发者可以通过处理该回调报告的信息,确保编码器正常运转。
123
124   > **注意:**
125   > 回调中不建议进行耗时操作。
126
127    ```cpp
128    // OH_AVCodecOnError回调函数的实现。
129    static void OnError(OH_AVCodec *codec, int32_t errorCode, void *userData)
130    {
131        (void)codec;
132        (void)errorCode;
133        (void)userData;
134    }
135    // OH_AVCodecOnStreamChanged回调函数的实现。
136    static void OnOutputFormatChanged(OH_AVCodec *codec, OH_AVFormat *format, void *userData)
137    {
138        (void)codec;
139        (void)format;
140        (void)userData;
141    }
142    // OH_AVCodecOnNeedInputBuffer回调函数的实现。
143    static void OnInputBufferAvailable(OH_AVCodec *codec, uint32_t index, OH_AVBuffer *data, void *userData)
144    {
145        (void)codec;
146        // 编码输入码流送入InputBuffer队列。
147        AEncBufferSignal *signal = static_cast<AEncBufferSignal *>(userData);
148        unique_lock<mutex> lock(signal->inMutex_);
149        signal->inQueue_.push(index);
150        signal->inBufferQueue_.push(data);
151        signal->inCond_.notify_all();
152    }
153    // OH_AVCodecOnNewOutputBuffer回调函数的实现。
154    static void OnOutputBufferAvailable(OH_AVCodec *codec, uint32_t index, OH_AVBuffer *data, void *userData)
155    {
156        (void)codec;
157        // 将对应输出buffer的index送入OutputQueue_队列。
158        // 将对应编码完成的数据data送入outBuffer队列。
159        AEncBufferSignal *signal = static_cast<AEncBufferSignal *>(userData);
160        unique_lock<mutex> lock(signal->outMutex_);
161        signal->outQueue_.push(index);
162        signal->outBufferQueue_.push(data);
163        signal->outCond_.notify_all();
164    }
165    ```
166    配置回调:
167    ```cpp
168    signal_ = new AEncBufferSignal();
169    OH_AVCodecCallback cb_ = {&OnError, &OnOutputFormatChanged, &OnInputBufferAvailable, &OnOutputBufferAvailable};
170    // 配置异步回调。
171    int32_t ret = OH_AudioCodec_RegisterCallback(audioEnc_, cb_, signal_);
172    if (ret != AV_ERR_OK) {
173        // 异常处理。
174    }
175    ```
176
1774. 调用OH_AudioCodec_Configure设置编码器。
178
179   配置选项key值说明:
180
181   <!--RP2-->
182   |             key               |       描述       |  AAC  |  Flac | MPEG(MP3) | G711mu |
183   | ----------------------------- | :--------------: | :---: | :---: | :------: | :---: |
184   | OH_MD_KEY_AUD_SAMPLE_RATE     |      采样率      |  必须  |  必须 |   必须   |  必须  |
185   | OH_MD_KEY_AUD_CHANNEL_COUNT   |      声道数      |  必须  |  必须 |   必须   |  必须  |
186   | OH_MD_KEY_AUDIO_SAMPLE_FORMAT |  输出音频流格式   |  必须  |  必须 |   必须   |  必须  |
187   | OH_MD_KEY_BITRATE             |       码率       |  可选  |  必须 |   必须   |   -   |
188   | OH_MD_KEY_CHANNEL_LAYOUT      |     声道布局     |  可选  |  必须 |    -     |   -   |
189   | OH_MD_KEY_MAX_INPUT_SIZE      |   最大输入长度    |  可选  |  可选 |   可选   |  可选  |
190   | OH_MD_KEY_COMPLIANCE_LEVEL    |    兼容性等级     |  -    |  可选 |    -     |   -    |
191   <!--RP2End-->
192
193   各音频编码类型参数范围说明:
194   | 音频编码类型 | 采样率(Hz)                                                                       |       声道数       |
195   | ----------- | ------------------------------------------------------------------------------- | :----------------: |
196   | <!--DelRow-->AAC         | 8000、11025、12000、16000、22050、24000、32000、44100、48000、64000、88200、96000 | 1、2、3、4、5、6、8 |
197   | Flac        | 8000、11025、12000、16000、22050、24000、32000、44100、48000、64000、88200、96000 |        1~8         |
198   | MP3         | 8000、11025、12000、16000、22050、24000、32000、44100、48000                     |        1~2         |
199   | G711mu      | 8000                                                                            |         1          |
200   <!--RP3--><!--RP3End-->
201
202   例如对一个44100Hz采样率、2声道立体声、SAMPLE_S16LE采样格式的PCM音频,以32000bps的码率进行AAC编码的调用流程如下:
203    <!--RP4-->
204    ```cpp
205    int32_t ret;
206    // 配置音频采样率(必须)。
207    constexpr uint32_t DEFAULT_SAMPLERATE = 44100;
208    // 配置音频码率(必须)。
209    constexpr uint64_t DEFAULT_BITRATE = 32000;
210    // 配置音频声道数(必须)。
211    constexpr uint32_t DEFAULT_CHANNEL_COUNT = 2;
212    // 配置音频声道布局(必须)。
213    constexpr OH_AudioChannelLayout CHANNEL_LAYOUT = OH_AudioChannelLayout::CH_LAYOUT_STEREO;
214    // 配置音频位深(必须)。
215    constexpr OH_BitsPerSample SAMPLE_FORMAT = OH_BitsPerSample::SAMPLE_S16LE;
216
217    OH_AVFormat *format = OH_AVFormat_Create();
218    // 写入format。
219    OH_AVFormat_SetIntValue(format, OH_MD_KEY_AUD_CHANNEL_COUNT, DEFAULT_CHANNEL_COUNT);
220    OH_AVFormat_SetIntValue(format, OH_MD_KEY_AUD_SAMPLE_RATE, DEFAULT_SAMPLERATE);
221    OH_AVFormat_SetLongValue(format, OH_MD_KEY_BITRATE, DEFAULT_BITRATE);
222    OH_AVFormat_SetIntValue(format, OH_MD_KEY_AUDIO_SAMPLE_FORMAT, SAMPLE_FORMAT);
223    OH_AVFormat_SetLongValue(format, OH_MD_KEY_CHANNEL_LAYOUT, CHANNEL_LAYOUT);
224
225    // 配置编码器。
226    ret = OH_AudioCodec_Configure(audioEnc_, format);
227    if (ret != AV_ERR_OK) {
228        // 异常处理。
229    }
230    ```
231    <!--RP4End-->
232    例FLAC调用流程:
233
234    ```cpp
235    int32_t ret;
236    // 配置音频采样率(必须)。
237    constexpr uint32_t DEFAULT_SAMPLERATE = 44100;
238    // 配置音频码率(必须)。
239    constexpr uint64_t DEFAULT_BITRATE = 261000;
240    // 配置音频声道数(必须)。
241    constexpr uint32_t DEFAULT_CHANNEL_COUNT = 2;
242    // 配置音频声道布局(必须)。
243    // 值为CH_LAYOUT_MONO、CH_LAYOUT_STEREO、CH_LAYOUT_SURROUND、CH_LAYOUT_QUAD、CH_LAYOUT_5POINT0、CH_LAYOUT_5POINT1、CH_LAYOUT_6POINT1或CH_LAYOUT_7POINT1其中一项。
244    constexpr OH_AudioChannelLayout CHANNEL_LAYOUT = OH_AudioChannelLayout::CH_LAYOUT_STEREO;
245    // 配置音频位深(必须) flac只有SAMPLE_S16LE和SAMPLE_S32LE。
246    constexpr OH_BitsPerSample SAMPLE_FORMAT = OH_BitsPerSample::SAMPLE_S32LE;
247    // 配置音频compliance level (默认值0,取值范围-2~2)。
248    constexpr int32_t COMPLIANCE_LEVEL = 0;
249
250    OH_AVFormat *format = OH_AVFormat_Create();
251    // 写入format。
252    OH_AVFormat_SetIntValue(format, OH_MD_KEY_AUD_CHANNEL_COUNT, DEFAULT_CHANNEL_COUNT);
253    OH_AVFormat_SetIntValue(format, OH_MD_KEY_AUD_SAMPLE_RATE, DEFAULT_SAMPLERATE);
254    OH_AVFormat_SetLongValue(format, OH_MD_KEY_BITRATE, DEFAULT_BITRATE);
255    // 配置音频精度。API 20前,FLAC编码必须设置此参数,设置为1即可;未设置此参数配置FLAC编码器时,调用OH_AudioCodec_Configure会返回错误码AV_ERR_INVALID_VAL。该值无实际作用,不会影响编码结果。从API 20开始,无需设置此参数。
256    // constexpr int32_t BITS_PER_CODED_SAMPLE = 1;
257    // OH_AVFormat_SetIntValue(format, OH_MD_KEY_BITS_PER_CODED_SAMPLE, BITS_PER_CODED_SAMPLE);
258    OH_AVFormat_SetIntValue(format, OH_MD_KEY_AUDIO_SAMPLE_FORMAT, SAMPLE_FORMAT);
259    OH_AVFormat_SetLongValue(format, OH_MD_KEY_CHANNEL_LAYOUT, CHANNEL_LAYOUT);
260    OH_AVFormat_SetLongValue(format, OH_MD_KEY_COMPLIANCE_LEVEL, COMPLIANCE_LEVEL);
261    // 配置编码器。
262    ret = OH_AudioCodec_Configure(audioEnc_, format);
263    if (ret != AV_ERR_OK) {
264        // 异常处理。
265    }
266    ```
267
268    <!--RP1--><!--RP1End-->
269
2705. 调用OH_AudioCodec_Prepare(),编码器就绪。
271
272    ```cpp
273    int32_t ret = OH_AudioCodec_Prepare(audioEnc_);
274    if (ret != AV_ERR_OK) {
275        // 异常处理。
276    }
277    ```
278
2796. 调用OH_AudioCodec_Start()启动编码器,进入运行态。
280
281   添加头文件:
282    ```c++
283    #include <fstream>
284    ```
285   使用示例:
286    ```c++
287    ifstream inputFile_;
288    ofstream outFile_;
289
290    // 根据实际使用情况填写输入文件路径。
291    const char* inputFilePath = "/";
292    // 根据实际使用情况填写输出文件路径。
293    const char* outputFilePath = "/";
294    // 打开待编码二进制文件路径(此处以输入为PCM文件为例)。
295    inputFile_.open(inputFilePath, ios::in | ios::binary);
296    // 配置编码文件输出路径(此处以输出为编码码流文件为例)。
297    outFile_.open(outputFilePath, ios::out | ios::binary);
298    // 开始编码。
299    int32_t ret = OH_AudioCodec_Start(audioEnc_);
300    if (ret != AV_ERR_OK) {
301        // 异常处理。
302    }
303    ```
304
3057. 调用OH_AudioCodec_PushInputBuffer(),写入待编码器的数据。需开发者填充完整的输入数据后调用。
306
307   每次输入的采样点数(SAMPLES_PER_FRAME)取值方法如下:
308
309   AAC-LC编码每帧包含1024个PCM样点,建议单次输入1024个样点的数据量。
310
311   <!--RP5--><!--RP5End-->
312
313   flac比较特殊,需要根据如下表格进行设置。
314
315   | 采样率 | 样点数 |
316   | :----: | :----: |
317   |  8000  |  576  |
318   | 16000 |  1152  |
319   | 22050 |  2304  |
320   | 24000 |  2304  |
321   | 32000 |  2304  |
322   | 44100 |  4608  |
323   | 48000 |  4608  |
324   | 88200 |  8192  |
325   | 96000 |  8192  |
326
327   > **说明:**
328   >
329   > 单次编码输入的数据量(单位:字节)为:采样点数(SAMPLES_PER_FRAME) * 声道数 * 单个采样点的占用字节。
330   >
331   > flac编码的样点数建议参考表格根据采样率对应的样点数进行设置,否则可能出现编码文件损坏问题。
332
333   ```c++
334    // 声道数。
335    constexpr int32_t DEFAULT_CHANNEL_COUNT = 2;
336    // 采样点数,这里以AAC-LC为例,采样点数为1024。
337    constexpr int32_t SAMPLES_PER_FRAME = 1024;
338    // 单次编码输入的数据量(单位:字节)为:采样点数 * 声道数 * 单个采样点的占用字节(以采样格式SAMPLE_S16LE为例)。
339    // 如果最后一帧数据不满足长度,建议进行丢弃或填充处理。
340    constexpr int32_t INPUT_FRAME_BYTES = SAMPLES_PER_FRAME * DEFAULT_CHANNEL_COUNT * sizeof(short);
341    uint32_t index = signal_->inQueue_.front();
342    auto buffer = signal_->inBufferQueue_.front();
343    OH_AVCodecBufferAttr attr = {0};
344    if (!inputFile_.eof()) {
345        inputFile_.read((char *)OH_AVBuffer_GetAddr(buffer), INPUT_FRAME_BYTES);
346        attr.size = INPUT_FRAME_BYTES;
347        attr.flags = AVCODEC_BUFFER_FLAGS_NONE;
348    } else {
349        attr.size = 0;
350        attr.flags = AVCODEC_BUFFER_FLAGS_EOS;
351    }
352    OH_AVBuffer_SetBufferAttr(buffer, &attr);
353    // 送入编码输入队列进行编码, index为对应队列下标。
354    int32_t ret = OH_AudioCodec_PushInputBuffer(audioEnc_, index);
355    if (ret != AV_ERR_OK) {
356        // 异常处理。
357    }
358    ```
359   在上方案例中,attr.flags代表缓冲区标记的类别。
360
361   如果是结束,需要将flags标识成AVCODEC_BUFFER_FLAGS_EOS。
362
363   | 枚举值 | 描述 |
364   | -------- | -------- |
365   | AVCODEC_BUFFER_FLAGS_NONE | 表示为普通帧。 |
366   | AVCODEC_BUFFER_FLAGS_EOS | 表示缓冲区是流结束帧。 |
367   | AVCODEC_BUFFER_FLAGS_CODEC_DATA | 表示缓冲区包含编解码特定数据。 |
368
3698. 调用OH_AudioCodec_FreeOutputBuffer(),释放编码后的数据。
370
371   在取走编码码流后,就应及时调用OH_AudioCodec_FreeOutputBuffer()进行释放。
372
373    ```c++
374    uint32_t index = signal_->outQueue_.front();
375    OH_AVBuffer *avBuffer = signal_->outBufferQueue_.front();
376    if (avBuffer == nullptr) {
377        // 异常处理
378    }
379    // 获取buffer attributes。
380    OH_AVCodecBufferAttr attr = {0};
381    int32_t ret = OH_AVBuffer_GetBufferAttr(avBuffer, &attr);
382    if (ret != AV_ERR_OK) {
383        // 异常处理。
384    }
385    // 将编码完成数据data写入到对应输出文件中。
386    outFile_.write(reinterpret_cast<char *>(OH_AVBuffer_GetAddr(avBuffer)), attr.size);
387    // 释放已完成写入的数据。
388    ret = OH_AudioCodec_FreeOutputBuffer(audioEnc_, index);
389    if (ret != AV_ERR_OK) {
390        // 异常处理。
391    }
392    if (attr.flags == AVCODEC_BUFFER_FLAGS_EOS) {
393        // 结束。
394    }
395    ```
396
3979. (可选)调用OH_AudioCodec_Flush()刷新编码器。
398
399   调用OH_AudioCodec_Flush()后,编码器处于Flush状态,会将当前编码队列清空。
400
401   此时需要调用OH_AudioCodec_Start()重新开始编码。
402
403   使用情况:
404
405   * 在文件EOS之后,需要调用刷新。
406   * 在执行过程中遇到可继续执行的错误时(即OH_AudioCodec_IsValid 为true)可以调用,然后重新调用OH_AudioCodec_Start。
407
408    ```c++
409    // 刷新编码器 audioEnc_。
410    int32_t ret = OH_AudioCodec_Flush(audioEnc_);
411    if (ret != AV_ERR_OK) {
412        // 异常处理。
413    }
414    // 重新开始编码。
415    ret = OH_AudioCodec_Start(audioEnc_);
416    if (ret != AV_ERR_OK) {
417        // 异常处理。
418    }
419    ```
420
42110. (可选)调用OH_AudioCodec_Reset()重置编码器。
422
423    调用OH_AudioCodec_Reset()后,编码器回到初始化的状态,需要调用OH_AudioCodec_Configure()重新配置,然后调用OH_AudioCodec_Start()重新开始编码。
424
425    ```c++
426    // 重置编码器 audioEnc_。
427    int32_t ret = OH_AudioCodec_Reset(audioEnc_);
428    if (ret != AV_ERR_OK) {
429        // 异常处理。
430    }
431    // 重新配置编码器参数。
432    ret = OH_AudioCodec_Configure(audioEnc_, format);
433    if (ret != AV_ERR_OK) {
434        // 异常处理。
435    }
436    ```
437
43811. 调用OH_AudioCodec_Stop()停止编码器。
439
440    停止后,可以通过Start重新进入已启动状态(started),但需要注意的是,如果编码器之前已输入数据,则需要重新输入编码器数据。
441
442    ```c++
443    // 终止编码器 audioEnc_。
444    int32_t ret = OH_AudioCodec_Stop(audioEnc_);
445    if (ret != AV_ERR_OK) {
446        // 异常处理。
447    }
448    ```
449
45012. 调用OH_AudioCodec_Destroy()销毁编码器实例,释放资源。
451
452    > **说明:**
453    > 资源不能重复销毁
454
455    ```c++
456    // 调用OH_AudioCodec_Destroy, 注销编码器。
457    int32_t ret = OH_AudioCodec_Destroy(audioEnc_);
458    if (ret != AV_ERR_OK) {
459        // 异常处理。
460    } else {
461        audioEnc_ = NULL; // 不可重复destroy。
462    }
463    ```
464
465## 相关实例
466
467针对音频编码,有以下相关实例可供参考:
468
469- [音频编码](https://gitee.com/openharmony/multimedia_av_codec/blob/master/test/nativedemo/audio_demo/avcodec_audio_avbuffer_aac_encoder_demo.cpp)
470