• <Android音频>Android native层使用TrackPlayer播放pcm


    一:概述

    这是一个c语言demo程序,android源码环境,编译得到 bin文件,push到设备上在shell环境运行,播放pcm数据。如果是app java开发,没有系统源码,就不建议往下看了。

    android native播放音频,可以使用Ndk层提供的Opensl es、AAudio。
      如果是在源码级别,可以随意改,比如这里使用framework内部的 TrackPlayer类,经研究源码发现android 对Opensl es 的支持,内部有条通路最后是通过 使用framework native层的TrackPlayer 类,来播放pcm数据。(有关opensl android分析在其他文章再叙述)

    二:实现

    上demo:(github**暂未上传)

    环境

    • ubuntu22.04 编译 aosp11 源码,得到emulator 运行。 
    • 编译本demo 得到  out/target/product/generic_x86_64/system/bin/TrackPlayerDemo 可执行程序,push到 emulator上运行。
    • 程序需要一个 48K,16bit, 双通道立体声的pcm 数据文件作为源,(可使用ffmpeg制作)

    源码

    main.cpp

    1. /*
    2. * canok 20220629
    3. * android aosp 11
    4. * 参照 android中对opensl的支持 源码
    5. * 使用TrackPlayer 播放pcm
    6. * 需要源码环境下编译。
    7. */
    8. #include <stdlib.h>
    9. #include <stdio.h>
    10. #include <iostream>
    11. #include <atomic>
    12. #include <thread>
    13. #include <media/TrackPlayerBase.h>
    14. using namespace android;
    15. struct PlayerData
    16. {
    17. FILE *fp_in = nullptr;
    18. std::atomic<bool> bEnd = false;
    19. };
    20. // namespace android
    21. // {
    22. // 在callback中写数据。
    23. static void audioTrack_callBack_pullFromBuffQueue(int event, void *user, void *info)
    24. {
    25. // printf("track[%s%d]\n",__FUNCTION__,__LINE__);
    26. PlayerData *data = (PlayerData *)user;
    27. if (data == nullptr)
    28. {
    29. printf("[%s%d] data =null \n", __FUNCTION__, __LINE__);
    30. return;
    31. }
    32. FILE *fp_in = data->fp_in;
    33. if (fp_in == NULL)
    34. {
    35. printf("[%s%d] fp_in =null \n", __FUNCTION__, __LINE__);
    36. return;
    37. }
    38. switch (event)
    39. {
    40. case android::AudioTrack::EVENT_MORE_DATA:
    41. {
    42. android::AudioTrack::Buffer *pBuff = (android::AudioTrack::Buffer *)info;
    43. size_t availSink = pBuff->size;
    44. // void *pSrc =
    45. // memcpy(pBuff->raw, pSrc, availSink);
    46. size_t ret = fread(pBuff->raw, 1, pBuff->size, fp_in);
    47. pBuff->size = ret;
    48. if (ret < 0)
    49. {
    50. data->bEnd = true;
    51. }
    52. std::cout << "read :" << ret << std::endl;
    53. }
    54. break;
    55. default:
    56. break;
    57. }
    58. }
    59. int main(int argc, const char *argv[])
    60. {
    61. // 使用的是一个 callback 的传输方式, 在AudioTrack的callback中写数据。// AudioPlayer_to_android.cpp audioTrack_callBack_pullFromBuffQueue
    62. // threadCanCallJava 在AudioTrack构造函数中调用set() 时给的是false: 而transferType AudioTrack构造函数中为 默认值为 TRANSFER_DEFAULT
    63. // 在set() 函数中会判断根据参数,最终将 transfertype设置为 TRASNFER_CALLBACk. :: ssharedBuffer==0 cbf !=NULL threadCanCallJava==false
    64. // 将会在 cballck 中得到EVENT_MORE_DATA 消息,然后这里面写数据
    65. PlayerData data;
    66. data.fp_in = fopen("yk_48000_2_16.pcm", "r");
    67. if (data.fp_in == nullptr)
    68. {
    69. printf("[%s%d]fopen failed ! we need a pcm src file!!!!!!\n", __FUNCTION__, __LINE__);
    70. return -1;
    71. }
    72. sp<android::AudioTrack> pat = new android::AudioTrack(
    73. AUDIO_STREAM_MUSIC, // pAudioPlayer->mStreamType, // streamType
    74. 48000, // sampleRate,
    75. AUDIO_FORMAT_PCM_16_BIT, // sles_to_android_sampleFormat(df_pcm), // format
    76. AUDIO_CHANNEL_OUT_STEREO, // channelMask, // channel mask
    77. 0, // frameCount
    78. AUDIO_OUTPUT_FLAG_NONE, // policy, // flags
    79. audioTrack_callBack_pullFromBuffQueue, // callback
    80. (void *)&data, // (void *) pAudioPlayer, // user // 自定义的数据!!!!!会在callback中得到这个参数
    81. 0, // notificationFrames, // see comment above
    82. AUDIO_SESSION_ALLOCATE // pAudioPlayer->mSessionId
    83. );
    84. sp<TrackPlayerBase> player = new TrackPlayerBase();
    85. player->init(pat.get(), PLAYER_TYPE_SLES_AUDIOPLAYER_BUFFERQUEUE, AUDIO_USAGE_MEDIA);
    86. player->start();
    87. player->setVolume(01.0);
    88. while (!data.bEnd)
    89. {
    90. std::this_thread::yield();
    91. }
    92. fclose(data.fp_in);
    93. return 0;
    94. }
    95. // }

    Android.bp

    1. cc_binary {
    2. name: "TrackPlayerDemo",
    3. srcs: [
    4. "main.cpp",
    5. ],
    6. shared_libs: [
    7. "libaudioclient",
    8. "libaudioutils",
    9. "libutils",
    10. "libbinder",
    11. "libmediametrics",
    12. ],
    13. header_libs: [
    14. "libmedia_headers",
    15. ],
    16. include_dirs: [
    17. "frameworks/av/media/libnbaio/include_mono/",
    18. ],
    19. cflags: [
    20. "-Wall",
    21. "-Werror",
    22. "-Wno-error=deprecated-declarations",
    23. "-Wno-unused-parameter",
    24. "-Wno-unused-variable",
    25. ],
    26. }

    编译

    两文件放到framework 一个自定义目录下, 经source lunch 编译环境的配置后,到该自定义子目录 mm -j8

    运行结果

     emulator虚拟机可以听到播放的音频:

     

    可能问题:

      无声,先要确保emulator 自身能发声,比如去设置里面改一下铃声,看是否有声音。要确保宿主机ubuntu有声音,  ubuntu系统可以在设置里面选择输出到hdmi, 🎧,还是扬声器。

     

  • 相关阅读:
    go并发操作且限制数量
    Qt HTTP 摘要认证(海康球机摄像机ISAPI开发)
    MySQL备份和恢复
    Linux项目实训一
    Java爬虫的使用案例及简单总结
    SAP:增强中用commit和wait up会导致操作异常
    Vue-router
    原装应广单片机 MCU芯片PMS152 SOP8封装 单片机开发
    Nvidia显卡L40S学习:产品规格,常用名词解释
    安卓基础学习 Day17|网格布局+计算器界面
  • 原文地址:https://blog.csdn.net/u012459903/article/details/125516095