【鸿蒙实战开发】HarmonyOS-AudioVivid本领详解

打印 上一主题 下一主题

主题 1030|帖子 1030|积分 3090

前言

Audio Vivid(菁彩三维声)是全球首个基于AI技能的音频编解码标准,由天下超高清视频产业联盟(UWA联盟)与数字音视频编解码技能标准工作组(AVS)团结订定,共同发布。包含音频PCM数据以及元数据的音频格式,相比传统立体声音源,Audio Vivid包含音频内容源的元数据信息,能够还原物理和感知天下中的真实听感,打造极致的沉浸式听觉体验。
HarmonyOS打造全链路高清空间音频系统,包含Audio Vivid编解码、空间音频渲染算法等关键本领,并在各类终端产物逐步构建全场景空间音频特性,从传统立体声升级到三维声,得到更好的音质、更沉浸的空间感。
HarmonyOS支持播放Audio Vivid格式音源,并在耳机实现双耳空间音频渲染重放,在手机、平板、PC等支持外放空间音频渲染重放。系统的空间音频渲染本领无感接入,不必要做特定适配。
搭配HarmonyOS高清空间音频渲染本领,将音乐中的人声和各种乐器声作为独立的声音对象,重新定义各种声音对象的位置、移动轨迹和声音大小、远近等要素,实现声音在听众四周及上方全面萦绕,实现更佳的空间音频沉浸式体验,得到影院、音乐厅等的临场感与艺术体验。佩戴支持头动跟踪的耳机收听空间音频,还能实现动态头动跟踪,让声音围绕听众重新定位,还原逼真的临场感。
以下主要介绍利用HarmonyOS进行Audio Vivid格式音源的端到端播放的流程。
Audio Vivid端到端播放包罗调用系统编解码本领进行解封装、解码,以及调用系统播放本领进行渲染播放两个部门。
AudioVivid解封装

获取到Audio Vivid封装的mp4文件后,先调用解封装相干接口,选中音频轨,读取每一帧Audio Vivid,送入解码器中。详细的API请参考音频解封装API参考。
在Cmake脚本中链接到动态库

  1. target_link_libraries(sample PUBLIC
  2. libnative_media_codecbase.so libnative_media_core.so
  3. libnative_media_acodec.so libnative_media_avdemuxer.so libnative_media_avsource.so
  4. )
复制代码
添加头文件

  1. //解封装头文件
  2. #include "multimedia/player_framework/native_avdemuxer.h"
  3. // 解封装解码传递信息结构体
  4. struct AudioSampleInfo {
  5. std::string audioCodecMime = "";
  6. int32_t audioSampleForamt = 0;
  7. int32_t audioSampleRate = 0;
  8. int32_t audioChannelCount = 0;
  9. int64_t audioChannelLayout = 0;
  10. uint8_t audioCodecConfig[100] = {0};
  11. size_t audioCodecSize = 0;
  12. };
  13. AudioSampleInfo  info;
复制代码
开发步调


  • 创建解封装实例。
  1. // ts code获取fd和size
  2. let inputFile = fs.openSync(filepath,fs.OpenMode.READ_ONLY);
  3. if(inputFile){
  4.     let inputFileState = fs.stateSync(inputFile.fd);
  5.     let inputFileSize = inputFIleState.size;
  6. }
复制代码
  1. //C++ code
  2. OH_AVSource *source = OH_AVSource_CreateWithFD(inputFd,0,inputFileSize);
  3. OH_AVDemuxer *demuxer = OH_AVDemuxer_CreateWithSource(source);
  4. auto sourceFormat = std::shared_ptr<OH_AVFormat>(OH_AVSource_GetSourceFormat(source_), OH_AVFormat_Destroy);
  5. int32_t trackCount = 0;
  6. OH_AVFormat_GetIntValue(sourceFormat.get(), OH_MD_KEY_TRACK_COUNT, &trackCount);
复制代码

  • 选中音频轨。
  1. int32_t trackCount = 0;
  2. OH_AVFormat_GetIntValue(sourceFormat.get(), OH_MD_KEY_TRACK_COUNT, &trackCount);
  3. for (int32_t index = 0; index < trackCount; index++) {
  4. int trackType = -1;
  5. auto trackFormat =
  6.     std::shared_ptr<OH_AVFormat>(OH_AVSource_GetTrackFormat(source_, index), OH_AVFormat_Destroy);
  7. // 获取轨道类型
  8. OH_AVFormat_GetIntValue(trackFormat.get(), OH_MD_KEY_TRACK_TYPE, &trackType);
  9. // 判断当前轨道为音频轨
  10. if (trackType == MEDIA_TYPE_AUD) {
  11.     // 选中音频轨
  12.     OH_AVDemuxer_SelectTrackByID(demuxer, index);
  13.     // 获取位深
  14.     OH_AVFormat_GetIntValue(trackFormat.get(), OH_MD_KEY_AUDIO_SAMPLE_FORMAT, &info.audioSampleForamt);
  15.     // 获取声道数
  16.     OH_AVFormat_GetIntValue(trackFormat.get(), OH_MD_KEY_AUD_CHANNEL_COUNT, &info.audioChannelCount);
  17.     // 获取声道布局
  18.     OH_AVFormat_GetLongValue(trackFormat.get(), OH_MD_KEY_CHANNEL_LAYOUT, &info.audioChannelLayout);
  19.     // 获取采样率
  20.     OH_AVFormat_GetIntValue(trackFormat.get(), OH_MD_KEY_AUD_SAMPLE_RATE, &info.audioSampleRate);
  21.     // 获取额外配置信息
  22.     uint8_t *addr = nullptr;
  23.     OH_AVFormat_GetBuffer(trackFormat.get(), OH_MD_KEY_CODEC_CONFIG, &addr, &info.audioCodecSize);
  24.     memcpy((void *)info.audioCodecConfig, (void *)addr, info.audioCodecSize);
  25.     // 获取解码器类型
  26.     char *audioCodecMime;
  27.     OH_AVFormat_GetStringValue(trackFormat.get(), OH_MD_KEY_CODEC_MIME, const_cast<char const **>(&audioCodecMime));
  28.     info.audioCodecMime = audioCodecMime;
  29.     int32_t trackId = index;
  30.     break;
  31.     }
  32. }
复制代码

  • 读取每一帧数据。
  1. OH_AVBuffer *buffer;
  2. int32_t ret = OH_AVDemuxer_ReadSampleBuffer(demuxer, trackId, buffer);
复制代码

  • 释放解封装实例。
  1.   int32_t Release()
  2. {
  3.     if (demuxer != nullptr) {
  4.         OH_AVDemuxer_Destroy(demuxer);
  5.         demuxer = nullptr;
  6.     }
  7.     if (source != nullptr) {
  8.         OH_AVSource_Destroy(source);
  9.         source = nullptr;
  10.     }
  11.     return AVCODEC_SAMPLE_ERR_OK;
  12. }
复制代码
AudioVivid解码

获取解封装后的数据,送入解码器中,利用解码器获取PCM和Metadata元数据。详细的API请参考音频解码API参考。
在Cmake脚本中链接到动态库

  1. target_link_libraries(sample PUBLIC
  2. libnative_media_codecbase.so libnative_media_core.so
  3. libnative_media_acodec.so libnative_media_avdemuxer.so libnative_media_avsource.so
  4. )
复制代码
添加头文件

  1. //解封装头文件
  2. #include "multimedia/player_framework/native_avdemuxer.h"
  3. // 解封装解码传递信息结构体
  4. struct AudioSampleInfo {
  5. std::string audioCodecMime = "";
  6. int32_t audioSampleForamt = 0;
  7. int32_t audioSampleRate = 0;
  8. int32_t audioChannelCount = 0;
  9. int64_t audioChannelLayout = 0;
  10. uint8_t audioCodecConfig[100] = {0};
  11. size_t audioCodecSize = 0;
  12. };
  13. AudioSampleInfo  info;
复制代码
定义相干实例

定义CodecBufferInfo
解码码流的属性定义,为后面传给播放的码流数据封装。
  1. struct CodecBufferInfo {
  2.     uint32_t bufferIndex = 0;
  3.     uintptr_t *buffer = nullptr;
  4.     uint8_t *bufferAddr = nullptr;
  5.     OH_AVCodecBufferAttr attr = {0, 0, 0, AVCODEC_BUFFER_FLAGS_NONE};
  6.     CodecBufferInfo(uint8_t *addr) : bufferAddr(addr){};
  7.     CodecBufferInfo(uint8_t *addr, int32_t bufferSize)
  8.         : bufferAddr(addr), attr({0, bufferSize, 0, AVCODEC_BUFFER_FLAGS_NONE}){};
  9.     CodecBufferInfo(uint32_t argBufferIndex, OH_AVMemory *argBuffer, OH_AVCodecBufferAttr argAttr)
  10.         : bufferIndex(argBufferIndex), buffer(reinterpret_cast<uintptr_t *>(argBuffer)), attr(argAttr){};
  11.     CodecBufferInfo(uint32_t argBufferIndex, OH_AVMemory *argBuffer)
  12.         : bufferIndex(argBufferIndex), buffer(reinterpret_cast<uintptr_t *>(argBuffer)){};
  13.     CodecBufferInfo(uint32_t argBufferIndex, OH_AVBuffer *argBuffer)
  14.         : bufferIndex(argBufferIndex), buffer(reinterpret_cast<uintptr_t *>(argBuffer)) {
  15.         OH_AVBuffer_GetBufferAttr(argBuffer, &attr);
  16.     };
  17. };
复制代码
定义解码工作队列
  1. class CodecUserData {
  2. public:
  3.     SampleInfo *sampleInfo = nullptr;
  4.     // 输入帧数
  5.     uint32_t inputFrameCount_ = 0;
  6.     // 输入队列锁,防止多线程同时操作输入队列
  7.     std::mutex inputMutex_;
  8.     // 输入线程的条件变量,当输入队列为空时用于阻塞输入线程
  9.     std::condition_variable inputCond_;
  10.     // 输入buffer队列,存放编解码器传给用户用来写入输入数据的buffer
  11.     std::queue<CodecBufferInfo> inputBufferInfoQueue_;
  12.     // 输出帧数
  13.     uint32_t outputFrameCount_ = 0;
  14.     // 输出队列锁,防止多线程同时操作输出队列
  15.     std::mutex outputMutex_;
  16.     // 输出线程的条件变量,当输出队列为空时用于阻塞输出线程
  17.     std::condition_variable outputCond_;
  18.     std::mutex renderMutex_;
  19.     std::condition_variable renderCond_;
  20.     // 输出buffer队列,存放编解码器传给用户用来存放输出数据的buffer
  21.     std::queue<CodecBufferInfo> outputBufferInfoQueue_;
  22.     std::shared_ptr<AudioDecoder> audioCodec_;
  23.     std::queue<unsigned char> renderQueue_;
  24.     void ClearQueue() {
  25.         {
  26.             std::unique_lock<std::mutex> lock(inputMutex_);
  27.             auto emptyQueue = std::queue<CodecBufferInfo>();
  28.             inputBufferInfoQueue_.swap(emptyQueue);
  29.         }
  30.         {
  31.             std::unique_lock<std::mutex> lock(outputMutex_);
  32.             auto emptyQueue = std::queue<CodecBufferInfo>();
  33.             outputBufferInfoQueue_.swap(emptyQueue);
  34.         }
  35.     }
  36. };
复制代码
定义回调函数
  1. class SampleCallback {
  2. public:
  3.     // 报错回调函数,当编解码器内部报错时调用,返回给用户相应错误码
  4.     static void OnCodecError(OH_AVCodec *codec, int32_t errorCode, void *userData);
  5.     // 参数修改回调函数,当编解码器参数被修改时调用,返回给用户被修改后的format参数
  6.     static void OnCodecFormatChange(OH_AVCodec *codec, OH_AVFormat *format, void *userData);
  7.     // 输入回调函数,当编解码器需要输入时调用,返回给用户用来写入输入数据的buffer及其对应的index
  8.     static void OnNeedInputBuffer(OH_AVCodec *codec, uint32_t index, OH_AVBuffer *buffer, void *userData);
  9.     // 输出回调函数,当编解码器生成新的输出数据时调用,返回给用户用来存放输出数据的buffer及其对应的index
  10.     static void OnNewOutputBuffer(OH_AVCodec *codec, uint32_t index, OH_AVBuffer *buffer, void *userData);
  11. };
  12. void SampleCallback::OnCodecError(OH_AVCodec *codec, int32_t errorCode, void *userData) {
  13.     (void)codec;
  14.     (void)errorCode;
  15.     (void)userData;
  16. }
  17. void SampleCallback::OnCodecFormatChange(OH_AVCodec *codec, OH_AVFormat *format, void *userData) {
  18. }
  19. void SampleCallback::OnNeedInputBuffer(OH_AVCodec *codec, uint32_t index, OH_AVBuffer *buffer, void *userData) {
  20.     if (userData == nullptr) {
  21.         return;
  22.     }
  23.     (void)codec;
  24.     CodecUserData *codecUserData = static_cast<CodecUserData *>(userData);
  25.     std::unique_lock<std::mutex> lock(codecUserData->inputMutex_);
  26.     // 将输入buffer存放到输入队列中
  27.     codecUserData->inputBufferInfoQueue_.emplace(index, buffer);
  28.     // 通知输入线程开始运行
  29.     codecUserData->inputCond_.notify_all();
  30. }
  31. void SampleCallback::OnNewOutputBuffer(OH_AVCodec *codec, uint32_t index, OH_AVBuffer *buffer, void *userData) {
  32.     if (userData == nullptr) {
  33.         return;
  34.     }
  35.     (void)codec;
  36.     CodecUserData *codecUserData = static_cast<CodecUserData *>(userData);
  37.     std::unique_lock<std::mutex> lock(codecUserData->outputMutex_);
  38.     // 将输出buffer存放到输出队列中
  39.     codecUserData->outputBufferInfoQueue_.emplace(index, buffer);
  40.     // 通知输出线程开始运行
  41.     codecUserData->outputCond_.notify_all();
  42. }
复制代码
开发步调


  • 创建解码实例。
  1. // 创建解码器
  2. OH_AVCodec * decoder = OH_AudioCodec_CreateByMime(info.audioCodecMime,false);
  3. // 参数配置
  4. OH_AVFormat *format = OH_AVFormat_Create();
  5. OH_AVFormat_SetIntValue(format, OH_MD_KEY_AUDIO_SAMPLE_FORMAT, SAMPLE_S16LE); //或者S24LE
  6. OH_AVFormat_SetIntValue(format, OH_MD_KEY_AUD_CHANNEL_COUNT, sampleInfo.audioChannelCount);
  7. OH_AVFormat_SetIntValue(format, OH_MD_KEY_AUD_SAMPLE_RATE, sampleInfo.audioSampleRate);
  8. OH_AVFormat_SetIntValue(format, OH_MD_KEY_AAC_IS_ADTS, 1);
  9. OH_AVFormat_SetLongValue(format, OH_MD_KEY_BITRATE, 96422);//码率,当前作为参考,解封装也可以获取到
  10. OH_AVFormat_SetBuffer(format, OH_MD_KEY_CODEC_CONFIG, sampleInfo.audioCodecConfig, sampleInfo.audioCodecSize);
  11. bool res = OH_AVFormat_SetLongValue(format, OH_MD_KEY_CHANNEL_LAYOUT, sampleInfo.audioChannelLayout);
  12. ret = OH_AudioCodec_Configure(decoder, format);
  13. OH_AVFormat_Destroy(format);
  14. format = nullptr;
  15. // 设置回调,用于输入输出buffer准备完毕后由系统回调出来
  16. int32_t ret = OH_AudioCodec_RegisterCallback(decoder,
  17.     {SampleCallback::OnCodecError, SampleCallback::OnCodecFormatChange,
  18.      SampleCallback::OnNeedInputBuffer, SampleCallback::OnNewOutputBuffer},codecUserData);
  19. // 准备回调和参数设置完毕后通知系统解码器准备好了,下一步准备启动。
  20. ret = OH_AudioCodec_Prepare(decoder)
复制代码

  • 音频写入解码器。
  1. int32_t PushInputData(CodecBufferInfo &info)
  2. {
  3.     int32_t  ret = OH_AVBuffer_SetBufferAttr(reinterpret_cast<OH_AVBuffer *>(info.buffer), &info.attr);
  4.     ret = OH_AudioCodec_PushInputBuffer(decoder, info.bufferIndex);
  5.     return 0;
  6. }
复制代码

  • 释放利用过的输出码流。
  1. CodecUserData*audioDecContext_ = new CodecUserData;
  2. void AudioDecInputThread()
  3. {
  4.     while (true) {
  5.         if(!isStarted_){
  6.            return;  
  7.         }
  8.         std::unique_lock<std::mutex> lock(audioDecContext_->inputMutex_);
  9.         // 阻塞输入线程,直接程序运行结束,或者输入队列不为空
  10.         bool condRet = audioDecContext_->inputCond_.wait_for(
  11.             lock, 5s, [this]() { return !isStarted_ || !audioDecContext_->inputBufferInfoQueue_.empty(); });
  12.         if(!isStarted_ || audioDecContext_->inputBufferInfoQueue_.empty()){
  13.            return;  
  14.         }
  15.         // 获取输入buffer
  16.         CodecBufferInfo bufferInfo = audioDecContext_->inputBufferInfoQueue_.front();
  17.         audioDecContext_->inputBufferInfoQueue_.pop();
  18.         audioDecContext_->inputFrameCount_++;
  19.         lock.unlock();
  20.         // 从解封装器中读取一帧数据写入输入buffer
  21.         demuxer_->ReadSample(demuxer_->GetAudioTrackId(), reinterpret_cast<OH_AVBuffer *>(bufferInfo.buffer), bufferInfo.attr);
  22.         int32_t ret = audioDecoder_->PushInputData(bufferInfo);
  23.         if(ret != 0){
  24.             return;
  25.         }
  26.         if(bufferInfo.attr.flags & AVCODEC_BUFFER_FLAGS_EOS){
  27.             return;
  28.         }
  29.     }
  30.     // StartRelease();
  31. }
复制代码

  • 音频写入线程。
  1. CodecUserData*audioDecContext_ = new CodecUserData;
  2. void AudioDecInputThread()
  3. {
  4.     while (true) {
  5.         if(!isStarted_){
  6.            return;  
  7.         }
  8.         std::unique_lock<std::mutex> lock(audioDecContext_->inputMutex_);
  9.         // 阻塞输入线程,直接程序运行结束,或者输入队列不为空
  10.         bool condRet = audioDecContext_->inputCond_.wait_for(
  11.             lock, 5s, [this]() { return !isStarted_ || !audioDecContext_->inputBufferInfoQueue_.empty(); });
  12.         if(!isStarted_ || audioDecContext_->inputBufferInfoQueue_.empty()){
  13.            return;  
  14.         }
  15.         // 获取输入buffer
  16.         CodecBufferInfo bufferInfo = audioDecContext_->inputBufferInfoQueue_.front();
  17.         audioDecContext_->inputBufferInfoQueue_.pop();
  18.         audioDecContext_->inputFrameCount_++;
  19.         lock.unlock();
  20.         // 从解封装器中读取一帧数据写入输入buffer
  21.         demuxer_->ReadSample(demuxer_->GetAudioTrackId(), reinterpret_cast<OH_AVBuffer *>(bufferInfo.buffer), bufferInfo.attr);
  22.         int32_t ret = audioDecoder_->PushInputData(bufferInfo);
  23.         if(ret != 0){
  24.             return;
  25.         }
  26.         if(bufferInfo.attr.flags & AVCODEC_BUFFER_FLAGS_EOS){
  27.             return;
  28.         }
  29.     }
  30.     // StartRelease();
  31. }
复制代码

  • 音频解码输出线程。
  1. void AudioDecOutputThread()
  2. {
  3.     while (true) {
  4.         if(!isStarted_){
  5.            return;  
  6.         }
  7.         std::unique_lock<std::mutex> lock(audioDecContext_->outputMutex_);
  8.         // 阻塞输出线程,直接程序运行结束,或者输出队列不为空
  9.         bool condRet = audioDecContext_->outputCond_.wait_for(
  10.             lock, 5s, [this]() { return !isStarted_ || !audioDecContext_->outputBufferInfoQueue_.empty(); });
  11.         if(!isStarted_ || audioDecContext_->outputBufferInfoQueue_.empty()){
  12.            return;  
  13.         }
  14.         // 获取输出buffer
  15.         CodecBufferInfo bufferInfo = audioDecContext_->outputBufferInfoQueue_.front();
  16.         audioDecContext_->outputBufferInfoQueue_.pop();
  17.         if(bufferInfo.attr.flags & AVCODEC_BUFFER_FLAGS_EOS){
  18.            return;  
  19.         }
  20.         audioDecContext_->outputFrameCount_++;
  21.         // 获取解码后的pcm数据
  22.         uint8_t *source = OH_AVBuffer_GetAddr(reinterpret_cast<OH_AVBuffer *>(bufferInfo.buffer));
  23.         OH_AVFormat * format = OH_AVBuffer_GetParameter(data);
  24.         uint8_t * metadata;
  25.         size_t size;
  26.         // 获取元数据
  27.         OH_AVFormat_GetBuffer(format, OH_MD_KEY_AUDIO_VIVID_METADATA, &metadata, &size);
  28. #ifdef DEBUG_DECODE
  29.         if (audioOutputFile_.is_open()) {
  30.             audioOutputFile_.write((const char*)OH_AVBuffer_GetAddr(reinterpret_cast<OH_AVBuffer *>(bufferInfo.buffer)), bufferInfo.attr.size);
  31.         }
  32. #endif
  33.         lock.unlock();
  34.         int32_t ret = audioDecoder_->FreeOutputData(bufferInfo.bufferIndex);
  35.         if(ret != 0){
  36.             return;
  37.         }
  38.     }
  39. }
复制代码

  • 启动解码。
  1. int ret = OH_AudioCodec_Start(decoder);
复制代码

  • 停止和释放实例。
  1. OH_AudioCodec_Stop(decoder);
  2. OH_AudioCodec_Destroy(decoder);
  3. decoder = nullptr;
复制代码
AudioVivid播放

在获取到解码后的Audio Vivid的PCM数据和元数据之后,可以调用OHAudio的相干播放接口,进行Audio Vivid格式音源的渲染播放。详细的API说明请参考OHAudio API参考。
在Cmake脚本中链接到动态库

  1. target_link_libraries(sample PUBLIC libohaudio.so)
复制代码
添加头文件

  1. #include <ohaudio/native_audiorenderer.h>
  2. #include <ohaudio/native_audiostreambuilder.h>
复制代码
开发步调

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

  • 创建构造器。
    OHAudio提供OH_AudioStreamBuilder接口,遵照构造器筹划模式,用于构建音频流。在Audio Vivid播放场景下,必要选择OH_AudioStream_Type为AUDIOSTREAM_TYPE_RENDERER,创建一个渲染播放范例的音频构造器。
  1. OH_AudioStreamBuilder* builder;
  2. OH_AudioStreamBuilder_Create(&builder, AUDIOSTREAM_TYPE_RENDERER);
复制代码

  • 配置音频流参数。
    创建音频播放构造器后,可以设置音频流所必要的参数,可以参考以下案例。
    Audio Vivid音源搭配系统空间音频渲染算法,播放效果和体验最佳。系统会根据输出音频流的工作场景(OH_AudioStream_Usage),选择利用对应的空间音频渲染效果,当前支持的工作场景包罗音乐、电影和有声读物。
  1. // 设置音频采样率为48000Hz
  2. OH_AudioStreamBuilder_SetSamplingRate(builder, 48000);
  3. // 设置音频声道为10 (假定输入Audio Vivid音源是5.1.2声床 + 2对象格式)
  4. OH_AudioStreamBuilder_SetChannelCount(builder, 10);
  5. // 设置音频声道布局为5.1.2 (声道布局只考虑声床,若想使用默认声道布局,可以传入 CH_LAYOUT_UNKNOWN 参数)
  6. OH_AudioStreamBuilder_SetChannelLayout(builder, CH_LAYOUT_5POINT1POINT2);
  7. // 设置音频采样格式
  8. OH_AudioStreamBuilder_SetSampleFormat(builder, AUDIOSTREAM_SAMPLE_S16LE);
  9. // 设置音频流的编码类型为Audio Vivid编码类型
  10. OH_AudioStreamBuilder_SetEncodingType(builder, AUDIOSTREAM_ENCODING_TYPE_AUDIOVIVID);
  11. // 设置输出音频流的工作场景,根据实际工作场景选择音乐、电影、有声读物等类型
  12. OH_AudioStreamBuilder_SetRendererInfo(builder, AUDIOSTREAM_USAGE_MUSIC);
复制代码

  • 设置音频回调函数。
    OHAudio利用回调模式进行音频流数据的写入,以及各种音频事件的上报,应用可以按需选择必要监听的音频事件。
    为了克制不可预期的举动,在设置音频回调函数时,请确认OH_AudioRenderer_Callbacks的每一个回调都被自定义的回调方法或空指针初始化。
    对于Audio Vivid播放场景,必要另外利用OH_AudioRenderer_WriteDataWithMetadataCallback进行PCM和元数据的写入。
  1. // 自定义音频流事件函数
  2. int32_t MyOnStreamEvent(
  3.     OH_AudioRenderer* renderer,
  4.     void* userData,
  5.     OH_AudioStream_Event event)
  6. {
  7.     // 根据event表示的音频流事件信息,更新播放器状态和界面
  8.     return 0;
  9. }
  10. // 自定义音频中断事件函数
  11. int32_t MyOnInterruptEvent(
  12.     OH_AudioRenderer* renderer,
  13.     void* userData,
  14.     OH_AudioInterrupt_ForceType type,
  15.     OH_AudioInterrupt_Hint hint)
  16. {
  17.     // 根据type和hint表示的音频中断信息,更新播放器状态和界面
  18.     return 0;
  19. }
  20. // 自定义异常回调函数
  21. int32_t MyOnError(
  22.     OH_AudioRenderer* renderer,
  23.     void* userData,
  24.     OH_AudioStream_Result error)
  25. {
  26.     // 根据error表示的音频异常信息,做出相应的处理
  27.     return 0;
  28. }
  29. // 配置回调函数
  30. OH_AudioRenderer_Callbacks callbacks;
  31. // Audio Vivid播放时,该回调可以置空,使用元数据回调方式进行数据写入
  32. callbacks.OH_AudioRenderer_OnWriteData = nullptr;
  33. // 对音频流事件进行监听,如果不需要,可以使用 nullptr 赋值
  34. callbacks.OH_AudioRenderer_OnStreamEvent = MyOnStreamEvent;
  35. // 对音频中断事件进行监听,如果不需要,可以使用 nullptr 赋值
  36. callbacks.OH_AudioRenderer_OnInterruptEvent = MyOnInterruptEvent;
  37. // 对音频异常事件进行监听,如果不需要,可以使用 nullptr 赋值
  38. callbacks.OH_AudioRenderer_OnError = MyOnError;
  39. //设置输出音频流的回调
  40. OH_AudioStreamBuilder_SetRendererCallback(builder, callbacks, nullptr);
  41. // 自定义同时写入PCM数据和元数据函数
  42. int32_t MyOnWriteDataWithMetadata(
  43.     OH_AudioRenderer* renderer,
  44.     void* userData,
  45.     void* audioData,
  46.     int32_t audioDataSize,
  47.     void* metadata,
  48.     int32_t metadataSize)
  49. {
  50.     // 将待播放的PCM数据和元数据,分别按audioDataSize和metadataSize写入buffer
  51.     return 0;
  52. }
  53. // 配置回调函数
  54. OH_AudioRenderer_WriteDataWithMetadataCallback metadataCallback = MyOnWriteDataWithMetadata;
  55. // 设置同时写入PCM数据和元数据的回调
  56. OH_AudioStreamBuilder_SetWriteDataWithMetadataCallback(builder, metadataCallback, nullptr);
复制代码

  • 利用配置好的构造器,构造播放音频流。
  1. OH_AudioRenderer* audioRenderer;
  2. OH_AudioStreamBuilder_GenerateRenderer(builder, &audioRenderer);
复制代码

  • 利用音频流。
    可以利用以下接口,实现对音频流的控制,完成开始播放、停息播放、停止播放、清除缓存等基本操作。
    在不再利用该条音频流时,可以释放播放实例,以便更好地管理内存。


  • 释放构造器。
    当构造器不再利用时,必要释放相干资源。
  1. OH_AudioStreamBuilder_Destroy(builder);
复制代码
写在最后

有很多小伙伴不知道学习哪些鸿蒙开发技能?不知道必要重点把握哪些鸿蒙应用开发知识点?而且学习时频仍踩坑,最终浪费大量时间。所以有一份实用的鸿蒙(HarmonyOS NEXT)文档用来跟着学习是非常有须要的。

盼望这一份鸿蒙学习文档能够给大家带来帮助,有必要的小伙伴自行领取,限时开源,先到先得~无套路领取!!
假如你是一名有经验的资深Android移动开发、Java开发、前端开发、对鸿蒙感爱好以及转行职员,可以直接领取这份资料
请点击→纯血版全套鸿蒙HarmonyOS学习文档
鸿蒙(HarmonyOS NEXT)5.0最新学习路线


路线图适合人群:

IT开发职员:想要拓展职业边界
零底子小白:鸿蒙爱好者,盼望从0到1学习,增长一项技能。
技能提拔/进阶跳槽:发展瓶颈期,提拔职场竞争力,快速把握鸿蒙技能
获取以上完备版高清学习路线,请点击→纯血版全套鸿蒙HarmonyOS学习文档

《鸿蒙 (HarmonyOS)开发入门讲授视频》


《鸿蒙生态应用开发V3.0白皮书》


《鸿蒙 (OpenHarmony)开发底子到实战手册》

OpenHarmony北向、南向开发情况搭建

《鸿蒙开发底子》

●ArkTS语言
●安装DevEco Studio
●运用你的第一个ArkTS应用
●ArkUI声明式UI开发
.……

《鸿蒙开发进阶》

●Stage模型入门
●网络管理
●数据管理
●电话服务
●分布式应用开发
●关照与窗口管理
●多媒体技能
●安全技能
●任务管理
●WebGL
●国际化开发
●应用测试
●DFX面向未来筹划
●鸿蒙系统移植和裁剪定制
……

《鸿蒙进阶实战》

●ArkTS实践
●UIAbility应用
●网络案例
……

获取以上完备鸿蒙HarmonyOS学习文档,请点击→纯血版全套鸿蒙HarmonyOS学习文档



免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

北冰洋以北

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表