1# 音频解码 2 3开发者可以调用本模块的Native API接口,完成音频解码,即将媒体数据解码为PCM码流。 4 5当前支持的解码能力如下: 6 7| 容器规格 | 音频解码类型 | 8| -------- | :--------------------------- | 9| mp4 | AAC、MPEG(MP3)、Flac、Vorbis<!--RP1--><!--RP1End--> | 10| m4a | AAC | 11| flac | Flac | 12| ogg | Vorbis<!--RP2--><!--RP2End--> | 13| aac | AAC | 14| mp3 | MPEG(MP3) | 15| amr | AMR(amrnb、amrwb) | 16| raw | G711mu | 17| ape | APE | 18 19**适用场景** 20 21- 音频播放 22 23 在播放音频之前,需要先解码音频,再将数据输送到硬件扬声器播放。 24- 音频渲染 25 26 在对音频文件进行音效处理之前,需要先解码再由音频处理模块进行音频渲染。 27- 音频编辑 28 29 音频编辑(如调整单个声道的播放倍速等)需要基于PCM码流进行,所以需要先将音频文件解码。 30> **说明:** 31> 32> 通过MP3音频编码流程生成的码流无法直接通过MP3音频解码流程进行解码。建议通过(PCM码流->MP3音频编码->封装->解封装->MP3音频解码)流程进行。 33 34## 开发指导 35 36详细的API说明请参考[API文档](../../reference/apis-avcodec-kit/_audio_codec.md)。 37参考以下示例代码,完成音频解码的全流程,包括:创建解码器、设置解码参数(采样率/码率/声道数等)、开始、刷新、重置、销毁资源。 38 39在应用开发过程中,开发者应按一定顺序调用方法,执行对应操作,否则系统可能会抛出异常或生成其他未定义的行为。具体顺序可参考下列开发步骤及对应说明。 40 41如下为音频解码调用关系图: 42 43- 虚线表示可选。 44 45- 实线表示必选。 46 47 48 49### 在 CMake 脚本中链接动态库 50 51```cmake 52target_link_libraries(sample PUBLIC libnative_media_codecbase.so) 53target_link_libraries(sample PUBLIC libnative_media_core.so) 54target_link_libraries(sample PUBLIC libnative_media_acodec.so) 55``` 56 57### 开发步骤 58 591. 添加头文件。 60 61 ```cpp 62 #include <multimedia/player_framework/native_avcodec_audiocodec.h> 63 #include <multimedia/native_audio_channel_layout.h> 64 #include <multimedia/player_framework/native_avcapability.h> 65 #include <multimedia/player_framework/native_avcodec_base.h> 66 #include <multimedia/player_framework/native_avformat.h> 67 #include <multimedia/player_framework/native_avbuffer.h> 68 ``` 69 702. 创建解码器实例对象,OH_AVCodec *为解码器实例指针。 71 72 ```cpp 73 //c++标准库命名空间 74 using namespace std; 75 // 通过 codecname 创建解码器 76 OH_AVCapability *capability = OH_AVCodec_GetCapability(OH_AVCODEC_MIMETYPE_AUDIO_MPEG, false); 77 const char *name = OH_AVCapability_GetName(capability); 78 OH_AVCodec *audioDec_ = OH_AudioCodec_CreateByName(name); 79 ``` 80 81 ```cpp 82 // 设置判定是否为编码;设置false表示当前是解码。 83 bool isEncoder = false; 84 // 通过 Mimetype 创建解码器 85 OH_AVCodec *audioDec_ = OH_AudioCodec_CreateByMime(OH_AVCODEC_MIMETYPE_AUDIO_MPEG, isEncoder); 86 ``` 87 88 ```cpp 89 // 初始化队列 90 class ADecBufferSignal { 91 public: 92 std::mutex inMutex_; 93 std::mutex outMutex_; 94 std::mutex startMutex_; 95 std::condition_variable inCond_; 96 std::condition_variable outCond_; 97 std::condition_variable startCond_; 98 std::queue<uint32_t> inQueue_; 99 std::queue<uint32_t> outQueue_; 100 std::queue<OH_AVBuffer *> inBufferQueue_; 101 std::queue<OH_AVBuffer *> outBufferQueue_; 102 }; 103 ADecBufferSignal *signal_; 104 ``` 105 1063. 调用OH_AudioCodec_RegisterCallback()注册回调函数。 107 注册回调函数指针集合OH_AVCodecCallback,包括: 108 109 - OH_AVCodecOnError:解码器运行错误。 110 - OH_AVCodecOnStreamChanged:码流信息变化,如声道变化等。 111 - OH_AVCodecOnNeedInputBuffer:运行过程中需要新的输入数据,即解码器已准备好,可以输入数据。 112 - OH_AVCodecOnNewOutputBuffer:运行过程中产生了新的输出数据,即解码完成。 113 114 开发者可以通过处理该回调报告的信息,确保解码器正常运转。 115 116 ```cpp 117 // OH_AVCodecOnError回调函数的实现 118 static void OnError(OH_AVCodec *codec, int32_t errorCode, void *userData) 119 { 120 (void)codec; 121 (void)errorCode; 122 (void)userData; 123 } 124 // OH_AVCodecOnStreamChanged回调函数的实现 125 static void OnOutputFormatChanged(OH_AVCodec *codec, OH_AVFormat *format, void *userData) 126 { 127 (void)codec; 128 (void)format; 129 (void)userData; 130 } 131 // OH_AVCodecOnNeedInputBuffer回调函数的实现 132 static void OnInputBufferAvailable(OH_AVCodec *codec, uint32_t index, OH_AVBuffer *data, void *userData) 133 { 134 (void)codec; 135 ADecBufferSignal *signal = static_cast<ADecBufferSignal *>(userData); 136 unique_lock<mutex> lock(signal->inMutex_); 137 signal->inQueue_.push(index); 138 signal->inBufferQueue_.push(data); 139 signal->inCond_.notify_all(); 140 // 解码输入码流送入inBufferQueue_队列 141 } 142 // OH_AVCodecOnNewOutputBuffer回调函数的实现 143 static void OnOutputBufferAvailable(OH_AVCodec *codec, uint32_t index, OH_AVBuffer *data, void *userData) 144 { 145 (void)codec; 146 ADecBufferSignal *signal = static_cast<ADecBufferSignal *>(userData); 147 unique_lock<mutex> lock(signal->outMutex_); 148 signal->outQueue_.push(index); 149 signal->outBufferQueue_.push(data); 150 signal->outCond_.notify_all(); 151 // 将对应输出buffer的 index 送入outQueue_队列 152 // 将对应解码完成的数据data送入outBufferQueue_队列 153 } 154 signal_ = new ADecBufferSignal(); 155 OH_AVCodecCallback cb_ = {&OnError, &OnOutputFormatChanged, &OnInputBufferAvailable, &OnOutputBufferAvailable}; 156 int32_t ret = OH_AudioCodec_RegisterCallback(audioDec_, cb_, signal_); 157 if (ret != AVCS_ERR_OK) { 158 // 异常处理 159 } 160 ``` 161 1624. (可选)OH_AudioCodec_SetDecryptionConfig设置解密配置。当获取到DRM信息(参考[音视频解封装](audio-video-demuxer.md)开发步骤第3步)后,通过此接口进行解密配置。DRM相关接口详见[DRM API文档](../../reference/apis-drm-kit/_drm.md)。此接口需在Prepare前调用。 163 164 添加头文件: 165 166 ```c++ 167 #include <multimedia/drm_framework/native_mediakeysystem.h> 168 #include <multimedia/drm_framework/native_mediakeysession.h> 169 #include <multimedia/drm_framework/native_drm_err.h> 170 #include <multimedia/drm_framework/native_drm_common.h> 171 ``` 172 在 CMake 脚本中链接动态库: 173 174 ``` cmake 175 target_link_libraries(sample PUBLIC libnative_drm.so) 176 ``` 177 178 使用示例: 179 ```c++ 180 // 根据DRM信息创建指定的DRM系统, 以创建"com.clearplay.drm"为例 181 MediaKeySystem *system = nullptr; 182 int32_t ret = OH_MediaKeySystem_Create("com.clearplay.drm", &system); 183 if (system == nullptr) { 184 printf("create media key system failed"); 185 return; 186 } 187 188 // 创建解密会话 189 MediaKeySession *session = nullptr; 190 DRM_ContentProtectionLevel contentProtectionLevel = CONTENT_PROTECTION_LEVEL_SW_CRYPTO; 191 ret = OH_MediaKeySystem_CreateMediaKeySession(system, &contentProtectionLevel, &session); 192 if (ret != DRM_OK) { 193 // 如创建失败,请查看DRM接口文档及日志信息 194 printf("create media key session failed."); 195 return; 196 } 197 if (session == nullptr) { 198 printf("media key session is nullptr."); 199 return; 200 } 201 // 获取许可证请求、设置许可证响应等 202 // 设置解密配置, 即将解密会话、安全通路标志(当前音频解密不支持安全通路,应设置为false)设置到解码器中。 203 bool secureAudio = false; 204 ret = OH_AudioCodec_SetDecryptionConfig(audioDec_, session, secureAudio); 205 ``` 206 2075. 调用OH_AudioCodec_Configure()配置解码器。 208 配置选项key值说明: 209 210 | | 描述 | AAC | Flac | Vorbis | MPEG | G711mu | AMR(amrnb、amrwb) | APE | 211 | ---------------------------- | :----------------------------------------------------------: | :--------------------------------: | :--: | :--------------------------------: | :--: | :-----------------: | :-------------------------------: | :-------------------------------: | 212 | OH_MD_KEY_AUD_SAMPLE_RATE | 采样率 | 必须 | 必须 | 必须 | 必须 | 必须 | 必须 | 必须 | 213 | OH_MD_KEY_AUD_CHANNEL_COUNT | 声道数 | 必须 | 必须 | 必须 | 必须 | 必须 | 必须 | 必须 | 214 | OH_MD_KEY_MAX_INPUT_SIZE | 最大输入长度 | 可选 | 可选 | 可选 | 可选 | 可选 | 可选 | 可选 | 215 | OH_MD_KEY_AAC_IS_ADTS | 是否adts | 可选,默认1 latm类型 | - | - | - | - | - | - | 216 | MD_KEY_AUDIO_SAMPLE_FORMAT | 输出音频流格式 | 可选(SAMPLE_S16LE,SAMPLE_F32LE) | - | 可选(SAMPLE_S16LE,SAMPLE_F32LE) | 可选 | 可选(默认SAMPLE_S16LE)| 可选(SAMPLE_S16LE,SAMPLE_F32LE)| 可选 | 217 | MD_KEY_BITRATE | 可选 | 可选 | 可选 | 可选 | 可选 | 可选 | 可选 | 可选 | 218 | MD_KEY_IDENTIFICATION_HEADER | ID Header | - | - | 必须(和Codec_Config二选一) | - | - | - | - | 219 | MD_KEY_SETUP_HEADER | Setup Header | - | - | 必须(和Codec_Config二选一) | - | - | - | - | 220 | MD_KEY_CODEC_CONFIG | MD_KEY_SETUP_HEADERID Header+Common Header+Setup Header 拼接 | - | | 必须(和上述ID和Setup二选一) | - | - | - | - | 221 222 各音频解码类型参数范围说明: 223 | 音频解码类型 | 采样率(Hz) | 声道数 | 224 | ----------- | ---------------------------------------------------------------------------------------------- | :----: | 225 | AAC | 8000、11025、12000、16000、22050、24000、32000、44100、48000、64000、88200、96000 | 1~8 | 226 | Flac | 8000、11025、12000、16000、22050、24000、32000、44100、48000、64000、88200、96000、192000 | 1~8 | 227 | Vorbis | 8000、11025、12000、16000、22050、24000、32000、44100、48000、64000、88200、96000、176400、192000 | 1~8 | 228 | MPEG(MP3) | 8000、11025、12000、16000、22050、24000、32000、44100、48000 | 1~2 | 229 | G711mu | 8000 | 1 | 230 | AMR(amrnb) | 8000 | 1 | 231 | AMR(amrwb) | 16000 | 1 | 232 | APE | 8000、11025、12000、16000、22050、24000、32000、44100、48000、64000、88200、96000、176400、192000 | 1~2 | 233 234 ```cpp 235 // 设置解码分辨率 236 int32_t ret; 237 // 配置音频采样率(必须) 238 constexpr uint32_t DEFAULT_SAMPLERATE = 44100; 239 // 配置音频码率(可选) 240 constexpr uint32_t DEFAULT_BITRATE = 32000; 241 // 配置音频声道数(必须) 242 constexpr uint32_t DEFAULT_CHANNEL_COUNT = 2; 243 // 配置最大输入长度(可选) 244 constexpr uint32_t DEFAULT_MAX_INPUT_SIZE = 1152; 245 // 配置是否为ADTS解码(aac解码时可选) 246 constexpr uint32_t DEFAULT_AAC_TYPE = 1; 247 OH_AVFormat *format = OH_AVFormat_Create(); 248 // 写入format 249 OH_AVFormat_SetIntValue(format, OH_MD_KEY_AUD_SAMPLE_RATE, DEFAULT_SAMPLERATE); 250 OH_AVFormat_SetIntValue(format, OH_MD_KEY_BITRATE, DEFAULT_BITRATE); 251 OH_AVFormat_SetIntValue(format, OH_MD_KEY_AUD_CHANNEL_COUNT, DEFAULT_CHANNEL_COUNT); 252 OH_AVFormat_SetIntValue(format, OH_MD_KEY_MAX_INPUT_SIZE, DEFAULT_MAX_INPUT_SIZE); 253 OH_AVFormat_SetIntValue(format, OH_MD_KEY_AAC_IS_ADTS, DEFAULT_AAC_TYPE); 254 // 配置解码器 255 ret = OH_AudioCodec_Configure(audioDec_, format); 256 if (ret != AV_ERR_OK) { 257 // 异常处理 258 } 259 ``` 260 2616. 调用OH_AudioCodec_Prepare(),解码器就绪。 262 263 ```cpp 264 ret = OH_AudioCodec_Prepare(audioDec_); 265 if (ret != AV_ERR_OK) { 266 // 异常处理 267 } 268 ``` 269 2707. 调用OH_AudioCodec_Start()启动解码器,进入运行态。 271 272 ```c++ 273 unique_ptr<ifstream> inputFile_ = make_unique<ifstream>(); 274 unique_ptr<ofstream> outFile_ = make_unique<ofstream>(); 275 // 打开待解码二进制文件路径 276 inputFile_->open(inputFilePath.data(), ios::in | ios::binary); 277 // 配置解码文件输出路径 278 outFile_->open(outputFilePath.data(), ios::out | ios::binary); 279 // 开始解码 280 ret = OH_AudioCodec_Start(audioDec_); 281 if (ret != AV_ERR_OK) { 282 // 异常处理 283 } 284 ``` 285 2868. (可选)调用OH_AVCencInfo_SetAVBuffer(),设置cencInfo。 287 288 若当前播放的节目是DRM加密节目,且由上层应用做媒体解封装,则须调用OH_AVCencInfo_SetAVBuffer()将cencInfo设置给AVBuffer,以实现AVBuffer中媒体数据的解密。 289 290 添加头文件: 291 292 ```c++ 293 #include <multimedia/player_framework/native_cencinfo.h> 294 ``` 295 在 CMake 脚本中链接动态库: 296 297 ``` cmake 298 target_link_libraries(sample PUBLIC libnative_media_avcencinfo.so) 299 ``` 300 301 使用示例: 302 ```c++ 303 auto buffer = signal_->inBufferQueue_.front(); 304 int64_t size; 305 int64_t pts; 306 uint32_t keyIdLen = DRM_KEY_ID_SIZE; 307 uint8_t keyId[] = { 308 0xd4, 0xb2, 0x01, 0xe4, 0x61, 0xc8, 0x98, 0x96, 309 0xcf, 0x05, 0x22, 0x39, 0x8d, 0x09, 0xe6, 0x28}; 310 uint32_t ivLen = DRM_KEY_IV_SIZE; 311 uint8_t iv[] = { 312 0xbf, 0x77, 0xed, 0x51, 0x81, 0xde, 0x36, 0x3e, 313 0x52, 0xf7, 0x20, 0x4f, 0x72, 0x14, 0xa3, 0x95}; 314 uint32_t encryptedBlockCount = 0; 315 uint32_t skippedBlockCount = 0; 316 uint32_t firstEncryptedOffset = 0; 317 uint32_t subsampleCount = 1; 318 DrmSubsample subsamples[1] = { {0x10, 0x16} }; 319 inputFile_.read(reinterpret_cast<char *>(&size), sizeof(size)); 320 inputFile_.read(reinterpret_cast<char *>(&pts), sizeof(pts)); 321 inputFile_.read((char *)OH_AVMemory_GetAddr(buffer), size); 322 OH_AVCencInfo *cencInfo = OH_AVCencInfo_Create(); 323 if (cencInfo == nullptr) { 324 // 异常处理 325 } 326 OH_AVErrCode errNo = OH_AVCencInfo_SetAlgorithm(cencInfo, DRM_ALG_CENC_AES_CTR); 327 if (errNo != AV_ERR_OK) { 328 // 异常处理 329 } 330 errNo = OH_AVCencInfo_SetKeyIdAndIv(cencInfo, keyId, keyIdLen, iv, ivLen); 331 if (errNo != AV_ERR_OK) { 332 // 异常处理 333 } 334 errNo = OH_AVCencInfo_SetSubsampleInfo(cencInfo, encryptedBlockCount, skippedBlockCount, firstEncryptedOffset, 335 subsampleCount, subsamples); 336 if (errNo != AV_ERR_OK) { 337 // 异常处理 338 } 339 errNo = OH_AVCencInfo_SetMode(cencInfo, DRM_CENC_INFO_KEY_IV_SUBSAMPLES_SET); 340 if (errNo != AV_ERR_OK) { 341 // 异常处理 342 } 343 errNo = OH_AVCencInfo_SetAVBuffer(cencInfo, buffer); 344 if (errNo != AV_ERR_OK) { 345 // 异常处理 346 } 347 errNo = OH_AVCencInfo_Destroy(cencInfo); 348 if (errNo != AV_ERR_OK) { 349 // 异常处理 350 } 351 ``` 352 3539. 调用OH_AudioCodec_PushInputBuffer(),写入待解码的数据。 354 355 如果是结束,需要对flag标识成AVCODEC_BUFFER_FLAGS_EOS。 356 357 ```c++ 358 uint32_t index = signal_->inQueue_.front(); 359 auto buffer = signal_->inBufferQueue_.front(); 360 int64_t size; 361 int64_t pts; 362 // size是待解码数据的每帧帧长度。pts是每帧的时间戳,用于指示音频应该何时被播放。 363 // size和pts的获取来源:音视频资源文件或者待解码的数据流 364 // 若是解码音视频资源文件,则需从解封装OH_AVDemuxer_ReadSampleBuffer的buffer中获取 365 // 若是解码数据流,则需要从数据流的提供者获取。 366 // 此处为了介绍解码功能以测试文件中保存的size和pts为示例 367 inputFile_.read(reinterpret_cast<char *>(&size), sizeof(size)); 368 inputFile_.read(reinterpret_cast<char *>(&pts), sizeof(pts)); 369 inputFile_.read((char *)OH_AVBuffer_GetAddr(buffer), size); 370 OH_AVCodecBufferAttr attr = {0}; 371 if (inputFile_->eof()) { 372 attr.size = 0; 373 attr.flags = AVCODEC_BUFFER_FLAGS_EOS; 374 } else { 375 attr.size = size; 376 attr.flags = AVCODEC_BUFFER_FLAGS_NONE; 377 } 378 attr.pts = pts; 379 OH_AVBuffer_SetBufferAttr(buffer, &attr); 380 int32_t ret = OH_AudioCodec_PushInputBuffer(audioDec_, index); 381 if (ret != AV_ERR_OK) { 382 // 异常处理 383 } 384 ``` 385 38610. 调用OH_AudioCodec_FreeOutputBuffer(),输出解码后的PCM码流。 387 388 <!--RP3--> 389 ```c++ 390 uint32_t index = signal_->outQueue_.front(); 391 OH_AVBuffer *data = signal_->outBufferQueue_.front(); 392 // 获取buffer attributes 393 OH_AVCodecBufferAttr attr = {0}; 394 ret = OH_AVBuffer_GetBufferAttr(data, &attr); 395 if (ret != AV_ERR_OK) { 396 // 异常处理 397 } 398 // 将解码完成数据data写入到对应输出文件中 399 pcmOutputFile_.write(reinterpret_cast<char *>(OH_AVBuffer_GetAddr(data)), attr.size); 400 ret = OH_AudioCodec_FreeOutputBuffer(audioDec_, index); 401 if (ret != AV_ERR_OK) { 402 // 异常处理 403 } 404 if (attr.flags == AVCODEC_BUFFER_FLAGS_EOS) { 405 // 结束 406 } 407 ``` 408 <!--RP3End--> 409 41011. (可选)调用OH_AudioCodec_Flush()刷新解码器。 411 调用OH_AudioCodec_Flush()后,解码器仍处于运行态,但会将当前队列清空,将已解码的数据释放。 412 此时需要调用OH_AudioCodec_Start()重新开始解码。 413 使用情况: 414 415 * 在文件EOS之后,需要调用刷新 416 * 在执行过程中遇到可继续执行的错误时(即OH_AudioCodec_IsValid 为true)调用 417 418 ```c++ 419 // 刷新解码器 audioDec_ 420 ret = OH_AudioCodec_Flush(audioDec_); 421 if (ret != AV_ERR_OK) { 422 // 异常处理 423 } 424 // 重新开始解码 425 ret = OH_AudioCodec_Start(audioDec_); 426 if (ret != AV_ERR_OK) { 427 // 异常处理 428 } 429 ``` 430 43112. (可选)调用OH_AudioCodec_Reset()重置解码器。 432 调用OH_AudioCodec_Reset()后,解码器回到初始化的状态,需要调用OH_AudioCodec_Configure()重新配置,然后调用OH_AudioCodec_Start()重新开始解码。 433 434 ```c++ 435 // 重置解码器 audioDec_ 436 ret = OH_AudioCodec_Reset(audioDec_); 437 if (ret != AV_ERR_OK) { 438 // 异常处理 439 } 440 // 重新配置解码器参数 441 ret = OH_AudioCodec_Configure(audioDec_, format); 442 if (ret != AV_ERR_OK) { 443 // 异常处理 444 } 445 ``` 446 44713. 调用OH_AudioCodec_Stop()停止解码器。 448 449 ```c++ 450 // 终止解码器 audioDec_ 451 ret = OH_AudioCodec_Stop(audioDec_); 452 if (ret != AV_ERR_OK) { 453 // 异常处理 454 } 455 ``` 456 45714. 调用OH_AudioCodec_Destroy()销毁解码器实例,释放资源。 458 459 > **说明:** 460 >不要重复销毁解码器 461 462 ```c++ 463 // 调用OH_AudioCodec_Destroy, 注销解码器 464 ret = OH_AudioCodec_Destroy(audioDec_); 465 if (ret != AV_ERR_OK) { 466 // 异常处理 467 } else { 468 audioDec_ = NULL; // 不可重复destroy 469 } 470 ``` 471 472## 相关实例 473 474针对音频解码,有以下相关实例可供参考: 475 476- [音频解码](https://gitee.com/openharmony/multimedia_av_codec/blob/master/test/nativedemo/audio_demo/avcodec_audio_avbuffer_decoder_demo.cpp)