Исследование движков воспроизведения (начало проекта, 2026-10-11…12)
Это исследование сделано до написания кода: по исходникам, issue-трекерам, Maven и дампу тестового ТВ. Часть выводов потом подтвердилась на реальном телевизоре, часть оказалась неверной. Что изменилось после тестов, сказано в конце этого файла, а итог описан в ../playback.md.
Файлы
| Файл | О чём |
|---|---|
decisions.md |
Принятые решения: гибридный движок, выбор движка, fallback, тестовый ТВ, что проверить прототипом (часть 7 общего jellyfin-client-research.md) |
media3.md |
media3/ExoPlayer: Dolby Vision, HDR, tunneling, частота дисплея, passthrough, FFmpeg, ASS (libass), контейнеры, DeviceProfile из jellyfin-androidtv |
engines.md |
Как устроены несколько движков в других клиентах (Findroid, Wholphin, JellyPlay, Swiftfin, Plezy, Kodi…), дизайн абстракции, Gradle-модули, libVLC против libmpv, алгоритм выбора, runtime-fallback |
device-xiaomi.md |
Тестовый ТВ Xiaomi S Pro Mini LED 75 и семейство MediaTek: известные баги декодеров, DV, passthrough, чек-лист проверок |
library-profile.md |
Профиль медиатеки тестового сервера: какие контейнеры, кодеки, HDR, аудио и субтитры встречаются и сколько |
tv-dump/ |
Сырые дампы ТВ: media_codecs-mt5896.xml, dumpsys display, audio_policy |
Полный исходный документ со всеми частями (Jellyfin API, обзор клиентов, libVLC, Android TV, инструменты): jellyfin-client-research.md.
Главное из исследования
Цель — воспроизводить всё, что можно, без серверного транскода и с правильным HDR и Dolby Vision. Официальный клиент Jellyfin, Moonfin и Dune на этом ТВ спотыкались каждый на своих файлах: транскодировали, давали тусклый HDR или зависали.
- Одного движка недостаточно.
- media3/ExoPlayer — единственный путь к аппаратному декодированию с HDR10, HDR10+, HLG и DV, а также к passthrough. Но он не открывает M2TS, ASF/WMV, ISO/BDMV, MKV со сжатием zlib, не умеет программно H.264 Hi10P, VC-1, MPEG-4 ASP и не рисует ASS сам.
- libVLC и libmpv играют почти всё через ffmpeg, но HDR у них ненадёжен.
- Отсюда гибрид: media3 основной, libVLC запасной.
- Запасным выбран libVLC 3.7.x, а не libmpv.
- Это официальный артефакт VideoLAN со стабильным Java API, внутри libass, PGS и VobSub, есть passthrough.
- Стоковый mpv-android переводит HDR в SDR через GLES, а AAR у него неофициальный.
- Цена libVLC — +24–55 МБ на ABI.
- Автоматический выбор движка до запроса PlaybackInfo. Данные файла от Jellyfin (контейнер, кодек, битность,
VideoRangeType, профиль DV, дорожки) сравниваются с возможностями устройства (MediaCodecList, HDR-типы дисплея,AudioTrack.isDirectPlaybackSupported). У каждого движка свой DeviceProfile. - Runtime-fallback по образцу Plezy. Если движок упал, завис или не дал кадра, сессия переходит на другой движок с той же позиции; если не справился и он — серверный транскод. Среди Jellyfin-клиентов такого нет ни у кого.
- Архитектура:
- свой узкий интерфейс
PlaybackEngineплюс интерфейсы-возможности (задержка звука, стиль субтитров и т. п.); - каждый движок — отдельный Gradle-модуль, подключается через Hilt
@IntoSet(позже DI перевели на Koin); - реализация движка «как media3
Player» (Findroid, Wholphin) приводит к десяткамTODO(), поэтому так не делаем.
- свой узкий интерфейс
- Риски, найденные заранее:
- DV P7 в media3 без DV-декодера не откатывается на HEVC;
- DV + HDR10+ зависает на MediaTek (#1895);
- HEVC-декодер MTK падает посреди потока (#3421), и
setEnableDecoderFallbackэто не ловит; - вечная буферизация при AC3 passthrough после быстрых перемоток (#3407);
- TrueHD и DTS-HD из приложения на встроенных динамиках, скорее всего, не выйдут в passthrough;
- смену частоты и HDR-режима media3 сам не делает.
- Тестовый ТВ (дамп через adb):
- MediaTek MT9689, Android 14, только 32-bit userspace (armeabi-v7a), 2,4 ГБ RAM;
- один режим дисплея 4K@60;
- дисплей заявляет DV, HDR10, HDR10+ и HLG;
- аппаратные AVC/HEVC/VP9/AV1 до 8K, MPEG-2/VC-1/MPEG-4 до 1080p.
Что тесты потом опровергли или уточнили
| Ожидание из исследования | Как оказалось на ТВ |
|---|---|
| DV P7 есть в железе (по XML-дампу) | В рантайме MediaCodecList не показывает dvhe.dtb (P7). Наличие в XML не гарантирует наличие в API |
| H.264 Hi10P — только программно (libVLC) | c2.mtk.avc.decoder заявляет High10 и играет Hi10P аппаратно |
| DV + HDR10+ виснет на MediaTek (#1895) | На этом ТВ играет без зависаний, и с DV-декодером, и как базовый слой |
| DV-декодер даёт «тусклую» картинку | ТВ действительно включает режим Dolby Vision (значок, DispService Hdr Dolby). «Тускло» — это режим изображения DV в настройках ТВ, а не поломка. DV-декодер включён по умолчанию |
Смена частоты нужна вручную (preferredDisplayModeId) |
Android видит один режим 4K@60, но на аппаратном видеослое ТВ сам переключает панель: 23,976 → 119,88 Гц, 25 → 100 Гц. Через GPU-путь (TextureView) этого нет |
| HTTPS для libVLC достаточно проверить на самоподписанном сертификате | libVLC (GnuTLS) через reverse proxy ненадёжен даже с нормальным сертификатом: зацикливается prefetch, перемотка к концу файла ждёт по ~10 с. Решено локальным HTTP-прокси внутри приложения |
VLCVideoLayout и libVLC — вопрос API |
Главные проблемы VLC оказались в звуке (многоканальный PCM на этом ТВ беззвучен) и в сетевом кэше (prefetch выбрасывает прочитанное) |
| VC-1 через media3, если есть декодер | Декодер зарегистрирован как MIME video/VC1 (регистр!), а media3 ищет video/wvc1 |
| Аппаратное декодирование в libVLC — по умолчанию | Опрос MediaCodec модулем VLC на MediaTek длится ~45 с. Аппаратно в VLC включаем только для видео больше 1080p |