feat(calls): Щас попробую легонечко с data channel побурмалдить

This commit is contained in:
Jganenok
2026-06-12 22:19:57 +07:00
parent 42f1fd5292
commit d7dcb076c3
7 changed files with 77 additions and 87 deletions
+64
View File
@@ -84,6 +84,12 @@ class CallSession {
static const double _speakLevelOn = 0.05;
static const int _speakHoldTicks = 3;
RTCDataChannel? _probeChannel;
bool _peerIsKomet = false;
static const String _probeQuestion = 'AreYouKomet?';
static const String _probeAnswer = 'YesImKomet😎';
bool get localVideo => _localVideo;
bool get localScreen => _localScreen;
MediaStream? get localVideoStream => _localVideoStream;
@@ -104,6 +110,7 @@ class CallSession {
final _state = StreamController<CallSessionState>.broadcast();
final _remoteStream = StreamController<MediaStream>.broadcast();
final _info = StreamController<void>.broadcast();
final _kometDetected = StreamController<void>.broadcast();
Stream<CallSessionState> get stateStream => _state.stream;
Stream<MediaStream> get remoteStreamStream => _remoteStream.stream;
@@ -111,6 +118,9 @@ class CallSession {
Stream<void> get infoUpdates => _info.stream;
Stream<void> get peerKometDetected => _kometDetected.stream;
bool get peerIsKomet => _peerIsKomet;
bool get isMuted => _muted;
bool get peerMuted => _peerMuted;
bool get peerVideo => _peerVideo;
@@ -482,6 +492,8 @@ class CallSession {
init: RTCRtpTransceiverInit(direction: TransceiverDirection.RecvOnly),
);
await _setupKometProbe(pc);
if (role == CallRole.caller) {
_setState(CallSessionState.ringing);
await _createAndSendOffer();
@@ -499,6 +511,7 @@ class CallSession {
});
pc.onIceCandidate = _onLocalCandidate;
pc.onTrack = (event) => unawaited(_onRemoteTrack(event));
pc.onDataChannel = (channel) => _bindProbeChannel(channel, ask: false);
pc.onIceConnectionState = (s) => logger.t('[call] ice $s');
pc.onConnectionState = (s) {
logger.t('[call] pc $s');
@@ -534,10 +547,56 @@ class CallSession {
}
}
Future<void> _setupKometProbe(RTCPeerConnection pc) async {
if (_topology == 'SERVER') return;
try {
final channel = await pc.createDataChannel(
'komet',
RTCDataChannelInit()..ordered = true,
);
_probeChannel = channel;
_bindProbeChannel(channel, ask: true);
} catch (_) {}
}
void _bindProbeChannel(RTCDataChannel channel, {required bool ask}) {
channel.onMessage = (message) => _onProbeMessage(channel, message);
channel.onDataChannelState = (state) {
if (ask && state == RTCDataChannelState.RTCDataChannelOpen) {
_sendProbe(channel, _probeQuestion);
}
};
}
void _onProbeMessage(RTCDataChannel channel, RTCDataChannelMessage message) {
if (message.isBinary) return;
final text = message.text;
if (text == _probeQuestion) {
_sendProbe(channel, _probeAnswer);
} else if (text == _probeAnswer) {
_markPeerKomet();
}
}
void _sendProbe(RTCDataChannel channel, String text) {
try {
channel.send(RTCDataChannelMessage(text));
} catch (_) {}
}
void _markPeerKomet() {
if (_peerIsKomet) return;
_peerIsKomet = true;
logger.t('[call] peer is Komet');
if (!_kometDetected.isClosed) _kometDetected.add(null);
_notifyInfo();
}
Future<void> _setupSfu() async {
if (_pc != null) {
await _pc!.close();
_pc = null;
_probeChannel = null;
_remoteDescSet = false;
_pendingCandidates.clear();
for (final track in _localStream?.getTracks() ?? <MediaStreamTrack>[]) {
@@ -1018,6 +1077,10 @@ class CallSession {
Future<void> _dispose() async {
_levelTimer?.cancel();
try {
await _probeChannel?.close();
} catch (_) {}
_probeChannel = null;
for (final track in _localStream?.getTracks() ?? <MediaStreamTrack>[]) {
await track.stop();
}
@@ -1033,6 +1096,7 @@ class CallSession {
if (!_state.isClosed) await _state.close();
if (!_remoteStream.isClosed) await _remoteStream.close();
if (!_info.isClosed) await _info.close();
if (!_kometDetected.isClosed) await _kometDetected.close();
}
void _applyConnectionInfo(Map<String, dynamic> msg, List iceServers) {