paullouisageneau / libdatachannel

C/C++ WebRTC network library featuring Data Channels, Media Transport, and WebSockets
https://libdatachannel.org/
Mozilla Public License 2.0
1.82k stars 365 forks source link

Any example demostrate how multiStream feature works? #393

Closed reischen80 closed 3 years ago

reischen80 commented 3 years ago

Hi, I reconstruct the cope-paste example code, try implementing Multi-Stream sending/receiving, when seperating the peerconnection procedure and dc hand shaking procedure, I find pc doesn't work(no any local sdp ouput) until the datachannel is created and the sdp negotiating can be go further. It looks the dc hand shaking is necessary for the p2p connection.
It that true? and anyone have the mulitstream example?

Thanks

stez-mind commented 3 years ago

I just did something similar a few days ago.

I only had a media stream (audio only in my case but it shouldn't matter), what worked for me was to explicitly call setLocalDescription() in the offered after adding the track to the peer connection, i.e.:

auto track = pc->addTrack(audio);
pc->setLocalDescription();
reischen80 commented 3 years ago

Er, Multi-stream I mentioned above means diffrent message id(stream) in one peerconnection, which is the main feature of SCTP.

And I want to test a standalone p2p connection before any datachannel creation, expecting go into the CONNECTING state and failed.

In your case, pc->setLocalDescription() without pc->addTrack(audio) before may lead to anther place. Try it.

paullouisageneau commented 3 years ago

You can't negotiate an "empty" PeerConnection with WebRTC, there needs to be something to negotiate, either data channels or media tracks. Therefore, you have to create a data channel on the offerer side. You can of course open more data channels afterwards from both sides.

Additionally, auto negotiation is enabled by default, so createDataChannel(), like setRemoteDescription(), will automatically set the local description internally. If you don't want this behavior, you can set disableAutoNegotiation = true in rtc::Configuration.

paullouisageneau commented 3 years ago

@reischen80 The connectivity test opens a second DataChannel on an already-negotiated PeerConnection, for example: https://github.com/paullouisageneau/libdatachannel/blob/3eb8ab567e8dab831559a7b09e427f009c0079d5/test/connectivity.cpp#L189

reischen80 commented 3 years ago

Yes. it works. I defaultly create a datachannel for negotiation. And add more datachannels if needed. Seems fine to the requirment. Thanks.

reischen80 commented 3 years ago

Update my multi-streaming progress. When Offerer create two or more datachannels consecutively(aka, dc0,dc1), and two Answerer handles (aka a0,a1) are listening this datachannels ACK message to finish the hand-shaking. Since each side is independent, it may lead to some chaos, like a0 is binded to both dc0 dc1 and a1 is discarded and fails.

So far I still cannot find the siganal (or State) to identify two of datachannels hand-shaking at the same time. Any tip? a

paullouisageneau commented 3 years ago

There should be no way for DataChannels to be mixed-up (user-negotiated ones can, but I assume you are referring to normal DataChannels here).

If one side calls createDataChannel() the other will get the other end of the DataChannel in the onDataChannel callback. On the creating side, the onOpen callback will indicate that the DataChannel handshake has finished.

reischen80 commented 3 years ago

When the 2nd Answerer created and listening before the 2nd Offerer created, everything works fine; while on the contrast, the 2nd Answer will be binded to the 1st Offerer, even the first dc pair is runing (onOpen / onDatachannel ready).

paullouisageneau commented 3 years ago

What do you mean by second answerer? There is only a single data channel callback on answerer side, which will be called a second time.

The order of data channel openings is not guaranteed. You shouldn't change callbacks on the answerer side without synchronization. It's done in the test only because the order of events is properly enforced.

reischen80 commented 3 years ago

Right, here is my experimental result:

  1. pc connecting;
  2. answerer listenning by onCreateDatachannel
  3. offerer create dc1
  4. answerer and offerer conntected with dc1 and begin to transfer things all the following time;
  5. at the same time, the answerer side is still listening another dc;
  6. offerer creates dc2
  7. answerer and offerer conntected with dc2;
  8. now we have two datachannels streaming at the same time;

if the order of stage5 and stage6 are switched, the answerer will replace the first dc handle and destroy the dc1 comminication.

Have to say, multistreaming establishment need to be sync upon higher level caller, is not so friendly for the application.

paullouisageneau commented 3 years ago

if the order of stage5 and stage6 are switched, the answerer will replace the first dc handle and destroy the dc1 comminication.

Have to say, multistreaming establishment need to be sync upon higher level caller, is not so friendly for the application.

It's because the API works similarly to the JavaScript API. You are not supposed to rely on creation order and change the onDataChannel callback on the fly.

If you want to distinguish data channels on the remote side, you must use their labels and store them accordingly:

pc.onDataChannel([](auto dc) {
    if(dc->label() == "myfirstdatachannel") {
        // This is the first data channel, store the reference somewhere
    } else if (dc->label() == "myseconddatachannel") {
        // This is the second data channel, store it elsewhere
    } else {
        // Unknown data channel
    }
});