6. webRTC
webrtc网上封装的很多,demo很多都是一个页面里实现的,今天实现了个完整的 , A 发视频给 B。
1.) A 方
<!DOCTYPE html> <html id="home" lang="en"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <style> p { padding: 1em; } li { border-bottom: 1px solid rgb(189, 189, 189); border-left: 1px solid rgb(189, 189, 189); padding: .5em; } </style> </head> <body> <script> var mediaConstraints = { optional: [], mandatory: { OfferToReceiveAudio: false, OfferToReceiveVideo: true } }; </script> <script> var offerer ,answererWin window.RTCPeerConnection = window.mozRTCPeerConnection || window.webkitRTCPeerConnection; window.RTCSessionDescription = window.mozRTCSessionDescription || window.RTCSessionDescription; window.RTCIceCandidate = window.mozRTCIceCandidate || window.RTCIceCandidate; navigator.getUserMedia = navigator.mozGetUserMedia || navigator.webkitGetUserMedia; window.URL = window.webkitURL || window.URL; window.iceServers = { iceServers: [{ url: 'stun:23.21.150.121' } ] }; </script> <script> /* offerer */ function offererPeer(video_stream) { offerer = new RTCPeerConnection(window.iceServers) offerer.addStream(video_stream) offerer.onaddstream = function (event) { // 本地显示video } offerer.onicecandidate = function (event) { if (!event || !event.candidate) return sendToP2({ 'action' : 'candidate', 'candidate' :event.candidate }) } offerer.createOffer(function (offer) { offerer.setLocalDescription(offer) sendToP2({ 'action' : 'create', 'offer':offer }) }, function() {}, mediaConstraints) } </script> <script> var video_constraints = { mandatory: {}, optional: [] } function getUserMedia(callback) { var n = navigator n.getMedia = n.webkitGetUserMedia || n.mozGetUserMedia n.getMedia({ audio: false, video: video_constraints }, callback, onerror) function onerror(e) { alert(JSON.stringify(e, null, '\t')) } } </script> <script> function sendToP2(data){ answererWin.postMessage(JSON.stringify(data) ,window.location) } function receiveMessage(data){ data = JSON.parse(data.data) switch ( data.action) { case 'answer' : offerer.setRemoteDescription(new RTCSessionDescription(data.answer)) break case "candidate": offerer.addIceCandidate(new RTCIceCandidate(data.candidate)) break } console.log('msg' ,data) } window.addEventListener("message", receiveMessage, false) answererWin = window.open('answer.html' ,'t') getUserMedia(function (video_stream) { offererPeer(video_stream) }); </script> </body> </html>
2.) B
<!DOCTYPE html> <html id="home" lang="en"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> </head> <body> <article> <div style="text-align:center;"> <div class="videos-container"> <video id="peer1-to-peer2" autoplay controls></video> <h2>Offerer-to-Answerer</h2> <h2>此页面刷新之后,必须重新刷新一下Offer页面</h2> </div> </div> <script> var mediaConstraints = { optional: [], mandatory: { OfferToReceiveAudio: true, OfferToReceiveVideo: true } }; </script> <script> var offerer, answerer; var offererToAnswerer = document.getElementById('peer1-to-peer2'); window.RTCPeerConnection = window.mozRTCPeerConnection || window.webkitRTCPeerConnection; window.RTCSessionDescription = window.mozRTCSessionDescription || window.RTCSessionDescription; window.RTCIceCandidate = window.mozRTCIceCandidate || window.RTCIceCandidate; navigator.getUserMedia = navigator.mozGetUserMedia || navigator.webkitGetUserMedia; window.URL = window.webkitURL || window.URL; window.iceServers = { iceServers: [{ url: 'stun:23.21.150.121' } ] }; </script> <script> /* answerer */ function answererPeer(offer, video_stream) { answerer = new RTCPeerConnection(window.iceServers); // answerer.addStream(video_stream); answerer.onaddstream = function (event) { offererToAnswerer.src = URL.createObjectURL(event.stream); offererToAnswerer.play(); }; answerer.onicecandidate = function (event) { if (!event || !event.candidate) return; sendToP1({ 'action' : 'candidate', 'candidate' :event.candidate }) //offerer.addIceCandidate(event.candidate); }; answerer.setRemoteDescription(new RTCSessionDescription(offer)); answerer.createAnswer(function (answer) { answerer.setLocalDescription(answer); sendToP1({ 'action' : 'answer' , 'answer' : answer }) //offerer.setRemoteDescription(answer); }, function() {}, mediaConstraints); } function receiveMessage(data){ data = JSON.parse(data.data) console.log(data) switch(data.action){ case "create": answererPeer(data.offer , data.stream) break case "candidate": answerer.addIceCandidate(new RTCIceCandidate(data.candidate)) break } } window.addEventListener("message", receiveMessage, false) function sendToP1(data) { opener.postMessage(JSON.stringify(data) , window.location) } </script> </article> </body> </html>
demo用 postMessage传递数据, 业务使用可以用websocket
A 先 createOffer ,生成的offer 供自己setLocalDescription ,并发给B
B 拿A的offer ,setRemoteDescription(offer) , 然后 createAnswer ,生成的answer 供自己setLocalDescription ,并发给A
A 拿B的answer 设置 setRemoteDescription(answer)
A onicecandidate 事件被触发 将得到的通道发给B
B addIceCandidate(new RTCIceCandidate(candidate)) 建立通道
B onicecandidate 事件被触发 将得到的通道发给A
A addIceCandidate(new RTCIceCandidate(candidate)) 建立通道
通道建立后视频就可以共享了
使用时,打开:offer.html 即可。