音频编解码:支持AAC/MP3/FLAC等格式处理(194)

📅 2026/7/17 16:09:06
音频编解码:支持AAC/MP3/FLAC等格式处理(194)
在鸿蒙HarmonyOS应用开发中音视频编解码是多媒体处理的核心能力。系统通过AVCodec Kit提供了强大的底层原子能力支持包括 AAC、MP3、FLAC 在内的多种主流音频格式的编码与解码。一、 核心编解码格式支持鸿蒙系统对音频编解码格式的支持非常全面开发者可以通过AVCodec模块直接调用音频解码支持系统支持对多种主流音频格式进行解码包括 AAC、MPEG(MP3)、FLAC、Vorbis、AMR(amrnb/amrwb)、G711MU、APE、AudioVivid11 以及 OPUS。同时支持对应的容器格式解封装如 mp4、m4a、flac、ogg、aac、mp3、amr、raw 和 ape。音频编码支持在音频编码方面系统支持将 PCM 原始音频数据压缩为 AAC、FLAC、MP3、G711mu、AMR、OPUS 等格式并可封装为 mp4、m4a、flac、aac、mp3、raw、amr、ogg 等目标容器格式。// AudioDecoderInit.cpp #include multimedia/player_framework/native_avcodec_audiocodec.h #include multimedia/player_framework/native_avcapability.h OH_AVCodec *CreateAudioDecoder() { // 设置判定是否为编码设置 false 表示当前是解码 bool isEncoder false; // 通过 MimeType 创建 AAC 解码器 OH_AVCodec *audioDec OH_AudioCodec_CreateByMime(OH_AVCODEC_MIMETYPE_AUDIO_AAC, isEncoder); if (audioDec nullptr) { // 异常处理解码器创建失败 } return audioDec; }二、 音频转码与处理流程在实际业务如格式转换、音频剪辑中通常需要经过“解封装 - 解码 - 编码 - 封装”的标准流程解封装Demuxer使用AVDemuxer将完整的音频文件如 .m4a解封装获取内部的编码数据流如 AAC 码流。解码Decode将编码数据送入AVCodec解码器还原为 PCM 原始音频数据。编码Encode将 PCM 数据送入目标格式的编码器如 MP3 或 FLAC 编码器压缩为新的编码数据。封装Muxer使用AVMuxer将新的编码数据按照目标格式封装为最终的音频文件。// Transcoding.cpp #include multimedia/player_framework/native_avcodec_audiocodec.h class Transcoding { public: int32_t Init(SampleInfo sampleInfo) { sampleInfo_ sampleInfo; // 1. 创建解复用器 (Demuxer) CreateDemuxer(); // 2. 创建音频解码器 (Decoder) CreateAudioDecoder(); // 3. 创建音频编码器 (Encoder) CreateAudioEncoder(); // 4. 创建复用器 (Muxer) CreateMuxer(); return AV_ERR_OK; } };三、 开发实战与生命周期管理使用 Native API 进行音频编解码时需要严格遵循生命周期并处理异步回调创建编解码器通过 MimeType如OH_AVCODEC_MIMETYPE_AUDIO_AAC或 Codec Name 创建编码器或解码器实例。注册异步回调必须注册OH_AVCodecCallback处理OH_AVCodecOnNeedInputBuffer请求输入数据、OH_AVCodecOnNewOutputBuffer输出解码/编码数据以及OH_AVCodecOnError异常处理等关键事件。配置与启动通过OH_AudioCodec_Configure设置采样率、声道数、码率等参数随后调用Prepare和Start启动编解码流程。资源释放处理完成后必须按顺序调用Stop、Reset和Destroy接口释放底层资源防止内存泄漏。// AudioCodecCallback.cpp #include mutex #include queue #include multimedia/player_framework/native_avcodec_audiocodec.h // 1. 初始化线程同步队列 class ADecBufferSignal { public: std::mutex inMutex_; std::mutex outMutex_; std::condition_variable inCond_; std::condition_variable outCond_; std::queueuint32_t inQueue_; std::queueuint32_t outQueue_; std::queueOH_AVBuffer * inBufferQueue_; std::queueOH_AVBuffer * outBufferQueue_; }; // 2. 实现核心回调函数 static void OnInputBufferAvailable(OH_AVCodec *codec, uint32_t index, OH_AVBuffer *data, void *userData) { ADecBufferSignal *signal static_castADecBufferSignal *(userData); std::unique_lockstd::mutex lock(signal-inMutex_); // 将待解码的码流数据送入输入队列 signal-inQueue_.push(index); signal-inBufferQueue_.push(data); signal-inCond_.notify_all(); } static void OnOutputBufferAvailable(OH_AVCodec *codec, uint32_t index, OH_AVBuffer *data, void *userData) { ADecBufferSignal *signal static_castADecBufferSignal *(userData); std::unique_lockstd::mutex lock(signal-outMutex_); // 将解码完成的 PCM 数据送入输出队列 signal-outQueue_.push(index); signal-outBufferQueue_.push(data); signal-outCond_.notify_all(); } // 3. 注册回调并启动流程 void RegisterAndStart(OH_AVCodec *audioDec, ADecBufferSignal *signal_) { OH_AVCodecCallback cb_ { nullptr, // OH_AVCodecOnError nullptr, // OH_AVCodecOnStreamChanged OnInputBufferAvailable, // OH_AVCodecOnNeedInputBuffer OnOutputBufferAvailable // OH_AVCodecOnNewOutputBuffer }; int32_t ret OH_AudioCodec_RegisterCallback(audioDec, cb_, signal_); if (ret AV_ERR_OK) { OH_AudioCodec_Prepare(audioDec); OH_AudioCodec_Start(audioDec); } }四、 性能优化容器与编码的匹配在进行音频封装时必须确保编码格式与目标容器兼容。例如系统 Muxer 目前不支持将 OPUS 封装为 OGG 容器若需实现此功能开发者需自行实现 OGG 容器的封装逻辑。异步回调防阻塞在OH_AVCodecCallback回调函数中严禁执行耗时操作或再次调用编解码器接口应通过队列Queue和条件变量将数据传递到独立的工作线程中处理。参数动态变化处理对于 Audio Vivid、AAC、FLAC 等格式解码过程中可能会发生采样率或声道数的变化。需注册OH_AVCodecOnStreamChanged回调及时获取并更新输出参数避免音频播放异常。// StreamChangedAndCapability.cpp #include multimedia/player_framework/native_avcodec_audiocodec.h #include multimedia/player_framework/native_avcapability.h // 1. 处理解码过程中的动态参数变化如采样率、声道数改变 static void OnOutputFormatChanged(OH_AVCodec *codec, OH_AVFormat *format, void *userData) { int32_t sampleRate; int32_t channelCount; // 实时获取并更新输出参数避免音频播放异常 if (OH_AVFormat_GetIntValue(format, OH_MD_KEY_AUD_SAMPLE_RATE, sampleRate) AV_ERR_OK) { // 动态调整下游音频渲染器的采样率配置 } if (OH_AVFormat_GetIntValue(format, OH_MD_KEY_AUD_CHANNEL_COUNT, channelCount) AV_ERR_OK) { // 动态调整声道数 } } // 2. 编码前校验参数支持情况防错指南 bool ValidateEncoderParams(const char *mimeType, int32_t sampleRate) { // 获取编码器能力 OH_AVCapability *capability OH_AVCodec_GetCapability(mimeType, true); if (capability nullptr) return false; // 确认待配置的采样率是否被支持 const int32_t *sampleRates nullptr; uint32_t sampleRateNum 0; OH_AVCapability_GetAudioSupportedSampleRates(capability, sampleRates, sampleRateNum); for (uint32_t i 0; i sampleRateNum; i) { if (sampleRates[i] sampleRate) { return true; // 参数合法 } } return false; // 参数不合法需调整 }五、 音频编解码实战Native API 异步回调与队列管理鸿蒙的 Native 音频编解码采用高度异步的设计开发者需要通过 C 的队列与条件变量来安全地流转数据。回调函数注册与数据流转开发者必须通过OH_AudioCodec_RegisterCallback注册回调集合。当解码器准备好接收数据时会触发OH_AVCodecOnNeedInputBuffer回调此时应将待解码的码流数据送入输入队列inBufferQueue_当解码完成时触发OH_AVCodecOnNewOutputBuffer将解码后的 PCM 数据送入输出队列outBufferQueue_。多线程安全与防阻塞由于回调函数中严禁进行耗时操作必须使用std::mutex和std::condition_variable进行线程同步。当主工作线程从队列中取出 Buffer 处理完毕后务必及时释放归还否则达到 Buffer 上限后解码器将停止工作。动态参数监听针对 AAC、FLAC 等格式解码过程中可能发生采样率或声道数变化。通过注册OH_AVCodecOnStreamChanged回调应用可实时获取最新的OH_MD_KEY_AUD_SAMPLE_RATE等参数从而动态调整下游的音频渲染器配置。// NativeAudioCodec.cpp #include multimedia/player_framework/native_avcodec_audiocodec.h #include mutex #include queue #include condition_variable // 1. 定义线程安全的 Buffer 信号队列 class AEncBufferSignal { public: std::mutex inMutex_; std::mutex outMutex_; std::condition_variable inCond_; std::condition_variable outCond_; std::queueuint32_t inQueue_; std::queueuint32_t outQueue_; std::queueOH_AVBuffer * inBufferQueue_; std::queueOH_AVBuffer * outBufferQueue_; }; // 2. 实现输入与输出回调 static void OnInputBufferAvailable(OH_AVCodec *codec, uint32_t index, OH_AVBuffer *data, void *userData) { AEncBufferSignal *signal static_castAEncBufferSignal *(userData); std::unique_lockstd::mutex lock(signal-inMutex_); signal-inQueue_.push(index); signal-inBufferQueue_.push(data); signal-inCond_.notify_all(); // 通知工作线程数据已就绪 } static void OnOutputBufferAvailable(OH_AVCodec *codec, uint32_t index, OH_AVBuffer *data, void *userData) { AEncBufferSignal *signal static_castAEncBufferSignal *(userData); std::unique_lockstd::mutex lock(signal-outMutex_); signal-outQueue_.push(index); signal-outBufferQueue_.push(data); signal-outCond_.notify_all(); // 通知工作线程处理输出数据 } // 3. 监听动态参数变化如采样率改变 static void OnStreamChanged(OH_AVCodec *codec, OH_AVFormat *format, void *userData) { int32_t sampleRate 0; OH_AVFormat_GetIntValue(format, OH_MD_KEY_AUD_SAMPLE_RATE, sampleRate); // 根据新的 sampleRate 动态调整下游音频渲染器配置 } // 4. 注册回调并启动 void InitAudioCodec(OH_AVCodec *audioEnc, AEncBufferSignal *signal_) { OH_AVCodecCallback cb_ { nullptr, // OnError OnStreamChanged, // OnStreamChanged OnInputBufferAvailable, // OnNeedInputBuffer OnOutputBufferAvailable // OnNewOutputBuffer }; OH_AudioCodec_RegisterCallback(audioEnc, cb_, signal_); OH_AudioCodec_Prepare(audioEnc); OH_AudioCodec_Start(audioEnc); }六、 跨平台与 ArkTS 层的高效集成对于不需要进行底层 C 复杂处理的常规音频业务鸿蒙同样提供了高效的跨平台与 ArkTS 层解决方案。Flutter 跨平台音频处理在 Flutter for OpenHarmony 开发中可使用flutter_sound插件。该插件完整适配了鸿蒙的底层能力支持 AAC、MP3、FLAC、Opus 等格式的录制与播放。开发者只需在pubspec.yaml中引入依赖并在module.json5中声明ohos.permission.MICROPHONE权限即可通过FlutterSoundPlayer快速实现音频流的控制。ArkTS 层播放器实例管理在 ArkTS 层使用AVPlayer播放音频时极易因实例未释放导致内存泄漏。最佳实践是采用单例模式管理AVPlayer实例或在每次播放结束后严格调用release()方法。同时应遵循“创建 - 配置 - 准备 - 播放 - 释放”的黄金生命周期流程避免重复触发MediaService启动带来的性能开销。// ArkTSAudioManager.ets import { media } from kit.MediaKit; export class ArkTSAudioManager { private static instance: ArkTSAudioManager; private avPlayer: media.AVPlayer | null null; // 1. 单例模式管理防止内存泄漏 public static getInstance(): ArkTSAudioManager { if (!this.instance) { this.instance new ArkTSAudioManager(); } return this.instance; } // 2. 遵循黄金生命周期创建 - 配置 - 准备 - 播放 public async playAudio(url: string) { try { // 释放旧实例 await this.release(); // 创建新实例 this.avPlayer await media.createAVPlayer(); // 状态机监听 this.avPlayer.on(stateChange, (state: string) { if (state initialized) { this.avPlayer?.prepare(); } else if (state prepared) { this.avPlayer?.play(); } }); // 设置资源路径 this.avPlayer.url url; } catch (err) { console.error(AVPlayer 初始化失败:, err); } } // 3. 严格的资源释放 public async release() { if (this.avPlayer) { this.avPlayer.off(stateChange); // 取消监听 await this.avPlayer.stop(); await this.avPlayer.reset(); await this.avPlayer.release(); this.avPlayer null; } } }