Wie wir Probleme beim RTMP-to-HLS-Streaming auf iOS und Android lösen

Lange Startzeiten, Video-Buffering, hohe Verzögerungen, Unterbrechungen und andere Probleme sind üblich, wenn man eine Anwendung für Streaming und Live-Streaming entwickelt. Jeder, der so einen Dienst schon einmal entwickelt hat, musste sich mit mindestens einer dieser Probleme auseinandersetzen.

In vorhergehenden Beiträgen haben wir darüber berichtet, wie man Streaming Apps für iOS und Android entwickelt. Heute berichten wir über die Probleme, die wir bei diesen Prozessen hatten, und wie wir sie gelöst haben.

Der Verwendung einer modernen Streaming-Plattform

Alles, was eine Mobile App machen muss, ist Video und Audio https://gcore.com der Kamera aufzunehmen, ein einen Datenstrom zu verpacken und diesen an die Zuschauer zu senden. Eine Streaming-Plattform wird benötigt, um diesen Inhalt an viele Zuschauer gleichzeitig zu übermitteln.

So funktioniert die adaptive Bitrate
Streaming mit der Gcore-Plattform

Das einzige Problem einer solchen Streaming-Plattform ist die Latenz. Die Übertragung ist ein komplizierter Prozess. Bei jeder Phase einer solchen Übertragung entstehen Latenzen.

Unsere Entwickler konnten eine stabile, funktionelle und schnelle Lösung entwickeln, die nur 5 Sekunden benötigt, um alle Prozesse zu starten, während die End-to-End-Latenz bei Übertragungen im Niedrigen-Latenz-Modus nur 4 Sekunden benötigt.

In der nachstehenden Tabelle finden Sie mehrere Plattformen, die das Latenzproblem auf ihre eigene Weise lösen. Wir haben verschiedene Lösungen verglichen und jede studiert, um die beste Lösung zu finden.

So funktioniert die adaptive Bitrate

Es dauert 5 Minuten, bis das Streaming auf Gcore Streaming-Plattform beginnt:

  1. Erstellen Sie ein kostenloses Konto. Sie müssen Ihre E-Mail-Adresse und Passwort angeben.
  2. Aktivieren Sie den Dienst, indem Sie Free Live oder einen anderen geeigneten Tarif auswählen.
  3. Erstellen Sie einen Stream und starten Sie die Übertragung.

Alle am Streaming beteiligten Prozesse sind untrennbar miteinander verbunden. Änderungen an einem Prozess wirken sich auf alle nachfolgenden aus. Es wäre also nicht korrekt, diese in einzelne Blöcke aufzuteilen. Wir überlegen, was und wie optimiert werden kann.

Verringerung der GOP Größe und Beschleunigung der Stream-Übertragung und des Empfangs

Um mit der Dekodierung und Verarbeitung eines Videostreams zu beginnen, benötigen wir einen iframe. Wir haben Tests durchgeführt und das optimale 2-Sekunden-iFrame-Intervall für unsere Anwendungen ausgewählt. In bestimmten Fällen kann der Wert jedoch auf 1 Sekunde geändert werden. Durch die Verringerung der GOP Länge wird die Dekodierung und damit der Beginn der Stream-Verarbeitung beschleunigt.

iOS

Set maxKeyFrameIntervalDuration = 2.

Android

Set iFrameIntervalInSeconds = 2.

Hintergrund-Streaming für ununterbrochene Unterhaltung

Wenn wir während des Streamings kurze Pausen benötigen, z.B. um zu einer anderen Anwendung zu wechseln, können wir das Streaming im Hintergrund fortsetzen, ohne dass das Video unterbrochen wird. So verlieren wir keine Zeit mit der Initialisierung aller Prozesse und halten die End-to-End-Latenz bei Wiederaufnahme der Übertragung minimal.

iOS

Apple verbietet die Aufnahme https://gcore.com Videos, während die App minimiert ist. Unsere erste Lösung bestand darin, die Kamera im richtigen Moment zu deaktivieren und sie bei der Wiederaufnahme der Übertragung wieder zu aktivieren. Zu diesem Zweck haben wir eine Systembenachrichtigung abonniert, die uns über Eintritt und Austritt in den Hintergrundstatus informiert.

Das hat nicht funktioniert. Die Verbindung wurde nicht unterbrochen, aber die Bibliothek hat das Video des RTMP Streams nicht gesendet. Aus diesem Grund haben wir beschlossen, Änderungen an der Bibliothek selbst vorzunehmen.

Jedes Mal, wenn das System einen Puffer mit Audiodaten an AVCaptureAudioDataOutputSampleBufferDelegate sendet, prüft es, ob alle Geräte https://gcore.com der Sitzung getrennt sind. Nur das Mikrofon sollte angeschlossen bleiben. Wenn alles stimmt, wird timingInfo erstellt. Es enthält Informationen über die Dauer, dts und pts eines Fragments.

Danach wird die Methode pushPauseImageIntoVideoStream der Klasse AVMixer aufgerufen, die das Bestehen eines zu pausierenden Bildes überprüft. Als nächstes wird mit der Methode pixelBufferFromCGImage ein CVPixelBuffer mit den Bilddaten erstellt, und der CMSampleBuffer selbst wird mit der Methode createBuffer erstellt, die an AVCaptureVideoDataOutputSampleBufferDelegate gesendet wird.

Erweiterung für AVMixer:

  • hasOnlyMicrophone prüft, ob alle Geräte außer dem Mikrofon https://gcore.com der Sitzung getrennt sind
  • func pushPauseImageIntoVideoStream entnimmt Daten aus dem Audiopuffer, erstellt einen Videopuffer und sendet ihn an AVCaptureVideoDataOutputSampleBufferDelegate
  • private func pixelBufferFromCGImage (image: CGImage) erzeugt und übermittelt CVPixelBuffer aus dem Bild
  • createBuffer (pixelBuffer: CVImageBuffer, timingInfo: input CMSampleTimingInfo) erzeugt und übermittelt einen CMSampleBuffer aus timingInfo und CVPixelBuffer

Wir fügen der Klasse AVMixer die Eigenschaft pauseImage hinzu:

Wir fügen in AVAudioIOUnit die Funktionalität der Methode func captureOutput (_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) hinzu:

Android

Mit Android war alles einfacher. Ein Blick in den Quellcode der https://gcore.com uns verwendeten Bibliothek macht deutlich, dass das Streaming tatsächlich in einem separaten Stream erfolgt.

Unter Berücksichtigung des Lebenszyklus der Komponente, in der unser Streaming initialisiert wird, haben wir beschlossen, es im ViewModel zu initialisieren — es bleibt während des gesamten Lebenszyklus der Komponente, an das es gebunden ist (Activity, Fragment), bestehen.

So funktioniert die adaptive Bitrate
Lebenszyklus des ViewModel

Im Lebenszyklus des ViewModel ändert sich nichts, auch nicht bei Änderungen der Konfiguration, der Ausrichtung, des Hintergrundübergangs usw.

Ein kleines Problem gibt es aber noch. Für das Streaming benötigen wir ein RtmpCamera2()-Objekt, das https://gcore.com einem OpenGlView-Objekt abhängt. Es handelt sich hierbei um ein Oberflächenelement, das also wegfällt, wenn die App in den Hintergrund geht und der Streaming-Prozess unterbrochen wird.

Die Lösung wurde schnell gefunden. Mit der Bibliothek können die Ansichtsoptionen des RtmpCamera2-Objekts einfach ersetzt werden. Wir können es durch ein Context-Objekt aus unserer Anwendung ersetzen. Die Lebensdauer reicht so lange, bis die App vom System entfernt oder vom Benutzer geschlossen wird.

Wir sehen das Löschen des OpenGlView-Objekts als Indikator dafür, dass die Anwendung im Hintergrund arbeitet und die Erstellung dieser Anzeige als Signal für die Rückkehr in den Vordergrund. Dafür müssen wir den entsprechenden Callback implementieren:

Als nächstes müssen wir, wie schon erwähnt, das OpenGlView-Objekt durch Context ersetzen, wenn wir zum Hintergrund und zurück in den Vordergrund wechseln. Dafür definieren wir die erforderlichen Methoden im ViewModel. Wir müssen das Streaming außerdem beenden, wenn ViewModel eliminiert wird.

Wenn wir das Streaming unterbrechen wollen, ohne in den Hintergrund zu wechseln, müssen wir nur die Kamera und das Mikrofon ausschalten. In diesem Modus wird die Bitrate auf 70–80 Kbit/s reduziert, wodurch Traffic eingespart werden kann.

WebSocket und Start des Players zum richtigen Zeitpunkt

Wir verwenden WebSocket, um die erforderlichen Angaben über den abspielbereiten Inhalt zu erhalten und das Streaming sofort zu starten:

Verwendung adaptiver Bitrate und Auflösung

Wenn wir das Streaming einem mobilen Gerät aus durchführen, werden Mobilfunknetze für die Videoübertragung genutzt. Das ist das Hauptproblem beim mobilen Streaming: Der Signalpegel und die Signalqualität hängen vielen Faktoren ab. Aus diesem Grund ist es notwendig, die Bitrate und die Auflösung an die verfügbare Bandbreite anzupassen. Auf diese Weise wird ein stabiler Streaming-Prozess unabhängig der Qualität der Internetverbindung des Zuschauers gewährleistet.

So funktioniert die adaptive Bitrate
So funktioniert die adaptive Bitrate

iOS

Zwei RTMPStreamDelegate-Methoden werden zur Implementierung der adaptiven Bitrate verwendet:

Beispiele für die Implementierung:

Die adaptive Auflösung wird entsprechend der Bitrate angepasst. Wir verwenden folgendes Verhältnis zwischen Auflösung und Bitrate als Ausgangspunkt:

Auflösung1920×10801280×720854×480640×360
Video-Bitrate6 Mbit/s2 Mbit/s0,8 Mbit/s0,4 Mbit/s

Sinkt die Bandbreite um mehr als die Hälfte der Differenz zwischen zwei angrenzenden Auflösungen, wechselt es zu einer niedrigeren Auflösung. Um die Bitrate zu erhöhen, wechseln wir auf eine höhere Auflösung.

Android

Um adaptive Bitraten zu verwenden, ändern wir die Implementierung der Schnittstelle ConnectCheckerRtmp:

Zusammenfassung

Streaming https://gcore.com mobilen Geräten ist keine schwierige Aufgabe. Durch die Verwendung https://gcore.com Open-Source-Code und unserer Streaming-Plattform lässt sich diese Aufgabe schnell und zu minimalen Kosten erledigen.

Natürlich kann es während des Entwicklungsprozesses immer zu Problemen kommen. Wir hoffen, dass Ihnen unsere Lösungen helfen werden, dieses Vorgehen zu vereinfachen und Ihre Aufgaben schneller zu erledigen.

In unseren Artikeln erfahren Sie mehr über die Entwicklung https://gcore.com Apps für das Streaming auf iOS und Android:

Repositories mit dem Quellcode mobiler Streaming-Anwendungen finden Sie auf GitHub: iOS, Android.

Streamen Sie mit unserer Streaming-Plattform problemlos auf mobile Geräte.

Mehr über Streaming-Plattform

Melden Sie sich an, um die neuesten Updates, Nachrichten und Funktionen zu erhalten.

Wir respektieren Ihren Posteingang und bemühen uns, Spam zu vermeiden