推荐使用OHAudio开发音频播放功能(C/C++)
OHAudio是系统在API version 10中引入的一套C API,此API在设计上实现归一,同时支持普通音频通路和低时延通路。仅支持PCM格式,适用于依赖Native层实现音频输出功能的场景。
OHAudio音频播放状态变化示意图:

使用入门
开发者要使用OHAudio提供的播放能力,需要添加对应的头文件。
以下各步骤示例为片段代码,可通过示例代码右下方链接获取完整示例。
在 CMake 脚本中链接动态库
target_link_libraries(sample PUBLIC libohaudio.so)
添加头文件
开发者通过引入<native_audiostreambuilder.h>和<native_audiorenderer.h>头文件,使用音频播放相关API。
#include <ohaudio/native_audiorenderer.h>
#include <ohaudio/native_audiostreambuilder.h>
开发步骤
详细的API说明请参考OHAudio。
音频流构造器
OHAudio提供OH_AudioStreamBuilder接口,遵循构造器设计模式,用于构建音频流。开发者需要根据业务场景,指定对应的OH_AudioStream_Type。
OH_AudioStream_Type包含两种类型:
- AUDIOSTREAM_TYPE_RENDERER
- AUDIOSTREAM_TYPE_CAPTURER
使用OH_AudioStreamBuilder_Create创建构造器示例:
OH_AudioStreamBuilder* builder;
// ...
OH_AudioStreamBuilder_Create(&builder, AUDIOSTREAM_TYPE_RENDERER);
在音频业务结束之后,开发者应该执行OH_AudioStreamBuilder_Destroy接口来销毁构造器。
OH_AudioStreamBuilder_Destroy(builder);
开发者可以通过以下几个步骤来实现一个简单的播放功能。
实现音频播放
-
创建构造器。
OH_AudioStreamBuilder* builder;// ...OH_AudioStreamBuilder_Create(&builder, AUDIOSTREAM_TYPE_RENDERER); -
配置音频流参数。
关于音频采样率可参考配置合适的音频采样率。
创建音频播放构造器后,可以设置音频流所需要的参数,可以参考下面的案例。
// 设置音频采样率。const int SAMPLING_RATE_48K = 48000;OH_AudioStreamBuilder_SetSamplingRate(builder, SAMPLING_RATE_48K);// 设置音频声道。const int channelCount = 2;OH_AudioStreamBuilder_SetChannelCount(builder, channelCount);// 设置音频采样格式。OH_AudioStreamBuilder_SetSampleFormat(builder, AUDIOSTREAM_SAMPLE_S16LE);// 设置音频流的编码类型。OH_AudioStreamBuilder_SetEncodingType(builder, AUDIOSTREAM_ENCODING_TYPE_RAW);// 设置输出音频流的工作场景。OH_AudioStreamBuilder_SetRendererInfo(builder, AUDIOSTREAM_USAGE_MUSIC);注意,播放的音频数据要通过回调接口写入,开发者要实现回调接口,从API version 12开始支持使用OH_AudioStreamBuilder_SetRendererWriteDataCallback设置数据回调函数。数据回调函数的声明请查看OH_AudioRenderer_OnWriteDataCallback。
-
设置音频回调函数。
多音频并发处理可参考文档处理音频焦点事件,仅接口语言差异。
-
从API version 12开始推荐使用OH_AudioRenderer_OnWriteDataCallback用于写入音频数据。
- 能填满回调所需长度数据的情况下,返回AUDIO_DATA_CALLBACK_RESULT_VALID,系统会取用完整长度的数据缓冲进行播放。请不要在未填满数据的情况下返回AUDIO_DATA_CALLBACK_RESULT_VALID,否则会导致杂音、卡顿等现象。
- 在无法填满回调所需长度数据的情况下,建议开发者返回AUDIO_DATA_CALLBACK_RESULT_INVALID,系统不会处理该段音频数据,然后会再次向应用请求数据,确认数据填满后返回AUDIO_DATA_CALLBACK_RESULT_VALID。
- 回调函数结束后,音频服务会把缓冲中数据放入队列里等待播放,因此请勿在回调外再次更改缓冲中的数据。对于最后一帧,如果数据不够填满缓冲长度,开发者需要使用剩余数据拼接空数据的方式,将缓冲填满,避免缓冲内的历史脏数据对播放效果产生不良的影响。
-
从API version 12开始可通过OH_AudioStreamBuilder_SetFrameSizeInCallback设置audioDataSize的大小。
// 自定义写入数据函数。static OH_AudioData_Callback_Result MyOnWriteData_New(OH_AudioRenderer* renderer,void* userData,void* audioData,int32_t audioDataSize){// 将待播放的数据,按audioDataSize长度写入audioData。// 如果开发者不希望播放某段audioData,返回AUDIO_DATA_CALLBACK_RESULT_INVALID即可。int32_t readCount = fread(audioData, audioDataSize, 1, g_fp);if (readCount < 0) {return AUDIO_DATA_CALLBACK_RESULT_INVALID;}if (feof(g_fp)) {fseek(g_fp, 0, SEEK_SET);}return AUDIO_DATA_CALLBACK_RESULT_VALID;}// 自定义音频中断事件函数。void MyOnInterruptEvent_New(OH_AudioRenderer* renderer,void* userData,OH_AudioInterrupt_ForceType type,OH_AudioInterrupt_Hint hint){// 根据type和hint表示的音频中断信息,更新播放器状态和界面。}// 自定义异常回调函数。void MyOnError_New(OH_AudioRenderer* renderer,void* userData,OH_AudioStream_Result error){// 根据error表示的音频异常信息,做出相应的处理。}// ...// 配置音频中断事件回调函数。OH_AudioRenderer_OnInterruptCallback OnInterruptCb = MyOnInterruptEvent_New;OH_AudioStreamBuilder_SetRendererInterruptCallback(builder, OnInterruptCb, nullptr);// 配置音频异常回调函数。OH_AudioRenderer_OnErrorCallback OnErrorCb = MyOnError_New;OH_AudioStreamBuilder_SetRendererErrorCallback(builder, OnErrorCb, nullptr);// 配置写入音频数据回调函数。OH_AudioRenderer_OnWriteDataCallback writeDataCb = MyOnWriteData_New;OH_AudioStreamBuilder_SetRendererWriteDataCallback(builder, writeDataCb, nullptr); -
-
构造播放音频流。
OH_AudioRenderer* audioRenderer;// ...OH_AudioStreamBuilder_GenerateRenderer(builder, &audioRenderer); -
使用音频流。
音频流中包含以下接口,用来实现对音频流的控制。
接口 说明 OH_AudioStream_Result OH_AudioRenderer_Start(OH_AudioRenderer* renderer) 开始播放。 OH_AudioStream_Result OH_AudioRenderer_Pause(OH_AudioRenderer* renderer) 暂停播放。 OH_AudioStream_Result OH_AudioRenderer_Stop(OH_AudioRenderer* renderer) 停止播放。 OH_AudioStream_Result OH_AudioRenderer_Flush(OH_AudioRenderer* renderer) 释放缓存数据。 OH_AudioStream_Result OH_AudioRenderer_Release(OH_AudioRenderer* renderer) 释放播放实例。 音频流控制接口执行会有耗时(例如OH_AudioRenderer_Stop接口需要播完缓存,单次执行普遍超过50ms),应避免在主线程中直接调用,以免造成界面显示卡顿。
-
释放构造器。
构造器不再使用时,需要释放相关资源。
应用需根据实际业务需求合理使用构造器,按需创建并及时释放,避免占用过多音频资源导致异常。
OH_AudioStreamBuilder_Destroy(builder);
设置音频流音量
开发者可使用OH_AudioRenderer_SetVolume接口设置当前音频流音量值。
float volume = 0.5f;
// 设置当前音频流音量值。
OH_AudioRenderer_SetVolume(audioRenderer, volume);
设置低时延模式
当设备支持低时延通路且采样率设置为48000Hz时,开发者可以使用低时延模式创建播放器,获得更高质量的音频体验。
开发流程与普通播放(实现音频播放)场景一致,仅需要在步骤1创建音频流构造器时,调用OH_AudioStreamBuilder_SetLatencyMode()设置低时延模式。
- 当音频播放场景OH_AudioStream_Usage为AUDIOSTREAM_USAGE_VOICE_COMMUNICATION和AUDIOSTREAM_USAGE_VIDEO_COMMUNICATION时,不支持主动设置低时延模式,系统会根据设备的能力,决策输出的音频通路。
- 低时延通路对于数据处理性能要求较高,应用数据生成缓慢时容易导致卡顿。普通音乐、视频播放场景下不建议设置该模式,仅推荐游戏、K歌等对时延敏感的应用设置低时延模式。
OH_AudioStreamBuilder_SetLatencyMode(builder, AUDIOSTREAM_LATENCY_MODE_FAST);
设置音频声道布局
播放音频文件时,可以通过设置音频的声道布局信息,指定渲染或播放时的扬声器摆位,使得渲染和播放效果更佳,获得更高质量的音频体验。
开发流程与普通播放(实现音频播放)场景一致,仅需要在步骤1创建音频流构造器时,调用OH_AudioStreamBuilder_SetChannelLayout()设置声道布局信息。
当声道布局与声道数不匹配时,创建音频流会失败。建议在设置声道布局时,确认下发的声道布局信息是否正确。
如果不知道准确的声道布局信息,或者开发者需要使用默认声道布局,可以不调用设置声道布局接口,或者下发CH_LAYOUT_UNKNOWN,以使用基于声道数的默认声道布局。
对于HOA(高阶立体环绕声)格式的音频,想要获得正确的渲染和播放效果,必须指定声道布局信息。
OH_AudioStreamBuilder_SetChannelLayout(builder, CH_LAYOUT_STEREO);
播放Audio Vivid格式音源
播放Audio Vivid(菁彩三维声)格式音频文件时,需要使用与普通播放不同的数据写入回调函数,该回调可以同时写入PCM(脉冲编码调制)数据与元数据。
开发流程与普通播放(实现音频播放)场景一致,仅需要在步骤1创建音频流构造器时,调用OH_AudioStreamBuilder_SetWriteDataWithMetadataCallback()设置PCM数据与元数据同时写入的回调函数,同时调用OH_AudioStreamBuilder_SetEncodingType()设置编码类型为AUDIOSTREAM_ENCODING_TYPE_AUDIOVIVID。
在播放Audio Vivid时,帧长是固定的,不可通过OH_AudioStreamBuilder_SetFrameSizeInCallback()设置回调帧长。同时,在设置播放声道数和声道布局时,需要将写入音源的声床数和对象数相加后进行设置。
// 自定义同时写入PCM数据和元数据函数。
int32_t MyOnWriteDataWithMetadata_New(
OH_AudioRenderer* renderer,
void* userData,
void* audioData,
int32_t audioDataSize,
void* metadata,
int32_t metadataSize)
{
// 将待播放的PCM数据和元数据,分别按audioDataSize和metadataSize写入buffer。
return 0;
}
// ...
// 设置编码类型。
OH_AudioStreamBuilder_SetEncodingType(builder, AUDIOSTREAM_ENCODING_TYPE_AUDIOVIVID);
// 配置回调函数。
OH_AudioRenderer_WriteDataWithMetadataCallback metadataCallback = MyOnWriteDataWithMetadata_New;
// 设置同时写入PCM数据和元数据的回调。
OH_AudioStreamBuilder_SetWriteDataWithMetadataCallback(builder, metadataCallback, nullptr);
注意事项
从API version 12开始不再推荐使用OH_AudioRenderer_Callbacks的方式设置音频回调函数。若必须使用,需要注意在设置音频回调函数时,通过下面两种方式中的任意一种来设置音频回调函数,避免不可预期的行为。
-
方式1:请确保OH_AudioRenderer_Callbacks的每一个回调都被自定义的回调方法或空指针初始化。
OH_AudioRenderer_Callbacks callbacks;// ...// 自定义写入数据函数。int32_t MyOnWriteData_Legacy(OH_AudioRenderer* renderer,void* userData,void* buffer,int32_t length){// 将待播放的数据,按length长度写入buffer。return 0;}// 自定义音频中断事件函数。int32_t MyOnInterruptEvent_Legacy(OH_AudioRenderer* renderer,void* userData,OH_AudioInterrupt_ForceType type,OH_AudioInterrupt_Hint hint){// 根据type和hint表示的音频中断信息,更新播放器状态和界面。return 0;}// ...// 配置回调函数,如果需要监听,则赋值。callbacks.OH_AudioRenderer_OnWriteData = MyOnWriteData_Legacy;callbacks.OH_AudioRenderer_OnInterruptEvent = MyOnInterruptEvent_Legacy;// (必选)无触发回调场景,使用空指针初始化。从API version 11开始,开发者如果需要监听设备变化,// 可直接使用OH_AudioRenderer_OutputDeviceChangeCallback替代。callbacks.OH_AudioRenderer_OnStreamEvent = nullptr;// (必选)如果不需要监听,使用空指针初始化。callbacks.OH_AudioRenderer_OnError = nullptr; -
方式2:使用前,初始化并清零结构体。
OH_AudioRenderer_Callbacks callbacks;// ...// 自定义写入数据函数。int32_t MyOnWriteData_Legacy(OH_AudioRenderer* renderer,void* userData,void* buffer,int32_t length){// 将待播放的数据,按length长度写入buffer。return 0;}// 自定义音频中断事件函数。int32_t MyOnInterruptEvent_Legacy(OH_AudioRenderer* renderer,void* userData,OH_AudioInterrupt_ForceType type,OH_AudioInterrupt_Hint hint){// 根据type和hint表示的音频中断信息,更新播放器状态和界面。return 0;}// ...// 使用前,初始化并清零结构体。OH_AudioRenderer_Callbacks callbacks = {0};// 配置需要的回调函数。callbacks.OH_AudioRenderer_OnWriteData = MyOnWriteData_Legacy;callbacks.OH_AudioRenderer_OnInterruptEvent = MyOnInterruptEvent_Legacy;