Случаят с допълнителните 40 ms при работата на Netflix

Оригиналът е на John Blair

8
3772

Приложението Netflix работи в стотици смарт телевизори, малки компютри във вид на флаш стикове и телевизионни приставки. Аз съм един от специалистите, които помагат на производителите да стартират нашите приложения на техните най-различни устройства. Тук ще се спрем на особено сложен въпрос, който попречи на излизането на една телевизионна приставка на европейския пазар.

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

В края на 2017 година ми се обадиха, за да обсъдим проблема с приложението Netflix, възникващ при една от най-новите телевизионни приставки. Това бе ново Android TV устройство с поддръжката на 4K резолюция, базирано на Android Open Source Project (AOSP) версия 5.0. Lollipop. Вече от няколко години работех в Netflix и помогнах за излизането на редица устройства на пазара. Но това бе моята първа приставка с Android TV.

На срещата присъстваха всичките четири страни: голяма европейска компания, предоставяща платена телевизия, която трябваше да представи това устройство на пазара (операторът), изпълнителят, който се занимаваше с интеграцията на фърмуерите (интеграторът), доставчикът на съответните чипове (SoC доставчикът) и аз (Netflix).

Интеграторът и Netflix вече бяха приключили строгия процес на сертификацията на Netflix, но по време на вътрешните тестове от страна на оператора, генералният директор на компанията съобщи за сериозен проблем: възпроизвеждането на Netflix сериозно лагва – тоест, видеото се възпроизвежда за съвсем кратко време, след това следва пауза, след това отново възпроизвеждане и отново пауза. Това се случва не винаги, но лагването стабилно започва няколко дни след включването на приставката. Показаха ми видеото – изглеждаше ужасно.

Интеграторът намери начин по всяко време да възпроизведе този проблем – няколко пъти стартиране на Netflix, стартиране на възпроизвеждането и след това завръщане в потребителския интерфейс. Неговите специалисти дори бяха направили скрипт за автоматизацията на този процес. Понякога това отнемаше цели пет минути, но този скрипт винаги съвсем надеждно възпроизвеждаше този странен бъг.

В същото време специалистът от компанията за доставка на чипове диагностицира основната причина: приложението Netflix за Android TV с име Ninja не успява да достави аудио данните. Лаговете са предизвикани от изпразването на буфера в хардуерния звуков конвейер. Възпроизвеждането на видеото спира когато декодерът чака от Ninja част от аудио потока и се възобновява, когато започват да постъпват нови данни. Интеграторът, доставчикът на чипове и операторът съвсем сериозно си мислеха, че проблемът е ясен. И всичките гледаха мен – Netflix – тоест, вие имате грешка във вашето приложение и вие трябва да я оправите. Усещах напрежението в гласа на изпратения представител на мобилния оператор. Излизането на това устройство на пазара се забавяше и започна да излиза извън рамките на бюджета. Всички очакваха от мен да намеря някакво решение и да постигна положителен резултат.

Разследването

Бях много скептичен. Същото това приложение Ninja работи на милиони устройства с Android TV, включително многобройни модели смарт телевизори и най-различни телевизионни приставки. Ако в Ninja има някаква грешка, то защо тя се проявява единствено в това устройство?

Започнах с това, че самостоятелно започнах да възпроизвеждам този проблем с помощта на скрипта, даден ми от интегратора. Позвъних на един колега от компанията производител на чиповете и го попитах дали не е виждал нещо подобно (не бе виждал). След това започнах да преглеждам сорс-кода на Ninja. Необходимо бе да открия точния блок с код, който отговаря за доставката на аудио данните. Много неща разбрах, но започнах да се губя в кода, който осъществява възпроизвеждането. Трябваше ми помощ.

Качих се на горния етаж при специалиста, който написа кода на аудио и видео конвейера на Ninja. Той ми показа своя код. След това и аз в продължение на няколко дни изучавах този код, за да мога да разбера как работят неговите основни модули и подпрограми, за да мога да задам прекъсвания и да създам логове. Приложението Netflix е сложно, но в опростен вид може да се каже, че то получава данните от Netflix сървъра, в продължение на няколко секунди буферира видео и аудио данните в съответното Android TV устройство, а след това препраща тези видео и аудио кадри към един от хардуерните декодери.

Опростено изобразяване на конвейера за възпроизвеждане

Нека за минутка да се спрем върху аудио/видео конвейера в приложението Netflix. Преди буфера на декодера всичко е абсолютно еднакво във всяка една телевизионна приставка и смарт телевизор, но прехвърлянето на данните в A/V буфер на декодера на устройството е процедура, специфична за всяко едно конкретно устройство и работи в собствен обособен. Задачата на тази процедура е да поддържа буфера на декодера пълен, докато извиква следващият пакет аудио или видео данни чрез API на Netflix.

В Ninja тази работа се извършва от стандартна нишка на Android. Използва се съвсем опростен алгоритъм за определяне на различните състояния, в който е реализирана несложна логика за обработката на различните състояния на възпроизвеждането, като при нормално възпроизвеждане нишката копира един кадър в API за възпроизвеждането от Android, като след това подава информация на планировчика за изчакване в продължение на 15 ms преди отново да извика манипулатора. При създаването на този Android процес е възможно нейното повторно стартиране в нещо като цикъл, но това се извършва от Android, а не от различните приложения.

При максималните 60 FPS устройството трябва да изобразява нов кадър на всеки 16,66 ms и проверката след всеки 15 ms всички случаи е предостатъчна. Ето защо интеграторът е решил, че проблемът е в аудио потока и аз се съсредоточих върху манипулатора, който доставя аудио пакетите към аудио обработката на Android.

Трябваше на всяка цена да се разбере откъде идват тези лагове – тези странни латентности. Предположих че е виновна някаква функция или подпрограма, извикана от манипулатора и поставих прекъсвания на много места в този манипулатор, като мислех че съвсем лесно ще намеря кода, който причинява тези лагове. Бързо стана ясно, че манипулаторът работи добре, в него няма нищо лошо и той дори работи в продължение на няколко милисекунди, когато възпроизвеждането на видео лагва.

Прозрението

В края на краищата аз се фокусирах върху три числа: скоростта на обмен на данните, времето за извикване на манипулатора и времето за прехвърляне на управлението от манипулатора обратно към Android. Написах скрипт за анализа на получените данни в лога и създадох графиката, показана по-долу, на който се вижда отговорът.

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

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

  1. Виждаме две области с високи пикове, където скоростта за обмен на данни достига 500 байта в милисекунда. Тази фаза е буферирането преди началото на възпроизвеждането. Манипулаторът копира данните толкова бързо, колкото изобщо може.
  2. Областта в средата показва нормалното възпроизвеждане. Аудио данните се прехвърлят със скорост около 45 байта в милисекунда.
  3. Областта на лаговете е отдясно. Това е случаят, когато аудио данните се прехвърлят със скорост около 10 байта в милисекунда. Това не е достатъчно бързо за поддържането на нормално възпроизвеждане

Неизбежният извод е, че оранжевата линия потвърждава анализа на специалиста от компанията производител на чипове. Наистина Ninja недостатъчно бързо доставя аудио данните.

Жълтата линия показва времето, което е било необходимо на самата процедура на манипулатора, изчислена според мярката за време в тази графика и е записана от самото начало и до края на процедурата. Както в нормалната, така и в областта на лагването, времето на манипулатора е еднакво – около 2 милисекунди. Пиковете показват случаите, когато това време се е оказало недостатъчно заради изпълняването на други задачи и и функции на устройството.

Истинската причина

Сивата линия показва времето между извикванията на манипулатора и тя ни подсказва нещо съвсем друго. При обикновеното възпроизвеждане манипулаторът се извиква приблизително на всеки 15 милисекунди. Но когато имаме лагове (дясната страна на диаграмата), виждаме че манипулаторът се извиква приблизително на всеки 55 милисекунди. Тук отнякъде се появяват излишни 40 милисекунди и в подобна ситуация манипулаторът по никакъв начин не може да се справи с възпроизвеждането. Но защо?

Съобщих за своето откритие на интегратора и на доставчика на чиповете (виждате ли, виновно е планирането на нишките и процесите в Android!), но те продължаваха да настояват, че проблемът трябва да бъде решен от Netflix. Защо например при всяко извикване на манипулатора не се копират повече данни? Това бе справедлива критика, но реализацията на подобно поведение би повлякла твърде дълбоки промени, каквито не исках да правя. Ето защо продължих да търся каква е първоначалната причина за появата на тези допълнителни 40 милисекунди.

Гмурнах се в сорс кода на Android и разбрах, че планирането на процесите на тази операционна система е конструкция от потребителското пространство, а планирането на процесите за синхронизация използват системното извикване epoll(). Знаех че производителността на epoll() не е гарантирана и подозирах че нещо непрекъснато оказва влияние на това извикване.

В този момент ме спаси друг специалист от компанията доставчик на чипове, който откри неприятен бъг, оправен в следващата версия на Android (Marshmallow). Оказа се че планирането на процесите и нишките в Android зависи от това, дали приложението работи на преден план или във фонов режим. На процесите във фонов режим се добавят допълнителни 40 милисекунди (40000000 наносекунди).

Тази грешка дълбоко в ядрото на Android означава, че това допълнително значение на таймера се запазва при прехвърлянето на процеса на преден план. Обикновено процесът на манипулатора за звук се създава, когато приложението работи на преден план, но понякога и малко по-рано, когато Ninja все още е във фонов режим. В тази ситуация възпроизвеждането започва да лагва.

Уроците

Това не е единственият бъг, който сме оправили в Android платформата, но определено бе най-трудният за откриване. Извън Netflix приложението и дори извън конвейера на възпроизвеждане, всички данни показваха, че грешката е в самото приложение Netflix.

Тази история показва някои аспекти на моята работа, която всъщност много обичам: невъзможно е да се предскажат или прогнозират всички проблеми, които нашите партньори ще хвърлят върху моите рамене. Много добре знам, че за намиране на подходящите решения е необходимо доброто познаване на редица системи, работа с отлични колеги и непрекъснатия стремеж да се научава нещо ново. Това което правя, оказва директно влияние на реалните хора и на полученото от тях удоволствие от един отличен продукт. Когато хората се наслаждават от гледането на Netflix в своя хол, знам че съм част от екипа, който направи всичко това възможно.

4.8 6 гласа
Оценете статията
Абонирай се
Извести ме за
guest
8 Коментара
стари
нови оценка
Отзиви
Всички коментари