1# 音频编码同步模式 2 3<!--Kit: AVCodec Kit--> 4<!--Subsystem: Multimedia--> 5<!--Owner: @mr-chencxy--> 6<!--Designer: @dpy2650---> 7<!--Tester: @baotianhao--> 8<!--Adviser: @zengyawen--> 9 10从API 20开始,支持音频编码同步模式。 11 12开发者可以调用本模块的Native API接口,完成音频编码,即将音频PCM编码压缩成不同的格式。 13 14接口不限制PCM数据的来源。开发者可以调用麦克风录制获取,也可以导入编辑后的PCM数据。通过音频编码,输出对应格式的码流,最后封装为目标格式文件。 15 16支持的编码能力请参考[AVCodec支持的格式](avcodec-support-formats.md#音频编码)。 17 18**适用场景** 19 20通常推荐使用异步模式,异步模式请参考[音频编码](audio-encoding.md)。若需要主动请求buffer去编码PCM,则可以使用同步模式。 21- 音频录制 22 23 通过录制传入PCM,然后编码成对应格式的码流,最后封装成所需格式的文件。具体封装方法请参考[媒体数据封装](audio-video-muxer.md)。 24- 音频编辑 25 26 编辑PCM后导出音频文件的场景,需要编码成对应音频格式,最后封装成所需格式的文件。具体封装方法请参考[媒体数据封装](audio-video-muxer.md)。 27> **说明:** 28> - AAC编码器默认采用的VBR可变码率模式,这可能导致与预期码率有偏差。 29> - AAC编码器默认输出携带ADTS头部,帧数据的前7字节为ADTS头部。 30 31## 开发指导 32 33详细的API说明请参考[AudioCodec](../../reference/apis-avcodec-kit/_audio_codec.md)。 34 35参考以下示例代码,完成音频编码的全流程,包括:创建编码器、设置编码参数(采样率/码率/声道数等)、开始/刷新/重置/销毁资源。 36 37在应用开发过程中,开发者应按一定顺序调用方法,执行对应操作,否则系统可能会抛出异常或生成其他未定义的行为。具体顺序可参考下列开发步骤及对应说明。 38 39音频编解码同步模式调用关系图如下所示: 40 41- 虚线表示可选。音频编码不涉及解密,无需调用OH_AudioCodec_SetDecryptionConfig。 42 43- 实线表示必选。 44 45 46 47### 在CMake脚本中链接动态库 48 49```cmake 50target_link_libraries(sample PUBLIC libnative_media_codecbase.so) 51target_link_libraries(sample PUBLIC libnative_media_core.so) 52target_link_libraries(sample PUBLIC libnative_media_acodec.so) 53``` 54> **说明:** 55> 56> 上述'sample'字样仅为示例,此处由开发者根据实际工程目录自定义。 57 58### 开发步骤 59 601. 添加头文件和命名空间。 61 62 ```cpp 63 #include <multimedia/player_framework/native_avcodec_audiocodec.h> 64 #include <multimedia/native_audio_channel_layout.h> 65 #include <multimedia/player_framework/native_avcapability.h> 66 #include <multimedia/player_framework/native_avcodec_base.h> 67 #include <multimedia/player_framework/native_avformat.h> 68 #include <multimedia/player_framework/native_avbuffer.h> 69 70 // c++标准库命名空间。 71 using namespace std; 72 ``` 73 742. 创建编码器实例对象,OH_AVCodec *为编码器实例指针。 75 76 应用可以通过媒体类型或编解码器名称创建编码器。 77 78 方法一:通过Mimetype创建编码器。 79 ```cpp 80 // 通过Mimetype创建编码器,这里示例创建的是AAC编码器,第二个参数设置为true表示当前是编码。 81 OH_AVCodec *audioEnc_ = OH_AudioCodec_CreateByMime(OH_AVCODEC_MIMETYPE_AUDIO_AAC, true); 82 ``` 83 方法二:通过codec name创建编码器。 84 ```cpp 85 // 通过codec name创建编码器。 86 OH_AVCapability *capability = OH_AVCodec_GetCapability(OH_AVCODEC_MIMETYPE_AUDIO_AAC, true); 87 const char *name = OH_AVCapability_GetName(capability); 88 OH_AVCodec *audioEnc_ = OH_AudioCodec_CreateByName(name); 89 ``` 90 913. 调用OH_AudioCodec_Configure配置编码器。 92 93 配置选项key值说明: 94 95 <!--RP1--> 96 | key | 描述 | AAC | Flac | MPEG(MP3) | G711mu | 97 | ----------------------------- | :--------------: | :---: | :---: | :------: | :---: | 98 | OH_MD_KEY_AUD_SAMPLE_RATE | 采样率 | 必须 | 必须 | 必须 | 必须 | 99 | OH_MD_KEY_AUD_CHANNEL_COUNT | 声道数 | 必须 | 必须 | 必须 | 必须 | 100 | OH_MD_KEY_AUDIO_SAMPLE_FORMAT | 输出音频流格式 | 必须 | 必须 | 必须 | 必须 | 101 | OH_MD_KEY_BITRATE | 码率 | 可选 | 必须 | 必须 | - | 102 | OH_MD_KEY_CHANNEL_LAYOUT | 声道布局 | 可选 | 必须 | - | - | 103 | OH_MD_KEY_MAX_INPUT_SIZE | 最大输入长度 | 可选 | 可选 | 可选 | 可选 | 104 | OH_MD_KEY_COMPLIANCE_LEVEL | 兼容性等级 | - | 可选 | - | - | 105 <!--RP1End--> 106 107 各音频编码类型参数范围说明: 108 | 音频编码类型 | 采样率(Hz) | 声道数 | 109 | ----------- | ------------------------------------------------------------------------------- | :----------------: | 110 | <!--DelRow-->AAC | 8000、11025、12000、16000、22050、24000、32000、44100、48000、64000、88200、96000 | 1、2、3、4、5、6、8 | 111 | Flac | 8000、11025、12000、16000、22050、24000、32000、44100、48000、64000、88200、96000 | 1~8 | 112 | MP3 | 8000、11025、12000、16000、22050、24000、32000、44100、48000 | 1~2 | 113 | G711mu | 8000 | 1 | 114 <!--RP2--><!--RP2End--> 115 116 例如,对44100Hz采样率、2声道立体声、SAMPLE_S16LE采样格式的PCM音频,以32000bps的码率进行AAC编码的调用流程如下: 117 <!--RP3--> 118 ```cpp 119 OH_AVErrCode ret; 120 // 配置音频采样率(必须)。 121 constexpr uint32_t DEFAULT_SAMPLERATE = 44100; 122 // 配置音频码率(必须)。 123 constexpr uint64_t DEFAULT_BITRATE = 32000; 124 // 配置音频声道数(必须)。 125 constexpr uint32_t DEFAULT_CHANNEL_COUNT = 2; 126 // 配置音频位深(必须)。 127 constexpr OH_BitsPerSample SAMPLE_FORMAT = OH_BitsPerSample::SAMPLE_S16LE; 128 // 配置音频声道布局(可选)。 129 constexpr OH_AudioChannelLayout CHANNEL_LAYOUT = OH_AudioChannelLayout::CH_LAYOUT_STEREO; 130 131 OH_AVFormat *format = OH_AVFormat_Create(); 132 // 写入format。 133 OH_AVFormat_SetIntValue(format, OH_MD_KEY_AUD_CHANNEL_COUNT, DEFAULT_CHANNEL_COUNT); 134 OH_AVFormat_SetIntValue(format, OH_MD_KEY_AUD_SAMPLE_RATE, DEFAULT_SAMPLERATE); 135 OH_AVFormat_SetLongValue(format, OH_MD_KEY_BITRATE, DEFAULT_BITRATE); 136 OH_AVFormat_SetIntValue(format, OH_MD_KEY_AUDIO_SAMPLE_FORMAT, SAMPLE_FORMAT); 137 OH_AVFormat_SetLongValue(format, OH_MD_KEY_CHANNEL_LAYOUT, CHANNEL_LAYOUT); 138 OH_AVFormat_SetIntValue(format, OH_MD_KEY_ENABLE_SYNC_MODE, 1); // 同步模式配置。 139 // 配置编码器。 140 ret = OH_AudioCodec_Configure(audioEnc_, format); 141 if (ret != AV_ERR_OK) { 142 // 异常处理。 143 } 144 ``` 145 <!--RP3End--> 146 FLAC编码调用示例: 147 148 ```cpp 149 OH_AVErrCode ret; 150 // 配置音频采样率(必须)。 151 constexpr uint32_t DEFAULT_SAMPLERATE = 44100; 152 // 配置音频码率(必须)。 153 constexpr uint64_t DEFAULT_BITRATE = 261000; 154 // 配置音频声道数(必须)。 155 constexpr uint32_t DEFAULT_CHANNEL_COUNT = 2; 156 // 配置音频声道布局(必须)。 157 // 值为CH_LAYOUT_MONO、CH_LAYOUT_STEREO、CH_LAYOUT_SURROUND、CH_LAYOUT_QUAD、CH_LAYOUT_5POINT0、CH_LAYOUT_5POINT1、CH_LAYOUT_6POINT1或CH_LAYOUT_7POINT1其中一项。 158 constexpr OH_AudioChannelLayout CHANNEL_LAYOUT = OH_AudioChannelLayout::CH_LAYOUT_STEREO; 159 // 配置音频位深(必须) flac只有SAMPLE_S16LE和SAMPLE_S32LE。 160 constexpr OH_BitsPerSample SAMPLE_FORMAT = OH_BitsPerSample::SAMPLE_S32LE; 161 // 配置音频compliance level(默认值0,取值范围[-2,2])。 162 constexpr int32_t COMPLIANCE_LEVEL = 0; 163 164 OH_AVFormat *format = OH_AVFormat_Create(); 165 // 写入format。 166 OH_AVFormat_SetIntValue(format, OH_MD_KEY_AUD_CHANNEL_COUNT, DEFAULT_CHANNEL_COUNT); 167 OH_AVFormat_SetIntValue(format, OH_MD_KEY_AUD_SAMPLE_RATE, DEFAULT_SAMPLERATE); 168 OH_AVFormat_SetLongValue(format, OH_MD_KEY_BITRATE, DEFAULT_BITRATE); 169 OH_AVFormat_SetIntValue(format, OH_MD_KEY_AUDIO_SAMPLE_FORMAT, SAMPLE_FORMAT); 170 OH_AVFormat_SetLongValue(format, OH_MD_KEY_CHANNEL_LAYOUT, CHANNEL_LAYOUT); 171 OH_AVFormat_SetLongValue(format, OH_MD_KEY_COMPLIANCE_LEVEL, COMPLIANCE_LEVEL); 172 OH_AVFormat_SetIntValue(format, OH_MD_KEY_ENABLE_SYNC_MODE, 1); // 同步模式配置。 173 // 配置编码器。 174 ret = OH_AudioCodec_Configure(audioEnc_, format); 175 if (ret != AV_ERR_OK) { 176 // 异常处理。 177 } 178 ``` 179 1804. 调用OH_AudioCodec_Prepare(),编码器就绪。 181 182 ```cpp 183 OH_AVErrCode ret = OH_AudioCodec_Prepare(audioEnc_); 184 if (ret != AV_ERR_OK) { 185 // 异常处理。 186 } 187 ``` 188 1895. 调用OH_AudioCodec_Start()启动编码器,进入运行态。 190 191 添加头文件: 192 ```c++ 193 #include <fstream> 194 ``` 195 使用示例: 196 ```c++ 197 ifstream inputFile_; 198 ofstream outFile_; 199 200 // 根据实际使用情况填写输入文件路径。 201 const char* inputFilePath = "/"; 202 // 根据实际使用情况填写输出文件路径。 203 const char* outputFilePath = "/"; 204 // 打开待编码二进制文件路径(此处以输入为PCM文件为例)。 205 inputFile_.open(inputFilePath, ios::in | ios::binary); 206 // 配置编码文件输出路径(此处以输出为编码码流文件为例,并非可播放的音频文件,可播放的音频文件需要音频码流封装到容器内生成)。 207 outFile_.open(outputFilePath, ios::out | ios::binary); 208 // 开始编码。 209 OH_AVErrCode ret = OH_AudioCodec_Start(audioEnc_); 210 if (ret != AV_ERR_OK) { 211 // 异常处理。 212 } 213 ``` 214 2156. 同步模式调用,写入待编码的PCM,获取编码输出的音频帧。 216 217 示例代码中,每次输入的采样点数SAMPLES_PER_FRAME取值方法如下: 218 219 AAC LC编码每帧包含1024个PCM样点,建议单次输入1024个样点的数据量。 220 221 <!--RP4--><!--RP4End--> 222 223 FLAC需要根据如下表格进行设置。 224 225 | 采样率 | 样点数 | 226 | :----: | :----: | 227 | 8000 | 576 | 228 | 16000 | 1152 | 229 | 22050 | 2304 | 230 | 24000 | 2304 | 231 | 32000 | 2304 | 232 | 44100 | 4608 | 233 | 48000 | 4608 | 234 | 88200 | 8192 | 235 | 96000 | 8192 | 236 237 单次编码输入的数据量(单位:字节)为:SAMPLES_PER_FRAME * 声道数 * 单个采样点的占用字节。 238 使用示例: 239 ```c++ 240 int32_t SAMPLES_PER_FRAME = 1024; 241 // AAC LC编码一帧包含1024采样点,2声道,输入数据为S16LE,16比特数据,占用2字节。 242 int32_t inputFrameBytes = SAMPLES_PER_FRAME * 2 * 2; 243 bool inputFinished = false; 244 OH_AVErrCode ret; 245 OH_AVCodecBufferAttr attr; 246 247 for (;;) { 248 uint32_t index = 0; 249 if (!inputFinished) { 250 ret = OH_AudioCodec_QueryInputBuffer(audioEnc_, &index, 20000); // 20000us 251 if (ret == AV_ERR_TRY_AGAIN_LATER) { 252 continue; 253 } 254 if (ret != AV_ERR_OK) { 255 // 异常处理。 256 break; 257 } 258 OH_AVBuffer *inputBuf = OH_AudioCodec_GetInputBuffer(audioEnc_, index); 259 if (inputBuf == nullptr) { 260 // 异常处理。 261 break; 262 } 263 memset(&attr, 0, sizeof(attr)); 264 if (!inputFile_.eof()) { 265 inputFile_.read((char *)OH_AVBuffer_GetAddr(inputBuf), inputFrameBytes); 266 int32_t readSize = inputFile_.gcount(); 267 attr.size = readSize; 268 attr.flags = readSize != 0 ? AVCODEC_BUFFER_FLAGS_NONE : AVCODEC_BUFFER_FLAGS_EOS; 269 } else { 270 inputFinished = true; 271 attr.size = 0; 272 attr.flags = AVCODEC_BUFFER_FLAGS_EOS; 273 } 274 if (OH_AVBuffer_SetBufferAttr(inputBuf, &attr) != AV_ERR_OK) { 275 // 异常处理。 276 } 277 if (OH_AudioCodec_PushInputBuffer(audioEnc_, index) != AV_ERR_OK) { 278 // 异常处理。 279 } 280 } 281 // 当输入的数据量可以编码出多帧数据时,需要多次调用获取输出缓冲区,才能取完编码后的数据。 282 ret = OH_AudioCodec_QueryOutputBuffer(audioEnc_, &index, 20000); // 20000us 283 if (ret == AV_ERR_TRY_AGAIN_LATER) { 284 // 超时,可能输入的数据不足以编码出一帧,或者超时时间设置过短。 285 continue; 286 } 287 if (ret != AV_ERR_OK) { 288 // 异常处理。 289 break; 290 } 291 OH_AVBuffer *outputBuf = OH_AudioCodec_GetOutputBuffer(audioEnc_, index); 292 if (outputBuf == nullptr) { 293 // 异常处理。 294 break; 295 } 296 if (OH_AVBuffer_GetBufferAttr(outputBuf, &attr) != AV_ERR_OK) { 297 // 异常处理。 298 break; 299 } 300 if (attr.flags & AVCODEC_BUFFER_FLAGS_EOS) { 301 // 输出结束。 302 break; 303 } 304 // 这里示例仅将数据写入文件记录。假如需要封装成音频文件,可参考媒体数据封装,调用OH_AVMuxer_WriteSampleBuffer封装数据。 305 outFile_.write(reinterpret_cast<char *>(OH_AVBuffer_GetAddr(outputBuf)), attr.size); 306 OH_AudioCodec_FreeOutputBuffer(audioEnc_, index); 307 } 308 ``` 309 310 在上方案例中,attr.flags表示缓冲区标记的类别。 311 312 如果是结束,需要将flags标识成AVCODEC_BUFFER_FLAGS_EOS。 313 314 | 枚举值 | 描述 | 315 | -------- | -------- | 316 | AVCODEC_BUFFER_FLAGS_NONE | 表示为普通帧。 | 317 | AVCODEC_BUFFER_FLAGS_EOS | 表示缓冲区是流结束帧。 | 318 | AVCODEC_BUFFER_FLAGS_CODEC_DATA | 表示缓冲区包含编解码特定数据。 | 319 3207. (可选)调用OH_AudioCodec_Reset()重置编码器。 321 322 调用OH_AudioCodec_Reset()后,编码器回到初始化状态。需要调用OH_AudioCodec_Configure()重新配置,然后调用OH_AudioCodec_Start()重新开始编码。 323 324 ```c++ 325 // 重置编码器。 326 OH_AVErrCode ret = OH_AudioCodec_Reset(audioEnc_); 327 if (ret != AV_ERR_OK) { 328 // 异常处理。 329 } 330 // 重新配置编码器参数。 331 ret = OH_AudioCodec_Configure(audioEnc_, format); 332 if (ret != AV_ERR_OK) { 333 // 异常处理。 334 } 335 ``` 336 3378. (可选)调用OH_AudioCodec_Stop()停止编码器。 338 339 停止后,可以通过Start重新进入已启动状态(started),但若编码器之前已输入数据,则需要重新输入编码器数据。 340 341 ```c++ 342 // 停止编码器。 343 OH_AVErrCode ret = OH_AudioCodec_Stop(audioEnc_); 344 if (ret != AV_ERR_OK) { 345 // 异常处理。 346 } 347 ``` 348 3499. 调用OH_AudioCodec_Destroy()销毁编码器实例,释放资源。 350 351 > **说明:** 352 > 353 > 禁止重复销毁编码器。 354 355 ```c++ 356 // 调用OH_AudioCodec_Destroy销毁编码器。 357 OH_AVErrCode ret = OH_AudioCodec_Destroy(audioEnc_); 358 if (ret != AV_ERR_OK) { 359 // 异常处理。 360 } else { 361 audioEnc_ = NULL; // 不可重复destroy。 362 } 363 ``` 364