Geheimnisvolles Problem
Ende 2017 erhielt ich einen Anruf, um ein Problem mit der Netflix-App auf der neuen Set-Top-Box zu besprechen. Es war ein neues 4K-fÀhiges Android TV, das auf Android Open Source Project (AOSP) Version 5.0, Lollipop, basiert. Ich habe mehrere Jahre bei Netflix gearbeitet und dabei geholfen, einige GerÀte auf den Markt zu bringen, aber dies war mein erster Android-Fernseher.
Alle vier Parteien waren in Kontakt: ein groĂes europĂ€isches Pay-TV-Unternehmen, das das GerĂ€t auf den Markt brachte (Betreiber), ein Firmware-Integrator (Integrator), ein System-on-a-Chip-Anbieter (Chip-Anbieter) und ich (Netflix).
Der Integrator und Netflix haben den strengen Zertifizierungsprozess von Netflix bereits abgeschlossen. WĂ€hrend eines internen Tests mit dem Betreiber meldete ein leitender Angestellter des Unternehmens jedoch ein ernstes Problem: Die Netflix-Wiedergabe verzögerte sich, was bedeutet, dass das Video nur fĂŒr eine sehr kurze Zeit abgespielt, dann angehalten, dann angehalten und dann angehalten wurde. Dies war nicht immer der Fall, begann jedoch einige Tage nach dem Einschalten der Konsole stetig zu verzögern. Sie zeigten das Video, es sah schrecklich aus.
Der Integrator hat einen Weg gefunden, um das Problem zu reproduzieren: Starten Sie Netflix mehrmals, starten Sie die Wiedergabe und kehren Sie dann zur BenutzeroberflĂ€che zurĂŒck. Sie stellten ein Skript zur Automatisierung des Prozesses bereit. Manchmal dauerte es bis zu fĂŒnf Minuten, aber das Skript reproduzierte den Fehler immer zuverlĂ€ssig.
In der Zwischenzeit diagnostizierte ein Ingenieur eines Chiplieferanten die Hauptursache: Eine Netflix Android TV-App namens Ninja konnte keine Audiodaten liefern. Verzögerungen werden durch UnterlĂ€ufe in der Hardware-Audio-Pipeline verursacht. Die Wiedergabe wurde gestoppt, als der Decoder auf einen Teil des Audiostreams vom Ninja wartete, und dann fortgesetzt, wenn neue Daten eintrafen. Der Integrator, der Chiplieferant und der Betreiber hielten das Problem fĂŒr klar. Und alle haben mich angeschaut: Netflix, Sie haben einen Fehler in Ihrer App und mĂŒssen ihn beheben. Ich hörte die Spannung in der Stimme des Vertreter des Betreibers. Die Veröffentlichung des GerĂ€ts war verzögert und ĂŒber dem Budget, und sie erwarteten Ergebnisse von mir.
Ermittlung
Ich war skeptisch. Dieselbe Ninja-App lĂ€uft auf Millionen von Android-TV-GerĂ€ten, einschlieĂlich Smart-TVs und anderen Set-Top-Boxen. Wenn es in Ninja einen Fehler gibt, warum tritt er nur auf diesem GerĂ€t auf?
Ich begann damit, das Problem selbst mit einem Skript des Integrators zu replizieren. Ich kontaktierte einen Kollegen vom Chiphersteller und fragte, ob er so etwas gesehen habe (nicht gesehen). Dann fing ich an, den Ninja-Quellcode zu studieren. Es war notwendig, den genauen Code zu finden, der fĂŒr die Lieferung von Audiodaten verantwortlich ist. Ich habe viel herausgefunden, aber ich begann mich in dem Code zu verlieren, der fĂŒr die Reproduktion verantwortlich ist, und ich brauchte Hilfe.
Ich ging nach oben und fand den Ingenieur, der die Audio- und Video-Pipeline von Ninja geschrieben hatte. Er machte mich mit dem Code bekannt. Danach habe ich es selbst einige Zeit studiert, um endlich die Hauptteile zu verstehen und meine eigenen Protokolle hinzuzufĂŒgen. Die Netflix-App ist komplex, ruft jedoch auf vereinfachte Weise Daten vom Netflix-Server ab, puffert Video- und Audiodaten auf dem GerĂ€t fĂŒr einige Sekunden und liefert dann die Video- und Audio-Frames einzeln an die Hardware-Decoder.
Zahl: 1. Vereinfachte Wiedergabe-Pipeline
Lassen Sie uns einen Moment ĂŒber die Audio / Video-Pipeline in der Netflix-App sprechen. Vor dem âDecoderpufferâ ist er auf jeder Set-Top-Box und jedem Fernseher genau gleich, aber das Verschieben von A / V-Daten in den Decoderpuffer eines GerĂ€ts ist ein gerĂ€tespezifisches Verfahren. Es lĂ€uft auf einem eigenen Thread. Der Zweck dieses Verfahrens besteht darin, den Decoderpuffer voll zu halten, indem der nĂ€chste Frame von Audio- oder Videodaten ĂŒber die Netflix-API aufgerufen wird. In Ninja wird diese Arbeit von einem Thread erledigt Android. Es gibt eine einfache Zustandsmaschine und eine Logik, um die verschiedenen WiedergabezustĂ€nde zu verarbeiten. Bei der normalen Wiedergabe kopiert der Stream jedoch einen Datenrahmen in die Android-Wiedergabe-API und weist den Thread-Scheduler an, 15 ms vor dem nĂ€chsten Handler-Aufruf zu warten. Wenn Sie einen Android-Thread erstellen, können Sie den Thread wie eine Schleife neu starten lassen, aber es ist der Android-Thread-Scheduler, der den Handler aufruft, nicht Ihre eigene Anwendung.
Bei maximal 60 FPS sollte das GerĂ€t alle 16,66 ms einen neuen Frame anzeigen, sodass eine ĂberprĂŒfung nach 15 ms ohnehin ausreicht. Da der Integrator feststellte, dass das Problem im Audiostream lag, konzentrierte ich mich auf den spezifischen Handler, der die Audiobeispiele an den Android-Audiodienst lieferte.
Es war notwendig zu verstehen, woher die Verzögerungen kommen, dh die Verzögerung. Ich nahm an, dass eine vom Handler aufgerufene Funktion schuld war, also verteilte ich die Protokollnachrichten ĂŒber den gesamten Handler und konnte den Code, der die Verzögerungen verursachte, leicht finden. Es wurde schnell klar, dass mit dem Handler nichts falsch war, und es funktionierte einige Millisekunden lang, selbst wenn die Wiedergabe verzögert war.
Ja, Einsicht
Am Ende konzentrierte ich mich auf drei Zahlen: Baudrate, Handler-Anrufzeit und Zeit, um die Kontrolle vom Handler zurĂŒck auf Android zu ĂŒbertragen. Ich habe ein Skript geschrieben, um die Protokollausgabe zu analysieren, und das folgende Diagramm generiert, das die Antwort zeigt. Zahl: 2. Visualisierung der Audio- Streaming- Bandbreite und der Handler-Timings Die orange Linie ist die Rate, mit der Daten vom Streaming-Puffer zum Android-Audiosystem ĂŒbertragen werden (Bytes pro Millisekunde). In diesem Diagramm gibt es drei verschiedene Szenarien:
- Zwei Bereiche mit hohen Spitzen, in denen die Datenraten 500 Byte pro Millisekunde erreichen. Diese Phase wird gepuffert, bevor die Wiedergabe gestartet wird. Der Handler kopiert die Daten so schnell wie möglich.
- â . 45 .
- , 10 . .
Unvermeidliche Schlussfolgerung: Die orange Linie bestÀtigt die Schlussfolgerungen des Ingenieurs von der Chipherstellerin. In der Tat ist Ninja nicht schnell genug, um Audiodaten zu liefern.
Um zu verstehen, warum, schauen wir uns die gelben und grauen Linien genauer an.
Die gelbe Linie zeigt die Zeit, die in der Handlerprozedur selbst verbracht wurde, berechnet aus den Zeitstempeln, die zu Beginn und am Ende der Prozedur aufgezeichnet wurden. Sowohl in normalen als auch in nacheilenden Bereichen ist die Zeit im Handler gleich: ca. 2 ms. Bursts zeigen FÀlle an, in denen die Zeiten aufgrund anderer Aufgaben am GerÀt langsamer sind.
Wahre Grundursache
Die graue Linie - die Zeit zwischen den Aufrufen des Handlers - erzÀhlt eine andere Geschichte. Bei normaler Wiedergabe wird der Handler ungefÀhr alle 15 ms aufgerufen. Bei Verzögerungen auf der rechten Seite wird der Handler ungefÀhr alle 55 ms aufgerufen. Zwischen den Aufrufen liegen zusÀtzliche 40 ms, und in einer solchen Situation kann er nicht mit der Wiedergabe mithalten. Aber warum?
Ich habe meine Entdeckung dem Integrator und Chiplieferanten gemeldet (siehe, der Android-Stream-Scheduler ist schuld!), Aber sie bestanden darauf, dass Netflix das Problem lösen sollte. Warum nicht jedes Mal, wenn der Handler aufgerufen wird, mehr Daten kopieren? Es war eine faire Kritik, aber die Implementierung dieses Verhaltens wĂŒrde tiefgreifende Ănderungen mit sich bringen, die ich nicht anstreben wollte, und so suchte ich weiter nach der Grundursache. Ich habe mich mit dem Android-Quellcode befasst und festgestellt, dass Android-Threads ein User-Space-Konstrukt sind und der Thread-Scheduler einen Systemaufruf zum Synchronisieren verwendet
epoll()
. Ich wusste, dass die Leistung
epoll()
nicht garantiert war, also vermutete ich, dass ihn etwas systematisch beeinflusste.
In diesem Moment wurde ich von einem anderen Ingenieur eines Chiplieferanten gerettet, der einen Fehler entdeckte , der bereits in der nĂ€chsten Version von Android (Marshmallow) behoben war. Es stellt sich heraus, dass der Android-Thread-Scheduler das Verhalten von Threads Ă€ndert, je nachdem, ob die Anwendung im Vordergrund oder im Hintergrund ausgefĂŒhrt wird. Hintergrund-Threads erhalten eine zusĂ€tzliche Latenz von 40 ms (40.000.000 ns).
Ein Fehler tief im Android-Kernel bedeutete, dass dieser zusĂ€tzliche Timer-Wert beibehalten wurde, als der Thread in den Vordergrund gerĂŒckt wurde. Normalerweise wurde der Audioprozessor-Thread erstellt, als sich die Anwendung im Vordergrund befand, manchmal jedoch etwas frĂŒher, als sich der Ninja noch im Hintergrund befand. In diesem Fall wĂŒrde die Wiedergabe verzögert.
Gewonnene Erkenntnisse
Dies ist nicht der letzte Fehler, den wir auf der Android-Plattform behoben haben, aber es war am schwierigsten, ihn aufzuspĂŒren. Es befand sich auĂerhalb der Netflix-App und sogar auĂerhalb der Wiedergabe-Pipeline, und alle Rohdaten zeigten einen Fehler in der Netflix-App selbst an.
Die Geschichte zeigt einen Aspekt meines Jobs, den ich liebe: Es ist unmöglich, alle Probleme vorherzusagen, die unsere Partner auf mich werfen werden. Und ich weiĂ, dass es fĂŒr das Lösen erforderlich ist, viele Systeme zu verstehen, mit groĂartigen Kollegen zusammenzuarbeiten und sich stĂ€ndig zu bemĂŒhen, neue Dinge zu lernen. Was ich tue, wirkt sich direkt auf echte Menschen und deren Freude an einem groĂartigen Produkt aus. Wenn Leute Netflix gerne in ihrem Wohnzimmer sehen, weiĂ ich, dass ich Teil des Teams bin, das dies ermöglicht hat.