跳到主要内容

推荐使用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);

开发者可以通过以下几个步骤来实现一个简单的播放功能。

实现音频播放

  1. 创建构造器。

    OH_AudioStreamBuilder* builder;
    // ...
    OH_AudioStreamBuilder_Create(&builder, AUDIOSTREAM_TYPE_RENDERER);
  2. 配置音频流参数。

    关于音频采样率可参考配置合适的音频采样率

    创建音频播放构造器后,可以设置音频流所需要的参数,可以参考下面的案例。

    // 设置音频采样率。
    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

  3. 设置音频回调函数。

    多音频并发处理可参考文档处理音频焦点事件,仅接口语言差异。

    • 从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);
  4. 构造播放音频流。

    OH_AudioRenderer* audioRenderer;
    // ...
    OH_AudioStreamBuilder_GenerateRenderer(builder, &audioRenderer);
  5. 使用音频流。

    音频流中包含以下接口,用来实现对音频流的控制。

    接口说明
    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),应避免在主线程中直接调用,以免造成界面显示卡顿。

  6. 释放构造器。

    构造器不再使用时,需要释放相关资源。

    应用需根据实际业务需求合理使用构造器,按需创建并及时释放,避免占用过多音频资源导致异常。

    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;

示例代码