Veröffentlichen und Abonnieren mit dem IVS Web Broadcast SDK | Streaming in Echtzeit
Dieses Dokument führt Sie durch die Schritte zum Veröffentlichen und Abonnieren einer Stufe mit dem Web Broadcast SDK von IVS-Streaming in Echtzeit.
Konzepte
Drei Kernkonzepte liegen der Echtzeit-Funktionalität zugrunde: Stage, Strategie und Ereignisse. Das Designziel besteht in der Minimierung der Menge an clientseitiger Logik, die für die Entwicklung eines funktionierenden Produkts erforderlich ist.
Stufe
Die Klasse Stage
ist der Hauptinteraktionspunkt zwischen der Hostanwendung und dem SDK. Sie stellt die Bühne selbst dar und dient dazu, der Bühne beizutreten und sie zu verlassen. Für das Erstellen einer Bühne und das Beitreten ist eine gültige, noch nicht abgelaufene Token-Zeichenfolge aus der Steuerebene erforderlich (dargestellt als token
). Einer Bühne beizutreten und sie zu verlassen, ist ganz einfach:
const stage = new Stage(token, strategy) try { await stage.join(); } catch (error) { // handle join exception } stage.leave();
Strategie
Über die Schnittstelle StageStrategy
kann die Hostanwendung dem SDK den gewünschten Status der Bühne mitteilen. Drei Funktionen müssen implementiert werden: shouldSubscribeToParticipant
, shouldPublishParticipant
und stageStreamsToPublish
. Alle werden im Folgenden behandelt.
Um eine definierte Strategie zu verwenden, übergeben Sie sie an den Stage
-Konstruktor. Im Folgenden finden Sie ein vollständiges Beispiel für eine Anwendung, die mithilfe einer Strategie die Webcam eines Teilnehmers auf der Bühne veröffentlicht und alle Teilnehmer abonniert. Der Zweck der einzelnen erforderlichen Strategiefunktionen wird in den folgenden Abschnitten ausführlich erläutert.
const devices = await navigator.mediaDevices.getUserMedia({ audio: true, video: { width: { max: 1280 }, height: { max: 720 }, } }); const myAudioTrack = new LocalStageStream(devices.getAudioTracks()[0]); const myVideoTrack = new LocalStageStream(devices.getVideoTracks()[0]); // Define the stage strategy, implementing required functions const strategy = { audioTrack: myAudioTrack, videoTrack: myVideoTrack, // optional updateTracks(newAudioTrack, newVideoTrack) { this.audioTrack = newAudioTrack; this.videoTrack = newVideoTrack; }, // required stageStreamsToPublish() { return [this.audioTrack, this.videoTrack]; }, // required shouldPublishParticipant(participant) { return true; }, // required shouldSubscribeToParticipant(participant) { return SubscribeType.AUDIO_VIDEO; } }; // Initialize the stage and start publishing const stage = new Stage(token, strategy); await stage.join(); // To update later (e.g. in an onClick event handler) strategy.updateTracks(myNewAudioTrack, myNewVideoTrack); stage.refreshStrategy();
Abonnieren von Teilnehmern
shouldSubscribeToParticipant(participant: StageParticipantInfo): SubscribeType
Wenn ein Remote-Teilnehmer der Bühne beitritt, fragt das SDK die Hostanwendung nach dessen gewünschtem Abonnementstatus. Die Optionen lauten NONE
, AUDIO_ONLY
und AUDIO_VIDEO
. Wenn ein Wert für diese Funktion zurückgegeben wird, muss sich die Hostanwendung nicht um den Veröffentlichungs-, den aktuellen Abonnement- oder den Verbindungsstatus des Bühne kümmern. Bei Rückgabe von AUDIO_VIDEO
wartet das SDK mit dem Abonnieren, bis der Remote-Teilnehmer etwas veröffentlicht. Außerdem aktualisiert es die Hostanwendung, indem es während des gesamten Prozesses Ereignisse ausgibt.
Hier folgt ein Beispiel für eine Implementierung:
const strategy = { shouldSubscribeToParticipant: (participant) => { return SubscribeType.AUDIO_VIDEO; } // ... other strategy functions }
Hierbei handelt es sich um die vollständige Implementierung dieser Funktion für eine Hostanwendung, bei der sich alle Teilnehmer stets gegenseitig sehen sollen; z. B. eine Video-Chat-Anwendung.
Weitergehende Implementierungen sind ebenfalls möglich. Beispiel: Angenommen, die Anwendung stellt bei der Erstellung des Tokens mit CreateParticipantToken ein role
-Attribut bereit. Die Anwendung könnte die attributes
-Eigenschaft für StageParticipantInfo
nutzen, um Teilnehmer anhand der vom Server bereitgestellten Attribute selektiv zu abonnieren:
const strategy = { shouldSubscribeToParticipant(participant) { switch (participant.attributes.role) { case 'moderator': return SubscribeType.NONE; case 'guest': return SubscribeType.AUDIO_VIDEO; default: return SubscribeType.NONE; } } // . . . other strategies properties }
Hiermit kann eine Bühne erstellt werden, auf der Moderatoren alle Gäste überwachen können, ohne selbst gesehen oder gehört zu werden. Die Hostanwendung könnte eine zusätzliche Geschäftslogik nutzen, damit Moderatoren sich gegenseitig sehen können, für Gäste aber unsichtbar bleiben.
Konfiguration für das Abonnieren von Teilnehmern
subscribeConfiguration(participant: StageParticipantInfo): SubscribeConfiguration
Wenn ein Remote-Teilnehmer abonniert wird (siehe Teilnehmer abonnieren), fragt das SDK die Host-Anwendung nach einer benutzerdefinierten Abonnementkonfiguration für diesen Teilnehmer ab. Diese Konfiguration ist optional und ermöglicht es der Hostanwendung, bestimmte Aspekte des Subscriber-Verhaltens zu steuern. Informationen darüber, was konfiguriert werden kann, finden Sie unter SubscribeConfiguration
Hier folgt ein Beispiel für eine Implementierung:
const strategy = { subscribeConfiguration: (participant) => { return { jitterBuffer: { minDelay: JitterBufferMinDelay.MEDIUM } } // ... other strategy functions }
Diese Implementierung aktualisiert die Mindestverzögerung für den Jitter-Buffer für alle abonnierten Teilnehmer auf die Voreinstellung MEDIUM
.
Wie bei shouldSubscribeToParticipant
sind auch hier weitergehende Implementierungen möglich. Die ParticipantInfo
-Angaben können verwendet werden, um die Abonnementkonfiguration für bestimmte Teilnehmer selektiv zu aktualisieren.
Wir empfehlen die Verwendung der Standardverhaltensweisen. Geben Sie die benutzerdefinierte Konfiguration nur an, wenn Sie ein bestimmtes Verhalten ändern möchten.
Veröffentlichen
shouldPublishParticipant(participant: StageParticipantInfo): boolean
Sobald die Verbindung zur Bühne hergestellt ist, überprüft das SDK per Anfrage an die Hostanwendung, ob ein bestimmter Teilnehmer etwas veröffentlichen soll. Dies wird nur bei lokalen Teilnehmern aufgerufen, die auf Grundlage des bereitgestellten Tokens zur Veröffentlichung berechtigt sind.
Hier folgt ein Beispiel für eine Implementierung:
const strategy = { shouldPublishParticipant: (participant) => { return true; } // . . . other strategies properties }
Sie ist für eine normale Video-Chat-Anwendung gedacht, bei der Benutzer immer etwas veröffentlichen möchten. Sie können die Audio- und Videowiedergabe stummschalten und die Stummschaltung aufheben, um umgehend ausgeblendet oder gesehen/gehört zu werden. (Sie können auch „Veröffentlichen/Veröffentlichung aufheben“ verwenden, was aber viel langsamer ist. „Stummschalten/Stummschalten aufheben“ ist für Anwendungsfälle vorzuziehen, in denen eine häufige Änderung der Sichtbarkeit wünschenswert ist.)
Auswählen von Streams zur Veröffentlichung
stageStreamsToPublish(): LocalStageStream[];
Beim Veröffentlichen wird hiermit bestimmt, welche Audio- und Videostreams veröffentlicht werden sollen. Dieser Punkt wird später unter Veröffentlichen eines Medienstreams ausführlicher behandelt.
Aktualisieren der Strategie
Die Strategie soll dynamisch sein: Die von einer der oben genannten Funktionen zurückgegebenen Werte lassen sich jederzeit ändern. Wenn die Hostanwendung beispielsweise erst veröffentlichen soll, wenn der Endbenutzer auf eine Schaltfläche tippt, können Sie eine Variable aus shouldPublishParticipant
zurückgeben (zum Beispiel hasUserTappedPublishButton
). Wenn sich diese Variable aufgrund einer Interaktion des Endbenutzers ändert, signalisieren Sie dem SDK per Aufruf von stage.refreshStrategy()
, dass es die Strategie nach den neuesten Werten abfragen und nur Dinge anwenden soll, die sich geändert haben. Wenn das SDK feststellt, dass sich der Wert shouldPublishParticipant
geändert hat, startet es den Veröffentlichungsprozess. Wenn alle Funktionen bei einer SDK-Abfrage den gleichen Wert zurückgeben wie zuvor, wird die Bühne mit dem Aufruf von refreshStrategy
nicht geändert.
Ändert sich der Rückgabewert von shouldSubscribeToParticipant
von AUDIO_VIDEO
in AUDIO_ONLY
, wird der Videostream für alle Teilnehmer mit geänderten Rückgabewerten entfernt, sofern zuvor ein Videostream vorhanden war.
Im Allgemeinen nutzt die Bühne die Strategie, um den Unterschied zwischen der vorherigen und der aktuellen Strategie am effizientesten anzuwenden. Dabei muss sich die Hostanwendung nicht um die ganzen Status kümmern, die für eine ordnungsgemäße Verwaltung erforderlich sind. Stellen Sie sich den Aufruf von stage.refreshStrategy()
daher als einen ressourcenschonenden Vorgang vor, da nur bei einer Änderung der Strategie etwas unternommen wird.
Ereignisse
Eine Stage
-Instance ist ein Ereignis-Emitter. Mit stage.on()
wird der Hostanwendung der Status der Bühne mitgeteilt. Aktualisierungen in der Benutzeroberfläche der Hostanwendung können in der Regel vollständig durch die Ereignisse unterstützt werden. Folgende Ereignisse werden unterstützt:
stage.on(StageEvents.STAGE_CONNECTION_STATE_CHANGED, (state) => {}) stage.on(StageEvents.STAGE_PARTICIPANT_JOINED, (participant) => {}) stage.on(StageEvents.STAGE_PARTICIPANT_LEFT, (participant) => {}) stage.on(StageEvents.STAGE_PARTICIPANT_PUBLISH_STATE_CHANGED, (participant, state) => {}) stage.on(StageEvents.STAGE_PARTICIPANT_SUBSCRIBE_STATE_CHANGED, (participant, state) => {}) stage.on(StageEvents.STAGE_PARTICIPANT_STREAMS_ADDED, (participant, streams) => {}) stage.on(StageEvents.STAGE_PARTICIPANT_STREAMS_REMOVED, (participant, streams) => {}) stage.on(StageEvents.STAGE_STREAM_MUTE_CHANGED, (participant, stream) => {})
Für die meisten dieser Ereignisse wird die entsprechende ParticipantInfo
bereitgestellt.
Es wird nicht erwartet, dass sich die von den Ereignissen bereitgestellten Informationen auf die Rückgabewerte der Strategie auswirken. Es wird beispielsweise nicht erwartet, dass sich der Rückgabewert von shouldSubscribeToParticipant
beim Aufruf von STAGE_PARTICIPANT_PUBLISH_STATE_CHANGED
ändert. Wenn die Hostanwendung einen bestimmten Teilnehmer abonnieren möchte, muss sie unabhängig von dessen Veröffentlichungsstatus den gewünschten Abonnementtyp zurückgeben. Das SDK muss dafür sorgen, dass entsprechend dem Status der Bühne und dem gewünschten Status der Strategie zum richtigen Zeitpunkt gehandelt wird.
Veröffentlichen eines Medienstreams
Lokale Geräte wie Mikrofone und Kameras werden mit den gleichen Schritten abgerufen, die oben unter Abrufen eines MediaStream von einem Gerät beschrieben sind. In dem Beispiel erstellen wir mit MediaStream
eine Liste von LocalStageStream
-Objekten, die für die Veröffentlichung durch das SDK verwendet werden:
try { // Get stream using steps outlined in document above const stream = await getMediaStreamFromDevice(); let streamsToPublish = stream.getTracks().map(track => { new LocalStageStream(track) }); // Create stage with strategy, or update existing strategy const strategy = { stageStreamsToPublish: () => streamsToPublish } }
Veröffentlichen einer Bildschirmfreigabe
Häufig müssen Anwendungen zusätzlich zur Webkamera des Benutzers eine Bildschirmfreigabe veröffentlichen. Das Veröffentlichen einer Bildschirmfreigabe erfordert die Erstellung eines zusätzlichen Tokens für die Bühne, insbesonder für die Veröffentlichung der Medien der Bildschirmfreigabe. Verwenden Sie getDisplayMedia
und beschränken Sie die Auflösung auf maximal 720p. Danach sind die Schritte ähnlich wie beim Veröffentlichen einer Kamera für die Bühne.
// Invoke the following lines to get the screenshare's tracks const media = await navigator.mediaDevices.getDisplayMedia({ video: { width: { max: 1280, }, height: { max: 720, } } }); const screenshare = { videoStream: new LocalStageStream(media.getVideoTracks()[0]) }; const screenshareStrategy = { stageStreamsToPublish: () => { return [screenshare.videoStream]; }, shouldPublishParticipant: (participant) => { return true; }, shouldSubscribeToParticipant: (participant) => { return SubscribeType.AUDIO_VIDEO; } } const screenshareStage = new Stage(screenshareToken, screenshareStrategy); await screenshareStage.join();
Anzeigen und Entfernen von Teilnehmern
Nach Abschluss von Abonnements erhalten Sie über das Ereignis STAGE_PARTICIPANT_STREAMS_ADDED
eine Reihe von StageStream
-Objekten. Zudem stellt das Ereignis Teilnehmerinformationen bereit, die Ihnen beim Anzeigen von Medienstreams helfen:
stage.on(StageEvents.STAGE_PARTICIPANT_STREAMS_ADDED, (participant, streams) => { const streamsToDisplay = streams; if (participant.isLocal) { // Ensure to exclude local audio streams, otherwise echo will occur streamsToDisplay = streams.filter(stream => stream.streamType === StreamType.VIDEO) } // Create or find video element already available in your application const videoEl = getParticipantVideoElement(participant.id); // Attach the participants streams videoEl.srcObject = new MediaStream(); streamsToDisplay.forEach(stream => videoEl.srcObject.addTrack(stream.mediaStreamTrack)); })
Wenn ein Teilnehmer die Veröffentlichung beendet oder dessen Abonnement eines Streams beendet wird, wird die Funktion STAGE_PARTICIPANT_STREAMS_REMOVED
mit den Streams aufgerufen, die entfernt wurden. Hostanwendungen sollten dies als Signal nutzen, um den Videostream des Teilnehmers aus dem DOM zu entfernen.
STAGE_PARTICIPANT_STREAMS_REMOVED
wird für alle Szenarien aufgerufen, in denen ein Stream entfernt werden könnte, darunter:
-
Der Remote-Teilnehmer beendet die Veröffentlichung.
-
Ein lokales Gerät beendet das Abonnement oder ändert das Abonnement von
AUDIO_VIDEO
inAUDIO_ONLY
. -
Der Remote-Teilnehmer verlässt die Bühne.
-
Der lokale Teilnehmer verlässt die Bühne.
Da STAGE_PARTICIPANT_STREAMS_REMOVED
bei allen Szenarien aufgerufen wird, ist keine benutzerdefinierte Geschäftslogik erforderlich, um Teilnehmer beim remoten oder lokalen Verlassen aus der Benutzeroberfläche zu entfernen.
Stummschalten von Medienstreams und Aufheben der Stummschaltung
LocalStageStream
-Objekte verfügen über eine setMuted
-Funktion, die das Stummschalten des Streams steuert. Diese Funktion kann für den Stream aufgerufen werden, bevor oder nachdem er von der Strategiefunktion stageStreamsToPublish
zurückgegeben wird.
Wichtig: Wenn nach einem Aufruf von refreshStrategy
eine neue LocalStageStream
-Objekt-Instance von stageStreamsToPublish
zurückgegeben wird, wird der Stummschaltungsstatus des neuen Streamobjekts auf die Bühne angewendet. Seien Sie vorsichtig beim Erstellen neuer LocalStageStream
-Instances, um sicherzustellen, dass der erwartete Stummschaltungsstatus beibehalten wird.
Überwachen des Medien-Stummschaltungsstatus von Remote-Teilnehmern
Wenn Teilnehmer den Stummschaltungsstatus ihres Videos oder Audios ändern, wird das Ereignis STAGE_STREAM_MUTE_CHANGED
mit einer Liste der Streams ausgelöst, die sich geändert haben. Verwenden Sie die Eigenschaft isMuted
für StageStream
, um die Benutzeroberfläche entsprechend zu aktualisieren:
stage.on(StageEvents.STAGE_STREAM_MUTE_CHANGED, (participant, stream) => { if (stream.streamType === 'video' && stream.isMuted) { // handle UI changes for video track getting muted } })
Sie können auch unter StageParticipantInfo
stage.on(StageEvents.STAGE_STREAM_MUTE_CHANGED, (participant, stream) => { if (participant.videoStopped || participant.audioMuted) { // handle UI changes for either video or audio } })
Abrufen von WebRTC-Statistiken
Um die neuesten WebRTC-Statistiken für einen veröffentlichten oder abonnierten Stream abzurufen, verwenden Sie getStats
für StageStream
. Hierbei handelt es sich um eine asynchrone Methode, mit der Sie Statistiken entweder über await oder durch Verkettung eines Promise abrufen können. Das Ergebnis ist ein RTCStatsReport
, ein Wörterbuch, das alle Standardstatistiken enthält.
try { const stats = await stream.getStats(); } catch (error) { // Unable to retrieve stats }
Optimieren von Medien
Für eine optimale Leistung wird empfohlen, Aufrufe von getUserMedia
und getDisplayMedia
entsprechend den folgenden Einschränkungen zu begrenzen:
const CONSTRAINTS = { video: { width: { ideal: 1280 }, // Note: flip width and height values if portrait is desired height: { ideal: 720 }, framerate: { ideal: 30 }, }, };
Sie können die Medien durch zusätzliche Optionen, die an den LocalStageStream
-Konstruktor übergeben werden, weiter einschränken:
const localStreamOptions = { minBitrate?: number; maxBitrate?: number; maxFramerate?: number; simulcast: { enabled: boolean } } const localStream = new LocalStageStream(track, localStreamOptions)
Im obigen Code:
-
minBitrate
legt eine Mindestbitrate fest, die der Browser voraussichtlich verwenden sollte. Ein Videostream mit geringer Komplexität kann jedoch dazu führen, dass der Encoder diese Bitrate unterschreitet. -
maxBitrate
legt eine maximale Bitrate fest, von der erwartet werden sollte, dass sie vom Browser für diesen Stream nicht überschritten wird. -
maxFramerate
legt eine maximale Framerate fest, von der erwartet werden sollte, dass sie vom Browser für diesen Stream nicht überschritten wird. -
Die Option
simulcast
ist nur in Chromium-basierten Browsern verwendbar. Sie ermöglicht das Senden von drei Wiedergabeebenen des Streams.-
Auf diese Weise kann der Server anhand ihrer Netzwerkbeschränkungen auswählen, welche Wiedergabeversion an andere Teilnehmer gesendet werden soll.
-
Wenn
simulcast
zusammen mit einemmaxBitrate
und/odermaxFramerate
Wert angegeben wird, wird erwartet, dass die höchste Wiedergabe-Ebene unter Berücksichtigung dieser Werte konfiguriert wird, vorausgesetzt,maxBitrate
unterschreitet nicht die Standardeinstellung der zweithöchsten Ebene des internen SDK-StandardwertsmaxBitrate
von 900 kbps. -
Wenn
maxBitrate
im Vergleich zum Standardwert der zweithöchsten Ebene als zu niedrig angegeben wird, wirdsimulcast
deaktiviert. -
simulcast
kann nicht ein- und ausgeschaltet werden, ohne die Medien erneut zu veröffentlichen, indemshouldPublishParticipant
false
zurückgibt,refreshStrategy
aufruft,shouldPublishParticipant
true
zurückgibt, undrefreshStrategy
wieder aufruft.
-
Abrufen von Teilnehmerattributen
Wenn Sie Attribute in der Endpunktanfrage CreateParticipantToken
angeben, können Sie die Attribute in den Eigenschaften von StageParticipantInfo
einsehen:
stage.on(StageEvents.STAGE_PARTICIPANT_JOINED, (participant) => { console.log(`Participant ${participant.id} info:`, participant.attributes); })
Umgang mit Netzwerkproblemen
Bei Unterbrechung der Netzwerkverbindung des lokalen Geräts versucht das SDK intern, die Verbindung ohne Benutzeraktion wiederherzustellen. In einigen Fällen ist das SDK nicht erfolgreich, weshalb eine Benutzeraktion erforderlich ist.
Generell kann der Status der Bühne über das Ereignis STAGE_CONNECTION_STATE_CHANGED
gesteuert werden:
stage.on(StageEvents.STAGE_CONNECTION_STATE_CHANGED, (state) => { switch (state) { case StageConnectionState.DISCONNECTED: // handle disconnected UI break; case StageConnectionState.CONNECTING: // handle establishing connection UI break; case StageConnectionState.CONNECTED: // SDK is connected to the Stage break; case StageConnectionState.ERRORED: // SDK encountered an error and lost its connection to the stage. Wait for CONNECTED. break; })
Im Allgemeinen können Sie einen fehlerhaften Status ignorieren, der nach dem erfolgreichen Beitritt zu einer Bühne auftritt, da das SDK versuchen wird, die Verbindung intern wiederherzustellen. Wenn das SDK einen ERRORED
-Status meldet und die Bühne über einen längeren Zeitraum (z. B. 30 Sekunden oder länger) im CONNECTING
-Status verbleibt, sind Sie wahrscheinlich vom Netzwerk getrennt worden.
Übertragung der Stage auf einen IVS-Kanal
Zum Übertragen einer Bühne erstellen Sie eine separate IVSBroadcastClient
-Sitzung und folgen Sie dann den oben beschriebenen üblichen Anweisungen für die Übertragung mit dem SDK. Mithilfe der Liste der über STAGE_PARTICIPANT_STREAMS_ADDED
offengelegten StageStream
können die Medienstreams der Teilnehmer abgerufen werden, die wie folgt auf die Zusammensetzung der übertragenen Streams angewendet werden können:
// Setup client with preferred settings const broadcastClient = getIvsBroadcastClient(); stage.on(StageEvents.STAGE_PARTICIPANT_STREAMS_ADDED, (participant, streams) => { streams.forEach(stream => { const inputStream = new MediaStream([stream.mediaStreamTrack]); switch (stream.streamType) { case StreamType.VIDEO: broadcastClient.addVideoInputDevice(inputStream, `video-${participant.id}`, { index: DESIRED_LAYER, width: MAX_WIDTH, height: MAX_HEIGHT }); break; case StreamType.AUDIO: broadcastClient.addAudioInputDevice(inputStream, `audio-${participant.id}`); break; } }) })
Optional können Sie eine Stage zusammenstellen und sie auf einen IVS-Kanal mit niedriger Latenz übertragen, um ein größeres Publikum zu erreichen. Sehen Sie Aktivierung mehrerer Hosts in einem Amazon-IVS-Stream im Benutzerhandbuch für IVS-Streaming mit niedriger Latenz.