Add device picker for webcams and microphones to the settings modal

This commit is contained in:
Noah 2023-07-09 12:33:02 -07:00
parent f4568b9087
commit 55b17f62c4
2 changed files with 67 additions and 12 deletions

View File

@ -277,11 +277,12 @@ const app = Vue.createApp({
} }
}, },
watch: { watch: {
"webcam.videoScale": () => { "webcam.videoScale": function() {
document.querySelectorAll(".video-feeds > .feed").forEach(node => { document.querySelectorAll(".video-feeds > .feed").forEach(node => {
node.style.width = null; node.style.width = null;
node.style.height = null; node.style.height = null;
}); });
localStorage.videoScale = this.webcam.videoScale;
}, },
fontSizeClass() { fontSizeClass() {
// Store the setting persistently. // Store the setting persistently.
@ -357,6 +358,10 @@ const app = Vue.createApp({
this.fontSizeClass = localStorage.fontSizeClass; this.fontSizeClass = localStorage.fontSizeClass;
} }
if (localStorage.videoScale != undefined) {
this.webcam.videoScale = localStorage.videoScale;
}
// Webcam mutality preferences from last broadcast. // Webcam mutality preferences from last broadcast.
if (localStorage.videoMutual === "true") { if (localStorage.videoMutual === "true") {
this.webcam.mutual = true; this.webcam.mutual = true;
@ -1127,7 +1132,9 @@ const app = Vue.createApp({
}, },
// Start broadcasting my webcam. // Start broadcasting my webcam.
startVideo(force) { // - force=true to skip the NSFW modal prompt (this param is passed by the button in that modal)
// - changeCamera=true to re-negotiate WebRTC connections with a new camera device (invoked by the Settings modal)
startVideo({force=false, changeCamera=false}) {
if (this.webcam.busy) return; if (this.webcam.busy) return;
// If we are running in PermitNSFW mode, show the user the modal. // If we are running in PermitNSFW mode, show the user the modal.
@ -1136,11 +1143,24 @@ const app = Vue.createApp({
return; return;
} }
this.webcam.busy = true; let mediaParams = {
navigator.mediaDevices.getUserMedia({
audio: true, audio: true,
video: true, video: {
}).then(stream => { width: { max: 1280 },
height: { max: 720 },
},
};
if (changeCamera) {
// Name the specific devices chosen by the user.
mediaParams.video.deviceId = { exact: this.webcam.videoDeviceID };
mediaParams.audio = {
deviceId: { exact: this.webcam.audioDeviceID },
};
}
this.webcam.busy = true;
navigator.mediaDevices.getUserMedia(mediaParams).then(stream => {
this.webcam.active = true; this.webcam.active = true;
this.webcam.elem.srcObject = stream; this.webcam.elem.srcObject = stream;
this.webcam.stream = stream; this.webcam.stream = stream;
@ -1159,6 +1179,11 @@ const app = Vue.createApp({
// Collect video and audio devices to let the user change them in their settings. // Collect video and audio devices to let the user change them in their settings.
this.getDevices(); this.getDevices();
// If we have changed devices, reconnect everybody's WebRTC channels for your existing watchers.
if (changeCamera) {
this.updateWebRTCStreams();
}
}).catch(err => { }).catch(err => {
this.ChatClient(`Webcam error: ${err}`); this.ChatClient(`Webcam error: ${err}`);
}).finally(() => { }).finally(() => {
@ -1177,11 +1202,13 @@ const app = Vue.createApp({
this.webcam.audioDevices = []; this.webcam.audioDevices = [];
devices.forEach(device => { devices.forEach(device => {
if (device.kind === 'videoinput') { if (device.kind === 'videoinput') {
// console.log(`Video device ${device.deviceId} ${device.label}`);
this.webcam.videoDevices.push({ this.webcam.videoDevices.push({
id: device.deviceId, id: device.deviceId,
label: device.label, label: device.label,
}); });
} else if (device.kind === 'audioinput') { } else if (device.kind === 'audioinput') {
// console.log(`Audio device ${device.deviceId} ${device.label}`);
this.webcam.audioDevices.push({ this.webcam.audioDevices.push({
id: device.deviceId, id: device.deviceId,
label: device.label, label: device.label,
@ -1193,6 +1220,36 @@ const app = Vue.createApp({
}) })
}, },
// Replace your video/audio streams for your watchers (on camera changes)
updateWebRTCStreams() {
console.log("Re-negotiating video and audio channels to your watchers.");
for (let username of Object.keys(this.WebRTC.pc)) {
let pc = this.WebRTC.pc[username];
if (pc.answerer != undefined) {
let oldTracks = pc.answerer.getSenders();
let newTracks = this.webcam.stream.getTracks();
// Remove and replace the tracks.
for (let old of oldTracks) {
if (old.track.kind === 'audio') {
for (let replace of newTracks) {
if (replace.kind === 'audio') {
old.replaceTrack(replace);
}
}
}
else if (old.track.kind === 'video') {
for (let replace of newTracks) {
if (replace.kind === 'video') {
old.replaceTrack(replace);
}
}
}
}
}
}
},
// Begin connecting to someone else's webcam. // Begin connecting to someone else's webcam.
openVideo(user, force) { openVideo(user, force) {
if (user.username === this.username) { if (user.username === this.username) {

View File

@ -114,13 +114,12 @@
</div> </div>
</div> </div>
<!-- Under construction
<div class="columns is-mobile" v-if="webcam.videoDevices.length > 0 || webcam.audioDevices.length > 0"> <div class="columns is-mobile" v-if="webcam.videoDevices.length > 0 || webcam.audioDevices.length > 0">
<div class="column"> <div class="column">
<label class="label">Video source</label> <label class="label">Video source</label>
<div class="select is-fullwidth"> <div class="select is-fullwidth">
<select v-model="webcam.videoDeviceID"> <select v-model="webcam.videoDeviceID" @change="startVideo({changeCamera: true, force: true})">
<option v-for="(d, i) in webcam.videoDevices" <option v-for="(d, i) in webcam.videoDevices"
:value="d.id"> :value="d.id">
[[ d.label || `Camera ${i}` ]] [[ d.label || `Camera ${i}` ]]
@ -132,7 +131,7 @@
<div class="column"> <div class="column">
<label class="label">Audio source</label> <label class="label">Audio source</label>
<div class="select is-fullwidth"> <div class="select is-fullwidth">
<select v-model="webcam.audioDeviceID"> <select v-model="webcam.audioDeviceID" @change="startVideo({changeCamera: true, force: true})">
<option v-for="(d, i) in webcam.audioDevices" <option v-for="(d, i) in webcam.audioDevices"
:value="d.id"> :value="d.id">
[[ d.label || `Microphone ${i}` ]] [[ d.label || `Microphone ${i}` ]]
@ -141,7 +140,6 @@
</div> </div>
</div> </div>
</div> </div>
-->
<h3 class="subtitle mb-2">Sounds</h3> <h3 class="subtitle mb-2">Sounds</h3>
@ -360,7 +358,7 @@
<div class="control has-text-centered"> <div class="control has-text-centered">
<button type="button" <button type="button"
class="button is-link mr-4" class="button is-link mr-4"
@click="startVideo(true); nsfwModalCast.visible=false">Start webcam</button> @click="startVideo({force: true}); nsfwModalCast.visible=false">Start webcam</button>
<button type="button" <button type="button"
class="button" class="button"
@click="nsfwModalCast.visible=false">Cancel</button> @click="nsfwModalCast.visible=false">Cancel</button>
@ -442,7 +440,7 @@
<button type="button" <button type="button"
v-else v-else
class="button is-small is-success px-1" class="button is-small is-success px-1"
@click="startVideo()" @click="startVideo({})"
:disabled="webcam.busy"> :disabled="webcam.busy">
<i class="fa fa-video mr-2"></i> <i class="fa fa-video mr-2"></i>
Share webcam Share webcam