
Compartir:
Enrico es un antiguo miembro del equipo de Vonage. Trabajó como ingeniero de soluciones, ayudando al equipo de ventas con su experiencia técnica. Es un apasionado de la nube, las startups y las nuevas tecnologías. Es cofundador de una startup WebRTC en Italia. Fuera del trabajo, le gusta viajar y probar tantas comidas raras como sea posible.
Crea una aplicación de vídeo multipartita con el nuevo Vonage Video Express
Tiempo de lectura: 6 minutos
Este artículo ha sido escrito en colaboración con Javier Molina Sanz
Esta publicación del blog te ayudará a desarrollar una aplicación de Video multipartita basada en ReactJS y el nuevo Vonage Video Express. Video Express ofrece de forma predeterminada las siguientes funciones:
Gestor de salas y participantes: lógica simplificada de publicación, suscripción y gestión de flujos
Gestión de diseñoInterfaz de usuario y gestor de diseño listos para usar y con capacidad de respuesta, con componentes personalizables
Optimización de la calidad de Video (velocidad de fotogramas y resolución) en función del número de participantes, tamaños de renderizado, CPU y condiciones de la red.
Optimización de la red: Elimine automáticamente el vídeo o el audio de los participantes que no estén visibles o no hablen, optimizando así los recursos de ancho de banda.
Facilidad de uso: Proporciona una interacción más natural sustituyendo la publicación, la suscripción y los flujos por Sala y Participantes.
¿Quieres saltar hasta el final? Puedes encontrar todo el código fuente de este tutorial en GitHub.
Arquitectura de aplicaciones
La aplicación se divide en dos secciones principales, servidor y cliente: Server-Side: un simple servidor NodeJS que se encarga de la generación de credenciales y la gestión de archivos Client-Side: una React SPA (aplicación de página única) utilizando React Hooks.
El lado del cliente es donde ocurre la verdadera acción. Gracias a Video Express, pudimos implementar una aplicación de videoconferencia multipartita escalable y con capacidad de respuesta que se ocupa de las optimizaciones que llevan mucho tiempo.
Cliente
La aplicación React aprovecha la plataforma @vonage/video-express a través de NPM. Recuerda que también puedes utilizar Video Express a través de una etiqueta de script HTML; consulta la Documentación de Video Express para más detalles.
La aplicación está basada en los React Hooks que vienen con React 16.8. A continuación, vamos a echar un vistazo más de cerca a los principales ganchos de esta aplicación.
UseRoom
En UseRoom es el que gestiona el ciclo de vida de nuestra sala de Video. Gracias a Video Express, no tenemos que gestionar el ciclo de vida de Session, Publisher y Subscriber. En su lugar, sólo tenemos que instanciar una Sala y utilizar el método room.join() que, a su vez, se encargará de todo por nosotros entre bastidores.
En primer lugar, necesitamos crear una función encargada de inicializar nuestro objeto Room y unirse a la llamada. Necesitamos proporcionar nuestra autenticación (apiKey, sessionIdy token) y otros parámetros opcionales utilizados como configuración del publicador como el userNameel contenedor donde nuestra Room será visible, y algunos parámetros opcionales del editor.
Dado que vamos a utilizar el gestor de diseño predeterminado que proporciona Video Express, vamos a pasar algunos parámetros de diseño: establecer una vista de cuadrícula para el diseño inicial definir un elemento HTML personalizado para la vista de pantalla compartida Puede encontrar la lista completa de parámetros aquí.
const createCall = useCallback(
(
{ apikey, sessionId, token },
roomContainer,
userName,
publisherOptions
) => {
if (!apikey || !sessionId || !token) {
throw new Error('Check your credentials');
}
roomRef.current = new MP.Room({
apiKey: apikey,
sessionId: sessionId,
token: token,
roomContainer: 'roomContainer',
participantName: userName,
managedLayoutOptions: {
layoutMode: 'grid',
screenPublisherContainer: 'screenSharingContainer'
}
})
startRoomListeners();
roomRef.current
.join({ publisherProperties: finalPublisherOptions })
.then(() => {
setConnected(true);
setCamera(roomRef.current.camera);
setScreen(roomRef.current.screen);
addLocalParticipant({ room: roomRef.current });
})
.catch(e => console.log(e));
},
[ ]
);
Una vez inicializado el Room inicializado, llamamos a la función startRoomListeners para iniciar los escuchadores de eventos en el objeto Room objeto. A continuación, llamamos al método room.join() con algún método opcional publisherSettings para unirse a la sesión. Necesitamos que los escuchadores de eventos nos notifiquen sobre eventos tales como un nuevo participante se une, un nuevo flujo de pantalla compartida creado, un usuario se está reconectando a la llamada, etc.
const startRoomListeners = () => {
if (roomRef.current) {
roomRef.current.on('connected', () => {
console.log('Room: connected');
});
roomRef.current.on('disconnected', () => {
setNetworkStatus('disconnected');
console.log('Room: disconnected');
});
roomRef.current.camera.on('created', () => {
setCameraPublishing(true);
console.log('camera publishing now');
});
roomRef.current.on('reconnected', () => {
setNetworkStatus('reconnected');
console.log('Room: reconnected');
});
roomRef.current.on('reconnecting', () => {
setNetworkStatus('reconnecting');
console.log('Room: reconnecting');
});
roomRef.current.on('participantJoined', participant => {
console.log(participant);
addParticipants({ participant: participant });
console.log('Room: participant joined: ', participant);
});
roomRef.current.on('participantLeft', (participant, reason) => {
removeParticipants({ participant: participant });
console.log('Room: participant left', participant, reason);
});
}
};
Fíjate que también hacemos un seguimiento de los participantes de la sesión para poder mostrar una lista de participantes. Crearemos una variable de estado que se actualizará cuando un participante entre o salga de la sala.
Otra función útil es el componente de estado de la red. Esta función será útil cuando el usuario se desconecte o se vuelva a conectar para actualizar la interfaz de usuario e informarle de sus problemas de red.
UtilizarDispositivos
Hoy en día es muy común disponer de varios dispositivos de audio/vídeo. Algunos usuarios preferirán utilizar auriculares y otros conectar una webcam externa al ordenador. En una aplicación de Video, es esencial permitir al usuario elegir entre diferentes dispositivos. La función useDevices explica cómo obtener una lista de dispositivos disponibles.
useEffect(() => {
navigator.mediaDevices.addEventListener('devicechange', getDevices);
getDevices();
return () => {
navigator.mediaDevices.removeEventListener('devicechange', getDevices);
};
}, [getDevices]);
Hemos configurado un receptor de eventos para detectar un cambio en los dispositivos multimedia; activaremos nuestra función getDevices() cuando esto ocurra.
const getDevices = useCallback(async () => {
if (!navigator.mediaDevices || !navigator.mediaDevices.enumerateDevices) {
console.log('enumerateDevices() not supported.');
return;
}
try {
const devices = await MP.getDevices();
const audioInputDevices = devices.filter(
(d) => d.kind.toLowerCase() === 'audioinput'
);
const audioOutputDevices = devices.filter(
(d) => d.kind.toLowerCase() === 'audiooutput'
);
const videoInputDevices = devices.filter(
(d) => d.kind.toLowerCase() === 'videoinput'
);
setDeviceInfo({
audioInputDevices,
videoInputDevices,
audioOutputDevices
});
// });
} catch (err) {
console.log('[loadDevices] - ', err);
}
}, []);
Nuestra función getDevices() función llamará a la función MP.geDispositivos() que devuelve una lista de dispositivos disponibles una vez que el usuario ha concedido acceso al dispositivo. A continuación, filtraremos los dispositivos y rellenaremos nuestro estado con los distintos dispositivos disponibles.
const [deviceInfo, setDeviceInfo] = useState({
audioInputDevices: [],
videoInputDevices: [],
audioOutputDevices: []
}); UsePreviewPublisher
El Video Express le ayuda también a implementar la experiencia de usuario previa a la llamada. De hecho, el Video Express implementa un Previsualizador . La idea de la PreviewPublisher es permitir a los desarrolladores previsualizar fácilmente los medios y asegurarse de que sus dispositivos (audio/vídeo) funcionan correctamente sin necesidad de crear un objeto Room objeto.
Crearemos una vista previa para permitir al usuario elegir el dispositivo correcto (si tiene varios), asegurarnos de que el micrófono capta el audio y la cámara funciona correctamente. Echa un vistazo a la implementación completa en GitHub.
Primero obtendremos los dispositivos disponibles de nuestro hook UseDevices.
const { deviceInfo, getDevices } = useDevices();Una vez que inicializamos el editor de vista previa con el elemento de destino, llamamos al método previewMedia para visualizar el contenido multimedia. También configuraremos algunos escuchadores de eventos para manejar el acceso al dispositivo y los audioLevel del dispositivo. Como puedes ver, no llamaremos a la función getDevices() hasta que el usuario haya concedido permiso a los dispositivos (sobre el accessAllowed evento)
const createPreview = useCallback(
async (targetEl, publisherOptions) => {
try {
const publisherProperties = Object.assign({}, publisherOptions);
console.log('[createPreview]', publisherProperties);
previewPublisher.current = new MP.PreviewPublisher(targetEl);
previewPublisher.current.on('audioLevelUpdated', (audioLevel) => {
calculateAudioLevel(audioLevel);
});
previewPublisher.current.on('accessAllowed', (audioLevel) => {
console.log('[createPreview] - accessAllowed');
setAccessAllowed(DEVICE_ACCESS_STATUS.ACCEPTED);
getDevices();
});
previewPublisher.current.on('accessDenied', (audioLevel) => {
console.log('[createPreview] - accessDenied');
setAccessAllowed(DEVICE_ACCESS_STATUS.REJECTED);
});
await previewPublisher.current.previewMedia({
targetElement: targetEl,
publisherProperties
});
setPreviewMediaCreated(true);
console.log(
'[Preview Created] - ',
previewPublisher.current.getVideoDevice()
);
} catch (err) {
console.log('[createPreview]', err);
}
},
[calculateAudioLevel, getDevices]
);
Estamos suscribiéndonos a algunos eventos del SDK para saber si el usuario ha permitido el acceso al dispositivo y para suscribirnos a eventos de Nivel de audio para actualizar la UI y asegurar al usuario que el micrófono capta audio. También mostraremos una alerta al usuario si el acceso a los dispositivos de audio/video ha sido denegado (ver implementación).
Sala de espera
Uno de los componentes más importantes de nuestra aplicación es la SalaDeEsperas porque es donde utilizaremos los comandos useDevices y usePreviewPublisher hooks. La sala de espera es una página previa a la llamada en la que el usuario puede elegir el dispositivo de audio y vídeo adecuado, comprobar si el micrófono y la cámara funcionan y elegir un nombre.
Así es nuestra sala de espera:
Screenshot of waiting room on mobile device
Tenemos algunas variables de estado que contendrán la elección del usuario; esto se hace para permitir que el usuario se una a la sala con el audio o video apagado, establecer un nombre, o cambiar los dispositivos de audio:
const roomToJoin = location?.state?.room || '';
const [roomName, setRoomName] = useState(roomToJoin);
const [userName, setUserName] = useState('');
const [isRoomNameInvalid, setIsRoomNameInvalid] = useState(false);
const [isUserNameInvalid, setIsUserNameInvalid] = useState(false);
const [localAudio, setLocalAudio] = useState(
user.defaultSettings.publishAudio
);
const [localVideo, setLocalVideo] = useState(
user.defaultSettings.publishVideo
);
const [localVideoSource, setLocalVideoSource] = useState(undefined); const [localAudioSource, setLocalAudioSource] = useState(undefined);
let [audioDevice, setAudioDevice] = useState('');
let [videoDevice, setVideoDevice] = useState('');Hemos creado un UserContext que maneja las opciones del usuario como las fuentes de audio y Video. Utilizaremos nuestro usePreviewPublisher hook para crear y destruir una vista previa del editor en nuestra sala de espera y tener una lista de dispositivos disponibles junto con otras variables de estado útiles.
const {
createPreview,
destroyPreview,
previewPublisher,
logLevel,
previewMediaCreated,
deviceInfo,
accessAllowed
} = usePreviewPublisher();La lógica comienza una vez que nuestro componente está montado y tenemos el contenedor para nuestra sala de espera. Crearemos una vista previa del editor.
useEffect(() => {
if (waitingRoomVideoContainer.current) {
createPreview(waitingRoomVideoContainer.current);
}
return () => {
destroyPreview();
};
}, [createPreview, destroyPreview]);
Tenemos un useEffect hook que se ejecuta una vez que se ha creado la vista previa e inicializa la lista de dispositivos con el dispositivo actual en uso. Observe la llamada a getAudioDevice() y getVideoDevice() ya que el primero es una promesa y el segundo es un método síncrono.
useEffect(() => {
if (previewPublisher && previewMediaCreated && deviceInfo) {
console.log('useEffect - preview', deviceInfo);
previewPublisher.getAudioDevice().then(currentAudioDevice => {
setAudioDevice(currentAudioDevice.deviceId);
});
const currentVideoDevice = previewPublisher.getVideoDevice();
console.log('currentVideoDevice', currentVideoDevice);
setVideoDevice(currentVideoDevice.deviceId);
}
}, [
deviceInfo,
previewPublisher,
setAudioDevice,
setVideoDevice,
previewMediaCreated
]);
La lógica para cambiar de dispositivo es casi la misma para audio y Video. Lo explicaremos para audio, pero recuerda que puedes consultar la implementación del módulo Sala de Espera .
useEffect(() => {
if (previewPublisher) {
if (localVideo && !previewPublisher.isVideoEnabled()) {
previewPublisher.enableVideo();
} else if (!localVideo && previewPublisher.isVideoEnabled()) {
previewPublisher.disableVideo();
}
}
}, [localVideo, previewPublisher]);
Tenemos un evento que se activa cuando el usuario cambia el dispositivo de vídeo en uso:
const handleVideoSource = React.useCallback(
e => {
const videoDeviceId = e.target.value;
setVideoDevice(e.target.value);
previewPublisher.setVideoDevice(videoDeviceId);
setLocalVideoSource(videoDeviceId);
},
[previewPublisher, setVideoDevice, setLocalVideoSource]
);
Conclusión
Este post muestra cómo integrar el flamante Video Express con una aplicación React. La aplicación implementa las principales funciones relacionadas con una aplicación de vídeo, como la sala de espera, la selección de dispositivos, la detección del estado de la red, la pantalla compartida, el chat y mucho más.
Siéntase libre de clonar el Repo Github y empezar a utilizarlo en su aplicación.
Compartir:
Enrico es un antiguo miembro del equipo de Vonage. Trabajó como ingeniero de soluciones, ayudando al equipo de ventas con su experiencia técnica. Es un apasionado de la nube, las startups y las nuevas tecnologías. Es cofundador de una startup WebRTC en Italia. Fuera del trabajo, le gusta viajar y probar tantas comidas raras como sea posible.