【问题标题】:Android with Nexus 6 -- how to avoid decreased OpenSL audio thread priority relating to app focus?带有 Nexus 6 的 Android - 如何避免与应用程序焦点相关的 OpenSL 音频线程优先级降低?
【发布时间】:2016-06-14 07:01:03
【问题描述】:

我在尝试使用 OpenSL ES 在运行 Android 6.0.1 的 Nexus 6 上实现低延迟流式音频播放时遇到了一个奇怪的问题。

我最初的尝试似乎遇到了饥饿问题,因此我在缓冲区完成回调函数中添加了一些基本的计时基准。我发现如果在我的应用程序打开时不断点击屏幕,音频可以正常播放,但如果我让它静置几秒钟,回调开始需要更长的时间。我能够始终如一地重现这种行为。有几点需要注意:

  • “几秒”~= 3-5 秒,不足以触发换屏
  • 我的应用程序的活动设置为 FLAG_KEEP_SCREEN_ON,因此无论如何都不应该发生屏幕变化
  • 我没有采取任何措施来尝试增加音频回调线程的优先级,因为我的印象是 Android 已经为这些线程保留了高优先级
  • 此行为发生在我的 Nexus 6 (Android 6.0.1) 上,但不在我也可用的 Galaxy S6 (Android 5.1.1) 上。

我看到的症状确实似乎是操作系统在与手机没有交互几秒钟后降低了音频线程的优先级。这是正确的吗?有什么办法可以避免这种行为?

【问题讨论】:

  • 在运行 Android 4.4.2 的 Nexus 5 上也不会发生。
  • 在播放期间记录线程优先级似乎表明在点击屏幕时线程优先级实际上并没有改变。也许触摸事件正在触发一个中断,这与调度程序可能返回的内容相混淆?
  • 我在我的应用程序中遇到了同样的问题。只要我与屏幕互动,一切都会顺利进行。一旦我停止触摸屏幕,它就会在几次(大约 3-5 秒)后大量退出。当我再次开始与屏幕交互时,它又恢复了流畅的操作。屏幕交互是否有任何实际作用,或者手指是否使用特定的控件/按钮触发都无关紧要。只要手指在屏幕上移动,所有音频都会流畅运行。这在 Android 5.x 上不会发生,它只是从 6.0 开始,Android N 上也是如此。
  • 这绝对不是应用程序的性能问题,我用一个预先计算的正弦波做了测试,行为是一样的。我使用nexus 6 进行测试。我还注意到,当应用程序“真正”进入后台时,行为完全相同。

标签: android c++ audio opensl nexus6


【解决方案1】:

在 Android 6 上强制重采样为本机设备采样率。

使用设备的原生采样率 48000。例如:

SLDataFormat_PCM 数据格式;

dataFormat.samplesPerSec = 48000;

【讨论】:

    【解决方案2】:

    在观看最新的 Google I/O 2016 音频演示时,我终于找到了这个问题的原因和(丑陋的)解决方案。

    只需观看大约一分钟的这个你管剪辑(从 8 分 56 秒开始): https://youtu.be/F2ZDp-eNrh4?t=8m56s

    它解释了为什么会发生这种情况以及如何摆脱它。

    事实上,Android 会在几秒钟的触摸不活动后降低 CPU 的速度,以减少电池使用量。视频中的那个人承诺很快就会为此提供适当的解决方案,但目前摆脱它的唯一方法是发送虚假触摸(这是官方建议)。

    Instrumentation instr = new Instrumentation();
    instr.sendKeyDownUpSync(KeyEvent.KEYCODE_BACKSLASH); // or whatever event you prefer
    

    每隔 1.5 秒用计时器重复一次,问题就会消失。

    我知道,这是一个丑陋的 hack,它可能会产生丑陋的副作用,必须加以处理。但就目前而言,它只是唯一的解决方案。

    更新: 关于您的最新评论……这是我的解决方案。 我在屏幕边界之外的位置使用常规 MotionEvent.ACTION_DOWN。其他一切都以不受欢迎的方式干扰了 UI。要避免 SecurityException,请在主活动的 onStart() 处理程序中初始化计时器并在 onStop() 处理程序中终止它。当应用程序进入后台时(取决于 CPU 负载)仍然存在可能遇到 SecurityException 的情况,因此您必须使用 try catch 块包围假触摸调用。

    请注意,我使用的是我自己的计时器框架,因此您必须转换代码以使用您想要使用的任何计时器。

    另外,我还不能确保代码是 100% 防弹的。我的应用已应用该 hack,但目前处于 beta 状态,因此我无法保证它在所有设备和 Android 版本上都能正常运行。

    Timer fakeTouchTimer = null;
    Instrumentation instr;
    void initFakeTouchTimer()
    {
        if (this.fakeTouchTimer != null)
        {
            if (this.instr == null)
            {
                this.instr = new Instrumentation();
            }
            this.fakeTouchTimer.restart();
        }
        else
        {
            if (this.instr == null)
            {
                this.instr = new Instrumentation();
            }
            this.fakeTouchTimer = new Timer(1500, Thread.MIN_PRIORITY, new TimerTask()
            {
                @Override
                public void execute()
                {
                    if (instr != null && fakeTouchTimer != null && hasWindowFocus())
                    {
                        try
                        {
                            long downTime = SystemClock.uptimeMillis();
    
                            MotionEvent event = MotionEvent.obtain(downTime, downTime, MotionEvent.ACTION_DOWN, -100, -100, 0);
                            instr.sendPointerSync(event);
                            event.recycle();
                        }
                        catch (Exception e)
                        {
                        }
                    }
                }
            }, true/*isInfinite*/);
        }
    }
    void killFakeTouchTimer()
    {
        if (this.fakeTouchTimer != null)
        {
            this.fakeTouchTimer.interupt();
            this.fakeTouchTimer = null;
            this.instr = null;
        }
    }
    
    @Override
    protected void onStop()
    {
        killFakeTouchTimer();
        super.onStop();
    
        .....
    }
    
    @Override
    protected void onStart()
    {
        initFakeTouchTimer();
        super.onStart();
    
        .....
    }
    

    【讨论】:

    • 感谢您的回答;这绝对是我试图解决的问题。我已经用他们建议的解决方法更新了我的应用程序,但不幸的是,它似乎比预期的更骇人听闻。特别是,如果在模拟触摸事件期间出现音量警告(没有跨应用程序化触摸事件),它可能会因安全异常而崩溃。你有没有机会自己实现一个更强大的版本?
    • 这就是我所说的丑陋副作用。 SecurityException 只是其中之一。我已经用我的最终解决方案更新了我的答案。
    • 我只是注意到到目前为止我没有处理诸如音量警告(或其他相同性质的情况)之类的情况。我更新了解决方案以涵盖所有这些情况。
    • 试过这个,假动作事件会干扰实际用户的触摸。相反,发送 KEYCODE_BACKSLASH 会选择布局中的第一个视图。 ://
    • 是的,KEYCODE_BACKSLASH 会干扰 UI,这是正确的。但看看最终的解决方案。它只触发 MotionEvent.ACTION_DOWN, -100, -100, 0),这意味着屏幕边界 (-100, -100) 之外的 ACTION_DOWN 事件(不是 ACTION_UP)。这些事件不会干扰,因为它们会被 Android 操作系统丢弃。但他们使过程保持在最大速度。此外,请仅对在 Android SDK 级别 23 或更高级别上运行的 Nexus 6 (shamu) 设备应用此修复程序,因为问题仅在这种情况下才会出现。两个多月以来,该修复程序已在我们所有的应用程序中生效,完全没有问题。
    【解决方案3】:

    众所周知,the audio pipeline in Android 6 has been completely rewritten。虽然这在大多数情况下改善了与延迟相关的问题,但它也并非不可能产生许多不良副作用,这种大规模更改通常会出现这种情况。

    虽然您的问题似乎并不常见,但您可以尝试以下几种方法:

    • 提高音频线程优先级。 Android中音频线程的默认优先级为-16,最高为-20,通常只对系统服务可用。虽然您无法将此值分配给音频线程,但您可以分配下一个最佳值:-19,在设置线程优先级时使用 ANDROID_PRIORITY_URGENT_AUDIO 标志。

    • 增加缓冲区数量以防止任何类型的抖动或延迟(您甚至可以增加到 16 个)。但是在某些设备上@9​​87654322@。

    • This SO post 提出了一些改善 Anrdoid 音频延迟的建议。特别感兴趣的是已接受答案中的第 3、4 和 5 点。

    • 通过查询hasSystemFeature(FEATURE_AUDIO_LOW_LATENCY)还是hasSystemFeature(FEATURE_AUDIO_PRO)来判断当前Android系统是否开启低延迟


    此外,this academic paper 讨论了改善 Android/OpenSL 中与音频延迟相关的问题的策略,包括与缓冲区和回调间隔相关的方法。

    【讨论】:

    • 感谢您的回答。不幸的是,我已经阅读了所有这些(非常有用的)资源并应用了其中建议的技术。我在声称支持低延迟音频的设备上以本机设备采样率和设备的本机缓冲区大小运行(事实上,我相信 Nexus 6 以最小的本机缓冲区时钟进入 192 帧 -在 48kHz 时为 4 毫秒)。由于您链接的 heatvst.com 文章中描述的原因,增加缓冲区数量没有帮助。我的问题是关于为什么 UI 交互似乎会影响音频播放。
    • 您是否安装了任何第三方应用程序,这些应用程序可能会在几秒钟的非交互后在后台获得焦点?
    • 如何让音频线程ANDROID_PRIORITY_URGENT_AUDIO优先?
    猜你喜欢
    • 2011-04-19
    • 1970-01-01
    • 1970-01-01
    • 2019-11-19
    • 2015-07-29
    • 2011-06-25
    • 1970-01-01
    • 2014-11-26
    相关资源
    最近更新 更多