В этой статье описаны некоторые советы и рекомендации по отладке звука Android.
Тройник раковина
«Три-приемник» — это функция отладки AudioFlinger, доступная только в пользовательских сборках и предназначенная для сохранения короткого фрагмента недавнего аудио для последующего анализа. Это позволяет сравнивать то, что было фактически воспроизведено или записано, с тем, что ожидалось.
В целях конфиденциальности тройник по умолчанию отключен как во время компиляции, так и во время выполнения. Чтобы использовать тройник, вам нужно будет включить его путем повторной компиляции, а также путем установки свойства. Обязательно отключите эту функцию после завершения отладки; тройник-приемник не следует оставлять включенным в производственных сборках.
Инструкции в этом разделе предназначены для 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
разветвляет и запускаетmediaserver
. -
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 #
, где # — это идентификатор процесса, который вы указали ранее. В главном logcat
вы должны увидеть дамп из media.log
, показывающий все журналы, приведшие к сбою.
dumpsys media.log