Android N音频播放延迟
背景
在Android N上使用MediaPlayer进行高频率的音频播放,会出现很严重的声音播放延迟的问题。比如快递业务场景,在业务员正确扫描快递面单后,需要播放一个声音来提示业务员该面单已经扫描完毕,可以进行下一单扫描。业务员也是通过这个声音来作为判断标准,只有在听到声音播放的情况下才会认为这个面单已经录进了系统(出现漏扫漏件是要扣钱的),才敢进行下一个快件的扫描。这个场景需要很高的工作效率,平均每个人每秒钟会扫描3-4个快件,也就是提示的声音每秒钟需要播放3-4次。这种条件背景下,如果代码中使用MediaPlayer去完成这么高频率的音频播放动作,会有很大的问题,声音播放延迟的问题很严重,因为声音播放不及时,会极大的影响业务员的工作效率。(并不是每个平台、每个Android系统都有这个问题,这里讨论的是高通8909 Android N平台)。
问题定位
08-02 18:45:30.834 367 589 D audio_hw_primary: enable_snd_device: snd_device(2: speaker)
08-02 18:45:30.839 367 589 D audio_hw_primary: enable_audio_route: apply mixer and update path: deep-buffer-playback
通过抓取日志分析,发现日志中有大量如上所示的"deep-buffer-playback"的日志信息,这是Android Audio系统播放音频文件最终采取的播放模式。很显然,这些声音的播放都是采取的"deep-buffer-playback"模式。在Android系统上,声音的播放主要有三种模式,分别是low-latency-playback、deep-buffer-playback和compressed-offload-playback。
- low-latency-playback: 用于按键音、游戏背景音等对时延要求高的声音输出。音频文件是在AP侧解码成PCM数据,然后再经由Audio DSP送给codec芯片播放出来。
- deep-buffer-playback: 用于音乐等对时延要求不高的声音输出。音频文件是在AP侧解码成PCM数据,如果有音效的话会再对PCM数据处理(android audio framework中有effect音效模块,支持的音效有均衡器、低音增强、环绕声等),然后再经由Audio DSP送给codec芯片播放出来。
- compressed-offload-playback: 用于音乐等声音输出,但是音频解码部分的工作是在Audio DSP中完成,AP侧只负责把音频码流送到Audo DSP中,送出去后AP侧会进行休眠,Audo DSP中会分配一块较大的buffer去处理此数据,在Audo DSP中进行解码、音效的处理等工作,在Audo DSP解码器处理完数据之前,它会唤醒AP侧去送下一包数据。用这种模式播放音频能有效的降低功耗,是最为推荐的播放音乐的模式。
结合日志和deep-buffer-playback 的定义,可以得出问题出在播放模式上,既在对实时性要求非常高的场景下采用了高延时的播放模式。那么,系统是如何判断播放音频的时候是采用low-latency-playback 还是 deep-buffer-playback?我们如何自主选择播放模式?
播放模式选择
反编译合作方的apk,发现他们播放的是wav格式的音频文件,并且使用的是MediaPlayer播放器播放音频。
大概的调用方式:
if (isPlayer) {return;
}
isPlayer = true;
if (mediaPlayer != null) {mediaPlayer.stop();mediaPlayer.release();mediaPlayer = null;
}
mediaPlayer = MediaPlayer.create(this, R.raw.xxx);
mediaPlayer.setOnCompletionListener(onCompletionListener);
mediaPlayer.start();
MediaPlayer的功能主要是用来播放音乐的,如mp3这种音频文件较大的流媒体,这种格式的播放对实时性要求并不高,所以在高通8909平台如果采用MediaPlayer来播放音频默认走的就是deep-buffer-playback模式。而用户需要播放的音频文件只是一个很短促的提示音,如果要实时性很高可以采用SoundPool的方式来播放,SoundPool会将音频文件预先缓存为一个16位PCM流,播放的时候直接通过Audio DSP送给codec芯片播放出来,少了很多流程,也就减少了很多延时。
01-01 10:48:51.357 377 608 D audio_hw_primary: start_output_stream: enter: stream(0xb34441c0)usecase(1: low-latency-playback) devices(0x2)
通过实际的测试,发现日志中的deep-buffer-playback 变成了 low-latency-playback,说明使用SoundPool播放音频文件默认走的是 low-latency-playback模式。不过SoundPool对播放的音频文件有很多限制,比如文件大小不能超过1M等,只能用来播放一些很短促的音效。
另外,MediaPlayer其实也有参数可以设置播放模式
AudioAttributes aa = new AudioAttributes.Builder().setContentType(AudioAttributes.CONTENT_TYPE_MUSIC).setFlags(AudioAttributes.FLAG_LOW_LATENCY).setUsage(AudioAttributes.USAGE_GAME).build();
mediaPlayer.setAudioAttributes(aa);
如上的setFlags(AudioAttributes.FLAG_LOW_LATENCY),就是设置MediaPlayer低延时播放,但是通过测试发现即使设置了FLAG_LOW_LATENCY也没什么卵用,播放音频的时候还是走的deep-buffer-playback模式,不知道是不是这个属性被废弃的原因。
解决方案
虽然找到了解决方案,换成SoundPool播放既可,但是说服不了合作方修改apk,商业合作就是这样,能把问题踢给别人绝对不会挂在自己身上。而且同样的apk在高通8917平台不会有问题,在MTK平台也没有问题,就在我们的高通8909平台有问题,这就没办法解释了,只能我们想办法在Android系统里改源代码。
status_t NuPlayer::Renderer::onOpenAudioSink(const sp<AMessage> &format,bool offloadOnly,bool hasVideo,uint32_t flags,bool isStreaming) {ALOGV("openAudioSink: offloadOnly(%d) offloadingAudio(%d)",offloadOnly, offloadingAudio());...省略...// We should always be able to set our playback settings if the sink is closed.LOG_ALWAYS_FATAL_IF(mAudioSink->setPlaybackRate(mPlaybackSettings) != OK,"onOpenAudioSink: can't set playback rate on closed sink");
+ int64_t tmp_duration;
+ format->findInt64("durationUs", &tmp_duration);
+ if (tmp_duration < 3000000000) {// duration < 3s.
+ pcmFlags = AUDIO_OUTPUT_FLAG_FAST;
+ }status_t err = mAudioSink->open(sampleRate,numChannels,(audio_channel_mask_t)channelMask,AVNuUtils::get()->getPCMFormat(format),0 /* bufferCount - unused */,mUseAudioCallback ? &NuPlayer::Renderer::AudioSinkCallback : NULL,mUseAudioCallback ? this : NULL,(audio_output_flags_t)pcmFlags,NULL,doNotReconnect,frameCount);...省略...
}
在frameworks/av/meida/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp文件的onOpenAudioSink方法中,添加如上带+号的代码段。
NuPlayer是做什么的?添加的代码段是什么意思?
NuPlayer是Android流媒体框架的核心,播放音频文件也在流媒体也就是NuPlayer的管理范围之内,而onOpenAudioSink这个方法中会最终决定播放音频文件的播放模式。添加的代码意思是如果该音频文件的播放时长小于3秒,就将播放模式强制改为low-latency-playback(既AUDIO_OUTPUT_FLAG_FAST,该常量在audio.h中定义)。
通过这种取巧的方式,既不影响MediaPlayer播放大的音频文件,也不需要合作方修改apk,算是一个比较兼容的修改方式,最终用户也未再反馈过该问题,算了解决了吧。
Android N音频播放延迟相关推荐
- android exo解码问题,Android Exoplayer音频播放异常
我使用ExoPlayer播放声音时遇到问题.即快速切换"停止 - 启动"异常发生Android Exoplayer音频播放异常 java.lang.NullPointerExcep ...
- 视频教程-FFmpeg打造Android万能音频播放器-Android
FFmpeg打造Android万能音频播放器 从事Android移动端开发多年.主导开发过直播.电商.聊天等各种类型APP和游戏SDK:熟悉Android音视频开发.底层NDK开发等:有开源项目:ht ...
- Android录音笔-音频播放器,不止适用于保存的录音
Android录音笔-音频播放器,不止适用于保存的录音 功能说明 点击录音列表项弹出播放对话框 对话框碎片的布局dialog_fragment.xml 对话框碎片MyDialog.java 对话框碎片 ...
- android 以音频播放器为例实现通知栏显示通知,并实现切歌、暂停、播放,并实现加载网络图片,并实现关闭第三方APP音频
首先先给大家看下效果 接下来我们看下具体如何实施 1.首先我们创建一个音频的单例对象,这样能保证每次在播放的的音频是唯一的(类名如:MediaPlayerUtil.java) package xxx; ...
- android 视频+音频播放器Demo
程序主界面 MainActivity.java 1.主界面,头部是两个TextView(自定义类似指针效果),底部是ViewPager.ViewPager中每个页面对应的是一个Fragment.这样就 ...
- android MediaPlayer音频播放demo
网上搜了些关于MediaPlayer的资料 1)如何获得MediaPlayer实例: 可以使用直接new的方式: MediaPlayer mp = new MediaPlayer(); 也可以使用cr ...
- android蓝牙和线同时播放,Android蓝牙音频播放和录制
我有一个蓝牙耳机(它可以播放立体声音乐)连接到我的Android手机(Android 4.4.3) . 现在我希望我的代码能够以高采样率(44100)播放立体声音乐并从该耳机录制音频 . 我按照以下帖 ...
- Android MP3音频播放器 仿唱片机播放动画,仿网易云播放动画,旋转动画,MediaPlayer AudioManager
废话不多说上代码 private AudioManager audioManager;private SimpleDateFormat format;private SeekBar seekBar;p ...
- Android——Mediaplay音频播放最详细最全面使用方法-以及自定义seekbar 样式
** Mediaplay最详细使用方法** 在前几个星期吧 小编的公司 一直有播放音频的这个 模块 但是因为小编 接触这块比较少 所以走了很多的 "丸子" 比如 狂点 播放 暂停按 ...
最新文章
- 2021襄阳谷城高考成绩查询,2021高考襄阳谷城县考生求助电话
- 怎么将jenkins打包后的war自动部署到jetty上?
- 新的GNSS精度度量是怎样定义的?
- 从零开始的linux 第十一章
- sql server 2008学习1–系统数据库
- NOIP2018-普及总结
- python某公司为员工发放奖品_python 练习2
- mysql数据库索引回表_简述 MySQL 数据库的覆盖索引与回表
- rpg人物制作软件_RPG游戏制作教程
- 电源与地之间的电容作用
- w3c html验证服务,W3C验证和Vue的HTML绑定语法(W3C Validation and Vue's HTML binding syntax)...
- 网站表单自动填写【使用pyautogui 】
- 荣耀修改WIFI服务器,数码教程资讯:荣耀路由2S怎么修改wifi的802.11n频宽
- 科普读书会丨《被讨厌的勇气》:愤怒不是目的,是一种工具
- LSH 近似最近邻查找
- 联想主板bios设置u盘启动项的方法怎么操作
- java毕业设计——基于JSP+sqlserver的人事管理系统设计与实现(毕业论文+程序源码)——人事管理系统
- 超市收银软件测试自学,超市收银系统测试计划.doc
- Linux Kernel 6.0 CXL Core pci.c 详解
- 国际快递顺丰API接口接入教程代码示例
热门文章
- CVE-2021-4034 Pkexec LPE原理精析
- 一分钟经理人精髓记录
- OSTA该项考试分为几个等级?
- Metabase——开源的大数据分析探索、可视化报表神器
- php漏洞是什么意思,PHP程序漏洞产生的原因分析与防范方法说明
- Building window 2008 Cluster using Virtual Box 4.1.18 with ISCSI (NAS4Free)
- htc vive 安装和使用
- java判断字符串中是否包含某个字符
- LabVIEW CompactRIO 开发指南2 CompactRIO软件架构
- js单击、双击、连续多次点击