Livestreaming von MobilgerÀten aus ermöglicht es Ihnen, mit Ihrem Publikum zu connecten, unabhÀngig davon, wo Sie sich gerade befinden. Entsprechende Dienste sind sehr beliebt und werden in den verschiedensten Anwendungsbereichen genutzt.
In unserem vorherigen Artikel âWie man eine mobile Streaming-App fĂŒr Android erstelltâ haben wir erlĂ€utert, wie man das Streamen auf Android-GerĂ€ten einrichtet. In diesem Artikel werden wir nĂ€her darauf eingehen, wie Sie Ihre eigene Anwendung fĂŒr mobiles Streaming und das Betrachten von Livestreams auf iOS erstellen und wie Sie diese in Gcore Streaming-Plattform integrieren können.

Streaming-Protokolle
Streaming-Plattformen unterstĂŒtzt. RTMP ist zuverlĂ€ssig und eignet sich aufgrund der niedrigen Latenzzeit und der wiederholten Ăbertragung von Datenpaketen auf der Grundlage des TCP Protokolls perfekt fĂŒr Livestreaming.
Um Content auf die GerĂ€te von Nutzern zu bringen und ihn dort abzuspielen, bieten Streaming-Plattformen die beliebten und skalierbaren Broadcast-Formate HLS und DASH an. DarĂŒber hinaus sind iOS-GerĂ€te von Haus aus mit einem AVPlayer ausgestattet, der die HLS Wiedergabe unterstĂŒtzt. Aus diesem Grund werden wir uns schwerpunktmĂ€Ăig mit diesem Protokoll befassen.
AuswÀhlen einer Bibliothek zum Erstellen eines RTMP Streams
Es gibt nur wenige Open-Source-Lösungen fĂŒr RTMP Streaming ĂŒber iOS-GerĂ€te, und noch weniger wirklich funktionelle Optionen. Werfen wir einen Blick auf die vorrangigen Lösungen:
- LFLiveKit ist veraltet â das letzte Update war am 21. Dezember 2016.
- HaishinKit wird derzeit unterstĂŒtzt und ist kostenfrei.
- Larix SDK funktioniert, ist aber kostenpflichtig (300 $ fĂŒr die Bereitstellung der Quelldateien).
- KSYLive ist veraltet â das letzte Update war am 22. MĂ€rz 2020, und die Beschreibung ist auf Chinesisch.
Vor diesem Hintergrund ist HaishinKit die am besten geeignete Bibliothek. Sie ist auf dem neuesten Stand, funktionell und zugleich eine kostenfreie Lösung mit guter Dokumentation.
Zudem hat HaishinKit zahlreiche Vorteile:
- Wird regelmĂ€Ăig aktualisiert
- UnterstĂŒtzt RTMP Wiedergabe
- Einfache Installation in der Anwendung
- Blendet die interne Verarbeitung mit AVCaptureSession aus â was besonders praktisch ist, wenn die Anwendung keine zusĂ€tzliche Verarbeitung mit der Sitzung benötigt
- UnterstĂŒtzt das Wechseln und Ausschalten der Kamera sowie das Deaktivieren des Mikrofons wĂ€hrend des Streamens
- Kann die Auflösung und die Bitrate Àndern sowie Video und Audio wÀhrend des Streamens aktivieren/deaktivieren
- Bietet die Möglichkeit, die Broadcast-Parameter flexibel zu konfigurieren
- VerfĂŒgt ĂŒber eine Option, den Stream anzuhalten
Es gibt jedoch auch eine Reihe von Nachteilen:
- Keine Option fĂŒr eine adaptive Bitrate
- Blendet die interne Verarbeitung von AVCaptureSession aus, was zu Problemen fĂŒhren kann, wenn Aktionen mit dem eingehenden Audio- und Videomaterial erforderlich sind
Streaming-Implementierung ĂŒber das RTMP Protokoll von einem iOS-GerĂ€t
Die Bibliothek bietet fĂŒr das Streamen zwei Arten von Objekten: RTMPStream und RTMPConnection.
Schauen wir uns Schritt fĂŒr Schritt an, wie man mobiles Streaming einrichtet.
1. Init
Um die Bibliothek HaishinKit in Ihrem Projekt zu verwenden, mĂŒssen Sie sie ĂŒber SPM hinzufĂŒgen, indem Sie Folgendes eingeben:
https://github.com/shogo4405/HaishinKit.swift
Aktuelle Version der Bibliothek
2. Berechtigungen
Geben Sie die erforderlichen Berechtigungen in der Info.plist des Projekts an:
- NSMicrophoneUsageDescription (PrivatsphĂ€re â Beschreibung der Mikrofonnutzung)
- NSCameraUsageDescription (PrivatsphĂ€re â Beschreibung der Kameranutzung)
3. Anzeigen des Kamera-Streams
Wenn Sie mit einer Smartphone-Kamera streamen, mĂŒssen Sie sehen, was der Stream zeigt. Zu diesem Zweck wird in einer entsprechenden Ansicht der Kamerastream auf dem Bildschirm angezeigt. In iOS wird fĂŒr diese Zwecke ein Objekt der Klasse MTHKView verwendet, an das das Objekt RTMPStream angehĂ€ngt ist.
let hkView = MTHKView(frame: .zero)hkView.videoGravity = AVLayerVideoGravity.resizeAspectFillhkView.attachStream(rtmpStream)NSLayoutConstraint.activate([ hkView.topAnchor.constraint(equalTo: topAnchor), hkView.leftAnchor.constraint(equalTo: leftAnchor), hkView.rightAnchor.constraint(equalTo: rightAnchor), hkView.bottomAnchor.constraint(equalTo: bottomAnchor)])
4. Vorbereitungen fĂŒr das Streamen
Zuallererst mĂŒssen Sie AVAudioSession konfigurieren und aktivieren. Sie können dies in der Anwendungsklasse AppDelegate tun:
private func setupSession() { do { try AVAudioSession.sharedInstance().setCategory(.playAndRecord, mode: .default, options: [.defaultToSpeaker, .allowBluetooth]) try AVAudioSession.sharedInstance().setActive(true) } catch { print(error) } }}
Erstellen Sie die Objekte RTMPConnection und RTMPStream:
let rtmpConnection = RTMPConnection()let rtmpStream = RTMPStream(connection: rtmpConnection)rtmpStream.attachAudio(AVCaptureDevice.default(for: AVMediaType.audio)) { error in // print(error)}rtmpStream.attachCamera(DeviceUtil.device(withPosition: .back)) { error in // print(error)}
Legen Sie die Parameter von rtmpStream fest:
rtmpStream.captureSettings = [ .fps: 30, // FPS .sessionPreset: AVCaptureSession.Preset.medium, // input video width/height // .isVideoMirrored: false, // .continuousAutofocus: false, // use camera autofocus mode // .continuousExposure: false, // use camera exposure mode // .preferredVideoStabilizationMode: AVCaptureVideoStabilizationMode.auto]rtmpStream.audioSettings = [ .muted: false, // mute audio .bitrate: 32 * 1000,]rtmpStream.videoSettings = [ .width: 640, // video output width .height: 360, // video output height .bitrate: 160 * 1000, // video output bitrate .profileLevel: kVTProfileLevel_H264_Baseline_3_1, // H264 Profile require "import VideoToolbox" .maxKeyFrameIntervalDuration: 2, // key frame / sec]// "0" means the same of inputrtmpStream.recorderSettings = [ AVMediaType.audio: [ AVFormatIDKey: Int(kAudioFormatMPEG4AAC), AVSampleRateKey: 0, AVNumberOfChannelsKey: 0, // AVEncoderBitRateKey: 128000, ], AVMediaType.video: [ AVVideoCodecKey: AVVideoCodecH264, AVVideoHeightKey: 0, AVVideoWidthKey: 0, /* AVVideoCompressionPropertiesKey: [ AVVideoMaxKeyFrameIntervalDurationKey: 2, AVVideoProfileLevelKey: AVVideoProfileLevelH264Baseline30, AVVideoAverageBitRateKey: 512000 ] */ ],]
5. Adaptive Videobitrate und -auflösung
Zur Implementierung der adaptiven Videobitrate und -auflösung werden zwei RTMPStreamDelegate-Methoden verwendet:
func rtmpStream (_stream: RTMPStream, didPublishSufficientBW connection: RTMPConnection)
â>â die Methode wird einmal pro Sekunde aufgerufen, wenn ausreichend Bandbreite zur VerfĂŒgung steht (wird zur Erhöhung der Bitrate und Auflösung verwendet)func rtmpStream (_stream: RTMPStream, didPublishInsufficientBW connection: RTMPConnection)
âdie Methode wird aufgerufen, wenn nicht ausreichend Bandbreite zur VerfĂŒgung steht (wird zur Verringerung der Bitrate und Auflösung verwendet)
Beispiele fĂŒr die Implementierung:
func rtmpStream(_ stream: RTMPStream, didPublishSufficientBW connection: RTMPConnection) { guard isLive, appState != .background else { return } sufficientBWCount += 1 let currentBitrate = stream.videoSettings[.bitrate] as! UInt32 let setting = VideoSettings.getVideoResolution(type: videoType) if currentBitrate < setting.bitrate && isLive && sufficientBWCount >= 3 { sufficientBWCount = 0 let updatedBitrate = currentBitrate + 100_000 stream.videoSettings[.bitrate] = updatedBitrate increasingResolution(bitrate: Int(updatedBitrate)) print("Increasing the bitrate to \(stream.videoSettings[.bitrate] ?? 0)") } if sufficientBWCount >= 60 { sufficientBWCount = 0 } } func rtmpStream(_ stream: RTMPStream, didPublishInsufficientBW connection: RTMPConnection) { guard isLive, appState != .background else { return } sufficientBWCount = 0 insufficientBWCount += 1 let currentBitrate = stream.videoSettings[.bitrate] as! UInt32 if insufficientBWCount >= 3 { insufficientBWCount = 0 let updatedBitrate = Double(currentBitrate) * 0.7 stream.videoSettings[.bitrate] = updatedBitrate reducingResolution(bitrate: Int(updatedBitrate)) print("Bitrate reduction to \(stream.videoSettings[.bitrate] ?? 0)") } } private func reducingResolution(bitrate: Int) { guard let lowerType = VideoSettings.VideoType(rawValue: currentVideoType.rawValue - 1) else { return } let lowerSettings = VideoSettings.getVideoResolution(type: lowerType) let currentSettings = VideoSettings.getVideoResolution(type: currentVideoType) if bitrate < currentSettings.bitrate - ((currentSettings.bitrate - lowerSettings.bitrate) / 2) { setupVideoSettings(type: lowerType) currentVideoType = lowerType print("Reducing the resolutin to \(lowerSettings.width) x \(lowerSettings.height)") } } private func increasingResolution(bitrate: Int) { guard let upperType = VideoSettings.VideoType(rawValue: currentVideoType.rawValue + 1), upperType.rawValue <= videoType.rawValue else { return } let upperSettings = VideoSettings.getVideoResolution(type: upperType) let currentSettings = VideoSettings.getVideoResolution(type: currentVideoType) if bitrate > currentSettings.bitrate - ((currentSettings.bitrate - upperSettings.bitrate) / 2) { setupVideoSettings(type: upperType) currentVideoType = upperType print("Increasing the resolutin to \(upperSettings.width) x \(upperSettings.height)") } }
6. Hintergrund-Streaming
Apple lĂ€sst keine Videoaufzeichnung im Hintergrund zu, was bedeutet, dass die Bibliothek keine Videofragmente an den Server senden kann. Es fĂŒhrt auf Serverseite zu AbstĂŒrzen und Unterbrechungen des Streams.
Daher haben wir beschlossen, die Bibliothek um zusÀtzliche Funktionen zum Senden eines statischen Bildes im Hintergrund zu erweitern. Unsere Version der Bibliothek können Sie unserem Projekt auf GitHub entnehmen.
7. Starten und Beenden von Livestreams
Stellen Sie die Verbindung zum Server her und starten Sie den Stream:
rtmpConnection.connect(connectString)rtmpStream.publish(publishString)
Um einen Stream anzuhalten, verwenden Sie die boolesche Eigenschaft âpausedâ mit dem Wert âTrueâ:
rtmpStream.paused = true
Integration mit Gcore Streaming-Plattform
Erstellen eines Kontos bei Gcore
Um die Streaming-Plattform in das Projekt zu integrieren, mĂŒssen Sie mit Ihrer E-Mail und Ihrem Kennwort ein kostenfreies Konto bei Gcore erstellen.
Aktivieren Sie den Dienst, indem Sie Free Live oder einen anderen geeigneten Tarif wÀhlen.
FĂŒr die Interaktion mit Gcore Streaming-Plattform werden wir die Gcore-API verwenden. Anfragen werden von nativem Code unter Verwendung der Methoden der Struktur NetworkManager ausgefĂŒhrt, die Daten an die Klasse HTTPCommunication ĂŒbertrĂ€gt. Das Parsen von Daten erfolgt ĂŒber das Protokoll CodingKey unter Verwendung der Struktur DataParser. Sie können bei Bedarf auch eine andere HTTP Bibliothek verwenden.
Ein Beispiel fĂŒr eine Methode zur Erstellung einer Anfrage:
private enum HTTPMethod: String { case GET, PATCH, POST, DELETE } private func createRequest(url: URL, token: String? = nil, json: [String:Any]? = nil, httpMethod: HTTPMethod) ->URLRequest { var request = URLRequest(url: url) if let token = token { request.allHTTPHeaderFields = [ "Authorization" : "Bearer \(token)" ] } if let json = json { let jsonData = try? JSONSerialization.data(withJSONObject: json) request.setValue("application/json", forHTTPHeaderField: "Content-Type") request.httpBody = jsonData } request.httpMethod = httpMethod.rawValue return request }
Autorisierung
Loggen Sie sich ein, um mit der API zu arbeiten. Verwenden Sie die E-Mail und das Kennwort, die Sie bei der Registrierung eingegeben haben, um den Access Token zu erhalten, den Sie fĂŒr weitere Anfragen benötigen.
func authorizationRequest(login: String, password: String, completionHandler: @escaping ((Data?, HTTPError?) ->Void)) { guard let url = URL(string: "https://api.gcdn.co/auth/jwt/login") else { return } self.completionHandler = completionHandler //json for http body request let json: [String: Any] = ["username": login, "password": password] //setup request let request = createRequest(url: url, json: json, httpMethod: .POST) let task = session.downloadTask(with: request) task.resume() }
Die PUSH URL abrufen
Es gibt zwei Möglichkeiten, die URL zum Senden des RTMP Streams abzurufen:
Methode 1. Senden Sie die Anfrage Get all live streams, um alle Livestreams abzurufen. Als Antwort erhalten Sie Daten ĂŒber alle in Ihrem Konto erstellten Streams.
Ein Beispiel fĂŒr das Senden einer Anfrage:
func allStreamsRequest(token: String, completionHandler: @escaping ((Data?, HTTPError?) -> Void)) { var components = URLComponents(string: "https://api.gcdn.co/vp/api/streams") //"with_broadcasts = 0" - because we get broadcasts in another method components?.queryItems = [ URLQueryItem(name: "with_broadcasts", value: "0") ] guard let url = components?.url else { return } self.completionHandler = completionHandler let request = createRequest(url: url, token: token, httpMethod: .GET) let task = session.downloadTask(with: request) task.resume() }}
Methode 2. Senden Sie die Anfrage Get live stream, um einen bestimmten Livestream abzurufen. Als Antwort erhalten Sie nur Daten ĂŒber den angegebenen Stream, sofern dieser existiert.
Ein Beispiel fĂŒr das Senden einer Anfrage:
func createStreamRequest(name: String, token: String, completionHandler: @escaping ((Data?, HTTPError?) -> Void)) { guard let url = URL(string: "https://api.gcdn.co/vp/api/streams") else { return } self.completionHandler = completionHandler //json for http body request let json: [String: Any] = [ "name" : name ] //setup request let request = createRequest(url: url, token: token, json: json, httpMethod: .POST) let task = session.downloadTask(with: request) task.resume() }
>Die Antworten auf diese Anfragen enthalten eine push_url, die als URL fĂŒr das Senden des RTMP Streams verwendet wird. Sobald der Stream beginnt, wird die Ăbertragung automatisch gestartet. WĂ€hlen Sie den gewĂŒnschten Stream in Ihrem persönlichen Konto aus. Sie können die Vorschau verwenden, bevor Sie den Stream auf Ihrer Website oder in Ihrem Player bereitstellen.
Wiedergabe eines aktiven Streams
Mit Gcore Streaming-Plattform können Sie Streams auf Ressourcen von Drittanbietern in verschiedenen Formaten, einschlieĂlich HLS, ĂŒbertragen.
In unserem Beispiel berĂŒcksichtigen wir kein simultanes Streamen und Wiedergeben auf einem GerĂ€t. Stattdessen sollte das Streamen von einem anderen GerĂ€t aus gestartet werden.
Um den aktiven Stream wiederzugeben, verwenden Sie den standardmĂ€Ăigen AVPlayer.
Wiedergabe starten
Vor der Wiedergabe sollte die hls_playlist_url des aktiven Streams in den Player eingebettet werden, wĂ€hrend dieser initialisiert wird. Die hls_playlist_url wird in der Antwort auf die oben erwĂ€hnte Anfrage âGet livestreamâ zurĂŒckgegeben.
func downloadHLS(for streamsID: [Int]) { guard let token = token else { return } for id in streamsID { let http = HTTPCommunication() http.getStreamRequest(token: token, streamID: id) { data, error in if let data = data, let url = URL(string: dataParser.parseStreamHLS(dаta: data)) { delegate?.streamHLSDidDownload(url, streamID: id) } } } }
Initialisierung des Players:
let broadcast = model.broadcasts[indexPath.row] guard let streamID = broadcast.streamIDs.first, let hls = model.getStreamHLS(streamID: streamID) else { return } let playerItem = AVPlayerItem(asset: AVURLAsset(url: hls)) playerItem.preferredPeakBitRate = 800 playerItem.preferredForwardBufferDuration = 1 let player = AVPlayer(playerItem: playerItem) let playerVC = GCPlayerViewController(player: player) playerVC.modalPresentationStyle = .fullScreen present(playerVC, animated: true, completion: nil)
Zusammenfassung
Anhand unserer Beispiele ist das Einrichten eines Livestreams in einer iOS-Anwendung denkbar einfach und nimmt nicht viel Zeit in Anspruch. Alles, was Sie dazu benötigen, ist die Open-Source-Bibliothek HaishinKit und Gcore Streaming-Plattform.
Gcore Streaming-API
Jeglichen im Artikel erwÀhnten Code finden Sie auf GitHub.
Ăhnliche Artikel
Melden Sie sich fĂŒr unseren Newsletter an
Erhalten Sie die neuesten Branchentrends, exklusive Einblicke und Gcore-Updates direkt in Ihren Posteingang.