openvibers/openvibe.live Commits

Live: devicechange only re-enumerates, never asks for camera or mic (#776)

OP OpenVibers committed 3 days ago · f54f7c7193 · 2 files, +36 −1

Changes

ddiff --git a/public/js/broadcast.js b/public/js/broadcast.js
iindex a97dcd9a..800b5ed0 100644
---- a/public/js/broadcast.js
++++ b/public/js/broadcast.js
@@@ -4823,7 +4823,9 @@ function _refreshVerticalPreview() {
navigator.mediaDevices._openvibeDevicechangeListening = true;
navigator.mediaDevices.addEventListener('devicechange', async () => {
console.log('[Broadcast] Device change detected — re-enumerating devices');
-- await populateDeviceLists().catch(() => {});
++ // Enumerate-only: a hardware event is not a press, so it must never ask for the camera or mic here.
++ // Permission still comes only from the "Turn on camera / microphone" press (requestMediaPermissions).
++ await refreshDeviceLists().catch(() => {});
// The Go Live card follows too (a camera or headset plugged in or out), so it needs no refresh button.
if (document.getElementById('bc-create-camera')) {
await navigator.mediaDevices.enumerateDevices().then(_populateCreateDeviceDropdowns).catch(() => {});
ddiff --git a/test/capture-on-load.test.js b/test/capture-on-load.test.js
nnew file mode 100644
iindex 00000000..29b8fbed
---- /dev/null
++++ b/test/capture-on-load.test.js
@@@ -0,0 +1,33 @@
++/**
++ * No capture until a press: a devicechange (plug/unplug) is a hardware event, not
++ * a person press, so it must only re-enumerate. It must never reach getUserMedia
++ * through populateDeviceLists / bcMediaSetup.request / startMediaCapture.
++ * Permission still comes only from the "Turn on camera / microphone" press.
++ */
++const assert = require('assert');
++const fs = require('fs');
++const path = require('path');
++
++const root = path.join(__dirname, '..');
++const read = (p) => fs.readFileSync(path.join(root, p), 'utf8');
++const broadcast = read('public/js/broadcast.js');
++const devices = read('public/js/broadcast-devices.js');
++
++// The devicechange handler must be enumerate-only.
++const at = broadcast.indexOf("addEventListener('devicechange'");
++assert.ok(at !== -1, 'broadcast.js must listen for devicechange');
++const handler = broadcast.slice(at, broadcast.indexOf('});', broadcast.indexOf("addEventListener('devicechange'")) + 3);
++assert.ok(/refreshDeviceLists\(\)/.test(handler), 'devicechange re-enumerates via refreshDeviceLists (enumerate-only)');
++for (const bad of ['populateDeviceLists', 'bcMediaSetup.request', 'getUserMedia(', 'startMediaCapture']) {
++ assert.ok(!handler.includes(bad), `devicechange must not reach ${bad}`);
++}
++console.log('OK capture-on-load: devicechange only re-enumerates');
++
++// The permission flow itself is unchanged: it still asks through the explainer.
++const populate = devices.slice(devices.indexOf('async function populateDeviceLists()'),
++ devices.indexOf('function _getPreferredCameraId()'));
++assert.ok(populate.includes("bcMediaSetup.request('mic')") && populate.includes("bcMediaSetup.request('camera')"),
++ 'populateDeviceLists must still ask per-device through the explainer');
++console.log('OK capture-on-load: permission flow unchanged (explainer, on press)');
++
++console.log('✅ capture on load test passed');