Skip to content

Commit a74f98e

Browse files
committed
feat: add mic audio to broadcast, viewer mute/unmute toggle
1 parent 2ffdf9a commit a74f98e

File tree

2 files changed

+29
-2
lines changed

2 files changed

+29
-2
lines changed

client/index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
<button onclick="captureToChannel()" class="viewer-capture-btn" title="Save frame to channel">📸
5858
Capture</button>
5959
<button onclick="raiseHand()" class="viewer-raise-btn" title="Raise hand"></button>
60+
<button onclick="toggleAudio()" class="viewer-audio-btn" id="audioToggleBtn" title="Unmute audio">🔇</button>
6061
<button onclick="toggleMinimize()" class="viewer-minimize-btn" title="Minimize"></button>
6162
<button onclick="leaveBroadcast()" class="viewer-close-btn" title="Leave broadcast"></button>
6263
</div>

client/js/app.js

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1724,8 +1724,16 @@ async function startBroadcast() {
17241724
try {
17251725
stream = await navigator.mediaDevices.getDisplayMedia({
17261726
video: { frameRate: { ideal: 30 }, width: { ideal: 1920 }, height: { ideal: 1080 } },
1727-
audio: false
1727+
audio: true
17281728
});
1729+
1730+
// Mix in microphone if available — failure is silent, broadcast continues without mic
1731+
try {
1732+
const micStream = await navigator.mediaDevices.getUserMedia({ audio: true });
1733+
micStream.getAudioTracks().forEach(track => stream.addTrack(track));
1734+
} catch (e) {
1735+
// Mic denied or unavailable — continue without it
1736+
}
17291737
} catch (e) {
17301738
return;
17311739
}
@@ -1884,17 +1892,25 @@ function leaveBroadcast() {
18841892
const joinBtn = document.getElementById('broadcastJoinBtn');
18851893
if (joinBtn) joinBtn.style.display = 'inline-block';
18861894

1895+
// Reset audio button to muted state
1896+
const audioBtn = document.getElementById('audioToggleBtn');
1897+
if (audioBtn) {
1898+
audioBtn.textContent = '🔇';
1899+
audioBtn.title = 'Unmute audio';
1900+
}
1901+
18871902
// Clean up viewer peer connection
18881903
if (viewerPeerConnection) {
18891904
viewerPeerConnection.close();
18901905
viewerPeerConnection = null;
18911906
}
18921907
broadcasterId = null;
18931908

1894-
// Stop video stream on viewer element
1909+
// Stop video stream and reset audio on viewer element
18951910
const video = document.getElementById('viewerFrame');
18961911
if (video) {
18971912
video.srcObject = null;
1913+
video.muted = true;
18981914
}
18991915
}
19001916

@@ -1953,6 +1969,16 @@ function toggleMinimize() {
19531969
panel.classList.toggle('minimized');
19541970
}
19551971

1972+
function toggleAudio() {
1973+
const video = document.getElementById('viewerFrame');
1974+
const btn = document.getElementById('audioToggleBtn');
1975+
if (!video || !btn) return;
1976+
1977+
video.muted = !video.muted;
1978+
btn.textContent = video.muted ? '🔇' : '🔊';
1979+
btn.title = video.muted ? 'Unmute audio' : 'Mute audio';
1980+
}
1981+
19561982
function makeDraggable(panel) {
19571983
const header = panel.querySelector('.viewer-panel-header');
19581984
if (!header) return;

0 commit comments

Comments
 (0)