
Teilen Sie:
Enrico ist ein ehemaliges Mitglied des Vonage-Teams. Er arbeitete als Solutions Engineer und unterstützte das Vertriebsteam mit seinem technischen Fachwissen. Er begeistert sich für die Cloud, Startups und neue Technologien. Er ist der Mitbegründer eines WebRTC-Startups in Italien. Außerhalb der Arbeit reist er gerne und probiert so viele verrückte Gerichte wie möglich.
Erstellen Sie eine Mehrparteien-Video-App mit dem neuen Vonage Video Express
Lesedauer: 5 Minuten
Dieser Artikel wurde geschrieben in Zusammenarbeit mit Javier Molina Sanz
Dieser Blogbeitrag hilft Ihnen bei der Entwicklung einer Multiparty Video Application auf Basis von ReactJS und dem neuen Vonage Video Express. Der Video Express bietet standardmäßig die folgenden Funktionen:
Raum- und Teilnehmer-ManagerVereinfachte Logik für Veröffentlichung, Abonnement und Stream-Management
Layout-Verwaltung: Out of the box responsive UI und Layout Manager mit anpassbaren Komponenten
Optimierung der Video-Qualität (Framerate und Auflösung) basierend auf der Anzahl der Teilnehmer, Rendering-Größen, CPU und Netzwerkbedingungen
Optimierung des Netzwerks: Entfernen Sie automatisch Video oder Audio für Teilnehmer, die nicht sichtbar sind oder nicht sprechen, und optimieren Sie so die Bandbreitenressourcen.
Benutzerfreundlichkeit: Es bietet eine natürlichere Interaktion, indem es Veröffentlichung, Abonnement und Streams durch Raum und Teilnehmer ersetzt.
Möchten Sie zum Ende springen? Sie finden den gesamten Quellcode für dieses Tutorial auf GitHub.
App-Architektur
Die Anwendung ist in zwei Hauptbereiche unterteilt: Server- und Client-Seite: Server-Seite: ein einfacher NodeJS-Server, der für die Generierung von Anmeldeinformationen und die Archivverwaltung zuständig ist Client-Seite: eine React SPA (Single Page Application) mit React Hooks.
Die Client-Seite ist der Ort, an dem die eigentliche Action stattfindet. Mit Video Express konnten wir eine reaktionsschnelle und skalierbare Mehrparteien-Videokonferenz-App implementieren, die sich um zeitraubende Optimierungen kümmert.
Kunde
Die React-Anwendung nutzt die @vonage/video-express Modul über NPM. Denken Sie daran, dass Sie Video Express auch über ein HTML-Skript-Tag verwenden können - sehen Sie sich die Video-Express-Dokumentation für alle Details.
Die Anwendung basiert auf React Hooks, die mit React 16.8 geliefert wurden. Als nächstes wollen wir uns die wichtigsten Hooks dieser Anwendung genauer ansehen.
UseRoom
Die UseRoom Hook ist derjenige, der den Lebenszyklus unseres Videoraums verwaltet. Dank Video Express müssen wir den Lebenszyklus von Session, Publisher und Subscriber nicht verwalten. Stattdessen müssen wir nur einen Raum Objekt instanziieren, dann die room.join() Methode verwenden, die wiederum alles für uns hinter den Kulissen erledigt.
Zunächst müssen wir eine Funktion erstellen, die für die Initialisierung unseres Raumobjekts und die Verbindung mit dem Anruf zuständig ist. Wir müssen unsere Authentifizierung bereitstellen (apiKey, sessionId, und token) und andere optionale Parameter, die als Publisher-Einstellungen verwendet werden, wie zum Beispiel die userName, den Container, in dem unser Raum sichtbar sein wird, und einige optionale Verlegereinstellungen.
Da wir den Standard-Layout-Manager verwenden, den Video Express bereitstellt, übergeben wir einige Layout-Parameter: Legen Sie eine Rasteransicht für das Anfangslayout fest, definieren Sie ein benutzerdefiniertes HTML-Element für die Screensharing-Ansicht Die vollständige Liste der Parameter finden Sie finden Sie hier.
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));
},
[ ]
);
Sobald das Room Objekt initialisiert wurde, rufen wir die startRoomListeners Funktion auf, um die Ereignis-Listener für das Room Objekt zu starten. Dann rufen wir die room.join() Methode mit einigen optionalen publisherSettings um der Sitzung beizutreten. Wir benötigen die Ereignislisten, um uns über Ereignisse zu benachrichtigen, z. B. wenn ein neuer Teilnehmer beitritt, ein neuer Bildschirmfreigabe-Stream erstellt wird, ein Benutzer sich wieder mit dem Anruf verbindet usw.
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);
});
}
};
Beachten Sie, dass wir auch die Teilnehmer der Sitzung verfolgen, um eine Liste der Teilnehmer anzeigen zu können. Wir werden eine Zustandsvariable erstellen, die aktualisiert wird, sobald ein Teilnehmer den Raum betritt oder verlässt.
Eine weitere hilfreiche Funktion ist die implementierte Netzwerkstatus-Komponente. Diese Funktion ist nützlich, wenn der Benutzer die Verbindung unterbricht oder wiederherstellt, um die Benutzeroberfläche zu aktualisieren und den Benutzer über seine Netzwerkprobleme zu informieren.
UseDevices
Heutzutage ist es sehr üblich, mehrere Audio-/Videogeräte zu verwenden. Manche Nutzer bevorzugen Kopfhörer, andere schließen lieber eine externe Webcam an ihren Computer an. Bei einer Video-Anwendung ist es wichtig, dem Benutzer die Wahl zwischen verschiedenen Geräten zu lassen. Die Seite useDevices Hook erklärt, wie man eine Liste der verfügbaren Geräte erhält.
useEffect(() => {
navigator.mediaDevices.addEventListener('devicechange', getDevices);
getDevices();
return () => {
navigator.mediaDevices.removeEventListener('devicechange', getDevices);
};
}, [getDevices]);
Wir haben einen Ereignis-Listener eingerichtet, um eine Änderung auf Mediengeräten zu erkennen; wir werden unsere getDevices() Funktion auslösen, wenn dies geschieht.
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);
}
}, []);
Unsere getDevices() Funktion ruft die Funktion MP.geDevices() Methode auf, die eine Liste der verfügbaren Geräte zurückgibt, sobald der Benutzer den Gerätezugang gewährt hat. Wir filtern dann durch die Geräte und füllen unseren Status mit den verschiedenen verfügbaren Geräten auf.
const [deviceInfo, setDeviceInfo] = useState({
audioInputDevices: [],
videoInputDevices: [],
audioOutputDevices: []
}); UsePreviewPublisher
Der Video Express hilft Ihnen auch bei der Implementierung der Benutzererfahrung vor dem Anruf. Tatsächlich implementiert der Video Express einen PreviewPublisher Klasse. Die Idee der PreviewPublisher Klasse ist es, den Entwicklern eine einfache Vorschau von Medien zu ermöglichen und sicherzustellen, dass Ihre Geräte (Audio/Video) einwandfrei funktionieren, ohne dass Sie ein Room Objekt zu erstellen.
Wir werden eine Vorschau erstellen, damit der Benutzer das richtige Gerät auswählen kann (falls er mehrere hat) und sicherstellen kann, dass das Mikrofon den Ton aufnimmt und die Kamera einwandfrei funktioniert. Sehen Sie sich die vollständige Implementierung auf GitHub.
Wir werden zunächst die verfügbaren Geräte von unserem UseDevices-Hook abrufen.
const { deviceInfo, getDevices } = useDevices();Sobald wir den Preview Publisher mit dem Zielelement initialisiert haben, rufen wir die previewMedia Methode auf, um die Medien zu visualisieren. Wir richten auch einige Ereignis-Listener ein, um den Gerätezugriff und audioLevel Ereignisse. Wie Sie sehen können, rufen wir die getDevices() Funktion erst dann auf, wenn der Benutzer die Erlaubnis für Geräte erteilt hat (bei dem accessAllowed Ereignis)
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]
);
Wir abonnieren einige Ereignisse aus dem SDK, um zu wissen, ob der Benutzer den Gerätezugriff erlaubt hat, und um Audio-Level-Ereignisse zu abonnieren, um die Benutzeroberfläche zu aktualisieren und dem Benutzer zu versichern, dass das Mikrofon Audio aufnimmt. Wir werden dem Benutzer auch eine Warnung anzeigen, wenn der Zugriff auf Audio-/Videogeräte verweigert wurde (siehe Implementierung).
Warteraum
Eine der wichtigsten Komponenten unserer Anwendung ist der WaitingRoom Komponente, denn hier werden wir die useDevices und usePreviewPublisher Haken verwenden. Der Warteraum ist eine Seite vor dem Anruf, auf der der Benutzer das richtige Audio- und Videogerät auswählen, überprüfen kann, ob das Mikrofon und die Kamera funktionieren, und einen Namen wählen kann.
So sieht unser Wartezimmer aus:
Screenshot of waiting room on mobile device
Wir haben einige Zustandsvariablen, die die Wahl des Benutzers enthalten; dies geschieht, damit der Benutzer dem Raum mit ausgeschaltetem Audio oder Video beitreten, einen Namen festlegen oder die Audiogeräte wechseln kann:
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('');Wir haben einen UserContext erstellt, der die Benutzerauswahlen wie Audio- und Videoquellen verwaltet. Wir werden unseren usePreviewPublisher Hook verwenden, um eine Verlagsvorschau für unser Wartezimmer zu erstellen und zu zerstören und eine Liste der verfügbaren Geräte sowie andere nützliche Statusvariablen zu erhalten.
const {
createPreview,
destroyPreview,
previewPublisher,
logLevel,
previewMediaCreated,
deviceInfo,
accessAllowed
} = usePreviewPublisher();Die Logik beginnt, sobald unsere Komponente montiert ist und wir den Container für unser Wartezimmer haben. Wir werden eine Publisher-Vorschau erstellen.
useEffect(() => {
if (waitingRoomVideoContainer.current) {
createPreview(waitingRoomVideoContainer.current);
}
return () => {
destroyPreview();
};
}, [createPreview, destroyPreview]);
Wir haben einen useEffect Hook, der ausgeführt wird, sobald die Vorschau erstellt wurde, und der die Liste der Geräte mit dem aktuell verwendeten Gerät initialisiert. Beachten Sie den Aufruf von getAudioDevice() und getVideoDevice() da ersteres ein Versprechen und letzteres eine synchrone Methode ist.
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
]);
Die Logik zum Wechseln der Geräte ist für Audio und Video nahezu identisch. Wir werden sie für Audio erklären, aber denken Sie daran, dass Sie die Implementierung des WaitingRoom Komponente überprüfen können.
useEffect(() => {
if (previewPublisher) {
if (localVideo && !previewPublisher.isVideoEnabled()) {
previewPublisher.enableVideo();
} else if (!localVideo && previewPublisher.isVideoEnabled()) {
previewPublisher.disableVideo();
}
}
}, [localVideo, previewPublisher]);
Wir haben einen Ereignis-Listener, der ausgelöst wird, wenn der Benutzer das verwendete Videogerät wechselt:
const handleVideoSource = React.useCallback(
e => {
const videoDeviceId = e.target.value;
setVideoDevice(e.target.value);
previewPublisher.setVideoDevice(videoDeviceId);
setLocalVideoSource(videoDeviceId);
},
[previewPublisher, setVideoDevice, setLocalVideoSource]
);
Schlussfolgerung
Dieser Beitrag zeigt, wie man das brandneue Video Express in eine React Application integriert. Die Anwendung implementiert die wichtigsten Funktionen im Zusammenhang mit einer Video-Anwendung, wie Wartezimmer, Geräteauswahl, Netzwerkstatus-Erkennung, Bildschirmfreigabe, Chat und mehr.
Bitte klonen Sie das Github Repo und verwenden Sie es in Ihrer Anwendung.
Teilen Sie:
Enrico ist ein ehemaliges Mitglied des Vonage-Teams. Er arbeitete als Solutions Engineer und unterstützte das Vertriebsteam mit seinem technischen Fachwissen. Er begeistert sich für die Cloud, Startups und neue Technologien. Er ist der Mitbegründer eines WebRTC-Startups in Italien. Außerhalb der Arbeit reist er gerne und probiert so viele verrückte Gerichte wie möglich.