Veröffentlichen und Abonnieren mit dem IVS Web Broadcast SDK | Streaming in Echtzeit - Amazon IVS

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 in der SDK-Referenzdokumentation.

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 in AUDIO_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 nach Statusinformationen darüber suchen, ob Audio oder Video stummgeschaltet sind:

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 einem maxBitrate und/oder maxFramerate 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-Standardwerts maxBitrate von 900 kbps.

    • Wenn maxBitrate im Vergleich zum Standardwert der zweithöchsten Ebene als zu niedrig angegeben wird, wird simulcast deaktiviert.

    • simulcast kann nicht ein- und ausgeschaltet werden, ohne die Medien erneut zu veröffentlichen, indem shouldPublishParticipant false zurückgibt, refreshStrategy aufruft, shouldPublishParticipant true zurückgibt, und refreshStrategy 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.