Publicación y suscripción con el SDK de transmisión para Android de IVS | Transmisión en tiempo real
Este documento explica los pasos necesarios para publicar y suscribirse a una fase mediante el SDK de transmisión para Android de la transmisión en tiempo real de IVS.
Conceptos
Los siguientes tres conceptos básicos subyacen a la funcionalidad de transmisión en tiempo real: escenario, estrategia y renderizador. El objetivo del diseño es minimizar la cantidad de lógica necesaria por parte del cliente para crear un producto que funcione.
Escenario
La clase Stage
es el principal punto de interacción entre la aplicación host y el SDK. Representa el escenario como tal y se usa para entrar y salir de él. Para crear un escenario e incorporarse a él, es necesaria una cadena de símbolos válida y que no haya vencido del plano de control (representada como token
). Entrar a un escenario y salir de él es sencillo.
Stage stage = new Stage(context, token, strategy); try { stage.join(); } catch (BroadcastException exception) { // handle join exception } stage.leave();
También se puede adjuntar StageRenderer
en la clase Stage
:
stage.addRenderer(renderer); // multiple renderers can be added
Strategy (Estrategia)
La interfaz Stage.Strategy
proporciona una forma para que la aplicación host comunique el estado deseado del escenario al SDK. Es necesario implementar las siguientes tres funciones: shouldSubscribeToParticipant
, shouldPublishFromParticipant
y stageStreamsToPublishForParticipant
. Todas se analizan a continuación.
Suscripción a participantes
Stage.SubscribeType shouldSubscribeToParticipant(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo);
Cuando un participante remoto se incorpora al escenario, el SDK consulta la aplicación host sobre el estado de suscripción deseado de ese participante. Las opciones son NONE
, AUDIO_ONLY
y AUDIO_VIDEO
. Al devolver un valor para esta función, la aplicación host no tiene que preocuparse por el estado de la publicación, el estado actual de la suscripción ni el estado de la conexión del escenario. Si se devuelve AUDIO_VIDEO
, el SDK espera a que el participante remoto publique antes de suscribirse y actualiza la aplicación host a través del renderizador durante todo el proceso.
Este es un ejemplo de implementación:
@Override Stage.SubscribeType shouldSubscribeToParticipant(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo) { return Stage.SubscribeType.AUDIO_VIDEO; }
Esta es la implementación completa de esta función para una aplicación host que siempre quiere que todos los participantes se vean entre sí; por ejemplo, una aplicación de videochat.
También son posibles implementaciones más avanzadas. Utilice la propiedad userInfo
en ParticipantInfo
para suscribirse de forma selectiva a los participantes en función de los atributos proporcionados por el servidor:
@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; } }
Esto se puede utilizar para crear un escenario en el que los moderadores puedan monitorear a todos los invitados sin que ellos mismos los vean ni los escuchen. La aplicación host podría utilizar una lógica empresarial adicional para permitir que los moderados se vean entre sí, pero permanezcan invisibles para los invitados.
Configuración de la suscripción a participantes
SubscribeConfiguration subscribeConfigurationForParticipant(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo);
Si se está subscribiendo a un participante remoto (consulte Suscripción a participantes), el SDK consulta a la aplicación host sobre una configuración de la subscrición personalizada para ese participante. Esta configuración es opcional y permite a la aplicación host controlar determinados aspectos del comportamiento de los suscriptores. Para obtener información sobre lo que se puede configurar, consulte SubscribeConfiguration
Este es un ejemplo de implementación:
@Override
public SubscribeConfiguration subscribeConfigrationForParticipant(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo) {
SubscribeConfiguration config = new SubscribeConfiguration();
config.jitterBuffer.setMinDelay(JitterBufferConfiguration.JitterBufferDelay.MEDIUM());
return config;
}
Esta implementación actualiza el retraso mínimo del búfer de fluctuación para todos los participantes suscritos al valor preestablecido MEDIUM
.
Como en el caso de shouldSubscribeToParticipant
, también son posibles implementaciones más avanzadas. El valor de ParticipantInfo
proporcionado se puede utilizar para actualizar selectivamente la configuración de suscripción para participantes específicos.
Se recomienda utilizar los comportamientos predeterminados. Especifique la configuración personalizada solo si hay un comportamiento en particular que desee cambiar.
Publicación
boolean shouldPublishFromParticipant(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo);
Una vez realizada la conexión al escenario, el SDK consulta la aplicación host para ver si un participante en particular tiene que publicar. Esto solo se invoca en los participantes locales que tienen permiso para publicar en función del token proporcionado.
Este es un ejemplo de implementación:
@Override boolean shouldPublishFromParticipant(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo) { return true; }
Esto es para una aplicación de videochat estándar en la que los usuarios siempre quieren publicar. Pueden activar y desactivar el sonido y el video para ocultarse o verse y escucharse al instante. (También pueden usar la opción de publicar o anular la publicación, pero esto es mucho más lento. Es preferible silenciar o activar el sonido en casos de uso en los que se quiera cambiar la visibilidad de manera frecuente).
Elección de las transmisiones que publicar
@Override List<LocalStageStream> stageStreamsToPublishForParticipant(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo); }
Al publicar, esto se utiliza para determinar qué transmisiones de audio y video se tienen que publicar. Esto se explica en mayor detalle más adelante en Publicación de una transmisión multimedia.
Actualización de la estrategia
La estrategia pretende ser dinámica: los valores devueltos por cualquiera de las funciones anteriores se pueden cambiar en cualquier momento. Por ejemplo, si la aplicación host no quiere publicar hasta que el usuario final presione un botón, puede devolver una variable de shouldPublishFromParticipant
(como hasUserTappedPublishButton
). Cuando esa variable cambie en función de una interacción del usuario final, llame a stage.refreshStrategy()
para indicar al SDK que debe consultar la estrategia a fin de obtener los valores más recientes y aplicar solo los cambios. Si el SDK observa que el valor shouldPublishFromParticipant
cambió, iniciará el proceso de publicación. Si las consultas del SDK y todas las funciones devuelven el mismo valor que antes, la llamada a refreshStrategy
no hará ninguna modificación en el escenario.
Si el valor devuelto de shouldSubscribeToParticipant
cambia de AUDIO_VIDEO
a AUDIO_ONLY
, la transmisión de video se eliminará para todos los participantes con valores devueltos modificados, si ya existía con anterioridad una transmisión de video.
Por lo general, el escenario utiliza la estrategia para aplicar de la manera más eficiente la diferencia entre las estrategias anteriores y actuales, sin que la aplicación host tenga que preocuparse por todo el estado necesario para administrarla correctamente. Por eso, piense en la llamada a stage.refreshStrategy()
como una operación barata, porque no hace nada a no ser que cambie la estrategia.
Renderer
La interfaz StageRenderer
comunica el estado del escenario a la aplicación host. Por lo general, los eventos proporcionados por el renderizador permiten el funcionamiento completo de las actualizaciones de la interfaz de usuario de la aplicación host. El renderizador ofrece el siguiente resultado:
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);
Para la mayoría de estos métodos, se proporcionan el Stage
y la ParticipantInfo
correspondientes.
No se espera que la información que proporciona el renderizador afecte a los valores de retorno de la estrategia. Por ejemplo, no se espera que el valor devuelto de shouldSubscribeToParticipant
cambie cuando se llama a onParticipantPublishStateChanged
. Si la aplicación host quiere suscribirse a un participante en particular, debe devolver el tipo de suscripción deseado, independientemente del estado de publicación de ese participante. El SDK es responsable de garantizar que se aplique el estado deseado de la estrategia en el momento correcto según el estado del escenario.
StageRenderer
se puede adjuntar a la clase del escenario:
stage.addRenderer(renderer); // multiple renderers can be added
Tenga en cuenta que solo los participantes que publican desencadenan onParticipantJoined
. onParticipantLeft
se desencadena cada vez que un participante deja de publicar o abandona la sesión del escenario.
Publicación de una transmisión multimedia
Los dispositivos locales, como las cámaras y los micrófonos integrados, se detectan a través de DeviceDiscovery
. A continuación, se muestra un ejemplo de cómo seleccionar la cámara frontal y el micrófono predeterminados y devolverlos como LocalStageStreams
para que los publique el 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; }
Visualización y eliminación de participantes
Cuando se complete la suscripción, recibirá una matriz de objetos StageStream
a través de la función onStreamsAdded
del renderizador. Puede obtener la vista previa de una ImageStageStream
:
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);
Puede recuperar las estadísticas de audio de una AudioStageStream
:
((AudioStageStream)stream).setStatsCallback((peak, rms) -> { // handle statistics });
Cuando un participante deja de publicar o anula su suscripción, se invoca la función onStreamsRemoved
con las transmisiones que se eliminaron. Las aplicaciones host tienen que usar esto como una señal para eliminar la transmisión de video del participante de la jerarquía de visualización.
onStreamsRemoved
se invoca para todas las situaciones en las que se puede eliminar una transmisión, como las siguientes:
-
El participante remoto deja de publicar.
-
Un dispositivo local cancela la suscripción o cambia la suscripción de
AUDIO_VIDEO
aAUDIO_ONLY
. -
El participante remoto abandona el escenario.
-
El participante local abandona el escenario.
Ya que onStreamsRemoved
se invoca en todas las situaciones, no es necesaria una lógica empresarial personalizada para eliminar a los participantes de la interfaz de usuario durante las operaciones de licencia remota o local.
Activación y desactivación del sonido de las transmisiones multimedia
Los objetos de LocalStageStream
tienen una función setMuted
que controla si la transmisión está silenciada. Esta función se puede invocar en la transmisión antes o después de que la devuelva la función de estrategia streamsToPublishForParticipant
.
Importante: Si streamsToPublishForParticipant
devuelve una nueva instancia de objeto de LocalStageStream
después de una llamada a refreshStrategy
, el estado de sonido desactivado del nuevo objeto de transmisión se aplicará al escenario. Tenga cuidado al crear nuevas instancias LocalStageStream
para asegurarse de que se mantenga el estado de sonido desactivado que se espera.
Monitoreo del estado de sonido desactivado en los contenidos multimedia del participante remoto
Cuando un participante cambia el estado de sonido desactivado de su transmisión de video o audio, se invoca la función onStreamMutedChanged
de renderizado con una lista de las transmisiones que cambiaron. Use el método getMuted
en StageStream
para actualizar la interfaz de usuario según corresponda.
@Override void onStreamsMutedChanged(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo, @NonNull List<StageStream> streams) { for (StageStream stream : streams) { boolean muted = stream.getMuted(); // handle UI changes } }
Obtención de estadísticas de WebRTC
Para obtener las estadísticas más recientes de WebRTC de una transmisión de publicación o de suscripción, utilice requestRTCStats
en StageStream
. Cuando se complete una recopilación, recibirá estadísticas a través de StageStream.Listener
, que se puede configurar en StageStream
.
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()); } } }
Obtención de los atributos de los participantes
Si especifica atributos en la solicitud de la operación de CreateParticipantToken
, podrá ver los atributos en las propiedades de ParticipantInfo
:
@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()); } }
Obtención de información de mejora adicional (SEI)
La unidad NAL de información de mejora adicional (SEI) se usa para almacenar metadatos alineados con los fotogramas junto con el video. Los clientes suscriptores pueden leer las cargas útiles SEI de un publicador que publica un video H.264 mediante la inspección de la propiedad embeddedMessages
en los objetos ImageDeviceFrame
que salen del ImageDevice
del publicador. Para ello, adquiera el ImageDevice
de un publicador y, a continuación, observe cada fotograma a través de una devolución de llamada proporcionada a setOnFrameCallback
, como se muestra en el siguiente ejemplo:
// 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 } } } })
Continuación de la sesión en segundo plano
Cuando la aplicación pase a segundo plano, es posible que quiera dejar de publicar o suscribirse solo al audio de otros participantes remotos. Para ello, actualice la implementación Strategy
para detener la publicación y suscríbase a AUDIO_ONLY
(o a NONE
, si corresponde).
// 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(); }
Codificación por capas con la transmisión simultánea
La codificación por capas con transmisión simultánea es una característica de transmisión en tiempo real de IVS que permite a quienes publican enviar múltiples capas de video con diferentes niveles de calidad, mientras que los suscriptores pueden cambiar estas capas de manera dinámica o manual. Esta característica se describe con más detalle en el documento Optimizaciones de transmisión.
Configuración de la codificación por capas (publicador)
Como publicador, para habilitar la codificación por capas con transmisión simultánea, agregue la siguiente configuración a LocalStageStream
en la instanciación:
// Enable Simulcast StageVideoConfiguration config = new StageVideoConfiguration(); config.simulcast.setEnabled(true); ImageLocalStageStream cameraStream = new ImageLocalStageStream(frontCamera, config); // Other Stage implementation code
En función de la resolución establecida en la configuración de video, se codificará y enviará una cantidad determinada de capas, tal como se define en la sección Capas, calidades y velocidades de fotogramas predeterminadas de Optimizaciones de transmisión.
Configuración de la codificación por capas (suscriptor)
Como suscriptor, no es necesario hacer nada para habilitar la codificación por capas. Si un publicador envía capas de transmisión simultánea, el servidor, de forma predeterminada, se adaptará dinámicamente entre las capas para elegir la calidad óptima en función de las condiciones de la red y del dispositivo del suscriptor.
Como alternativa, existen varias opciones para seleccionar las capas explícitas que envía el publicador, tal y como se describe a continuación.
Opción 1: preferencia de calidad de la capa inicial
Mediante la estrategia subscribeConfiguration
, es posible elegir qué capa inicial desea recibir como suscriptor:
@Override public SubscribeConfiguration subscribeConfigrationForParticipant(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo) { SubscribeConfiguration config = new SubscribeConfiguration(); config.simulcast.setInitialLayerPreference(SubscribeSimulcastConfiguration.InitialLayerPreference.LOWEST_QUALITY); return config; }
De forma predeterminada, a los suscriptores siempre se les envía primero la capa de calidad más baja; poco a poco se incrementa hasta llegar a la capa de calidad más alta. Esto optimiza el consumo de ancho de banda del usuario final y proporciona el mejor tiempo de inicio del video, lo cual reduce las congelaciones iniciales de video para los usuarios en redes más débiles.
Estas opciones se encuentran disponibles para InitialLayerPreference
:
LOWEST_QUALITY
: el servidor entrega primero la capa de video de menor calidad. Esto optimiza tanto el consumo de ancho de banda como el tiempo de inicio del contenido multimedia. La calidad se define como la combinación de tamaño, velocidad de bits y velocidad de fotogramas del video. Por ejemplo, un video 720p es de menor calidad que un video 1080p.HIGHEST_QUALITY
: el servidor entrega primero la capa de video de mayor calidad. Esto optimiza la calidad, pero puede aumentar el tiempo de inicio del contenido multimedia. La calidad se define como la combinación de tamaño, velocidad de bits y velocidad de fotogramas del video. Por ejemplo, el video 1080p es de mayor calidad que el video 720p.
Nota: Para que se apliquen las preferencias de la capa inicial, es necesario volver a suscribirse, ya que estas actualizaciones no se aplican a la suscripción activa.
Opción 2: capa preferida para la transmisión
Una vez iniciada la transmisión, podrá utilizar el método de estrategia preferredLayerForStream
. Este método de estrategia expone la información del participante y de la transmisión.
El método de estrategia se puede devolver de la siguiente manera:
El objeto de capa directamente en función de lo que
RemoteStageStream.getLayers
devuelve.nulo, que indica que no se debe seleccionar ninguna capa y que se prefiere la adaptación dinámica.
Por ejemplo, la siguiente estrategia hará que los usuarios seleccionen siempre la capa de video de menor calidad disponible:
@Nullable @Override public RemoteStageStream.Layer preferredLayerForStream(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo, @NonNull RemoteStageStream stream) { return stream.getLowestQualityLayer(); }
Para restablecer la selección de capas y volver a la adaptación dinámica, devuelva nulo o sin definir en la estrategia. En este ejemplo, appState
es una variable ficticia que representa el estado posible de la aplicación.
@Nullable @Override public RemoteStageStream.Layer preferredLayerForStream(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo, @NonNull RemoteStageStream stream) { if (appState.isAutoMode) { return null; } else { return appState.layerChoice; } }
Opción 3: ayudantes de la capa RemoteStageStream
RemoteStageStream
cuenta con varios ayudantes que se pueden utilizar para tomar decisiones sobre la selección de capas y mostrar las selecciones correspondientes a los usuarios finales:
-
Eventos de capa: además de
StageRenderer
,RemoteStageStream.Listener
cuenta con eventos que comunican los cambios de adaptación de capas y transmisión simultánea:-
void onAdaptionChanged(boolean adaption)
-
void onLayersChanged(@NonNull List<Layer> layers)
void onLayerSelected(@Nullable Layer layer, @NonNull LayerSelectedReason reason)
-
-
Métodos de capa:
RemoteStageStream
tiene varios métodos de ayudante que se pueden utilizar para obtener información sobre la transmisión y las capas que se presentan. Estos métodos están disponibles en la transmisión remota proporcionada en la estrategiapreferredLayerForStream
, así como en las transmisiones remotas expuestas a través deStageRenderer.onStreamsAdded
.stream.getLayers
stream.getSelectedLayer
stream.getLowestQualityLayer
stream.getHighestQualityLayer
stream.getLayersWithConstraints
Para conocer los detalles, consulte la clase RemoteStageStream
en la documentación de referencia del SDKLayerSelected
, si se devuelve UNAVAILABLE
, esto indica que no se ha podido seleccionar la capa solicitada. En su lugar, se selecciona la mejor opción posible, que suele ser una capa de menor calidad para mantener la estabilidad de la transmisión.
Limitaciones de la configuración de video
El SDK no permite forzar el uso del modo vertical ni del horizontal mediante StageVideoConfiguration.setSize(BroadcastConfiguration.Vec2 size)
. En la orientación vertical, la dimensión más pequeña se utiliza como el ancho; en la orientación horizontal, como la altura. Esto significa que las dos siguientes llamadas a setSize
tendrán el mismo efecto en la configuración de video:
StageVideo Configuration config = new StageVideo Configuration(); config.setSize(BroadcastConfiguration.Vec2(720f, 1280f); config.setSize(BroadcastConfiguration.Vec2(1280f, 720f);
Gestión de los problemas de red
Cuando se pierde la conexión de red del dispositivo local, el SDK se intenta volver a conectar internamente sin que el usuario lleve a cabo ninguna acción. En algunos casos, el SDK no funciona de manera correcta y es necesario que el usuario actúe. Hay dos errores principales relacionados con la pérdida de la conexión de red:
-
Código de error 1400, mensaje: “Se ha perdido la conexión de pares debido a un error de red desconocido”
-
Código de error 1300, mensaje: “Se han agotado los reintentos”
Si se recibe el primer error, pero no el segundo, el SDK sigue conectado al escenario e intentará restablecer sus conexiones automáticamente. Como medida de seguridad, puede hacer una llamada a refreshStrategy
sin cambiar los valores devueltos por el método de estrategia para iniciar un intento de reconexión manual.
Si se recibe el segundo error, los intentos de reconexión del SDK fallaron y el dispositivo local ya no está conectado al escenario. En este caso, intente reincorporarse al escenario con una llamada a join
después de que se restablezca la conexión de red.
En general, el que aparezcan errores después de incorporarse a un escenario correctamente indica que el SDK no pudo restablecer la conexión. Cree un nuevo objeto Stage
e intente incorporarse cuando mejoren las condiciones de la red.
Uso de micrófonos Bluetooth
Para publicar con dispositivos de micrófono Bluetooth, debe iniciar una conexión SCO Bluetooth:
Bluetooth.startBluetoothSco(context); // Now bluetooth microphones can be used … // Must also stop bluetooth SCO Bluetooth.stopBluetoothSco(context);