Я сталкиваюсь со странной проблемой при попытке воспроизведения потокового аудио с низкой задержкой на Nexus 6 под управлением Android 6.0.1 с использованием OpenSL ES.
Моя первоначальная попытка, казалось, страдала от проблем с голоданием, поэтому я добавил некоторые базовые временные тесты в функцию обратного вызова завершения буфера. Я обнаружил, что звук воспроизводится нормально, если я постоянно касаюсь экрана, когда мое приложение открыто, но если я оставляю его в покое на несколько секунд, обратный вызов начинает занимать гораздо больше времени. Я могу воспроизвести это поведение последовательно. Несколько вещей, чтобы отметить:
Симптомы, которые я вижу, на самом деле кажутся такими, что ОС сбивает приоритет аудиопотока после нескольких секунд отсутствия взаимодействия с телефоном. Это правильно? Есть ли способ, которым я могу избежать этого поведения?
Просматривая последнюю аудиопрезентацию Google I / O 2016, я наконец нашел причину и (некрасивое) решение этой проблемы.
Просто посмотрите примерно одну минуту этого ролика (начиная с 8 минут 56 секунд):
https://youtu.be/F2ZDp-eNrh4?t=8m56s
Это объясняет, почему это происходит, и как вы можете избавиться от этого.
Фактически, Android замедляет работу процессора после нескольких секунд бездействия сенсорного экрана, чтобы уменьшить расход заряда батареи. Парень на видео обещает правильное решение для этого в ближайшее время, но на данный момент единственный способ избавиться от него, это отправить поддельные штрихи (это официальная рекомендация).
Instrumentation instr = new Instrumentation();
instr.sendKeyDownUpSync(KeyEvent.KEYCODE_BACKSLASH); // or whatever event you prefer
Повторите это с таймером каждые 1,5 секунды, и проблема исчезнет.
Я знаю, это безобразный хак, и он может иметь неприятные побочные эффекты, с которыми нужно справиться. Но пока это просто единственное решение.
Обновить:
Что касается вашего последнего комментария … вот мое решение.
Я использую обычный MotionEvent.ACTION_DOWN в месте за пределами экрана. Все остальное мешало пользовательскому интерфейсу. Чтобы избежать SecurityException, инициализируйте таймер в обработчике onStart () основного действия и завершите его в обработчике onStop (). Все еще существуют ситуации, когда приложение переходит в фоновый режим (в зависимости от загрузки процессора), в котором вы можете столкнуться с SecurityException, поэтому вы должны окружить ложный сенсорный вызов блоком try catch.
Пожалуйста, обратите внимание, что я использую мою собственную структуру таймера, поэтому вы должны преобразовать код, чтобы использовать любой таймер, который вы хотите использовать.
Кроме того, я пока не могу гарантировать, что код на 100% пуленепробиваемый. Мои приложения применяют этот хак, но в настоящее время они находятся в бета-состоянии, поэтому я не могу дать вам никакой гарантии, если это работает правильно на всех устройствах и версиях 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();
.....
}
Хорошо известно, что аудио конвейер в Android 6 был полностью переписан. Хотя в большинстве случаев это улучшило проблемы, связанные с задержкой, не исключено, что оно породило ряд нежелательных побочных эффектов, как это обычно бывает при таких масштабных изменениях.
Хотя ваша проблема не кажется распространенной, есть несколько вещей, которые вы можете попробовать:
Увеличьте приоритет аудиопотока. Приоритет по умолчанию для аудио потоков в Android -16
с максимальным -20
Обычно доступны только для системных служб. Хотя вы не можете присвоить это значение вашей аудиопотоке, вы можете назначить следующую лучшую вещь: -19
используя ANDROID_PRIORITY_URGENT_AUDIO
флаг при установке приоритета потока.
Увеличьте количество буферов для предотвращения любого дрожания или задержки (вы можете даже увеличить до 16). Однако на некоторых устройствах обратный вызов для заполнения нового буфера не всегда вызывается, когда он должен.
Этот ТАК пост Есть несколько предложений по улучшению задержки аудио на Anrdoid. Особый интерес представляют пункты 3, 4 и 5 в принятом ответе.
Проверьте, поддерживает ли текущая система Android низкую задержку путем запроса ли hasSystemFeature(FEATURE_AUDIO_LOW_LATENCY)
или же hasSystemFeature(FEATURE_AUDIO_PRO)
,
Дополнительно, эта академическая статья обсуждаются стратегии по улучшению проблем задержки аудио в Android / OpenSL, включая подходы, связанные с интервалом между буфером и обратным вызовом.
Принудительная повторная выборка с частотой дискретизации устройства на Android 6.
Используйте собственную частоту дискретизации устройства 48000. Например:
SLDataFormat_PCM dataFormat;
dataFormat.samplesPerSec = 48000;