fixed camera mirroring and added fip camera button
This commit is contained in:
@@ -97,6 +97,11 @@
|
||||
<path d="M17 10.5V7c0-.55-.45-1-1-1H4c-.55 0-1 .45-1 1v10c0 .55.45 1 1 1h12c.55 0 1-.45 1-1v-3.5l4 4v-11l-4 4z"/>
|
||||
</svg>
|
||||
</button>
|
||||
<button id="flip-cam-btn" class="control-btn mobile-only" title="Flip Camera">
|
||||
<svg viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M20 4h-3.17L15 2H9L7.17 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm-8 3c.98 0 1.87.31 2.6.84l-1.1 1.1c-.44-.28-.95-.44-1.5-.44-1.65 0-3 1.35-3 3h1.75L8.5 16.75 6.25 14.5H8c0-2.21 1.79-4 4-4zm0 10c-.98 0-1.87-.31-2.6-.84l1.1-1.1c.44.28.95.44 1.5.44 1.65 0 3-1.35 3-3h-1.75l2.25-2.25 2.25 2.25H16c0 2.21-1.79 4-4 4z"/>
|
||||
</svg>
|
||||
</button>
|
||||
<button id="screen-btn" class="control-btn" title="Share Screen (S)">
|
||||
<svg viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M20 18c1.1 0 1.99-.9 1.99-2L22 6c0-1.1-.9-2-2-2H4c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2H0v2h24v-2h-4zM4 6h16v10H4V6z"/>
|
||||
|
||||
@@ -26,6 +26,7 @@ document.addEventListener('DOMContentLoaded', async () => {
|
||||
if (typeof initVoiceChangerListeners === 'function') initVoiceChangerListeners();
|
||||
$('mic-btn').addEventListener('click', toggleMic);
|
||||
$('cam-btn').addEventListener('click', toggleCam);
|
||||
$('flip-cam-btn')?.addEventListener('click', flipCamera);
|
||||
$('screen-btn').addEventListener('click', toggleScreen);
|
||||
$('volume-btn').addEventListener('click', toggleVolume);
|
||||
$('users-btn').addEventListener('click', toggleSidebar);
|
||||
|
||||
+44
-1
@@ -42,7 +42,7 @@ async function toggleCam() {
|
||||
if (!state.camEnabled) {
|
||||
try {
|
||||
const videoStream = await navigator.mediaDevices.getUserMedia({
|
||||
video: getVideoConstraints()
|
||||
video: { ...getVideoConstraints(), facingMode: { ideal: state.facingMode || 'user' } }
|
||||
});
|
||||
const videoTrack = videoStream.getVideoTracks()[0];
|
||||
state.localStream.addTrack(videoTrack);
|
||||
@@ -109,6 +109,49 @@ async function toggleCam() {
|
||||
updateUI();
|
||||
}
|
||||
|
||||
// Mobile flip-camera button: switch between front ('user') and back ('environment').
|
||||
// If the camera is off, we just remember the choice for next time it's turned on.
|
||||
async function flipCamera() {
|
||||
state.facingMode = (state.facingMode === 'environment') ? 'user' : 'environment';
|
||||
|
||||
if (!state.camEnabled) {
|
||||
updateUI();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const newStream = await navigator.mediaDevices.getUserMedia({
|
||||
video: { ...getVideoConstraints(), facingMode: { ideal: state.facingMode } }
|
||||
});
|
||||
const newTrack = newStream.getVideoTracks()[0];
|
||||
|
||||
// Swap the camera track in the local stream (leave any screen-share track alone).
|
||||
const oldTrack = state.localStream.getVideoTracks()[0];
|
||||
if (oldTrack) {
|
||||
oldTrack.stop();
|
||||
state.localStream.removeTrack(oldTrack);
|
||||
}
|
||||
state.localStream.addTrack(newTrack);
|
||||
|
||||
// Replace on each peer's CAMERA video sender (skip the screen-share sender).
|
||||
for (const [peerId, peer] of Object.entries(state.peers)) {
|
||||
const sender = peer.pc.getSenders().find(s => s.track && s.track.kind === 'video' && s !== peer.screenSender);
|
||||
if (sender) await sender.replaceTrack(newTrack);
|
||||
}
|
||||
|
||||
// Re-apply outgoing shaping to the fresh sender track.
|
||||
if (typeof applyVideoBitrateCap === 'function') applyVideoBitrateCap();
|
||||
if (typeof applyAudioOnlyGatingAll === 'function') applyAudioOnlyGatingAll();
|
||||
|
||||
updateVideoGrid();
|
||||
console.log('[Camera] Flipped to', state.facingMode);
|
||||
} catch (e) {
|
||||
console.error('[Camera] Flip failed:', e);
|
||||
// Revert the desired facing so the button state stays truthful.
|
||||
state.facingMode = (state.facingMode === 'environment') ? 'user' : 'environment';
|
||||
}
|
||||
}
|
||||
|
||||
async function toggleScreen() {
|
||||
// Breakout room is voice-only - block enabling screen share.
|
||||
if (!state.screenEnabled && state.users['local']?.breakout) {
|
||||
|
||||
@@ -26,6 +26,9 @@ const state = {
|
||||
micEnabled: true,
|
||||
camEnabled: false,
|
||||
screenEnabled: false,
|
||||
// Which camera to capture: 'user' (front, default) or 'environment' (back). Flipped
|
||||
// via the mobile flip-camera button.
|
||||
facingMode: 'user',
|
||||
volumeEnabled: true,
|
||||
maximizedPeer: null,
|
||||
sidebarOpen: true,
|
||||
|
||||
@@ -1475,6 +1475,8 @@ body {
|
||||
|
||||
/* Mobile-only settings rows (e.g. Speaker Mode toggle) */
|
||||
.mobile-only-setting { display: flex; }
|
||||
/* Mobile-only controls (e.g. flip-camera button) */
|
||||
.mobile-only { display: flex; }
|
||||
/* Speaker device picker is useless on mobile - setSinkId isn't supported and
|
||||
enumerateDevices returns nothing meaningful. Hide it; the Speaker Mode toggle
|
||||
replaces it. */
|
||||
@@ -1483,6 +1485,7 @@ body {
|
||||
|
||||
/* Default visibility outside the mobile breakpoint. */
|
||||
.mobile-only-setting { display: none; }
|
||||
.mobile-only { display: none; }
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.video-tile { border-radius: 8px; }
|
||||
|
||||
Reference in New Issue
Block a user