WebRtc使用时需要先搭建两个服务器一个是信令服务器,一个是ICE服务器,搭建方法请见此博客
https://blog.csdn.net/u011077027/article/details/86225524
1,每个客户端到服务器的链路路径称之为SDP
2,WebRTC拿到每个客户端的SDP后,通过webRtc的算法算出每个客户端之间的最短路径,从而让他自己实现p2p打洞连接
3,首先要搭建服务器端,一个是信令服务器,也就是房间服务器,用于SDP交换;一个是ICE服务器,也就是打洞服务器,交换内网最近的路由
4,可以在客户端也就是android端搭建一个WebSocketManager,用于房间服务器的连接,另一个是PeerConnectionManager,用于打洞服务器连接,再用一个WebRtcManager用于管理它两
5,在WebRtc中写一个connect方法用于链接房间服务器,调用WebSocketManager的connect方法,调用时传递socket地址进去
6,在WebSocketManager的connect方法,通过new WebSocketClient(uri)实例化连接客户端,并在onOpen方法中去做连接好后的一些事情,比如跳转到视频通话页面
mWebSocketClient = new WebSocketClient(uri) {
@Override
public void onOpen(ServerHandshake handshakedata) {
//连接好房间服务器后做的事情,跳转到聊天页面,加入房间服务器
}
}
7,在WebSocketManager方法中写一个joinRoom的方法
public void joinRoom(String roomId) {
//1,组装一个地址ID信息的json
//发送出去
mWebSocketClient.send();
}
8,服务器返回的几种参数的意思
_join //加入房间服务器
_peers //房间服务器加入成功
_now_peer //多了一个人
_offer //主动发起,请求视频通话
_ice_candidate //SDP交换
_answer //被叫方 同意或拒绝请求
9,在WebSocket的构造方法中初始化PeerConnectionManager,当发起连接后得到服务器同意连接响应后,
我们调用PeerConnectionManager的joinToRoom的方法
public void joinToRoom(WebSocketManager javaWebSocket, boolean isVideoEnable, String myID) {
//建立本地预览,建立线程池
executor.execute(new Runnable() {
@Override
public void run() {
//创建工厂
}
})
}
10,使用WebRTC自带库来处理音频源和视频源
private PeerConnectionFactory creatConnectionFactory() {
//视频编码 视频解码
//音频编码 音频解码
VideoEncoderFactory encodeFactory = new DefaultVideoEncoderFactory(
rootEglBase.getEglBaseContext(), true, true);
VideoDecoderFactory decoderFactory = new DefaultVideoDecoferFactory(
rootFglBase.getEglBaseContext());
//初始化
peerConnectionFactory.initialize(PeerConnection, InitializationOption.builder(context).
creatInitializationOptions());
//设置参数
PeerConnectionFactory.option options = new PeerConnectionFactory.Options();
PeerConnectionFactory peerConnectionFactory = PeerConnectionFactory.builder().setOptions(options).
setAudioPericeModule(JavaAudioDeviceModule.builder(context).creatAudioDeviceMoudle().
setVideoDecoderFactory(decoderFactory).
setViodeEncoderFactory(encoderFactory).creatPeerConnectionFactory());
return peerConnectionFactory;
}
11, 在run方法内主要做这么几件事
//webrtc 链接工厂
//添加视频流 音频流
//完成传输层的业务,比方处理有人进来了,出去了之类的
//给每个人发送链接请求
//p2p链接
12,run方法实现
//创建工厂
factory = creatConnectionFactory();
//添加一个总流
//音频是数据源,创建一个音频轨道,新建一个类来管理音频创建
13,首先创建一个MediaConstras,这是一个集合,每个集合对应的是key value
private MediaConstraints creatAudioConstraints() {
MediaConstraints audioConstraints = new MediaConstraints();
//设置音频各种参数
audioConstrains.mandatory.add(new MediaConstraints.keyValuePair(/*四种参数*/,true));
//四种参数
//googEchoCancelLation 回音消除
private static final String AUDIO_ECHO_CANCELLATION_CONSTRATNT = "googEchoCancelLation";
//googNoiseSuppression 噪音抑制
AUDIO_NOISE_SUPPRESSION_CONSTRAINT
//googAutoGainControl 自动增益控制
AUDIO_AUTO_GAIN_CONTROL_CONTRAINT
//googAlighPassFilter 高通滤波器
AUDIO_HIGH_PASS_FILTER_CONTRAINT
}
14, 再回到run方法
//添加一个总流
AudioSource audioSource = factory.createAudioSource(creatAudioConstraints());
//音频是数据源,创建一个音频轨道
AudioTrack audioTrack = factory.creatAudioTrack("ARDAMSa0", audioSource);
//音频轨道创建成功,成功设置音频轨道的数据源
mediaStream.addTrack(audioTrack);
15, 创建视频源
//创建视频源,也就是Camera
VideoCapturer videoCapture;
//判断一下手机是否支持Camera2
if (Camera2Enumrator.isSupported(context)) {
//Camera2 大于21就支持
Camera2Enumerator enumerator = new Camera2Enumerator(context);
//前置摄像头的捕获,没有就直接用后置
videoCapture = createCameraCapture(enumerator);
} else {
Camera1Enumrator enumerator = new Camera1Enumerator(context);
videoCapture = createCameraCapture(enumerator);
}
16,创建视频轨
//数据源
VideoSource videoSource = factory.createVideoSource(videoCapturer.isScreencast());
//如果用户打开视频轨道
if (videoEnable) {
//视频源,摄像投捕获设备
VideoCapturer viodeoCapturer = creatVideoCapture();
//创建预览surface
SurfaceTextureHelper surfaceTextureHelp = SurfaceTextureHelper,creat("catureThread",
rootEglBase.getEglBaseContext());
//摄像头的数据和videoSource进行绑定
viodeoCapturer.initialize(surfaceTextureHelp, context, videoSource.getCaptureObserver())
//设置预览窗口大小
viodeoCapturer.startCapture(320, 240, 10);
//videoSource和videoTrack进行关联
VideoTrack videoTrack = factory.createVideoTrack("ARDAMSv0", videoSource);
//添加视频轨
mediaStream.addTrack(videoTrack);
}
17,在ChatRoomActivity中新建方法onSetLocalStream
public void onSetLoacalStream(MediaStream stream, String useID) {
//总流 音频流 视频流
if (stream.videoTracks.size() > 0) {
localVideoTrack = stream.videoTracks.get(0);
}
}
18, //如果有多人聊天要动态变化添加SurfaceView
runOnUiThread(new Runnable() {
@Override
public void run() {
addView(userID, stream);
}
});
19, 新建一个方法addView专门处理添加SurfaceView逻辑
private void addView(String useID, MediaStream stream) {
//不用surfaceView 用webrtc给我们提供的surfaceRender
SurfaceViewRenderer surfaceViewRenderer = new SurfaceViewRenderer(this);
//初始化SurfaceViewRenderer
surfaceViewRenderer.init(rootEglBase.getEglBaseContext(), null);
//设置缩放模式
surfaceViewRenderer.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FIT);
//设置屏幕
surfaceViewRenderer.setMirror(true);
... ...
}
20, //关联数据源
if (stream.videoTrack.size()>0) {
stream.videoTrack.get(0).addsink(surfaceViewRenderer);
}
21, //计算布局
videoViews.put(userID, surfaceViewRenderer);
persons.add(userID);
int size = videoView.Size();
for (int i = 0; i < size; i++) {
//设置surfaceViewRenderer的layoutParams
render1.setLayoutParams(layoutParams);
}
22, 当我们加入到一个有人的房间,到WebSocketManager的handleJoinRoom(Map map)中,创建peerConnection
PeerConnection.RTCConfiguration rtcConfiguration = new PeerConnection.RTCConfiguration(ICEServers);
return factory.creatPeerConnection(rtcConfiguration, new ICEObserver());
23, 新建一个ICEObserver
private class ICEObserver implements PeerConnection.Observer{
@Override
public void onSignalingChange(PeerConnection.SignalingState){
//当网络发生变换会调用此方法
}
}
24,这个链接的每个人都会创建一个PeerConnection
connectionPeerPic.put(str, peer);
25,这是建立了一个链接,但是是一个空的链接,没有初始化
creatPeerConnections();
//给房间服务器的其他人发送一个offer
creatOffer();
26, 我们只需在本地做这两件事就好,其他的WebRTC会帮我们完成
//设置本地SDP
pc.setLoacalDescription();
//设置远端的SDP
pc.setRemoteDescription();
26,接下来进行两个比较重要的环节,一个是进行SDP交换,一个是ICE交换,也就是打洞
1,首先offer
方通过new RTCPeerConnection(config)
建立PeerConnection
2, offer
方通过creatOffer
生成sessionDescription
,设置localDescription
,并通过信令服务器发送给Answer
方
3,Answer
方收到offer
,发现并没有与之对应的peerConnection
,新建peerConnection
,并设置remoteDecription
4,Answer
方通过creatAnswer
生成sessionDescription
,设置localDescription
,并通过信令服务器发送answer
5, offer
方收到answer
,设置remoteDescription
6,SDP交换结束
ICE交换也就是打洞的过程,它是指P2P直接通信;使用STUN
服务器实现突破NAT
的P2P通信;使TURN
中继服务器实现突破防火墙的中继通信。
1,双方在new RTCPeerConnection(config)
建立连接后,当网络候选者可用时,会触发icecandidate
事件
2,在onIcecandidtate
时间处理程序中,将Candidate
通过信令服务器发送给对方
3,双方在接受彼此的candidate
后,通过addIceCandidate
将对方的candidate
加入到PeerConnection
实例中
结束:
在连接建立前或连接后调用peerConnection.addStream()
;
方法将本地视频/音频数据流加入到connection
中;
当对方接受到视频流的时候会触发addStream
事件,使其处理程序中我们可以接受数据流将其显示。