Skip to content
This repository has been archived by the owner on Aug 25, 2021. It is now read-only.

Commit

Permalink
Implement Dart Future FFI bindings for Jason API (#196, #182)
Browse files Browse the repository at this point in the history
  • Loading branch information
evdokimovs authored May 13, 2021
1 parent eca02f9 commit 57b7b71
Show file tree
Hide file tree
Showing 22 changed files with 1,338 additions and 103 deletions.
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions jason/.cargo/config
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
[target.wasm32-unknown-unknown]
runner = 'wasm-bindgen-test-runner'
[target.wasm32-unknown-unknown]
runner = 'wasm-bindgen-test-runner'
39 changes: 37 additions & 2 deletions jason/flutter/example/integration_test/jason.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import 'package:medea_jason/display_video_track_constraints.dart';
import 'package:medea_jason/jason.dart';
import 'package:medea_jason/track_kinds.dart';
import 'package:medea_jason/media_stream_settings.dart';
import 'package:medea_jason/reconnect_handle.dart';
import 'package:medea_jason/remote_media_track.dart';
import 'package:medea_jason/room_close_reason.dart';

Expand All @@ -28,8 +29,8 @@ void main() {
var jason = Jason();
var mediaManager = jason.mediaManager();

var devices = mediaManager.enumerateDevices();
var tracks = mediaManager.initLocalTracks(MediaStreamSettings());
var devices = await mediaManager.enumerateDevices();
var tracks = await mediaManager.initLocalTracks(MediaStreamSettings());

expect(devices.length, equals(3));
expect(tracks.length, equals(3));
Expand Down Expand Up @@ -217,4 +218,38 @@ void main() {
track.free();
expect(() => track.kind(), throwsStateError);
});

testWidgets('RoomHandle', (WidgetTester tester) async {
var jason = Jason();
var room = jason.initRoom();

await room.join('token');
await room.setLocalMediaSettings(MediaStreamSettings(), true, false);
await room.muteAudio();
await room.unmuteAudio();
await room.muteVideo(MediaSourceKind.Device);
await room.unmuteVideo(MediaSourceKind.Display);
await room.disableVideo(MediaSourceKind.Display);
await room.enableVideo(MediaSourceKind.Device);
await room.disableAudio();
await room.enableAudio();
await room.disableRemoteAudio();
await room.enableRemoteAudio();
await room.disableRemoteVideo();
await room.enableRemoteVideo();
});

testWidgets('ReconnectHandle', (WidgetTester tester) async {
var jason = Jason();
var room = jason.initRoom();

var handleFut = Completer<ReconnectHandle>();
room.onConnectionLoss((reconnectHandle) {
handleFut.complete(reconnectHandle);
});
var handle = await handleFut.future;

await handle.reconnectWithDelay(155);
await handle.reconnectWithBackoff(1, 2, 3);
});
}
2 changes: 2 additions & 0 deletions jason/flutter/lib/audio_track_constraints.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import 'jason.dart';
import 'util/move_semantic.dart';
import 'util/nullable_pointer.dart';

// TODO: Typedefs should have names that are not bound to concrete methods and
// moved to shared lib, so they can be reused and won't pollute things.
typedef _new_C = Pointer Function();
typedef _new_Dart = Pointer Function();

Expand Down
2 changes: 2 additions & 0 deletions jason/flutter/lib/jason.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import 'room_handle.dart';
import 'util/move_semantic.dart';
import 'util/nullable_pointer.dart';
import 'util/callback.dart' as callback;
import 'util/completer.dart' as completer;

typedef _new_C = Pointer Function();
typedef _new_Dart = Pointer Function();
Expand Down Expand Up @@ -61,6 +62,7 @@ DynamicLibrary _dl_load() {
throw 'Failed to initialize Dart API. Code: $initResult';
}
callback.registerFunctions(dl);
completer.registerFunctions(dl);

return dl;
}
Expand Down
22 changes: 14 additions & 8 deletions jason/flutter/lib/media_manager.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ import 'util/move_semantic.dart';
import 'util/nullable_pointer.dart';
import 'util/ptrarray.dart';

typedef _initLocalTracks_C = PtrArray Function(Pointer, Pointer);
typedef _initLocalTracks_Dart = PtrArray Function(Pointer, Pointer);
typedef _initLocalTracks_C = Handle Function(Pointer, Pointer);
typedef _initLocalTracks_Dart = Object Function(Pointer, Pointer);

typedef _enumerateDevices_C = PtrArray Function(Pointer);
typedef _enumerateDevices_Dart = PtrArray Function(Pointer);
typedef _enumerateDevices_C = Handle Function(Pointer);
typedef _enumerateDevices_Dart = Object Function(Pointer);

typedef _free_C = Void Function(Pointer);
typedef _free_Dart = void Function(Pointer);
Expand Down Expand Up @@ -46,17 +46,23 @@ class MediaManagerHandle {

/// Obtains [LocalMediaTrack]s objects from local media devices (or screen
/// capture) basing on the provided [MediaStreamSettings].
List<LocalMediaTrack> initLocalTracks(MediaStreamSettings caps) {
return _initLocalTracks(ptr.getInnerPtr(), caps.ptr.getInnerPtr())
Future<List<LocalMediaTrack>> initLocalTracks(
MediaStreamSettings caps) async {
PtrArray tracks =
await (_initLocalTracks(ptr.getInnerPtr(), caps.ptr.getInnerPtr())
as Future);
return tracks
.intoPointerList()
.map((e) => LocalMediaTrack(NullablePointer(e)))
.toList();
}

/// Returns a list of [InputDeviceInfo] objects representing available media
/// input devices, such as microphones, cameras, and so forth.
List<InputDeviceInfo> enumerateDevices() {
return _enumerateDevices(ptr.getInnerPtr())
Future<List<InputDeviceInfo>> enumerateDevices() async {
var fut = _enumerateDevices(ptr.getInnerPtr()) as Future;
PtrArray devices = await fut;
return devices
.intoPointerList()
.map((e) => InputDeviceInfo(NullablePointer(e)))
.toList();
Expand Down
48 changes: 48 additions & 0 deletions jason/flutter/lib/reconnect_handle.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,24 @@ import 'util/nullable_pointer.dart';
typedef _free_C = Void Function(Pointer);
typedef _free_Dart = void Function(Pointer);

typedef _reconnect_with_delay_C = Handle Function(Pointer, Int64);
typedef _reconnect_with_delay_Dart = Object Function(Pointer, int);

typedef _reconnect_with_backoff_C = Handle Function(
Pointer, Int64, Double, Int64);
typedef _reconnect_with_backoff_Dart = Object Function(
Pointer, int, double, int);

final _free = dl.lookupFunction<_free_C, _free_Dart>('ReconnectHandle__free');

final _reconnect_with_delay =
dl.lookupFunction<_reconnect_with_delay_C, _reconnect_with_delay_Dart>(
'ReconnectHandle__reconnect_with_delay');

final _reconnect_with_backoff =
dl.lookupFunction<_reconnect_with_backoff_C, _reconnect_with_backoff_Dart>(
'ReconnectHandle__reconnect_with_backoff');

/// External handle used to reconnect to a media server when connection is lost.
///
/// This handle is passed to the `RoomHandle.onConnectionLoss()` callback.
Expand All @@ -20,6 +36,38 @@ class ReconnectHandle {
/// provided [Pointer].
ReconnectHandle(this.ptr);

/// Tries to reconnect a `Room` after the provided delay in milliseconds.
///
/// If the `Room` is already reconnecting then new reconnection attempt won't
/// be performed. Instead, it will wait for the first reconnection attempt
/// result and use it here.
Future<void> reconnectWithDelay(int delayMs) async {
await (_reconnect_with_delay(ptr.getInnerPtr(), delayMs) as Future);
}

/// Tries to reconnect a `Room` in a loop with a growing backoff delay.
///
/// The first attempt to reconnect is guaranteed to happen not earlier than
/// `starting_delay_ms`.
///
/// Also, it guarantees that delay between reconnection attempts won't be
/// greater than `max_delay_ms`.
///
/// After each reconnection attempt, delay between reconnections will be
/// multiplied by the given `multiplier` until it reaches `max_delay_ms`.
///
/// If the `Room` is already reconnecting then new reconnection attempt won't
/// be performed. Instead, it will wait for the first reconnection attempt
/// result and use it here.
///
/// If `multiplier` is negative number then `multiplier` will be considered as
/// `0.0`.
Future<void> reconnectWithBackoff(
int startingDelayMs, double multiplier, int maxDelay) async {
await (_reconnect_with_backoff(
ptr.getInnerPtr(), startingDelayMs, multiplier, maxDelay) as Future);
}

/// Drops the associated Rust struct and nulls the local [Pointer] to it.
@moveSemantics
void free() {
Expand Down
Loading

0 comments on commit 57b7b71

Please sign in to comment.