В этой статье описаны некоторые советы и рекомендации по отладке звука Android.
Тройник Раковина
«Тройник» — это функция отладки AudioFlinger, доступная только в пользовательских сборках, для сохранения короткого фрагмента недавнего звука для последующего анализа. Это позволяет сравнивать то, что было на самом деле воспроизведено или записано, с тем, что ожидалось.
В целях конфиденциальности приемник tee по умолчанию отключен как во время компиляции, так и во время выполнения. Чтобы использовать тройник, вам нужно будет включить его, перекомпилировав, а также установив свойство. Обязательно отключите эту функцию после завершения отладки; тройник не следует оставлять включенным в производственных сборках.
Инструкции в этом разделе предназначены для Android 7.x и выше. Для Android 5.x и 6.x замените /data/misc/audioserver
на /data/misc/media
. Кроме того, вы должны использовать сборку userdebug или eng. Если вы используете сборку userdebug, отключите verity с помощью:
adb root && adb disable-verity && adb reboot
Настройка во время компиляции
-
cd frameworks/av/services/audioflinger
- Отредактируйте файл
Configuration.h
. -
#define TEE_SINK
. -
libaudioflinger.so
. -
adb root
-
adb remount
- Отправьте или синхронизируйте новый
libaudioflinger.so
с/system/lib
устройства.
Настройка во время выполнения
-
adb shell getprop | grep ro.debuggable
Убедитесь, что вывод:[ro.debuggable]: [1]
-
adb shell
-
ls -ld /data/misc/audioserver
Подтвердите, что вывод:
drwx------ media media ... media
Если каталог не существует, создайте его следующим образом:
mkdir /data/misc/audioserver
chown media:media /data/misc/audioserver
-
echo af.tee=# > /data/local.prop
Где значениеaf.tee
представляет собой число, описанное ниже. -
chmod 644 /data/local.prop
-
reboot
Значения свойства af.tee
Значение af.tee
— это число от 0 до 7, выражающее сумму нескольких битов, по одному на каждую функцию. См. код в AudioFlinger::AudioFlinger()
в AudioFlinger.cpp
для объяснения каждого бита, но кратко:
- 1 = ввод
- 2 = выход FastMixer
- 4 = AudioRecord и AudioTrack для каждой дорожки
Пока нет бита для глубокого буфера или обычного микшера, но вы можете получить аналогичные результаты, используя «4».
Тестируйте и собирайте данные
- Запустите аудиотест.
-
adb shell dumpsys media.audio_flinger
- Найдите в
dumpsys
такую строку:
tee copied to /data/misc/audioserver/20131010101147_2.wav
Это файл PCM .wav. - Затем
adb pull
извлекает любые интересующие файлы/data/misc/audioserver/*.wav
; обратите внимание, что имена файлов дампа для конкретных треков не отображаются в выходных данныхdumpsys
, но по-прежнему сохраняются в/data/misc/audioserver
после закрытия трека. - Просмотрите файлы дампа на предмет конфиденциальности, прежде чем делиться ими с другими.
Предложения
Попробуйте эти идеи для более полезных результатов:
- Отключите звуки касания и щелчки клавиш, чтобы сократить количество прерываний тестового вывода.
- Максимизируйте все объемы.
- Отключите приложения, которые воспроизводят звук или записывают с микрофона, если они не представляют интереса для вашего теста.
- Дампы трека сохраняются только тогда, когда трек закрыт; вам может потребоваться принудительно закрыть приложение, чтобы сбросить его данные, относящиеся к треку.
- Сделайте
dumpsys
сразу после теста; имеется ограниченное количество доступного места для записи. - Чтобы не потерять файлы дампа, периодически загружайте их на хост. Сохраняется только ограниченное количество файлов дампа; старые дампы удаляются после достижения этого предела.
Восстановить
Как отмечалось выше, функцию тройника не следует оставлять включенной. Восстановите сборку и устройство следующим образом:
- Верните изменения исходного кода в
Configuration.h
. -
libaudioflinger.so
. - Отправьте или синхронизируйте восстановленный
libaudioflinger.so
с/system/lib
устройства. -
adb shell
-
rm /data/local.prop
-
rm /data/misc/audioserver/*.wav
-
reboot
медиа.лог
Макросы ALOGx
Стандартный API ведения журнала языка Java в Android SDK — android.util.Log .
Соответствующий API языка C в Android NDK — __android_log_print
, объявленный в <android/log.h>
.
В нативной части платформы Android мы предпочитаем макросы с именами ALOGE
, ALOGW
, ALOGI
, ALOGV
и т. д. Они объявлены в <utils/Log.h>
, и в целях этой статьи мы будем называть их ALOGx
.
Все эти API просты в использовании и хорошо понятны, поэтому они широко распространены на платформе Android. В частности, процесс mediaserver
, в который входит звуковой сервер AudioFlinger, широко использует ALOGx
.
Тем не менее, у ALOGx
и его друзей есть некоторые ограничения:
- Они восприимчивы к «спаму в журнале»: буфер журнала является общим ресурсом, поэтому он может легко переполниться из-за несвязанных записей журнала, что приведет к пропуску информации. Вариант
ALOGV
по умолчанию отключен во время компиляции. Но, конечно, даже это может привести к спаму в журнале, если оно включено. - Базовые системные вызовы ядра могут блокироваться, что может привести к инверсии приоритетов и, следовательно, к помехам и неточностям измерения. Это особенно важно для критичных ко времени потоков, таких как
FastMixer
иFastCapture
. - Если конкретный журнал отключен для уменьшения нежелательной почты в журнале, то любая информация, которая могла бы быть захвачена этим журналом, будет потеряна. Невозможно включить конкретный журнал задним числом, после того как станет ясно, что журнал был бы интересен.
NBLOG, media.log и MediaLogService
API NBLOG
и связанный с ним процесс media.log
и служба MediaLogService
вместе образуют более новую систему ведения журналов для мультимедиа и специально разработаны для решения вышеуказанных проблем. Мы будем свободно использовать термин «media.log» для обозначения всех трех, но, строго говоря, NBLOG
— это API ведения журналов C++, media.log
— это имя процесса Linux, а MediaLogService
— служба связывания Android для изучения журналов.
«Временная шкала» media.log
представляет собой серию записей журнала, относительный порядок которых сохраняется. По соглашению каждый поток должен использовать собственную временную шкалу.
Преимущества
Преимущества системы media.log
заключаются в том, что она:
- Не спамит основной журнал до тех пор, пока это не потребуется.
- Может быть проверен даже при
mediaserver
или зависании медиасервера. - Является неблокирующим на временной шкале.
- Предлагает меньше помех для производительности. (Конечно, ни одна форма регистрации не является полностью ненавязчивой.)
Архитектура
На приведенной ниже диаграмме показана взаимосвязь процесса mediaserver
и процесса init
до того, как будет введен media.log
:
Примечательные моменты:
-
init
forks и execsmediaserver
. -
init
обнаруживает смертьmediaserver
и при необходимости выполняет повторный форк. -
ALOGx
не отображается.
На диаграмме ниже показано новое соотношение компонентов после добавления в архитектуру media.log
:
Важные изменения:
- Клиенты используют
NBLOG
API для создания записей журнала и добавления их в кольцевой буфер в общей памяти. -
MediaLogService
может в любой момент сбросить содержимое кольцевого буфера. - Циклический буфер разработан таким образом, что любое повреждение разделяемой памяти не приведет к сбою
MediaLogService
, и он по-прежнему сможет выгрузить столько буфера, на который не повлияло повреждение. - Циклический буфер не блокируется и не блокируется как для записи новых записей, так и для чтения существующих записей.
- Для записи или чтения из циклического буфера не требуется никаких системных вызовов ядра (кроме необязательных временных меток).
Где использовать
Начиная с Android 4.4, в AudioFlinger есть только несколько точек журнала, которые используют систему media.log
. Хотя новые API не так просты в использовании, как ALOGx
, они также не слишком сложны. Мы рекомендуем вам изучить новую систему ведения журнала для тех случаев, когда это необходимо. В частности, это рекомендуется для потоков AudioFlinger, которые должны выполняться часто, периодически и без блокировки, таких как FastMixer
и FastCapture
.
Как использовать
Добавить журналы
Во-первых, вам нужно добавить журналы в свой код.
В FastMixer
и FastCapture
используйте такой код:
logWriter->log("string"); logWriter->logf("format", parameters); logWriter->logTimestamp();
Поскольку эта временная шкала NBLog
используется только FastMixer
и FastCapture
, нет необходимости во взаимном исключении.
В других потоках AudioFlinger используйте mNBLogWriter
:
mNBLogWriter->log("string"); mNBLogWriter->logf("format", parameters); mNBLogWriter->logTimestamp();
Для потоков, отличных от FastMixer
и FastCapture
, временная шкала NBLog
потока может использоваться как самим потоком, так и операциями связывателя. NBLog::Writer
не обеспечивает никакого неявного взаимного исключения для каждой временной шкалы, поэтому убедитесь, что все журналы происходят в контексте, в котором удерживается мьютекс mLock
.
После добавления журналов пересоберите AudioFlinger.
Предупреждение. Для каждого потока требуется отдельная временная шкала NBLog::Writer
, чтобы обеспечить безопасность потоков, поскольку временные шкалы по умолчанию не включают мьютексы. Если вы хотите, чтобы несколько потоков использовали одну и ту же временную шкалу, вы можете защитить их с помощью существующего мьютекса (как описано выше для mLock
). Или вы можете использовать NBLog::LockedWriter
вместо NBLog::Writer
. Однако это сводит на нет основное преимущество этого API: его неблокирующее поведение.
Полный API NBLog
находится по адресу frameworks/av/include/media/nbaio/NBLog.h
.
Включить медиа.лог
media.log
по умолчанию отключен. Он активен только тогда, когда свойство ro.test_harness
равно 1
. Вы можете включить его:
adb root
adb shell
echo ro.test_harness=1 > /data/local.prop
chmod 644 /data/local.prop
reboot
Соединение теряется при перезагрузке, поэтому:
adb shellТеперь команда
ps media
покажет два процесса:- медиа.лог
- медиасервер
Запишите идентификатор процесса mediaserver
на потом.
Отображение временных шкал
Вы можете вручную запросить дамп журнала в любое время. Эта команда показывает журналы со всех активных и последних временных шкал, а затем очищает их:
dumpsys media.log
Обратите внимание, что временные шкалы по дизайну независимы, и нет возможности объединять временные шкалы.
Восстановление логов после смерти медиасервера
Теперь попробуйте убить процесс mediaserver
: kill -9 #
, где # — идентификатор процесса, который вы указали ранее. Вы должны увидеть дамп из media.log
в главном logcat
, показывающий все журналы, приведшие к сбою.
dumpsys media.log