android webrtc 多人网状P2P视频聊天

什么是网状P2P?假设3个人视频会议,每个人都要同时接收2路流,上传两路流。每个客户端要创建多个(N-1个)PeerConnection ,同时和多个人建立 P2P 连接。

多人P2P跟两人P2P通信的唯一区别就是要创建多个 PeerConnection ,也很简单。

比较复杂的地方其实是聊天室信令的设计与实现,客户端还比较简单。网状P2P服务器压力很小,服务端只有信令不涉及流的处理,客户端压力较大,因为要同时处理多路流。

一个用户新进入房间,这个用户要分别和房间里所有的用户建立P2P连接。要么新加入的用户主动发起连接 offer,要么其他用户向新加入的用户发起连接 offer。这两种其实区别不大。

我们来定义一个规则一个用户新加入房间,房间内的所有用户主动发起 offer 来连接新加入的用户。

默认定义一个房间

// 默认就一个房间,所有人都在一个房间内
public static Set roomSet = Collections.synchronizedSet(new HashSet<>());

首先所有用户还是先注册自己到服务端

if ("register".equals(event)) {
    // 注册
    String userId = jsonObject.getString("userId");
    if (!userMap.containsKey(userId)) {
        ctx.channel().attr(userIdKey).set(userId);
        userMap.put(userId, ctx.channel());
        logger.info("user {} 上线", userId);
    } else {
        logger.info("user{}已经上线");
    }
}

定义一个加入房间的信令

 } else if ("joinRoom".equals(event)) {
    String userId = ctx.channel().attr(userIdKey).get();
    if (userId == null) {
        logger.error("该用户未注册,请先注册");
        return;
    }
    roomSet.add(userId);
    logger.info("用户{}加入房间", userId);
    for (String uid : roomSet) {
        if (!userId.equals(uid) && userMap.containsKey(uid)) {
            // 向房间内的其他人转发新加入房间的消息,附加新加入房间的用户ID
            JSONObject joinRoomObj = new JSONObject();
            joinRoomObj.put("event", "joinRoom");
            joinRoomObj.put("userId", userId);
            userMap.get(uid).writeAndFlush(new TextWebSocketFrame(joinRoomObj.toJSONString()));
        }
    }
}

新用户加入房间后向房间内的其他用户推送一条新用户加入的消息,其他用户收到这个消息后主动来连接这个新用户。

相应的定义一个离开房间的信令

 } else if ("leaveRoom".equals(event)) {
    String userId = ctx.channel().attr(userIdKey).get();
    if (userId == null) {
        logger.error("该用户未注册,请先注册");
        return;
    }
    roomSet.remove(userId);
    logger.info("用户{}离开房间", userId);
    for (String uid : roomSet) {
        if (userMap.containsKey(uid)) {
            // 向房间内的其他人转发新离开房间的消息
            JSONObject leaveRoomObj = new JSONObject();
            leaveRoomObj.put("event", "leaveRoom");
            leaveRoomObj.put("userId", userId);
            userMap.get(uid).writeAndFlush(new TextWebSocketFrame(leaveRoomObj.toJSONString()));
        }
    }
}

sdp, trickle 等信令消息的转发不用动,直接根据接收者ID转发就行


} else if ("sdp".equals(event) || "trickle".equals(event)) {
    // sdp 和 ICE trickle 消息根据接收者直接转发给对方
    logger.info("收到消息 {} {}->{} <<== {}",
            event,
            jsonObject.getString("sender"),
            jsonObject.getString("receiver"),
            jsonObject.toString());
    String receiver = jsonObject.getString("receiver");
    if (!roomSet.contains(receiver)) {
        logger.error("接收者{}没有加入房间", receiver);
        return;
    }
    if (receiver != null && userMap.containsKey(receiver)) {
        msg.retain();
        userMap.get(receiver).writeAndFlush(msg);
        logger.info("转发消息 {} {}->{} ==>> {}",
                event,
                jsonObject.getString("sender"),
                jsonObject.getString("receiver"),
                jsonObject.toString());
    }
}
videoroom.jpeg

3个手机依次安装3个用户,依次加入房间,很简单实现了多人P2P通信。

假设领导要你搞一个10人以内的视频会议,网状P2P其实挺合适,做一个完善一点的房间管理服务,客户端再优化完善一下,一到两周搞定。

人再多的话网状就不太适合了,就得 SFU 或 MCU 了,得架设一个 webrtc 网关了,且听下回分解。

客户端源码参考:https://github.com/lesliebeijing/WebRtcDemo 里面的VideoRoomActivity
signalserver: 参考 https://github.com/lesliebeijing/WebrtcSignalingDemo/tree/branch_videoroom

你可能感兴趣的:(android webrtc 多人网状P2P视频聊天)