| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217 |
- var pc = null;
- function negotiate() {
- pc.addTransceiver('video', { direction: 'recvonly' });
- pc.addTransceiver('audio', { direction: 'recvonly' });
- return pc.createOffer().then((offer) => {
- console.log('Created offer: type=' + offer.type + ', sdp_length=' + offer.sdp.length);
- return pc.setLocalDescription(offer).then(() => {
- console.log('Set local description complete, iceGatheringState=' + pc.iceGatheringState);
- });
- }).then(() => {
- // wait for ICE gathering to complete
- return new Promise((resolve, reject) => {
- if (pc.iceGatheringState === 'complete') {
- console.log('ICE gathering already complete');
- resolve();
- } else {
- const checkState = () => {
- if (pc.iceGatheringState === 'complete') {
- pc.removeEventListener('icegatheringstatechange', checkState);
- console.log('ICE gathering complete');
- resolve();
- }
- };
- pc.addEventListener('icegatheringstatechange', checkState);
-
- // 5秒超时
- setTimeout(() => {
- pc.removeEventListener('icegatheringstatechange', checkState);
- resolve(); // 继续执行,即使 ICE 汇集未完成
- }, 5000);
- }
- });
- }).then(() => {
- var offer = pc.localDescription;
- console.log('Sending offer: type=' + offer.type + ', sdp_length=' + offer.sdp.length);
- return fetch('/offer', {
- body: JSON.stringify({
- sdp: offer.sdp,
- type: offer.type,
- }),
- headers: {
- 'Content-Type': 'application/json'
- },
- method: 'POST'
- }).then((response) => {
- console.log('Received response status:', response.status);
- return response;
- });
- }).then((response) => {
- if (!response.ok) {
- throw new Error('服务器返回错误: ' + response.status);
- }
- return response.json();
- }).then((answer) => {
- console.log('Received answer: type=' + answer.type + ', sdp_length=' + answer.sdp.length);
- document.getElementById('sessionid').value = answer.sessionid
- return pc.setRemoteDescription(answer).then(() => {
- console.log('Successfully set remote description');
- });
- }).catch((e) => {
- console.error('Error in negotiate:', e);
- let errorMsg = '连接失败: ';
- if (e.name === 'InvalidStateError') {
- errorMsg += '连接状态异常,请刷新页面后重试';
- } else if (e.name === 'SyntaxError') {
- errorMsg += '服务器响应格式错误,请检查服务器状态';
- } else if (e.message && e.message.includes('state')) {
- errorMsg += '连接状态错误,请刷新页面后重试';
- } else if (e.message && e.message.includes('服务器返回错误')) {
- errorMsg += e.message + ',请检查后端日志';
- } else {
- errorMsg += e.message || e.toString();
- }
- alert(errorMsg);
- });
- }
- function start() {
- var config = {
- sdpSemantics: 'unified-plan'
- };
- // 使用多个 STUN 服务器(提高连接成功率)
- config.iceServers = [
- { urls: ['stun:stun.l.google.com:19302'] },
- { urls: ['stun:stun1.l.google.com:19302'] },
- { urls: ['stun:stun.services.mozilla.com'] },
- { urls: ['stun:stun.voip.blackberry.com:3478'] }
- ];
- pc = new RTCPeerConnection(config);
- // 添加连接状态日志
- pc.addEventListener('connectionstatechange', function() {
- console.log('PeerConnection state:', pc.connectionState);
- // 将日志发送到后端
- fetch('/log', {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({ type: 'webrtc', message: 'PeerConnection state: ' + pc.connectionState })
- }).catch(() => {});
-
- // 连接成功时的提示
- if (pc.connectionState === 'connected') {
- console.log('✅ WebRTC connection established successfully!');
- document.getElementById('startBtn').style.display = 'none';
- document.getElementById('stopBtn').style.display = 'inline-block';
- } else if (pc.connectionState === 'failed') {
- console.error('❌ WebRTC connection failed!');
- alert('WebRTC 连接失败,请检查网络连接或刷新页面重试');
- }
- });
-
- pc.addEventListener('iceconnectionstatechange', function() {
- console.log('ICE connection state:', pc.iceConnectionState);
- fetch('/log', {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({ type: 'webrtc', message: 'ICE connection state: ' + pc.iceConnectionState })
- }).catch(() => {});
-
- // ICE 连接超时处理
- if (pc.iceConnectionState === 'disconnected') {
- console.warn('⚠️ ICE disconnected, attempting to reconnect...');
- } else if (pc.iceConnectionState === 'failed') {
- console.error('❌ ICE connection failed!');
- alert('ICE 连接失败,可能是网络防火墙阻止了 WebRTC 连接');
- }
- });
-
- pc.addEventListener('icegatheringstatechange', function() {
- console.log('ICE gathering state:', pc.iceGatheringState);
- fetch('/log', {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({ type: 'webrtc', message: 'ICE gathering state: ' + pc.iceGatheringState })
- }).catch(() => {});
- });
- // 收集 ICE 候选
- pc.addEventListener('icecandidate', function(event) {
- if (event.candidate) {
- console.log('ICE candidate:', event.candidate.candidate);
- fetch('/log', {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({ type: 'webrtc', message: 'ICE candidate: ' + event.candidate.candidate })
- }).catch(() => {});
- } else {
- console.log('ICE gathering complete');
- fetch('/log', {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({ type: 'webrtc', message: 'ICE gathering complete' })
- }).catch(() => {});
- }
- });
- // connect audio / video
- pc.addEventListener('track', (evt) => {
- if (evt.track.kind == 'video') {
- const video = document.getElementById('video');
- if (video) {
- video.srcObject = evt.streams[0];
- console.log('Video track attached successfully');
- } else {
- console.error('Video element not found in DOM');
- }
- } else if (evt.track.kind == 'audio') {
- const audio = document.getElementById('audio');
- if (audio) {
- audio.srcObject = evt.streams[0];
- console.log('Audio track attached successfully');
- // 尝试播放音频(处理浏览器自动播放策略)
- audio.play().catch(e => {
- console.warn('Auto-play was prevented, user interaction needed:', e);
- });
- } else {
- console.warn('Audio element not found in DOM, but continuing with video only');
- }
- }
- });
- document.getElementById('startBtn').style.display = 'none';
- negotiate();
- document.getElementById('stopBtn').style.display = 'inline-block';
- }
- function stop() {
- document.getElementById('stopBtn').style.display = 'none';
- // close peer connection
- setTimeout(() => {
- pc.close();
- }, 500);
- }
- window.onunload = function(event) {
- // 在这里执行你想要的操作
- setTimeout(() => {
- pc.close();
- }, 500);
- };
- window.onbeforeunload = function (e) {
- setTimeout(() => {
- pc.close();
- }, 500);
- e = e || window.event
- // 兼容IE8和Firefox 4之前的版本
- if (e) {
- e.returnValue = '关闭提示'
- }
- // Chrome, Safari, Firefox 4+, Opera 12+ , IE 9+
- return '关闭提示'
- }
|