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

Veröffentlichen und Abonnieren mit dem IVS Android Broadcast SDK | Streaming in Echtzeit

Dieses Dokument führt Sie durch die Schritte zum Veröffentlichen und Abonnieren einer Stufe mit dem Android Broadcast SDK von IVS-Streaming in Echtzeit.

Konzepte

Drei Kernkonzepte liegen der Echtzeit-Funktionalität zugrunde: Stage, Strategie und Renderer. 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.

Stage stage = new Stage(context, token, strategy); try { stage.join(); } catch (BroadcastException exception) { // handle join exception } stage.leave();

In der Klasse Stage erfolgt auch das Anhängen des StageRenderer:

stage.addRenderer(renderer); // multiple renderers can be added

Strategie

Über die Schnittstelle Stage.Strategy kann die Hostanwendung dem SDK den gewünschten Status der Bühne mitteilen. Drei Funktionen müssen implementiert werden: shouldSubscribeToParticipant, shouldPublishFromParticipant und stageStreamsToPublishForParticipant. Alle werden im Folgenden behandelt.

Abonnieren von Teilnehmern

Stage.SubscribeType shouldSubscribeToParticipant(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo);

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 das SDK die Hostanwendung während des gesamten Prozesses über den Renderer.

Hier folgt ein Beispiel für eine Implementierung:

@Override Stage.SubscribeType shouldSubscribeToParticipant(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo) { return Stage.SubscribeType.AUDIO_VIDEO; }

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. Nutzen Sie die Eigenschaft userInfo für ParticipantInfo, um Teilnehmer anhand der vom Server bereitgestellten Attribute selektiv zu abonnieren:

@Override Stage.SubscribeType shouldSubscribeToParticipant(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo) { switch(participantInfo.userInfo.get(“role”)) { case “moderator”: return Stage.SubscribeType.NONE; case “guest”: return Stage.SubscribeType.AUDIO_VIDEO; default: return Stage.SubscribeType.NONE; } }

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 subscribeConfigurationForParticipant(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo);

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:

@Override public SubscribeConfiguration subscribeConfigrationForParticipant(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo) { SubscribeConfiguration config = new SubscribeConfiguration(); config.jitterBuffer.setMinDelay(JitterBufferConfiguration.JitterBufferDelay.MEDIUM()); return config; }

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

boolean shouldPublishFromParticipant(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo);

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:

@Override boolean shouldPublishFromParticipant(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo) { return true; }

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

@Override List<LocalStageStream> stageStreamsToPublishForParticipant(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo); }

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 shouldPublishFromParticipant 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 shouldPublishFromParticipant geändert hat, startet es den Veröffentlichungsprozess. Wenn alle Funktionen bei einer SDK-Abfrage den gleichen Wert zurückgeben wie zuvor, werden mit dem Aufruf von refreshStrategy keine Änderungen an der Bühne durchgeführt.

Ä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.

Renderer

Die Schnittstelle StageRenderer teilt der Hostanwendung den Status der Bühne mit. Aktualisierungen in der Benutzeroberfläche der Hostanwendung können in der Regel vollständig über die vom Renderer bereitgestellten Ereignisse gesteuert werden. Der Renderer stellt die folgenden Funktionen bereit:

void onParticipantJoined(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo); void onParticipantLeft(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo); void onParticipantPublishStateChanged(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo, @NonNull Stage.PublishState publishState); void onParticipantSubscribeStateChanged(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo, @NonNull Stage.SubscribeState subscribeState); void onStreamsAdded(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo, @NonNull List<StageStream> streams); void onStreamsRemoved(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo, @NonNull List<StageStream> streams); void onStreamsMutedChanged(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo, @NonNull List<StageStream> streams); void onError(@NonNull BroadcastException exception); void onConnectionStateChanged(@NonNull Stage stage, @NonNull Stage.ConnectionState state, @Nullable BroadcastException exception); void onStreamAdaptionChanged(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo, @NonNull RemoteStageStream stream, boolean adaption); void onStreamLayersChanged(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo, @NonNull RemoteStageStream stream, @NonNull List<RemoteStageStream.Layer> layers); void onStreamLayerSelected(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo, @NonNull RemoteStageStream stream, @Nullable RemoteStageStream.Layer layer, @NonNull RemoteStageStream.LayerSelectedReason reason);

Für die meisten dieser Methoden werden die entsprechende Stage und ParticipantInfo bereitgestellt.

Es wird nicht erwartet, dass sich die vom Renderer bereitgestellten Informationen auf die Rückgabewerte der Strategie auswirken. Es wird beispielsweise nicht erwartet, dass sich der Rückgabewert von shouldSubscribeToParticipant beim Aufruf von onParticipantPublishStateChanged ä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.

Der StageRenderer kann der Bühnenklasse angefügt werden:

stage.addRenderer(renderer); // multiple renderers can be added

Hinweis: Nur veröffentlichende Teilnehmer lösen onParticipantJoined aus. Wenn Teilnehmer die Veröffentlichung beenden oder die Bühnensitzung verlassen, wird onParticipantLeft ausgelöst.

Veröffentlichen eines Medienstreams

Lokale Geräte wie eingebaute Mikrofone und Kameras werden über DeviceDiscovery erkannt. Hier folgt ein Beispiel für die Auswahl der nach vorne gerichteten Kamera und des Standardmikrofons und deren anschließende Rückgabe als LocalStageStreams zur Veröffentlichung durch das SDK:

DeviceDiscovery deviceDiscovery = new DeviceDiscovery(context); List<Device> devices = deviceDiscovery.listLocalDevices(); List<LocalStageStream> publishStreams = new ArrayList<LocalStageStream>(); Device frontCamera = null; Device microphone = null; // Create streams using the front camera, first microphone for (Device device : devices) { Device.Descriptor descriptor = device.getDescriptor(); if (!frontCamera && descriptor.type == Device.Descriptor.DeviceType.Camera && descriptor.position = Device.Descriptor.Position.FRONT) { front Camera = device; } if (!microphone && descriptor.type == Device.Descriptor.DeviceType.Microphone) { microphone = device; } } ImageLocalStageStream cameraStream = new ImageLocalStageStream(frontCamera); AudioLocalStageStream microphoneStream = new AudioLocalStageStream(microphoneDevice); publishStreams.add(cameraStream); publishStreams.add(microphoneStream); // Provide the streams in Stage.Strategy @Override @NonNull List<LocalStageStream> stageStreamsToPublishForParticipant(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo) { return publishStreams; }

Anzeigen und Entfernen von Teilnehmern

Nach Abschluss von Abonnements erhalten Sie über die Funktion onStreamsAdded des Renderers eine Reihe von StageStream-Objekten. Sie können die Vorschau von einem ImageStageStream abrufen:

ImagePreviewView preview = ((ImageStageStream)stream).getPreview(); // Add the view to your view hierarchy LinearLayout previewHolder = findViewById(R.id.previewHolder); preview.setLayoutParams(new LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT)); previewHolder.addView(preview);

Außerdem können Sie die Statistiken des Audiolevels von einem AudioStageStream abrufen:

((AudioStageStream)stream).setStatsCallback((peak, rms) -> { // handle statistics });

Wenn ein Teilnehmer die Veröffentlichung beendet oder dessen Abonnement beendet wird, wird die Funktion onStreamsRemoved mit den Streams aufgerufen, die entfernt wurden. Hostanwendungen sollten dies als Signal nutzen, um den Videostream des Teilnehmers aus der Ansichtshierarchie zu entfernen.

onStreamsRemoved 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 onStreamsRemoved 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 streamsToPublishForParticipant zurückgegeben wird.

Wichtig: Wenn nach einem Aufruf von refreshStrategy eine neue LocalStageStream-Objekt-Instance von streamsToPublishForParticipant 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 ein Teilnehmer den Stummschaltungsstatus seines Video- oder Audiostreams ändert, wird die Funktion onStreamMutedChanged des Renderers mit einer Liste der Streams aufgerufen, die sich geändert haben. Verwenden Sie die Methode getMuted für StageStream, um die Benutzeroberfläche entsprechend zu aktualisieren.

@Override void onStreamsMutedChanged(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo, @NonNull List<StageStream> streams) { for (StageStream stream : streams) { boolean muted = stream.getMuted(); // handle UI changes } }

Abrufen von WebRTC-Statistiken

Um die neuesten WebRTC-Statistiken für einen veröffentlichten oder abonnierten Stream abzurufen, verwenden Sie requestRTCStats für StageStream. Nach Abschluss einer Erfassung erhalten Sie Statistiken über den StageStream.Listener, der für StageStream eingestellt werden kann.

stream.requestRTCStats(); @Override void onRTCStats(Map<String, Map<String, String>> statsMap) { for (Map.Entry<String, Map<String, string>> stat : statsMap.entrySet()) { for(Map.Entry<String, String> member : stat.getValue().entrySet()) { Log.i(TAG, stat.getKey() + “ has member “ + member.getKey() + “ with value “ + member.getValue()); } } }

Abrufen von Teilnehmerattributen

Wenn Sie Attribute in der Vorgangsanfrage CreateParticipantToken angeben, können Sie die Attribute in den Eigenschaften von ParticipantInfo einsehen:

@Override void onParticipantJoined(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo) { for (Map.Entry<String, String> entry : participantInfo.userInfo.entrySet()) { Log.i(TAG, “attribute: “ + entry.getKey() + “ = “ + entry.getValue()); } }

Abrufen von SEI-Daten (Supplemental Enhancement Information)

Die NAL-Einheit für Supplemental Enhancement Information (SEI) wird verwendet, um Frame-orientierte Metadaten zusammen mit dem Video zu speichern. Subscriber können SEI-Nutzlasten von einem Publisher lesen, der H.264-Video veröffentlicht, indem sie die Eigenschaften embeddedMessages der ImageDeviceFrame-Objekte aus dem ImageDevice des Publishers überprüfen. Erlangen Sie dazu das ImageDevice eines Publishers und beobachten Sie dann jeden Frame über einen Callback an setOnFrameCallback, wie im folgenden Beispiel gezeigt:

// in a StageRenderer’s onStreamsAdded function, after acquiring the new ImageStream val imageDevice = imageStream.device as ImageDevice imageDevice.setOnFrameCallback(object : ImageDevice.FrameCallback { override fun onFrame(frame: ImageDeviceFrame) { for (message in frame.embeddedMessages) { if (message is UserDataUnregisteredSeiMessage) { val seiMessageBytes = message.data val seiMessageUUID = message.uuid // interpret the message's data based on the UUID } } } })

Fortsetzen der Sitzung im Hintergrund

Wenn die App in den Hintergrund wechselt, können Sie die Veröffentlichung beenden oder das Abonnement auf das Audio anderer Remote-Teilnehmer beschränken. Dazu aktualisieren Sie die Implementierung Ihrer Strategy, um die Veröffentlichung zu beenden und AUDIO_ONLY zu abonnieren (oder gegebenenfalls NONE).

// Local variables before going into the background boolean shouldPublish = true; Stage.SubscribeType subscribeType = Stage.SubscribeType.AUDIO_VIDEO; // Stage.Strategy implementation @Override boolean shouldPublishFromParticipant(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo) { return shouldPublish; } @Override Stage.SubscribeType shouldSubscribeToParticipant(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo) { return subscribeType; } // In our Activity, modify desired publish/subscribe when we go to background, then call refreshStrategy to update the stage @Override void onStop() { super.onStop(); shouldPublish = false; subscribeTpye = Stage.SubscribeType.AUDIO_ONLY; stage.refreshStrategy(); }

Mehrschichtige Kodierung mit Simulcast

Bei der mehrschichtigen Kodierung mit Simulcast handelt es sich um ein Feature für IVS-Echtzeit-Streaming, mit dessen Hilfe Publisher mehrere Videoschichten unterschiedlicher Qualität senden können. Subscriber können diese Schichten dynamisch oder manuell konfigurieren. Das Feature wird im Dokument Streaming-Optimierungen ausführlicher beschrieben.

Konfigurieren mehrschichtiger Kodierung (Publisher)

Um als Publisher die mehrschichtige Kodierung mit Simulcast zu aktivieren, fügen Sie dem LocalStageStream bei der Instanziierung die folgende Konfiguration hinzu:

// Enable Simulcast StageVideoConfiguration config = new StageVideoConfiguration(); config.simulcast.setEnabled(true); ImageLocalStageStream cameraStream = new ImageLocalStageStream(frontCamera, config); // Other Stage implementation code

Je nach der in der Videokonfiguration eingestellten Auflösung wird eine festgelegte Anzahl von Schichten kodiert und gesendet, wie im Abschnitt Standardmäßige Schichten, Qualitäten und Bildraten von Streaming-Optimierungen definiert.

Außerdem können Sie optional einzelne Ebenen innerhalb der Simulcast-Konfiguration konfigurieren:

// Enable Simulcast StageVideoConfiguration config = new StageVideoConfiguration(); config.simulcast.setEnabled(true); List<StageVideoConfiguration.Simulcast.Layer> simulcastLayers = new ArrayList<>(); simulcastLayers.add(StagePresets.SimulcastLocalLayer.DEFAULT_720); simulcastLayers.add(StagePresets.SimulcastLocalLayer.DEFAULT_180); config.simulcast.setLayers(simulcastLayers); ImageLocalStageStream cameraStream = new ImageLocalStageStream(frontCamera, config); // Other Stage implementation code

Alternativ können Sie eigene benutzerdefinierte Ebenenkonfigurationen für bis zu drei Ebenen erstellen. Wenn Sie ein leeres Array oder keinen Wert angeben, werden die oben beschriebenen Standardwerte verwendet. Ebenen werden mit den folgenden erforderlichen Eigenschaftssetzern beschrieben:

  • setSize: Vec2;

  • setMaxBitrate: integer;

  • setMinBitrate: integer;

  • setTargetFramerate: integer;

Ausgehend von den Voreinstellungen können Sie entweder einzelne Eigenschaften überschreiben oder eine völlig neue Konfiguration erstellen:

// Enable Simulcast StageVideoConfiguration config = new StageVideoConfiguration(); config.simulcast.setEnabled(true); List<StageVideoConfiguration.Simulcast.Layer> simulcastLayers = new ArrayList<>(); // Configure high quality layer with custom framerate StageVideoConfiguration.Simulcast.Layer customHiLayer = StagePresets.SimulcastLocalLayer.DEFAULT_720; customHiLayer.setTargetFramerate(15); // Add layers to the list simulcastLayers.add(customHiLayer); simulcastLayers.add(StagePresets.SimulcastLocalLayer.DEFAULT_180); config.simulcast.setLayers(simulcastLayers); ImageLocalStageStream cameraStream = new ImageLocalStageStream(frontCamera, config); // Other Stage implementation code

Informationen zu Höchstwerten, Grenzwerten und Fehlern, die bei der Konfiguration einzelner Ebenen ausgelöst werden können, finden Sie in der SDK-Referenzdokumentation.

Konfigurieren mehrschichtiger Kodierung (Subscriber)

Subscriber müssen nichts unternehmen, um die mehrschichtige Kodierung zu aktivieren. Wenn ein Publisher Simulcast-Schichten sendet, passt sich der Server standardmäßig dynamisch den Schichten an, um je nach Gerät und Netzwerkbedingungen des Subscribers die optimale Qualität auszuwählen.

Alternativ gibt es mehrere nachfolgend beschriebene Optionen, um explizite Schichten auszuwählen, die der Publisher sendet.

Option 1: Einstellung für die Qualität der Anfangsschicht

Mit der Strategie subscribeConfigurationForParticipant können Sie auswählen, welche Anfangsschicht Sie als Subscriber erhalten möchten:

@Override public SubscribeConfiguration subscribeConfigrationForParticipant(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo) { SubscribeConfiguration config = new SubscribeConfiguration(); config.simulcast.setInitialLayerPreference(SubscribeSimulcastConfiguration.InitialLayerPreference.LOWEST_QUALITY); return config; }

Standardmäßig wird Subscribern zunächst immer die Schicht mit der niedrigsten Qualität gesendet. Nach und nach wird die Qualität gesteigert, bis die Schicht mit der höchsten Qualität erreicht ist. Das optimiert den Bandbreitenverbrauch der Endbenutzer, verkürzt die Zeit bis zum Abspielen des Videos und verringert das anfängliche Einfrieren von Videos bei Benutzern in Netzwerken mit geringerer Bandbreite.

Folgende Optionen sind für InitialLayerPreference verfügbar:

  • LOWEST_QUALITY – Der Server stellt zuerst die Videoschicht mit der niedrigsten Qualität bereit. Dadurch werden der Bandbreitenverbrauch und die Zeit bis zum Abspielen von Medien optimiert. Die Qualität ist definiert als die Kombination aus Größe, Bitrate und Bildrate des Videos. Beispielsweise weisen 720p-Videos eine geringere Qualität auf als 1080p-Videos.

  • HIGHEST_QUALITY – Der Server stellt zuerst die Videoschicht mit der höchsten Qualität bereit. Das optimiert die Qualität, kann aber die Zeit bis zum Abspielen von Medien verlängern. Die Qualität ist definiert als die Kombination aus Größe, Bitrate und Bildrate des Videos. Beispielsweise weisen 1080p-Videos eine höhere Qualität auf als 720p-Videos.

Hinweis: Damit die anfänglichen Schichteinstellungen (der Aufruf setInitialLayerPreference) wirksam werden, muss ein neues Abonnement abgeschlossen werden, da diese Updates für das aktive Abonnement nicht gelten.

Option 2: Bevorzugte Schicht für Streams

Mit der Strategiemethode preferredLayerForStream können Sie eine Schicht auswählen, nachdem der Stream gestartet wurde. Diese Strategiemethode erhält die Teilnehmer- und Streaminformationen, sodass Sie eine Schicht für jeden Teilnehmer auswählen können. Das SDK ruft diese Methode als Reaktion auf bestimmte Ereignisse auf, z. B. wenn sich die Streamschichten ändern, sich der Teilnehmerstatus ändert oder die Hostanwendung die Strategie aktualisiert.

Der Strategiemodus gibt ein RemoteStageStream.Layer-Objekt zurück, wobei es sich um Folgendes handeln kann:

  • ein Schichtobjekt, z. B. eines, das von RemoteStageStream.getLayers zurückgegeben wird

  • null, was bedeutet, dass keine Schicht ausgewählt werden sollte und eine dynamische Anpassung bevorzugt wird

Bei der folgenden Strategie wählen die Benutzer beispielsweise immer die Videoschicht mit der niedrigsten verfügbaren Qualität aus:

@Nullable @Override public RemoteStageStream.Layer preferredLayerForStream(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo, @NonNull RemoteStageStream stream) { return stream.getLowestQualityLayer(); }

Um die Schichtauswahl zurückzusetzen und zur dynamischen Anpassung zurückzukehren, geben Sie in der Strategie null oder undefiniert zurück. In diesem Beispiel ist appState eine Platzhaltervariable, die den Status der Hostanwendung darstellt.

@Nullable @Override public RemoteStageStream.Layer preferredLayerForStream(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo, @NonNull RemoteStageStream stream) { if (appState.isAutoMode) { return null; } else { return appState.layerChoice; } }

Option 3: Helferobjekte für RemoteStageStream-Schicht

RemoteStageStream weist mehrere Helferobjekte auf, mit deren Hilfe Entscheidungen über die Schichtauswahl getroffen und Endbenutzern die entsprechende Auswahl angezeigt werden kann:

  • Schichtereignisse – Neben StageRenderer verfügt der RemoteStageStream.Listener über Ereignisse, die Änderungen bei der Schicht- und Simulcast-Anpassung kommunizieren:

    • void onAdaptionChanged(boolean adaption)

    • void onLayersChanged(@NonNull List<Layer> layers)

    • void onLayerSelected(@Nullable Layer layer, @NonNull LayerSelectedReason reason)

  • SchichtmethodenRemoteStageStream verfügt über mehrere Helfermethoden, mit denen Informationen über den Stream und die präsentierten Schichten abgerufen werden können. Diese Methoden sind sowohl für den in der Strategie preferredLayerForStream bereitgestellten Remote-Stream als auch für Remote-Streams verfügbar, die über StageRenderer.onStreamsAdded verfügbar gemacht werden.

    • stream.getLayers

    • stream.getSelectedLayer

    • stream.getLowestQualityLayer

    • stream.getHighestQualityLayer

    • stream.getLayersWithConstraints

Einzelheiten finden Sie im Abschnitt zur Klasse RemoteStageStream in der SDK-Referenzdokumentation. Falls als Grund für LayerSelected UNAVAILABLE zurückgegeben wird, bedeutet das, dass die angeforderte Schicht nicht ausgewählt werden konnte. Stattdessen wird eine bestmögliche Auswahl getroffen. Dabei handelt es sich in der Regel um eine Schicht mit niedrigerer Qualität, um die Stabilität des Streams zu gewährleisten.

Einschränkungen der Videokonfiguration

Das SDK unterstützt kein Erzwingen des Hoch- oder Querformats mit StageVideoConfiguration.setSize(BroadcastConfiguration.Vec2 size). Im Hochformat wird die kleinere Dimension als Breite verwendet, im Querformat als Höhe. Das bedeutet, dass die folgenden beiden Aufrufe von setSize die gleiche Auswirkung auf die Videokonfiguration haben:

StageVideo Configuration config = new StageVideo Configuration(); config.setSize(BroadcastConfiguration.Vec2(720f, 1280f); config.setSize(BroadcastConfiguration.Vec2(1280f, 720f);

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. Es gibt zwei Hauptfehler im Zusammenhang mit der Unterbrechung der Netzwerkverbindung:

  • Fehlercode 1400, Meldung: „Die PeerConnection wurde aufgrund eines unbekannten Netzwerkfehlers unterbrochen.“

  • Fehlercode 1300, Meldung: „Die Zahl der Wiederholungsversuche ist ausgeschöpft.“

Wenn der erste Fehler empfangen wird, der zweite jedoch nicht, ist das SDK immer noch mit der Bühne verbunden und versucht, die Verbindungen automatisch wiederherzustellen. Zur Sicherheit können Sie refreshStrategy ohne Änderungen an den Rückgabewerten der Strategiemethode aufrufen, um einen manuellen Neuverbindungsversuch auszulösen.

Wenn der zweite Fehler empfangen wird, sind die Neuverbindungsversuche des SDK fehlgeschlagen und das lokale Gerät ist nicht mehr mit der Bühne verbunden. Versuchen Sie in diesem Fall, der Bühne erneut beizutreten, indem Sie join aufrufen, nachdem die Netzwerkverbindung wiederhergestellt wurde.

Im Allgemeinen deutet das Auftreten von Fehlern nach dem erfolgreichen Beitritt zu einer Bühne darauf hin, dass das SDK beim Wiederherstellen einer Verbindung nicht erfolgreich war. Erstellen Sie ein neues Stage-Objekt und versuchen Sie, der Bühne beizutreten, wenn sich die Netzwerkbedingungen verbessern.

Verwenden von Bluetooth-Mikrofonen

Um mit Bluetooth-Mikrofongeräten zu veröffentlichen, müssen Sie eine Bluetooth-SCO-Verbindung herstellen:

Bluetooth.startBluetoothSco(context); // Now bluetooth microphones can be used … // Must also stop bluetooth SCO Bluetooth.stopBluetoothSco(context);