<!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; } }