<!DOCTYPE html> <html> <head> <script type='text/javascript' src='https://cdn.scaledrone.com/scaledrone.min.js'></script> <meta charset="utf-8"> <meta name="viewport" content="width=device-width"> <style> body { display: flex; height: 100vh; margin: 0; align-items: center; justify-content: center; padding: 0 50px; font-family: -apple-system, BlinkMacSystemFont, sans-serif; } video { max-width: calc(50% - 100px); margin: 0 50px; box-sizing: border-box; border-radius: 2px; padding: 0; box-shadow: rgba(156, 172, 172, 0.2) 0px 2px 2px, rgba(156, 172, 172, 0.2) 0px 4px 4px, rgba(156, 172, 172, 0.2) 0px 8px 8px, rgba(156, 172, 172, 0.2) 0px 16px 16px, rgba(156, 172, 172, 0.2) 0px 32px 32px, rgba(156, 172, 172, 0.2) 0px 64px 64px; } .copy { position: fixed; top: 10px; left: 50%; transform: translateX(-50%); font-size: 16px; color: rgba(0, 0, 0, 0.5); } </style> </head> <body> <div class="copy">Send your URL to a friend to start a video call</div> <video id="localVideo" autoplay muted></video> <video id="remoteVideo" autoplay></video> <script src="script.js"></script> </body> </html> <script> // 如果需要,生成隨機的房間名稱 if (!location.hash) { location.hash = Math.floor(Math.random() * 0xFFFFFF).toString(16); } const roomHash = location.hash.substring(1); console.log(roomHash) // 用您自己的通道ID替換 const drone = new ScaleDrone('yiS12Ts5RdNhebyM'); //房間名稱前須加上“可觀察到的-” const roomName = 'observable-' + roomHash; const configuration = { iceServers: [{ urls: 'stun:stun.l.google.com:19302' }] }; let room; let pc; function onSuccess() {}; function onError(error) { console.error(error); }; drone.on('open', error => { if (error) { return console.error(error); } room = drone.subscribe(roomName); room.on('open', error => { if (error) { onError(error); } }); // 我們連接到房間並接收到一組“成員” // 連接到房間(包括我們)。信令服務器准備好了。 room.on('members', members => { console.log('MEMBERS', members); // 如果我們是第二個連接到房間的用戶,我們將創建offer const isOfferer = members.length === 2; startWebRTC(isOfferer); }); }); // 通過Scaledrone發送信號數據 function sendMessage(message) { drone.publish({ room: roomName, message }); } function startWebRTC(isOfferer) { pc = new RTCPeerConnection(configuration); // “onicecandidate”在ICE代理需要交付a時通知我們 // 通過信令服務器向另一個對等點發送消息 pc.onicecandidate = event => { if (event.candidate) { sendMessage({'candidate': event.candidate}); } }; // If user is offerer let the 'negotiationneeded' event create the offer if (isOfferer) { pc.onnegotiationneeded = () => { pc.createOffer().then(localDescCreated).catch(onError); } } // 當遠程流到達時,將其顯示在#remoteVideo元素中 pc.ontrack = event => { const stream = event.streams[0]; if (!remoteVideo.srcObject || remoteVideo.srcObject.id !== stream.id) { remoteVideo.srcObject = stream; } }; navigator.mediaDevices.getUserMedia({ audio: true, video: true, }).then(stream => { // 在#localVideo元素中顯示本地視頻 localVideo.srcObject = stream; // 添加要發送到conneting對等點的流 stream.getTracks().forEach(track => pc.addTrack(track, stream)); }, onError); // 聽Scaledrone的信號數據 room.on('data', (message, client) => { // 消息是由我們發出的 if (client.id === drone.clientId) { return; } if (message.sdp) { // 這是在收到來自其他同事的提議或回答后調用的 pc.setRemoteDescription(new RTCSessionDescription(message.sdp), () => { // 當收到offer時,讓我們答復 if (pc.remoteDescription.type === 'offer') { pc.createAnswer().then(localDescCreated).catch(onError); } }, onError); } else if (message.candidate) { // 將新的ICE候選項添加到我們的連接遠程描述中 pc.addIceCandidate( new RTCIceCandidate(message.candidate), onSuccess, onError ); } }); } function localDescCreated(desc) { pc.setLocalDescription( desc, () => sendMessage({'sdp': pc.localDescription}), onError ); } </script>
免費的stun服務器:
stun:stun1.l.google.com:19302 stun:stun2.l.google.com:19302 stun:stun3.l.google.com:19302 stun:stun4.l.google.com:19302 stun:23.21.150.121 stun:stun01.sipphone.com stun:stun.ekiga.net stun:stun.fwdnet.net stun:stun.ideasip.com stun:stun.iptel.org stun:stun.rixtelecom.se stun:stun.schlund.de stun:stunserver.org stun:stun.softjoys.com stun:stun.voiparound.com stun:stun.voipbuster.com stun:stun.voipstunt.com stun:stun.voxgratia.org stun:stun.xten.com
線上視頻必須配置https或者本地localhost 才能實現視頻通信
windows配置https
server { # HTTPS 默認443端口 listen 443 ssl; server_name localhost; # 證書文件配置,指定證書的路徑,除了證書路徑其他配置都默認 ssl_certificate D:/Dev/https-sll/1_www.deceen.com_bundle.crt; ssl_certificate_key D:/Dev/https-sll/2_www.deceen.com.key; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_ciphers HIGH:!aNULL:!MD5:!DH; #設置長連接 keepalive_timeout 70; #減少點擊劫持 add_header X-Frame-Options DENY; #禁止服務器自動解析資源類型 add_header X-Content-Type-Options nosniff; #防XSS攻擊 add_header X-Xss-Protection 1; # 代碼的根目錄 root html; # 默認index index /video/webrtc/index.html; # 訪問日志 #access_log /home/wwwlogs/example.com.log main; # 文件的規則(詳見http://seanlook.com/2015/05/17/nginx-location-rewrite/index.html) location / { try_files $uri $uri/ /index.php$is_args$args; } location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$ { expires 30d; } location ~ .*\.(js|css)?$ { expires 12h; } }