Publicación y suscripción con el SDK de transmisión para web de IVS | Transmisión en tiempo real - Amazon IVS

Publicación y suscripción con el SDK de transmisión para web 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 web 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 eventos. 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 y salir de un escenario es sencillo:

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

Strategy (Estrategia)

La interfaz StageStrategy 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, shouldPublishParticipant y stageStreamsToPublish. Todas se analizan a continuación.

Para usar una estrategia definida, pásela al constructor de Stage. El siguiente es un ejemplo completo de una aplicación que utiliza una estrategia para publicar la cámara web de un participante en el escenario y suscribirse a todos los participantes. El propósito de cada función de estrategia necesaria se explica detalladamente en las siguientes secciones.

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();

Suscripción a participantes

shouldSubscribeToParticipant(participant: StageParticipantInfo): SubscribeType

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 al emitir eventos durante todo el proceso.

Este es un ejemplo de implementación:

const strategy = { shouldSubscribeToParticipant: (participant) => { return SubscribeType.AUDIO_VIDEO; } // ... other strategy functions }

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. Por ejemplo, supongamos que la aplicación proporciona un atributo role al crear el token con CreateParticipantToken. La aplicación podría utilizar la propiedad attributes en StageParticipantInfo para suscribirse de forma selectiva a los participantes en función de los atributos proporcionados por el servidor:

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 }

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 moderadores se vean entre sí, pero permanezcan invisibles para los invitados.

Configuración de la suscripción a participantes

subscribeConfiguration(participant: StageParticipantInfo): SubscribeConfiguration

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 en la documentación de referencia del SDK.

Este es un ejemplo de implementación:

const strategy = { subscribeConfiguration: (participant) => { return { jitterBuffer: { minDelay: JitterBufferMinDelay.MEDIUM } } // ... other strategy functions }

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

shouldPublishParticipant(participant: StageParticipantInfo): boolean

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:

const strategy = { shouldPublishParticipant: (participant) => { return true; } // . . . other strategies properties }

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

stageStreamsToPublish(): LocalStageStream[];

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 shouldPublishParticipant (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 shouldPublishParticipant cambió, se 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á cambios en el escenario.

Si el valor devuelto de shouldSubscribeToParticipant cambia de AUDIO_VIDEO a AUDIO_ONLY, la transmisión de video se elimina 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.

Eventos

Una instancia Stage es un emisor de eventos. Con stage.on(), el estado del escenario se comunica a la aplicación host. Por lo general, los eventos permiten actualizar por completo la interfaz de usuario de la aplicación host. Los eventos son los siguientes:

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) => {})

Para la mayoría de estos métodos, se proporciona la ParticipantInfo correspondiente.

No se espera que la información que proporcionan los eventos 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 STAGE_PARTICIPANT_PUBLISH_STATE_CHANGED. 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.

Publicación de una transmisión multimedia

Los dispositivos locales, como micrófonos y cámaras, se recuperan siguiendo los mismos pasos descritos anteriormente en Recuperación de MediaStream de un dispositivo. En el ejemplo, utilizamos MediaStream para crear una lista de objetos LocalStageStream que el SDK utiliza para publicar:

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 } }

Publicación de una pantalla compartida

Las aplicaciones suelen necesitar publicar una pantalla compartida además de la cámara web del usuario. Para publicar una pantalla compartida, es necesario crear un token adicional para la fase, específicamente para publicar el contenido multimedia de la pantalla compartida. Use getDisplayMedia y restrinja la resolución a un máximo de 720p. Después de eso, los pasos son similares a publicar una cámara en la fase.

// 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();

Visualización y eliminación de participantes

Cuando se complete la suscripción, recibirá una matriz de objetos StageStream a través del evento STAGE_PARTICIPANT_STREAMS_ADDED. El evento también le brinda información sobre los participantes que le será de ayuda al visualizar las transmisiones multimedia:

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)); })

Cuando un participante deja de publicar o anula la suscripción de una transmisión, se invoca la función STAGE_PARTICIPANT_STREAMS_REMOVED 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 DOM.

STAGE_PARTICIPANT_STREAMS_REMOVED 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 a AUDIO_ONLY.

  • El participante remoto abandona el escenario.

  • El participante local abandona el escenario.

Ya que STAGE_PARTICIPANT_STREAMS_REMOVED 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 stageStreamsToPublish.

Importante: Si stageStreamsToPublish 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 los participantes cambian el estado de sonido desactivado de su video o audio, el evento STAGE_STREAM_MUTE_CHANGED se activa con una lista de las transmisiones que cambiaron. Use la propiedad isMuted en StageStream para actualizar su interfaz de usuario según corresponda:

stage.on(StageEvents.STAGE_STREAM_MUTE_CHANGED, (participant, stream) => { if (stream.streamType === 'video' && stream.isMuted) { // handle UI changes for video track getting muted } })

Además, puede consultar StageParticipantInfo para obtener información del estado sobre si el audio o el video están silenciados:

stage.on(StageEvents.STAGE_STREAM_MUTE_CHANGED, (participant, stream) => { if (participant.videoStopped || participant.audioMuted) { // handle UI changes for either video or audio } })

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 getStats en StageStream. Este es un método asíncrono con el que puede recuperar estadísticas mediante await o encadenando una promesa. El resultado es un RTCStatsReport, un diccionario que contiene todas las estadísticas estándar.

try { const stats = await stream.getStats(); } catch (error) { // Unable to retrieve stats }

Optimización del contenido multimedia

Se recomienda limitar las llamadas a getUserMedia y a getDisplayMedia con las siguientes restricciones para obtener el mejor rendimiento:

const CONSTRAINTS = { video: { width: { ideal: 1280 }, // Note: flip width and height values if portrait is desired height: { ideal: 720 }, framerate: { ideal: 30 }, }, };

Puede restringir aún más el contenido multimedia mediante opciones adicionales que se pasan al constructor LocalStageStream:

const localStreamOptions = { minBitrate?: number; maxBitrate?: number; maxFramerate?: number; simulcast: { enabled: boolean } } const localStream = new LocalStageStream(track, localStreamOptions)

En el código anterior:

  • minBitrate establece la velocidad de bits mínima que se espera que utilice el navegador. Sin embargo, una transmisión de video de poca complejidad puede hacer que el codificador establezca una velocidad inferior a esta velocidad de bits.

  • maxBitrate establece una velocidad de bits máxima que se espera que el navegador no supere para esta transmisión.

  • maxFramerate establece una velocidad de fotogramas máxima que se espera que el navegador no supere para esta transmisión.

  • La opción simulcast solo se puede utilizar en navegadores basados en Chromium. Permite enviar tres capas de representación de la transmisión.

    • Esto permite al servidor elegir qué representación enviar a otros participantes, en función de sus limitaciones de red.

    • Cuando simulcast se especifica junto con un valor de maxBitrate o maxFramerate, se espera que la capa de representación más alta se configure teniendo en cuenta estos valores, siempre que maxBitrate no sea inferior al valor predeterminado de maxBitrate (900 kbps) de la segunda capa más alta del SDK interno.

    • Si maxBitrate se especifica como demasiado baja en comparación con el valor predeterminado de la segunda capa más alta, simulcast se desactivará.

    • simulcast no se puede activar y desactivar sin volver a publicar el contenido multimedia. Para ello, shouldPublishParticipant tiene que devolver false y llamar a refreshStrategyshouldPublishParticipant tiene que devolver true y llamar a refreshStrategy otra vez.

Obtención de los atributos de los participantes

Si especifica atributos en la solicitud del punto de conexión de CreateParticipantToken, puede ver los atributos en las propiedades de StageParticipantInfo:

stage.on(StageEvents.STAGE_PARTICIPANT_JOINED, (participant) => { console.log(`Participant ${participant.id} info:`, participant.attributes); })

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.

En términos generales, el estado del escenario se puede gestionar mediante el evento STAGE_CONNECTION_STATE_CHANGED:

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; })

En general, puede ignorar un estado de error que se produzca tras unirse correctamente a una fase, ya que el SDK intentará recuperarse internamente. Si el SDK informa de un estado ERRORED y la fase permanece en el estado CONNECTING durante un periodo prolongado (por ejemplo, 30 segundos o más), es probable que se haya desconectado de la red.

Transmisión del escenario a un canal de IVS

Para transmitir un escenario, cree una sesión de IVSBroadcastClient independiente y, a continuación, siga las instrucciones habituales para la transmisión con el SDK, descritas con anterioridad. La lista de StageStream expuestas mediante STAGE_PARTICIPANT_STREAMS_ADDED se puede utilizar para recuperar las transmisiones multimedia participantes aplicables a la composición del flujo de transmisión, de la siguiente manera:

// 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; } }) })

Si lo desea, puede componer un escenario y transmitirlo a un canal de IVS de baja latencia para llegar a un público más amplio. Consulte Habilitación de varios hosts en una transmisión de Amazon IVS en la Guía del usuario de transmisión de baja latencia.