-
Notifications
You must be signed in to change notification settings - Fork 201
Wait for data channel to open
#67
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -43,16 +43,11 @@ class Engine extends Disposable with EventsEmittable<EngineEvent> { | |
| PCTransport? get primary => _subscriberPrimary ? subscriber : publisher; | ||
|
|
||
| // data channels for packets | ||
| rtc.RTCDataChannel? _reliableDC; | ||
| rtc.RTCDataChannel? _lossyDC; | ||
| rtc.RTCDataChannel? _reliableDCPub; | ||
| rtc.RTCDataChannel? _lossyDCPub; | ||
| rtc.RTCDataChannel? _reliableDCSub; | ||
| rtc.RTCDataChannel? _lossyDCSub; | ||
|
|
||
| rtc.RTCDataChannelState get reliableDataChannelState => | ||
| _reliableDC?.state ?? rtc.RTCDataChannelState.RTCDataChannelClosed; | ||
|
|
||
| rtc.RTCDataChannelState get lossyDataChannelState => | ||
| _lossyDC?.state ?? rtc.RTCDataChannelState.RTCDataChannelClosed; | ||
| bool _iceConnected = false; | ||
|
|
||
| ConnectionState _connectionState = ConnectionState.disconnected; | ||
|
|
@@ -192,49 +187,61 @@ class Engine extends Disposable with EventsEmittable<EngineEvent> { | |
| Future<void> sendDataPacket( | ||
| lk_models.DataPacket packet, | ||
| ) async { | ||
| // make sure we do have a data connection | ||
| await _ensurePublisherConnected(); | ||
| // | ||
| rtc.RTCDataChannel? publisherDataChannel(Reliability reliability) => | ||
| reliability == Reliability.reliable ? _reliableDCPub : _lossyDCPub; | ||
|
|
||
| rtc.RTCDataChannelState publisherDataChannelState( | ||
| Reliability reliability) => | ||
| publisherDataChannel(reliability)?.state ?? | ||
| rtc.RTCDataChannelState.RTCDataChannelClosed; | ||
|
|
||
| final reliability = packet.kind.toSDKType(); | ||
|
|
||
| // construct the data channel message | ||
| final message = | ||
| rtc.RTCDataChannelMessage.fromBinary(packet.writeToBuffer()); | ||
|
|
||
| // chose data channel | ||
| final rtc.RTCDataChannel? channel = | ||
| packet.kind == lk_models.DataPacket_Kind.LOSSY ? _lossyDC : _reliableDC; | ||
| if (_subscriberPrimary) { | ||
| // make sure publisher transport is connected | ||
|
|
||
| // send if channel exists | ||
| if (channel == null) { | ||
| throw UnexpectedStateException('Data channel is not ready'); | ||
| } | ||
| if (publisher?.pc.iceConnectionState?.isConnected() != true) { | ||
| logger.fine('Publisher is not connected...'); | ||
|
|
||
| logger.fine('sendDataPacket(label:${channel.label})'); | ||
| await channel.send(message); | ||
| } | ||
| // start negotiation | ||
| if (publisher?.pc.iceConnectionState != | ||
| rtc.RTCIceConnectionState.RTCIceConnectionStateChecking) { | ||
| await negotiate(); | ||
| } | ||
|
|
||
| Future<void> _ensurePublisherConnected() async { | ||
| logger.fine('ensurePublisherConnected()'); | ||
| if (!_subscriberPrimary) { | ||
| return; | ||
| } | ||
| logger.fine('Waiting for publisher to ice-connect...'); | ||
| await events.waitFor<EnginePublisherIceStateUpdatedEvent>( | ||
| filter: (event) => event.iceState.isConnected(), | ||
| duration: Timeouts.iceConnection, | ||
| ); | ||
| } | ||
|
|
||
| if (publisher?.pc.iceConnectionState?.isConnected() == true) { | ||
| logger.warning('[$objectId] publisher is already connected'); | ||
| return; | ||
| // wait for data channel to open (if not already) | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is where it waits for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there some callback to indicate data channel open failure if it fails for some reason?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| if (publisherDataChannelState(packet.kind.toSDKType()) != | ||
| rtc.RTCDataChannelState.RTCDataChannelOpen) { | ||
| logger.fine('Waiting for data channel ${reliability} to open...'); | ||
| await events.waitFor<PublisherDataChannelStateUpdatedEvent>( | ||
| filter: (event) => event.type == reliability, | ||
| duration: Timeouts.connection, | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| // start negotiation | ||
| await negotiate(); | ||
|
|
||
| logger.fine('[PUBLISHER] waiting for to ice-connect ' | ||
| '(current: ${publisher?.pc.iceConnectionState})'); | ||
| // chose data channel | ||
| final rtc.RTCDataChannel? channel = publisherDataChannel(reliability); | ||
|
|
||
| await events.waitFor<EnginePublisherIceStateUpdatedEvent>( | ||
| filter: (event) => event.iceState.isConnected(), | ||
| duration: Timeouts.iceConnection, | ||
| ); | ||
| if (channel == null) { | ||
| throw UnexpectedStateException( | ||
| 'Data channel for ${packet.kind.toSDKType()} is null'); | ||
| } | ||
|
|
||
| logger.fine('[PUBLISHER] connected'); | ||
| logger.fine('sendDataPacket(label:${channel.label})'); | ||
| await channel.send(message); | ||
| } | ||
|
|
||
| @internal | ||
|
|
@@ -421,11 +428,16 @@ class Engine extends Disposable with EventsEmittable<EngineEvent> { | |
| ..binaryType = 'binary' | ||
| ..ordered = true | ||
| ..maxRetransmits = 0; | ||
| _lossyDC = | ||
| _lossyDCPub = | ||
| await publisher?.pc.createDataChannel(_lossyDCLabel, lossyInit); | ||
| _lossyDC?.onMessage = _onDCMessage; | ||
| _lossyDC?.stateChangeStream | ||
| .listen((state) => _onDCStateUpdated(Reliability.lossy, state)); | ||
| _lossyDCPub?.onMessage = _onDCMessage; | ||
| _lossyDCPub?.stateChangeStream | ||
| .listen((state) => events.emit(PublisherDataChannelStateUpdatedEvent( | ||
| isPrimary: !_subscriberPrimary, | ||
| state: state, | ||
| type: Reliability.lossy, | ||
| ))); | ||
| // _onDCStateUpdated(Reliability.lossy, state) | ||
| } catch (_) { | ||
| logger.severe('[$objectId] createDataChannel() did throw $_'); | ||
| } | ||
|
|
@@ -434,11 +446,15 @@ class Engine extends Disposable with EventsEmittable<EngineEvent> { | |
| final reliableInit = rtc.RTCDataChannelInit() | ||
| ..binaryType = 'binary' | ||
| ..ordered = true; | ||
| _reliableDC = | ||
| _reliableDCPub = | ||
| await publisher?.pc.createDataChannel(_reliableDCLabel, reliableInit); | ||
| _reliableDC?.onMessage = _onDCMessage; | ||
| _reliableDC?.stateChangeStream | ||
| .listen((state) => _onDCStateUpdated(Reliability.reliable, state)); | ||
| _reliableDCPub?.onMessage = _onDCMessage; | ||
| _reliableDCPub?.stateChangeStream | ||
| .listen((state) => events.emit(PublisherDataChannelStateUpdatedEvent( | ||
| isPrimary: !_subscriberPrimary, | ||
| state: state, | ||
| type: Reliability.reliable, | ||
| ))); | ||
| } catch (_) { | ||
| logger.severe('[$objectId] createDataChannel() did throw $_'); | ||
| } | ||
|
|
@@ -450,29 +466,32 @@ class Engine extends Disposable with EventsEmittable<EngineEvent> { | |
| logger.fine('Server opened DC label: ${dc.label}'); | ||
| _reliableDCSub = dc; | ||
| _reliableDCSub?.onMessage = _onDCMessage; | ||
| _reliableDCSub?.stateChangeStream | ||
| .listen((state) => _onDCStateUpdated(Reliability.reliable, state)); | ||
| _reliableDCSub?.stateChangeStream.listen((state) => | ||
| _reliableDCPub?.stateChangeStream.listen( | ||
| (state) => events.emit(SubscriberDataChannelStateUpdatedEvent( | ||
| isPrimary: _subscriberPrimary, | ||
| state: state, | ||
| type: Reliability.reliable, | ||
| )))); | ||
| break; | ||
| case _lossyDCLabel: | ||
| logger.fine('Server opened DC label: ${dc.label}'); | ||
| _lossyDCSub = dc; | ||
| _lossyDCSub?.onMessage = _onDCMessage; | ||
| _lossyDCSub?.stateChangeStream | ||
| .listen((event) => _onDCStateUpdated(Reliability.lossy, event)); | ||
| _lossyDCSub?.stateChangeStream.listen((event) => | ||
| _reliableDCPub?.stateChangeStream.listen( | ||
| (state) => events.emit(SubscriberDataChannelStateUpdatedEvent( | ||
| isPrimary: _subscriberPrimary, | ||
| state: state, | ||
| type: Reliability.lossy, | ||
| )))); | ||
| break; | ||
| default: | ||
| logger.warning('Unknown DC label: ${dc.label}'); | ||
| break; | ||
| } | ||
| } | ||
|
|
||
| void _onDCStateUpdated( | ||
| Reliability channel, | ||
| rtc.RTCDataChannelState state, | ||
| ) { | ||
| logger.fine('Data channel state updated ${channel} ${state}'); | ||
| } | ||
|
|
||
| void _onDCMessage(rtc.RTCDataChannelMessage message) { | ||
| // always expect binary | ||
| if (!message.isBinary) { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What happens if there are a bunch of data channel messages sent by the app in this state? Are all of them becoming promises and get sent (promises resolved) when the data channel connects? Or are the messages just dropped on the floor?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
promises should be pending until
waitForcondition clears (received notification) or timeouts.actually maybe there should be more checks to prevent
negotiate()being called multiple times.. 🤔(not sure how immediate the state changes to
RTCIceConnectionStateChecking)