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: {
"webcam.videoScale": () => {
"webcam.videoScale": function() {
document.querySelectorAll(".video-feeds > .feed").forEach(node => {
node.style.width = null;
node.style.height = null;
});
localStorage.videoScale = this.webcam.videoScale;
},
fontSizeClass() {
// Store the setting persistently.
@ -357,6 +358,10 @@ const app = Vue.createApp({
this.fontSizeClass = localStorage.fontSizeClass;
}
if (localStorage.videoScale != undefined) {
this.webcam.videoScale = localStorage.videoScale;
}
// Webcam mutality preferences from last broadcast.
if (localStorage.videoMutual === "true") {
this.webcam.mutual = true;
@ -1127,7 +1132,9 @@ const app = Vue.createApp({
},
// 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 we are running in PermitNSFW mode, show the user the modal.
@ -1136,11 +1143,24 @@ const app = Vue.createApp({
return;
}
this.webcam.busy = true;
navigator.mediaDevices.getUserMedia({
let mediaParams = {
audio: true,
video: true,
}).then(stream => {
video: {
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.elem.srcObject = 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.
this.getDevices();
// If we have changed devices, reconnect everybody's WebRTC channels for your existing watchers.
if (changeCamera) {
this.updateWebRTCStreams();
}
}).catch(err => {
this.ChatClient(`Webcam error: ${err}`);
}).finally(() => {
@ -1177,11 +1202,13 @@ const app = Vue.createApp({
this.webcam.audioDevices = [];
devices.forEach(device => {
if (device.kind === 'videoinput') {
// console.log(`Video device ${device.deviceId} ${device.label}`);
this.webcam.videoDevices.push({
id: device.deviceId,
label: device.label,
});
} else if (device.kind === 'audioinput') {
// console.log(`Audio device ${device.deviceId} ${device.label}`);
this.webcam.audioDevices.push({
id: device.deviceId,
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.
openVideo(user, force) {
if (user.username === this.username) {

View File

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