
Der einfachste Weg, Videos auf einem mobilen Gerät abzuspielen, besteht darin, die Verbindung zu einem vorhandenen Player im System zu öffnen. Dies ist jedoch nicht immer effektiv.
Sie können ExoPlayer verwenden und optimieren oder sogar Ihren eigenen Video-Player nur mit Codecs und Sockets schreiben. In dem Artikel wird über die Arbeit des Streamings und der Videowiedergabe gesprochen und darüber, wie die Verzögerung beim Starten des Videos verringert, die Reaktionszeit zwischen dem Streamer und dem Betrachter verkürzt und der Stromverbrauch und die Eisenlast optimiert werden können.
Wir werden dies anhand bestimmter Anwendungen als Beispiel analysieren: Odnoklassniki Mobile Client (wo Videos abgespielt werden) und OK Live (wo Sendungen vom Telefon auf 1080p gestreamt werden). Es gibt keine Meisterkurse zum Abspielen eines Videos anhand von Codebeispielen. Die Geschichte konzentriert sich darauf, wie das Video von innen aussieht und wie Sie mit Kenntnis der allgemeinen Architektur von Videoplayern und Video-Streaming jedes System verstehen und verbessern können.
Das Material basiert auf der Abschrift des Berichts von
Alexander Tobol (
@alatobol ) und
Ivan Grigoriev (
@ivan_a ) von der
Mobius- Konferenz.
Eintrag
Für den Anfang - ein paar Zahlen zum Video in Odnoklassniki.
Der durchschnittliche durchschnittliche tägliche VOD-Verkehr (Video on Demand) beträgt mehr als eineinhalb Terabit pro Sekunde und bei Live-Übertragungen mehr als 3 Terabit pro Sekunde.
Jetzt gibt es in OK mehr als 870 Millionen Videoaufrufe pro Tag, von denen mehr als die Hälfte von Mobilgeräten stammen.

Wenn Sie sich die Geschichte des Streamings ansehen, erschien 2007 ein mobiles Video auf YouTube. Wir sind später in diesen Zug gestiegen, aber 2014-2015 hatten wir bereits eine 4K-Videowiedergabe auf Mobilgeräten und in den letzten Jahren haben wir unsere Player aktiv weiterentwickelt. Darüber und das Gespräch wird gehen.
Der zweite Trend, der 2015 bei Periscope auftrat, war die Ausstrahlung von Telefonen. Wir haben unsere OK Live-Anwendung gestartet, mit der Sie sogar Full HD-Videos über Mobilfunknetze streamen können. In der zweiten Hälfte des Materials werden wir auch über Streaming sprechen.
Wir werden uns nicht mit der API für die Arbeit mit Videos befassen, sondern tauchen jetzt tief ein und versuchen herauszufinden, was im Inneren passiert.

Wenn Sie ein Video mit einer Kamera aufnehmen, gelangt es zum Codec, von dort zum Socket und dann zum Server (unabhängig davon, ob VOD oder Live). Und dann verteilt der Server es in umgekehrter Reihenfolge an das Publikum.
Beginnen wir mit dem KPI-Player. Was wollen wir von ihm?
- Schneller erster Frame. Benutzer möchten nicht auf den Start der Wiedergabe warten.
- Fehlende Pufferung. Niemand mag es, in einen Oberkörper zu laufen.
- Hohe Qualität. Als es noch fast keinen 4K-Inhalt gab, haben wir bereits 4K-Unterstützung für „Outgrowing“ aktiviert: Wenn Sie den Player dafür ausschalten und die Leistung ermitteln, wird 1080p auch auf schwachen Geräten perfekt wiedergegeben.
- UX-Anforderungen. Wir brauchen das Video, um es beim Scrollen auf dem Band abzuspielen, und für das Band müssen wir das Video vorab abrufen.
Auf diese Weise gibt es viele Probleme. Der Stream für 4K-Videos ist groß, und wir arbeiten auf Mobilgeräten, bei denen Probleme mit dem Netzwerk auftreten, verschiedene Funktionen von Videoformaten und Containern auf verschiedenen Geräten vorhanden sind und die Geräte selbst ebenfalls zu einem Problem werden können.
Wo startet das Video Ihrer Meinung nach schneller, unter iOS oder Android?
In der Tat ist jede Antwort richtig: Es kommt darauf an, was, wo und wie man spielt. Wenn wir eine Region Russlands mit einem nicht so guten Netzwerk nehmen, werden wir sehen, dass AVPlayer bei etwa 800 Millisekunden startet. Mit demselben Netzwerk wird ExoPlayer auf Android, das ein anderes Format spielt, in 660 ms gestartet. Und wenn Sie Ihren Player auf iOS machen, kann er noch schneller laufen.

Es gibt eine Nuance darin, dass wir den Durchschnitt für Benutzer messen und die durchschnittliche Leistung von iOS-Geräten höher ist als bei Android.
Der erste Teil des Materials wird theoretisch sein: Wir werden lernen, was Video ist und wie die Architektur eines Live-Players aussieht. Und im zweiten Teil vergleichen wir die Spieler und sprechen darüber, wann Sie Ihre eigenen schreiben sollen.
Teil eins
Was ist Video?
Beginnen wir mit den grundlegendsten. Video ist 60 oder 24 Bilder pro Sekunde.
Das Speichern mit einem vollständigen Satz von Bildern ist natürlich ziemlich teuer. Daher werden sie folgendermaßen gespeichert: Einige Frames werden als Referenzframes (I-Frames) bezeichnet, während andere (B-Frames und P-Frames) als „Diffs“ bezeichnet werden. Tatsächlich haben Sie eine JPG-Datei und bestimmte Änderungen daran.

Es gibt auch das Konzept der GOP (Bildgruppe) - dies ist ein unabhängiger Satz von Rahmen, der mit einem Referenzrahmen beginnt und mit einem Satz von Unterschieden fortgesetzt wird. Es kann unabhängig gespielt, ausgepackt und so weiter werden. Wenn Sie einen Opornik in der Gruppe verloren haben, sind die verbleibenden Frames dort nicht mehr relevant.
Es gibt viele Codierungsalgorithmen, Transformationsmatrizen, Bewegungssuche und dergleichen - darin unterscheiden sich Codecs.
Codec-Leistung

Der Klassiker H.264 ist seit 2003 bekannt und hat sich gut entwickelt. Wir werden seine Wirksamkeit als Basis nehmen. Er arbeitet und spielt überall. Es bietet Hardware-Unterstützung für CPU / GPU (sowohl unter iOS als auch unter Android). Dies bedeutet, dass es entweder einen speziellen Coprozessor gibt, der ihn codieren kann, oder integrierte Befehlssätze, mit denen Sie dies schnell tun können. Im Durchschnitt bietet die Hardwareunterstützung eine bis zu 10-mal schnellere Leistung und spart Akkulaufzeit.
Im Jahr 2010 erschien VP8 von Google. In Bezug auf die Effizienz unterscheidet es sich nicht von H.264. Nun, tatsächlich ist die Effektivität des Codecs eine sehr kontroverse Sache. In der Stirn wird es als Verhältnis des Originalvideos zum komprimierten Video gemessen, aber es ist klar, dass es unterschiedliche Videoartefakte gibt. Daher bieten wir einen
Link zu detaillierten Vergleichen von Codecs der Moskauer Staatlichen Universität. Hier beschränken wir uns jedoch auf die Tatsache, dass VP8 auf eine Softwareorganisation ausgerichtet ist, Sie es überall hin mitnehmen können und es normalerweise als Fallback verwendet wird, wenn keine native H.264-Unterstützung vorhanden ist.
2013 erschien eine neue Generation von Codecs - H.265 (HEVC) und VP9. Der H.265-Codec erhöht die Effizienz um 50%. Bei Android-Videos können sie jedoch nicht codiert werden. Der Decoder wurde nur mit Android 5.0+ angezeigt. Aber unter iOS gibt es Unterstützung.
Es gibt eine Alternative zu H.265 - VP9. Trotzdem, aber von Google unterstützt. Nun, V9 ist YouTube und H.265 ist Netflix. Jeder hat seine eigenen Besonderheiten: Einer funktioniert nicht mit iOS, der andere hat Probleme mit Android. Am Ende bleiben viele auf H.264.
In Zukunft wird uns der AV1-Codec versprochen, er hat bereits eine Software-Implementierung und seine Effizienz ist 35% höher als die der 2013er Codecs. Jetzt in Chrome und Firefox verfügbar, und im Jahr 2020 verspricht Google Hardware-Unterstützung - ich denke, wir werden höchstwahrscheinlich alle darauf umsteigen.
Schließlich kündigten sie kürzlich den H.266 / JVEC-Codec an und sagten, dass alles besser und schneller sein werde.
Das Hauptmuster: Je höher die Effizienz des Codecs ist, desto mehr Rechenressourcen werden von den Geräten benötigt.
Im Allgemeinen nimmt standardmäßig jeder H.264, und für bestimmte Geräte kann dies kompliziert sein.
Qualität, Auflösung und Bitrate
Im Jahr 2019 werden Sie niemanden mit adaptiver Qualität überraschen: Benutzer laden Videos in einer Qualität hoch oder streamen sie, und wir schneiden eine Reihe verschiedener Qualitäten ab und senden die am besten geeigneten an Geräte.
In diesem Fall muss die Videoauflösung mit der Bitrate korrelieren. Wenn die Auflösung verdoppelt wird, sollte sich auch die Bitrate verdoppeln:

Wenn Sie eine große Auflösung mit einer niedrigen Bitrate komprimieren oder umgekehrt, kommt es natürlich entweder zu Artefakten oder zu einem nutzlosen Brennen der Bitrate.
Wie ist die Bitrate des codierten Videos mit der ursprünglichen Informationsmenge zu vergleichen? Auf einem 4K-Bildschirm können wir fast 6 Gbit / s an Informationen wiedergeben (wenn Sie alle Pixel und ihre Frequenz mit 60 Bildern pro Sekunde zählen), während die Codec-Bitrate 50 Mbit / s betragen kann. Das heißt, der Codec komprimiert das Video bis zu 100 Mal.
Liefertechnik
Sie haben Audio und Video mit einigen Codecs gepackt. Wenn Sie es einfach zu Hause lassen, können Sie alle Audio- und Videodaten addieren, indem Sie einen kleinen Index hinzufügen, der angibt, ab welcher Sekunde Audio und Video gestartet werden. Das Video kann jedoch nicht auf das Telefon übertragen werden. Für das Online-Streaming zum Viewer gibt es zwei Hauptklassen von Protokollen: Streaming und Segmentierung.

Das Streaming-Protokoll impliziert, dass Sie auf dem Server, auch auf dem Client, einen Status haben und Daten senden. Der Server kann beispielsweise die Qualität anpassen. Sehr oft ist dies eine UDP-Verbindung.
Solche Protokolle sind für den Server sehr komplex und schwer zu liefern. Für stark geladene Übersetzungen verwenden wir segmentierte Protokolle, die auf HTTP basieren, von Nginx und CDN zwischengespeichert werden können und viel einfacher zu verteilen sind. Und der Server ist für nichts verantwortlich und in diesem Fall zustandslos.
Wie die Segmentlieferung aussieht: Wir schneiden das vorhandene Video in Segmente und begleiten sie mit einem Header für Audio und Video, MPEG-TS und MP4 als Beispiel für den Transport. Am Telefon geben wir ein Manifest mit Informationen darüber, wo und für welche Qualität das Segment liegt, und dieses Manifest kann regelmäßig aktualisiert werden.
In der Vergangenheit liefert Apple über HLS und Android über DASH. Mal sehen, wie sie sich unterscheiden.
Beginnen wir mit dem älteren HLS, es hat ein Manifest, das alle verfügbaren Qualitäten beschreibt - niedrig, mittel, hoch und so weiter. Es gibt Bitraten dieser Eigenschaften, so dass der Spieler sofort die richtige auswählen kann. Er wählt die Qualität und erhält ein verschachteltes Manifest mit einer Liste von Links zu Segmenten. Die Dauer dieser Segmente wird ebenfalls angegeben.

Hier gibt es eine interessante Funktion: Um das erste Bild abzuspielen, müssen Sie zwei zusätzliche Roundtrips durchführen. Bei der ersten Anforderung erhalten Sie das Hauptmanifest, die zweitverschachtelten Manifeste und greifen erst dann auf die Daten selbst zu, was nicht sehr gut ist.

Die zweite Schwierigkeit: HLS wurde für die Arbeit im Internet über HTTP entwickelt, aber der ältere MPEG-2-Transportstrom wurde als Container für Videodaten ausgewählt, der für ganz andere Zwecke entwickelt wurde: die Übertragung eines Signals von einem Satelliten in verrauschten Kanälen. Als Ergebnis erhalten wir zusätzliche Header, die im Fall von HLS völlig nutzlos sind und nur zusätzlichen Aufwand verursachen.

Fügen Sie den Netzwerk-Overhead und die Komplexität des Parsens hinzu: Wenn Sie versuchen, 4K in DASH und HLS in Chrome zu spielen, werden Sie den Unterschied spüren, wenn Ihr Computer mit HLS-Paketen „abhebt“.
Apple versucht dies zu lösen. 2016 kündigten sie die Möglichkeit der Verwendung von fragmentiertem MPEG-4 an. Es gab eine gewisse Unterstützung für DASH in HLS, aber das zusätzliche RTT und seine Funktionen verschwanden nicht.

DASH sieht etwas einfacher aus: Sie haben ein Manifest mit allen darin enthaltenen Qualitäten, und jede Qualität besteht aus einer Reihe von Segmenten. Sie können ein Segment spielen, um in einer Qualität zu spielen, und dann verstehen, dass die Geschwindigkeit gestiegen ist, vom nächsten Segment, um zu einem anderen zu wechseln. Alle Segmente beginnen immer mit Referenzrahmen, sodass umgeschaltet werden kann.
Hier ist ein kleiner Teller über die Auswahl:

In HLS sind historisch unterstützte Videocodecs nur H.264, in MPEG-DASH können Sie jeden schieben. Das Hauptproblem von HLS ist eine zusätzliche Rundreise zu Beginn, die sowohl auf iOS als auch auf Android mit 4.0 gut funktioniert. Und DASH wird hauptsächlich von Google (Chrome und Android) unterstützt und kann nicht unter iOS gespielt werden.
Player-Architektur
Wir haben das Video mehr oder weniger aussortiert. Nun wollen wir sehen, wie jeder Player aussieht.

Beginnen wir mit dem Netzwerkteil: Beim Starten eines Videos folgt der Player dem Manifest, wählt irgendwie die Qualität aus, folgt dann dem Segment, lädt es herunter, muss dann die Frames dekodieren, versteht, dass sich genügend Frames im Puffer für die Wiedergabe befinden, und startet dann die Wiedergabe.
Die allgemeine Architektur des Spielers:

Es gibt einen Netzwerkteil, einen Socket, von dem die Daten stammen.
Danach - ein Demultiplexer oder etwas, das Audio- und Videostreams von einem Transport (HLS / DASH) erhält. Sie sendet sie an die entsprechenden Codecs.
Codecs dekodieren Video und Audio, und dann passiert das Interessanteste: Sie müssen synchronisiert werden, damit Video und Audio gleichzeitig abgespielt werden. Hierfür gibt es verschiedene Mechanismen, die auf Zeitstempeln basieren.
Dann müssen Sie es irgendwo rendern - in Textur, Oberfläche, GL oder Metall, überall.
Und am Eingang befindet sich eine Ladesteuerung, die die Daten lädt und den Puffer steuert.
Wie sieht die Laststeuerung bei allen Spielern aus? Es gibt einige Datenmengen, die heruntergeladen werden müssen. Der Spieler wartet, bis er heruntergeladen wurde, beginnt dann zu spielen und wir laden weiter herunter. Wir haben die maximale Puffergrenze, bei deren Erreichen der Download stoppt. Danach sinkt während der Wiedergabe die Datenmenge im Puffer - und es gibt einen Mindestrand, an dem das Laden beginnt. Das alles lebt also auch:

Wie sieht der Hauptschleifen-Thread aus? Spieler sind mit dem Konzept des „Tick Threads“ vertraut, es scheint hier zu sein. Es gibt einen Teil, der für das Netzwerk verantwortlich ist und alles in einem Puffer stapelt. Es gibt einen Extraktor, der es entpackt und an die Codecs sendet, wo sein Zwischenpuffer und dann zum Rendern gehen. Und Sie haben ein Häkchen, das sie verschiebt und steuert und sich mit der Synchronisation befasst.

Draußen haben Sie eine Anwendung, die einige Befehle über eine Nachrichtenwarteschlange sendet und einige Informationen über Listener empfängt. Und manchmal kann ein Gegendruck auftreten, der die Qualität verringert - beispielsweise in einer Situation, in der Ihr Puffer leer ist oder der Render nicht mehr zurechtkommt (z. B. werden Drop-Frames angezeigt).
Schätzer
Bei der Anpassung stützt sich der Player auf zwei Hauptparameter: Netzwerkgeschwindigkeit und Datenpuffer.
So sieht es aus: Zunächst wird eine bestimmte Qualität reproduziert, beispielsweise 720p. Sie haben einen wachsenden Puffer, der immer mehr zwischengespeichert wird. Dann wächst die Geschwindigkeit, Sie verstehen, dass Sie noch mehr herunterladen können, der Puffer wächst. Und in diesem Moment verstehen Sie, dass Sie einige Grenzen des Mindestpuffers überschreiten, wenn Sie die folgende Qualität ausprobieren können.

Es ist klar, dass Sie es sorgfältig versuchen müssen: Es gibt auch einen Schätzer, der angibt, ob Sie diese Qualität in Bezug auf die Netzwerkgeschwindigkeit erreichen können. Wenn Sie in diese Bewertung passen und der Pufferbestand dies zulässt, wechseln Sie beispielsweise zu 1080p und spielen weiter.
Überdruckschutz
Bei uns erschien sie im Laufe der Zeit durch Versuch und Irrtum. Die Notwendigkeit dafür entsteht, wenn Sie Ihre Ausrüstung leicht überlasten.
Es gibt eine Situation, in der das Netzwerk während der Wiedergabe stumpf wird oder die Ressourcen im Backend zur Neige gehen. Wenn der Player die Wiedergabe fortsetzt, beginnt er aufzuholen.
In diesem Moment hat sich im Manifest des Spielers eine riesige Anzahl von Segmenten angesammelt, die alle schnell auf einmal heruntergeladen werden und wir bekommen einen "Verkehrsstoß". Die Situation kann sich verschlimmern, wenn auf den Clients eine Zeitüberschreitung auftritt und der Player beginnt, die Daten erneut abzufragen. Daher ist es unbedingt erforderlich, einen Gegendruck im System vorzusehen.
Der erste einfache Weg, den wir natürlich verwenden, ist der Throttler auf dem Server. Er versteht, dass der Verkehr endet, die Qualität verringert und die Kunden absichtlich verlangsamt, um nicht genau diesen Schlag zu bekommen.

Dies wirkt sich jedoch nicht sehr gut auf die Schätzer aus. Sie können die gleichen "Wendungen" erzeugen. Unterstützen Sie daher nach Möglichkeit die Entfernung von Qualität aus dem Manifest. Dazu müssen Sie entweder das Manifest regelmäßig aktualisieren oder, wenn es einen Feedback-Kanal gibt, den Befehl zum Entfernen der Qualität geben, und der Player wechselt automatisch zu einem anderen, niedrigeren.
Spieler
Unter iOS gibt es nur nativen AVPlayer, unter Android gibt es jedoch eine Auswahl. Es gibt einen nativen MediaPlayer, aber einen Open Source Java-basierten ExoPlayer, den Anwendungen "mitbringen". Was sind ihre Vor- und Nachteile?
Vergleichen Sie alle drei:

Im Fall von adaptivem Streaming spielt ExoPlayer DASH / HLS und verfügt über viele erweiterbare Module für andere Protokolle, während AVPlayer immer schlechter wird.
Die Unterstützung von Betriebssystemversionen ist im Prinzip für alle überall geeignet.
Beim Vorabrufen wissen Sie, dass Sie nach dem Ende eines Videos Folgendes auf dem Band abspielen und vorab laden möchten.
Es gibt ein Problem mit Bugfixes von nativen Spielern. Im Fall von ExoPlayer rollen Sie es einfach in eine neue Version Ihrer Anwendung, aber in nativem AVPlayer und MediaPlayer wird der Fehler erst in der nächsten Betriebssystemversion behoben. Wir sind schmerzhaft darauf gestoßen: In iOS 8.01 wurde unser Video schlecht abgespielt, in iOS 8.02 funktionierte das gesamte Portal nicht mehr, in 8.03 funktionierte alles wieder. Und in diesem Fall hing nichts von uns ab. Wir saßen nur da und warteten darauf, dass Apple die nächste Version herausbrachte.
Das ExoPlayer-Team spricht über die Ineffizienz des Energieverbrauchs bei Audio. Es gibt allgemeine Empfehlungen von Google: Um Audio abzuspielen, verwenden Sie MediaPlayer für alles andere Exo.
Verstanden werden wir ExoPLayer mit DASH für Videos unter Android und AVPlayer mit HLS unter iOS verwenden.
Schneller erster Frame
Denken Sie auch hier an die Zeit bis zum ersten Frame. So sieht es unter iOS HLS aus: zuerst RTT hinter dem Manifest, dann eine weitere RTT hinter dem verschachtelten Manifest, erst dann - das Segment abrufen und spielen. In Android ist eine RTT weniger, es beginnt etwas besser.

Puffergröße
Nun beschäftigen wir uns mit den Puffern. Wir haben eine Mindestmenge an Daten, die heruntergeladen werden müssen, bevor wir mit dem Spielen beginnen. In AVPlayer wird dieser Wert mit AVPlayerItem PreferredForwardBufferDuration konfiguriert.

Unter Android verfügt ExoPlayer über viel mehr Konfigurationsmechanismen. Es gibt denselben Mindestpuffer, der zum Starten benötigt wird. Es gibt jedoch auch eine separate Einstellung für das Zurückweisen (wenn Ihr Netzwerk ausfiel, gingen die Daten aus dem Puffer aus und es wurde zurückgegeben):

Was ist der Gewinn? Wenn Sie ein gutes Netzwerk haben, starten Sie schnell und kämpfen um einen schnellen ersten Frame. Zum ersten Mal können Sie versuchen, ein Risiko einzugehen. Wenn das Netzwerk jedoch während der Wiedergabe unterbrochen wird, müssen Sie offensichtlich mehr Pufferung anfordern, um während der Zurückweisung wiederzugeben, damit kein wiederholtes Problem auftritt.
Originalqualität

HLS unter iOS hat ein cooles Problem: Es beginnt immer mit der ersten Qualität im m3u8-Manifest. Was du ihm zurückgibst, wird beginnen. Und nur dann wird die Download-Geschwindigkeit gemessen und die Wiedergabe in normaler Qualität gestartet. Es ist klar, dass dies nicht erlaubt sein sollte.
Logische Optimierung - Qualität neu sortieren. Entweder auf dem Server (durch Hinzufügen eines zusätzlichen Parameters zur bevorzugten Qualität wird das Manifest neu sortiert) oder auf dem Client (erstellen Sie einen Proxy, der dies für Sie erledigt).
Und unter Android gibt es dafür einen DefaultBandwidthMeter-Parameter. Es gibt einen Wert an, der die Standardbandbreite Ihres Bandes berücksichtigt.

So funktioniert es: Der Code enthält eine riesige Konstantentabelle, und die Parameter sind einfach - das Land (Region) und die Art der Verbindung (Wi-Fi, 2G, 3G, 4G). Was sind die Bedeutungen? Wenn Sie beispielsweise über WLAN verfügen und sich in den USA befinden, beträgt Ihre anfängliche Bandbreite 5,6 Mbit / s. Und wenn 3G 700 kbps ist.
Es ist ersichtlich, dass 4G nach Schätzungen von Google in Russland 2-3 Mal schneller ist als in Amerika.
Es ist klar, dass Russland ein großes Land ist, und ein solches Setup hat uns überhaupt nicht gepasst.
Wenn Sie dies einfach tun möchten, merken Sie sich daher den vorherigen Wert für das aktuelle Netzwerk, subtrahieren Sie für alle Fälle eine Einheit und starten Sie es.Wenn Sie eine große Anwendung haben, die Videos auf der ganzen Welt wiedergibt, sammeln Sie Statistiken zu Subnetzen und empfehlen Sie vom Server die Qualität, von der aus Sie beginnen sollen. Beachten Sie, dass es nach dem Puffern ratsam ist, den Wert des Puffers zu erhöhen (unter Android ist dies problemlos zulässig).So beschleunigen Sie den Rücklauf
(seek), , . , , , .

, , - . iOS, , , , ( , , ).
ExoPlayer 2.7.0 , , « ». . , .

( , ), - , Android prepare(mediaSource), seekTo(). , , , . — :

, ( , ), . ( 100 ), , .

iOS , Android legacy-.
TextureView. , , , , UI. — .
SurfaceView. , . Android- . YouTube , .
GLSurfaceView — . , .

: , ExoPlayer, 23%. «» 10%. 4% . 4% — , .
: Android
- MediaPlayer , ExoPlayer
- start, seek, swap
- ,
- view
: iOS
iOS :
- RTT HLS AVPlayer
- AVPlayer#pause
- — , iOS
DASH-, « live-». :
- cURL GCDAsyncSocket
- AVAssetReader,
- CADisplayLink
- AVSampleBufferDisplayLayer
, . 28%, «» 6%. , HLS DASH 100 /, 6%.
iOS :
- start seek
- HLS over Fragmented mp4
- DASH-
, .
:
, , .
- ( mp4)
- (ExoPlayer, AVPlayer)
- firstFrame, seek, emptyBuffer
- ( )
- - , . 4, : performance, , .
— .
:
, ?

API . API iOS Android, — , .
: - wrapper , POSIX-, , .
??
— . , , .
: , , . — low latency.
, — . .
— 4K. , , . , 30 , . , .
, , , . ( 100 ).
- , , .
. 100 , . , 300 kbps FullHD- 480p, FullHD . , : , , overhead-. .
:

, , . , - , , .
MediaCodec VideoToolbox ( ). Server Transcoder.
— , .
, . , , reliability — ( ), throughput — ( ) low latency — ( ).

, . , - .
, : RTMP WebRTC — , OKMP — .
, RTMP TCP, — UDP.
RTMP
Was gibt er? In gewisser Weise ist dies ein Standard, der von allen Diensten unterstützt wird - YouTube, Twitch, Flash, OK. Sie verwenden es, damit Benutzer Live-Streams hochladen können. Wenn Sie einen Live-Stream an einen Drittanbieter streamen möchten, müssen Sie höchstwahrscheinlich mit RTMP arbeiten.
Die minimale Verzögerung, die wir von einem Bandlaufwerk zu einem Player erreichen konnten, beträgt 300 ms, aber dies ist in einem idealen Netzwerk bei schönem Wetter. Wenn wir ein echtes Netzwerk haben, wächst die Verzögerung normalerweise auf 2-3 Sekunden, und wenn alles im Netzwerk schlecht ist, kann sie auf mehrere zehn Sekunden anwachsen.
RTMP unterstützt das Ändern der Auflösung und der Bitrate im laufenden Betrieb (die anderen genannten Protokolle sind dieselben, es gibt jedoch fehlerhafte Informationen zu RTMP, dass keine Änderungen im laufenden Betrieb vorgenommen werden).
Von den Minuspunkten: Basierend auf TCP (wir werden später erklären, warum dies schlecht ist) ist die Verzögerung unkontrolliert.
Wenn Sie sich das Dreieck ansehen, kann RTMP keine geringe Latenz geben. Es kann erhalten werden, aber überhaupt nicht garantiert.
Außerdem ist RTMP ein kleiner Mist: Es unterstützt keine neuen Codecs, da Adobe dies nicht tut und die Dokumentation ziemlich alt und krumm ist.

Warum ist TCP nicht für Live-Übertragungen geeignet? TCP gibt eine Zustellgarantie: Die Daten, die Sie in den Socket eingeben, werden genau in der Reihenfolge und in der Form geliefert, in der Sie sie dort abgelegt haben. Nichts wird fallen gelassen oder neu angeordnet. TCP wird dies entweder tun oder sterben. Dies bedeutet jedoch, dass eine Verzögerungsgarantie ausgeschlossen ist - er kann keine alten Daten löschen, die möglicherweise bereits gesendet werden müssen. Der Puffer, die Rückstände usw. beginnen zu wachsen.

Zur Veranschaulichung das Head of Line-Blockierungsproblem. Es findet sich nicht nur im Streaming, sondern auch in vielen anderen Fällen.
Was ist das? Wir haben einen anfangs leeren Empfängerpuffer. Wir empfangen Daten von irgendwoher: viele Daten und viele IP-Pakete. Wir haben das erste IP-Paket empfangen, und auf dem Empfänger können wir mit der Methode recv () dieses Paket subtrahieren, Daten abrufen, verlieren, rendern. Aber dann ging plötzlich das zweite Paket verloren. Was passiert als nächstes?
Um ein verlorenes IP-Paket wiederherzustellen, muss TCP erneut übertragen. Dazu müssen Sie RTT ausgeben, während die erneute Übertragung ebenfalls verloren gehen kann, und wir werden in Zyklen vorgehen. Wenn es viele Pakete gibt, wird dies definitiv passieren.
Danach kommen viele Daten, die wir nicht lesen können, weil wir stehen und auf das zweite Paket warten. Obwohl er einen Broadcast-Frame zeigte, der vor fünf Minuten passiert ist und nicht mehr benötigt wird.
Um ein anderes Problem zu verstehen, schauen wir uns die RTMP-Anpassung an. Wir machen die Anpassung auf der Absenderseite. Wenn das Netzwerk Daten nicht mit der Geschwindigkeit stopfen kann, mit der sie in den Socket gestellt werden, wird der Puffer gefüllt und der Socket sagt EWOULDBLOCK oder wird blockiert, wenn in diesem Moment die Blockierung verwendet wird.

Erst in diesem Moment verstehen wir, dass wir Probleme haben und die Qualität reduzieren müssen.
Angenommen, wir haben ein Netzwerk mit einer bestimmten Geschwindigkeit von 4 Mbit / s. Wir haben eine Sockelgröße von 250 KB gewählt (entsprechend 0,5 Sekunden bei unserer Geschwindigkeit). Plötzlich fiel das Netzwerk zehnmal aus - dies ist eine normale Situation. Wir haben 400 kbps. Der Puffer füllte sich schnell in einer halben Sekunde und erst in diesem Moment verstehen wir, dass wir herunterschalten müssen.

Das Problem ist jedoch, dass wir einen 250-KB-Puffer haben, der 5 Sekunden lang übertragen wird. Wir sind bereits völlig im Rückstand: Wir müssen zuerst die alten Daten verschieben, und erst dann werden neue und angepasste Daten die Echtzeit einholen.
Was zu tun ist? Hier ist unser „Dreieck der Kompromisse“ nur relevant.

- Wir können den Senderpuffer reduzieren, anstatt 0,5 Sekunden - 0,1 Sekunden. Wir verlieren jedoch Bandbreite, da wir häufig in Panik geraten und herunterschalten. Darüber hinaus funktioniert TCP so, dass, wenn Sie einen Senderpuffer kleiner als RTT setzen, Sie nicht die volle Bandbreite des Kanals nutzen können, dieser um ein Vielfaches abnimmt.
- Wir können den Empfängerpuffer erhöhen. Mit einem großen Puffer, Daten ankommen, können wir einige Unregelmäßigkeiten innerhalb des Puffers ausgleichen. Aber natürlich verlieren wir die geringe Latenz, da wir sofort einen 5-Sekunden-Puffer eingerichtet haben.
- Wir können alte Daten aggressiv löschen. In TCP besteht die einzige Möglichkeit darin, die Verbindung zu trennen und neu zu erstellen. Wir verlieren an Zuverlässigkeit, weil der Spieler zu diesem Zeitpunkt nichts zu zeigen hat.
WebRTC
Dies ist eine C ++ - Bibliothek, die bereits Erfahrungen berücksichtigt und auf UDP ausgeführt wird. Builds unter iOS, Android, ist in Browser integriert, unterstützt HTML5. Da es für P2P-Anrufe gesperrt ist, beträgt die Verzögerung 0,1-1 Sekunden.

Von den Minuspunkten: Dies ist eine monolithische Bibliothek mit einer Fülle von Vermächtnissen, die nicht entfernt werden können. Aufgrund seines Fokus auf P2P-Aufrufe wird außerdem eine geringe Latenz priorisiert. Es scheint, dass wir das wollten, aber dafür opfert sie andere Parameter. Und es gibt keine Einstellungen zum Ändern der Prioritäten.
Es sollte auch berücksichtigt werden, dass die Bibliothek für eine Konversation zwischen zwei Clients ohne Server clientorientiert ist. Sie müssen nach einem Server eines Drittanbieters suchen oder einen eigenen schreiben.
Was soll ich wählen - RTMP oder WebRTC? Wir haben beide Protokolle implementiert und in verschiedenen Szenarien getestet. In der Grafik hat WebRTC eine geringe Verzögerung, aber einen geringen Durchsatz, während RTMP das Gegenteil hat. Und zwischen ihnen ist ein Loch.
Und wir wollten ein Protokoll erstellen, das diese Lücke vollständig abdeckt und sowohl im WebRTC- als auch im RTMP-Modus funktioniert. Sie machten und nannten es OKMP.

Okmp
Dies ist ein flexibles Protokoll für UDP.
Unterstützt Multiplexing. Was bedeutet das: Es gibt mehrere Kanäle innerhalb der Sitzung (im Fall von OK Live - der Manager, Audio und Video). Innerhalb jedes Kanals wird garantiert, dass die Daten in einer bestimmten Reihenfolge geliefert werden (es wird jedoch nicht garantiert, dass sie selbst geliefert werden), und die Reihenfolge zwischen den Kanälen wird nicht garantiert, da dies nicht wichtig ist.
Was gibt es? Erstens gab es uns die Möglichkeit, Kanäle zu priorisieren. Wir können sagen, dass der Steuerkanal eine hohe Priorität hat, der Ton mittel ist und das Video niedrig ist. Video-Jitter und ungleichmäßige Videoübertragung sind leichter zu verschleiern, und der Benutzer hat weniger Probleme mit Videoproblemen als mit unangenehmem Stottern von Audio.

Zusätzlich bietet unser Protokoll eine optionale Liefergarantie. Wir können sagen, dass wir auf einem bestimmten Kanal im TCP-Modus mit garantierter Zustellung arbeiten und im Rest einige Drops zulassen.
Dank dessen kann auch eine Verzögerungsgarantie gegeben werden: Es gibt keine Garantie für eine Verzögerung auf dem TCP-Kanal, aber auf den anderen, auf denen Tropfen zulässig sind, wird ein Schwellenwert festgelegt, nach dem die Daten zu fallen beginnen und wir die Lieferung alter Daten einstellen.
Für Audio beträgt dies beispielsweise 1 Sekunde und für Video 0,5 Sekunden. Warum ist die Schwelle anders? Dies ist ein weiterer Priorisierungsmechanismus. Da es für uns wichtiger ist, dass das Audio flüssig ist, lassen wir das Video zunächst fallen.
Unser Protokoll ist flexibel konfiguriert: Es gibt keinen einzigen Betriebsmodus. Wir ändern die Einstellungen im laufenden Betrieb, um in den gewünschten Modus zu wechseln, ohne dass dies für den Benutzer sichtbar ist. Warum? Zum Beispiel für dieselben Videoanrufe: Wenn ein Videoanruf in einem Stream gestartet wird, übertragen wir ihn leise in den Modus mit niedriger Latenz. Und dann zurück zum Durchsatzmodus für maximale Qualität.
Implementierungsschwierigkeiten

Wenn Sie sich entscheiden, Ihr Protokoll in UDP zu schreiben, werden Sie natürlich auf einige Probleme stoßen. Mit TCP erhalten wir Mechanismen, die wir selbst auf UDP schreiben müssen:
- Packen / Entpacken. Sie müssen die Daten in Pakete mit einer Größe von ca. 1,5 KB schneiden, damit sie in das MTU-Netzwerk passen.
- Nachbestellung. Sie senden Pakete in einer Reihenfolge, und sie werden unterwegs neu angeordnet und kommen in einer anderen. Um dies zu überwinden, müssen Sie die Reihenfolge mit der Paketnummer festlegen und diese am Empfänger neu anordnen.
- Verluste. Natürlich gibt es Verluste. Wenn ein Verlust auftritt, muss der Empfänger dem Absender separat mitteilen, dass "ich diese Pakete empfangen habe, diese aber nicht erhalten habe", und der Absender muss die fehlenden Pakete erneut übertragen. Oder sie fallen lassen.
- Flusskontrolle Wenn der Empfänger keine Daten empfängt, nicht mit der Geschwindigkeit Schritt hält, mit der wir sie verschieben, können die Daten verloren gehen. Wir müssen diese Situation verarbeiten. Im Fall von TCP wird der Sende-Socket blockiert, und im Fall von UDP wird er nicht blockiert. Sie müssen sich darüber im Klaren sein, dass der Empfänger keine Daten empfängt, und die gesendete Datenmenge reduzieren.
- Überlastungskontrolle. Ähnliches gilt, nur in diesem Fall ist das Netzwerk gestorben. Wenn wir Pakete an das verstorbene Netzwerk senden, zerstören wir nicht nur unsere Verbindung, sondern auch die benachbarten.
- Verschlüsselung Sie müssen sich um die Verschlüsselung kümmern
- ... und vieles mehr
OKMP vs RTMP
Was haben wir bekommen, als wir angefangen haben, OKMP anstelle von RTMP zu verwenden?
- Die durchschnittliche Erhöhung der OKLive-Bitrate beträgt 30%.
- Jitter (Maß für ungleichmäßige Paketankunft) - 0% (im Durchschnitt gleich).
- Jitter Audio - -25%
- Jitter Video - 40%
Änderungen in Audio und Video - Demonstration der Prioritäten in unserem Protokoll. Audio geben wir eine höhere Priorität, und es begann aufgrund des Videos reibungsloser zu kommen.
So wählen Sie ein Protokoll für das Streaming aus

Wenn Sie eine geringe Latenz benötigen - WebRTC.
Wenn Sie mit externen Diensten arbeiten und Videos auf Diensten von Drittanbietern veröffentlichen möchten, müssen Sie RTMP verwenden.
Wenn Sie ein auf Ihre Skripte zugeschnittenes Protokoll wünschen, implementieren Sie Ihr eigenes.