Skip to content

Commit ecc7441

Browse files
authored
Merge branch 'main' into feat/data-stream
2 parents bf4f0bf + fa3802b commit ecc7441

File tree

8 files changed

+377
-308
lines changed

8 files changed

+377
-308
lines changed

example/lib/pages/connect.dart

-4
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,6 @@ class _ConnectPageState extends State<ConnectPage> {
4747
if (lkPlatformIs(PlatformType.android)) {
4848
_checkPermissions();
4949
}
50-
51-
if (lkPlatformIsMobile()) {
52-
LiveKitClient.initialize(bypassVoiceProcessing: true);
53-
}
5450
}
5551

5652
@override

lib/src/core/engine.dart

+48-62
Original file line numberDiff line numberDiff line change
@@ -23,28 +23,16 @@ import 'package:connectivity_plus/connectivity_plus.dart';
2323
import 'package:flutter_webrtc/flutter_webrtc.dart' as rtc;
2424
import 'package:meta/meta.dart';
2525

26-
import '../e2ee/options.dart';
27-
import '../events.dart';
28-
import '../exceptions.dart';
26+
import 'package:livekit_client/livekit_client.dart';
2927
import '../extensions.dart';
3028
import '../internal/events.dart';
3129
import '../internal/types.dart';
32-
import '../logger.dart';
33-
import '../managers/event.dart';
34-
import '../options.dart';
3530
import '../proto/livekit_models.pb.dart' as lk_models;
3631
import '../proto/livekit_rtc.pb.dart' as lk_rtc;
37-
import '../publication/local.dart';
3832
import '../support/disposable.dart';
39-
import '../support/platform.dart';
4033
import '../support/region_url_provider.dart';
4134
import '../support/websocket.dart';
42-
import '../track/local/video.dart';
4335
import '../types/internal.dart';
44-
import '../types/other.dart';
45-
import '../types/video_dimensions.dart';
46-
import '../utils.dart';
47-
import 'room.dart';
4836
import 'signal_client.dart';
4937
import 'transport.dart';
5038

@@ -143,6 +131,11 @@ class Engine extends Disposable with EventsEmittable<EngineEvent> {
143131
lk_models.DataPacket_Kind.LOSSY: false,
144132
};
145133

134+
List<lk_models.Codec>? _enabledPublishCodecs;
135+
136+
List<lk_models.Codec>? get enabledPublishCodecs => _enabledPublishCodecs;
137+
138+
146139
void clearReconnectTimeout() {
147140
if (reconnectTimeout != null) {
148141
reconnectTimeout?.cancel();
@@ -207,7 +200,7 @@ class Engine extends Disposable with EventsEmittable<EngineEvent> {
207200
);
208201

209202
// wait for join response
210-
await _signalListener.waitFor<SignalJoinResponseEvent>(
203+
await events.waitFor<EngineJoinResponseEvent>(
211204
duration: this.connectOptions.timeouts.connection,
212205
onTimeout: () => throw ConnectException(
213206
'Timed out waiting for SignalJoinResponseEvent',
@@ -254,56 +247,13 @@ class Engine extends Disposable with EventsEmittable<EngineEvent> {
254247
}
255248

256249
@internal
257-
Future<lk_models.TrackInfo> addTrack({
258-
required String cid,
259-
required String name,
260-
required lk_models.TrackType kind,
261-
required lk_models.TrackSource source,
262-
VideoDimensions? dimensions,
263-
bool? dtx,
264-
Iterable<lk_models.VideoLayer>? videoLayers,
265-
Iterable<lk_rtc.SimulcastCodec>? simulcastCodecs,
266-
String? sid,
267-
String? videoCodec,
268-
String? stream,
269-
bool? disableRed,
270-
}) async {
271-
// TODO: Check if cid already published
272-
273-
lk_models.Encryption_Type encryptionType = lk_models.Encryption_Type.NONE;
274-
if (roomOptions.e2eeOptions != null && !isSVCCodec(videoCodec ?? '')) {
275-
switch (roomOptions.e2eeOptions!.encryptionType) {
276-
case EncryptionType.kNone:
277-
encryptionType = lk_models.Encryption_Type.NONE;
278-
break;
279-
case EncryptionType.kGcm:
280-
encryptionType = lk_models.Encryption_Type.GCM;
281-
break;
282-
case EncryptionType.kCustom:
283-
encryptionType = lk_models.Encryption_Type.CUSTOM;
284-
break;
285-
}
286-
}
287-
250+
Future<lk_models.TrackInfo> addTrack(lk_rtc.AddTrackRequest req) async {
288251
// send request to add track
289-
signalClient.sendAddTrack(
290-
cid: cid,
291-
name: name,
292-
type: kind,
293-
source: source,
294-
dimensions: dimensions,
295-
dtx: dtx,
296-
videoLayers: videoLayers,
297-
encryptionType: encryptionType,
298-
simulcastCodecs: simulcastCodecs,
299-
sid: sid,
300-
stream: stream,
301-
disableRed: disableRed,
302-
);
252+
signalClient.sendAddTrack(req);
303253

304254
// wait for response, or timeout
305255
final event = await _signalListener.waitFor<SignalLocalTrackPublishedEvent>(
306-
filter: (event) => event.cid == cid,
256+
filter: (event) => event.cid == req.cid,
307257
duration: connectOptions.timeouts.publish,
308258
onTimeout: () => throw TrackPublishException(),
309259
);
@@ -987,10 +937,16 @@ class Engine extends Disposable with EventsEmittable<EngineEvent> {
987937
await _createPeerConnections(rtcConfiguration);
988938
}
989939

990-
if (!_subscriberPrimary) {
991-
// for subscriberPrimary, we negotiate when necessary (lazy)
940+
if (!_subscriberPrimary || event.response.fastPublish) {
941+
_enabledPublishCodecs = event.response.enabledPublishCodecs;
942+
943+
/// for subscriberPrimary, we negotiate when necessary (lazy)
944+
/// and if `response.fastPublish == true`, we need to negotiate
945+
/// immediately
992946
await negotiate();
993947
}
948+
949+
events.emit(EngineJoinResponseEvent(response: event.response));
994950
})
995951
..on<SignalReconnectResponseEvent>((event) async {
996952
var iceServersFromServer =
@@ -1153,11 +1109,41 @@ extension EnginePrivateMethods on Engine {
11531109
}
11541110

11551111
extension EngineInternalMethods on Engine {
1112+
@internal
1113+
Future<rtc.RTCRtpTransceiver> createTransceiverRTCRtpSender(
1114+
LocalTrack track,
1115+
PublishOptions opts,
1116+
List<rtc.RTCRtpEncoding>? encodings,
1117+
) async {
1118+
if (publisher == null) {
1119+
throw UnexpectedConnectionState('publisher is closed');
1120+
}
1121+
1122+
if (track.mediaStreamTrack.kind == 'video' && opts is VideoPublishOptions) {
1123+
track.codec = opts.videoCodec;
1124+
}
1125+
var transceiverInit = rtc.RTCRtpTransceiverInit(
1126+
direction: rtc.TransceiverDirection.SendOnly,
1127+
);
1128+
if (encodings != null) {
1129+
transceiverInit.sendEncodings = encodings;
1130+
}
1131+
final transceiver = await publisher!.pc.addTransceiver(
1132+
track: track.mediaStreamTrack,
1133+
kind: track is LocalVideoTrack
1134+
? rtc.RTCRtpMediaType.RTCRtpMediaTypeVideo
1135+
: rtc.RTCRtpMediaType.RTCRtpMediaTypeAudio,
1136+
init: transceiverInit,
1137+
);
1138+
return transceiver;
1139+
}
1140+
11561141
@internal
11571142
List<lk_rtc.DataChannelInfo> dataChannelInfo() => [
11581143
_reliableDCPub,
11591144
_lossyDCPub
11601145
].nonNulls.where((e) => e.id != -1).map((e) => e.toLKInfoType()).toList();
1146+
11611147
@internal
11621148
Future<rtc.RTCRtpSender> createSimulcastTransceiverSender(
11631149
LocalVideoTrack track,

lib/src/core/room.dart

+85-85
Original file line numberDiff line numberDiff line change
@@ -296,91 +296,6 @@ class Room extends DisposableChangeNotifier with EventsEmittable<RoomEvent> {
296296
}
297297

298298
void _setUpSignalListeners() => _signalListener
299-
..on<SignalJoinResponseEvent>((event) {
300-
_roomInfo = event.response.room;
301-
_name = event.response.room.name;
302-
_metadata = event.response.room.metadata;
303-
_serverVersion = event.response.serverVersion;
304-
_serverRegion = event.response.serverRegion;
305-
306-
if (_isRecording != event.response.room.activeRecording) {
307-
_isRecording = event.response.room.activeRecording;
308-
emitWhenConnected(
309-
RoomRecordingStatusChanged(activeRecording: _isRecording));
310-
}
311-
312-
logger.fine('[Engine] Received JoinResponse, '
313-
'serverVersion: ${event.response.serverVersion}');
314-
315-
_localParticipant ??= LocalParticipant(
316-
room: this,
317-
info: event.response.participant,
318-
);
319-
320-
if (engine.fullReconnectOnNext) {
321-
_localParticipant!.updateFromInfo(event.response.participant);
322-
}
323-
324-
if (connectOptions.protocolVersion.index >= ProtocolVersion.v8.index &&
325-
engine.fastConnectOptions != null &&
326-
!engine.fullReconnectOnNext) {
327-
var options = engine.fastConnectOptions!;
328-
329-
var audio = options.microphone;
330-
bool audioEnabled = audio.enabled == true || audio.track != null;
331-
if (audioEnabled) {
332-
if (audio.track != null) {
333-
_localParticipant!.publishAudioTrack(audio.track as LocalAudioTrack,
334-
publishOptions: roomOptions.defaultAudioPublishOptions);
335-
} else {
336-
_localParticipant!.setMicrophoneEnabled(true,
337-
audioCaptureOptions: roomOptions.defaultAudioCaptureOptions);
338-
}
339-
}
340-
341-
var video = options.camera;
342-
bool videoEnabled = video.enabled == true || video.track != null;
343-
if (videoEnabled) {
344-
if (video.track != null) {
345-
_localParticipant!.publishVideoTrack(video.track as LocalVideoTrack,
346-
publishOptions: roomOptions.defaultVideoPublishOptions);
347-
} else {
348-
_localParticipant!.setCameraEnabled(true,
349-
cameraCaptureOptions: roomOptions.defaultCameraCaptureOptions);
350-
}
351-
}
352-
353-
var screen = options.screen;
354-
bool screenEnabled = screen.enabled == true || screen.track != null;
355-
if (screenEnabled) {
356-
if (screen.track != null) {
357-
_localParticipant!.publishVideoTrack(
358-
screen.track as LocalVideoTrack,
359-
publishOptions: roomOptions.defaultVideoPublishOptions);
360-
} else {
361-
_localParticipant!.setScreenShareEnabled(true,
362-
screenShareCaptureOptions:
363-
roomOptions.defaultScreenShareCaptureOptions);
364-
}
365-
}
366-
}
367-
368-
for (final info in event.response.otherParticipants) {
369-
logger.fine(
370-
'Creating RemoteParticipant: sid = ${info.sid}(identity:${info.identity}) '
371-
'tracks:${info.tracks.map((e) => e.sid)}');
372-
_getOrCreateRemoteParticipant(info.identity, info);
373-
}
374-
375-
if (e2eeManager != null && event.response.sifTrailer.isNotEmpty) {
376-
e2eeManager!.keyProvider
377-
.setSifTrailer(Uint8List.fromList(event.response.sifTrailer));
378-
}
379-
380-
logger.fine('Room Connect completed');
381-
382-
events.emit(RoomConnectedEvent(room: this, metadata: _metadata));
383-
})
384299
..on<SignalParticipantUpdateEvent>(
385300
(event) => _onParticipantUpdateEvent(event.participants))
386301
..on<SignalSpeakersChangedEvent>(
@@ -482,6 +397,91 @@ class Room extends DisposableChangeNotifier with EventsEmittable<RoomEvent> {
482397
});
483398

484399
void _setUpEngineListeners() => _engineListener
400+
..on<EngineJoinResponseEvent>((event) {
401+
_roomInfo = event.response.room;
402+
_name = event.response.room.name;
403+
_metadata = event.response.room.metadata;
404+
_serverVersion = event.response.serverVersion;
405+
_serverRegion = event.response.serverRegion;
406+
407+
if (_isRecording != event.response.room.activeRecording) {
408+
_isRecording = event.response.room.activeRecording;
409+
emitWhenConnected(
410+
RoomRecordingStatusChanged(activeRecording: _isRecording));
411+
}
412+
413+
logger.fine('[Engine] Received JoinResponse, '
414+
'serverVersion: ${event.response.serverVersion}');
415+
416+
_localParticipant ??= LocalParticipant(
417+
room: this,
418+
info: event.response.participant,
419+
);
420+
421+
if (engine.fullReconnectOnNext) {
422+
_localParticipant!.updateFromInfo(event.response.participant);
423+
}
424+
425+
if (connectOptions.protocolVersion.index >= ProtocolVersion.v8.index &&
426+
engine.fastConnectOptions != null &&
427+
!engine.fullReconnectOnNext) {
428+
var options = engine.fastConnectOptions!;
429+
430+
var audio = options.microphone;
431+
bool audioEnabled = audio.enabled == true || audio.track != null;
432+
if (audioEnabled) {
433+
if (audio.track != null) {
434+
_localParticipant!.publishAudioTrack(audio.track as LocalAudioTrack,
435+
publishOptions: roomOptions.defaultAudioPublishOptions);
436+
} else {
437+
_localParticipant!.setMicrophoneEnabled(true,
438+
audioCaptureOptions: roomOptions.defaultAudioCaptureOptions);
439+
}
440+
}
441+
442+
var video = options.camera;
443+
bool videoEnabled = video.enabled == true || video.track != null;
444+
if (videoEnabled) {
445+
if (video.track != null) {
446+
_localParticipant!.publishVideoTrack(video.track as LocalVideoTrack,
447+
publishOptions: roomOptions.defaultVideoPublishOptions);
448+
} else {
449+
_localParticipant!.setCameraEnabled(true,
450+
cameraCaptureOptions: roomOptions.defaultCameraCaptureOptions);
451+
}
452+
}
453+
454+
var screen = options.screen;
455+
bool screenEnabled = screen.enabled == true || screen.track != null;
456+
if (screenEnabled) {
457+
if (screen.track != null) {
458+
_localParticipant!.publishVideoTrack(
459+
screen.track as LocalVideoTrack,
460+
publishOptions: roomOptions.defaultVideoPublishOptions);
461+
} else {
462+
_localParticipant!.setScreenShareEnabled(true,
463+
screenShareCaptureOptions:
464+
roomOptions.defaultScreenShareCaptureOptions);
465+
}
466+
}
467+
}
468+
469+
for (final info in event.response.otherParticipants) {
470+
logger.fine(
471+
'Creating RemoteParticipant: sid = ${info.sid}(identity:${info.identity}) '
472+
'tracks:${info.tracks.map((e) => e.sid)}');
473+
_getOrCreateRemoteParticipant(info.identity, info);
474+
}
475+
476+
if (e2eeManager != null && event.response.sifTrailer.isNotEmpty) {
477+
e2eeManager!.keyProvider
478+
.setSifTrailer(Uint8List.fromList(event.response.sifTrailer));
479+
}
480+
481+
logger.fine('Room Connect completed');
482+
483+
events.emit(RoomConnectedEvent(room: this, metadata: _metadata));
484+
})
485485
..on<EngineResumedEvent>((event) async {
486486
// re-send tracks permissions
487487
localParticipant?.sendTrackSubscriptionPermissions();

0 commit comments

Comments
 (0)