不思議な問題
2017年後半に、新しいセットトップボックスのNetflixアプリの問題について話し合うための電話がありました。これは、Android Open Source Project(AOSP)バージョン5.0、Lollipopに基づく新しい4K対応のAndroidTVでした。私はNetflixで数年間働き、いくつかのデバイスの発売を手伝いましたが、これは私の最初のAndroidTVでした。
デバイスを発売するヨーロッパの大手PayTV会社(オペレーター)、ファームウェアインテグレーター(インテグレーター)、システムオンチップサプライヤー(チップサプライヤー)、そして私(Netflix)の4者全員が連絡を取りました。
インテグレーターとNetflixは、Netflixの厳格な認証プロセスをすでに完了していますが、オペレーターとの内部テスト中に、会社の幹部が深刻な問題を報告しました。Netflixの再生が遅れ、ビデオが非常に短時間再生され、一時停止され、次に一時停止され、その後一時停止されました。これは常に発生するわけではありませんが、コンソールの電源を入れてから数日後に着実に遅れ始めました。彼らはビデオを見せました、それはひどいように見えました。
インテグレーターは、問題を再現する方法を見つけました。Netflixを数回起動し、再生を開始してから、UIに戻ります。彼らは、プロセスを自動化するためのスクリプトを提供しました。5分もかかることもありましたが、スクリプトは常にバグを確実に再現していました。
一方、チップサプライヤのエンジニアが根本原因を診断しました。NinjaというNetflix AndroidTVアプリがオーディオデータの配信に問題を抱えていました。ラグは、ハードウェアオーディオパイプラインのアンダーランが原因で発生します。デコーダーが忍者からのオーディオストリームの一部を待っているときに再生が停止し、新しいデータが到着すると再生が再開されました。インテグレーター、チップサプライヤー、オペレーターは全員、問題は明らかだと考えていました。そして彼らは皆私を見ました:Netflix、あなたはあなたのアプリケーションにバグがあり、あなたはそれを修正する必要があります。オペレーター代表の声で緊張が聞こえた。デバイスのリリースは遅れて予算を超えており、彼らは私からの結果を期待していました。
調査
私は懐疑的でした。この同じNinjaアプリは、スマートTVやその他のセットトップボックスを含む数百万のAndroidTVデバイスで動作します。 Ninjaにバグがある場合、なぜこのデバイスでのみ発生するのですか?
インテグレーターのスクリプトを使用して、自分で問題を再現することから始めました。私はチップ会社の同僚に連絡して、彼がこのようなものを見たことがあるかどうか尋ねました(見られませんでした)。それから私は忍者のソースコードを研究し始めました。オーディオデータの配信を担当する正確なコードを見つける必要がありました。私は多くのことを理解しましたが、再現を担当するコードに迷い始め、助けが必要でした。
2階に行って、Ninjaオーディオとビデオのパイプラインを書いたエンジニアを見つけました。彼は私にコードを紹介してくれました。その後、ようやく主要部分を理解し、自分のログを追加するために、私自身もしばらく勉強しました。 Netflixアプリは複雑ですが、簡略化された形式で、Netflixサーバーからデータを取得し、デバイス上のビデオとオーディオのデータを数秒間バッファリングしてから、ビデオとオーディオのフレームを一度に1つずつハードウェアデコーダーに配信します。
図: 1.簡素化された再生パイプライン
Netflixアプリのオーディオ/ビデオパイプラインについて少し話しましょう。 「デコーダバッファ」の前は、すべてのセットトップボックスとTVでまったく同じですが、A / Vデータをデバイスのデコーダバッファに移動するのはデバイス固有の手順です。独自のスレッドで実行されます。この手順の目的は、Netflix APIを介してオーディオまたはビデオデータの次のフレームを呼び出すことにより、デコーダーバッファをいっぱいに保つことです。忍者では、この作業はスレッドによって行われ ますアンドロイド。単純なステートマシンとさまざまな再生状態を処理するロジックがありますが、通常の再生では、ストリームは1フレームのデータをAndroid再生APIにコピーし、スレッドスケジューラに次のハンドラー呼び出しの前に15ミリ秒待機するように指示します。 Androidスレッドを作成するときに、ループのようにスレッドの再起動を要求できますが、ハンドラーを呼び出すのはAndroidスレッドスケジューラであり、独自のアプリケーションではありません。
最大60FPSでは、デバイスは16.66ミリ秒ごとに新しいフレームを表示する必要があるため、15ミリ秒後にチェックするだけで十分です。インテグレーターは問題がオーディオストリームにあると判断したため、オーディオサンプルをAndroidオーディオサービスに配信している特定のハンドラーに焦点を合わせました。
遅れがどこから来るのか、つまり遅れを理解する必要がありました。ハンドラーによって呼び出された関数のせいであると想定したので、ログメッセージをハンドラー全体に分散させ、ラグの原因となったコードを簡単に見つけようとしました。ハンドラーに問題がないことがすぐに明らかになり、再生が遅れていても数ミリ秒間動作しました。
ええ、洞察力
最後に、ボーレート、ハンドラーの呼び出し時間、ハンドラーからAndroidに制御を戻す時間の3つの数値に焦点を当てました。ログ出力を解析するスクリプトを作成し、応答を示す以下のグラフを生成しました。 図:2.オーディオストリーミング帯域幅とハンドラータイミングの視覚化オレンジ色の線は、データがストリーミングバッファーからAndroidオーディオシステムに転送される速度(バイト/ミリ秒)です。この図には、次の3つの異なるシナリオがあります。
- データレートが1ミリ秒あたり500バイトに達する、ピークが高い2つの領域。このフェーズは、再生を開始する前にバッファリングしています。ハンドラーはデータをできるだけ速くコピーします。
- — . 45 .
- , 10 . .
必然的な結論:オレンジ色の線は、チップ会社のエンジニアの結論を裏付けています。確かに、忍者はオーディオデータを配信するのに十分な速度ではありません。
その理由を理解するために、黄色と灰色の線を詳しく見てみましょう。
黄色の線は、ハンドラープロシージャ自体に費やされた時間を示し、プロシージャの開始時と終了時に記録されたタイムスタンプから計算されます。通常の領域と遅れている領域の両方で、ハンドラーの時間は同じで、約2ミリ秒です。バーストは、デバイスで実行されている他のタスクのために時間が遅くなる場合を示します。
真の根本原因
灰色の線(ハンドラーの呼び出し間の時間)は、別の話をしています。通常の再生では、ハンドラーは約15ミリ秒ごとに呼び出されます。右側のラグの場合、ハンドラーは約55ミリ秒ごとに呼び出されます。呼び出しの間に余分な40ミリ秒があり、そのような状況では再生に追いつくことができません。しかし、なぜ?
私は自分の発見をインテグレーターとチップサプライヤーに報告しましたが(Androidストリームスケジューラーのせいです!)、彼らはNetflixが問題を解決するはずだと主張し続けました。ハンドラーが呼び出されるたびに、さらに多くのデータをコピーしてみませんか?それは公正な批判でしたが、そのような行動を実行することは私が行きたくない重大な変化を伴うので、私は根本的な原因を探し続けました。 Androidのソースコードを調べたところ、Androidスレッドはユーザースペースの構成要素であり、スレッドスケジューラはシステム呼び出しを使用して同期していることがわかりました
epoll()
。パフォーマンスが
epoll()
保証されていないことはわかっていた ので、何かが体系的に彼に影響を与えているのではないかと思いました。
この時点で、次のバージョンのAndroid(Marshmallow)ですでに修正されているバグを発見した、チップサプライヤーの別のエンジニアに助けられ ました。Androidスレッドスケジューラは、アプリケーションがフォアグラウンドで実行されているかバックグラウンドで実行されているかに応じて、スレッドの動作を変更することがわかりました。バックグラウンドスレッドには、さらに40ミリ秒(40,000,000 ns)のレイテンシが割り当てられます。
Androidカーネルの深いバグは、スレッドが前面に出されたときにこの余分なタイマー値が持続することを意味していました。通常、オーディオプロセッサスレッドは、アプリケーションがフォアグラウンドにあるときに作成されましたが、忍者がまだバックグラウンドにあるときに少し前に作成されることもありました。これが発生した場合、再生が遅れ始めます。
学んだ教訓
これはAndroidプラットフォームで修正した最後のバグではありませんが、追跡するのが最も困難でした。それはNetflixアプリの外にあり、再生パイプラインの外にもあり、すべての生データはNetflixアプリ自体にエラーがあることを示していました。
物語は私が愛する私の仕事の側面を示しています:私たちのパートナーが私に投げかけるすべての問題を予測することは不可能です。そして、それらを解決するには、多くのシステムを理解し、優れた同僚と協力し、常に新しいことを学ぶように自分自身を推し進める必要があることを私は知っています。私がしていることは、実際の人々と彼らの素晴らしい製品の楽しみに直接的な影響を及ぼします。人々がリビングルームでNetflixを見るのを楽しむとき、私はそれを可能にしたチームの一員であることを知っています。