解决getUserMedia播放中视频切换deviceId后源未变更问题
切换正在播放的媒体流视频源问题
用例
通过下拉菜单切换可用视频源,保持视频处于播放状态。
需求
需要切换由navigator.mediaDevices.getUserMedia启动的正在播放的视频流源,尝试过关闭现有轨道、将srcObject设为null,再传入新的deviceId重新启动流,但未成功。
问题描述
切换下拉菜单后,视频元素关联的deviceId显示已更新,但实际视频源并未改变。
现有代码
JavaScript代码
$(document).ready(function() { var videoDeviceList = []; var selectedDeviceId = ""; if (!navigator.mediaDevices.enumerateDevices) { console.log("enumerateDevices() not supported."); } else { // 枚举摄像头和麦克风设备 navigator.mediaDevices.enumerateDevices() .then((devices) => { devices.forEach((device) => { if (device.kind === 'videoinput') { selectedDeviceId = selectedDeviceId === "" ? device.deviceId : selectedDeviceId; videoDeviceList.push({ deviceId: device.deviceId, deviceLabel: device.label }); } }); videoDeviceList.forEach((item) => { $("#videoList").append("<option id='" + item.deviceId + "'>" + item.deviceLabel + "</option>"); }); }) .catch((err) => { console.error(`${err.name}: ${err.message}`); }); } $("#btn").click(function() { $(this).attr("disabled", true); startOrUpdateCameraFeed(); }); $('#videoList').change(function() { selectedDeviceId = $(this).find('option:selected').attr('id'); console.log("selectedDeviceId: ", selectedDeviceId); startOrUpdateCameraFeed(); }); function startOrUpdateCameraFeed() { const video = document.getElementById('video'); // 关闭所有正在运行的视频流 if (window.videoStream !== null && window.videoStream !== undefined && window.videoStream.length > 0 && video) { window.videoStream.forEach((stream) => { if (stream) { if ('getTracks' in stream && stream.getTracks().length > 0) { stream.getTracks().forEach((track) => stopTrack(track)); } if ('getVideoTracks' in stream && stream.getVideoTracks().length > 0) { stream.getVideoTracks().forEach((track) => stopTrack(track)); } if ('getAudioTracks' in stream && stream.getAudioTracks().length > 0) { stream.getAudioTracks().forEach((track) => stopTrack(track)); } } }); window.videoStream = []; video.srcObject = null; } // 使用新的摄像头设备ID启动流 var constraints = { deviceId: { exact: selectedDeviceId } }; console.log("constraints: ", constraints); navigator.mediaDevices .getUserMedia({ video: true, video: { width: { ideal: 4096, max: 4096 }, height: { ideal: 2160, max: 2160 }, deviceId: constraints, } }).then(function(selfStream) { if (window.videoStream === null || window.videoStream === undefined) { window.videoStream = []; } if (video) { console.log('called video: ', window.videoStream); video.srcObject = selfStream; window.videoStream.push(selfStream); } }) .catch(function(err0r) { console.log('Something went wrong regular before video!', err0r); }); } function stopTrack (track){ if (track && 'stop' in track) { track.stop(); } }; });
HTML & CSS代码
<b>请授予网站访问摄像头列表的权限。</b><br/><br/> 切换摄像头源: <select id="videoList"> <option disabled>选择摄像头</option> </select> <div id="videoContainer"> <video id="video" height="100%" width="100%" autoplay></video> </div> <button id="btn"> 启动初始视频流 </button> <style> video { height: 100%; width:100%; border: 1px solid red; } #videoContainer { height: 150px; width: 250px; } </style>
问题原因及修复方案
核心问题
调用getUserMedia时存在两处错误:
- 重复定义
video参数:对象中重复的键会被后面的覆盖,但属于冗余写法; deviceId参数结构错误:你把constraints对象({deviceId: {exact: selectedDeviceId}})直接赋值给了video配置里的deviceId,导致浏览器无法正确识别目标设备。
修复后的核心代码
修改startOrUpdateCameraFeed函数,简化逻辑并修正参数结构:
function startOrUpdateCameraFeed() { const video = document.getElementById('video'); // 关闭当前流的所有轨道 if (window.videoStream) { window.videoStream.getTracks().forEach(track => track.stop()); window.videoStream = null; video.srcObject = null; } // 正确构造视频约束 const videoConstraints = { width: { ideal: 4096, max: 4096 }, height: { ideal: 2160, max: 2160 }, deviceId: { exact: selectedDeviceId } }; navigator.mediaDevices.getUserMedia({ video: videoConstraints }) .then(function(selfStream) { window.videoStream = selfStream; video.srcObject = selfStream; }) .catch(function(error) { console.log('启动视频流出错:', error); }); }
其他优化点
- 简化轨道停止逻辑:直接通过
stream.getTracks()获取所有轨道并停止,无需重复检查不同类型的轨道; - 移除冗余数组:每次切换都会替换为新流,无需用数组维护多个流实例,单个变量存储当前流即可;
- 修正拼写错误:将
err0r改为error,提升代码可读性。
内容的提问来源于stack exchange,提问作者Jivan
相关产品推荐
相关产品推荐

