Added Hotswapping to Settings.
This commit is contained in:
@@ -16,7 +16,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<button onclick="loadDevices()">Refresh Devices</button>
|
<button onclick="refreshDevicesAndPreview()">Refresh Devices</button>
|
||||||
<button onclick="joinChannelCall()">Join Call</button>
|
<button onclick="joinChannelCall()">Join Call</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -96,19 +96,25 @@ async function ensurePeerConnection() {
|
|||||||
LogMessage("ICE gathering state: " + peerConnection.iceGatheringState);
|
LogMessage("ICE gathering state: " + peerConnection.iceGatheringState);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
async function ensureLocalMedia() {
|
async function ensureLocalMedia(forceReload = false) {
|
||||||
if (localStream) return;
|
|
||||||
|
|
||||||
const localMediaStatus = document.getElementById("localMediaStatus");
|
const localMediaStatus = document.getElementById("localMediaStatus");
|
||||||
const localVideoStatus = document.getElementById("localVideoStatus");
|
const localVideoStatus = document.getElementById("localVideoStatus");
|
||||||
|
const localVideo = document.getElementById("localVideo");
|
||||||
const cameraSelect = document.getElementById("cameraSelect");
|
const cameraSelect = document.getElementById("cameraSelect");
|
||||||
const micSelect = document.getElementById("micSelect");
|
const micSelect = document.getElementById("micSelect");
|
||||||
|
|
||||||
|
if (localStream && !forceReload) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (localStream) {
|
||||||
|
localStream.getTracks().forEach(track => track.stop());
|
||||||
|
localStream = null;
|
||||||
|
}
|
||||||
|
|
||||||
let selectedCameraId = cameraSelect ? cameraSelect.value : "";
|
let selectedCameraId = cameraSelect ? cameraSelect.value : "";
|
||||||
let selectedMicId = micSelect ? micSelect.value : "";
|
let selectedMicId = micSelect ? micSelect.value : "";
|
||||||
|
|
||||||
let mediaError = null;
|
|
||||||
|
|
||||||
const videoConstraint = selectedCameraId
|
const videoConstraint = selectedCameraId
|
||||||
? { deviceId: { exact: selectedCameraId } }
|
? { deviceId: { exact: selectedCameraId } }
|
||||||
: false;
|
: false;
|
||||||
@@ -125,7 +131,6 @@ async function ensureLocalMedia() {
|
|||||||
|
|
||||||
LogMessage("Local media initialized");
|
LogMessage("Local media initialized");
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
mediaError = err;
|
|
||||||
LogMessage("selected media failed: " + err);
|
LogMessage("selected media failed: " + err);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -137,28 +142,69 @@ async function ensureLocalMedia() {
|
|||||||
LogMessage("Local media initialized with audio only fallback");
|
LogMessage("Local media initialized with audio only fallback");
|
||||||
} catch (audioErr) {
|
} catch (audioErr) {
|
||||||
LogMessage("audio-only failed: " + audioErr);
|
LogMessage("audio-only failed: " + audioErr);
|
||||||
|
|
||||||
if (localMediaStatus) localMediaStatus.textContent = "Local media failed";
|
if (localMediaStatus) localMediaStatus.textContent = "Local media failed";
|
||||||
if (localVideoStatus) localVideoStatus.textContent = "Local video: unavailable";
|
if (localVideoStatus) localVideoStatus.textContent = "Local video: unavailable";
|
||||||
throw mediaError;
|
if (localVideo) localVideo.srcObject = null;
|
||||||
|
|
||||||
|
throw audioErr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const localVideo = document.getElementById("localVideo");
|
const hasVideo = localStream.getVideoTracks().length > 0;
|
||||||
|
const hasAudio = localStream.getAudioTracks().length > 0;
|
||||||
|
|
||||||
if (localStream.getVideoTracks().length > 0) {
|
localVideo.srcObject = hasVideo ? localStream : null;
|
||||||
localVideo.srcObject = localStream;
|
|
||||||
if (localVideoStatus) localVideoStatus.textContent = "Local video: active";
|
if (localVideoStatus) {
|
||||||
if (localMediaStatus) localMediaStatus.textContent = "Local media: audio + video";
|
localVideoStatus.textContent = hasVideo
|
||||||
} else {
|
? "Local video: active"
|
||||||
localVideo.srcObject = null;
|
: "Local video: unavailable";
|
||||||
if (localVideoStatus) localVideoStatus.textContent = "Local video: unavailable";
|
|
||||||
if (localMediaStatus) localMediaStatus.textContent = "Local media: audio only";
|
|
||||||
LogMessage("No camera available, continuing without video");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const track of localStream.getTracks()) {
|
if (localMediaStatus) {
|
||||||
peerConnection.addTrack(track, localStream);
|
localMediaStatus.textContent = `Local media: audio=${hasAudio} video=${hasVideo}`;
|
||||||
LogMessage(`Added local track: ${track.kind}`);
|
}
|
||||||
|
|
||||||
|
if (!hasVideo) {
|
||||||
|
LogMessage("No camera available, continuing without video");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function applyLocalStreamToPeerConnection() {
|
||||||
|
if (!peerConnection || !localStream) return;
|
||||||
|
|
||||||
|
const senders = peerConnection.getSenders();
|
||||||
|
|
||||||
|
const audioTrack = localStream.getAudioTracks()[0] || null;
|
||||||
|
const videoTrack = localStream.getVideoTracks()[0] || null;
|
||||||
|
|
||||||
|
const audioSender = senders.find(s => s.track && s.track.kind === "audio");
|
||||||
|
const videoSender = senders.find(s => s.track && s.track.kind === "video");
|
||||||
|
|
||||||
|
if (audioSender) {
|
||||||
|
await audioSender.replaceTrack(audioTrack);
|
||||||
|
LogMessage("Replaced audio track on peer connection");
|
||||||
|
} else if (audioTrack) {
|
||||||
|
peerConnection.addTrack(audioTrack, localStream);
|
||||||
|
LogMessage("Added audio track to peer connection");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (videoSender) {
|
||||||
|
await videoSender.replaceTrack(videoTrack);
|
||||||
|
LogMessage("Replaced video track on peer connection");
|
||||||
|
} else if (videoTrack) {
|
||||||
|
peerConnection.addTrack(videoTrack, localStream);
|
||||||
|
LogMessage("Added video track to peer connection");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function refreshDevicesAndPreview() {
|
||||||
|
await loadDevices();
|
||||||
|
await ensureLocalMedia(true);
|
||||||
|
|
||||||
|
if (peerConnection) {
|
||||||
|
await applyLocalStreamToPeerConnection();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -250,6 +296,7 @@ async function channelCallJoin(activeCall)
|
|||||||
// LogMessage("Active call: " + activeCall);
|
// LogMessage("Active call: " + activeCall);
|
||||||
await ensurePeerConnection2();
|
await ensurePeerConnection2();
|
||||||
await ensureLocalMedia();
|
await ensureLocalMedia();
|
||||||
|
await applyLocalStreamToPeerConnection();
|
||||||
|
|
||||||
if (activeCall)
|
if (activeCall)
|
||||||
{
|
{
|
||||||
@@ -431,6 +478,27 @@ async function loadDevices() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function wireDeviceSelectors() {
|
||||||
|
const cameraSelect = document.getElementById("cameraSelect");
|
||||||
|
const micSelect = document.getElementById("micSelect");
|
||||||
|
|
||||||
|
if (cameraSelect) {
|
||||||
|
cameraSelect.onchange = async () => {
|
||||||
|
LogMessage("Camera changed");
|
||||||
|
await ensureLocalMedia(true);
|
||||||
|
await applyLocalStreamToPeerConnection();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (micSelect) {
|
||||||
|
micSelect.onchange = async () => {
|
||||||
|
LogMessage("Microphone changed");
|
||||||
|
await ensureLocalMedia(true);
|
||||||
|
await applyLocalStreamToPeerConnection();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function waitForIceGatheringComplete(pc) {
|
async function waitForIceGatheringComplete(pc) {
|
||||||
if (pc.iceGatheringState === "complete") return;
|
if (pc.iceGatheringState === "complete") return;
|
||||||
|
|
||||||
@@ -456,4 +524,6 @@ window.addEventListener("load", async () => {
|
|||||||
LogMessage("RTC page loaded");
|
LogMessage("RTC page loaded");
|
||||||
window.HybridWebView.SendRawMessage("rtc_page_ready");
|
window.HybridWebView.SendRawMessage("rtc_page_ready");
|
||||||
await loadDevices();
|
await loadDevices();
|
||||||
|
wireDeviceSelectors();
|
||||||
|
await ensureLocalMedia(true);
|
||||||
});
|
});
|
||||||
Reference in New Issue
Block a user