Русский
Русский
English
Статистика
Реклама

Android tv

Как портировать SDK Flutter на ТВ-приставку для разработки и запуска приложений Android TV

15.04.2021 16:11:54 | Автор: admin

Недавно мы успешно портировали фреймворк Flutter на ТВ-приставку c открытой программной платформой RDK. В этой статье расскажем о трудностях, с которыми пришлось столкнуться, и предложим решения для успешного запуска и повышения производительности.

Учитывая, что программный стек RDK или Reference Design Kit сейчас активно используется для разработки OTT-приложений, голосового управления приставками и других продвинутых функций для видео по запросу (VoD), мы хотели разобраться, сможет ли Flutter работать на ТВ-приставке. Оказалось, что да, но, как это обычно бывает, есть нюансы.

Далее мы по шагам распишем процесс портирования и запуска Flutter на встраиваемых Linux-платформах и разберемся, как этот SDK с открытым исходным кодом от Google чувствует себя на железе с ограниченными ресурсами и ARM-процессорами.

Но прежде чем переходить непосредственно к Flutter и его преимуществам скажем пару слов об исходном решении, которое было задействовано на ТВ-приставке. На плате работала связка набор библиотек EFL + протокол Wayland, а рисование примитивов было реализовано из node.js на основе плагинного нативного модуля. Это решение неплохо себя показало с точки зрения производительности при отображении кадров, однако сам EFL отнюдь не самый новый фреймворк для отрисовки. А в режиме выполнения node.js со своим огромным event-loopом казался уже не самой перспективной идеей. В то же время Flutter мог позволить нам задействовать более производительную связку рендеринга.

Для тех, кто не в теме: первую версию этого SDK с открытым кодом Google представил еще шесть лет назад. Тогда этот набор средств разработки годился только для Android. Сейчас на нем можно писать приложения для веба, iOS, Linux и даже Google Fuchsia. :-) Рабочий язык для разработки приложений на Flutter Dart, в свое время он был предложен в качестве альтернативы JavaScript.

Перед нами стоял вопрос: даст ли переход на Flutter какой-то выигрыш по производительности? Ведь подход там совершенно иной, хоть в конечном счете и имеется та же графическая подсистема Wayland + OpenGL. Ну и как там с поддержкой процессоров с neon-инструкциями? Были и другие вопросы, например, нюансы по переносу UI на dart или то, что поддержка Linux находится в стадии альфы-беты.

Сборка Flutter Engine для ТВ-приставок на базе ARM

Итак, начнем. Вначале Futter нужно запустить на чужеродной платформе с Wayland + OpenGL ES. В основе рендеринга у Flutter лежит библиотека Skia, которая прекрасно поддерживает OpenGL ES, поэтому в теории все выглядело хорошо.

При сборке Flutter под наши целевые устройства (три ТВ-приставки с RDK), к нашему удивлению, проблемы возникли только на одной. Не будем с ней сражаться, т.к. из-за старой архитектуре intel x86 она для нас не является приоритетной. Лучше сосредоточимся на оставшихся двух ARM-платформах.

Вот, с какими опциями мы собирали Flutter Engine:

./flutter/tools/gn \      --embedder-for-target \      --target-os linux \      --linux-cpu arm \      --target-sysroot DEVICE_SYSROOT      --disable-desktop-embeddings \      --arm-float-abi hard      --target-toolchain /usr      --target-triple arm-linux-gnueabihf      --runtime-mode debugninja -C out/linux_debug_unopt_arm

Большинство опций понятны: собираем под 32-битный ARM-процессор и Linux, выключая при этом все лишнее через --embedder-for-target --disable-desktop-embeddings.

Для сборки в системе должен быть установлен clang версии 9 и выше, т.е. это стандартный сборочный механизм Flutter, инструментарий кросс-компиляции gcc не пойдет. Самое важное подать корректный target-sysroot устройства с RDK.

Честно говоря, мы удивились, что при сборке не возникло вообще никаких нюансов. На выходе получаем заветную библиотеку flutter_engine.so и заголовок с необходимыми функциями для эмбеддера.

Теперь можно собрать целевой проект flutter/dart с нашей библиотекой/движком. Это сделать легко:

flutter --local-engine-src-path PATH_TO_BUILDED_ENGINE_src --local-engine=host_debug_unopt build bundle

Важно! Сборка проекта должна происходить не на устройстве с собранной библиотекой, а на хостовой, т.е. x86_64!

Для этого достаточно еще раз пройти путь сборкой gn и ninja только под x86_64! Именно она указывается в параметре host_debug_unopt.

PATH_TO_BUILDED_ENGINE_src это путь, где находится engine/src/out.

За запуск Flutter Engine под системой обычно отвечает embedder, именно он конфигурирует Flutter под целевую систему и дает основные контексты рендеринга библиотеке Skia и Dart-обработчику. Не так давно в состав Flutter добавили linux-embedder, и, в частности, GTK-embedder, так что можно воспользоваться им из коробки. На нашей платформе на момент портирования это был не вариант, нужно было что-то независимое от GTK.

Рассмотрим некоторые особенности реализации, которые пришлось учесть с кастомным эмбеддером (все, кто любит разбирать не нюансы, а исходники целиком, может сразу перейти к проекту нашего форка с доработками на github.com). К тому же по производительности наш вариант немного выигрывал у версии GTK, что было крайне важно для заказчика, и не тянул за собой весь зоопарк GTK-библиотек.

Так что же вообще нужно от эмбеддера для запуска flutter-приложения? Достаточно, чтобы он просто вызывал из библотеки flutter_engine.so

FlutterEngineRun(FLUTTER_ENGINE_VERSION, &config, &args, display /* userdata */, &engine_);

где в качестве параметров идет передача настроек проекта (директория с собранным flutter bundle) FlutterProjectArgs args и аргументов рендеринга FlutterRendererConfig config.

В первой структуре как раз задается путь bundle-пакета, собранного flutter-утилитой, а во второй используются контексты OpenGL .

// пример использования на github.com/DEgITx/flutter_wayland/blob/master/src/flutter_application.cc

Все довольно примитивно, но этого достаточно для запуска приложения.

Проблемы и их решение

Теперь поговорим о нюансах, с которыми мы столкнулись на этапе портирования. А как же без них? Не только ведь библиотеки собирать :-)

1. Краш эмбеддера и замена очередности вызова функций

Первая проблема, с которой мы столкнулись краш эмбеддера под платформой. Казалось бы, инициализация egl-контекста в других приложения происходит нормально, FlutterRendererConfig инициализирован корректно, но нет эмбеддер не заводится. Значит в связке что-то явно не так. Оказалось, eglBindAPI нельзя вызывать перед eglGetDisplay, на котором происходит особая инициализация nexus-драйвера дисплея (у нас платформа базируется на чипе BCM). В обычном Linux это не проблема, но на целевой платформе оказалась иначе.

Корректная инициализация эмбеддера выглядит так:

egl_display_ = eglGetDisplay(display_);if (egl_display_ == EGL_NO_DISPLAY) {  LogLastEGLError();  FL_ERROR("Could not access EGL display.");  return false;}if (eglInitialize(egl_display_, nullptr, nullptr) != EGL_TRUE) {  LogLastEGLError();  FL_ERROR("Could not initialize EGL display.");  return false;}if (eglBindAPI(EGL_OPENGL_ES_API) != EGL_TRUE) {  LogLastEGLError();  FL_ERROR("Could not bind the ES API.");  return false;}

// github.com/DEgITx/flutter_wayland/blob/master/src/wayland_display.cc корректная реализация, т.е. помогла измененная очередность вызова функций.

Теперь, когда нюанс запуска улажен, мы рады увидеть заветное демо-окно приложения на экране :-).

2. Оптимизация производительности

Настало время проверить производительность. И, честно говоря, она нас не сильно порадовала в режиме отладки (debug mode). Что-то работало шустро, что-то наоборот, имело большие просадки по фреймам и тормозило гораздо больше, чем что-то похожее на EFL+Node.js.

Мы немного расстроились и начали копать дальше. В SDK Flutter есть специальный режим компиляции машинного кода AOT, это даже не jit, а именно компиляция в нативный код со всеми сопутствующими оптимизациями, именно это подразумевается под по релиз-версией Flutter. Такой поддержки у нас в эмбеддере пока не было, добавляем.

Необходимы определенные инструкции, поданные аргументами к FlutterEngineRun

// полная реализация github.com/DEgITx/flutter_wayland/blob/master/src/elf.cc

vm_snapshot_instructions_ = dlsym(fd, "_kDartVmSnapshotInstructions");if (vm_snapshot_instructions_ == NULL) {  error_ = strerror(errno);  break;}vm_isolate_snapshot_instructions_ = dlsym(fd, "_kDartIsolateSnapshotInstructions");if (vm_isolate_snapshot_instructions_ == NULL) {  error_ = strerror(errno);  break;}vm_snapshot_data_ = dlsym(fd, "_kDartVmSnapshotData");if (vm_snapshot_data_ == NULL) {  error_ = strerror(errno);  break;}vm_isolate_snapshot_data_ = dlsym(fd, "_kDartIsolateSnapshotData");if (vm_isolate_snapshot_data_ == NULL) {  error_ = strerror(errno);  break;}
if (vm_snapshot_data_ == NULL || vm_snapshot_instructions_ == NULL || vm_isolate_snapshot_data_ == NULL || vm_isolate_snapshot_instructions_ == NULL) {  return false;}*vm_snapshot_data = reinterpret_cast <  const uint8_t * > (vm_snapshot_data_);*vm_snapshot_instructions = reinterpret_cast <  const uint8_t * > (vm_snapshot_instructions_);*vm_isolate_snapshot_data = reinterpret_cast <  const uint8_t * > (vm_isolate_snapshot_data_);*vm_isolate_snapshot_instructions = reinterpret_cast <  const uint8_t * > (vm_isolate_snapshot_instructions_);
FlutterProjectArgs args;// передаем все необходимое в argsargs.vm_snapshot_data = vm_snapshot_data;args.vm_snapshot_instructions = vm_snapshot_instructions;args.isolate_snapshot_data = vm_isolate_snapshot_data;args.isolate_snapshot_instructions = vm_isolate_snapshot_instructions;

Теперь, когда все есть, нужно собрать приложение особым образом, чтобы получить AOT-скомпилированный модуль под целевую платформу. Это можно сделать, выполнив команду из корня dart-проекта:

$HOST_ENGINE/dart-sdk/bin/dart \--disable-dart-dev \$HOST_ENGINE/gen/frontend_server.dart.snapshot \--sdk-root $DEVICE_ENGINE}/flutter_patched_sdk/ \--target=flutter \-Ddart.developer.causal_async_stacks=false \-Ddart.vm.profile=release \-Ddart.vm.product=release \--bytecode-options=source-positions \--aot \--tfa \--packages .packages \--output-dill build/tmp/app.dill \--depfile build/kernel_snapshot.d \package:lib/main.dart$DEVICE_ENGINE/gen_snapshot                               \    --deterministic                                             \    --snapshot_kind=app-aot-elf                                 \    --elf=build/lib/libapp.so                                   \    --no-causal-async-stacks                                    \    --lazy-async-stacks                                         \    build/tmp/app.dill

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

-Ddart.vm.profile=release \
-Ddart.vm.product=release \

указывают на то, что нам не нужен профайлер в комплекте и у нас продуктовая версия.

output-dill нужен для построения нативной библитеки libapp.so.

Самыми важными для нас являются пути $DEVICE_ENGINE и $HOST_ENGINE два собранных движка под целевую (ARM) и хост-системы (x86_64) соответственно. Тут важно ничего не перепутать и убедиться, что libapp.so получается именно 32-битной ARM-версией:

$ file libapp.so libapp.so: ELF 32-bit LSB shared object, ARM, EABI5 version 1 (SYSV), dynamically linked

Запускаем и-и-и-и... вуаля! все работает!

И работает шустрее значительно! Теперь уже можно говорить о сравнимой производительности и эффективности рендеринга с исходным приложением на базе набора библиотек EFL. Рендеринг работает почти без запинки и почти идеально на простых приложениях.

3. Подключение устройств ввода

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

4. Интерфейс на ТВ-приставке под Linux и Android и как увеличить производительность в 23 раза

Коснемся еще нескольких нюансов производительности, с которыми столкнулись в продуктовом UI-приложении. Нас очень обрадовала идентичность работы UI как на целевом устройстве, так и на Linux и Android. Уже сейчас Flutter может вполне может похвастаться очень гибкой портируемостью.

Еще отметим интересный опыт оптимизации самого dart-приложения под целевую платформу. Нас разочаровала довольно низкая производительность продуктового приложения (в отличии от демок). Мы взяли в руки профайлер и начали копать идовольно быстро обнаружили активное использование функций __brcm_cpu_dcache_flush и khrn_copy_8888_to_tf32 во время анимаций (на платформе используется чип процессора Broadcom/BCM ). Явно происходило какое-то очень жесткое пиксельное программное трансформирование или копирование во время анимаций. В итоге виновник был найден: в одной из панелей был задействован эффект размытия:

//...filter: new ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0),//...

Комментирование одного этого эффекта дало увеличение производительности приложения в дватри раза и вывело приложение на стабильные 50-60 fps. Это хороший пример того, как один специфический эффект может уничтожить производительность во всем приложении на Flutter при том, что он был зайствован всего-лишь в одной панели, которая большую часть времени была скрыта.

Итого

В результате мы получили не просто работающее продуктовое приложение, а работающее приложение с качественным фреймрейтом на Flutter на целевом устройстве. Форк и наша версия эмбеддера под RDK и другие платформы на основе Wayland находится тут: github.com/DEgITx/flutter_wayland

Надеемся, опыт нашей команды в разработке и портировании ПО для ТВ-приставок и Smart TV пригодится вам в своих проектах и послужит отправной точкой для портирования Flutter на других устройствах.

[!?] Вопросы и комментарии приветствуются. На них будет отвечать автор статьи Алексей Касьянчук, наш инженер-программист

Подробнее..

Тестируем плату для 4K Android ТВ-приставок на чипе Realtek RTD1395

27.08.2020 10:09:54 | Автор: admin


Мы недавно получили посылку от партнеров в Тайване компании Realtek. Внутри была плата Hercules OTT 32-битная, с памятью на 2 Гб и предустановленной ОС Android 9. Посмотрим, что она умеет и как ее можно использовать для разработки новых ТВ-приставок.

Плата Hercules OTT представляет собой Android IPTV box, который воспроизводит видео любых форматов с максимальным разрешением 4K. Обеспечена поддержка стриминговых сервисов Youtube, Netflix и др. посредством dash и hls. Будут работать и любые другие приложения для Android, такие как приложения аудио-видеозвонков и конференций. Однако с играми будут проблемы из за неудобства управления (придется подключать геймпад).

Realtek представила платы на базе чипа RTD1395 еще в 2018 году, их задействовали в своих абонентских устройствах операторы IP и кабельного телевидения, а также программисты, которые разрабатывают ПО для ТВ-приставок. Чип RTD1395 с четырьмя ядрами ARM Cortex-A53 и граф. процессором Mali-470 стал тогда более дешевой альтернативой для других моделей RTD1295 и RTD1296, но при этом поддерживал те же возможности работы с видео 4K. Сейчас аналогичные решения есть у Amlogic, Mediatek и других чип-вендоров, но о них мы расскажем в другой раз.

А сейчас сфокусируемся на нашей посылке.

Тестируем железо на плате


Так как плата Hercules ОТТ является отладочной, мы проверяли только физические способности чипа.

Итак, характеристики RTD1395ES:
  • 4-ядерный ARM Cortex-A53
  • Графический процессор ARM Mali-470 MP4
  • Декодер H.265 4K 60 fps, VP9 4K 60 fps, H.264 4K 30 fps
  • Кодировщик H.264 1080P 60 fps
  • HDR10, HLG, TCH Prime, Dolby Vision, HDR10+
  • USB2.0 хост/устройство
  • HDMI Tx v2.0b + CEC
  • 10/100 MAC и PHY + Gigabit Ethernet MAC
  • PCIe 2.0
  • SGMII
  • SDIO 3.0 и SD 3.0
  • I2S ввод/вывод
  • S/PDIF вывод




Результаты тестирования платы:
  • 2 х USB 2.0 оба работают
  • 1 х Ethernet 100 Мбитный порт работает
  • 1 х HDMI работает
  • 1 х S/PDIF работает
  • 1 х 3,5 мм аудиоразъем работает
  • 1 х слот для SD-карт работает
  • 1 х ИК-порт так как родного пульта у нас не было, а пульт от Amlogic не подошёл, то протестировать не смогли
  • 1 х USB Type-C только как питание
  • 1 х WIFI/Bluetooth-модуль: wifi работает, bluetooth не проверяли


Питание платы реализовано по кабелю Type-C на 5 Вольт так как отдельный разъём питания не распаян.

Для навигации в Android мы пробовали использовать пульт Opal с usb-свистком, который прежде работал со всеми IPTV-коробками, но на этой работать он отказался и даже не определялся в lsusb. Из-за этого пришлось пользоваться клавиатурой/мышью, что не очень удобно из-за отсутствия кнопок Back и Menu.

Сам процессор во время работы прилично греется, поэтому мы прилепили радиатор в виде тормозной колодки (см. на фото выше).

Тестируем воспроизведение видео


1. Запускаем 56 тестовых роликов


Проверяем возможности декодирования с помощью тестовых видео с сайта http://jell.yfish.us/

Открыть список из 56 видеофайлов для проверки:
jellyfish-3-mbps-hd-h264.mkv
jellyfish-3-mbps-hd-hevc-10bit.mkv
jellyfish-3-mbps-hd-hevc.mkv
jellyfish-5-mbps-hd-h264.mkv
jellyfish-5-mbps-hd-hevc.mkv
jellyfish-10-mbps-hd-h264.mkv
jellyfish-10-mbps-hd-hevc-10bit.mkv
jellyfish-10-mbps-hd-hevc.mkv
jellyfish-15-mbps-hd-h264.mkv
jellyfish-15-mbps-hd-hevc.mkv
jellyfish-20-mbps-hd-h264.mkv
jellyfish-20-mbps-hd-hevc-10bit.mkv
jellyfish-20-mbps-hd-hevc.mkv
jellyfish-25-mbps-hd-h264.mkv
jellyfish-25-mbps-hd-hevc.mkv
jellyfish-30-mbps-hd-h264.mkv
jellyfish-30-mbps-hd-hevc.mkv
jellyfish-35-mbps-hd-h264.mkv
jellyfish-35-mbps-hd-hevc.mkv
jellyfish-40-mbps-hd-h264.mkv
jellyfish-40-mbps-hd-hevc-10bit.mkv
jellyfish-40-mbps-hd-hevc.mkv
jellyfish-45-mbps-hd-h264.mkv
jellyfish-45-mbps-hd-hevc.mkv
jellyfish-50-mbps-hd-h264.mkv
jellyfish-50-mbps-hd-hevc.mkv
jellyfish-55-mbps-hd-h264.mkv
jellyfish-55-mbps-hd-hevc.mkv
jellyfish-60-mbps-hd-h264.mkv
jellyfish-60-mbps-hd-hevc-10bit.mkv
jellyfish-60-mbps-hd-hevc.mkv
jellyfish-70-mbps-hd-h264.mkv
jellyfish-70-mbps-hd-hevc.mkv
jellyfish-80-mbps-hd-h264.mkv
jellyfish-80-mbps-hd-hevc.mkv
jellyfish-90-mbps-hd-h264.mkv
jellyfish-90-mbps-hd-hevc-10bit.mkv
jellyfish-90-mbps-hd-hevc.mkv
jellyfish-100-mbps-hd-h264.mkv
jellyfish-100-mbps-hd-hevc.mkv
jellyfish-110-mbps-hd-h264.mkv
jellyfish-110-mbps-hd-hevc.mkv
jellyfish-120-mbps-4k-uhd-h264.mkv
jellyfish-120-mbps-4k-uhd-hevc-10bit.mkv
jellyfish-140-mbps-4k-uhd-h264.mkv
jellyfish-140-mbps-4k-uhd-hevc-10bit.mkv
jellyfish-160-mbps-4k-uhd-h264.mkv
jellyfish-160-mbps-4k-uhd-hevc-10bit.mkv
jellyfish-180-mbps-4k-uhd-h264.mkv
jellyfish-180-mbps-4k-uhd-hevc-10bit.mkv
jellyfish-200-mbps-4k-uhd-h264.mkv
jellyfish-200-mbps-4k-uhd-hevc-10bit.mkv
jellyfish-250-mbps-4k-uhd-h264.mkv
jellyfish-250-mbps-4k-uhd-hevc-10bit.mkv
jellyfish-300-mbps-4k-uhd-hevc-10bit.mkv
jellyfish-400-mbps-4k-uhd-hevc-10bit.mkv


Из всех протестированных видео не запустились только 3 файла из-за краша в realtek.rvsd@1.0-service:
jellyfish-250-mbps-4k-uhd-hevc-10bit.mkv
jellyfish-300-mbps-4k-uhd-hevc-10bit.mkv
jellyfish-400-mbps-4k-uhd-hevc-10bit.mkv

Лог ошибки воспроизведения:
DEBUG (4077 4077)  Fpid: 1815, tid: 4069, name: rvsd@1.0-servic  >>> /vendor/bin/hw/vendor.realtek.rvsd@1.0-service <<<            DEBUG (4077 4077)  Fsignal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------                                                    DEBUG (4077 4077)  FAbort message: 'FORTIFY: pthread_mutex_destroy called on a destroyed mutex (0xa7b1b06c)'


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

Скриншоты работы видеоплеера от Realtek получить не удалось. Вероятно, дело в том что плеер реализован не средствами Android, а имеет доступ напрямую к фреймбуферу, чтобы защитить контент от пиратства. В VLC и ExoPlayer скриншоты делаются без проблем.

При тестировании этих же видео в VLC файлы h264 играет до 180 Мбит/с, дальше уже не успевает подготавливать кадры. HEVC перестаёт воспроизводить на 120 Мбит/с (судя по логам крашей из-за нехватки памяти).

2. Запускаем тест Geekbench3


В тесте Geekbench3 система набрала 523/1656 баллов SingleCore/MultiCore, что примерно равно производительности чипа Amlogic S905 (500/1400) а в multicore даже немного опережает. Но при этом значительно отстаёт от Amlogic S905X2 (700/2200).



3. Проверяем разные fps


Мы тестировали видео с разной кадровой частотой (fps): 23.976, 24, 25, 50, 59.94, 60. С этим проблем не возникло.

Варианты видео с прогрессивной и чересстрочной развёрткой также воспроизводятся успешно.

4. Запускаем стримы HLS и Mpeg-Dash


Также протестировали воспроизведение HLS и Mpeg-Dash-стримов с помощью нескольких способов, а именно:

Способ 1. Воспроизведение стримов через агрегатор фильмов HD VideoBox, в котором при запуске видео можно выбрать плеер, в нашем случае VideoPlayer.




Способ 2. Воспроизведение HLS и Mpeg-Dash- стримов через youtube-приложение. Для тестирования брали ролики 4K 60 fps. Просадок кадров или затормаживания не заметили. На всех скриншотах из youtube получился чёрный экран вместо картинки, вероятно, из-за защиты контента.



На SmartYoutube скриншоты делаются корректно.

Способ 3. Скормить mpd- и m3u8-файлы напрямую плееру через файловый менеджер или браузер никак не удалось, но в некоторых случаях браузер способен воспроизводить видео.

Способ 4. Воспроизведение стримов DASH и HSL через Exoplayer Demo. Все видео, ссылки на которых были живы, воспроизводились успешно. Единственное, что не воспроизводилось защищённые видео Widevine, которые сопровождались надписью Это устройство не поддерживает требуемую DRM-схему (This device does not support the required DRM scheme)

5. Встроенные тесты видео от Realtek


Также на плате Hercules ОТТ есть и собственное предустановленное приложение для тестирования воспроизведения сразу нескольких видео. В ходе тестирования выяснилось, что в режиме, когда экран делится на 2 или в режиме картинка-в-картинке, более-менее сносно воспроизводится видео только с битрейтом до 45 Мбит/с и желательно MPEG4:



Видео с низким битрейтом 360 кбит/с, но 60 fps не осилил даже 2 видео одновременно.

Видео с низким битрейтом 670 кбит/с и 15 fps и разрешением 320x240 пикселей осилил одновременно 8 видео, при 9 и более некоторые могли не запуститься и кадры в видео терялись.



Также на плате Realtek предустановлена программа Encoder, которая позволяет транскодировать до 4 видео одновременно из mp4 в AVC-ts-файлы.





Проверили, что она может транскодировать:
1 видео 1080p60fps (скорость транскодирования около 54 fps)
2 параллельных видео 1080p30fps (скорость транскодирования около 23 fps)

6. Запускаем приложение OpenCamera


Для последней проверки запускаем приложение OpenCamera и подключаем usb-веб-камеру Logitech С920 для тестирования кодирования видеозаписи. Выяснили, что в формате AVC на плате можно записать видео до 1080p27fps. В формате HEVC записать видео не удалось.

Выводы


Тесты показали, что в целом чип RTD1395 соответствует заявленным показателям. Плату Hercules ОТТ можно использовать для разработки и отладки устройств типа Android TV, дисплеев Miracast and Intel Wireless, ТВ-приставок и стриминговых OTT-плееров. Также на ней можно разрабатывать прошивку на основе AOSP для операторов IP-телевидения.

А для полной объективной оценки этого решения можно еще изучить конкурентов с аналогичными характеристиками, таких как Amlogic s905x и более новый S905X2.
Подробнее..

Перевод Запуск Netflix на телевизорах и приставках. Лишние 40 миллисекунд

15.12.2020 20:09:12 | Автор: admin
Приложение Netflix работает на сотнях умных телевизоров, стиков и телевизионных приставок. Я один из инженеров, которые помогают производителям запустить наше приложение на их устройствах. В этой статье обсудим особенно сложный вопрос, который помешал выходу одной телеприставки на европейский рынок.

Таинственная проблема


В конце 2017 года меня позвали на созвон, чтобы обсудить проблему с приложением Netflix на новой телеприставке. Это было новое устройство Android TV с поддержкой 4K, на базе Android Open Source Project (AOSP) версии 5.0, Lollipop. Я уже несколько лет работал в Netflix и помог выпустить несколько девайсов, но это был моё первое устройство Android TV.

На связи были все четыре стороны: крупная европейская компания платного ТВ, запускающая устройство (оператор), подрядчик, интегрирующий прошивку (интегратор), поставщик системы-на-чипе (поставщик чипов) и я (Netflix).

Интегратор и Netflix уже завершили строгий процесс сертификации Netflix, но во время внутреннего испытания у оператора руководитель компании сообщил о серьёзной проблеме: воспроизведение Netflix лагало, то есть видео воспроизводилось очень короткое время, затем пауза, затем снова, затем пауза. Это происходило не всегда, но стабильно начинало лагать через нескольких дней после включения приставки. Они показали видео, оно выглядело ужасно.

Интегратор нашёл способ воспроизвести проблему: несколько раз запустить Netflix, начать воспроизведение, а затем вернуться в UI. Они предоставили скрипт для автоматизации процесса. Иногда это занимало целых пять минут, но скрипт всегда надёжно воспроизводил баг.

Тем временем инженер из компании-поставщика чипов диагностировал основную причину: приложение Netflix для Android TV под названием Ninja не успевало доставлять аудиоданные. Лаги вызваны опустошением буфера в аппаратном звуковом конвейере. Воспроизведение останавливалось, когда декодер ждал от Ninja часть аудиопотока, а затем оно возобновлялось, когда поступали новые данные. Интегратор, поставщик чипов и оператор все думали, что проблема понятна. И все они смотрели на меня: Netflix, у вас есть ошибка в вашем приложении, и вы должны её исправить. Я слышал напряжение в голосе представителя оператора. Выпуск устройства задерживался и выходил за рамки бюджета, и они ожидали от меня результатов.

Расследование


Я был настроен скептически. Это же самое приложение Ninja работает на миллионах устройств Android TV, включая умные телевизоры и другие телевизионные приставки. Если в Ninja ошибка, то почему она происходит только на этом устройстве?

Я начал с того, что сам воспроизвёл проблему с помощью скрипта от интегратора. Я связался с коллегой из компании-производителя микросхем и спросил, видел ли он что-нибудь подобное (не видел). Затем начал изучать исходный код Ninja. Нужно было найти точный код, который отвечает за доставку аудиоданных. Я во многом разобрался, но начал теряться в коде, который отвечает за воспроизведение, и мне нужна была помощь.

Я поднялся наверх и нашёл инженера, который написал аудио- и видеоконвейер Ninja, он познакомил меня с кодом. После этого я сам ещё некоторое время изучал его, чтобы окончательно понять главные части и добавить свои логи. Приложение Netflix сложное, но в упрощённом виде оно получает данные с сервера Netflix, в течение нескольких секунд буферизует видео- и аудиоданные на устройстве, а затем доставляет видео- и аудиокадры по одному на аппаратные декодеры.


Рис. 1. Упрощённый конвейер воспроизведения

Давайте на минутку поговорим об аудио/видеоконвейере в приложении Netflix. До буфера декодера он абсолютно одинаковый на каждой приставке и телевизоре, но перемещение данных A/V в буфер декодера устройства это процедура, специфичная для конкретного устройства. Она выполняется в собственном потоке. Задача этой процедуры состоит в том, чтобы поддерживать буфер декодера полным, вызывая следующий кадр аудио- или видеоданных через API от Netflix. В Ninja эта работа выполняется потоком Android. Существует простая машина состояний и некоторая логика для обработки различных состояний воспроизведения, но при нормальном воспроизведении поток копирует один кадр данных в API воспроизведения Android, а затем сообщает планировщику потока подождать 15 мс перед следующим вызовом обработчика. При создании потока Android можно запросить повторный запуск потока, словно цикле, но именно планировщик потоков Android вызывает обработчик, а не ваше собственное приложение.

На максимальных 60 FPS устройство должно отображать новый кадр каждые 16,66мс, поэтому проверки через 15мс хватает в любом случае. Поскольку интегратор определил, что проблема в аудиопотоке, я сосредоточился на конкретном обработчике, который доставлял аудиосэмплы в аудиослужбу Android.

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

Ага, озарение


В конце концов я сосредоточился на трёх числах: скорость передачи данных, время вызова обработчика и время передачи управления от обработчика обратно Android. Я написал скрипт для анализа выходных данных журнала и сгенерировал график ниже, на котором виден ответ.


Рис. 2. Визуализация пропускной способности аудиопотока и тайминги обработчика

Оранжевая линия это скорость перемещения данных из буфера потоковой передачи в аудиосистему Android (байт в миллисекунду). На этой диаграмме три различных сценария:

  1. Две области с высокими пиками, где скорость передачи данных достигает 500байт в миллисекунду. Эта фаза буферизация перед началом воспроизведения. Обработчик копирует данные быстро, как только может.
  2. Область посередине это нормальное воспроизведение. Аудиоданные перемещаются со скоростью около 45 байт в миллисекунду.
  3. Область лагов находится справа, когда аудиоданные перемещаются со скоростью около 10 байт в миллисекунду. Это недостаточно быстро для поддержания воспроизведения.

Неизбежный вывод: оранжевая линия подтверждает выводы инженера из компании-производителя чипов. Действительно, Ninja недостаточно быстро доставляет аудиоданные.

Чтобы понять причину, давайте внимательнее изучим жёлтые и серые линии.

Жёлтая линия показывает время, проведённое в самой процедуре обработчика, вычисленное по меткам времени, записанным в начале и по завершению процедуры. Как в нормальной, так и в лагающей области время в обработчике одинаковое: около 2мс. Всплески показывают случаи, когда время оказалось медленнее из-за выполнения других задач на устройстве.

Истинная первопричина


Серая линия время между вызовами обработчика говорит о другом. При обычном воспроизведении обработчик вызывается примерно каждые 15 мс. В случае лагов справа обработчик вызывается примерно каждые 55мс Между вызовами возникают лишние 40мс, и в такой ситуации он никак не может угнаться за воспроизведением. Но почему?

Я сообщил о своём открытии интегратору и поставщику чипов (смотрите, виноват планировщик потоков Android!), но они продолжали настаивать, что решать проблему должен Netflix. Почему бы при каждом вызове обработчика не копировать больше данных? Это была справедливая критика, но реализация такого поведения повлекла бы глубокие изменения, на которые я не хотел идти, поэтому продолжил поиски первопричины. Я нырнул в исходный код Android и узнал, что потоки Android это конструкция пользовательского пространства, а планировщик потоков для синхронизации использует системный вызов epoll(). Я знал, что производительность epoll() не гарантирована, поэтому подозревал, что нечто систематически на него влияет.

В этот момент меня спас другой инженер из компании-поставщика чипов, который обнаружил баг, уже исправленный в следующей версии Android (Marshmallow). Оказывается, планировщик потоков Android изменяет поведение потоков в зависимости от того, работает приложение на переднем плане или в фоновом режиме. Потокам в фоновом режиме назначается дополнительная задержка 40мс (40000000нс).

Ошибка глубоко в ядре Android означала, что это дополнительное значение таймера сохранялось при перемещении потока на передний план. Обычно поток обработчика звука создавался, когда приложение на переднем плане, но иногда немного раньше, когда Ninja всё ещё находилась в фоновом режиме. Если такое случалось, воспроизведение начинало лагать.

Полученные уроки


Это не последняя ошибка, которую мы исправили на платформе Android, но её было труднее всех отследить. Она находилась вне приложения Netflix и даже вне конвейера воспроизведения, а все исходные данные указывали на ошибку в самом приложении Netflix.

История иллюстрирует аспект моей работы, которую я люблю: невозможно предсказать все проблемы, которые наши партнёры сбросят на меня. И я знаю, что для их решения нужно понимать множество систем, работать с отличными коллегами и постоянно подталкивать себя к изучению нового. То, что я делаю, оказывает непосредственное влияние на реальных людей и на их удовольствие от отличного продукта. Когда люди наслаждаются просмотром Netflix в своей гостиной, я знаю, что являюсь частью команды, которая сделала это возможным.
Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru