/** * 设备检测demo */ /* global $ TRTC presetting getOS getBroswer cameraId micId */ // 用于记录检测结果,生成检测报告 let hasCameraDevice = false, hasMicAndVoiceDevice = false, hasCameraConnect, hasVoiceConnect, hasMicConnect, hasNetworkConnect; let cameraTestingResult = {}; let voiceTestingResult = {}; let micTestingResult = {}; let networkTestingResult = {}; // 记录检测步骤,用于关闭时清空弹窗 let completedTestingPageIdList = []; let curTestingPageId = ''; let localStream = null; let client = null; let timeout = null; // 监听到network-quality事件的次数 let networkQualityNum = 0; const deviceFailAttention = '1. 若浏览器弹出提示,请选择“允许”
' + '2. 若杀毒软件弹出提示,请选择“允许”
' + '3. 检查浏览器设置,允许网页访问摄像头及麦克风
' + '4. 检查摄像头/麦克风是否正确连接并开启
' + '5. 尝试重新连接摄像头/麦克风
' + '6. 尝试重启电脑后重新检测'; const networkFailAttention = '1. 请检查设备是否联网
' + '2. 请刷新网页后再次检测
' + '3. 请尝试更换网络后再次检测'; // 网络参数对照表 const NETWORK_QUALITY = { '0': '未知', '1': '极佳', '2': '较好', '3': '一般', '4': '差', '5': '极差', '6': '断开' }; // 设备检测tab页签对应的执行方法 const pageCallbackConfig = { 'camera-testing-body': 'startCameraTesting', 'voice-testing-body': 'startVoiceTesting', 'mic-testing-body': 'startMicTesting', 'network-testing-body': 'startNetworkTesting' }; // 判断是否为safari浏览器 let isSafari = /Safari/.test(navigator.userAgent) && !/Chrome/.test(navigator.userAgent); hideVoiceForSafari(); /** * safari浏览器中隐藏扬声器相关检测 */ function hideVoiceForSafari() { if (!isSafari) return; $('#connect-voice').hide(); $('#device-voice').hide(); $('#voice-testing').hide(); $('#voice-report').hide(); $('#device-mic').addClass('safari'); $('#device-network').addClass('safari'); $('#mic-testing').addClass('safari'); $('#network-testing').addClass('safari'); } // 是否是本地路径打开 let isFilePath = location.href.indexOf('file://') > -1; /** * 设备检测初始化 */ async function deviceTestingInit() { // 点击【设备检测】文字, 点击 【重新连接】按钮 $('#device-testing-btn, #connect-again-btn').on('click', () => { startDeviceConnect(); }); // 连接设备错误icon $('#connect-attention-icon').on('mouseover', () => { $('#connect-attention-info').show(); }); // 连接设备错误icon $('#connect-attention-icon').on('mouseout', () => { $('#connect-attention-info').hide(); }); // 【开始检测】开始设备检测按钮 $('#start-test-btn').on('click', function() { if ($(this).hasClass('start-gray')) return; $('#device-testing-prepare').hide(); $('#device-testing').show(); startCameraTesting(); }); // 摄像头检测失败/成功 $('#camera-fail, #camera-success').on('click', function() { cameraTestingResult.statusResult = $(this).attr('id') === 'camera-success'; $('#camera-testing-body').hide(); localStream.close(); // safari浏览器跳过扬声器检测 isSafari ? startMicTesting() : startVoiceTesting(); }); // 播放器检测失败/成功 $('#voice-fail, #voice-success').on('click', function() { voiceTestingResult.statusResult = $(this).attr('id') === 'voice-success'; $('#voice-testing-body').hide(); let audioPlayer = document.querySelector('#audio-player'); if (!audioPlayer.paused) { audioPlayer.pause(); } startMicTesting(); }); // 麦克风测试失败/成功 $('#mic-fail, #mic-success').on('click', function() { micTestingResult.statusResult = $(this).attr('id') === 'mic-success'; $('#mic-testing-body').hide(); localStream.close(); startNetworkTesting(); }); // 点击【查看检测报告】按钮 $('#testing-report-btn').on('click', () => { showTestingReport(); localStream.close(); client && client.leave(); client && client.off('network-quality'); }); // 点击【重新测试】按钮 $('#testing-again').on('click', () => { $('#device-testing-report').hide(); startDeviceConnect(); completedTestingPageIdList = []; }); // 点击【测试完成】按钮 / 点击关闭图标 $('#testing-finish, #device-testing-close-btn').on('click', () => { finishDeviceTesting(); }); // 测试tab页切换 $('#camera-testing, #voice-testing, #mic-testing, #network-testing').on('click', function() { let targetPageId = $(this).attr('id') + '-body'; if ( targetPageId !== curTestingPageId && completedTestingPageIdList.indexOf(targetPageId) > -1 ) { $(`#${curTestingPageId}`).hide(); localStream && localStream.close(); client && client.leave(); client && client.off('network-quality'); // 停止播放器的音乐 let audioPlayer = document.querySelector('#audio-player'); if (!audioPlayer.paused) { audioPlayer.pause(); } // 展示要切换的设备检测tab页面 $(`#${targetPageId}`).show(); window[pageCallbackConfig[targetPageId]] && window[pageCallbackConfig[targetPageId]](); } }); // 摄像头设备切换 $('#camera-select').change(async function() { let newCameraId = $(this) .children('option:selected') .val(); localStorage.setItem('txy_webRTC_cameraId', newCameraId); cameraTestingResult.device = { label: $(this) .children('option:selected') .text(), deviceId: $(this) .children('option:selected') .val(), kind: 'videoinput' }; await localStream.switchDevice('video', newCameraId); }); // 扬声器设备切换 $('#voice-select').change(async function() { let newVoiceId = $(this) .children('option:selected') .val(); localStorage.setItem('txy_webRTC_voiceId', newVoiceId); voiceTestingResult.device = { label: $(this) .children('option:selected') .text(), deviceId: $(this) .children('option:selected') .val(), kind: 'audiooutput' }; let audioPlayer = document.querySelector('#audio-player'); await audioPlayer.setSinkId(newVoiceId); }); // 麦克风设备切换 $('#mic-select').change(async function() { let newMicID = $(this) .children('option:selected') .val(); localStorage.setItem('txy_webRTC_micId', newMicID); micTestingResult.device = { label: $(this) .children('option:selected') .text(), deviceId: $(this) .children('option:selected') .val(), kind: 'audioinput' }; await localStream.switchDevice('audio', newMicID); }); $('body').on('click', function() { $('#device-connect-list').hide(); }); // 获取设备信息 await getDevicesInfo(); // 初始化设备弹窗信息 deviceDialogInit(); } /** * 获取设备信息及网络连接信息 */ async function getDevicesInfo() { let micList = await TRTC.getMicrophones(); let voiceList = await TRTC.getSpeakers(); let cameraList = await TRTC.getCameras(); let index = isFilePath ? 'label' : 'deviceId'; if (cameraList.length > 0) { hasCameraDevice = true; } if (micList.length > 0) { hasMicAndVoiceDevice = true; } cameraList.forEach(camera => { if (camera[index].length > 0) { hasCameraConnect = true; } }); micList.forEach(mic => { if (mic[index].length > 0) { hasMicConnect = true; } }); if (isSafari) { hasVoiceConnect = true; } else { voiceList.forEach(voice => { if (voice[index].length > 0) { hasVoiceConnect = true; } }); } hasNetworkConnect = !!navigator.onLine; } /** * 判断是否展示弹窗 */ function deviceDialogInit() { if (!localStorage.getItem('txy_device_testing')) { localStorage.setItem('txy_device_testing', Date.now()); startDeviceConnect(); } else { // 在首页展示设备连接结果 let showDeviceStatus = function() { $('#device-connect-list').show(); timeout = setTimeout(() => { $('#device-connect-list').hide(); }, 3000); $('#connect-camera').css('color', `${hasCameraConnect ? 'green' : 'red'}`); $('#connect-voice').css('color', `${hasVoiceConnect ? 'green' : 'red'}`); $('#connect-mic').css('color', `${hasMicConnect ? 'green' : 'red'}`); $('#connect-network').css('color', `${hasNetworkConnect ? 'green' : 'red'}`); if (!(hasCameraConnect && hasVoiceConnect && hasMicConnect && hasNetworkConnect)) { $('#device-testing-btn').css('color', 'red'); } else { $('#device-testing-btn').css('color', 'green'); } }; showDeviceStatus(); if (!(hasCameraConnect && hasVoiceConnect && hasMicConnect)) { navigator.mediaDevices .getUserMedia({ video: hasCameraDevice, audio: hasMicAndVoiceDevice }) .then(async () => { // 重新获取设备信息 await getDevicesInfo(); // 更新首页popover的option list getDevicesList(); // 展示连接结果 showDeviceStatus(); }) .catch(err => {}); } } } /** * 弹窗-设备连接检查 */ function startDeviceConnect() { // 显示设备检测弹窗 $('#device-testing-root').show(); // 设备检测弹窗-设备连接页 $('#device-testing-prepare').show(); curTestingPageId = 'device-testing-prepare'; initTestingTabTitle(); // 在设备检测弹窗显示设备连接信息 let showDeviceConnectInfo = function() { if (!(hasCameraConnect && hasVoiceConnect && hasMicConnect && hasNetworkConnect)) { $('#device-testing-btn').css('color', 'red'); } else { $('#device-testing-btn').css('color', 'green'); } // 隐藏设备连接失败提示 $('#connect-attention-container').hide(); // 设备连接中 $('#device-loading').show(); $('#connect-info') .text('设备正在连接中,请稍等') .css('color', '#cccccc'); $('#device-camera, #device-voice, #device-mic, #device-network').removeClass( 'connect-success connect-fail' ); $('#connect-again-btn').hide(); $('#start-test-btn') .addClass('start-gray') .show(); // 设备连接结束,展示连接结果 setTimeout(() => { $('#device-loading').hide(); $('#device-camera') .removeClass('connect-success connect-fail') .addClass(`${hasCameraConnect ? 'connect-success' : 'connect-fail'}`); $('#device-voice') .removeClass('connect-success connect-fail') .addClass(`${hasVoiceConnect ? 'connect-success' : 'connect-fail'}`); $('#device-mic') .removeClass('connect-success connect-fail') .addClass(`${hasMicConnect ? 'connect-success' : 'connect-fail'}`); $('#device-network') .removeClass('connect-success connect-fail') .addClass(`${hasNetworkConnect ? 'connect-success' : 'connect-fail'}`); if (!(hasCameraConnect && hasVoiceConnect && hasMicConnect)) { let connectInfo = hasNetworkConnect ? '设备连接失败,请允许网页访问摄像头及麦克风' : '设备及网络连接失败,请允许网页访问摄像头及麦克风并检查网络连接'; $('#connect-info') .text(connectInfo) .css('color', 'red'); // 显示设备连接失败引导 $('#connect-attention-container').show(); $('#connect-attention-info').html(deviceFailAttention); // 切换按钮状态 $('#start-test-btn').hide(); $('#connect-again-btn').show(); } if (hasCameraConnect && hasVoiceConnect && hasMicConnect && !hasNetworkConnect) { $('#connect-info') .text('网络连接失败,请检查网络连接') .css('color', 'red'); // 显示网络连接失败引导 $('#connect-attention-container').show(); $('#connect-attention-info').html(networkFailAttention); // 切换按钮状态 $('#start-test-btn').hide(); $('#connect-again-btn').show(); } if (hasCameraConnect && hasVoiceConnect && hasMicConnect && hasNetworkConnect) { $('#connect-info') .text('设备及网络连接成功,请开始设备检测') .css('color', '#32CD32'); $('#connect-again-btn').hide(); $('#start-test-btn') .removeClass('start-gray') .show(); } }, 2000); }; showDeviceConnectInfo(); // 如果有设备未连接,唤起请求弹窗 if (!(hasCameraConnect && hasVoiceConnect && hasMicConnect)) { navigator.mediaDevices .getUserMedia({ video: hasCameraDevice, audio: hasMicAndVoiceDevice }) .then(async () => { // 重新获取设备信息 await getDevicesInfo(); // 更新首页popover的option list getDevicesList(); // 显示设备连接信息 showDeviceConnectInfo(); }) .catch(err => { console.log('err', err.name); }); } } /** * 更新首页popover的option list */ function getDevicesList() { // populate camera options TRTC.getCameras().then(devices => { $('#camera-option').empty(); devices.forEach(device => { if (!cameraId) { // eslint-disable-next-line no-global-assign cameraId = device.deviceId; } let div = $('
'); div.attr('id', device.deviceId); div.html(device.label); div.appendTo('#camera-option'); }); }); // populate microphone options TRTC.getMicrophones().then(devices => { $('#mic-option').empty(); devices.forEach(device => { if (!micId) { // eslint-disable-next-line no-global-assign micId = device.deviceId; } let div = $('
'); div.attr('id', device.deviceId); div.html(device.label); div.appendTo('#mic-option'); }); }); } /** * 摄像头检测页-检测展示摄像头设备选择列表 */ async function updateCameraDeviceList() { let cameraDevices = await TRTC.getCameras(); cameraDevices.filter(camera => camera.deviceId !== 'default'); $('#camera-select').empty(); cameraDevices.forEach(camera => { let option = $(''); option.attr('value', camera.deviceId); option.html(camera.label); option.appendTo('#camera-select'); }); // 如果有用户设备选择缓存,优先使用缓存的deviceId let cacheCameraDevice = cameraDevices.filter( camera => camera.deviceId === localStorage.getItem('txy_webRTC_cameraId') ); if (cacheCameraDevice.length > 0) { $('#camera-select').val(localStorage.getItem('txy_webRTC_cameraId')); cameraTestingResult.device = cacheCameraDevice[0]; } else { $('#camera-select').val(cameraDevices[0].deviceId); cameraTestingResult.device = cameraDevices[0]; } } /** * 摄像头设备测试 */ async function startCameraTesting() { $('#camera-testing-body').show(); curTestingPageId = 'camera-testing-body'; $('#camera-testing') .removeClass('icon-normal') .addClass('icon-blue complete'); completedTestingPageIdList.push('camera-testing-body'); completedTestingPageIdList = [...new Set(completedTestingPageIdList)]; await updateCameraDeviceList(); // 创建本地视频流 await createLocalStream( { audio: false, video: true, cameraId: cameraTestingResult.device.deviceId }, 'camera-video' ); } /** * 初始化/更新扬声器设备数组 */ async function updateVoiceDeviceList() { // 获取扬声器设备并展示在界面中 let voiceDevices = await TRTC.getSpeakers(); voiceDevices = voiceDevices.filter(voice => voice.deviceId !== 'default'); $('#voice-select').empty(); voiceDevices.forEach(voice => { let option = $(''); option.attr('value', voice.deviceId); option.html(voice.label); option.appendTo('#voice-select'); }); // 如果有用户设备选择缓存,优先使用缓存的deviceId let cacheVoiceDevice = voiceDevices.filter( mic => mic.deviceId === localStorage.getItem('txy_webRTC_voiceId') ); if (cacheVoiceDevice.length > 0) { $('#voice-select').val(localStorage.getItem('txy_webRTC_voiceId')); voiceTestingResult.device = cacheVoiceDevice[0]; } else { $('#voice-select').val(voiceDevices[0].deviceId); voiceTestingResult.device = voiceDevices[0]; } } /** * 播放器设备测试 */ async function startVoiceTesting() { $('#voice-testing-body').show(); curTestingPageId = 'voice-testing-body'; $('#voice-testing') .removeClass('icon-gray') .addClass('icon-blue complete'); completedTestingPageIdList.push('voice-testing-body'); completedTestingPageIdList = [...new Set(completedTestingPageIdList)]; await updateVoiceDeviceList(); } /** * 更新/初始化麦克风设备 */ async function updateMicDeviceList() { // 展示麦克风设备选择 let micDevices = await TRTC.getMicrophones(); micDevices = micDevices.filter(mic => mic.deviceId !== 'default'); $('#mic-select').empty(); micDevices.forEach(mic => { let option = $(''); option.attr('value', mic.deviceId); option.html(mic.label); option.appendTo('#mic-select'); }); // 如果有用户设备选择缓存,优先使用缓存的deviceId let cacheMicDevice = micDevices.filter( mic => mic.deviceId === localStorage.getItem('txy_webRTC_micId') ); if (cacheMicDevice.length > 0) { $('#mic-select').val(localStorage.getItem('txy_webRTC_micId')); micTestingResult.device = cacheMicDevice[0]; } else { $('#mic-select').val(micDevices[0].deviceId); micTestingResult.device = micDevices[0]; } } /** * 麦克风设备测试 */ async function startMicTesting() { $('#mic-testing-body').show(); curTestingPageId = 'mic-testing-body'; $('#mic-testing') .removeClass('icon-gray') .addClass('icon-blue complete'); completedTestingPageIdList.push('mic-testing-body'); completedTestingPageIdList = [...new Set(completedTestingPageIdList)]; await updateMicDeviceList(); // 展示麦克风的声音大小显示 if ($('#mic-bar-container').children().length === 0) { for (let index = 0; index < 28; index++) { $('
') .addClass('mic-bar') .appendTo('#mic-bar-container'); } } // 创建本地音频流 await createLocalStream( { audio: true, microphoneId: micTestingResult.device.deviceId, video: false }, 'audio-container' ); // 监听音量,并量化显示出来 setInterval(() => { let volume = localStream.getAudioLevel(); let num = Math.ceil(28 * volume); $('#mic-bar-container') .children('.active') .removeClass('active'); for (let i = 0; i < num; i++) { $('#mic-bar-container') .children() .slice(0, i) .addClass('active'); } }, 100); } /** * 系统信息展示 */ async function startNetworkTesting() { $('#network-testing-body').show(); $('#testing-report-btn').hide(); curTestingPageId = 'network-testing-body'; $('#network-testing') .removeClass('icon-gray') .addClass('icon-blue complete'); completedTestingPageIdList.push('network-testing-body'); completedTestingPageIdList = [...new Set(completedTestingPageIdList)]; networkQualityNum = 0; $('#uplink-network') .addClass('network-loading') .text(''); // 获取系统信息 $('#system').empty(); let OSInfo = getOS(); $('
') .text(OSInfo.osName) .appendTo('#system'); // 获取浏览器及版本信息 $('#browser').empty(); let browser = getBroswer(); $('
') .text(`${browser.broswer} ${browser.version}`) .appendTo('#browser'); // 获取ip地址信息 // $('#ip').empty(); // let IPAddress = await getIPAddress(); // $('
').text(IPAddress).appendTo('#ip'); // networkTestingResult.IPAddress = IPAddress; // 是否支持屏幕分享能力 $('#screen-share').empty(); let isScreenShareSupported = TRTC.isScreenShareSupported(); $('
') .text(isScreenShareSupported ? '支持' : '不支持') .appendTo('#screen-share'); // 上下行网络质量 presetting.login(false, async options => { client = TRTC.createClient({ mode: 'rtc', ...options }); client.on('network-quality', event => { networkQualityNum++; // 收到3次'network-quality'事件的时候认为拿到了网络实际质量 if (networkQualityNum === 3) { networkTestingResult.upLinkNetwork = event.uplinkNetworkQuality; networkTestingResult.downLinkNetwork = event.downlinkNetworkQuality; $('#uplink-network') .removeClass('network-loading') .text(NETWORK_QUALITY[String(networkTestingResult.upLinkNetwork)]); $('#testing-report-btn').show(); client && client.leave(); client && client.off('network-quality'); } }); await client.join({ roomId: options.roomId }); await createLocalStream( { audio: true, video: false }, 'audio-container' ); await client.publish(localStream); // 音频轨道静音 localStream.muteAudio(); }); } /** * 展示检测报告 */ function showTestingReport() { $('#device-testing').hide(); $('#network-testing-body').hide(); $('#device-testing-report').show(); curTestingPageId = 'device-testing-report'; // 摄像头检测结果 $('#camera-name').text(cameraTestingResult.device.label); if (cameraTestingResult.statusResult) { $('#camera-testing-result') .text('正常') .css('color', 'green'); } else { $('#camera-testing-result') .text('异常') .css('color', 'red'); } // 扬声器检测结果(safari浏览器不显示扬声器检测结果) if (!isSafari) { $('#voice-name').text(voiceTestingResult.device.label); if (voiceTestingResult.statusResult) { $('#voice-testing-result') .text('正常') .css('color', 'green'); } else { $('#voice-testing-result') .text('异常') .css('color', 'red'); } } // 麦克风检测结果 $('#mic-name').text(micTestingResult.device.label); if (micTestingResult.statusResult) { $('#mic-testing-result') .text('正常') .css('color', 'green'); } else { $('#mic-testing-result') .text('异常') .css('color', 'red'); } // 网络检测结果 // $('#network-name').text(networkTestingResult.IPAddress); $('#network-name').text('网络质量'); $('#network-testing-result') .html(`${NETWORK_QUALITY[String(networkTestingResult.upLinkNetwork)]}`) .css('color', `${Number(networkTestingResult.upLinkNetwork) > 3 ? 'red' : 'green'}`); } /** * 结束设备检测,隐藏设备检测弹窗 */ function finishDeviceTesting() { $('#device-testing-root').hide(); $('#device-testing').hide(); $(`#${curTestingPageId}`).hide(); curTestingPageId = ''; completedTestingPageIdList = []; // 停止摄像头/麦克风的流采集并释放摄像头/麦克风设备 localStream && localStream.close(); client && client.leave(); client && client.off('network-quality'); // 停止播放器的音乐 let audioPlayer = document.querySelector('#audio-player'); if (!audioPlayer.paused) { audioPlayer.pause(); } audioPlayer.currentTime = 0; } /** * 恢复检测页面头部图标的状态 */ function initTestingTabTitle() { ['camera', 'voice', 'mic', 'network'].forEach(item => { $(`#${item}-testing`) .removeClass('icon-blue complete') .addClass('icon-gray'); }); } /** * 监听设备变化 */ navigator.mediaDevices.ondevicechange = async function(event) { // 当前在摄像头检测页 if (curTestingPageId === 'camera-testing-body') { await updateCameraDeviceList(); return; } // 当前在扬声器检测页 if (curTestingPageId === 'voice-testing-body') { await updateVoiceDeviceList(); return; } // 当前在麦克风检测页 if (curTestingPageId === 'mic-testing-body') { await updateMicDeviceList(); return; } }; /** * 抽离createStream的公共处理函数 */ async function createLocalStream(constraints, container) { localStream = TRTC.createStream(constraints); try { await localStream.initialize(); } catch (error) { switch (error.name) { case 'NotReadableError': // 当系统或浏览器异常的时候,可能会出现此错误,您可能需要引导用户重启电脑/浏览器来尝试恢复。 alert('暂时无法访问摄像头/麦克风,请确保当前没有其他应用请求访问摄像头/麦克风,并重试'); return; case 'NotAllowedError': // 用户拒绝授权访问摄像头或麦克风 | 屏幕分享,您需要引导客户来授权访问 alert('用户已拒绝授权访问摄像头或麦克风'); return; case 'NotFoundError': // 找不到摄像头或麦克风设备 alert('找不到摄像头或麦克风设备'); return; case 'OverConstrainedError': alert( '采集属性设置错误,如果您指定了 cameraId/microphoneId,请确保它们是一个有效的非空字符串' ); return; default: alert('未知错误'); return; } } container && localStream.play(container); }