工作中遇到要视频直播的需求,前提是不能依赖Flash前端,于是就找到了WebRtc的相关资料.
什么GetUserMedia,RTCPeerConnection,DataChannel我不多说.
简单讲就是谷歌把实时通信层打包进浏览器.而这一套实时通信层又是来源于电信通信领域.
所以浏览器两端交互需要依赖一个叫做信令服务器的东西,来协助两端完成连接.
简单说下流程
以A呼叫B为例
A呼叫B
1.告知Server,我要找B
2.Server查一下有没有B,有就传达,没有就不传达,至于结果需不需要告知A,那全看心情了.反正这东西是自己实现的.WebRTC里就提了一嘴这东西,没具体规范.
3.B得到A的呼叫(准确说叫Offer)
4.B解析Offer,回应(Answer)到Server,Server回给A
5.A收到Answer,解析.
6.这时,A和B就算勾搭上了.剩下的事情就交给他们自己办了.
Offer 和 Answer 都属于SDP.具体规范http://datatracker.ietf.org/doc/draft-nandakumar-rtcweb-sdp/01/
解析这些是交给浏览器做的.
先看Server端的Java实现
package org.rtc.sip; import java.io.IOException; import javax.websocket.OnClose; import javax.websocket.OnError; import javax.websocket.OnMessage; import javax.websocket.OnOpen; import javax.websocket.Session; import javax.websocket.server.ServerEndpoint; import com.google.gson.Gson; @ServerEndpoint(value = "/sipserver") public class SignalWorker { private Gson gson = new Gson(); private Session session; public Session getSession() { return session; } public void setSession(Session session) { this.session = session; } public SignalWorker() { } @OnClose public void onClose(Session session) { } @OnError public void onError(Throwable throwable) { } @OnMessage public void onMessage(String message, Session session) { System.out.println("收到消息:" + message); SIPObject sip = gson.fromJson(message, SIPObject.class); try { if(sip.getAction().equals("login")){ SIPSessionManager.addNewSession(sip.getFrom(), this); }else if(sip.getAction().equals("offer")){ SignalWorker sker=SIPSessionManager.getSignalWorker(sip.getTo()); if(sker!=null){ sker.getSession().getBasicRemote().sendText(message); } else{ miss(); } }else if(sip.getAction().equals("answer")){ SignalWorker sker=SIPSessionManager.getSignalWorker(sip.getTo()); if(sker!=null){ sker.getSession().getBasicRemote().sendText(message); } else{ miss(); } }else if(sip.getAction().equals("candidate")){ SignalWorker sker=SIPSessionManager.getSignalWorker(sip.getTo()); if(sker!=null){ sker.getSession().getBasicRemote().sendText(message); } } } catch (IOException e) { e.printStackTrace(); } } @OnOpen public void onOpen(Session session) { this.session = session; } public void miss(){ SIPObject resp=new SIPObject(); resp.setAction("miss"); try { session.getBasicRemote().sendText(gson.toJson(resp)); } catch (IOException e) { e.printStackTrace(); } } public void send(String msg) { System.out.println(msg); session.getAsyncRemote().sendText(msg); } }
代码逻辑很简单,就是A->B,B->A,
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>WebRtc</title> <script src="rtc_suit/jquery-2.1.3.min.js"></script> <script src="rtc_suit/yuhan_rtc.js"></script> </head> <body> <video id="localVideo" style="width:400px;height:400px;" ></video> <button onclick="Call('Baidu')"> call</button> <br/> <video id="remoteVideo" style="width:400px;height:400px;" ></video> <script> var rtcg=new RTCSuit({id:"Google",to:"Baidu",sipws:"ws://192.168.0.119:8080/WebTest/sipserver"}); rtcg.connectSip(); var rtcb=new RTCSuit({id:"Baidu",to:"Google",sipws:"ws://192.168.0.119:8080/WebTest/sipserver"}); rtcb.connectSip(); rtcb.bindRemoteMedia("remoteVideo"); function Call(to){ rtcg.bindLocalMedia("localVideo",true,true,function(rst,stream){ if(rst){ rtcg.sendOffer(); } }); } //rtc.sendOffer(); </script> </body> </html>
上面HTML代码逻辑也很简单.
两端Baidu和Google先要登陆在SIPServer上.
Google呼叫baidu是在本地完成了摄像头视频获取后发出的Offer.
整个WebRtc交互用一个JS库屏蔽掉了复杂的代码.
代码只简单实现了功能,未完善.贴出来分享.
/** * */ // define override var RTCPeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection; navigator.getUserMedia = navigator.webkitGetUserMedia || navigator.mozGetUserMedia; //ICE SERVER var iceServer = { "iceServers": [{ "url": "stun:stun.l.google.com:19302" }] }; //LOG function log(msg,level){ console.log(new Date().toLocaleString()+" "+msg); } function RTCSuit(opt) { this.sipws=opt.sipws;//信令服务器地址 this.id=opt.id||new Date().getTime();//身份信息 this.to=opt.to; this.conn=null;//websocket对象,connect时初始化 this.sessionId=null; this.peercn=new RTCPeerConnection(opt.ice||iceServer); this.peercn.rtc=this; //init peer this.peercn.onicecandidate = RTCSuit.prototype.onIceCandidate; this.peercn.onconnecting = RTCSuit.prototype.onPeerConnecting; this.peercn.onopen = RTCSuit.prototype.onPeerOpen; this.peercn.onaddstream = RTCSuit.prototype.onRemotePeerStreamAdd; this.peercn.onremovestream = RTCSuit.prototype.onPeerStreamRemove; this.localNode=null; this.remoteNode=null; return this; } //视频媒体交互 RTCSuit.prototype.bindLocalMedia=function(id, enableAudio, enableVideo,callback) { var rtc=this; navigator.getUserMedia({ "audio" : enableAudio, "video" : enableVideo }, function(stream) {//success var videoNode=document.getElementById(id); rtc.localNode=videoNode; videoNode.autoplay=true; videoNode.src= URL.createObjectURL(stream); rtc.peercn.addStream(stream); //触发事件,通知视频流绑定成功 if(typeof(callback)=="function"){ callback(true,stream); } log("bind local media success"); },function(error){//error if(typeof(callback)=="function"){ callback(false,null); } }); }; RTCSuit.prototype.bindRemoteMedia=function(id){ var videoNode=document.getElementById(id); videoNode.autoplay=true; this.remoteNode=videoNode; }; //PEER交互 RTCSuit.prototype.sendOffer=function(){ var rtc=this; if(!rtc.conn.readyState){ setTimeout(function(){rtc.sendOffer()},300);return; } this.peercn.createOffer(function(desc){//success rtc.peercn.setLocalDescription(desc); rtc.send({action:"offer",sdpWrap:desc}); },function(error){ rtc.onSendOfferError(error); }); }; RTCSuit.prototype.sendAnswer=function(){ var rtc=this; if(!rtc.conn.readyState){ setTimeout(function(){rtc.sendOffer()},300);return; } this.peercn.createAnswer(function(desc){//success rtc.peercn.setLocalDescription(desc); rtc.send({action:"answer",sdpWrap:desc}); },function(error){ }); } RTCSuit.prototype.onAddStreamToPeer=function(){ log("get here"); }; RTCSuit.prototype.onIceCandidate=function(event){ var rtc=this.rtc; if (event.candidate) { rtc.send({action:"candidate",sdpWrap:{ type : "candidate", label : event.candidate.sdpMLineIndex, id : event.candidate.sdpMid, candidate : event.candidate.candidate }}); } else { console.log("End of candidates."); } }; RTCSuit.prototype.onPeerConnecting=function(){ log(" peer connecting"); }; RTCSuit.prototype.onPeerOpen=function(){ log(" peer open"); }; RTCSuit.prototype.onRemotePeerStreamAdd=function(event){ var url = webkitURL.createObjectURL(event.stream); this.rtc.remoteNode.src=url; }; RTCSuit.prototype.onPeerStreamRemove=function(){ }; //信令交互 RTCSuit.prototype.send=function(json){ var rtc=this; json.from=rtc.id; json.to=rtc.to; var smsg=JSON.stringify(json); if(rtc.conn.readyState) rtc.conn.send(smsg); log(rtc.id+" 发送"+smsg); }; RTCSuit.prototype.connectSip=function(sipws,callback){ sipws=sipws||this.sipws; var rtc=this; this.conn=new WebSocket(sipws); this.conn.onopen=function(){ rtc.send({id:rtc.id,action:"login"}); log("sip server connected"); }; //core msg process this.conn.onmessage=function(e){ log(rtc.id+" 收到消息:"+e.data); var msg=e.data; var json=eval("("+msg+")"); if(typeof(rtc["on"+json.action]) == "function"){ rtc["on"+json.action](json); }else{ log("error response:"+msg); } }; }; RTCSuit.prototype.onoffer=function(json){ this.peercn.setRemoteDescription(new RTCSessionDescription(json.sdpWrap)); this.sendAnswer(); log("recv offer") }; RTCSuit.prototype.onanswer=function(json){ this.peercn.setRemoteDescription(new RTCSessionDescription(json.sdpWrap)); log("recv answer"); }; RTCSuit.prototype.oncandidate=function(json){ var msg=json.sdpWrap; var candidate = new RTCIceCandidate({ sdpMLineIndex : msg.label, candidate : msg.candidate }); this.peercn.addIceCandidate(candidate); };
最后代码奉上....环境 tomcat8.0 java 1.8
相关推荐
基于WebSocket、WebRTC实现浏览器视频通话, 仅供参考,大家一起讨论学习!
基于Springboot实现的多人视频在线会议服务器前端(webrtc项目)源码+使用说明.7z 【多人视频在线会议-后端源码】https://download.csdn.net/download/DeepLearning_/87317683 视频会议的前端 使用webrtc实现图像传输...
基于webRTC的简单视频会议app源码+项目说明.zip基于webRTC的简单视频会议app源码+项目说明.zip基于webRTC的简单视频会议app源码+项目说明.zip基于webRTC的简单视频会议app源码+项目说明.zip基于webRTC的简单视频会议...
基于webrtc+clmtrackr.js实现的视频录制、人脸检测、活体检测源码+体验地址.zip 基于webrtc+clmtrackr.js实现的视频录制、人脸检测、活体检测源码+体验地址.zip 基于webrtc+clmtrackr.js实现的视频录制、人脸检测、...
基于WebRTC的在线视频会议(源码+项目说明)(高分毕设).zip基于WebRTC的在线视频会议(源码+项目说明)(高分毕设).zip基于WebRTC的在线视频会议(源码+项目说明)(高分毕设).zip基于WebRTC的在线视频会议...
【资源说明】 1、该资源包括项目的全部源码,下载可以直接使用!...基于JavaScript WebRTC实现跨平台音视频通话(源码+项目说明)(实现1对1视频,多人视频,视频直播,视频会议,房间管理,权限管理等).zip
【资源说明】 1、该资源包括项目的全部源码,下载可以直接使用! 2、本项目适合作为计算机、数学、电子信息等专业的课程设计、期末大作业和毕设...基于webrtc的视频会议系统源码.zip基于webrtc的视频会议系统源码.zip
WebRTC学习笔记01-最简单实现一对一视频通讯代码
考虑到是公司内部使用,没有采用第三方收费通讯接口,刚开始尝试过nginx+rtmp,但是视频延迟太高,后来经过查询测试总结,最终使用websocket+webrtc实现,测试延迟最高为0.3s左右,可以供大家参考学习
毕业设计 基于WebRTC的在线视频会议源码+部署文档+全部数据资料(优秀项目).zip毕业设计 基于WebRTC的在线视频会议源码+部署文档+全部数据资料(优秀项目).zip毕业设计 基于WebRTC的在线视频会议源码+部署文档+...
【资源说明】 1、该资源内项目代码都是经过测试运行成功,功能正常的情况下...基于JavaScript WebRTC实现跨平台音视频通话(源码+项目说明)(实现1对1视频,多人视频,视频直播,视频会议,房间管理,权限管理等).zip
android webrtcdemo WebRTC 学习demo 多人视频 多人通话 一对一
【WebRTC】用WebRTC做即时视频聊天应用 * 1 - 为了方便用到了回声Agora的信令服务,所以需要去Agora注册并获得AppID,不需要Token。 * 2 - main.js开头的地方需要贴上你的App-ID。 ```javascript let APP_ID = "YOU...
【资源说明】 1、该资源包括项目的全部源码,下载可以直接使用! 2、本项目适合作为计算机、数学、电子信息等专业的课程设计、期末...基于webrtc+clmtrackr.js实现的视频录制、人脸检测、活体检测(源码+项目说明).zip
基于Vue、fabric、webRTC开发的音视频会议画板(源码+项目说明).zip基于Vue、fabric、webRTC开发的音视频会议画板(源码+项目说明).zip基于Vue、fabric、webRTC开发的音视频会议画板(源码+项目说明).zip基于Vue...
WebRTC开发的视频通话,修改了之前的一个小例子,可以正常运行,后台java的websocket,用于学习交流
通过本课程的学习,学员便可搭建自己的webrtc服务器,实现web、app、微信之间的音视频通话功能,且可应用于实际项目,纯粹的干货学习视频。该视频是作者多年在工作中的实际应用,让学员在理解原理的同时,一步一步带...
毕设&课设&项目&实训-WebRTC实现了基于网页的视频会议,标准是WHATWG 协议,目的是通过浏览器提供简单的javascript就可以达到实时通讯 【项目资源】: 包含前端、后端、移动开发、操作系统、人工智能、物联网、信息...
包含前端、后端、移动开发、操作系统、人工智能、物联网、信息化管理、数据库、硬件开发、大数据、课程资源、音视频、网站开发等各种技术项目的源码。 包括STM32、ESP8266、PHP、QT、Linux、iOS、C++、Java、python...
包含前端、后端、移动开发、操作系统、人工智能、物联网、信息化管理、数据库、硬件开发、大数据、课程资源、音视频、网站开发等各种技术项目的源码。 包括STM32、ESP8266、PHP、QT、Linux、iOS、C++、Java、python...