最近各大直播網站都比較火,想探究一下是怎么玩的。但是看了幾個大牛的回答,感覺有太多陌生的東西,嘗試起來成本略高。發現了有個東西叫webrtc,有人分析過他不適合做流量大,人數多的直播。但是我也只是玩一下,感受一下視頻連通的感覺。
一開始在github上面看到了一個,比較全大部分的API都做了demo,還有canva 3d融合webrtc的例子。因為大部分例子都是沒有經過網絡的通訊,所以想用socket寫一個交換信令的demo。其中也找到過demo但是沒試通過...然后看一些博客和API自己寫一個小demo,幫助理解webrtc
目錄結構就是這樣 只拉取了express socket.io
前端代碼:
html部分
<script src="/socket.io/socket.io.js"></script> Local: <br> <video id="localVideo" autoplay style="width:80px"></video><br> Remote: <br> <video id="remoteVideo" autoplay style="width:80px"></video>
js部分
// 判斷發起端 var isCaller = window.location.href.split('#')[1]; // 信令服務器的Socket連接 var socket = io.connect('http://localhost:3000'); // stun和turn服務器 var iceServer = null; // iceServer在局域網下可以通訊 var pc = new webkitRTCPeerConnection(iceServer); // 發送給的其他端通知 pc.onicecandidate = function(event){ if (event.candidate !== null) { debugger console.log('onicecandidate -----'); socket.emit('message',JSON.stringify({ "event": "_ice_candidate", "data": { "candidate": event.candidate } })); } }; // 建立好p2p通道以后 會通過OnAddStream返回一個音視頻流的對象 pc.onaddstream = function(event){ document.getElementById('remoteVideo').src = URL.createObjectURL(event.stream); }; // offer和answer var sendOfferFn = function(desc){ pc.setLocalDescription(desc); console.log('Offer -----'); socket.emit('message',JSON.stringify(desc)); }, sendAnswerFn = function(desc){ pc.setLocalDescription(desc); console.log('Answer -----'); socket.emit('message',JSON.stringify(desc)); }; // 獲取本地音頻和視頻流 navigator.webkitGetUserMedia({ "audio": true, "video": true }, function(stream){ //在localVideo輸出音視頻 document.getElementById('localVideo').src = URL.createObjectURL(stream); //把本地的媒體流綁定到PeerConnection pc.addStream(stream); //發起端 發送offer if(isCaller){ pc.createOffer(sendOfferFn, function (error) { console.log('Failure callback: ' + error); }); } }, function(error){ console.log('getUserMedia error: ' + error); }); socket.on('message',function(event){//處理socket請求 if(event.type){ console.log('請求以返回--------'+event.type); }else{ console.log('--------') console.log(event); } if(event.type=='offer' && !isCaller){ pc.setRemoteDescription(event) pc.createAnswer(sendAnswerFn, function (error) { console.log('Failure callback: ' + error); }); } if(event.type=='answer' && isCaller){ pc.setRemoteDescription(event) } if(event.event=='_ice_candidate'){ pc.addIceCandidate(new RTCIceCandidate(event.data.candidate)); } })
node部分
var express = require('express'); var app = express() var server = require('http').Server(app); var io = require('socket.io')(server); server.listen(3000); app.use(express.static(__dirname + '/js')); app.get('/', function (req, res) { res.sendfile(__dirname + '/index.html'); }); io.on('connection', function (socket) { socket.on('message',function(data){ var message=JSON.parse(data); if(message.type){ console.log(message.type); }else{ console.log(message.event); } console.log('請求已經廣播出去--------'); socket.broadcast.emit('message',message); }) });
訪問localhost:3000開啟一個端
然后訪問localhost:3000#true (開始進行對localhos:3000進行連接)
下面具體流程轉載 煙雨任平生的
- ClientA首先創建PeerConnection對象,然后打開本地音視頻設備,將音視頻數據封裝成MediaStream添加到PeerConnection中。
- ClientA調用PeerConnection的CreateOffer方法創建一個用於offer的SDP對象,SDP對象中保存當前音視頻的相關參數。ClientA通過PeerConnection的SetLocalDescription方法將該SDP對象保存起來,並通過Signal服務器發送給ClientB。
- ClientB接收到ClientA發送過的offer SDP對象,通過PeerConnection的SetRemoteDescription方法將其保存起來,並調用PeerConnection的CreateAnswer方法創建一個應答的SDP對象,通過PeerConnection的SetLocalDescription的方法保存該應答SDP對象並將它通過Signal服務器發送給ClientA。
- ClientA接收到ClientB發送過來的應答SDP對象,將其通過PeerConnection的SetRemoteDescription方法保存起來。
- 在SDP信息的offer/answer流程中,ClientA和ClientB已經根據SDP信息創建好相應的音頻Channel和視頻Channel並開啟Candidate數據的收集,Candidate數據可以簡單地理解成Client端的IP地址信息(本地IP地址、公網IP地址、Relay服務端分配的地址)。
- 當ClientA收集到Candidate信息后,PeerConnection會通過OnIceCandidate接口給ClientA發送通知,ClientA將收到的Candidate信息通過Signal服務器發送給ClientB,ClientB通過PeerConnection的AddIceCandidate方法保存起來。同樣的操作ClientB對ClientA再來一次。
- 這樣ClientA和ClientB就已經建立了音視頻傳輸的P2P通道,ClientB接收到ClientA傳送過來的音視頻流,會通過PeerConnection的OnAddStream回調接口返回一個標識ClientA端音視頻流的MediaStream對象,在ClientB端渲染出來即可。同樣操作也適應ClientB到ClientA的音視頻流的傳輸。
具體流程可以參考:http://www.cnblogs.com/fangkm/p/4364553.html 他寫的比較細節,清新脫俗