diff --git a/public/viewer.js b/public/viewer.js index ee19491..26eb3b6 100644 --- a/public/viewer.js +++ b/public/viewer.js @@ -5,6 +5,34 @@ const overlay = document.getElementById('overlay'); let device; let recvTransport; let consumers = {}; // consumerId -> consumer +let hasUserInteracted = false; + +// Track user interaction — Chrome mobile needs this before play() works +document.addEventListener('click', () => { hasUserInteracted = true; }, { once: true }); +document.addEventListener('touchstart', () => { hasUserInteracted = true; }, { once: true }); + +// Make overlay clickable to start playback on mobile +overlay.addEventListener('click', () => { + if (remoteVideo.srcObject) { + remoteVideo.muted = false; + remoteVideo.play().then(() => { + overlay.classList.add('hidden'); + remoteVideo.classList.add('active'); + }).catch(e => console.error('Play failed:', e)); + } +}); + +function tryPlay() { + remoteVideo.play().then(() => { + overlay.classList.add('hidden'); + remoteVideo.classList.add('active'); + }).catch(() => { + // Autoplay blocked — show tap prompt + overlay.classList.remove('hidden'); + overlay.querySelector('h1').innerText = 'Tap to Play'; + overlay.querySelector('.status-indicator span:last-child').innerText = 'Stream ready — tap anywhere'; + }); +} async function init() { try { @@ -94,9 +122,6 @@ async function consumeProducer(producerId, kind) { remoteVideo.srcObject = new MediaStream(); } remoteVideo.srcObject.addTrack(track); - - remoteVideo.classList.add('active'); - overlay.classList.add('hidden'); // Resume the consumer (server starts them paused) await new Promise((resolve, reject) => { @@ -106,6 +131,9 @@ async function consumeProducer(producerId, kind) { }); }); + // Try to play after resuming + tryPlay(); + consumer.on('trackended', () => { console.log(`Track ended for consumer ${consumer.id}`); }); @@ -130,7 +158,6 @@ socket.on('newProducer', async ({ producerId, kind }) => { socket.on('producerClosed', ({ consumerId }) => { const consumer = consumers[consumerId]; if (consumer) { - // Remove the track from the video element if (remoteVideo.srcObject) { const track = consumer.track; if (track) { @@ -141,7 +168,6 @@ socket.on('producerClosed', ({ consumerId }) => { delete consumers[consumerId]; } - // If no more consumers, show overlay if (Object.keys(consumers).length === 0) { remoteVideo.classList.remove('active'); remoteVideo.srcObject = null; @@ -151,14 +177,11 @@ socket.on('producerClosed', ({ consumerId }) => { // Broadcaster connected - try to initialize socket.on('broadcasterConnected', () => { - // Re-init to pick up new producers if (!device) { init(); } else { - // Just fetch new producers socket.emit('getProducers', async (producers) => { for (const { producerId, kind } of producers) { - // Check if we're already consuming this producer const alreadyConsuming = Object.values(consumers).some(c => c.producerId === producerId); if (!alreadyConsuming) { await consumeProducer(producerId, kind); @@ -170,7 +193,6 @@ socket.on('broadcasterConnected', () => { // Broadcaster disconnected socket.on('broadcasterDisconnected', () => { - // Close all consumers Object.values(consumers).forEach(consumer => { consumer.close(); }); @@ -184,12 +206,6 @@ socket.on('broadcasterDisconnected', () => { overlay.querySelector('.status-indicator span:last-child').innerText = 'Waiting for new stream...'; }); -// Auto-unmute when the user interacts with the document -document.addEventListener('click', () => { - remoteVideo.muted = false; - remoteVideo.volume = 1.0; -}, { once: true }); - // Initialize on connect socket.on('connect', () => { socket.emit('viewer'); diff --git a/server.js b/server.js index 9d22925..57b12a6 100644 --- a/server.js +++ b/server.js @@ -38,6 +38,12 @@ const mediaCodecs = [ minptime: 10 } }, + { + kind: 'video', + mimeType: 'video/VP8', + clockRate: 90000, + parameters: {} + }, { kind: 'video', mimeType: 'video/H264', @@ -47,12 +53,6 @@ const mediaCodecs = [ 'profile-level-id': '42e01f', 'level-asymmetry-allowed': 1 } - }, - { - kind: 'video', - mimeType: 'video/VP8', - clockRate: 90000, - parameters: {} } ];