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

Автор оригинала: John Blair
  • Перевод
Приложение 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 в своей гостиной, я знаю, что являюсь частью команды, которая сделала это возможным.

Комментарии 58

    +5
    Спасибо, показательная статья, что нужно искать первопричину. Хорошо, что ещё существуют такие специалисты. А то обычная практика, к сожалению, это придумывать костыли для костылей, которые подвешены за верёвочку.
      +3

      Искать первопричину — это важно, но не всегда эту причину могут устранить за приемлимые время и деньги, откуда и получаются костыли. И тут вторая проблема, что после того, как причину устранили (а это может быть не скоро), те самые костыли обрасли новыми костылями

      +14

      Так уж получилось, что похоже я практически в то же время натолкнулся на (эту же?) проблему 40ms но в контесте WebRTC в приложении под Android после сворачивания: в одном случае решилось заменой использования pthread_cond_timedwait_relative_np на pthread_cond_timedwait, в другом случае починилось заменой nanosleep(0) на sched_yield. Правда до кода в Android, вызывающиего проблему я так тогда и не докопался.

        –1
        Познавательно
          0

          Чорт. У нас сейчас тоже очень похожая проблема c ExoPlayer. Видео играет несколько секунд, stall на 0.5 сек, играет, stall,… а в логах — audio buffer underrun.

            –3

            Гениально. Такие глубокие познания.....

              +20

              С ума сойти, приложение 200-миллиардной компании работает только потому, что в многопоточный мобильной операционной системе кто-то гарантированно его дёргает ровно раз в 16,6 мс. А если не дёргает, то не работает.

                +9
                ну извините, другой многозадачности на пользовательские процессоры еще не завезли. (Тем более что качественное видео на самом деле жрет ресурсы не хуже многих динамичных игр)

                … меня например больше всего поразил вот этот фрагмент:
                Почему бы при каждом вызове обработчика не копировать больше данных? Это была справедливая критика, но реализация такого поведения повлекла бы глубокие изменения, на которые я не хотел идти, поэтому продолжил поиски первопричины. Я нырнул в исходный код Android

                Что это за такие глубокие изменения, что вместо «увеличить буфер под данные в 2 раза» (например) быстрее вышло проанализировать исходники Андроида?
                … разве что задачей на самом деле стояло написать отчет «почему это не моя вина, и почему я ничего у себя менять не буду»
                  +8

                  Потому что меняя что-то тут, нужно будет протестировать все остальные девайсы и все остальные оси на предмет "оно всё ещё работает как надо", а если нет — это очень дорогие изменения.
                  Либо ставить костыль "если этот вендор на этой версии андроида, тогда".

                    –1
                    Это совокупность плохого дизайна и накопленной сложности разработки (технический долг).
                    Код написано исходя из текущих ожиданий, но никто не гарантирует что завтра Google не придет и не скажет: «теперь мы делаем вызов каждые 20 мс» и все равно придется переписывать приложение под новые версии, или «теперь мы делаем вызов каждую 1 мс» и это вызовет другие проблемы.
                      +3

                      Разумеется, гарантий нет, но риски никто не отменял. Лучше потратить день и докопаться, чем быстренько починить и потом разгребать. И гугл скажет про то, что будет в следующей версии (для этого есть change notes между версиями), а я говорил про риск поломать сейчас в другом месте.

                        0

                        Можно размер буфера сделать динамическим, в случае если аудиобуфер опустошается быстрее заполнения — динамически подстраивать размер выгрузки.
                        IMO Это будет чуть более future-proof решение чем полагаться на магические константы.

                          0
                          Кстати динамический буфер может усугубить проблему — выделение блока памяти и заполнение делаются не мгновенно. Если нет жёсткого лимита ОЗУ, кмк надёжнее выделить нужное количество буферов достаточного объёма.
                    +1
                    Вы думаете звук или видео в любом другом приложении на любой другой платформе работают как-то иначе? Нет. И тут ничего плохого нет.
                    (upd: сорри, промахнулся уровнем комментария)
                      +3
                      качественное видео на самом деле жрет ресурсы не хуже многих динамичных игр

                      Не жрёт. Пруф очень простой — на iPad можно часами напролёт смотреть видео в высоком разрешении с Youtube от батарейки.

                      На пользовательских устройствах его декодирует аппаратный декодер и пишет кадры напрямую в память видеокарты. Это крошечный ASIC с потреблением в доли Вт, который, как правило, лицензируют у ARM вместе с GPU.
                        +3
                        Речь о том, что проблемы в принципе бы не было, если бы афтар приложения не копировал тупо по одному кадру равномерно с частотой воспроизведения, а просто поддерживал бы принимающие буфера на стороне устройства более-менее полными, копируя пореже по несколько кадров.
                        Причём афтар статьи сам об этом пишет, но архитектура кода не позволяет.
                        А ведь это ключевое приложение многомиллиардной корпорации, с зарплатами разрабов по 200+.
                        0

                        С ума сойти, все приложения компаний с капитализацией > 10 триллионов работают только потому, что вокруг триллионы кусочков кремния правильно делают +1 после выполнения инструкции INC на десятках разных архитектур и десятках тысяч разных моделей этих кусочков кремния. И делают это уже последние лет 50.

                          +5

                          Что-то как-то мимо. Любая функция отложенного выполнения, будь то sleep, set_interval или set_timeout, никогда не гарантирует, что выполнение будет отложено ровно на указанное количество милли/наносекунд. Это всегда остается на совести планировщика/ос и сильно зависит от текущей нагрузки. Полагаться на определённое поведение очень самонадеянно, в чем и убедились герои статьи.

                            +1

                            Ага, не гарантирует. И это "не гарантирует" кое-как компенсировать научились. Но нормально компенсировать можно только однократную небольшую проблему, а не крупную повторяющуюся.


                            Иными словами, есть некоторая разница между запозданием пробуждения потока на 1 миллисекунду (что нормально) и постоянным лагом в 40мс.

                              +3

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


                              Таким образом, полагаться на то, что sleep закончится за 16 мс ровно так же наивно, как и полагаться, что ioctl для swap buffers в видеоадаптере будет выполнен за vblank время. А, вдруг, он 300мс займёт? А потом ещё раз. И ещё раз.


                              А нужно 60 fps. И что делать? Тут ситуация такая же.

                                0

                                Примерно поэтому, производитель ТВ приставки так подстраивался под нетфликс, и не выпускал пока не заработало. Никто ничего не гарантирует, и если что, надо было бы наращивать ресурсы, а не ПО.

                            0

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

                              0
                              А как ещё это делать оптимально?
                                +2

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


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


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


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

                                  0
                                  Это верно, не спорю. Но как я понял, тут проблема в том, что буфер опустошался до того, как была вызвана функция его заполнения. То, что он потом мог быть заполнен, никак не избавит от заикания прямо сейчас.
                                    +2

                                    Конкретно для озвученных таймингов (55мс ожидания и 16,6мс на кадр) подобная "подстройка" приведёт к пропуску трёх кадров из каждый четырёх. Ну да, формально видео будет показываться, но мне почему-то кажется, что 15фпс вместо 60 — не то что нужно пользователю.


                                    Когда требуется показывать 60 кадров в секунду — лишняя задержка в 40мс является недопустимой и не поддаётся никакой компенсации.

                                      0

                                      Такое было бы возможно, если бы кодек от задержки данных не останавливался, а продолжал отмерять виртуальные секунды и показывал только те кадры, которые успели доставиться. Однако в реальности при задержке данных (например, проседание сети), кодек останавливается на том месте, где у него было недостаточно данных и продолжает воспроизведение когда эти данные до него доходят.


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

                                        0

                                        Мне сложно понять по описанию где именно у них была проблема, может быть буферы и правда бы всё исправили. Но там в любом случае обязан быть хотя бы один поток, который просыпается на каждый кадр. И если ОС "добавит" задержку в него — всё поломается в любом случае...

                              +3
                              Что это за такие глубокие изменения, что вместо «увеличить буфер под данные в 2 раза» (например) быстрее вышло проанализировать исходники Андроида?

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

                                А что в итоге?
                                В сорцах Android есть баг, ок.
                                Из оригинальной статьи не понятно:


                                1. Код Netflix нативный или на Java/Kotlin?
                                2. Какое API используется для декодирования: штатный MediaCodec из Android SDK/NDK?
                                  2.1 Если да, то почему бы не пихать данные в декодеры с максимальной скоростью? (синхронизация ведь все-равно произойдет на стороне рендеров)
                                3. Как была решена проблема?
                                  3.1 Увеличением размера буферов
                                  3.2 Использованием других примитивов синхронизации (а какие были до этого?)
                                  3.3 Перепрошивкой всех приставок более новой версией ОС?
                                  ...

                                Это классическая ситуация, когда в прейбечном пэйплайне одна из веток (аудио/видео) подпирает другую.
                                Собсвенно для решения подобных проблем и делают асинхронные очереди по каждой ветке.
                                Забавно, что автору было дешевле изучить код AOSP, чем адаптировать свой код к суровой реальности.

                                  +1

                                  По моему очевидно что они пофиксили создание тредов чтобы проблемный не становился бэкграунд тредом и шедулился нормально.

                                    +3
                                    Нет, неочевидно.
                                    Непонятно они пересобирали ядро на 5й версии или пересобирали все приставки на 6й.
                                    Чувак молодец, что нашел известный баг, но говорить «нетфликс не будет работать на 5й версии» (но другими словами) тоже неправильно.
                                    Все, кто длительное время разрабатывает под андроид, встречали разные баги ядра, от которых никуда не деться, но никто не говорил что «ну, живите с этим».
                                      +1

                                      Но это не фикс для плейера. Это просто воркэраунд позволяющий скрыть проблему.

                                        0
                                        Почему это? Если перефразировать — они обнаружили что поток создавался с неправильным приоритетом и стали создавать его справильным приоритетом — так лучше выглядит?
                                          +1
                                          Почему это?

                                          Потому, что синхронизация плейбэчных мультимедиа графов должа происходить в конечной точке — аудио/видео рендере, а не где-то по пути перед декодером.
                                          Так работают медиафреймворки DirectShow/GStreamer/OpenMAX и в них нет таких детских болячек, как в сабжевой статье.


                                          Разумеется, если мы пишем аудио рендер, например на Qt, с использованием QAudioOutput, то должны повысить приоритет потоку, разгребающему входную очередь до QThread::TimeCriticalPriority. В противном случае получим заикания.


                                          У нетфликс своеобразное решение, они подпирают аудио/видео ветки посередине.
                                          При том, что потом будет еще один подпор на рендере (скорость работы DMA для звукового ЦАП никто под нас подстраивать не будет, оно потребляет данные монотонно со скоростью штатного кварцевого генератора).

                                      +10
                                      Как была решена проблема?

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


                                      The integrator back-ported the patch (linked above) to L. It is literally only a few lines so this was straightforward. Some of the hardest problems take 1 or 2 lines to fix :)
                                        +4
                                        Some of the hardest problems take 1 or 2 lines to fix

                                        Как же это знакомо. Дни (и ночи) дебага с периодическими комментариями «да как так то бл...» заканчиваются фиксом в одну-две строчки.

                                          –1
                                          Интегратор бэк-портировал багфикс андроида

                                          Получается какая-то антиреклама.
                                          Netflix выявил у себя архитектурную проблему и осознанно отказался фиксить.

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

                                              Подобная "архитектурная проблема" есть у любого аудио- или видеопроигрывателя, а также в большинстве игр.


                                              Всё-таки это ответственность ОС — следить чтобы те приложения, с которыми работает пользователь, получали больше процессорного времени.

                                                0
                                                Подобная "архитектурная проблема" есть у любого аудио- или видеопроигрывателя

                                                Вы не правы. В комментарии выше описал почему

                                          +1
                                          Рядовые программисты: красивые абстракции, виртуальные машины и прочие стремления к идеальному коду.
                                          Самый Главный Отдел: так, 60FPS, некая магическая константа «15мс», здесь гвоздями прибьём, таак падажжи.
                                          Звучит смешно, но удели они достаточное внимание кешированию нескольких кадров и куче подобных не всплывших упрощений, они могли бы и не успеть выйти на рынок.
                                            0
                                            Кхм, а если видеофайл с удвоенной скоростью, и телек на 120 кадров — тогда как?
                                              0
                                              А в нетфликсе есть 120 кадров?
                                                0

                                                Похоже, что у нетфликса контент предзакодирован специально для их плеера(ов).
                                                В результате декодеры работают с идеальными входными данными и плеерописателям можно "не напрягаться"

                                                  +1

                                                  Ну это наверное самая большая проблема всех стриминговых сервисов :( приходится делать стримы под конкретные плейеры. И это реальная боль.

                                                    0

                                                    Так у них нет сторонних плееров. У них свое приложение под все распространённые программные платформы.

                                                      0

                                                      Я имел ввиду общую проблему всех стриминговых сервисов. Свой плейер решает некоторое количество проблем, но добавляет другие. Только просто протестировать плейер на разношерстной братии теликов, боксов, писишек и тп требует очень больших усилилий и ресурсов.

                                                        0

                                                        Конечно это не просто, но для такой большой компании не думаю что сложная задача.


                                                        Я подробностей не знаю, но думаю что у них есть сертификация клиентского оборудования и так далее (это видно из переведенной статьи). Просто так в рандомный телик ее не запихнут. Айфоны-айпады протестировать просто — их мало. Основная проблема, думаю, с разношерстными андроидами.


                                                        Сейчас работаю в телекоме, как раз в IPTV, и у нас есть в STBшке (внутре у ней андроед, но пользователям не доступен, только оболочка) приложуха Netflix. И есть тестовый аккаунт в Netflix для проверки и прочего. При случае узнаю у причастных как ее туда пропихивали...

                                                +4
                                                Подобный баг устранили на OnePlus3T в приложении ivi. Через несколько секунд видео начинало сильно «тормозить». Методом проб и ошибок вычислил, что если тыкать по экрану пальцем, то видео не лагало. Корень проблемы был где-то в прошивке девайса, т.к. при обновлении прошивки баг исчезал. Но у нас все еще было несколько тысяч пользователей с этим устройством и не обновленной прошивкой, и они о себе напоминали в маркете. Решение было ужасным, но рабочим: для данного девайса во время воспроизведения видео добавили нагрузку процессора раз в несколько секунд.
                                                	public static void spinCpu(final long spinMs) {
                                                		final long startMs = System.currentTimeMillis();
                                                		int count = 0;
                                                		while (System.currentTimeMillis() - startMs < spinMs) {
                                                			count++;
                                                		}
                                                	}

                                                Проблема решена, но какова цена :)
                                                  0
                                                  Решение было ужасным

                                                  А в чём проблема то? Процессор пытался заснуть или что-то более интересное?
                                                    +1
                                                    Девайс начинал тротлить при не активности пользователя в течение нескольких секунд. Даже при включенном экране и проигрывающемся видео.
                                                      0

                                                      WakeLock'и тоже не спасали?

                                                        0
                                                        WakeLock-и были, и система не засыпала, а также не выключала экран. Но процессор начинала тротлить через несколько секунд.
                                                    –2

                                                    Биткоины фоном помайнили?

                                                    +5
                                                    Я не знаю как там было реализовано в Android, но в древней винде емнип можно было 1) создать очередь воспроизведения из нескольких блоков (когда буфер в одном блоке заканчивается винда переходит к следующему) и 2) там можно было задать коллбэк который дергался ОС когда такое случалось. Двух блоков часто хватало чтобы пока воспроизводится один обработчик заполнил другой, но поскольку воспроизведение тогда часто шло с медленного IO (какого-нибудь CD-ROM например) то буфер можно было сделать и побольше. И никакой нужды в том антипаттерне который описал автор статьи. Сомневаюсь что дизайн у Android хуже чем дизайн древнего WinAPI. Присоединяюсь к тем кто поражается тому что людям проще полезть в исходники ОС чем написать нормально свой собственный код так чтобы он не закладывался на мягко говоря не гарантированные вещи.
                                                      +2

                                                      В Android всё точно так же: с версии 2.3 поддерживается OpenSL ES, где можно создавать очередь из буфферов, в более поздних версиях появились новые API. Просто не нужно говнокодить как в Netfilx. Я писал систему потокового вещания поверх RTP, и не было никаких проблем ни в Jelly Bean, ни в Marshmallow.

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

                                                    Самое читаемое