В этой статье описаны некоторые советы и рекомендации по отладке звука 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/audioserverchown 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 :

Рисунок 1. Архитектура до использования media.log
Примечательные моменты:
-
initразветвляет и запускаетmediaserver. -
initобнаруживает смертьmediaserverи при необходимости повторно разветвляет его. - Журналирование
ALOGxне отображается.
На диаграмме ниже показаны новые взаимоотношения компонентов после добавления media.log в архитектуру:

Рисунок 2. Архитектура после media.log
Важные изменения:
- Клиенты используют
NBLOGAPI для создания записей журнала и добавления их в кольцевой буфер в общей памяти. -
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 rootadb shellecho ro.test_harness=1 > /data/local.propchmod 644 /data/local.propreboot
Соединение теряется при перезагрузке, поэтому:
adb shell
ps media теперь покажет два процесса:- медиа.лог
- медиасервер
Запишите идентификатор процесса mediaserver на будущее.
Отобразить временные рамки
Вы можете вручную запросить дамп журнала в любое время. Эта команда показывает журналы всех активных и последних временных шкал, а затем очищает их:
dumpsys media.log
Обратите внимание, что по замыслу временные шкалы независимы, и возможности их объединения не существует.
Восстановление журналов после смерти медиасервера
Теперь попробуйте убить процесс mediaserver : kill -9 # , где # — это идентификатор процесса, который вы указали ранее. В главном logcat вы должны увидеть дамп из media.log , показывающий все журналы, приведшие к сбою.
dumpsys media.log