четверг, 16 июня 2016 г.

Кратко о разработке для ТВ-приставок: Amazon FireTV

Здесь будет история моего фейла и как не получилась трансляция на FireTV.

FireTV, хоть и внешне похож на Хромкаст (все мои приставки — самые простые стики), является куда более продвинутым устройством. Работает он под управлением модифицированного Андроида 5, то есть на нём можно не только транслировать видео с телефона, но и запускать полноценные Android-приложения (учитывая, однако, особенности управления с пульта).

Мне для начала нужно было реализовать в приложении для телефона трансляцию на FireTV, аналогичную Хромкасту. Для этого у Амазона есть библиотека Fling SDK. С Google Cast она не имеет ничего общего, устроена гораздо проще, интегрируется куда легче. Правда, понадобилось делать собственный диалог выбора устройств, чтобы там отображались и хромкасты, и амазоны. Ну и вообще пришлось пострадать с архитектурой, чтобы сделать работу с обоими устройствами более-менее единообразной, но в целом ничего страшного.

Проблемы

Однако, дальше оказалось, что трансляция на FireTV работает довольно паршиво. Такие, к примеру, возникали баги:

  • FireTvStateListener возвращает какие попало значения для прогресса видео. Например, так:
    21:40:13.189 19831-21015/D: position [0]
    21:40:13.459 19831-21023/D: position [0]
    21:40:13.764 19831-21033/D: position [0]
    21:40:14.179 19831-21042/D: position [0]
    21:40:14.589 19831-21050/D: position [0]
    21:40:14.919 19831-21060/D: position [0]
    21:40:15.314 19831-21070/D: position [0]
    21:40:15.649 19831-20997/D: position [0]
    21:40:15.934 19831-21006/D: position [0]
    21:40:16.229 19831-21015/D: position [3118]
    21:40:16.544 19831-21023/D: position [3118]
    21:40:16.919 19831-21033/D: position [2003]
    21:40:17.319 19831-21042/D: position [2003]
    21:40:17.709 19831-21050/D: position [3118]
    21:40:18.244 19831-21060/D: position [3118]
    21:40:18.459 19831-21070/D: position [3118]
    21:40:18.754 19831-20997/D: position [3118]
    21:40:19.154 19831-21006/D: position [3118]
    21:40:19.494 19831-21015/D: position [3118]
    21:40:19.889 19831-21023/D: position [3118]
    21:40:20.124 19831-21033/D: position [3118]
    21:40:20.344 19831-21042/D: position [7371]
    21:40:20.559 19831-21050/D: position [7371]
    21:40:20.799 19831-21060/D: position [7371]
    21:40:21.119 19831-21070/D: position [6360]
    21:40:21.324 19831-20997/D: position [7371]
    21:40:21.824 19831-21006/D: position [7371]
    21:40:22.189 19831-21015/D: position [2003]
    21:40:22.439 19831-21023/D: position [9386]
    21:40:22.769 19831-21033/D: position [6360]
    21:40:23.029 19831-21042/D: position [7371]
    21:40:23.309 19831-21050/D: position [10382]
    21:40:23.534 19831-21060/D: position [10382]
    21:40:23.774 19831-21070/D: position [9386]
        
  • Методы для управления воспроизведением (play/pause) совершенно необязательно срабатывают. Довольно часто случается, что вызываешь pause(), а он тебе: неа, не буду. И вот такой эксепшен:
    java.util.concurrent.ExecutionException: java.io.IOException: Cannot pause media device
        at java.util.concurrent.FutureTask.report(FutureTask.java:93)
        at java.util.concurrent.FutureTask.get(FutureTask.java:163)
        at co.unreel.videoapp.playback.remote.firetv.UnreelFireTvPlayer$ErrorResultHandler.futureIsNow(UnreelFireTvPlayer.java:210)
        at com.amazon.whisperplay.fling.media.controller.impl.PlayerDeviceImpl$AsyncFutureTask.done(PlayerDeviceImpl.java:594)
        at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:355)
        at java.util.concurrent.FutureTask.setException(FutureTask.java:222)
        at java.util.concurrent.FutureTask.run(FutureTask.java:242)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
        at java.lang.Thread.run(Thread.java:818)
    Caused by: java.io.IOException: Cannot pause media device
        at com.amazon.whisperplay.fling.media.controller.impl.PlayerDeviceImpl$23.call(PlayerDeviceImpl.java:471)
        at java.util.concurrent.FutureTask.run(FutureTask.java:237)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
        at java.lang.Thread.run(Thread.java:818)
    Caused by: com.amazon.whisperlink.exception.WPTException: Server busy. Doesn't accept new connections
        at com.amazon.whisperlink.transport.TWhisperLinkTransport.getWPTExceptionByErrorCode(TWhisperLinkTransport.java:1461)
        at com.amazon.whisperlink.util.Connection.throwUsingResponseCode(Connection.java:781)
        at com.amazon.whisperlink.util.Connection.doConnectOnce(Connection.java:667)
        at com.amazon.whisperlink.util.Connection.doConnect(Connection.java:538)
        at com.amazon.whisperlink.util.Connection.doConnect(Connection.java:493)
        at com.amazon.whisperlink.util.Connection.connect(Connection.java:337)
        at com.amazon.whisperplay.fling.media.controller.impl.PlayerDeviceImpl$23.call(PlayerDeviceImpl.java:457)
        at java.util.concurrent.FutureTask.run(FutureTask.java:237)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
        at java.lang.Thread.run(Thread.java:818)
    
  • Очень долго работает seekTo. Захочешь промотать на 10 минут — будешь ждать минуту.
  • Вызов play не выключает скринсейвер. Проигрывание идет, звук слышен, но видео закрыто амазоновскими картинками.

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

Конечно, увидев столько странностей, сразу полезешь смотреть, а что там у YouTube. Выяснилось, что у него трансляция устроена совсем по-другому. Устройство FireTV обнаруживается только в том случае, если на нём установлено приложение YouTube, и потом всё воспроизведение идёт через это приложение. В общем, хромкастовский подход здесь не сработал. В документации написано что-то про Custom App Receiver, может это правильный путь. А мы пока FireTV отложили, так что продолжение эпопеи следует.

Комментариев нет: