He escrito algunas publicaciones últimamente sobre transmisiones en vivo de múltiples hosts con Amazon Interactive Video Service (Amazon IVS). Es una característica emocionante que abre mundos de posibilidades que simplemente no estaban disponibles hasta hace poco. Primero vimos cómo crear una aplicación de chat en vivo de varios hosts .
A continuación, vimos cómo transmitir esa sesión de chat en vivo a un canal de Amazon IVS.
Cuando analizamos la adición de participantes de chat al cliente de transmisión en la última publicación, probablemente notó que hice un poco de trampa y codifiqué los valores VideoComposition
que le indican al cliente de transmisión el tamaño y la posición del video del participante en el cliente.
Bueno, engañado es una palabra fuerte, digamos que simplifiqué intencionalmente el código para centrarme en el proceso de transmisión de una sesión de chat en vivo.
Esencialmente, lo que estamos buscando aquí es modificar el tamaño y la posición del video del participante en la transmisión para que cuando haya un video, el diseño se vea así:
Pero cuando hay dos videos, el diseño cambiará a algo como esto:
Y cuando son cinco:
Tienes la idea: un diseño dinámico que cambia según la cantidad de participantes.
En esta publicación, veremos un enfoque que podría utilizar para facilitar un poco la creación de un diseño dinámico. Desarrollaremos la solución en la última publicación, por lo que si aún no ha leído esa publicación, probablemente sea una buena idea hacerlo ahora.
En la última publicación, escuchamos un evento llamado STAGE_PARTICIPANT_STREAMS_ADDED
. En el controlador de eventos para ese evento, agregamos a nuestros participantes al DOM y procesamos el audio y el video en la instancia IVSBroadcastClient
.
Para generar un diseño dinámico, necesitaremos realizar un seguimiento de cuántos participantes hay actualmente en la sesión, por lo que agregaremos una matriz llamada participantIds
como una variable global. Modifiquemos el controlador de eventos para enviar la identificación del participante actual a esa matriz.
stage.on(StageEvents.STAGE_PARTICIPANT_STREAMS_ADDED, (participant, streams) => { //add participant id to array participantIds.push(participant.id); renderParticipant(participant, streams); renderVideosToClient(participant, streams.find(s => s.streamType === StreamType.VIDEO)); renderAudioToClient(participant, streams.find(s => s.streamType === StreamType.AUDIO)); updateVideoCompositions(); });
En la última publicación, mencioné que el método updateVideoCompositions()
no se mostraba porque la implementación variaría. Hablaremos sobre una posible implementación en un momento.
Por ahora, echemos un vistazo a cómo podemos obtener una configuración de diseño dinámico en lugar de codificarla como lo hicimos en la publicación anterior.
Una forma de obtener un tamaño y una posición dinámicos es recorrer la matriz de participantes y calcularlos en función de la cantidad de participantes, el tamaño del <canvas>
y la cantidad deseada de filas, columnas y relleno. Pero, ¿ por qué ?
Eso suena como mucho código difícil y trabajo innecesario cuando te das cuenta de que estos valores nunca cambian. Si tiene un participante, el video tendrá un tamaño fijo y estará centrado en el <canvas>
.
No importa cuántos participantes se agreguen: el diseño de cada video siempre será el mismo para un número determinado de participantes. Entonces, ¿por qué perder tiempo y ciclos de CPU cuando podemos precalcular estos valores y almacenarlos en una matriz de matrices?
Para mi demostración, pasé algún tiempo determinando los mejores valores con un intensivo de 30 minutos con lápiz, papel y calculadora para determinar los valores de composición para cada diseño posible. Tenga en cuenta: no estudié matemáticas o arte como lo demuestra el siguiente boceto.
Para esta demostración, limité mi transmisión en vivo para mostrar solo videos para los primeros 6 participantes. Su caso de uso puede dictar algo diferente, pero tener más de 6 videos de participantes en una transmisión en vivo se vuelve demasiado ocupado en mi experiencia.
Aquí está el resultado de mis cálculos:
const layouts = [ [{ height: 720, width: 1280, x: 320, y: 180 }], [{ height: 450, width: 800, x: 80, y: 315 }, { height: 450, width: 800, x: 1040, y: 315 }], [{ height: 450, width: 800, x: 80, y: 45 }, { height: 450, width: 800, x: 1040, y: 45 }, { height: 450, width: 800, x: 560, y: 585 }], [{ height: 450, width: 800, x: 80, y: 45 }, { height: 450, width: 800, x: 1040, y: 45 }, { height: 450, width: 800, x: 80, y: 585 }, { height: 450, width: 800, x: 1040, y: 585 }], [{ height: 337, width: 600, x: 20, y: 100 }, { height: 337, width: 600, x: 650, y: 100 }, { height: 337, width: 600, x: 1280, y: 100 }, { height: 337, width: 600, x: 340, y: 640 }, { height: 337, width: 600, x: 980, y: 640 }], [{ height: 337, width: 600, x: 20, y: 100 }, { height: 337, width: 600, x: 650, y: 100 }, { height: 337, width: 600, x: 1280, y: 100 }, { height: 337, width: 600, x: 20, y: 640 }, { height: 337, width: 600, x: 650, y: 640 }, { height: 337, width: 600, x: 1280, y: 640 }] ];
Eso puede parecer abrumador, pero considere que cada elemento en el elemento de la matriz externa contiene una matriz de composiciones para cada video.
Si hay 3 participantes, podemos hacer referencia al tercer elemento en la matriz externa, y la posición de la identificación del participante en la matriz de ID participantIds
determinará qué composición se aplicará a ese video.
Podemos modificar nuestra función renderVideosToClient()
para obtener la composición adecuada y usar esos valores cuando agregamos el video al cliente de transmisión.
const renderVideosToClient = async (participant, stream) => { const participantId = participant.id; const videoId = `video-${participantId}`; // get the index of this participantId const pIdx = participantIds.indexOf(participantId); let composition = layouts[participantIds.length - 1][pIdx]; config.index = 2; const mediaStream = new MediaStream(); mediaStream.addTrack(stream.mediaStreamTrack); broadcastClient.addVideoInputDevice(mediaStream, videoId, composition); };
Pero recuerde: si solo hacemos esto cuando se agrega un participante, las composiciones de video anteriores seguirán reflejando la composición que se aplicó cuando se agregaron. Ahí es donde entra en escena la función updateVideoCompositions()
.
Aquí recorremos la matriz participantIds
, tomamos la composición adecuada de layouts
y usamos updateVideoDeviceComposition()
(broadcastClient
.
const updateVideoCompositions = async () => { let idx = 0; for (const p of participantIds) { const videoId = `video-${p}`; let config = layouts[filteredParticipantIds.length - 1][idx]; config.index = 2; broadcastClient.updateVideoDeviceComposition(videoId, config); idx = idx + 1; } };
También debemos asegurarnos de que cuando un participante abandone el escenario, eliminemos la identificación del participante de la matriz y actualicemos nuevamente la composición para todos los videos.
stage.on(StageEvents.STAGE_PARTICIPANT_STREAMS_REMOVED, (participant, streams) => { const participantId = participant.id; // remove participant id from array const pIdx = participantIds.findIndex(id => id === participantId); participantIds.splice(pIdx, 1); const videoTrackId = `video-${participantId}`; const audioTrackId = `audio-${participantId}`; if (broadcastClient.getVideoInputDevice(videoTrackId)) broadcastClient.removeVideoInputDevice(videoTrackId); if (broadcastClient.getAudioInputDevice(audioTrackId)) broadcastClient.removeAudioInputDevice(audioTrackId); const videoId = `${participantId}-video`; document.getElementById(videoId).closest('.participant-col').remove(); updateVideoCompositions(); });
Como se mencionó anteriormente, lo más probable es que desee limitar la cantidad de videos que se agregan a la transmisión en vivo a través del cliente de transmisión. Es posible que desee agregar una imagen estática en lugar del video final para mostrar que hay más participantes de los que se muestran:
En esta publicación, aprendimos un enfoque para los diseños dinámicos al transmitir una etapa de varios hosts con Amazon IVS. En una publicación futura, veremos opciones adicionales para transmitir con múltiples anfitriones. Como siempre, si tiene alguna pregunta o comentario, déjelos a continuación.
También publicado aquí