1

I am using mesh architecture for webRTC app for multi-user,the video chat works fine for multi-user, for text chat i created a dataChannel on the peer who creates offer and the onDataChannel handler creates a dataChannel on the other peer. The text chat works for 2 users but when there are 3 users, the 1st client (to join) is able to see all other clients messages but can send message to only 2nd client (to join), the 2nd client (to join) is able to see message from 1st client and send to only 1st client, 3rd client receives message from none but can send to 1st client.

the onDataChannel event handler in PeerConnection.Observer

            @Override
            public void onDataChannel(DataChannel dataChannel) {
                Log.d("DataChannel", "onDataChannel" + " , state: " + dataChannel.state());
                DataChannel.Observer dcObserver = new DcObserver(){
                    @Override
                    public void onStateChange() {
                        Log.d(TAG, "onStateChange: remote data channel state: " + dChannel.state().toString());
                    }

                    @Override
                    public void onMessage(DataChannel.Buffer buffer) {
                        Log.d(TAG, "onMessage: got message");
                        readMessage(buffer.data);
                    }
                };
                dataChannel.registerObserver(dcObserver);
                dataChannels.add(dataChannel);
                dcObservers.add(dcObserver);
            }
        });

Creating DataChannel while creating peerConnections

@Override
    public void onNewPeerJoined(String socketId, boolean createOffer) {
        showToast("Remote Peer Joined");
        PeerConnection peerConnection = getOrCreatePeerConnection(socketId);
        if (createOffer) {
            dChannel = peerConnection.createDataChannel("DataChannel",new DataChannel.Init());
            DataChannel.Observer dcObserver = new DcObserver(){
                @Override
                public void onStateChange() {
                    Log.d(TAG, "onStateChange: remote data channel state: " + dChannel.state().toString());
                }

                @Override
                public void onMessage(DataChannel.Buffer buffer) {
                    Log.d(TAG, "onMessage: got message");
                    readMessage(buffer.data);
                }
            };
            dChannel.registerObserver(dcObserver);
            dcObservers.add(dcObserver);
            dataChannels.add(dChannel);
            sdpConstraints = new MediaConstraints();
            sdpConstraints.mandatory.add(
                    new MediaConstraints.KeyValuePair("OfferToReceiveAudio", "true"));
            sdpConstraints.mandatory.add(
                    new MediaConstraints.KeyValuePair("OfferToReceiveVideo", "true"));
            //sdpConstraints.optional.add(new MediaConstraints.KeyValuePair("DtlsSrtpKeyAgreement", "true"));
            peerConnection.createOffer(new CustomSdpObserver("localCreateOffer") {
                @Override
                public void onCreateSuccess(SessionDescription sessionDescription) {
                    super.onCreateSuccess(sessionDescription);
                    peerConnection.setLocalDescription(new CustomSdpObserver("localSetLocalDesc"), sessionDescription);
                    Log.d("onCreateSuccess", "SignallingClient emit ");
                    SignallingClient.getInstance(roomName).emitSessionDescription(sessionDescription, socketId);
                }
            }, sdpConstraints);

        }
    }

Sending messages

public void sendMessage() {
        String message = editText.getText().toString();
        if (message.isEmpty()) {
            return;
        }
        editText.setText("");
        textView.append(HtmlCompat.fromHtml("<b>" + userName + "</b>",HtmlCompat.FROM_HTML_MODE_LEGACY));
        textView.append(": " + message + "\n");
        String str = "<b>" + userName + "</b> : " + message;
        ByteBuffer data = stringToByteBuffer("-s" + str, Charset.defaultCharset());
        for (int i = 0; i < dataChannels.size(); i++){
            Log.d("info","send_data dataChannel" + i);
            dataChannels.get(i).send(new DataChannel.Buffer(data, false));
        }
    }

2 Answers2

0

Data channels are not meant to chat, not even for a 1-to-1 chat. Imagine a scenario when A sends a message to B and B is not online to receive it. A could wait for B to be online again but what if A is not online when B is online again. Now extend this scenario to multiuser, it just doesn't offer the right experience.

You need something more reliable for a multiparty chat with the store and forward ability - have a look here https://mesibo.com/livedemo/ It has multi-user video/voice conferencing, 1-to-1, and group chat. The source code is here https://github.com/mesibo/conferencing. Although this demo uses mesibo API, you can use any API of your choice.

mesibo
  • 3,970
  • 6
  • 25
  • 43
  • I am not just using the dataChannel for chat, i would be using it for file transfer as well, i was just trying it for chat to see how dataChannels can be used for multiple peers,if this issue is solved than both of above features would work, can you help me with this issue? – tanmaypanda Jun 25 '20 at 04:48
0

2 things I can suggest:

  1. dChannel will only hold last data channel you have opened on peer. Consider making it an array and hold all data channels.
  2. Add Optional constraints to SDPConstraints DtlsSrtpKeyAgreement = true and internalSctpDataChannels = true
  • Actually, I am storing the dChannel in dataChannels list. Also I tried adding the two optional constraints, the issue still exits. Also whenever i implement the onRenegotiationNeeded method in PeerConnection.Observer (i implemented the interface as a class and use override to implement additional functionality to the methods while creating those objects in main activity) it gets called multiple time and crashes the app – tanmaypanda Jun 28 '20 at 11:43