惯例 ,WebRTC是什么####
就是 Web browsers with Real-Time Communications 啊
它是Google开源的啊,不要钱啊!
为什么要用WebRTC?####
就是老板叫看一下的啊
它是Google开源的啊,不要钱啊!
就是这样,不要在意太多细节 —— 百里潋長
目标####
在内网里使用iOS实现视频通话
有了目标就很容易知道需要些什么
服务器######
WebRTC使用libjingle进行穿透,p2p传输数据,连接过程为
尝试直连……
尝试使用Stun Service进行穿透……
尝试使用Turn Service进行中转……
这里只进行内网视频通话,就不需要准备Stun以及Turn服务器了。但即便如此,在交换通信的元数据(信令)的时候依然需要一个服务器,并且WebRTC并没有统一实现 —— 我们还需要简单地实现一个信令服务器来支持这个Demo。
我使用Java搭建信令服务器,WebSocket是一个用来做WebRTC信令的好方式,今后加上Web一起视频也是轻松加愉快
iOS######
- Safari显然还没有支持WebRTC的视频通话,只能使用代码来实现,在这里使用
libjingle_peerconnection
库(传送门)来实现WebRTC通话 - iOS上使用WebSocket有一个非常不错的轮子
SocketRocket
(传送门) - 其他常用库
AFNetworking
、ReactiveCocoa
、Mantle
、SVProgressHUD
、Masonry
我们不生产代码,我们只是Github的搬运工 —— 农夫三拳,有点疼……
从信令服务开始####
JavaEE 7中出了JSR-356:Java API for WebSocket规范。我们需要使用JDK 7 以上版本,以Tomcat作为Web容器需要Tomcat 7.0.47 以上版本即可。
Java WebSocket只是跑起来不可谓不简单,我们新建一个Java Web项目名字就随便叫做VideoMeeting
好了。再随便创建一个SocketAction
类来完成信令转发,只需要简单地标注@ServerEndpoint (访问地址)
,@OnOpen (新建连接)
,@OnClose (断开连接)
,@OnMessage (接受到消息)
这几个注解就可以完成工作
package com.hsq.videomeeting.socketservice;
import java.lang.reflect.Method;
import javax.websocket.CloseReason;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
@ServerEndpoint("/socket/{userId}")
public class SocketAction {
private Session session;
private int userId;
@OnOpen
public void onOpen(Session session, @PathParam(value="userId")int userId) {
//注意 @ServerEndpoint 标注时声明了可以传入一个userId字段,此处再次声明并接收,作为用户及Session唯一标识
this.userId = userId;
this.session = session;
//这是另一个随便写的处理类,将Session保存,必要时通过userid可以查找到并处理一些逻辑,此处省略,在OnMessage处有说明省了哪些逻辑的
SocketService.addAction(this);
System.out.println("打开连接:"+userId);
}
@OnClose
public void onClose(CloseReason c) {
SocketService.removeAction(this);
System.out.println("关闭连接:"+userId);
}
@OnError
public void onError(Throwable t) {
}
@SuppressWarnings("unchecked")
@OnMessage
public void onMessage(String message) {
String decodeString = null;
try {
decodeString = new String(decodeBase64(message),"utf-8");
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("消息 "+decodeString);
//此处省略一点代码哟~
//我传递的message是一个经过base64编码的JSON串,此处需要:
//1、解析收到的message串,获取发送者id,接受者id以及消息主体内容
//2、如果内容是offer/anwser/Candidate,则通过接受者id查找到其Session,使用send转发出去(offer什么的在后面客户端解释)
//3、还有其他诸如接听、挂断、拒绝、正在通话中等等就自己脑洞啦
}
public boolean send(String jsonMessage){
try {
String base64JSON = encodeBase64(jsonMessage.getBytes("utf-8"));
session.getBasicRemote().sendText(base64JSON);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/***
* 编码
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
public static String encodeBase64(byte[] input) throws Exception {
Class clazz=Class.forName("com.sun.org.apache.xerces.internal.impl.dv.util.Base64");
Method mainMethod= clazz.getMethod("encode", byte[].class);
mainMethod.setAccessible(true);
Object retObj=mainMethod.invoke(null, new Object[]{input});
return (String)retObj;
}
/***
* 解码
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
public static byte[] decodeBase64(String input) throws Exception {
Class clazz=Class.forName("com.sun.org.apache.xerces.internal.impl.dv.util.Base64");
Method mainMethod= clazz.getMethod("decode", String.class);
mainMethod.setAccessible(true);
Object retObj=mainMethod.invoke(null, input);
return (byte[])retObj;
}
public Session getSession() {
return session;
}
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + userId;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
SocketAction other = (SocketAction) obj;
if (userId != other.userId)
return false;
return true;
}
}
我们把它发布到Tomcat中运行起来,在iOS中尝试使用SocketRocket
进行连接
NSString * const WEBSOCKET_ADDRESS = @"ws://192.168.168.41:9999/VideoMeeting/socket/";
NSString *urlStr = [NSString stringWithFormat:@"%@%d", WEBSOCKET_ADDRESS,[User user].userId];
SRWebSocket *socket = [[SRWebSocket alloc] initWithURL:[NSURL URLWithString:urlStr]];
socket.delegate = self;
[socket open];
这时就能在SRWebSocket的Delegate回调函数中获取到消息
////Delegate - WebSocket正常创建连接
- (void)webSocketDidOpen:(SRWebSocket *)webSocket {
NSLog(@"连接到信令服务器啦");
}
////Delegate - 接受消息的函数
- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(NSString *)message {
NSLog(@"接受到消息啦:%@",message);
}
有了信令服务器,不过还有关于用户体系和好友关系链之类的接口,不过不是视频必须并且与WebRTC无关,就不啰嗦了,接下来就开始着手敲iOS端的代码了啊喂