stihl не предоставил(а) никакой дополнительной информации.
Сегодня нас с тобой ждет настоящая экспедиция вглубь Android. Нашей сиюминутной целью будет устранить баг воспроизведения звука на телеприставке X96Q PRO. На пути к этой цели мы подробно изучим все части звуковой системы и научимся вносить изменения в низкоуровневые настройки, недоступные из графического интерфейса.
Android в нашей подопытной приставке точно такой же, как в телефонах и автомагнитолах. Все принципиальные сведения, кроме конкретных схем и конкретного кода библиотеки HAL, актуальны для всех устройств на Android 10, и, с большой долей вероятности, от Android 7 до Android 13 включительно. Начиная с версии 14 в Android стали использовать язык описания интерфейса AIDL вместо HIDL, но это касается в большей степени низкоуровневых разработчиков.
или Зарегистрируйся в варианте с двумя гигабайтами оперативки. Ее операционная система Android 10 не захламлена, в ней разблокирована возможность получения прав суперпользователя. Кажется, то что надо.
Заполучив приставку, я ее подключил, настроил и поставил на нее привычный набор приложений. На первый взгляд все было прекрасно: видео воспроизводилась, музыка звучала, игры игрались.
Но уже на второй день я почувствовал неладное. Какой бы медиаконтент ни играл, в звуковом сопровождении постоянно проскакивали то ли пощелкивания, то ли потрескивания, как будто во время просмотра аналогового телевидения рядом проходит грозовой фронт.
В художественных фильмах или новостных программах это почти незаметно, но в отдельных музыкальных композициях звук искажался до невозможности. Я не аудиофил и меня не испугать записью концерта в MP3 со скоростью потока 128 Кбит/с. Но щелчки все же стали действовать на нервы. Я понял, что если это не исправить, то скоро начнет дергаться глаз.
Если у тебя есть устройство с Android, ты можешь прямо сейчас проверить, присущ ли ему описываемый дефект. Для этого воспроизведи файл Для просмотра ссылки Войдиили Зарегистрируйся с записью гудка европейской телефонной станции (это синусоида с частотой 425 Гц). На качественном устройстве тон должен быть чистым, мягким и ровным. Если же звук плавает, в нем слышны посторонние шумы или потрескивания, то сочувствую: твое устройство подвержено проблеме, которую мы будем решать в этой статье.
Возможно, тебя миновала эта неприятность, но тебе интересно устройство аудиоподсистемы Android 10 и способ изменения частоты дискретизации выводимого приставкой аудиопотока. Об этом я тоже постараюсь рассказать.
Я перебрал несколько медиапроигрывателей, но это тоже не дало результата. Вот и все, обычный пользователь потерпел поражение в этой схватке с Android. Поэтому на сцену пришлось выйти инженеру. Инженерный подход к решению проблем подразумевает сбор и изучение доступной информации об объекте, его диагностику для выяснения причин проблемы, выбор решения и исправление ситуации. Что ж, приступим!
План исследования звуковой подсистемы Android следующий:
В большинстве случаев восстановление работоспособности возможно, но для этого потребуются специальные знания и софт. С последним сложнее всего. На Для просмотра ссылки Войдиили Зарегистрируйся приставки X96Q PRO есть четыре варианта firmware, но только один подходит к моему экземпляру. Попытка «прошить» другие приводит к тому, что устройство перестает опознаваться даже сервисным ПО.
Поэтому трезво оцени свои силы перед тем, как что‑то сделать на реальном оборудовании. Как минимум заранее ознакомься с опытом неудачников на тематических форумах. Но все равно будь готов к тому, что устройство может быть безнадежно испорчено.
Команды можно выполнять непосредственно в консоли устройства из приложения‑терминала, например, Для просмотра ссылки Войдиили Зарегистрируйся. Но я использовал возможности удаленной отладки устройств с Android, для чего установил на свой компьютер пакет Для просмотра ссылки Войди или Зарегистрируйся с утилитой командной строки adb.
Для замены конфигов и установки утилиты tinymix я использовал Для просмотра ссылки Войдиили Зарегистрируйся, которое дает права суперпользователя и позволяет оформлять модификации в виде подключаемых модулей.
Хоть приставка и была изначально рутована, в использовании Magisk нашлось неоспоримое преимущество. Если с новым вариантом конфига приставка не загружается, достаточно дважды перезагрузить ее, переподключая блок питания. Magisk подсчитывает количество неудачных включений и на третий раз отключает все установленные модули, что позволяет приставке загрузиться в заводском исполнении.
Для использования в компьютерной технике аналоговая физическая величина — в данном случае давление воздуха — представляется конечной последовательностью чисел из ограниченного набора с помощью двух подходов: дискретизации по времени и квантования по уровню.
Оцифровка звука происходит путем дискретизации по времени и квантования по уровню
Суть дискретизации состоит в записи значений физической величины не непрерывно, а только в определенные моменты времени, следующие с определенной частотой. Выбор частоты дискретизации определяется теоремой отсчетов (Котельникова — Найквиста — Шеннона), которая гласит, что исходный сигнал, максимальная частота компонентов которого не превышает F, можно полностью восстановить по измеренным отсчетам, следующим с частотой не меньше 2 × F. Таким образом, по отсчетам с частотой дискретизации 44100 Гц можно восстановить сигнал, частота компонентов которого не превышает 22050 Гц, что перекрывает указанный выше диапазон звуков, слышимых человеком.
Впрочем, точное восстановление сигнала возможно лишь в том случае, если измеренные значения записаны без ошибок. Однако аналого‑цифровые преобразователи (АЦП) отображают измеренную датчиком физическую величину на конечную разрядную сетку машинного представления числа, выполняя квантование значения по уровню. Это неизбежно ведет к ошибкам (погрешностям) записи измеренных значений. Например, 16-разрядные АЦП непрерывный интервал изменения физической величины отображают на 216 = 65536 уровней, которые могут быть пронумерованы целыми числами из диапазона -32768...+32767.
Описанный способ записи аналогового сигнала в цифровой форме называется импульсно‑кодовой модуляцией PCM (Pulse-Code Modulation). Для увеличения компактности записей используются разные методы их сжатия: с потерями (AAC, MP3, OGG) или без потерь (FLAC, WMA Losseless). Но перед воспроизведением данные все равно потребуется распаковать в импульсно‑кодовую форму, которая пригодна для непосредственного преобразования в физическую величину — звук.
Приложения для воспроизведения звука в Android используют объект класса Для просмотра ссылки Войдиили Зарегистрируйся, с помощью которого данные в кодировке PCM из программного буфера направляются в устройство воспроизведения. Служба Audio Flinger тесно взаимодействует со службой Audio Policy, которая на основе конфига audio_policy_configuration.xml определяет, какие устройства и в каком режиме должны использоваться для воспроизведения звука.
Устройство звуковой подсистемы Android
Слой аппаратной абстракции (HAL) сопоставляет стандартные виртуальные устройства Android — такие как AUDIO_DEVICE_OUT_SPEAKER (встроенный динамик), AUDIO_DEVICE_OUT_WIRED_HEADPHONE (проводные наушники) и AUDIO_DEVICE_OUT_AUX_DIGITAL (цифровой аудиовыход) — с конкретным физическим оборудованием. Это оборудование определяется и настраивается на уровне ядра Linux через описание в дереве устройств (Device Tree).
Разработчики HAL перечисляют устройства, которые могут использоваться аудиосервером, в секции <devicePorts> файла audio_policy_configuration.xml, например:
В приведенном примере виртуальному устройству AUDIO_DEVICE_OUT_SPEAKER, которое играет роль приемника звука sink (в противоположность источникам звука — source), назначается имя Speaker. По этому имени на устройство ссылаются в других местах конфига. Например, при перечислении аудиоустройств, которые встроены в приставку (в противоположность устройствам, которые могут подключаться периодически):
Первая команда рассказывает о том, как были интерпретированы настройки из audio_policy_configuration.xml. В частности, какой именно конфиг используется:
Далее dumpsys в понятной форме отображает правила, взятые из конфига. Например, сведения об устройствах и приоритетном канале воспроизведения звука описываются так:
А вот расшифровка:
Порты микшера mixPort описывают разные технологии обработки звуковых данных, которые определяются комбинацией значений в атрибуте flags. Например, в настройках обязательно должен присутствовать единственный приоритетный порт микшера с флагом AUDIO_OUTPUT_FLAG_PRIMARY. Другие варианты портов: AUDIO_OUTPUT_FLAG_DEEP_BUFFER — использование «глубокого» буфера для аудиопотоков, допускающих задержку воспроизведения, AUDIO_OUTPUT_FLAG_DIRECT — направление данных непосредственно на устройство воспроизведения, AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD — обработка упакованных данных (MP3, AAC и других) с помощью аппаратного декодера.
Связи портов микшера с устройствами воспроизведения звука описываются в секции маршрутов <routes>. Для каждого устройства‑приемника sink может быть указано через запятую несколько портов‑источников звука sources.
Если тип type маршрута указан как mix, то активными могут быть одновременно несколько из перечисленных портов, а если mux, то только один порт из списка. В любом случае выбор источников определяется аудиосервером в зависимости от запроса приложения.
Для устройств воспроизведения звука devicePort и портов микшера mixPort могут быть указаны профили profile, информирующие аудиосервер о способности устройств воспринимать аудиоданные. Профили характеризуются:
или Зарегистрируйся. Там же есть образец конфига, где показано использование расширенного набора этих констант, — Для просмотра ссылки Войди или Зарегистрируйся.
Если команда dumpsys media.audio_policy выполнена в то время, когда устройство проигрывало звук, то в блоке Outputs dump можно увидеть некоторые сведения об активных аудиопотоках. Однако гораздо больше информации о воспроизводящемся звуке предоставляет команда dumpsys media.audio_flinger:
В начале секции Output thread сообщается о формате аудиоданных, направляемых на устройство воспроизведения Output device (AUDIO_DEVICE_OUT_SPEAKER): PCM16 (AUDIO_FORMAT_PCM_16BIT), стерео (channel count: 2), с частотой дискретизации 44100 Гц (Sample rate: 44100 Hz). А воспроизводимый поток закодирован в PCM16 (Format 01), моно (Chn mask 01), с частотой дискретизации 32000 Гц (SRate 32000).
В выводе команды dumpsys media.audio_flinger много сведений о разных параметрах аудиосистемы. Не все они интуитивно понятны, и мне не попадалась документация с их подробным описанием. Поскольку за формирование всей этой информации отвечают методы AudioFlinger::ThreadBase::dump..., которые определены в файле исходных текстов Android 10 Для просмотра ссылки Войдиили Зарегистрируйся, именно к нему следует обращаться за необходимыми разъяснениями. В коде и сопроводительных комментариях помимо ответов на возникающие вопросы можно найти массу информации для размышления. Чего стоят одни лишь блоки FIXME!
<devicePort tagName="Built-In Mic" type="AUDIO_DEVICE_IN_BUILTIN_MIC" role="source" />
Это не помогло. Далее, анализируя информацию о службе Audio Flinger, я заметил такую закономерность: что бы ни воспроизводилось — файлы из аудиоколлекции, потоки интернет‑радио или онлайн телевидения, — на устройство воспроизведения Output device данные всегда передаются в одном и том же формате: PCM16, стерео, 44100 Гц. Само по себе это не плохо, но, может быть, причиной артефактов в звуке становится перекодировка? Вызывает недоумение и тот факт, что в файле audio_policy_configuration.xml присутствуют профили только с частотой дискретизации 48000 Гц. Я попробовал везде заменить 48000 на 44100, но это тоже не оказало никакого эффекта.
Следующий вопрос, который у меня возник: а почему в качестве устройства воспроизведения используется AUDIO_DEVICE_OUT_SPEAKER? Ведь никаких колонок непосредственно к приставке не подключено. В то же время, в перечне устройств <devicePorts> есть HDMI Out, которому соответствует AUDIO_DEVICE_OUT_AUX_DIGITAL, или, что то же самое, AUDIO_DEVICE_OUT_HDMI, — гораздо лучше отражающее действительность. Поэтому я добавил запись <item>HDMI Outitem>в секцию <attachedDevices> и перезагрузил приставку. Звук пропал совсем. Лишь приложив ухо к динамику телевизора можно было услышать тихий шепот.
Команда dumpsys media.audio_flinger показала, что звук теперь направляется на устройство AUDIO_DEVICE_OUT_AUX_DIGITAL. Прекрасная новость!
Но внимательный анализ строки о воспроизводимом потоке позволил установить, что уровень звука G (от англ. Gain) составляет -40 дБ, а не 0, как было раньше. И на кнопки регулировки громкости пульта дистанционного управления приставка перестала реагировать. Что ж, попробуем с этим разобраться.
Уровень цифровых сигналов принято указывать в децибелах относительно полной шкалы dBFS (deciBels relative to Full Scale), когда измеряется отношение A/Amax, где A — амплитуда оцениваемого сигнала, а Amax — максимально возможная амплитуда. Мощность пропорциональна квадрату амплитуды, поэтому G = 20 * Lg (A/Amax). В таком случае 0 дБ соответствует A = Amax, а все остальные возможные уровни сигнала A описываются отрицательными значениями, поскольку отношение A/Amax < 1. Так, -40 дБ соответствует сигналу, амплитуда которого в 102 = 100 раз меньше максимальной.
Она призывает обратить внимание на два подключаемых файла: audio_policy_volumes_drc.xml и default_volume_tables.xml.
В файле audio_policy_volumes_drc.xml лежат описания траекторий регулировки громкости (volume) для разных категорий устройств (deviceCategory) при воспроизведении разных аудиопотоков (stream). Например, кривая изменения звука для динамиков DEVICE_CATEGORY_SPEAKER при воспроизведении музыки AUDIO_STREAM_MUSIC описывается четырьмя лежащими на ней точками (<point>):
Каждая точка представлена двумя значениями: уровнем громкости по шкале процентов (от 0 до 100) и соответствующей ему степенью ослабления сигнала в миллибелах — сотых долях децибелов.
С устройством SPEAKER все более‑менее понятно. Но никаких зацепок для AUX_DIGITAL или HDMI в файле не видно. Обратимся к исходным текстам Android 10. В файле Для просмотра ссылки Войдиили Зарегистрируйся определен класс Volume, в котором есть метод, сопоставляющий идентификаторы устройств с их категориями:
Оказывается, устройство AUDIO_DEVICE_OUT_AUX_DIGITAL (оно же HDMI) относится к категории DEVICE_CATEGORY_EXT_MEDIA.
Возвращаемся к файлу audio_policy_volumes_drc.xml и видим, что все теги volume для интересующей нас категории вместо таблиц с точками кривой громкости содержат ссылки на них в атрибуте ref:
Сами же таблицы находятся в файле default_volume_tables.xml:
А почему уровень звука для воспроизводимого потока составляет именно -40 дБ? Узнаем, какой уровень громкости установлен на приставке:
Если отобразить значение 3 из шкалы уровней громкости 0...15 на шкалу процентов, то получим 3 / 15 × 100 = 20, что попадает прямо на вторую строчку таблицы точек кривой громкости и соответствует значению -4000 мБ = -40 дБ.
Кривая громкости DEFAULT_MEDIA_VOLUME_CURVE
Изменить установленный уровень громкости ни через интерфейс Android, ни из командной строки мне не удалось. Поэтому я просто в файле audio_policy_volumes_drc.xml для всех тегов с категорией устройства DEVICE_CATEGORY_EXT_MEDIA прописал ссылку на таблицу FULL_SCALE_VOLUME_CURVE, которая без вариантов задает максимальный уровень громкости:
После перезагрузки звук в режиме AUX_DIGITAL/HDMI появился, но его качество не стало лучше. Зато теперь регулировать громкость можно только на телевизоре, не трогая приставку, и это здорово.
Вариант использования звуковой подсистемы в режиме караоке из руководства по Allwinner H616
Здесь изображен интерфейс внутренней шины APBIF (Advanced Peripheral Bus InterFace), через три канала DMA TX FIFO которого сигнал от цифрового микрофона, музыкальный трек и системные оповещения поступают в звуковой концентратор AHUB (Audio HUB). Концентратор направляет аудиопотоки в цифровой аудиомикшер DAM0 (Digital Audio Mixer), где они смешиваются и передаются через первый из трех каналов DMA RX FIFO на шину OWA (One Wire Audio), а сигнал от цифрового микрофона отдельно выводится через второй канал. Кроме того, объединенный аудиопоток из микшера направляется на шины I2S0 и I2S1 телевизионных выходов CVBS и HDMI.
Интерес для нас представляют «карты» (в терминологии ALSA) 0, 2 и 3, которые изображают устройства Audio Codec, Audio HUB и выход HDMI соответственно. Каталоги pcmNc и pcmNp соответствуют «устройствам» (в терминологии ALSA) захвата (capture) и воспроизведения (playback) звука соответственно.
Когда приставка воспроизводит звук, информация о состоянии активных устройств появляется в файлах status, sw_params и hw_params их подкаталогов. Вот какие устройства были задействованы при разных настройках звука в Android:
В таблице символ «плюс» означает, что устройство используется, а «минус» — что нет. Конфигурацию используемого устройства можно подсмотреть в файле hw_params. В моем случае для всех активных устройств его содержимое было одинаковым, какие бы варианты медиаданных я ни воспроизводил:
или Зарегистрируйся.
Вот что получилось в файле tinymix_0.txt:
Здесь перечислены элементы управления карты Audio Codec, представленные шестнадцатью переменными. Их значения можно изменять так:
Для установки переменных типа BOOL надо использовать цифры 1 (Вкл/On) или 0 (Откл/Off). Переменные типа INT задаются числом из допустимого диапазона dsrange, а переменные типа ENUM — указанием конкретного значения из набора возможных.
или Зарегистрируйся. Сам модуль устанавливать не стал, потому что его оформление соответствует устаревшим версиям Magisk. Кстати, команду tinymix надо выполнять с правами суперпользователя.
Чтобы исключить возможные помехи для воспроизводимого звука, я попробовал уменьшить до нуля уровень сигналов от линейного входа «LINEIN to output mixer gain control» и FM-радио «FMIN to output mixer gain control». Это не повлияло на качество звука, так же, как и увеличение громкости цифрового звука (digital volume) не подействовало на звук в режиме HDMI. А отключение линейного выхода LINEOUT Switch приводило к остановке воспроизведения медиапотока. Видимо, драйвер информировал приложение медиапроигрывателя о возникновении проблемы.
Еще большее поле для настроек имеет карта 2 — Audio HUB:
Звук на телевизоре по‑прежнему есть, как и те артефакты, на борьбу с которыми направлены наши усилия.
Звук пропал, и воспроизведение в приложении медиаплеера остановилось. Через несколько секунд звук появился снова, а состояние выхода из Off автоматически переключилось на On. Вероятно, драйвер обнаружил проблему и самостоятельно ее исправил. Таким образом, можно с большой уверенностью заявить, что выход I2S1 соответствует HDMI.
А к чему привязан I2S2? Может быть, к CVBS? Я попробовал направить источник APBIF_TXDIF2 на I2S0:
Звук снова пропал, как будто выход отключили. Получается, что из APBIF_TXDIF2 не поступает никаких аудиоданных, поэтому как его назначение, так и назначение I2S2 остаются под вопросом. Когда воспроизведение автоматически восстанавливается, в журнал logcat записываются такие строки:
Может быть, неработающие элементы управления относятся к ahub_bt_out? Как бы то ни было, с учетом полученной информации я нарисовал такую схему работы ТВ‑приставки.
Реальная схема работы звуковой подсистемы ТВ‑приставки X96Q PRO
Как еще можно попробовать устранить проблемы со звуком? Изменение «ahub audio format Function» с pcm на DTS, DOLBY_DIGITAL_PLUS и даже null не возымело никакого эффекта. Тогда я решил напоследок переконфигурировать Audio HUB так, чтобы звуковой поток проходил через микшер DAM0. Это приблизило бы работу звуковой подсистемы к эталонному примеру из даташита.
Экспериментальная схема работы звуковой подсистемы ТВ‑приставки X96Q PRO
Для реализации своего замысла я выполнил следующие команды:
Звук по новой схеме заработал как ни в чем ни бывало, только потрескивания и пощелкивания из него не исчезли. Нужны какие‑то новые идеи.
или Зарегистрируйся работы USB Creative SoundBlaster MP3+ на Raspberry Pi.
Участник, инициировавший дискуссию, отмечает зависимость качества воспроизводимого звука от комбинации параметров period_size/buffer_size, которыми инициализируется устройство ALSA. При некоторых значениях наблюдаются дефекты, которые автор сообщения назвал «заиканием» (shuttering).
В ответах другие пользователи сообщают, что проблема может заключаться в выполнении принудительного внутреннего преобразования частоты дискретизации из 48000 Гц к 44100 Гц при прохождении аудиопотока по элементам звуковой подсистемы.
Это обсуждение побудило меня продолжить поиски в направлении параметров цифрового звука. Я вспомнил о том, что в audio_policy_configuration.xml везде прописаны значения 48000, а все аудиопотоки воспроизводятся с частотой дискретизации 44100. Но, оглядываясь назад, я не смог припомнить никаких возможностей каким‑то образом повлиять на эту ситуацию. Кроме того, я тестировал воспроизведение медиаданных, которые были закодированы с разными частотами дискретизации: 16000, 22050, 24000, 32000, 44100 и 48000, — и все они имели дефекты звучания.
В каталоге /vendor/etc/ нашелся файл audio_platform_info.xml, содержащий параметры для инициализации звуковых устройств:
Однако, во‑первых, я не смог обнаружить никаких признаков использования этого файла операционной системой приставки, а, во‑вторых, в нем мы снова видим частоту дискретизации 48000.
Наверное, пришло время вступить в самую темную область звуковой подсистемы Android и заняться изучением библиотеки промежуточного слоя Audio HAL. Почему я так мрачно высказываюсь об этой части звуковой подсистемы? Потому что остальные ее компоненты неплохо изучены и документированы, а вот HAL — это прерогатива производителя конкретного устройства, которая обычно сопровождается всякими словами типа сonfidential, и мне бы очень не хотелось во все это погружаться. Но по всем законам сказочного жанра, чему быть, того не миновать.
или Зарегистрируйся сообщает, что библиотека HAL должна иметь имя audio.primary.<device>.so. На приставке нашлись два файла, отвечающих этому требованию: /vendor/lib/hw/audio.primary.default.so и /vendor/lib/hw/audio.primary.cupid.so. Наибольший интерес представляет второй файл, потому что именно он реализует функции, специфичные для нашего устройства. Изучим его подробнее.
Посмотрим, какие аудиоустройства обслуживает библиотека HAL audio.primary.cupid.so:
Аналогичным образом мы можем посмотреть, какие системные настройки ей известны:
Присваивание свойству vendor.mediasw.sft.rawdata значения с помощью команды setprop на воспроизведение звука не оказывает влияния, потому что значение как этого свойства, так и свойства vendor.audio.output.active переустанавливаются в соответствии с настройками звука в Android.
Файл громоздкий, потому что учитывает возможность подключения и отключения разных устройств ввода и вывода звука, таких как USB-микрофоны и беспроводные гарнитуры. При обычном использовании приставки записи об установке режимов работы в журнале logcat выглядят так:
Режиму media-speaker-headphones соответствует вот эта часть конфига:
Сопоставив этот фрагмент со схемой звуковой подсистемы Allwinner H616 и режимами работы компонента Audio Codec, который мы исследовали на уровне ALSA, ты без особого труда увидишь, как применяются настройки из этого файла. Но мы по‑прежнему не знаем, где устанавливаются параметры цифрового звука.
или Зарегистрируйся о микросхеме с таким наименованием.
Оказалось, она представляет собой высокоинтегрированную аудиоподсистему производства X-Powers и предназначена для использования в Wi-Fi колонках, планшетах и смартфонах. Ее описание перекликается с тем, что мы видели в руководстве по Allwinner H616, но принцип работы и управляющие параметры здесь рассмотрены более подробно.
Из Для просмотра ссылки Войдиили Зарегистрируйся на микросхему можно понять (и простить), почему для основного канала вывода звука в модуле HAL был выбран странный для телевизионной приставки идентификатор media-speaker-headphones.
Что еще мы можем предпринять? Давай посмотрим, что записывается в журнал logcat при старте воспроизведения медиапотока с частотой дискретизации 32000 Гц приложением Для просмотра ссылки Войдиили Зарегистрируйся.
Строки с пометкой audio_hw_primary записаны библиотекой Audio HAL. Из этого отрывка видно, что, несмотря на частоту воспроизводимых аудиоданных 32000 Гц и значения 48000 Гц во всех настройках, библиотека HAL инициализирует устройства AUDIO_CODEC и AUDIO_HDMI значением 44100 Гц. Я решительно не понимаю, почему так происходит, поэтому решил исследовать код библиотеки HAL. Исходников мне найти не удалось, пришлось воспользоваться дизассемблером.
Дизассемблируем библиотеку audio.primary.cupid.so:
Теперь выполним поиск по значению 44100 в полученном листинге:
Ответом стали 18 строк с командами загрузки числа 44100 в разные регистры:
Наиболее примечательный участок кода выглядит так:
Похоже, что он записывает десятичные числа 960, 1920 и 44100 в структуру, на которую указывает содержимое регистра r8. А где нам встречались эти значения? В файле, отражающем настройки звуковой подсистемы драйверов Linux /proc/asound/card3/pcm0p/sub0/hw_params. Видимо, именно здесь происходит подготовка этой подсистемы к воспроизведению.
Какие выводы из всего этого можно сделать? Во‑первых, библиотека инициализирует звуковую подсистему фиксированными значениями, представленными в ее коде, а не полученными из конфигурационных файлов или настроек операционной системы. Во‑вторых, эти значения вшиты непосредственно в машинный код команд, а не хранятся отдельно в сегменте данных. Последний факт усложняет задачу изменения этих значений.
Кроме того, у меня было настроение Бармалея, который в ответ на замечание главного героя кинофильма «Айболит-66» о том, что нехорошо обманывать маленькую обезьянку, нетерпеливо возразил: «Я не могу ждать, пока она вырастет!». Поэтому я принял авантюрное решение: заменить значение 44100 на 48000 во всех командах mov, где оно встречается.
С одной стороны, вероятно, существуют участки кода, в которых значение 44100 используется не для инициализации звуковой подсистемы, а, например, для сравнения с параметром предоставленного к воспроизведению аудиопотока. С другой стороны, если раньше при инициализации звуковой подсистемы всегда использовалось значение 44100, то сейчас вместо него будет стоять 48000.
Я бы ни за что не стал общим чохом вслепую менять в банковском приложении 100 на 150, если бы вышел закон о новой пороговой сумме каких‑то операций. Потому что 100 может быть как величиной этой суммы, так и, например, коэффициентом перевода рублей в копейки. Да и финансовая сфера — совсем не та область, в которой допустимы подобные легкомысленные решения.
Но 44100 — слишком уж характерная величина, которую сложно с чем‑нибудь перепутать. Кроме того, маловероятно, что частота дискретизации явно влияет на выделение буферов (иначе такое грубое вмешательство могло бы привести к утечкам памяти). Ну и в самом худшем случае ущерб ограничится неисправной телевизионной приставкой — потеря не так велика.
В этот момент последовательный рассудительный инженер уходит со сцены, и в лучах софитов на ней появляется хакер!
Что менять, мы уже видим. Теперь надо выяснить, на что это менять. Для этого посмотрим, как выглядят команды загрузки в регистры значения 48000:
Сопоставив шестнадцатеричные машинные коды с мнемоническими записями команд, можно сделать вывод, что для замены команд movw r?, #44100 на movw r?, #48000 надо выполнить такие подстановки:
Полная таблица исправлений для файла audio.primary.cupid.so с контрольной суммой MD5 daa172745b41d0db7e7e03bdaae4796b будет выглядеть следующим образом:
Контрольная сумма MD5 исправленного файла — c58c080cca142114dcd840d0d94f66a7.
Звук приставки стал если не идеальным, то вполне приемлемым. Из аудиосопровождения телевизионных программ исчезли «царапающие» щелчки и запинания, постепенно ушло и нервное напряжение, связанное с ожиданием проявления очередного дефекта.
Проверяю параметры аудиопотока с помощью команды dumpsys media.audio_flinger:
Output thread 0xeb193880, name AudioOut_D, tid 2114, type 0 (MIXER):
Она показывает, что исходные аудиоданные с частотой дискретизации 44100 Гц преобразуются для передачи на аудиовыход с частотой дискретизации 48000 Гц. Но неожиданно для меня изменились значения HAL frame count: 896 вместо 960 и HAL buffer size: 3584 вместо 3840. А что произошло на уровне ALSA? Содержимое файла /proc/asound/card3/pcm0p/sub0/hw_params подтверждает изменение частоты дискретизации выходного потока, при этом величины period_size и buffer_size остались прежними:
Может быть, изменившееся на уровне аудиосервера значение HAL frame count привело к тому, что операции заполнения аппаратного и программного буферов аудиоданных разнеслись по времени, в результате чего устранились накладки, которые происходили раньше?
Как бы то ни было, многочасовая эксплуатация телевизионной приставки с измененной библиотекой Audio HAL не выявила никаких побочных эффектов. Разнообразные медиапотоки воспроизводились с хорошим качеством звука и не вызывали сбоев.
В качестве основного устройства воспроизведения можно вернуть AUDIO_DEVICE_OUT_SPEAKER, если отсутствие возможности регулировки уровня громкости для AUDIO_DEVICE_OUT_HDMI вызывает дискомфорт — это не испортит достигнутого результата.
Последняя модификация устранила проблему низкого качества звука на телевизоре, к которому подключена приставка X96Q PRO. Способ, которым были выполнены исправления, нельзя назвать бесспорным. Но он позволил быстро достичь приемлемого результата, а последовательный инженерный подход при отсутствии исходников потребовал бы слишком больших усилий и времени.
Ради интереса я проверил состав дистрибутивов Android 10 для некоторых других устройств, построенных на платформе Allwinner H313/H616. Оказалось, что файл библиотеки Audio HAL от телевизионной приставки X96Q PRO ничем не отличается от тех, которые поставляются в дистрибутивах для Orange Pi Zero2 и Tanix TX6S.
Отсутствие массовых сообщений о проблеме с воспроизведением звука на подключенных к таким устройствам телевизорах может говорить о том, что описанные в начале статьи звуковые дефекты стали результатом несовместимости аудиоданных, которые выдает приставка, с конкретной моделью телевизора. Но это не отменяет того факта, что параметры, прописанные в конфигах, не соответствуют фактически устанавливаемым при воспроизведении звука. Не помешала бы и возможность управления этими параметрами через графический интерфейс Android.
Печально, но факт: причина проблем крылась в софте приставки. Похоже, ему в сфере массового производства уделяют все меньше внимания. В результате даже качественное «железо» не всегда может раскрыть свои лучшие стороны.
Android в нашей подопытной приставке точно такой же, как в телефонах и автомагнитолах. Все принципиальные сведения, кроме конкретных схем и конкретного кода библиотеки HAL, актуальны для всех устройств на Android 10, и, с большой долей вероятности, от Android 7 до Android 13 включительно. Начиная с версии 14 в Android стали использовать язык описания интерфейса AIDL вместо HIDL, но это касается в большей степени низкоуровневых разработчиков.
В каком ухе у меня жужжит?
Эта история началась с замены телевизионной приставки. У меня был старенький девайс с одним гигабайтом памяти — на смену ему я выбрал модель Для просмотра ссылки ВойдиЗаполучив приставку, я ее подключил, настроил и поставил на нее привычный набор приложений. На первый взгляд все было прекрасно: видео воспроизводилась, музыка звучала, игры игрались.
Но уже на второй день я почувствовал неладное. Какой бы медиаконтент ни играл, в звуковом сопровождении постоянно проскакивали то ли пощелкивания, то ли потрескивания, как будто во время просмотра аналогового телевидения рядом проходит грозовой фронт.
В художественных фильмах или новостных программах это почти незаметно, но в отдельных музыкальных композициях звук искажался до невозможности. Я не аудиофил и меня не испугать записью концерта в MP3 со скоростью потока 128 Кбит/с. Но щелчки все же стали действовать на нервы. Я понял, что если это не исправить, то скоро начнет дергаться глаз.
Если у тебя есть устройство с Android, ты можешь прямо сейчас проверить, присущ ли ему описываемый дефект. Для этого воспроизведи файл Для просмотра ссылки Войди
Возможно, тебя миновала эта неприятность, но тебе интересно устройство аудиоподсистемы Android 10 и способ изменения частоты дискретизации выводимого приставкой аудиопотока. Об этом я тоже постараюсь рассказать.
Методика и инструменты исследования
Сначала, как любой нормальный человек, я попытался настроить звук через интерфейс Android, но выбор там невелик: всего‑то и можно, что включить или выключить AUDIO_CODEC, AUDIO_HDMI и режим проброса звука passthrough. Вне зависимости от установленных флажков, дефекты звука продолжали свое деструктивное воздействие на психику.Я перебрал несколько медиапроигрывателей, но это тоже не дало результата. Вот и все, обычный пользователь потерпел поражение в этой схватке с Android. Поэтому на сцену пришлось выйти инженеру. Инженерный подход к решению проблем подразумевает сбор и изучение доступной информации об объекте, его диагностику для выяснения причин проблемы, выбор решения и исправление ситуации. Что ж, приступим!
План исследования звуковой подсистемы Android следующий:
- Анализ системного журнала Android, который можно получить с помощью команды logcat.
- Изучение состояний служб Android, сведения о которых можно получить с помощью команды dumpsys.
- Изучение состояния подсистемы ALSA по содержимому каталога /proc/asound и с помощью утилиты tinymix.
- Анализ динамической библиотеки слоя абстракции Audio HAL в среде Ubuntu с помощью дизассемблера objdump.
- Анализ исходных текстов операционной системы Android.
Осторожно, кирпич!
Внося изменения в конфигурационные файлы Android-устройства, оказалось удивительно легко привести его в неработоспособное состояние, или, как говорят, «окирпичить». Например, если в теге HDMI Out файла audio_policy_configuration.xml слово Out написать с маленькой буквы, загрузка телевизионной приставки будет останавливаться на этапе демонстрации анимированной заставки.В большинстве случаев восстановление работоспособности возможно, но для этого потребуются специальные знания и софт. С последним сложнее всего. На Для просмотра ссылки Войди
Поэтому трезво оцени свои силы перед тем, как что‑то сделать на реальном оборудовании. Как минимум заранее ознакомься с опытом неудачников на тематических форумах. Но все равно будь готов к тому, что устройство может быть безнадежно испорчено.
Команды можно выполнять непосредственно в консоли устройства из приложения‑терминала, например, Для просмотра ссылки Войди
Для замены конфигов и установки утилиты tinymix я использовал Для просмотра ссылки Войди
Хоть приставка и была изначально рутована, в использовании Magisk нашлось неоспоримое преимущество. Если с новым вариантом конфига приставка не загружается, достаточно дважды перезагрузить ее, переподключая блок питания. Magisk подсчитывает количество неудачных включений и на третий раз отключает все установленные модули, что позволяет приставке загрузиться в заводском исполнении.
info
Иногда Magisk приводит к интересным побочным эффектам. Я заметил, что при активном модуле с исправлениями после перезагрузки восстанавливается расположение плиток на рабочем столе ATV Launcher. Чтобы переместить плитки «навсегда», надо сначала отключить модуль, потом выполнить перемещения, после чего снова его включить. Такая защита от случайного нарушения привычной раскладки интерфейса мне показалась очень удобной.
Что такое цифровой звук
Чтобы лучше понимать работу звуковой подсистемы приставки, надо вспомнить, что собой представляют звуковые данные. Как ты знаешь из школьного курса физики, человек слышит звук из‑за изменения давления воздуха на расположенную в ухе барабанную перепонку с частотой 20 — 20000 Гц. Этот процесс, как и все остальные физические процессы в макромире, благодаря явлению инерции является плавным и непрерывным.Для использования в компьютерной технике аналоговая физическая величина — в данном случае давление воздуха — представляется конечной последовательностью чисел из ограниченного набора с помощью двух подходов: дискретизации по времени и квантования по уровню.

Суть дискретизации состоит в записи значений физической величины не непрерывно, а только в определенные моменты времени, следующие с определенной частотой. Выбор частоты дискретизации определяется теоремой отсчетов (Котельникова — Найквиста — Шеннона), которая гласит, что исходный сигнал, максимальная частота компонентов которого не превышает F, можно полностью восстановить по измеренным отсчетам, следующим с частотой не меньше 2 × F. Таким образом, по отсчетам с частотой дискретизации 44100 Гц можно восстановить сигнал, частота компонентов которого не превышает 22050 Гц, что перекрывает указанный выше диапазон звуков, слышимых человеком.
Впрочем, точное восстановление сигнала возможно лишь в том случае, если измеренные значения записаны без ошибок. Однако аналого‑цифровые преобразователи (АЦП) отображают измеренную датчиком физическую величину на конечную разрядную сетку машинного представления числа, выполняя квантование значения по уровню. Это неизбежно ведет к ошибкам (погрешностям) записи измеренных значений. Например, 16-разрядные АЦП непрерывный интервал изменения физической величины отображают на 216 = 65536 уровней, которые могут быть пронумерованы целыми числами из диапазона -32768...+32767.
Описанный способ записи аналогового сигнала в цифровой форме называется импульсно‑кодовой модуляцией PCM (Pulse-Code Modulation). Для увеличения компактности записей используются разные методы их сжатия: с потерями (AAC, MP3, OGG) или без потерь (FLAC, WMA Losseless). Но перед воспроизведением данные все равно потребуется распаковать в импульсно‑кодовую форму, которая пригодна для непосредственного преобразования в физическую величину — звук.
Звуковая подсистема приставки
В Android запросы на работу со звуком обслуживает служба Audio Flinger сервера звука, которая через слой абстракции от аппаратуры HAL (Hardware Abstraction Layer) управляет звуковой подсистемой ALSA (Advanced Linux Sound System) уровня ядра.Приложения для воспроизведения звука в Android используют объект класса Для просмотра ссылки Войди

Слой аппаратной абстракции (HAL) сопоставляет стандартные виртуальные устройства Android — такие как AUDIO_DEVICE_OUT_SPEAKER (встроенный динамик), AUDIO_DEVICE_OUT_WIRED_HEADPHONE (проводные наушники) и AUDIO_DEVICE_OUT_AUX_DIGITAL (цифровой аудиовыход) — с конкретным физическим оборудованием. Это оборудование определяется и настраивается на уровне ядра Linux через описание в дереве устройств (Device Tree).
Разработчики HAL перечисляют устройства, которые могут использоваться аудиосервером, в секции <devicePorts> файла audio_policy_configuration.xml, например:
Код:
<devicePorts>
<devicePort tagName="Speaker" type="AUDIO_DEVICE_OUT_SPEAKER" role="sink"></devicePort>
<devicePort tagName="HDMI Out" type="AUDIO_DEVICE_OUT_AUX_DIGITAL" role="sink"></devicePort>
</devicePorts>
В приведенном примере виртуальному устройству AUDIO_DEVICE_OUT_SPEAKER, которое играет роль приемника звука sink (в противоположность источникам звука — source), назначается имя Speaker. По этому имени на устройство ссылаются в других местах конфига. Например, при перечислении аудиоустройств, которые встроены в приставку (в противоположность устройствам, которые могут подключаться периодически):
Код:
<attachedDevices>
<item>Speaker</item>
</attachedDevices>
Уровень аудиосервера
Сбор информации о службах
Анализировать работу звуковой подсистемы Android на уровне аудиосервера помогает информация о состоянии служб, которую выводит команда dumpsys. В нашем случае полезны два варианта этой команды:
Код:
dumpsys media.audio_policy
dumpsys media.audio_flinger
Config source: /vendor/etc/audio_policy_configuration.xml
Далее dumpsys в понятной форме отображает правила, взятые из конфига. Например, сведения об устройствах и приоритетном канале воспроизведения звука описываются так:
Код:
<modules>
<module name="primary" halVersion="2.0">
<attachedDevices>
<item>Speaker</item>
...
</attachedDevices>
<defaultOutputDevice>Speaker</defaultOutputDevice>
<mixPorts>
<mixPort name="primary output" role="source" flags="AUDIO_OUTPUT_FLAG_PRIMARY">
<profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
</mixPort>
...
</mixPorts>
<devicePorts>
<devicePort tagName="Speaker" type="AUDIO_DEVICE_OUT_SPEAKER" role="sink"></devicePort>
<devicePort tagName="HDMI Out" type="AUDIO_DEVICE_OUT_AUX_DIGITAL" role="sink"></devicePort>
...
</devicePorts>
<routes>
<route type="mix" sink="Speaker" sources="primary output"/>
<route type="mix" sink="HDMI Out" sources="primary output"/>
...
</routes>
</module>
</modules>
А вот расшифровка:
Код:
- Available output devices:
Device 1:
- id: 2
- tag name: Speaker
- type: AUDIO_DEVICE_OUT_SPEAKER
- Profiles:
Profile 0:[dynamic format][dynamic channels][dynamic rates]
...
HW Modules dump:
- HW Module 1:
- name: primary
- handle: 10
- version: 2.0
- outputs:
output 0:
- name: primary output
- Profiles:
Profile 0:
- format: AUDIO_FORMAT_PCM_16_BIT
- sampling rates:48000
- channel masks:0x0003
- flags: 0x0002 (AUDIO_OUTPUT_FLAG_PRIMARY)
- Supported devices:
Device 1:
- id: 2
- tag name: Speaker
- type: AUDIO_DEVICE_OUT_SPEAKER
...
Device 7:
- tag name: HDMI Out
- type: AUDIO_DEVICE_OUT_AUX_DIGITAL|AUDIO_DEVICE_OUT_HDMI
Порты микшера mixPort описывают разные технологии обработки звуковых данных, которые определяются комбинацией значений в атрибуте flags. Например, в настройках обязательно должен присутствовать единственный приоритетный порт микшера с флагом AUDIO_OUTPUT_FLAG_PRIMARY. Другие варианты портов: AUDIO_OUTPUT_FLAG_DEEP_BUFFER — использование «глубокого» буфера для аудиопотоков, допускающих задержку воспроизведения, AUDIO_OUTPUT_FLAG_DIRECT — направление данных непосредственно на устройство воспроизведения, AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD — обработка упакованных данных (MP3, AAC и других) с помощью аппаратного декодера.
Связи портов микшера с устройствами воспроизведения звука описываются в секции маршрутов <routes>. Для каждого устройства‑приемника sink может быть указано через запятую несколько портов‑источников звука sources.
Если тип type маршрута указан как mix, то активными могут быть одновременно несколько из перечисленных портов, а если mux, то только один порт из списка. В любом случае выбор источников определяется аудиосервером в зависимости от запроса приложения.
Для устройств воспроизведения звука devicePort и портов микшера mixPort могут быть указаны профили profile, информирующие аудиосервер о способности устройств воспринимать аудиоданные. Профили характеризуются:
- форматом format, например AUDIO_FORMAT_PCM_16_BIT, AUDIO_FORMAT_PCM_FLOAT, AUDIO_FORMAT_DTS_HD;
- частотой дискретизации samplingRates, например 32000, 44100, 48000 Гц;
- количеством каналов channelMasks, например, AUDIO_CHANNEL_OUT_MONO, AUDIO_CHANNEL_OUT_STEREO, AUDIO_CHANNEL_OUT_5POINT1.
www
В исходниках Android 10 можно найти определения перечисленных выше и других возможных констант — в файле Для просмотра ссылки ВойдиЕсли команда dumpsys media.audio_policy выполнена в то время, когда устройство проигрывало звук, то в блоке Outputs dump можно увидеть некоторые сведения об активных аудиопотоках. Однако гораздо больше информации о воспроизводящемся звуке предоставляет команда dumpsys media.audio_flinger:
Код:
Output thread 0xea794240, name AudioOut_D, tid 2032, type 0 (MIXER):
I/O handle: 13
Standby: no
Sample rate: 44100 Hz
HAL frame count: 960
HAL format: 0x1 (AUDIO_FORMAT_PCM_16_BIT)
HAL buffer size: 3840 bytes
Channel count: 2
Channel mask: 0x00000003 (front-left, front-right)
Processing format: 0x1 (AUDIO_FORMAT_PCM_16_BIT)
Processing frame size: 4 bytes
Pending config events: none
Output device: 0x2 (AUDIO_DEVICE_OUT_SPEAKER)
Input device: 0 (AUDIO_DEVICE_NONE)
Audio source: 0 (AUDIO_SOURCE_DEFAULT)
...
AudioStreamOut: 0xea727d48 flags 0x2 (AUDIO_OUTPUT_FLAG_PRIMARY)
...
1 Tracks of which 1 are active
Type Id Active Client Session Port Id S Flags Format Chn mask SRate ST Usg CT G db L dB R dB VS dB Server FrmCnt FrmRdy F Underruns Flushed Latency
65 yes 12662 57 17 A 0x000 00000001 00000001 32000 3 1 3 0 0 0 0 000381F5 8000 8000 A
В начале секции Output thread сообщается о формате аудиоданных, направляемых на устройство воспроизведения Output device (AUDIO_DEVICE_OUT_SPEAKER): PCM16 (AUDIO_FORMAT_PCM_16BIT), стерео (channel count: 2), с частотой дискретизации 44100 Гц (Sample rate: 44100 Hz). А воспроизводимый поток закодирован в PCM16 (Format 01), моно (Chn mask 01), с частотой дискретизации 32000 Гц (SRate 32000).
В выводе команды dumpsys media.audio_flinger много сведений о разных параметрах аудиосистемы. Не все они интуитивно понятны, и мне не попадалась документация с их подробным описанием. Поскольку за формирование всей этой информации отвечают методы AudioFlinger::ThreadBase::dump..., которые определены в файле исходных текстов Android 10 Для просмотра ссылки Войди
Переключение с колонок на цифровой выход
Первое, что я сделал, — это убрал из конфига упоминания о встроенном микрофоне. Его в приставке нет, но, возможно, попытка его как‑то активировать или использовать приводит к шумам.<devicePort tagName="Built-In Mic" type="AUDIO_DEVICE_IN_BUILTIN_MIC" role="source" />
Это не помогло. Далее, анализируя информацию о службе Audio Flinger, я заметил такую закономерность: что бы ни воспроизводилось — файлы из аудиоколлекции, потоки интернет‑радио или онлайн телевидения, — на устройство воспроизведения Output device данные всегда передаются в одном и том же формате: PCM16, стерео, 44100 Гц. Само по себе это не плохо, но, может быть, причиной артефактов в звуке становится перекодировка? Вызывает недоумение и тот факт, что в файле audio_policy_configuration.xml присутствуют профили только с частотой дискретизации 48000 Гц. Я попробовал везде заменить 48000 на 44100, но это тоже не оказало никакого эффекта.
Следующий вопрос, который у меня возник: а почему в качестве устройства воспроизведения используется AUDIO_DEVICE_OUT_SPEAKER? Ведь никаких колонок непосредственно к приставке не подключено. В то же время, в перечне устройств <devicePorts> есть HDMI Out, которому соответствует AUDIO_DEVICE_OUT_AUX_DIGITAL, или, что то же самое, AUDIO_DEVICE_OUT_HDMI, — гораздо лучше отражающее действительность. Поэтому я добавил запись <item>HDMI Outitem>в секцию <attachedDevices> и перезагрузил приставку. Звук пропал совсем. Лишь приложив ухо к динамику телевизора можно было услышать тихий шепот.
Команда dumpsys media.audio_flinger показала, что звук теперь направляется на устройство AUDIO_DEVICE_OUT_AUX_DIGITAL. Прекрасная новость!
Код:
Output thread 0xeba93640, name AudioOut_D, tid 2068, type 0 (MIXER):
...
Output device: 0x400 (AUDIO_DEVICE_OUT_AUX_DIGITAL|AUDIO_DEVICE_OUT_HDMI)
...
1 Tracks of which 1 are active
Type Id Active Client Session Port Id S Flags Format Chn mask SRate ST Usg CT G db L dB R dB VS dB Server FrmCnt FrmRdy F Underruns Flushed Latency
55 yes 5309 17 7 A 0x000 00000005 00000003 44100 3 1 3 -40 0 0 0 00389A00 3848 2888 A 0 0 unavail
info
Децибел (дБ) — это числовая величина, равная десяти (деци) десятичным логарифмам отношения двух сравниваемых величин (бел): G = 10 * Lg (P2/P1). В аудиотехнике P1 и P2 — это мощности входного и выходного сигнала соответственно.Уровень цифровых сигналов принято указывать в децибелах относительно полной шкалы dBFS (deciBels relative to Full Scale), когда измеряется отношение A/Amax, где A — амплитуда оцениваемого сигнала, а Amax — максимально возможная амплитуда. Мощность пропорциональна квадрату амплитуды, поэтому G = 20 * Lg (A/Amax). В таком случае 0 дБ соответствует A = Amax, а все остальные возможные уровни сигнала A описываются отрицательными значениями, поскольку отношение A/Amax < 1. Так, -40 дБ соответствует сигналу, амплитуда которого в 102 = 100 раз меньше максимальной.
Настройка уровня громкости
В конце конфига audio_policy_configuration.xml есть группа строк, обозначенная как «Секция громкости»:
Код:
<!-- Volume section -->
<xi:include href="audio_policy_volumes_drc.xml"/>
<xi:include href="default_volume_tables.xml"/>
Она призывает обратить внимание на два подключаемых файла: audio_policy_volumes_drc.xml и default_volume_tables.xml.
В файле audio_policy_volumes_drc.xml лежат описания траекторий регулировки громкости (volume) для разных категорий устройств (deviceCategory) при воспроизведении разных аудиопотоков (stream). Например, кривая изменения звука для динамиков DEVICE_CATEGORY_SPEAKER при воспроизведении музыки AUDIO_STREAM_MUSIC описывается четырьмя лежащими на ней точками (<point>):
Код:
<volume stream="AUDIO_STREAM_MUSIC" deviceCategory="DEVICE_CATEGORY_SPEAKER">
<point>1,-2400</point>
<point>33,-1200</point>
<point>66,-600</point>
<point>100,0</point>
</volume>
Каждая точка представлена двумя значениями: уровнем громкости по шкале процентов (от 0 до 100) и соответствующей ему степенью ослабления сигнала в миллибелах — сотых долях децибелов.
С устройством SPEAKER все более‑менее понятно. Но никаких зацепок для AUX_DIGITAL или HDMI в файле не видно. Обратимся к исходным текстам Android 10. В файле Для просмотра ссылки Войди
Код:
static device_category getDeviceCategory(audio_devices_t device)
{
switch(getDeviceForVolume(device)) {
...
case AUDIO_DEVICE_OUT_LINE:
case AUDIO_DEVICE_OUT_AUX_DIGITAL:
case AUDIO_DEVICE_OUT_USB_DEVICE:
return DEVICE_CATEGORY_EXT_MEDIA;
...
default:
return DEVICE_CATEGORY_SPEAKER;
}
}
Оказывается, устройство AUDIO_DEVICE_OUT_AUX_DIGITAL (оно же HDMI) относится к категории DEVICE_CATEGORY_EXT_MEDIA.
Возвращаемся к файлу audio_policy_volumes_drc.xml и видим, что все теги volume для интересующей нас категории вместо таблиц с точками кривой громкости содержат ссылки на них в атрибуте ref:
Код:
<volume stream="AUDIO_STREAM_MUSIC" deviceCategory="DEVICE_CATEGORY_EXT_MEDIA"
ref="DEFAULT_MEDIA_VOLUME_CURVE"/>
Код:
<reference name="DEFAULT_MEDIA_VOLUME_CURVE">
<!-- Default Media reference Volume Curve -->
<point>1,-5800</point>
<point>20,-4000</point>
<point>60,-1700</point>
<point>100,0</point>
</reference>
А почему уровень звука для воспроизводимого потока составляет именно -40 дБ? Узнаем, какой уровень громкости установлен на приставке:
Код:
media volume --stream 3 --get
[v] will control stream=3 (STREAM_MUSIC)
[v] will get volume
[v] Connecting to AudioService
[v] volume is 3 in range [0..15]
Если отобразить значение 3 из шкалы уровней громкости 0...15 на шкалу процентов, то получим 3 / 15 × 100 = 20, что попадает прямо на вторую строчку таблицы точек кривой громкости и соответствует значению -4000 мБ = -40 дБ.

Изменить установленный уровень громкости ни через интерфейс Android, ни из командной строки мне не удалось. Поэтому я просто в файле audio_policy_volumes_drc.xml для всех тегов с категорией устройства DEVICE_CATEGORY_EXT_MEDIA прописал ссылку на таблицу FULL_SCALE_VOLUME_CURVE, которая без вариантов задает максимальный уровень громкости:
Код:
<reference name="FULL_SCALE_VOLUME_CURVE">
<!-- Full Scale reference Volume Curve -->
<point>0,0</point>
<point>100,0</point>
</reference>
После перезагрузки звук в режиме AUX_DIGITAL/HDMI появился, но его качество не стало лучше. Зато теперь регулировать громкость можно только на телевизоре, не трогая приставку, и это здорово.
Уровень ALSA
Изучение документации
Несмотря на определенный прогресс в настройке приставки, на уровне аудиосервера решить проблему с потрескиванием мне не удалось. Тогда я решил погрузиться на самое дно и посмотреть, как выглядит ситуация с точки зрения ALSA. К сожалению, в открытом доступе спецификации чипа Allwinner H313 не нашлось. Производитель ограничился выпуском брошюры, из которой можно узнать, что в звуковой подсистеме этого чипа имеются:- Audio Codec c двумя каналами DAC (ЦАП) и одним аудиовыходом;
- DMIC — цифровой микрофон;
- OWA OUT — однопроводная шина для передачи аудиоданных;
- Audio HUB — звуковой концентратор, поддерживающий три канала I2S/PCM.
www
- Брошюра по SoC Allwinner H313 (Для просмотра ссылки Войди
или Зарегистрируйся) - Брошюра по Soc Allwinner H616 (Для просмотра ссылки Войди
или Зарегистрируйся) - Для просмотра ссылки Войди
или Зарегистрируйся

Здесь изображен интерфейс внутренней шины APBIF (Advanced Peripheral Bus InterFace), через три канала DMA TX FIFO которого сигнал от цифрового микрофона, музыкальный трек и системные оповещения поступают в звуковой концентратор AHUB (Audio HUB). Концентратор направляет аудиопотоки в цифровой аудиомикшер DAM0 (Digital Audio Mixer), где они смешиваются и передаются через первый из трех каналов DMA RX FIFO на шину OWA (One Wire Audio), а сигнал от цифрового микрофона отдельно выводится через второй канал. Кроме того, объединенный аудиопоток из микшера направляется на шины I2S0 и I2S1 телевизионных выходов CVBS и HDMI.
Исследование /proc/asound
Но это все теория, а как используется чип H313 в приставке X96Q PRO? Знакомство с реализацией подсистемы ALSA я начал с изучения структуры каталогов /proc/asound. Вот как она выглядит:
Код:
/proc/asound/
card0 <- audiocodec
pcm0c,pcm0p — SUNXI-CODEC sun50iw9-codec-0
card1 <- sndspdif
pcm0c,pcm0p — SUNXI-SPDIF spdif-hifi-0
card2 <- sndahub
pcm0c,pcm0p — Media Stream sunxi-ahub-aif1-0
pcm1c,pcm1p — System Stream sunxi-ahub-aif2-1
pcm2c,pcm2p — Accompany Stream sunxi-ahub-aif3-2
card3 <- sndhdmi
pcm0p — SUNXI-HDMIAUDIO audiohdmi-dai-0
card4 <- sndaudio0
pcm0c,pcm0p — SUNXI-AUDIO snd-soc-dummy-dai-0
Интерес для нас представляют «карты» (в терминологии ALSA) 0, 2 и 3, которые изображают устройства Audio Codec, Audio HUB и выход HDMI соответственно. Каталоги pcmNc и pcmNp соответствуют «устройствам» (в терминологии ALSA) захвата (capture) и воспроизведения (playback) звука соответственно.
Когда приставка воспроизводит звук, информация о состоянии активных устройств появляется в файлах status, sw_params и hw_params их подкаталогов. Вот какие устройства были задействованы при разных настройках звука в Android:
Настройка/Устройства ALSA | card0/pcm0p | card2/pcm0p | card3/pcm0p |
---|---|---|---|
AUDIO_CODEC + AUDIO_HDMI | + | + | + |
AUDIO_HDMI | - | + | + |
AUDIO_HDMI + Passthrough | - | + | + |
Код:
access: RW_INTERLEAVED
format: S16_LE
subformat: STD
channels: 2
rate: 44100 (44100/1)
period_size: 960
buffer_size: 1920
Терминология ALSA
Для понимания содержимого файлов hw_params надо усвоить некоторые Для просмотра ссылки Войди- Семпл (sample) — это данные об отдельном измерении звука, например, отклонении мембраны микрофона или динамика от равновесного состояния. В режиме PCM16 они представлены 16 битами или двумя байтами.
- Фрейм (frame) — это набор семплов, достаточный для описания звуковой системы в определенный момент времени. Состояние стереосистемы определяется семплами левого и правого каналов, в режиме PCM16 это уже четыре байта. При частоте дискретизации 44100 Гц скорость потока звуковых данных для стереосистемы составляет 44100 Гц × 4 байта = 176 400 байт/с.
- Период (period) — это порция данных, которую ALSA загружает в аппаратный буфер звукового устройства. В нашем примере используется период размером period_size 960 фреймов или 3840 байт, которые будут воспроизведены примерно за 21,8 мс. Размер буфера buffer_size позволяет хранить два периода: один воспроизводится, другой готовится. Поэтому за 43,6 мс будут сгенерированы два аппаратных прерывания для подгрузки данных.
Применение утилиты tinymix
Информацию другого рода об уровне ALSA можно получить с помощью утилиты tinymix. Например, следующие команды выводят сведения об «элементах управления» компонентов ALSA и их возможных значениях:
Код:
tinymix -D 0 -a > tinymix_0.txt
tinymix -D 1 -a > tinymix_1.txt
...
tinymix -D 4 -a > tinymix_4.txt
Вот что получилось в файле tinymix_0.txt:
Код:
Mixer name: 'audiocodec'
Number of controls: 16
ctl type num name value
range/values
0 ENUM 1 codec hub mode null >hub_disable hub_enable
1 INT 1 digital volume 0 (dsrange 0->63)
2 INT 1 LINEIN to output mixer gain control 3 (dsrange 0->7)
3 INT 1 FMIN to output mixer gain control 3 (dsrange 0->7)
4 INT 1 LINEOUT volume 31 (dsrange 0->31)
5 BOOL 1 LINEOUT Switch On
6 BOOL 1 Left Output Mixer DACL Switch On
7 BOOL 1 Left Output Mixer DACR Switch Off
8 BOOL 1 Left Output Mixer FMINL Switch Off
9 BOOL 1 Left Output Mixer LINEINL Switch Off
10 BOOL 1 Right Output Mixer DACL Switch Off
11 BOOL 1 Right Output Mixer DACR Switch On
12 BOOL 1 Right Output Mixer FMINR Switch Off
13 BOOL 1 Right Output Mixer LINEINR Switch Off
14 ENUM 1 Left LINEOUT Mux >LOMixer LROMixer
15 ENUM 1 Right LINEOUT Mux >ROMixer LROMixer
Здесь перечислены элементы управления карты Audio Codec, представленные шестнадцатью переменными. Их значения можно изменять так:
Код:
tinymix -D 0 'digital volume' 63
tinymix -D 0 'LINEOUT Switch' 0
tinymix -D 0 'Left LINEOUT Mux' LROMixer
Для установки переменных типа BOOL надо использовать цифры 1 (Вкл/On) или 0 (Откл/Off). Переменные типа INT задаются числом из допустимого диапазона dsrange, а переменные типа ENUM — указанием конкретного значения из набора возможных.
www
Я извлек двоичный файл tinymix из архива Для просмотра ссылки ВойдиЧтобы исключить возможные помехи для воспроизводимого звука, я попробовал уменьшить до нуля уровень сигналов от линейного входа «LINEIN to output mixer gain control» и FM-радио «FMIN to output mixer gain control». Это не повлияло на качество звука, так же, как и увеличение громкости цифрового звука (digital volume) не подействовало на звук в режиме HDMI. А отключение линейного выхода LINEOUT Switch приводило к остановке воспроизведения медиапотока. Видимо, драйвер информировал приложение медиапроигрывателя о возникновении проблемы.
Еще большее поле для настроек имеет карта 2 — Audio HUB:
Код:
Mixer name: 'sndahub'
Number of controls: 26
ctl type num name value
range/values
0 ENUM 1 ahub audio format Function null >pcm AC3 MPEG1 MP3 MPEG2 AAC DTS ATRAC ONE_BIT_AUDIO DOLBY_DIGITAL_PLUS DTS_HD MAT WMAPRO
1 BOOL 1 I2S0IN Switch Off
2 BOOL 1 I2S0OUT Switch Off
3 BOOL 1 I2S1IN Switch Off
4 BOOL 1 I2S1OUT Switch On
5 BOOL 1 I2S2IN Switch Off
6 BOOL 1 I2S2OUT Switch On
7 BOOL 1 I2S3IN Switch Off
8 BOOL 1 I2S3OUT Switch Off
9 BOOL 1 DAM0IN Switch Off
10 BOOL 1 DAM1IN Switch Off
11 BOOL 1 DAM0OUT Switch Off
12 BOOL 1 DAM1OUT Switch Off
13 ENUM 1 APBIF0 Src Select >NONE APBIF_TXDIF0 APBIF_TXDIF1 APBIF_TXDIF2 I2S0_TXDIF I2S1_TXDIF I2S2_TXDIF I2S3_TXDIF DAM0_TXDIF DAM1_TXDIF
14 ENUM 1 APBIF1 Src Select >NONE APBIF_TXDIF0 APBIF_TXDIF1 APBIF_TXDIF2 I2S0_TXDIF I2S1_TXDIF I2S2_TXDIF I2S3_TXDIF DAM0_TXDIF DAM1_TXDIF
15 ENUM 1 APBIF2 Src Select >NONE APBIF_TXDIF0 APBIF_TXDIF1 APBIF_TXDIF2 I2S0_TXDIF I2S1_TXDIF I2S2_TXDIF I2S3_TXDIF DAM0_TXDIF DAM1_TXDIF
16 ENUM 1 I2S0 Src Select >NONE APBIF_TXDIF0 APBIF_TXDIF1 APBIF_TXDIF2 I2S0_TXDIF I2S1_TXDIF I2S2_TXDIF I2S3_TXDIF DAM0_TXDIF DAM1_TXDIF
17 ENUM 1 I2S1 Src Select NONE >APBIF_TXDIF0 APBIF_TXDIF1 APBIF_TXDIF2 I2S0_TXDIF I2S1_TXDIF I2S2_TXDIF I2S3_TXDIF DAM0_TXDIF DAM1_TXDIF
18 ENUM 1 I2S2 Src Select NONE APBIF_TXDIF0 APBIF_TXDIF1 >APBIF_TXDIF2 I2S0_TXDIF I2S1_TXDIF I2S2_TXDIF I2S3_TXDIF DAM0_TXDIF DAM1_TXDIF
19 ENUM 1 I2S3 Src Select >NONE APBIF_TXDIF0 APBIF_TXDIF1 APBIF_TXDIF2 I2S0_TXDIF I2S1_TXDIF I2S2_TXDIF I2S3_TXDIF DAM0_TXDIF DAM1_TXDIF
20 ENUM 1 DAM0Chan0 Src Select >NONE APBIF_TXDIF0 APBIF_TXDIF1 APBIF_TXDIF2 I2S0_TXDIF I2S1_TXDIF I2S2_TXDIF I2S3_TXDIF DAM0_TXDIF DAM1_TXDIF
21 ENUM 1 DAM0Chan1 Src Select >NONE APBIF_TXDIF0 APBIF_TXDIF1 APBIF_TXDIF2 I2S0_TXDIF I2S1_TXDIF I2S2_TXDIF I2S3_TXDIF DAM0_TXDIF DAM1_TXDIF
22 ENUM 1 DAM0Chan2 Src Select >NONE APBIF_TXDIF0 APBIF_TXDIF1 APBIF_TXDIF2 I2S0_TXDIF I2S1_TXDIF I2S2_TXDIF I2S3_TXDIF DAM0_TXDIF DAM1_TXDIF
23 ENUM 1 DAM1Chan0 Src Select >NONE APBIF_TXDIF0 APBIF_TXDIF1 APBIF_TXDIF2 I2S0_TXDIF I2S1_TXDIF I2S2_TXDIF I2S3_TXDIF DAM0_TXDIF DAM1_TXDIF
24 ENUM 1 DAM1Chan1 Src Select >NONE APBIF_TXDIF0 APBIF_TXDIF1 APBIF_TXDIF2 I2S0_TXDIF I2S1_TXDIF I2S2_TXDIF I2S3_TXDIF DAM0_TXDIF DAM1_TXDIF
25 ENUM 1 DAM1Chan2 Src Select >NONE APBIF_TXDIF0 APBIF_TXDIF1 APBIF_TXDIF2 I2S0_TXDIF I2S1_TXDIF I2S2_TXDIF I2S3_TXDIF DAM0_TXDIF DAM1_TXDIF
Управление аудиоконцентратором
Обнаружив в названиях переменных знакомые по даташиту аббревиатуры аудиоконцентратора, я решил построить реальную схему работы ТВ‑приставки. По состояниям элементов управления видно, что:- активными являются выходы I2S1OUT и I2S2OUT;
- источники данных для I2S1 и I2S2 — это каналы APBIF_TXFDIF0 и APBIF_TXDIF2 соответственно.
tinymix -D 2 'I2S2OUT Switch' 0
Звук на телевизоре по‑прежнему есть, как и те артефакты, на борьбу с которыми направлены наши усилия.
tinymix -D 2 'I2S1OUT Switch' 0
Звук пропал, и воспроизведение в приложении медиаплеера остановилось. Через несколько секунд звук появился снова, а состояние выхода из Off автоматически переключилось на On. Вероятно, драйвер обнаружил проблему и самостоятельно ее исправил. Таким образом, можно с большой уверенностью заявить, что выход I2S1 соответствует HDMI.
А к чему привязан I2S2? Может быть, к CVBS? Я попробовал направить источник APBIF_TXDIF2 на I2S0:
tinymix -D 2 'I2S1 Src Select' APBIF_TXDIF2
Звук снова пропал, как будто выход отключили. Получается, что из APBIF_TXDIF2 не поступает никаких аудиоданных, поэтому как его назначение, так и назначение I2S2 остаются под вопросом. Когда воспроизведение автоматически восстанавливается, в журнал logcat записываются такие строки:
Код:
D audio_hw_primary: enable_ahub_hdmi_out
D audio_hw_primary: enable_ahub_bt_out

Как еще можно попробовать устранить проблемы со звуком? Изменение «ahub audio format Function» с pcm на DTS, DOLBY_DIGITAL_PLUS и даже null не возымело никакого эффекта. Тогда я решил напоследок переконфигурировать Audio HUB так, чтобы звуковой поток проходил через микшер DAM0. Это приблизило бы работу звуковой подсистемы к эталонному примеру из даташита.

Для реализации своего замысла я выполнил следующие команды:
Код:
tinymix -D 2 'I2S2OUT Switch' 0
tinymix -D 2 'I2S2 Src Select' NONE
tinymix -D 2 'DAM0Chan0 Src Select' APBIF_TXDIF0
tinymix -D 2 'DAM0IN Switch' 1
tinymix -D 2 'DAM0OUT Switch' 1
tinymix -D 2 'I2S1 Src Select' DAM0_TXDIF
Звук по новой схеме заработал как ни в чем ни бывало, только потрескивания и пощелкивания из него не исчезли. Нужны какие‑то новые идеи.
Поиск свежих идей
Я временно перенес поиски решения в интернет и наткнулся на Для просмотра ссылки ВойдиУчастник, инициировавший дискуссию, отмечает зависимость качества воспроизводимого звука от комбинации параметров period_size/buffer_size, которыми инициализируется устройство ALSA. При некоторых значениях наблюдаются дефекты, которые автор сообщения назвал «заиканием» (shuttering).
В ответах другие пользователи сообщают, что проблема может заключаться в выполнении принудительного внутреннего преобразования частоты дискретизации из 48000 Гц к 44100 Гц при прохождении аудиопотока по элементам звуковой подсистемы.
Это обсуждение побудило меня продолжить поиски в направлении параметров цифрового звука. Я вспомнил о том, что в audio_policy_configuration.xml везде прописаны значения 48000, а все аудиопотоки воспроизводятся с частотой дискретизации 44100. Но, оглядываясь назад, я не смог припомнить никаких возможностей каким‑то образом повлиять на эту ситуацию. Кроме того, я тестировал воспроизведение медиаданных, которые были закодированы с разными частотами дискретизации: 16000, 22050, 24000, 32000, 44100 и 48000, — и все они имели дефекты звучания.
В каталоге /vendor/etc/ нашелся файл audio_platform_info.xml, содержащий параметры для инициализации звуковых устройств:
Код:
...
<!-- main output profile -->
<platform_devices_profile>
<platform_devices devices="OUT_SPK|OUT_EAR|OUT_HP|OUT_SPK_AND_HP|OUT_DULSPK|OUT_DULSPK_HP"/>
<snd_card_config
type="frontend" card_name="audiocodec" device="0"
channels="2" rate="48000" period_size="1024" period_count="2"
/>
</platform_devices_profile>
...
<!-- hdmi -->
<platform_devices_profile>
<platform_devices devices="OUT_HDMI"/>
<snd_card_config
type="frontend" card_name="sndhdmi" device="0"
channels="2" rate="48000" period_size="512" period_count="2"
/>
</platform_devices_profile>
...
Однако, во‑первых, я не смог обнаружить никаких признаков использования этого файла операционной системой приставки, а, во‑вторых, в нем мы снова видим частоту дискретизации 48000.
Наверное, пришло время вступить в самую темную область звуковой подсистемы Android и заняться изучением библиотеки промежуточного слоя Audio HAL. Почему я так мрачно высказываюсь об этой части звуковой подсистемы? Потому что остальные ее компоненты неплохо изучены и документированы, а вот HAL — это прерогатива производителя конкретного устройства, которая обычно сопровождается всякими словами типа сonfidential, и мне бы очень не хотелось во все это погружаться. Но по всем законам сказочного жанра, чему быть, того не миновать.
Уровень слоя абстракции Audio HAL
Исследование файла audio.primary.cupid.so
Что нам известно о промежуточном слое Audio HAL и вообще, где его искать? Для просмотра ссылки ВойдиПосмотрим, какие аудиоустройства обслуживает библиотека HAL audio.primary.cupid.so:
Код:
strings audio.primary.cupid.so | grep AUDIO_DEVICE_
start_output_stream, AUDIO_DEVICE_OUT_SPEAKER
start_output_stream AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET
start_output_stream AUDIO_DEVICE_OUT_AUX_DIGITAL
Аналогичным образом мы можем посмотреть, какие системные настройки ей известны:
Код:
strings audio.primary.cupid.so | grep vendor
vendor.audio.input.active
vendor/etc/ac100_paths.xml
vendor.audio.output.active
vendor.mediasw.sft.rawdata
Присваивание свойству vendor.mediasw.sft.rawdata значения с помощью команды setprop на воспроизведение звука не оказывает влияния, потому что значение как этого свойства, так и свойства vendor.audio.output.active переустанавливаются в соответствии с настройками звука в Android.
Настройки в ac100_paths.xml
Интересен случайно попавший в выборку файл /vendor/etc/ac100_paths.xml. Как оказалось, он используется библиотекой вместо audio_mixer_paths.xml. В файле ac100_paths.xml содержится описание параметров для устройств ALSA. Эти параметры выставляются для разных режимов работы аудиоподсистемы.Файл громоздкий, потому что учитывает возможность подключения и отключения разных устройств ввода и вывода звука, таких как USB-микрофоны и беспроводные гарнитуры. При обычном использовании приставки записи об установке режимов работы в журнале logcat выглядят так:
Код:
D audio_route: Apply path: media-speaker-headphones
D audio_route: Apply path: media-headset-mic
Режиму media-speaker-headphones соответствует вот эта часть конфига:
Код:
<mixer>
...
<path name="media-speaker-headphones">
<path name="dac-mixer" />
<ctl name="Left Output Mixer DACL Switch" value="1" />
<ctl name="Left Output Mixer DACR Switch" value="0" />
<ctl name="Right Output Mixer DACL Switch" value="0" />
<ctl name="Right Output Mixer DACR Switch" value="1" />
<ctl name="LINEOUT volume" value="31" />
<ctl name="LINEOUT Switch" value="1" />
</path>
...
</mixer>
Сопоставив этот фрагмент со схемой звуковой подсистемы Allwinner H616 и режимами работы компонента Audio Codec, который мы исследовали на уровне ALSA, ты без особого труда увидишь, как применяются настройки из этого файла. Но мы по‑прежнему не знаем, где устанавливаются параметры цифрового звука.
Чип X-Powers
У тебя не возник вопрос, что означает «AC100» в имени файла? Я тоже этим заинтересовался и нашел в сети Для просмотра ссылки ВойдиОказалось, она представляет собой высокоинтегрированную аудиоподсистему производства X-Powers и предназначена для использования в Wi-Fi колонках, планшетах и смартфонах. Ее описание перекликается с тем, что мы видели в руководстве по Allwinner H616, но принцип работы и управляющие параметры здесь рассмотрены более подробно.
Из Для просмотра ссылки Войди
Что еще мы можем предпринять? Давай посмотрим, что записывается в журнал logcat при старте воспроизведения медиапотока с частотой дискретизации 32000 Гц приложением Для просмотра ссылки Войди
Код:
V mpv : [af:v] [out] 32000Hz stereo 2ch s16
V mpv : [cplayer:v] audio ready
V mpv : [cplayer:v] starting audio playback
V mpv : [cplayer:v] playback restart complete @ 0.000000, audio=playing, video=eof
D audio_hw_primary: start_output_stream out->format : 0x00000000
V audio_hw_primary: start_output_stream, line: 1654
D audio_route: Apply path: media-speaker-headphones
D audio_route: Apply path: media-headset-mic
V audio_hw_primary: use AUDIO_CODEC to playback audio
D audio_hw_primary: open card=0, port=0
D audio_hw_primary: config:2 44100 960 2 0 5760 1 0
D audio_hw_primary: set_raw_flag(card=0, raw_flag=1)
D audio_hw_primary: set_raw_flag(card=2, raw_flag=1)
D audio_hw_primary: set_raw_flag control_name = ahub audio format Function)
V audio_hw_primary: do not use out resampler
V audio_hw_primary: use AUDIO_HDMI to playback audio
D audio_hw_primary: open card=3, port=0
D audio_hw_primary: config:2 44100 960 2 0 5760 1 0
D audio_hw_primary: set_raw_flag(card=3, raw_flag=1)
D audio_hw_primary: set_raw_flag control_name = hdmi audio format Function)
D audio_hw_primary: set_raw_flag(card=2, raw_flag=1)
D audio_hw_primary: set_raw_flag control_name = ahub audio format Function)
V audio_hw_primary: do not use out resampler
D audio_hw_primary: enable_ahub_hdmi_out
D audio_hw_primary: enable_ahub_bt_out
Строки с пометкой audio_hw_primary записаны библиотекой Audio HAL. Из этого отрывка видно, что, несмотря на частоту воспроизводимых аудиоданных 32000 Гц и значения 48000 Гц во всех настройках, библиотека HAL инициализирует устройства AUDIO_CODEC и AUDIO_HDMI значением 44100 Гц. Я решительно не понимаю, почему так происходит, поэтому решил исследовать код библиотеки HAL. Исходников мне найти не удалось, пришлось воспользоваться дизассемблером.
Дизассемблирование библиотеки HAL
Для дизассемблирования понадобится утилита objdump, понимающая машинный код процессоров ARM. Поэтому в Ubuntu установим пакет binutils-arm-linux-gnueabihf:sudo apt install binutils-arm-linux-gnueabihf
Дизассемблируем библиотеку audio.primary.cupid.so:
arm-linux-gnueabihf-objdump -D audio.primary.cupid.so > audio.primary.cupid.so.dasm
Теперь выполним поиск по значению 44100 в полученном листинге:
grep '44100' audio.primary.cupid.so.dasm
Ответом стали 18 строк с командами загрузки числа 44100 в разные регистры:
Код:
51cc: f64a 4044 movw r0, #44100 ; 0xac44
52cc: f64a 4344 movweq r3, #44100 ; 0xac44
6356: f64a 4144 movw r1, #44100 ; 0xac44
63b4: f64a 4044 movweq r0, #44100 ; 0xac44
6d06: f64a 4244 movw r2, #44100 ; 0xac44
7196: f64a 4044 movw r0, #44100 ; 0xac44
72d0: f64a 4044 movw r0, #44100 ; 0xac44
74b8: f64a 4144 movweq r1, #44100 ; 0xac44
74de: f64a 4044 movw r0, #44100 ; 0xac44
7508: f64a 4344 movw r3, #44100 ; 0xac44
7522: f64a 4344 movw r3, #44100 ; 0xac44
762e: f64a 4044 movw r0, #44100 ; 0xac44
7724: f64a 4144 movw r1, #44100 ; 0xac44
772c: f64a 4244 movwne r2, #44100 ; 0xac44
798a: f64a 4044 movw r0, #44100 ; 0xac44
79a6: f64a 4044 movw r0, #44100 ; 0xac44
79c0: f64a 4344 movw r3, #44100 ; 0xac44
7a9e: f64a 4344 movw r3, #44100 ; 0xac44
Наиболее примечательный участок кода выглядит так:
Код:
7186: f44f 7070 mov.w r0, #960 ; 0x3c0
718a: f8c8 00a0 str.w r0, [r8, #160] ; 0xa0
718e: f44f 60f0 mov.w r0, #1920 ; 0x780
7192: f8c8 0090 str.w r0, [r8, #144] ; 0x90
7196: f64a 4044 movw r0, #44100 ; 0xac44
719a: f8c8 0080 str.w r0, [r8, #128] ; 0x80
Похоже, что он записывает десятичные числа 960, 1920 и 44100 в структуру, на которую указывает содержимое регистра r8. А где нам встречались эти значения? В файле, отражающем настройки звуковой подсистемы драйверов Linux /proc/asound/card3/pcm0p/sub0/hw_params. Видимо, именно здесь происходит подготовка этой подсистемы к воспроизведению.
Какие выводы из всего этого можно сделать? Во‑первых, библиотека инициализирует звуковую подсистему фиксированными значениями, представленными в ее коде, а не полученными из конфигурационных файлов или настроек операционной системы. Во‑вторых, эти значения вшиты непосредственно в машинный код команд, а не хранятся отдельно в сегменте данных. Последний факт усложняет задачу изменения этих значений.
Исправление библиотеки HAL
Как же исправить двоичный файл библиотеки HAL, чтобы реализованные в нем функции ориентировали компоненты ALSA на работу с частотой дискретизации 48000 Гц? Ассемблера для архитектуры ARM я не знаю, поэтому не могу проанализировать логику работы соответствующих участков библиотеки, чтобы локализовать конкретные команды, требующие изменения.Кроме того, у меня было настроение Бармалея, который в ответ на замечание главного героя кинофильма «Айболит-66» о том, что нехорошо обманывать маленькую обезьянку, нетерпеливо возразил: «Я не могу ждать, пока она вырастет!». Поэтому я принял авантюрное решение: заменить значение 44100 на 48000 во всех командах mov, где оно встречается.
С одной стороны, вероятно, существуют участки кода, в которых значение 44100 используется не для инициализации звуковой подсистемы, а, например, для сравнения с параметром предоставленного к воспроизведению аудиопотока. С другой стороны, если раньше при инициализации звуковой подсистемы всегда использовалось значение 44100, то сейчас вместо него будет стоять 48000.
Я бы ни за что не стал общим чохом вслепую менять в банковском приложении 100 на 150, если бы вышел закон о новой пороговой сумме каких‑то операций. Потому что 100 может быть как величиной этой суммы, так и, например, коэффициентом перевода рублей в копейки. Да и финансовая сфера — совсем не та область, в которой допустимы подобные легкомысленные решения.
Но 44100 — слишком уж характерная величина, которую сложно с чем‑нибудь перепутать. Кроме того, маловероятно, что частота дискретизации явно влияет на выделение буферов (иначе такое грубое вмешательство могло бы привести к утечкам памяти). Ну и в самом худшем случае ущерб ограничится неисправной телевизионной приставкой — потеря не так велика.
В этот момент последовательный рассудительный инженер уходит со сцены, и в лучах софитов на ней появляется хакер!
Что менять, мы уже видим. Теперь надо выяснить, на что это менять. Для этого посмотрим, как выглядят команды загрузки в регистры значения 48000:
Код:
grep '#48000' audio.primary.cupid.so.dasm
634e: f64b 3180 movw r1, #48000 ; 0xbb80
7002: f64b 3380 movw r3, #48000 ; 0xbb80
7028: f64b 3380 movw r3, #48000 ; 0xbb80
7734: f64b 3180 movw r1, #48000 ; 0xbb80
773c: f64b 3280 movwne r2, #48000 ; 0xbb80
Сопоставив шестнадцатеричные машинные коды с мнемоническими записями команд, можно сделать вывод, что для замены команд movw r?, #44100 на movw r?, #48000 надо выполнить такие подстановки:
- 4BF6 8030 вместо 4AF6 4440,
- 4BF6 8031 вместо 4AF6 4441,
- 4BF6 8032 вместо 4AF6 4442,
- 4BF6 8033 вместо 4AF6 4443.
Полная таблица исправлений для файла audio.primary.cupid.so с контрольной суммой MD5 daa172745b41d0db7e7e03bdaae4796b будет выглядеть следующим образом:
Код:
0x51CC: 4A F6 44 40 -> 4B F6 80 30
0x52CC: 4A F6 44 43 -> 4B F6 80 33
0x6356: 4A F6 44 41 -> 4B F6 80 31
0x63B4: 4A F6 44 40 -> 4B F6 80 30
0x6D06: 4A F6 44 42 -> 4B F6 80 32
0x7196: 4A F6 44 40 -> 4B F6 80 30
0x72D0: 4A F6 44 40 -> 4B F6 80 30
0x74B8: 4A F6 44 41 -> 4B F6 80 31
0x74DE: 4A F6 44 40 -> 4B F6 80 30
0x7508: 4A F6 44 43 -> 4B F6 80 33
0x7522: 4A F6 44 43 -> 4B F6 80 33
0x762E: 4A F6 44 40 -> 4B F6 80 30
0x7724: 4A F6 44 41 -> 4B F6 80 31
0x772C: 4A F6 44 42 -> 4B F6 80 32
0x798A: 4A F6 44 40 -> 4B F6 80 30
0x79A6: 4A F6 44 40 -> 4B F6 80 30
0x79C0: 4A F6 44 43 -> 4B F6 80 33
0x7A9E: 4A F6 44 43 -> 4B F6 80 33
0xC008: 44 AC -> 80 BB
0xC038: 44 AC -> 80 BB
0xC068: 44 AC -> 80 BB
Контрольная сумма MD5 исправленного файла — c58c080cca142114dcd840d0d94f66a7.
Испытание исправленной библиотеки
Ставлю исправленную библиотеку на телевизионную приставку и перезагружаю ее. Она не зависла на этапе загрузки — это уже хорошо! Теперь запускаю воспроизведение тестового сигнала... и слышу прекрасный чистый тон телефонного гудка! В это трудно проверить, но авантюра удалась!Звук приставки стал если не идеальным, то вполне приемлемым. Из аудиосопровождения телевизионных программ исчезли «царапающие» щелчки и запинания, постепенно ушло и нервное напряжение, связанное с ожиданием проявления очередного дефекта.
Проверяю параметры аудиопотока с помощью команды dumpsys media.audio_flinger:
Output thread 0xeb193880, name AudioOut_D, tid 2114, type 0 (MIXER):
Код:
I/O handle: 13
Standby: no
Sample rate: 48000 Hz
HAL frame count: 896
HAL format: 0x1 (AUDIO_FORMAT_PCM_16_BIT)
HAL buffer size: 3584 bytes
Channel count: 2
Channel mask: 0x00000003 (front-left, front-right)
Processing format: 0x5 (AUDIO_FORMAT_PCM_FLOAT)
...
1 Tracks of which 1 are active
Type Id Active Client Session Port Id S Flags Format Chn mask SRate ST Usg CT G db L dB R dB VS dB Server FrmCnt FrmRdy F Underruns Flushed Latency
56 yes 6665 25 9 A 0x000 00000005 00000001 44100 3 1 2 0 0 0 0 00081F97 6372 3197 A 0 0 unavail
Она показывает, что исходные аудиоданные с частотой дискретизации 44100 Гц преобразуются для передачи на аудиовыход с частотой дискретизации 48000 Гц. Но неожиданно для меня изменились значения HAL frame count: 896 вместо 960 и HAL buffer size: 3584 вместо 3840. А что произошло на уровне ALSA? Содержимое файла /proc/asound/card3/pcm0p/sub0/hw_params подтверждает изменение частоты дискретизации выходного потока, при этом величины period_size и buffer_size остались прежними:
Код:
access: RW_INTERLEAVED
format: S16_LE
subformat: STD
channels: 2
rate: 48000 (48000/1)
period_size: 960
buffer_size: 1920
Может быть, изменившееся на уровне аудиосервера значение HAL frame count привело к тому, что операции заполнения аппаратного и программного буферов аудиоданных разнеслись по времени, в результате чего устранились накладки, которые происходили раньше?
Как бы то ни было, многочасовая эксплуатация телевизионной приставки с измененной библиотекой Audio HAL не выявила никаких побочных эффектов. Разнообразные медиапотоки воспроизводились с хорошим качеством звука и не вызывали сбоев.
В качестве основного устройства воспроизведения можно вернуть AUDIO_DEVICE_OUT_SPEAKER, если отсутствие возможности регулировки уровня громкости для AUDIO_DEVICE_OUT_HDMI вызывает дискомфорт — это не испортит достигнутого результата.
Заключение
Мы проделали долгий путь, в ходе которого познакомились со всеми уровнями звуковой подсистемы Android 10, изменили устройство вывода звука с AUDIO_DEVICE_OUT_SPEAKER на AUDIO_DEVICE_OUT_HDMI, выставили необходимый уровень громкости и, в конце концов, исправили библиотеку Audio HAL так, чтобы аудиопоток выводился с частотой дискретизации 48000 Гц, а не 44100 Гц.Последняя модификация устранила проблему низкого качества звука на телевизоре, к которому подключена приставка X96Q PRO. Способ, которым были выполнены исправления, нельзя назвать бесспорным. Но он позволил быстро достичь приемлемого результата, а последовательный инженерный подход при отсутствии исходников потребовал бы слишком больших усилий и времени.
Ради интереса я проверил состав дистрибутивов Android 10 для некоторых других устройств, построенных на платформе Allwinner H313/H616. Оказалось, что файл библиотеки Audio HAL от телевизионной приставки X96Q PRO ничем не отличается от тех, которые поставляются в дистрибутивах для Orange Pi Zero2 и Tanix TX6S.
Отсутствие массовых сообщений о проблеме с воспроизведением звука на подключенных к таким устройствам телевизорах может говорить о том, что описанные в начале статьи звуковые дефекты стали результатом несовместимости аудиоданных, которые выдает приставка, с конкретной моделью телевизора. Но это не отменяет того факта, что параметры, прописанные в конфигах, не соответствуют фактически устанавливаемым при воспроизведении звука. Не помешала бы и возможность управления этими параметрами через графический интерфейс Android.
Печально, но факт: причина проблем крылась в софте приставки. Похоже, ему в сфере массового производства уделяют все меньше внимания. В результате даже качественное «железо» не всегда может раскрыть свои лучшие стороны.