Беспроводные технологии передачи звука на базе Bluetooth: что же лучше?

uz_por_7qlk9snfb7v_jg2x5iae.jpeg
С развитием технологий так привычные всем «ламповые» аналоговые наушники уходят в историю — их всё больше вытесняют беспроводные собратья на базе Bluetooth.

Современные смартфоны лишаются привычного разъёма в угоду влаго- и пылезащищённости.

Разработчики выпускают всё новые версии протокола Bluetooth и всё новые версии кодеков, обещая «быстрее, выше, сильнее» — меньшие задержки в воспроизведении и лучшее качество.

Настолько ли всё хорошо? Давайте посмотрим.

Введение


Я не буду углубляться в техническую реализацию протоколов, а также в скучные спецификации. Уважаемый ValdikSS, который в большой степени выступил вдохновителем и даже научным консультантом в этой статье, готовит исчерпывающий материал касательно кодеков — и там всё будет изложено куда более подробно и технически верно.

Рассказать я хочу в большей степени о личном опыте. Ну и немножко занимательной (скучной?) практики.

Полтора года назад я загорелся идеей aptX. Да, я прочитал массу обзоров наподобие этого и поверил во все эти технические навороты и возможности. Родился ребёнок — и очень хотелось ночью с женой смотреть передачи в наушниках, не создавая шума и не будя никого в доме.

Во что же это вылилось?

Качество


Начнём с цифр и фактов (привет, Википедия!)

SBC — старый добрый кодек, обязательный при соблюдении стандарта A2DP. Кодек — результат работы Франса де Бонта (F. de Bont, M. Groenewegen and W. Oomen, «A High Quality Audio-Coding System at 128 kb/s», 98th AES Convention, Febr. 25–28, 1995) и использования алгоритмов, описанных в патенте EP-0400755B1. Примечательно, что авторы патента разрешают бесплатное использование SBC только в приложении Bluetooth, тем не менее патент истёк 2 июня 2010 года. Поскольку стандарт A2DP весьма распространён, крайне трудно найти наушники или колонки, в которых не было бы поддержки SBC.
Кодек обеспечивает частоту дискретизации 16, 32, 44.1, 48 кГц со скоростью потока 10–1500 кбит/с. Да, Вы не ослышались. До 1500 кбит/с. В кодеке просто нет лимита по битрейту. Но об этом — позже.

Кодек aptX разработан в 1988 году в Королевском Университете в Белфасте. Да, до Bluetooth ещё было ещё порядка десятка лет, так что кодек использовался в профессиональной звуковой аппаратуре. В настоящий момент права принадлежат компании Qualcomm, а потому использование требует лицензирования и лицензионных отчислений. По состоянию на 2014 год стоимость приблизительно такова: единовременный платёж $6000 и ≈$1 за каждое выпущенное устройство для партий до 10000 устройств. По этой причине многие устройства с чипами Snapdragon 835, 845, 821, 820, 810, 805, 801, 800, 650, 615, 410 вполне возможно и поддерживают aptX, но он там не активирован, поскольку лицензия не была приобретена. Об этом — также ниже.
При разрядности 16 бит и частоте дискретизации 48 кГц кодек может обеспечивать скорость потока 384 кбит/с (dual channel).
Список продуктов, официально поддерживающих aptX. На Aliexpress можно найти очень много неведомых систем с поддержкой aptX, но будьте готовы к тому, что на поверку там окажется тот же старый добрый SBC — и не более.

aptX HD — тот же самый кодек, но с другим профилем кодирования, имеет скорость потока 576 кбит/с, поддержку частоты дискретизации до 48 кГц и разрядность до 24 бит. Некоторые называют этот кодек aptX Loseless –, но это полный бред хотя бы потому, что в настоящий момент невозможно достигнуть значения потока, который мог бы переносить loseless-данные. Особым преимуществом этого кодека регулируемая задержка кодирования, которая может снижаться до 1 мс при частоте дискретизации 48 кГц. Также кодек крайне выгоден с позиции загрузки процессора, в чём выражается преимущество по сравнению с МР3 и ААС.
Список продуктов, официально поддерживающих aptX HD. Он достаточно невелик.

aptX Low latency (или LL) — специальная версия кодека, позволяющая снизить время задержки звука до менее 40 мс. Список продуктов, официально поддерживающих aptX LL.

image
Вот она. Именно эта картинка в своё время купила меня с потрохами. Задержки! Ведь кому захочется слышать звук взрыва в каком-то боевике, крик монстра в ужастике или рёв толпы на футбольном матче, когда всё уже закончилось?

Но так ли всё это на самом деле?

Увы, нет.

Как и в любом маркетинговом материале, цифры притянуты за уши. Задержка во многом зависит от буферизации в системе и реализации кодека. Так, задержка с SBC вполне может быть на уровне менее 40 мс, что принимая во внимание стандарты телевизионного вещания (+40 мс… −60 мс) — вполне допустимо.

Резюмируя:

  1. Ни один кодек не может быть лучше проводной технологии, поскольку ни один кодек не позволяет достичь истинного сжатия без потерь.
  2. Самым популярным кодеком является SBC. Он же — наиболее гибок в настройках. И несмотря на то, что aptX был выпущен ранее, он не смог побить популярности SBC, видимо, из-за бесплатности последнего.
  3. Качество звука крайне зависит от реализации кодека, а также от аппаратного исполнения наушников/колонок вообще — если сама по себе колонка слабая, то качество не улучшишь ни одним кодеком. Поэтому в дальнейшем, сравнивая качество, мы будем говорить о воспроизведении одного и того же контента от одного и того же источника на одних и тех же колонках/наушниках, но с различными кодеками.

Практические и очень субъективные итоги


fjufkjmv-sxcezemnqcz3ztyue4.jpeg
Информация базируется на уже упомянутом полуторагодовом опыте эксплуатации, сравнения и привлечения сторонних слушателей.

Опыт базируется на прослушивании loseless на плеере SONY Walkman NWZ-A17, где кодек можно выбирать, а также на просмотре различных передач с выводом звука посредством Avantree Priva III.

Наушников было трое: Sennheiser PMX 60, Koss Porta Pro и Koss UR-20.

В качестве приёмников беспроводного сигнала использовались Jabra BT3030 (SBC) и Avantree Clipper Pro (aptX).

Также использовалась колонка Voombox Outdoor (SBC) и наушники с костной проводимостью Aftershokz Trekz Titanium (aptX).

Все эквалайзеры и улучшатели отключались — и это важно.

Итого:

  1. Качество проигрываемого звука при проводном подключении — всегда лучше. Это без сомнения.
  2. Разницу между SBC и aptX услышать крайне затруднительно — и только в случае некоторых типов музыки. Например, автор статьи чётко слышал разницу на соло виолончелей в классических композициях, при этом для скрипки и инструментов низких частот разница была менее уловима. На современных жанрах — поп, электронная музыка и рок — разницу не слышно. В ряде случаев субъективно казалось, что SBC лучше передаёт звук, чем aptX.
  3. Задержку между SBC и aptX можно заметить только если подключится к одному источнику и разные приёмники вставить в разные уши (ну левый канал — SBC, а правый — aptX к примеру). Задержку с картинкой увидеть практически нереально, а потому история, что aptX предназначен для динамичных сцен и контента — миф.
  4. Удивление вызвало качество звучания на довольно дешёвых и «не именитых» Voombox Outdoor. Видимо, это и есть удачная реализация SBC, о которой говорилось выше.
  5. Совершенно непонятна реализация aptX в наушниках с костной проводимостью — технология весьма специфична, а потому потери в качестве значительны из-за самой технологии. Принимая во внимание малый диапазон «дальности» работы устройства, крайне плохую реализацию сопряжения с двумя устройствами могу констатировать, что Aftershokz — компания, которая больше вкладывает в маркетинг, чем в разработку.

Не спорю, что если подключать всяческие спектральные анализаторы и прочее — можно и нужно увидеть разницу. Но человеческое ухо, а ещё хуже — среднестатистическое человеческое ухо — не спектральный прибор, а потому все эти нюансы не слышит.

Практика: исправляем то, что можно исправить


Часть 1. Включаем aptX.


Как уже было сказано, в некоторых устройствах использование aptX отключено, вероятно, во избежание патентных преследований.

Этот вопрос можно решить достаточно просто — подсунув устройству библиотеки для реализации работы кодека и прописав возможность работы с этим кодеком в build.prop.

На просторах интернета имеется большое количество решений такого характера. Я взял на себя смелость объединить их в одно, при этом реализовав в виде модуля для Magisk. Да, я очень люблю этот проект и считаю, что реализация изменений в системе в виде модулей Magisk — лучше и безопасное решение с возможностью сохранения системы максимально в первозданном виде и лёгким способом отката назад.

Модуль можно скачать отсюда. Да, я знаю про гитхаб. И нет, пока у меня нет времени туда его выкладывать.

Записи в build.prop, включающие aptX, а при возможности — и aptX HD, будут эмулироваться модулем автоматически.
vglcguyqsbbyxptonmhdw-5ta3y.png

Часть 2. Повышаем битрейт SBC.


Как уже сообщалось, кодек SBC принципиально не имеет ограничений по битрейту. Однако производители обычно ставят ограничение в 342 кбит/с для моно- и 345 кбит/с для стереосигнала с целью обспечения надёжной работы со всеми типами принимающих устройств.

При этом спецификация A2DP v1.2, которая была активна с 2007 по 2015 год, предписывает всем декодирующим устройствам корректно работать с битрейтами до 320 кбит/с для моно- и 512 кбит/с в случае стереосигнала.

В новой версии спецификации ограничение по битрейту отсутствует вообще. Предполагается, что современные наушники, выпущенные после 2015 года и поддерживающие EDR, могут поддерживать битрейты до 730 кбит/с.

По факту это конечно же не так. В обширном исследовании, проведённом ValdikSS, было найдено, что практически все приёмные устройства надёжно работают со значениями битрейта 454 кбит/с, и достаточно большое количество — с битрейтом 507 кбит/с.

ValdikSS на основании своих исследований направил замечания разработчикам Lineage OS и в Google, но на данный момент никакой реакции не последовало.

Таким образом, нам остаётся только вручную внести модификации в Bluetooth-стек.

Нам потребуется IDA Pro с возможностью декомпилирования ARM, любой HEX-редактор (я использовал WinHEX) и файл bluetooth.default.so с нашего устройства. Обычно он находится по пути /system/lib/hw и реже — ещё и по пути /system/lib64/hw (безусловно нужен рут-доступ).

Итак, открываем файл bluetooth.default.so Описанные ниже операции и модификации применимы только к оригинальному стеку Android (bluedroid). Если вы видите строку «Needed Library 'com.qualcomm.qti.bluetooth_audio@1.0.so'» или подобную в IDA Pro, с большой вероятностью, эта инструкция вам не поможет.
cf3ljbjgckew4pd9tbutsy-tyaw.png

Наша первая задача — заменить Joint Stereo на Dual Channel в стандартной конфигурации.

Работать мы будем с функцией bta_av_build_src_cfg.

Чтобы отыскать эту процедуру в IDA, воспользуемся поиском по строке характерного сообщения в журнал отладки «Cant parse src cap ret = %d»:
whdkexvibrxijvj7pqb-buzb0z8.png
ea2hnhbszbtyieokldywuch2qvi.png

В итоге довольно быстро мы находим саму функцию в виде кода:
mf5__9yydhgwip0lvemgqwcwocw.png

Наша задача — подменить исходную структуру проверок
if (src_cap.ch_mode & A2D_SBC_IE_CH_MD_JOINT)
pref_cap.ch_mode = A2D_SBC_IE_CH_MD_JOINT;
else if (src_cap.ch_mode & A2D_SBC_IE_CH_MD_STEREO)
pref_cap.ch_mode = A2D_SBC_IE_CH_MD_STEREO;
else if (src_cap.ch_mode & A2D_SBC_IE_CH_MD_DUAL)
pref_cap.ch_mode = A2D_SBC_IE_CH_MD_DUAL;
else if (src_cap.ch_mode & A2D_SBC_IE_CH_MD_MONO)
pref_cap.ch_mode = A2D_SBC_IE_CH_MD_MONO;

на
if (src_cap.ch_mode & A2D_SBC_IE_CH_MD_DUAL)
pref_cap.ch_mode = A2D_SBC_IE_CH_MD_DUAL;
else if (src_cap.ch_mode & A2D_SBC_IE_CH_MD_STEREO)
pref_cap.ch_mode = A2D_SBC_IE_CH_MD_STEREO;
else if (src_cap.ch_mode & A2D_SBC_IE_CH_MD_DUAL)
pref_cap.ch_mode = A2D_SBC_IE_CH_MD_DUAL;
else if (src_cap.ch_mode & A2D_SBC_IE_CH_MD_MONO)
pref_cap.ch_mode = A2D_SBC_IE_CH_MD_MONO;

Это можно сделать несколькими способами.

Первый — подмена инструкций TST.W R0, #1 на TST.W R0, #4 и MOVS R0, #1 на MOVS R0, #4 в последовательности проверок:
6ezusojttx0gavinlppmqeroet8.png

В байт-коде это замена х01 на х04. При этом важно отметить характерные последовательности байтов, по которым можно найти этот паттерн. Не углубляясь сильно в детали, скажу, что по сути это поиск последовательности
10 20 8D F8 04 00 9D F8 0D 00 10 F0 01 0F ?? ?? 10 F0 02 0F ?? ?? 10 F0 04 0F ?? ?? 10 F0 08 0F ?? ?? 08 20 ?? ?? 01 20 ?? ?? 02 20 ?? ?? 04 20
и её замена на
?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? 04 ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? 04 ?? ?? ?? ?? ?? ?? ?? ?? ??

Однако у данного способа есть недостатки.

Ряд компиляторов меняют последовательность исполнения команд в зависимости от оптимизации. И в таком случае найти искомый паттерн не удаётся, а иногда механизм проверок в структуре вообще вносится в инлайн-код. Поэтому более надёжным является изменение константы btif_av_sbc_default_config.

Для начала — найдём её. Она в самом начале нашей функции, ведь
void bta_av_build_src_cfg (UINT8 *p_pref_cfg, UINT8 *p_src_cap)
{
tA2D_SBC_CIE src_cap;
tA2D_SBC_CIE pref_cap;
UINT8 status = 0;
/* initialize it to default SBC configuration */
A2D_BldSbcInfo(AVDT_MEDIA_AUDIO, (tA2D_SBC_CIE *) &btif_av_sbc_default_config, p_pref_cfg);
/* now try to build a preferred one */
/* parse configuration */
if ((status = A2D_ParsSbcInfo(&src_cap, p_src_cap, TRUE)) != 0)
{
APPL_TRACE_DEBUG(" Cant parse src cap ret = %d", status);

А вот и она:
xij2dtvma0evlnggxkxzqxkvdn4.png
7az936re6naesontslfi1ogkd5c.png

Видно, что сама btif_av_sbc_default_config представляет собой последовательность байт 20 01 10 04 01 35 02, при этом первый байт кодирует частоту дискретизации и может быть 10 (48 кГц) и 20 (44 кГц), а потому не специфичен. Таким образом наша задача — замена последовательности
01 10 04 01 35 02
на
04 ?? ?? ?? ?? ??

Это позволит изменить логику работы структуры аналогичным образом, но при этом оптимизации компилятора не доставят проблем.

В ряде случаев инициатором подключения выступают сами наушники или колонки. В этом случае режим определяется функцией bta_av_co_audio_init.
Функция характерна строкой «bta_av_co_audio_init: %d» и легко отыскивается в коде:
fi5okug9tbn6gvleetxo93uk9qg.png

Перечисление возможных режимов подключения выполняется в следующей команде:
switch (index)
{
case BTIF_SV_AV_AA_SBC_INDEX:
/* Set up for SBC codec for SRC*/
*p_codec_type = BTA_AV_CODEC_SBC;
/* This should not fail because we are using constants for parameters */
A2D_BldSbcInfo(AVDT_MEDIA_AUDIO, (tA2D_SBC_CIE *) &bta_av_co_sbc_caps, p_codec_info);
/* Codec is valid */
return TRUE;

при этом константа bta_av_co_sbc_caps имеет следующую структуру:
const tA2D_SBC_CIE bta_av_co_sbc_caps =
{
(A2D_SBC_IE_SAMP_FREQ_44), /* samp_freq */
(A2D_SBC_IE_CH_MD_MONO | A2D_SBC_IE_CH_MD_STEREO | A2D_SBC_IE_CH_MD_JOINT | A2D_SBC_IE_CH_MD_DUAL), /* ch_mode */
(A2D_SBC_IE_BLOCKS_16 | A2D_SBC_IE_BLOCKS_12 | A2D_SBC_IE_BLOCKS_8 | A2D_SBC_IE_BLOCKS_4), /* block_len */
(A2D_SBC_IE_SUBBAND_4 | A2D_SBC_IE_SUBBAND_8), /* num_subbands */
(A2D_SBC_IE_ALLOC_MD_L | A2D_SBC_IE_ALLOC_MD_S), /* alloc_mthd */
BTA_AV_CO_SBC_MAX_BITPOOL, /* max_bitpool */
A2D_SBC_IE_MIN_BITPOOL /* min_bitpool */
};

Константа легко находится в коде, в моём случае она равна 20 0F F0 0C 03 35 02:
kjhwoibpuk8brwa3kzvxiuxgeba.png

Обратите внимание на байт 0F — он обеспечивает возможность подключения с любым из допустимых режимов, поскольку
x0F = A2D_SBC_IE_CH_MD_MONO | A2D_SBC_IE_CH_MD_STEREO | A2D_SBC_IE_CH_MD_JOINT | A2D_SBC_IE_CH_MD_DUAL = x08 | x02 | x01 | x04

Наша задача изменить это значение таким образом:
x0F = A2D_SBC_IE_CH_MD_DUAL = x04

Следовательно, необходимо заменить
?? 0F F0 0C 03 35 02
на
?? 04 ?? ?? ?? ?? ??

Итак, мы заставили стек подключаться в режиме Dual Channel как в случае инициирования подключения устройством, так и в случае инициирования подключения принимающей сигнал стороной.

Теперь необходимо убрать ограничения в битрейте либо повысить его верхний порог.

Необходимо обработать btif_media_task_get_sbc_rate. Аналогичным образом по поиску характерной строки «non-edr a2dp sink detected, restrict rate to %d» отыскиваем функцию в коде:
kpgoj2xsq6vt8kbnexy81jy6j6s.png

Ограничение битрейта выражается в строке
UINT16 rate = DEFAULT_SBC_BITRATE (что в свою очередь равно 328 кбит/с)

В коде это так:
tfbabnrxfks3pejhzqzdegiwn04.png

Изменим это значение на 454 кбит/с — это выше стандартного и работает с подавляющим большинством приёмных устройств. Для этого заменим байты
B1 4F F4 A4 74 ?? E0
на
?? ?? ?? E3 ?? ?? ??

Также следует выполнить поиск по паттерну
E0 4F F4 A4 74 ?? E0
и заменить его на
?? ?? ?? E3 ?? ?? ??
— это требуется для ряда устройств.

Значение Е3 может быть другим в зависимости от желаемое максимального битрейта:

  • E3 — 454 кбит/с
  • F1 — 482 кбит /с
  • F3 — 486 кбит/с
  • 10 — 576 кбит/с
  • 48 — без ограничений


В целом, это определяется байт-кодом операции MOV.W R4, XXX.

На практике стоит поэкспериментировать и выбрать максимальное значение, при котором во всех Ваших принимающих устройствах происходит стабильный приём сигнала, отсутствует треск, прерывания и искажения.

На всех приёмниках в моём эксперименте (выше я их указал) таким значением было 576 кбит/с, в качестве источника сигнала выступал телефон Xiaomi Redmi 4x MIUI10 Android 7.1.

На основе описанных действий был создан дженерик-патч, который находит указанные паттерны в Bluetooth.default.so и заменяет их? включая принудительно режим Dual Channel и устанавливая лимит битрейта в 454 кбит/с. При необходимости, значение лимита может быть легко изменено на основании поиска и замены соответствующего байта — внимательный читатель сделает это без труда.

Подчёркиваю: патч работает только в случае bluedroid-стека и скорее всего не будет успешным в случае стека Fluoride и версии Android 8 и новее.

Патч можно скачать отсюда.

Замену оригинального файла настоятельно рекомендуется выполнять в виде модулей Magisk, для себя я это сделал следующим образом. Обратите внимание: данные модули сделаны мной для телефона Xiaomi Redmi 4×3/32 Гб с актуальной на момент написания статьи стоковой глобальной стабильной прошивкой MIUI 10. В Вашем случае файл bluetooth.default.so придётся заменить на свой собственный, пропатченный, как описано выше. Возможно также, что файл придётся продублировать по пути /system/lib64/hw — это зависит от модели и версии прошивки Вашего телефона.

Данный подход с использованием модулей Magisk позволяет легко изменять максимальный битрейт и вообще отключать изменения, если окажется, что какое-то из принимающих устройств не поддерживает Dual Channel.

Заключение


В настоящий момент в погоне за продажами многие компании подают некоторые технологические новинки как обоснование более высокой цены.

На практике оказывается, что уже имеющиеся, более дешёвые технологии не до конца развиты, а технологические новинки — не до конца внедрены, что значительным образом сказывается на качестве.

Очень часто пользователи испытывают «эффект плацебо», убеждая себя в совершенстве того или иного продукта только потому, что он новее или более красочно преподнесён. По факту, это качество оказывается мнимым.

Несмотря на очевидное ухудшение качества беспроводной передачи звука по сравнению с проводным вариантом, по-видимому, современные производители устройств нацелены на полный переход к беспроводным технологиям. При этом для обоснования повышения цены используются маркетинговые уловки: защита от погружения в воду телефона (как можно разговаривать под водой? зачем ронять устройство в воду?), использование более дорогих кодеков и т.д. При этом потенциал имеющегося популярного кодека SBC до конца не освоен.

Нам не удалось ни получить разъяснений от Google касательно лимита битрейта в 328 кбит/с, ни добиться устранения этого лимита и добавления опции включения Dual Channel в меню Bluetooth от разработчиков Lineage OS.

Спасибо всем, кто дочитал до конца!
obmrccmcn21gzh8g1ca3g3ykfs4.jpeg

© Habrahabr.ru