Pass adaptive_stream when connecting (#109)

* impl

* ignore fvm files

* Update pubspec.lock

* comment
This commit is contained in:
Hiroshi Horie
2022-04-26 02:57:24 +09:00
committed by GitHub
parent c43cd4b427
commit e23131fbbf
10 changed files with 84 additions and 52 deletions
+3
View File
@@ -80,3 +80,6 @@ build/
# Windows # Windows
_build _build
# FVM
.fvm/
+13 -5
View File
@@ -61,7 +61,9 @@ class Engine extends Disposable with EventsEmittable<EngineEvent> {
// remember url and token for reconnect // remember url and token for reconnect
String? url; String? url;
String? token; String? token;
ConnectOptions? connectOptions;
ConnectOptions connectOptions;
RoomOptions roomOptions;
bool _subscriberPrimary = false; bool _subscriberPrimary = false;
@@ -71,6 +73,8 @@ class Engine extends Disposable with EventsEmittable<EngineEvent> {
late final _signalListener = signalClient.createListener(synchronized: true); late final _signalListener = signalClient.createListener(synchronized: true);
Engine({ Engine({
required this.connectOptions,
required this.roomOptions,
SignalClient? signalClient, SignalClient? signalClient,
PeerConnectionCreate? peerConnectionCreate, PeerConnectionCreate? peerConnectionCreate,
}) : signalClient = signalClient ?? SignalClient(LiveKitWebSocket.connect), }) : signalClient = signalClient ?? SignalClient(LiveKitWebSocket.connect),
@@ -93,12 +97,15 @@ class Engine extends Disposable with EventsEmittable<EngineEvent> {
Future<void> connect( Future<void> connect(
String url, String url,
String token, String token, {
ConnectOptions? connectOptions, ConnectOptions? connectOptions,
) async { RoomOptions? roomOptions,
}) async {
this.url = url; this.url = url;
this.token = token; this.token = token;
this.connectOptions = connectOptions ?? const ConnectOptions(); // update new options (if exists)
this.connectOptions = connectOptions ?? this.connectOptions;
this.roomOptions = roomOptions ?? this.roomOptions;
_updateConnectionState(ConnectionState.connecting); _updateConnectionState(ConnectionState.connecting);
@@ -108,6 +115,7 @@ class Engine extends Disposable with EventsEmittable<EngineEvent> {
url, url,
token, token,
connectOptions: this.connectOptions, connectOptions: this.connectOptions,
roomOptions: this.roomOptions,
); );
// wait for join response // wait for join response
@@ -262,6 +270,7 @@ class Engine extends Disposable with EventsEmittable<EngineEvent> {
url!, url!,
token!, token!,
connectOptions: connectOptions, connectOptions: connectOptions,
roomOptions: roomOptions,
reconnect: true, reconnect: true,
); );
@@ -320,7 +329,6 @@ class Engine extends Disposable with EventsEmittable<EngineEvent> {
// RTCConfiguration? config; // RTCConfiguration? config;
// use server-provided iceServers if not provided by user // use server-provided iceServers if not provided by user
final connectOptions = this.connectOptions ?? const ConnectOptions();
final serverIceServers = final serverIceServers =
_serverProvidedIceServers.map((e) => e.toSDKType()).toList(); _serverProvidedIceServers.map((e) => e.toSDKType()).toList();
+19 -22
View File
@@ -27,8 +27,11 @@ import 'engine.dart';
/// * active speakers are different /// * active speakers are different
/// {@category Room} /// {@category Room}
class Room extends DisposableChangeNotifier with EventsEmittable<RoomEvent> { class Room extends DisposableChangeNotifier with EventsEmittable<RoomEvent> {
// expose engine's params
/// connection state of the room /// connection state of the room
ConnectionState get connectionState => engine.connectionState; ConnectionState get connectionState => engine.connectionState;
ConnectOptions get connectOptions => engine.connectOptions;
RoomOptions get roomOptions => engine.roomOptions;
/// map of SID to RemoteParticipant /// map of SID to RemoteParticipant
UnmodifiableMapView<String, RemoteParticipant> get participants => UnmodifiableMapView<String, RemoteParticipant> get participants =>
@@ -64,12 +67,6 @@ class Room extends DisposableChangeNotifier with EventsEmittable<RoomEvent> {
UnmodifiableListView<Participant>(_activeSpeakers); UnmodifiableListView<Participant>(_activeSpeakers);
List<Participant> _activeSpeakers = []; List<Participant> _activeSpeakers = [];
ConnectOptions? get connectOptions => _connectOptions;
ConnectOptions? _connectOptions;
RoomOptions? get roomOptions => _roomOptions;
RoomOptions? _roomOptions;
final Engine engine; final Engine engine;
// suppport for multiple event listeners // suppport for multiple event listeners
late final EventsListener<EngineEvent> _engineListener; late final EventsListener<EngineEvent> _engineListener;
@@ -77,12 +74,15 @@ class Room extends DisposableChangeNotifier with EventsEmittable<RoomEvent> {
late final EventsListener<SignalEvent> _signalListener; late final EventsListener<SignalEvent> _signalListener;
Room({ Room({
ConnectOptions? connectOptions, ConnectOptions connectOptions = const ConnectOptions(),
RoomOptions? roomOptions, RoomOptions roomOptions = const RoomOptions(),
Engine? engine, Engine? engine,
}) : _connectOptions = connectOptions, }) : engine = engine ??
_roomOptions = roomOptions, Engine(
engine = engine ?? Engine() { connectOptions: connectOptions,
roomOptions: roomOptions,
) {
//
_engineListener = this.engine.createListener(); _engineListener = this.engine.createListener();
_setUpEngineListeners(); _setUpEngineListeners();
@@ -116,13 +116,13 @@ class Room extends DisposableChangeNotifier with EventsEmittable<RoomEvent> {
String token, { String token, {
ConnectOptions? connectOptions, ConnectOptions? connectOptions,
RoomOptions? roomOptions, RoomOptions? roomOptions,
}) async { }) =>
// update options if provided engine.connect(
_connectOptions = connectOptions ?? _connectOptions; url,
_roomOptions = roomOptions ?? this.roomOptions; token,
connectOptions: connectOptions,
return engine.connect(url, token, this.connectOptions); roomOptions: roomOptions,
} );
void _setUpSignalListeners() => _signalListener void _setUpSignalListeners() => _signalListener
..on<SignalJoinResponseEvent>((event) { ..on<SignalJoinResponseEvent>((event) {
@@ -157,10 +157,8 @@ class Room extends DisposableChangeNotifier with EventsEmittable<RoomEvent> {
..on<SignalStreamStateUpdatedEvent>( ..on<SignalStreamStateUpdatedEvent>(
(event) => _onSignalStreamStateUpdateEvent(event.updates)) (event) => _onSignalStreamStateUpdateEvent(event.updates))
..on<SignalSubscribedQualityUpdatedEvent>((event) { ..on<SignalSubscribedQualityUpdatedEvent>((event) {
// Signal for Dynacast
final options = roomOptions ?? const RoomOptions();
// Dynacast is off or is unsupported // Dynacast is off or is unsupported
if (!options.dynacast || _serverVersion == '0.15.1') { if (!roomOptions.dynacast || _serverVersion == '0.15.1') {
logger.fine('Received subscribed quality update' logger.fine('Received subscribed quality update'
' but Dynacast is off or server version is not supported.'); ' but Dynacast is off or server version is not supported.');
return; return;
@@ -456,7 +454,6 @@ class Room extends DisposableChangeNotifier with EventsEmittable<RoomEvent> {
} }
Future<void> _sendSyncState() async { Future<void> _sendSyncState() async {
final connectOptions = this.connectOptions ?? const ConnectOptions();
final sendUnSub = connectOptions.autoSubscribe; final sendUnSub = connectOptions.autoSubscribe;
final participantTracks = final participantTracks =
participants.values.map((e) => e.participantTracks()); participants.values.map((e) => e.participantTracks());
+4 -1
View File
@@ -45,13 +45,15 @@ class SignalClient extends Disposable with EventsEmittable<SignalEvent> {
Future<void> connect( Future<void> connect(
String uriString, String uriString,
String token, { String token, {
ConnectOptions? connectOptions, required ConnectOptions connectOptions,
required RoomOptions roomOptions,
bool reconnect = false, bool reconnect = false,
}) async { }) async {
final rtcUri = await Utils.buildUri( final rtcUri = await Utils.buildUri(
uriString, uriString,
token: token, token: token,
connectOptions: connectOptions, connectOptions: connectOptions,
roomOptions: roomOptions,
reconnect: reconnect, reconnect: reconnect,
); );
@@ -85,6 +87,7 @@ class SignalClient extends Disposable with EventsEmittable<SignalEvent> {
uriString, uriString,
token: token, token: token,
connectOptions: connectOptions, connectOptions: connectOptions,
roomOptions: roomOptions,
validate: true, validate: true,
forceSecure: rtcUri.isSecureScheme, forceSecure: rtcUri.isSecureScheme,
); );
+7 -8
View File
@@ -50,14 +50,14 @@ class LocalParticipant extends Participant<LocalTrackPublication> {
// Use defaultPublishOptions if options is null // Use defaultPublishOptions if options is null
publishOptions = publishOptions =
publishOptions ?? room.roomOptions?.defaultAudioPublishOptions; publishOptions ?? room.roomOptions.defaultAudioPublishOptions;
final trackInfo = await room.engine.addTrack( final trackInfo = await room.engine.addTrack(
cid: track.getCid(), cid: track.getCid(),
name: track.name, name: track.name,
kind: track.kind, kind: track.kind,
source: track.source.toPBType(), source: track.source.toPBType(),
dtx: publishOptions?.dtx, dtx: publishOptions.dtx,
); );
await track.start(); await track.start();
@@ -105,7 +105,7 @@ class LocalParticipant extends Participant<LocalTrackPublication> {
// Use defaultPublishOptions if options is null // Use defaultPublishOptions if options is null
publishOptions = publishOptions =
publishOptions ?? room.roomOptions?.defaultVideoPublishOptions; publishOptions ?? room.roomOptions.defaultVideoPublishOptions;
// use constraints passed to getUserMedia by default // use constraints passed to getUserMedia by default
VideoDimensions dimensions = track.currentOptions.params.dimensions; VideoDimensions dimensions = track.currentOptions.params.dimensions;
@@ -202,8 +202,7 @@ class LocalParticipant extends Participant<LocalTrackPublication> {
final track = pub.track; final track = pub.track;
if (track != null) { if (track != null) {
final roomOptions = room.roomOptions ?? const RoomOptions(); if (room.roomOptions.stopLocalTrackOnUnpublish) {
if (roomOptions.stopLocalTrackOnUnpublish) {
await track.stop(); await track.stop();
} }
@@ -304,15 +303,15 @@ class LocalParticipant extends Participant<LocalTrackPublication> {
} else if (enabled) { } else if (enabled) {
if (source == TrackSource.camera) { if (source == TrackSource.camera) {
final track = await LocalVideoTrack.createCameraTrack( final track = await LocalVideoTrack.createCameraTrack(
room.roomOptions?.defaultCameraCaptureOptions); room.roomOptions.defaultCameraCaptureOptions);
return await publishVideoTrack(track); return await publishVideoTrack(track);
} else if (source == TrackSource.microphone) { } else if (source == TrackSource.microphone) {
final track = await LocalAudioTrack.create( final track = await LocalAudioTrack.create(
room.roomOptions?.defaultAudioCaptureOptions); room.roomOptions.defaultAudioCaptureOptions);
return await publishAudioTrack(track); return await publishAudioTrack(track);
} else if (source == TrackSource.screenShareVideo) { } else if (source == TrackSource.screenShareVideo) {
final track = await LocalVideoTrack.createScreenShareTrack( final track = await LocalVideoTrack.createScreenShareTrack(
room.roomOptions?.defaultScreenShareCaptureOptions); room.roomOptions.defaultScreenShareCaptureOptions);
return await publishVideoTrack(track); return await publishVideoTrack(track);
} }
} }
+1 -2
View File
@@ -10,7 +10,6 @@ import '../core/signal_client.dart';
import '../events.dart'; import '../events.dart';
import '../extensions.dart'; import '../extensions.dart';
import '../logger.dart'; import '../logger.dart';
import '../options.dart';
import '../participant/remote.dart'; import '../participant/remote.dart';
import '../proto/livekit_models.pb.dart' as lk_models; import '../proto/livekit_models.pb.dart' as lk_models;
import '../proto/livekit_rtc.pb.dart' as lk_rtc; import '../proto/livekit_rtc.pb.dart' as lk_rtc;
@@ -187,7 +186,7 @@ class RemoteTrackPublication<T extends RemoteTrack>
_cancelPendingTrackSettingsUpdateRequest?.call(); _cancelPendingTrackSettingsUpdateRequest?.call();
_visibilityTimer?.cancel(); _visibilityTimer?.cancel();
final roomOptions = participant.room.roomOptions ?? const RoomOptions(); final roomOptions = participant.room.roomOptions;
if (roomOptions.adaptiveStream && newValue is RemoteVideoTrack) { if (roomOptions.adaptiveStream && newValue is RemoteVideoTrack) {
// Start monitoring visibility // Start monitoring visibility
_visibilityTimer = Timer.periodic( _visibilityTimer = Timer.periodic(
+3 -3
View File
@@ -141,13 +141,12 @@ class Utils {
static Future<Uri> buildUri( static Future<Uri> buildUri(
String uriString, { String uriString, {
required String token, required String token,
ConnectOptions? connectOptions, required ConnectOptions connectOptions,
required RoomOptions roomOptions,
bool reconnect = false, bool reconnect = false,
bool validate = false, bool validate = false,
bool forceSecure = false, bool forceSecure = false,
}) async { }) async {
connectOptions ??= const ConnectOptions();
final Uri uri = Uri.parse(uriString); final Uri uri = Uri.parse(uriString);
final useSecure = uri.isSecureScheme || forceSecure; final useSecure = uri.isSecureScheme || forceSecure;
@@ -173,6 +172,7 @@ class Utils {
queryParameters: <String, String>{ queryParameters: <String, String>{
'access_token': token, 'access_token': token,
'auto_subscribe': connectOptions.autoSubscribe ? '1' : '0', 'auto_subscribe': connectOptions.autoSubscribe ? '1' : '0',
'adaptive_stream': roomOptions.adaptiveStream ? '1' : '0',
if (reconnect) 'reconnect': '1', if (reconnect) 'reconnect': '1',
'protocol': connectOptions.protocolVersion.toStringValue(), 'protocol': connectOptions.protocolVersion.toStringValue(),
'sdk': 'flutter', 'sdk': 'flutter',
+6 -6
View File
@@ -42,7 +42,7 @@ packages:
name: build name: build
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.2.1" version: "2.3.0"
built_collection: built_collection:
dependency: transitive dependency: transitive
description: description:
@@ -56,7 +56,7 @@ packages:
name: built_value name: built_value
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "8.1.4" version: "8.2.0"
characters: characters:
dependency: transitive dependency: transitive
description: description:
@@ -112,7 +112,7 @@ packages:
name: dart_style name: dart_style
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.2.2" version: "2.2.3"
dart_webrtc: dart_webrtc:
dependency: "direct main" dependency: "direct main"
description: description:
@@ -126,7 +126,7 @@ packages:
name: device_info_plus name: device_info_plus
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "3.2.2" version: "3.2.3"
device_info_plus_linux: device_info_plus_linux:
dependency: transitive dependency: transitive
description: description:
@@ -140,7 +140,7 @@ packages:
name: device_info_plus_macos name: device_info_plus_macos
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.2.2" version: "2.2.3"
device_info_plus_platform_interface: device_info_plus_platform_interface:
dependency: transitive dependency: transitive
description: description:
@@ -405,7 +405,7 @@ packages:
name: source_gen name: source_gen
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.2.1" version: "1.2.2"
source_span: source_span:
dependency: transitive dependency: transitive
description: description:
+22 -3
View File
@@ -12,6 +12,9 @@ import '../mock/test_data.dart';
import '../mock/websocket_mock.dart'; import '../mock/websocket_mock.dart';
void main() { void main() {
const connectOptions = ConnectOptions();
const roomOptions = RoomOptions();
late SignalClient client; late SignalClient client;
late MockWebSocketConnector connector; late MockWebSocketConnector connector;
setUp(() async { setUp(() async {
@@ -29,7 +32,12 @@ void main() {
predicate<SignalConnectionStateUpdatedEvent>( predicate<SignalConnectionStateUpdatedEvent>(
(event) => event.newState == ConnectionState.connected), (event) => event.newState == ConnectionState.connected),
])); ]));
await client.connect(exampleUri, token); await client.connect(
exampleUri,
token,
connectOptions: connectOptions,
roomOptions: roomOptions,
);
}); });
test('reconnect', () async { test('reconnect', () async {
expect( expect(
@@ -41,13 +49,24 @@ void main() {
event.newState == ConnectionState.connected && event.newState == ConnectionState.connected &&
event.didReconnect == true), event.didReconnect == true),
])); ]));
await client.connect(exampleUri, token, reconnect: true); await client.connect(
exampleUri,
token,
connectOptions: connectOptions,
roomOptions: roomOptions,
reconnect: true,
);
}); });
}); });
group('messaging', () { group('messaging', () {
test('join', () async { test('join', () async {
await client.connect(exampleUri, token); await client.connect(
exampleUri,
token,
connectOptions: connectOptions,
roomOptions: roomOptions,
);
expect(client.events.streamCtrl.stream, expect(client.events.streamCtrl.stream,
emits(isA<SignalJoinResponseEvent>())); emits(isA<SignalJoinResponseEvent>()));
connector.handlers?.onData!(joinResponse.writeToBuffer()); connector.handlers?.onData!(joinResponse.writeToBuffer());
+5 -1
View File
@@ -15,7 +15,11 @@ class E2EContainer {
wsConnector = MockWebSocketConnector(); wsConnector = MockWebSocketConnector();
client = SignalClient(wsConnector.connect); client = SignalClient(wsConnector.connect);
engine = Engine( engine = Engine(
signalClient: client, peerConnectionCreate: MockPeerConnection.create); signalClient: client,
peerConnectionCreate: MockPeerConnection.create,
connectOptions: const ConnectOptions(),
roomOptions: const RoomOptions(),
);
room = Room(engine: engine); room = Room(engine: engine);
} }