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