慕课网课程webrtc入门学习后的总结
android {
...
compileOptions {
sourceCompatibility = '1.8'
targetCompatibility = '1.8'
}
}
dependencies {
implementation 'io.socket:socket.io-client:1.0.0'
//webrtc库 ,可自己编译
implementation 'org.webrtc:google-webrtc:1.0.+'
//Android 6.0+ 动态权限申请库
implementation 'pub.devrel:easypermissions:1.1.3'
}
String[] perms = {Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO};
if (!EasyPermissions.hasPermissions(this, perms)) {
EasyPermissions.requestPermissions(this, "Need permissions for camera & microphone", 0, perms);
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this);
}
// 渲染本地视频
//渲染远程视频
//日志打印
private void initView() {
mLogcatView = findViewById(R.id.LogcatView);
mLocalSurfaceView = findViewById(R.id.LocalSurfaceView);
mRemoteSurfaceView = findViewById(R.id.RemoteSurfaceView);
mRootEglBase = EglBase.create();
//本地视频流渲染初始化
mLocalSurfaceView.init(mRootEglBase.getEglBaseContext(), null);
mLocalSurfaceView.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FILL);
mLocalSurfaceView.setMirror(true);
mLocalSurfaceView.setEnableHardwareScaler(false);
//远程视频流渲染初始化
mRemoteSurfaceView.init(mRootEglBase.getEglBaseContext(), null);
mRemoteSurfaceView.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FILL);
mRemoteSurfaceView.setMirror(true);
mRemoteSurfaceView.setEnableHardwareScaler(true);
mRemoteSurfaceView.setZOrderMediaOverlay(true);
}
/**
* 创建PC工厂
*
* @return
*/
private PeerConnectionFactory createPeerConnectionFactory() {
//创建视频编解码工厂
VideoEncoderFactory encoderFactory = new DefaultVideoEncoderFactory(mRootEglBase.getEglBaseContext(),
false,
true);
VideoDecoderFactory decoderFactory = new DefaultVideoDecoderFactory(mRootEglBase.getEglBaseContext());
//初始化PC工厂参数
PeerConnectionFactory.InitializationOptions initializationOptions = PeerConnectionFactory
.InitializationOptions
.builder(this)
.setEnableInternalTracer(true)
.createInitializationOptions();
PeerConnectionFactory.initialize(initializationOptions);
PeerConnectionFactory.Builder builder = PeerConnectionFactory.builder()
.setVideoDecoderFactory(decoderFactory)
.setVideoEncoderFactory(encoderFactory);
builder.setOptions(null);
return builder.createPeerConnectionFactory();
}
private void initPeerConnectionFactor() {
mPeerConnectionFactory = createPeerConnectionFactory();
// NOTE: this _must_ happen while PeerConnectionFactory is alive!
Logging.enableLogToDebugOutput(Logging.Severity.LS_VERBOSE);
mSurfaceTextureHelper = SurfaceTextureHelper.create("CaptureThread", mRootEglBase.getEglBaseContext());
//创建视频源 ,参数是否截屏
VideoSource videoSource = mPeerConnectionFactory.createVideoSource(false);
//创建捕获视频流
mVideoCapturer = createVideoCapturer();
//初始化视频铺货器
mVideoCapturer.initialize(mSurfaceTextureHelper, getApplicationContext(), videoSource.getCapturerObserver());
//视频源和视频轨绑定
mVideoTrack = mPeerConnectionFactory.createVideoTrack(VIDEO_TRACK_ID, videoSource);
mVideoTrack.setEnabled(true);
//视频流用本地View控件显示
mVideoTrack.addSink(mLocalSurfaceView);
//创建音频源
AudioSource audioSource = mPeerConnectionFactory.createAudioSource(new MediaConstraints());
mAudioTrack = mPeerConnectionFactory.createAudioTrack(AUDIO_TRACK_ID, audioSource);
mAudioTrack.setEnabled(true);
}
public PeerConnection createPeerConnection() {
Log.i(TAG, "Create PeerConnection ...");
LinkedList iceServers = new LinkedList<>();
//搭建好stun服务器可以加上配置
/*PeerConnection.IceServer iceServer = PeerConnection.IceServer.builder("")
.setPassword("")
.setUsername("")
.createIceServer();
iceServers.add(iceServer);*/
PeerConnection.RTCConfiguration rtcConfig = new PeerConnection.RTCConfiguration(iceServers);
// TCP candidates are only useful when connecting to a server that supports
// ICE-TCP.
rtcConfig.tcpCandidatePolicy = PeerConnection.TcpCandidatePolicy.DISABLED;
//rtcConfig.bundlePolicy = PeerConnection.BundlePolicy.MAXBUNDLE;
//rtcConfig.rtcpMuxPolicy = PeerConnection.RtcpMuxPolicy.REQUIRE;
rtcConfig.continualGatheringPolicy = PeerConnection.ContinualGatheringPolicy.GATHER_CONTINUALLY;
// Use ECDSA encryption.
//rtcConfig.keyType = PeerConnection.KeyType.ECDSA;
// Enable DTLS for normal calls and disable for loopback calls.
rtcConfig.enableDtlsSrtp = true;
//rtcConfig.sdpSemantics = PeerConnection.SdpSemantics.UNIFIED_PLAN;
PeerConnection connection = mPeerConnectionFactory.createPeerConnection(rtcConfig, mPeerConnectionObserver);
if (connection == null) {
return null;
}
//pc音视频轨
List mediaStreamLabels = Collections.singletonList("ARDAMS");
connection.addTrack(mVideoTrack, mediaStreamLabels);
connection.addTrack(mAudioTrack, mediaStreamLabels);
return connection;
}
这里的事件是client和server使用socket.io触发的一些事件
public interface OnSignalEventListener {
void onConnected();
void onConnecting();
void onDisconnected();
void onUserJoined(String roomName, String userID);
void onUserLeaved(String roomName, String userID);
void onRemoteUserJoined(String roomName);
void onRemoteUserLeaved(String roomName, String userID);
void onRoomFull(String roomName, String userID);
void onMessage(JSONObject message);
}
io.sockets.on('connection', (socket)=> {
socket.on('message', (room, data)=>{
socket.to(room).emit('message',room, data);
});
socket.on('join', (room)=>{
socket.join(room);
var myRoom = io.sockets.adapter.rooms[room];
var users = (myRoom)? Object.keys(myRoom.sockets).length : 0;
logger.debug('the user number of room is: ' + users);
if(users < USERCOUNT){
socket.emit('joined', room, socket.id); //发给除自己之外的房间内的所有人
if(users > 1){
socket.to(room).emit('otherjoin', room, socket.id);
}
}else{
socket.leave(room);
socket.emit('full', room, socket.id);
}
//socket.emit('joined', room, socket.id); //发给自己
//socket.broadcast.emit('joined', room, socket.id); //发给除自己之外的这个节点上的所有人
//io.in(room).emit('joined', room, socket.id); //发给房间内的所有人
});
socket.on('leave', (room)=>{
var myRoom = io.sockets.adapter.rooms[room];
var users = (myRoom)? Object.keys(myRoom.sockets).length : 0;
logger.debug('the user number of room is: ' + (users-1));
//socket.emit('leaved', room, socket.id);
//socket.broadcast.emit('leaved', room, socket.id);
socket.to(room).emit('bye', room, socket.id);
socket.emit('leaved', room, socket.id);
//io.in(room).emit('leaved', room, socket.id);
});
});
//发送消息
public void joinRoom(String url, String roomName) {
Log.i(TAG, "joinRoom: " + url + ", " + roomName);
try {
mSocket = IO.socket(url);
mSocket.connect();
} catch (URISyntaxException e) {
e.printStackTrace();
return;
}
//mUserId = userId;
mRoomName = roomName;
listenSignalEvents();
mSocket.emit("join", mRoomName);
}
public void leaveRoom() {
Log.i(TAG, "leaveRoom: " + mRoomName);
if (mSocket == null) {
return;
}
mSocket.emit("leave", mRoomName);
mSocket.close();
mSocket = null;
}
public void sendMessage(JSONObject message) {
Log.i(TAG, "broadcast: " + message);
if (mSocket == null) {
return;
}
mSocket.emit("message", mRoomName, message);
}
//接收消息,监听服务器的消息
mSocket.on(Socket.EVENT_CONNECT_ERROR, new Emitter.Listener() {
@Override
public void call(Object... args) {
Log.e(TAG, "onConnectError: " + args);
}
});
mSocket.on(Socket.EVENT_ERROR, new Emitter.Listener() {
@Override
public void call(Object... args) {
Log.e(TAG, "onError: " + args);
}
});
mSocket.on(Socket.EVENT_CONNECT, new Emitter.Listener() {
@Override
public void call(Object... args) {
String sessionId = mSocket.id();
Log.i(TAG, "onConnected");
if (mOnSignalEventListener != null) {
mOnSignalEventListener.onConnected();
}
}
});
mSocket.on(Socket.EVENT_CONNECTING, new Emitter.Listener() {
@Override
public void call(Object... args) {
Log.i(TAG, "onConnecting");
if (mOnSignalEventListener != null) {
mOnSignalEventListener.onConnecting();
}
}
});
mSocket.on(Socket.EVENT_DISCONNECT, new Emitter.Listener() {
@Override
public void call(Object... args) {
Log.i(TAG, "onDisconnected");
if (mOnSignalEventListener != null) {
mOnSignalEventListener.onDisconnected();
}
}
});
mSocket.on("joined", new Emitter.Listener() {
@Override
public void call(Object... args) {
String roomName = (String) args[0];
String userId = (String) args[1];
if (/*!mUserId.equals(userId) &&*/ mOnSignalEventListener != null) {
//mOnSignalEventListener.onRemoteUserJoined(userId);
mOnSignalEventListener.onUserJoined(roomName, userId);
}
//Log.i(TAG, "onRemoteUserJoined: " + userId);
Log.i(TAG, "onUserJoined, room:" + roomName + "uid:" + userId);
}
});
mSocket.on("leaved", new Emitter.Listener() {
@Override
public void call(Object... args) {
String roomName = (String) args[0];
String userId = (String) args[1];
if (/*!mUserId.equals(userId) &&*/ mOnSignalEventListener != null) {
//mOnSignalEventListener.onRemoteUserLeft(userId);
mOnSignalEventListener.onUserLeaved(roomName, userId);
}
Log.i(TAG, "onUserLeaved, room:" + roomName + "uid:" + userId);
}
});
mSocket.on("otherjoin", new Emitter.Listener() {
@Override
public void call(Object... args) {
String roomName = (String) args[0];
String userId = (String) args[1];
if (mOnSignalEventListener != null) {
mOnSignalEventListener.onRemoteUserJoined(roomName);
}
Log.i(TAG, "onRemoteUserJoined, room:" + roomName + "uid:" + userId);
}
});
mSocket.on("bye", new Emitter.Listener() {
@Override
public void call(Object... args) {
String roomName = (String) args[0];
String userId = (String) args[1];
if (mOnSignalEventListener != null) {
mOnSignalEventListener.onRemoteUserLeaved(roomName, userId);
}
Log.i(TAG, "onRemoteUserLeaved, room:" + roomName + "uid:" + userId);
}
});
mSocket.on("full", new Emitter.Listener() {
@Override
public void call(Object... args) {
//释放资源
mSocket.disconnect();
mSocket.close();
mSocket = null;
String roomName = (String) args[0];
String userId = (String) args[1];
if (mOnSignalEventListener != null) {
mOnSignalEventListener.onRoomFull(roomName, userId);
}
Log.i(TAG, "onRoomFull, room:" + roomName + "uid:" + userId);
}
});
mSocket.on("message", new Emitter.Listener() {
@Override
public void call(Object... args) {
String roomName = (String)args[0];
JSONObject msg = (JSONObject) args[1];
if (mOnSignalEventListener != null) {
mOnSignalEventListener.onMessage(msg);
}
Log.i(TAG, "onMessage, room:" + roomName + "data:" + msg);
}
});
MediaConstraints mediaConstraints = new MediaConstraints();
mediaConstraints.mandatory.add(new MediaConstraints.KeyValuePair("OfferToReceiveAudio", "true"));
mediaConstraints.mandatory.add(new MediaConstraints.KeyValuePair("OfferToReceiveVideo", "true"));
//不打开dtls无法和web端通信
mediaConstraints.optional.add(new MediaConstraints.KeyValuePair("DtlsSrtpKeyAgreement", "true"));
mPeerConnection.createOffer(new SimpleSdpObserver() {
@Override
public void onCreateSuccess(SessionDescription sessionDescription) {
Log.i(TAG, "Create local offer success: \n" + sessionDescription.description);
mPeerConnection.setLocalDescription(new SimpleSdpObserver(), sessionDescription);
JSONObject message = new JSONObject();
try {
message.put("type", "offer");
message.put("sdp", sessionDescription.description);
SignalClient.getInstance().sendMessage(message);
} catch (JSONException e) {
e.printStackTrace();
}
}
}, mediaConstraints);
private void onRemoteOfferReceived(JSONObject message) {
logcatOnUI("Receive Remote Call ...");
if (mPeerConnection == null) {
mPeerConnection = createPeerConnection();
}
try {
String description = message.getString("sdp");
mPeerConnection.setRemoteDescription(
new SimpleSdpObserver(),
new SessionDescription(
SessionDescription.Type.OFFER,
description));
doAnswerCall();//发送answer
} catch (JSONException e) {
e.printStackTrace();
}
}
MediaConstraints sdpMediaConstraints = new MediaConstraints();
Log.i(TAG, "Create answer ...");
mPeerConnection.createAnswer(new SimpleSdpObserver() {
@Override
public void onCreateSuccess(SessionDescription sessionDescription) {
Log.i(TAG, "Create answer success !");
mPeerConnection.setLocalDescription(new SimpleSdpObserver(),
sessionDescription);
JSONObject message = new JSONObject();
try {
message.put("type", "answer");
message.put("sdp", sessionDescription.description);
SignalClient.getInstance().sendMessage(message);
} catch (JSONException e) {
e.printStackTrace();
}
}
}, sdpMediaConstraints);
private void onRemoteAnswerReceived(JSONObject message) {
try {
String description = message.getString("sdp");
mPeerConnection.setRemoteDescription(
new SimpleSdpObserver(),
new SessionDescription(
SessionDescription.Type.ANSWER,
description));
} catch (JSONException e) {
e.printStackTrace();
}
}
offer 和answer后就完成了信令交互
//PeerConnection.Observer 监听到Candidate变化了 ,发送Candidate
@Override
public void onIceCandidate(IceCandidate iceCandidate) {
Log.i(TAG, "onIceCandidate: " + iceCandidate);
try {
JSONObject message = new JSONObject();
//message.put("userId", RTCSignalClient.getInstance().getUserId());
message.put("type", "candidate");
message.put("label", iceCandidate.sdpMLineIndex);
message.put("id", iceCandidate.sdpMid);
message.put("candidate", iceCandidate.sdp);
SignalClient.getInstance().sendMessage(message);
} catch (JSONException e) {
e.printStackTrace();
}
}
//收到Candidate
private void onRemoteCandidateReceived(JSONObject message) {
try {
IceCandidate remoteIceCandidate =
new IceCandidate(message.getString("id"),
message.getInt("label"),
message.getString("candidate"));
mPeerConnection.addIceCandidate(remoteIceCandidate);
} catch (JSONException e) {
e.printStackTrace();
}
}
//我注释掉了https服务 ,使用http
'use strict'
var log4js = require('log4js');
var http = require('http');
//var https = require('https');
var fs = require('fs');
var socketIo = require('socket.io');
var express = require('express');
var serveIndex = require('serve-index');
var USERCOUNT = 3;
log4js.configure({
appenders: {
file: {
type: 'file',
filename: 'app.log',
layout: {
type: 'pattern',
pattern: '%r %p - %m',
}
}
},
categories: {
default: {
appenders: ['file'],
level: 'debug'
}
}
});
var logger = log4js.getLogger();
var app = express();
//app.use(serveIndex('./public')); //这些是放web端代码的地方 ,我只测试Android端
//app.use(express.static('./public'));
//http server
var http_server = http.createServer(app);
http_server.listen(80, '0.0.0.0'); //监听80端口
//https_server.listen(443, '0.0.0.0');
//var options = {
// key : fs.readFileSync('./cert/2280243_starcloud.club.key'),
// cert: fs.readFileSync('./cert/2280243_starcloud.club.pem')
//}
//https server
//var https_server = https.createServer(options, app);
var io = socketIo.listen(http_server);
io.sockets.on('connection', (socket)=> {
socket.on('message', (room, data)=>{
socket.to(room).emit('message',room, data);
});
socket.on('join', (room)=>{
socket.join(room);
var myRoom = io.sockets.adapter.rooms[room];
var users = (myRoom)? Object.keys(myRoom.sockets).length : 0;
logger.debug('the user number of room is: ' + users);
if(users < USERCOUNT){
socket.emit('joined', room, socket.id); //发给除自己之外的房间内的所有人
if(users > 1){
socket.to(room).emit('otherjoin', room, socket.id);
}
}else{
socket.leave(room);
socket.emit('full', room, socket.id);
}
//socket.emit('joined', room, socket.id); //发给自己
//socket.broadcast.emit('joined', room, socket.id); //发给除自己之外的这个节点上的所有人
//io.in(room).emit('joined', room, socket.id); //发给房间内的所有人
});
socket.on('leave', (room)=>{
var myRoom = io.sockets.adapter.rooms[room];
var users = (myRoom)? Object.keys(myRoom.sockets).length : 0;
logger.debug('the user number of room is: ' + (users-1));
//socket.emit('leaved', room, socket.id);
//socket.broadcast.emit('leaved', room, socket.id);
socket.to(room).emit('bye', room, socket.id);
socket.emit('leaved', room, socket.id);
//io.in(room).emit('leaved', room, socket.id);
});
});