We have introduced WebRTC and read the example of https://apprtc.appspot.com/on the Internet (which may need to be accessed through a wall). This example is an application deployed on the Google App Engine and relies on the Gae environment, the background language is Python and also relies on the Google App Engine channel API. Therefore, it cannot be run locally or be expanded. After studying the Python source code in the example, I decided to implement it in Java. After Tomcat 7, I started to support websocket and planned to use websocket instead of Google.
The App Engine channel API implements frontend and backend communication. In this example, Java + websocket serves as the communication between clients and video transmission. Video Transmission relies on WebRTC. The instance features:
- HTML5
- No plug-ins required
- Resource usage is not very large, and the cost of the server is relatively small. As long as the client establishes a connection, video transmission is completely completed by a browser.
- Implemented through Js. Theoretically, WebRTC can run as long as the browser supports websocket (currently only passed chrome testing, chrome version 24.0.1312.2 Dev-m)
For front-end JS Code and objects used, visit http://www.html5rocks.com/en/tutorials/webrtc/basics/#for detailed code introduction. Here I will only introduce the changes I have made. First, I will establish a connection for the client to obtain the status in real time. In the Gae example, it is implemented through the Gae channel API, here I use websocket to implement the Code:
function openChannel() {console.log("Opening channel.");socket = new WebSocket("ws://192.168.1.102:8080/RTCApp/websocket?u=${user}");socket.onopen = onChannelOpened;socket.onmessage = onChannelMessage;socket.onclose = onChannelClosed;}
Create a websocket connection and register related events. Here we use Java to implement websocket connection:
package org.rtc.servlet;import java.io.IOException;import javax.servlet.ServletException;import javax.servlet.annotation.WebServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import org.apache.catalina.websocket.StreamInbound;import org.apache.catalina.websocket.WebSocketServlet;import org.rtc.websocket.WebRTCMessageInbound;@WebServlet(urlPatterns = { "/websocket"})public class WebRTCWebSocketServlet extends WebSocketServlet {private static final long serialVersionUID = 1L;private String user;public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {this.user = request.getParameter("u");super.doGet(request, response);} @Override protected StreamInbound createWebSocketInbound(String subProtocol) { return new WebRTCMessageInbound(user); }}
If you want to implement websocket, you must use Tomcat 7 or later, and introduce: Catalina. jar, tomcat-coyote.jar two jar packages, after deployment to Tomcat 7 Have To Go To webapps/application to delete the two ar packages or else can not start, the biggest difference between websocket access and normal access is that websocketservlet is inherited. For more information about websocket, visit http://redstarofsleep.iteye.com/blog/1488639. Let's take a look at the implementation of the webrtcmessageinbound class:
Package Org. RTC. websocket; import Java. io. ioexception; import Java. NIO. bytebuffer; import Java. NIO. charbuffer; import Org. apache. catalina. websocket. messageinbound; import Org. apache. catalina. websocket. wsoutbound; public class webrtcmessageinbound extends messageinbound {private final string user; Public webrtcmessageinbound (string user) {This. user = user;} Public String getuser () {return this. user ;}@ override protected void onopen (wsoutbound outbound) {// triggers a connection event and adds a connection webrtcmessageinboundpool to the connection pool. addmessageinbound (this) ;}@ override protected void onclose (INT status) {// triggers the Close event and removes the webrtcmessageinboundpool connection in the connection pool. removemessageinbound (this) ;}@ override protected void onbinarymessage (bytebuffer message) throws ioexception {Throw new unsupportedoperationexception ("binary message not supported. ") ;}@ override protected void ontextmessage (charbuffer message) throws ioexception {}}
Webrtcmessageinbound inherits messageinbound and binds two events. The key is the connection event, which stores the connection in the connection pool, when Client A initiates a message sending, it extracts the connection from client B and sends the data. Let's look at the webrtcmessageinboundpool class:
Package Org. RTC. websocket; import Java. io. ioexception; import Java. NIO. charbuffer; import Java. util. hashmap; import Java. util. map; public class implements {Private Static final map <string, webrtcmessageinbound> connections = new hashmap <string, webrtcmessageinbound> (); public static void addmessageinbound (webrtcmessageinbound) {// Add a connection to system. out. println ("User:" + inbound. getuser () + "Join .. "); connections. put (inbound. getuser (), inbound);} public static void removemessageinbound (webrtcmessageinbound inbound) {// Remove Connection connections. remove (inbound. getuser ();} public static void sendmessage (string user, string message) {try {// send data to a specific user system. out. println ("send message to user:" + User + "message content:" + message); webrtcmessageinbound inbound = connections. get (User); If (inbound! = NULL) {inbound. getwsoutbound (). writetextmessage (charbuffer. Wrap (Message) ;}} catch (ioexception e) {e. printstacktrace ();}}}
The most important thing in the webrtcmessageinboundpool class is the sendmessage method, which sends data to a specific user. Let's take a look at this Code:
function openChannel() {console.log("Opening channel.");socket = new WebSocket("ws://192.168.1.102:8080/RTCApp/websocket?u=${user}");socket.onopen = onChannelOpened;socket.onmessage = onChannelMessage;socket.onclose = onChannelClosed;}
How did $ {user} come from? In fact, there is a segment for processing before entering this page:
Package Org. RTC. servlet; import Java. io. ioexception; import Java. util. UUID; import javax. servlet. servletexception; import javax. servlet. annotation. webservlet; import javax. servlet. HTTP. httpservlet; import javax. servlet. HTTP. httpservletrequest; import javax. servlet. HTTP. httpservletresponse; import Org. apache. commons. lang. stringutils; import Org. RTC. room. webrtcroommanager; @ webservlet (urlpatterns = {"/room"}) P Ublic class webrtcroomservlet extends httpservlet {Private Static final long serialversionuid = 1l; Public void doget (httpservletrequest request, httpservletresponse response) throws servletexception, ioexception {This. dopost (request, response);} public void dopost (httpservletrequest request, httpservletresponse response) throws servletexception, ioexception {string r = request. getparameter ("R"); If (St Ringutils. isempty (R) {// if the room is empty, a new room number r = string is generated. valueof (system. currenttimemillis (); response. sendredirect ("room? R = "+ r);} else {INTEGER initiator = 1; string user = UUID. randomuuid (). tostring (). replace ("-", ""); // generate a user ID string if (! Webrtcroommanager. haveuser (R) {// no one may be logged in for the first time, so you have to wait for the connection, if someone enters the page with this room, the video connection initiator = 0 will be initiated; // if there is no room, no connection request will be sent} webrtcroommanager. adduser (R, user); // Add a user string basepath = request to the room. getscheme () + ": //" + request. getservername () + ":" + request. getserverport () + request. getcontextpath () + "/"; string roomlink = basepath + "room? R = "+ R; string roomkey = r; // set the request variable. setattribute ("initiator", initiator); Request. setattribute ("roomlink", roomlink); Request. setattribute ("roomkey", roomkey); Request. setattribute ("user", user); Request. getrequestdispatcher ("index. JSP "). forward (request, response );}}}
This is the process before entering the room. But how does the client initiate a video call?
Function initialize () {console. log ("Initializing; room =$ {roomkey }. "); card = document. getelementbyid ("card"); localvideo = document. getelementbyid ("localvideo"); minivideo = document. getelementbyid ("minivideo"); remotevideo = document. getelementbyid ("remotevideo"); resetstatus (); openchannel (); getusermedia () ;}function getusermedia () {try {navigator. webkitgetusermedia ({'audio': True, 'video': true}, onus Ermediasuccess, onusermediaerror); console. log ("requested access to local media with new syntax. ");} catch (e) {try {navigator. webkitgetusermedia ("video, audio", onusermediasuccess, onusermediaerror); console. log ("requested access to local media with old syntax. ");} catch (e) {alert (" webkitgetusermedia () failed. is the mediastream flag enabled in about: Flags? "); Console. log ("webkitgetusermedia failed with exception:" + E. message) ;}} function onusermediasuccess (Stream) {console. log ("user has granted access to local media. "); var url = webkiturl. createobjecturl (Stream); localvideo. style. opacity = 1; localvideo. src = URL; localstream = stream; // caller creates peerconnection. if (initiator) maybestart ();} function maybestart () {If (! Started & localstream & channelready) {setstatus ("Connecting... "); console. log ("creating peerconnection. "); createpeerconnection (); console. log ("adding local stream. "); PC. addstream (localstream); started = true; // caller initiates offer to peer. if (initiator) docall () ;}} function docall () {console. log ("sending offer to peer. "); If (isrtcpeerconnection) {PC. createoffer (setlocalandsendmessage, null, mediac Onstraints);} else {var offer = pc. createoffer (mediaconstraints); PC. setlocaldescription (PC. sdp_offer, offer); sendmessage ({type: 'offer ', SDP: offer. tosdp ()}); PC. startice () ;}} function setlocalandsendmessage (sessiondescription) {PC. setlocaldescription (sessiondescription); sendmessage (sessiondescription);} function sendmessage (Message) {var msgstring = JSON. stringify (Message); console. log ('send message: '+ m Sgstring); Path = 'message? R =$ {roomkey} '+' & U =$ {user} '; var xhr = new XMLHttpRequest (); xhr. open ('post', path, true); xhr. send (msgstring );}
After the page is loaded, the initialize method is called. The getusermedia method is called in the initialize method. This method obtains the video from the local camera and sends a connection request after obtaining the video, establish a connection pipeline on the client, and send a connection request to another client through sendmessage. The parameter is the room number of the current call and the current login user, which is the log generated by the connection:
Package Org. RTC. servlet; import Java. io. bufferedreader; import Java. io. ioexception; import Java. io. inputstreamreader; import javax. servlet. servletexception; import javax. servlet. servletinputstream; import javax. servlet. annotation. webservlet; import javax. servlet. HTTP. httpservlet; import javax. servlet. HTTP. httpservletrequest; import javax. servlet. HTTP. httpservletresponse; import net. SF. JSON. jsonobject; import Org. RTC. room. webrtcroommanager; import Org. RTC. websocket. response; @ webservlet (urlpatterns = {"/message"}) public class extends httpservlet {Private Static final long serialversionuid = 1l; Public void doget (httpservletrequest request, httpservletresponse response) throws servletexception, ioexception {super. dopost (request, response);} public void dopost (httpservletr Equest request, httpservletresponse response) throws servletexception, ioexception {string r = request. getparameter ("R"); // room number string u = request. getparameter ("U"); // caller bufferedreader BR = new bufferedreader (New inputstreamreader (servletinputstream) request. getinputstream (); string line = NULL; stringbuilder sb = new stringbuilder (); While (line = BR. readline ())! = NULL) {sb. append (line); // gets the input stream, mainly the video location information} string message = sb. tostring (); jsonobject JSON = jsonobject. fromobject (Message); If (JSON! = NULL) {string type = JSON. getstring ("type"); If ("bye ". equals (type) {// exit the video chat system from the client. out. println ("User:" + u + "exit .. "); webrtcroommanager. removeuser (R, u) ;}} string otheruser = webrtcroommanager. getotheruser (R, U); // get the call object if (U. equals (otheruser) {message = message. replace ("\" offer \ "", "\" Answer \ ""); message = message. replace ("A = crypto: 0 aes_cm_128_hmac_sha1_32", "A = xrypto: 0 aes_cm_128_hmac_sha1_32"); message = message. replace ("A = ice-options: Google-ice \ r \ n", "");} // send the connection data webrtcmessageinboundpool to the other party. sendmessage (otheruser, message );}}
In this way, the client sends connection data to the client through websokcet, and then receives the Video Based on the received data:
Function onchannelmessage (Message) {console. log ('Received message: '+ message. data); If (isrtcpeerconnection) processsignalingmessage (message. data); // create a video connection elseprocesssignalingmessage00 (message. data);} function processsignalingmessage (Message) {var MSG = JSON. parse (Message); If (MSG. type = 'offer ') {// callee creates peerconnectionif (! Initiator &&! Started) maybestart (); // we only know jsep version after createpeerconnection (). if (isrtcpeerconnection) PC. setremotedescription (New rtcsessiondescription (MSG); elsepc. setremotedescription (PC. sdp_offer, new sessiondescription (MSG. SDP); doanswer ();} else if (MSG. type = 'answer' & started) {PC. setremotedescription (New rtcsessiondescription (MSG);} else if (MSG. type = 'canonicaldate' & started) {var candidate = new rtcicecandidate ({sdpmlineindex: MSG. label, Candidate: MSG. candidate}); PC. addicecandidate (candidate);} else if (MSG. type === 'bye' & started) {onremotehangup ();}}
In this way, video calls on browsers are implemented through Java, websocket, and WebRTC.
I have another question. The websocket expiration time I have defined is 20 seconds, which is too short. I hope you will advise how to set the expiration time of websocket.
Can you enter the demo address with your friends? Extra = devchannel & platform = Win
Source code download http://download.csdn.net/detail/leecho571/5117399
We recommend that you use the Chrome browser for testing. You can join the group: 197331959.