WebRTC 服务器
- 信令服务器(signaling)
- 转发服务器(TURN)
- 穿透服务器(STUN)
官方提供的实现原理,如下图
集成到 Android 客户端
- 摄像头,录音,SD卡读取权限动态获取
- 在 build.gradle 引入 WebSocket 和 WebRTC
dependencies {
implementation 'org.webrtc:google-webrtc:1.0.28513'
implementation 'org.java-websocket:Java-WebSocket:1.4.0'
implementation 'com.google.code.gson:gson:2.8.5'
}
WebRTC 使用
- 创建 WebSocket
private void connectionWebsocket() {
try {
webSocketClient = new WebSocketClient(URI.create(Constant.URL)) {
@Override
public void onOpen(ServerHandshake handshakedata) {
setText("已连接");
Log.e(TAG, "onOpen == Status == " + handshakedata.getHttpStatus() + " StatusMessage == " + handshakedata.getHttpStatusMessage());
Model model = new Model(Constant.REGISTER, getFromName(), getFrom(), getToName(), getTo());
webSocketClient.send(new Gson().toJson(model));
}
@Override
public void onMessage(String message) {
Log.e(TAG, "onMessage == " + message);
if (!TextUtils.isEmpty(message)) {
Model model = new Gson().fromJson(message, Model.class);
if (model != null) {
String id = model.getId();
if (!TextUtils.isEmpty(id)) {
int isSucceed = model.getIsSucceed();
switch (id) {
case Constant.REGISTER_RESPONSE:
if (isSucceed == Constant.RESPONSE_SUCCEED) {
Message msg = new Message();
msg.obj = Constant.OPEN;
handler.sendMessage(msg);
Log.e(TAG, "连接成功");
} else if (isSucceed == Constant.RESPONSE_FAILURE) {
Log.e(TAG, "注册失败,已经注册");
}
break;
case Constant.CALL_RESPONSE:
if (isSucceed == Constant.RESPONSE_SUCCEED) {
Log.e(TAG, "对方在线,创建sdp offer");
createOffer();
} else if (isSucceed == Constant.RESPONSE_FAILURE) {
Log.e(TAG, "对方不在线,连接失败");
}
break;
case Constant.INCALL:
isIncall();
break;
case Constant.INCALL_RESPONSE:
if (isSucceed == Constant.RESPONSE_SUCCEED) {
createOffer();
Log.e(TAG, "对方同意接听");
} else if (isSucceed == Constant.RESPONSE_FAILURE) {
Log.e(TAG, "对方拒绝接听");
}
break;
case Constant.OFFER:
//收到对方offer sdp
SessionDescription sessionDescription1 = model.getSessionDescription();
peerConnection.setRemoteDescription(observer, sessionDescription1);
createAnswer();
break;
case Constant.CANDIDATE:
//服务端 发送 接收方sdpAnswer
IceCandidate iceCandidate = model.getIceCandidate();
if (iceCandidate != null) {
peerConnection.addIceCandidate(iceCandidate);
}
break;
}
}
}
}
}
@Override
public void onClose(int code, String reason, boolean remote) {
setText("已关闭");
Log.e(TAG, "onClose == code " + code + " reason == " + reason + " remote == " + remote);
}
@Override
public void onError(Exception ex) {
setText("onError == " + ex.getMessage());
Log.e(TAG, "onError== " + ex.getMessage());
}
};
webSocketClient.connect();
} catch (Exception e) {
Log.d(TAG, "socket Exception : " + e.getMessage());
}
}
- 创建 PeerConnection
private void createPeerConnection() {
//Initialising PeerConnectionFactory
InitializationOptions initializationOptions = InitializationOptions.builder(this)
.setEnableInternalTracer(true)
.setFieldTrials("WebRTC-H264HighProfile/Enabled/")
.createInitializationOptions();
PeerConnectionFactory.initialize(initializationOptions);
//创建EglBase对象
eglBase = EglBase.create();
PeerConnectionFactory.Options options = new PeerConnectionFactory.Options();
options.disableEncryption = true;
options.disableNetworkMonitor = true;
peerConnectionFactory = PeerConnectionFactory.builder()
.setVideoDecoderFactory(new DefaultVideoDecoderFactory(eglBase.getEglBaseContext()))
.setVideoEncoderFactory(new DefaultVideoEncoderFactory(eglBase.getEglBaseContext(), true, true))
.setOptions(options)
.createPeerConnectionFactory();
// 配置STUN穿透服务器 转发服务器
iceServers = new ArrayList<>();
PeerConnection.IceServer iceServer = PeerConnection.IceServer.builder(Constant.STUN).createIceServer();
iceServers.add(iceServer);
streamList = new ArrayList<>();
PeerConnection.RTCConfiguration configuration = new PeerConnection.RTCConfiguration(iceServers);
PeerConnectionObserver connectionObserver = getObserver();
peerConnection = peerConnectionFactory.createPeerConnection(configuration, connectionObserver);
/*
DataChannel.Init 可配参数说明:
ordered:是否保证顺序传输;
maxRetransmitTimeMs:重传允许的最长时间;
maxRetransmits:重传允许的最大次数;
*/
DataChannel.Init init = new DataChannel.Init();
if (peerConnection != null) {
channel = peerConnection.createDataChannel(Constant.CHANNEL, init);
}
DateChannelObserver channelObserver = new DateChannelObserver();
connectionObserver.setObserver(channelObserver);
initView();
initObserver();
}
- 初始化 View
private void initSurfaceview(SurfaceViewRenderer localSurfaceView) {
localSurfaceView.init(eglBase.getEglBaseContext(), null);
localSurfaceView.setMirror(true);
localSurfaceView.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FILL);
localSurfaceView.setKeepScreenOn(true);
localSurfaceView.setZOrderMediaOverlay(true);
localSurfaceView.setEnableHardwareScaler(false);
}
- 初始化音频和视频
/**
* 创建本地视频
*
* @param localSurfaceView
*/
private void startLocalVideoCapture(SurfaceViewRenderer localSurfaceView) {
VideoSource videoSource = peerConnectionFactory.createVideoSource(true);
SurfaceTextureHelper surfaceTextureHelper = SurfaceTextureHelper.create(Thread.currentThread().getName(), eglBase.getEglBaseContext());
VideoCapturer videoCapturer = createVideoCapturer();
videoCapturer.initialize(surfaceTextureHelper, this, videoSource.getCapturerObserver());
videoCapturer.startCapture(Constant.VIDEO_RESOLUTION_WIDTH, Constant.VIDEO_RESOLUTION_HEIGHT, Constant.VIDEO_FPS); // width, height, frame per second
videoTrack = peerConnectionFactory.createVideoTrack(Constant.VIDEO_TRACK_ID, videoSource);
videoTrack.addSink(localSurfaceView);
MediaStream localMediaStream = peerConnectionFactory.createLocalMediaStream(Constant.LOCAL_VIDEO_STREAM);
localMediaStream.addTrack(videoTrack);
peerConnection.addTrack(videoTrack, streamList);
peerConnection.addStream(localMediaStream);
}
/**
* 创建本地音频
*/
private void startLocalAudioCapture() {
//语音
MediaConstraints audioConstraints = new MediaConstraints();
//回声消除
audioConstraints.mandatory.add(new MediaConstraints.KeyValuePair("googEchoCancellation", "true"));
//自动增益
audioConstraints.mandatory.add(new MediaConstraints.KeyValuePair("googAutoGainControl", "true"));
//高音过滤
audioConstraints.mandatory.add(new MediaConstraints.KeyValuePair("googHighpassFilter", "true"));
//噪音处理
audioConstraints.mandatory.add(new MediaConstraints.KeyValuePair("googNoiseSuppression", "true"));
AudioSource audioSource = peerConnectionFactory.createAudioSource(audioConstraints);
audioTrack = peerConnectionFactory.createAudioTrack(Constant.AUDIO_TRACK_ID, audioSource);
MediaStream localMediaStream = peerConnectionFactory.createLocalMediaStream(Constant.LOCAL_AUDIO_STREAM);
localMediaStream.addTrack(audioTrack);
audioTrack.setVolume(Constant.VOLUME);
peerConnection.addTrack(audioTrack, streamList);
peerConnection.addStream(localMediaStream);
}
创建 WebRTC 调整后的流程图
代码自取: