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

Commit

Permalink
Implement FFI callbacks bindings for Jason API (#195, #182)
Browse files Browse the repository at this point in the history
  • Loading branch information
evdokimovs authored Apr 29, 2021
1 parent 44e5aaf commit e01d09c
Show file tree
Hide file tree
Showing 41 changed files with 6,120 additions and 249 deletions.
326 changes: 161 additions & 165 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ RUST_VER := 1.51
CHROME_VERSION := 89.0
FIREFOX_VERSION := 87.0

CARGO_NDK_VER := 2.2.0-ndkr22b-rust$(RUST_VER)
CARGO_NDK_VER := 2.3.0-ndkr22b-rust$(RUST_VER)
ANDROID_NDK_TARGETS := arm64-v8a \
armeabi-v7a \
x86 \
Expand Down
144 changes: 134 additions & 10 deletions jason/flutter/example/integration_test/jason.dart
Original file line number Diff line number Diff line change
@@ -1,23 +1,28 @@
import 'dart:async';

import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:medea_jason/audio_track_constraints.dart';
import 'package:medea_jason/connection_handle.dart';
import 'package:medea_jason/device_video_track_constraints.dart';
import 'package:medea_jason/display_video_track_constraints.dart';
import 'package:medea_jason/jason.dart';
import 'package:medea_jason/kind.dart';
import 'package:medea_jason/device_video_track_constraints.dart';
import 'package:medea_jason/media_stream_settings.dart';
import 'package:medea_jason/display_video_track_constraints.dart';
import 'package:medea_jason/remote_media_track.dart';
import 'package:medea_jason/room_close_reason.dart';

void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();

// testWidgets('Jason', (WidgetTester tester) async {
// var jason = Jason();
// var room = jason.initRoom();
//
// expect(() => jason.mediaManager(), returnsNormally);
// expect(() => jason.closeRoom(room), returnsNormally);
// expect(() => jason.closeRoom(room), throwsStateError);
// });
testWidgets('Jason', (WidgetTester tester) async {
var jason = Jason();
var room = jason.initRoom();

expect(() => jason.mediaManager(), returnsNormally);
expect(() => jason.closeRoom(room), returnsNormally);
expect(() => jason.closeRoom(room), throwsStateError);
});

testWidgets('MediaManager', (WidgetTester tester) async {
var jason = Jason();
Expand Down Expand Up @@ -93,4 +98,123 @@ void main() {
settings.audio(constraints2);
expect(() => constraints2.deviceId('deviceId'), throwsStateError);
});

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

var allFired = List<Completer>.generate(4, (_) => Completer());

room.onClose((reason) {
allFired[0].complete();
});

room.onConnectionLoss((reconnectHandle) {
allFired[1].complete();
});

room.onLocalTrack((localTrack) {
allFired[2].complete();
});

room.onNewConnection((connection) {
allFired[3].complete();
});

await Future.wait(allFired.map((e) => e.future))
.timeout(Duration(seconds: 1));

room.free();

expect(() => room.onNewConnection((_) {}), throwsStateError);
});

testWidgets('RoomCloseReason', (WidgetTester tester) async {
var jason = Jason();
var room = jason.initRoom();
var reasonFut = Completer<RoomCloseReason>();

room.onClose((reason) {
reasonFut.complete(reason);
});

var reason = await reasonFut.future.timeout(Duration(seconds: 1));

expect(reason.reason(), equals('RoomCloseReason.reason'));
expect(reason.isClosedByServer(), equals(false));
expect(reason.isErr(), equals(true));
reason.free();
expect(() => reason.isErr(), throwsStateError);
});

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

var connFut = Completer<ConnectionHandle>();
room.onNewConnection((conn) {
connFut.complete(conn);
});
var conn = await connFut.future;

expect(conn.getRemoteMemberId(),
equals('ConnectionHandle.get_remote_member_id'));
var allFired = List<Completer>.generate(2, (_) => Completer());
conn.onQualityScoreUpdate((score) {
allFired[0].complete(score);
});
conn.onClose(() {
allFired[1].complete();
});

var res = await Future.wait(allFired.map((e) => e.future))
.timeout(Duration(seconds: 1));
expect(res[0], 4);
});

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

var connFut = Completer<ConnectionHandle>();
room.onNewConnection((conn) {
connFut.complete(conn);
});
var conn = await connFut.future;

var trackFut = Completer<RemoteMediaTrack>();
conn.onRemoteTrackAdded((remoteTrack) {
trackFut.complete(remoteTrack);
});

var track = await trackFut.future;

expect(track.enabled(), equals(true));
expect(track.muted(), equals(false));
expect(track.kind(), equals(MediaKind.Video));
expect(track.mediaSourceKind(), equals(MediaSourceKind.Device));

var allFired = List<Completer>.generate(5, (_) => Completer());
track.onEnabled(() {
allFired[0].complete();
});
track.onDisabled(() {
allFired[1].complete();
});
track.onMuted(() {
allFired[2].complete();
});
track.onUnmuted(() {
allFired[3].complete();
});
track.onStopped(() {
allFired[4].complete();
});

await Future.wait(allFired.map((e) => e.future))
.timeout(Duration(seconds: 1));

track.free();
expect(() => track.kind(), throwsStateError);
});
}
36 changes: 36 additions & 0 deletions jason/flutter/lib/connection_handle.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'dart:ffi';
import 'package:ffi/ffi.dart';

import 'jason.dart';
import 'remote_media_track.dart';
import 'util/move_semantic.dart';
import 'util/native_string.dart';
import 'util/nullable_pointer.dart';
Expand All @@ -13,12 +14,33 @@ typedef _getRemoteMemberId_Dart = Pointer<Utf8> Function(Pointer);
typedef _free_C = Void Function(Pointer);
typedef _free_Dart = void Function(Pointer);

typedef _onClose_C = Void Function(Pointer, Handle);
typedef _onClose_Dart = void Function(Pointer, void Function());

typedef _onRemoteTrackAdded_C = Void Function(Pointer, Handle);
typedef _onRemoteTrackAdded_Dart = void Function(
Pointer, void Function(Pointer));

typedef _onQualityScoreUpdate_C = Void Function(Pointer, Handle);
typedef _onQualityScoreUpdate_Dart = void Function(Pointer, void Function(int));

final _getRemoteMemberId =
dl.lookupFunction<_getRemoteMemberId_C, _getRemoteMemberId_Dart>(
'ConnectionHandle__get_remote_member_id');

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

final _onClose =
dl.lookupFunction<_onClose_C, _onClose_Dart>('ConnectionHandle__on_close');

final _onRemoteTrackAdded =
dl.lookupFunction<_onRemoteTrackAdded_C, _onRemoteTrackAdded_Dart>(
'ConnectionHandle__on_remote_track_added');

final _onQualityScoreUpdate =
dl.lookupFunction<_onQualityScoreUpdate_C, _onQualityScoreUpdate_Dart>(
'ConnectionHandle__on_quality_score_update');

class ConnectionHandle {
late NullablePointer ptr;

Expand All @@ -28,6 +50,20 @@ class ConnectionHandle {
return _getRemoteMemberId(ptr.getInnerPtr()).nativeStringToDartString();
}

void onClose(void Function() f) {
_onClose(ptr.getInnerPtr(), f);
}

void onRemoteTrackAdded(void Function(RemoteMediaTrack) f) {
_onRemoteTrackAdded(ptr.getInnerPtr(), (t) {
f(RemoteMediaTrack(NullablePointer(t)));
});
}

void onQualityScoreUpdate(void Function(int) f) {
_onQualityScoreUpdate(ptr.getInnerPtr(), f);
}

@moveSemantics
void free() {
_free(ptr.getInnerPtr());
Expand Down
27 changes: 25 additions & 2 deletions jason/flutter/lib/jason.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ library jason;

import 'dart:ffi';
import 'dart:io';

import 'media_manager.dart';
import 'room_handle.dart';
import 'util/move_semantic.dart';
import 'util/nullable_pointer.dart';
import 'util/callback.dart' as callback;

typedef _new_C = Pointer Function();
typedef _new_Dart = Pointer Function();
Expand Down Expand Up @@ -38,8 +40,29 @@ final _close_room =
final _free = dl.lookupFunction<_free_C, _free_Dart>('Jason__free');

DynamicLibrary _dl_load() {
if (Platform.isAndroid) return DynamicLibrary.open('libjason.so');
throw UnsupportedError('This platform is not supported.');
if (!Platform.isAndroid) {
throw UnsupportedError('This platform is not supported.');
}
if (NativeApi.majorVersion != 2) {
// If the DartVM we're running on does not have the same major version as
// this file was compiled against, refuse to initialize: the symbols are not
// compatible.
throw 'You are running unsupported NativeApi version.';
}

var dl = DynamicLibrary.open('libjason.so');

var initResult = dl.lookupFunction<
IntPtr Function(Pointer<Void>),
int Function(
Pointer<Void>)>('init_dart_api_dl')(NativeApi.initializeApiDLData);

if (initResult != 0) {
throw 'Failed to initialize Dart API. Code: $initResult';
}
callback.registerFunctions(dl);

return dl;
}

class Jason {
Expand Down
50 changes: 50 additions & 0 deletions jason/flutter/lib/remote_media_track.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,21 @@ typedef _mediaSourceKind_Dart = int Function(Pointer);
typedef _free_C = Void Function(Pointer);
typedef _free_Dart = void Function(Pointer);

typedef _onEnabled_C = Void Function(Pointer, Handle);
typedef _onEnabled_Dart = void Function(Pointer, void Function());

typedef _onDisabled_C = Void Function(Pointer, Handle);
typedef _onDisabled_Dart = void Function(Pointer, void Function());

typedef _onMuted_C = Void Function(Pointer, Handle);
typedef _onMuted_Dart = void Function(Pointer, void Function());

typedef _onUnmuted_C = Void Function(Pointer, Handle);
typedef _onUnmuted_Dart = void Function(Pointer, void Function());

typedef _onStopped_C = Void Function(Pointer, Handle);
typedef _onStopped_Dart = void Function(Pointer, void Function());

final _enabled =
dl.lookupFunction<_enabled_C, _enabled_Dart>('RemoteMediaTrack__enabled');

Expand All @@ -32,6 +47,21 @@ final _mediaSourceKind =
dl.lookupFunction<_mediaSourceKind_C, _mediaSourceKind_Dart>(
'RemoteMediaTrack__media_source_kind');

final _onEnabled = dl.lookupFunction<_onEnabled_C, _onEnabled_Dart>(
'RemoteMediaTrack__on_enabled');

final _onDisabled = dl.lookupFunction<_onDisabled_C, _onDisabled_Dart>(
'RemoteMediaTrack__on_disabled');

final _onMuted =
dl.lookupFunction<_onMuted_C, _onMuted_Dart>('RemoteMediaTrack__on_muted');

final _onUnmuted = dl.lookupFunction<_onUnmuted_C, _onUnmuted_Dart>(
'RemoteMediaTrack__on_unmuted');

final _onStopped = dl.lookupFunction<_onStopped_C, _onStopped_Dart>(
'RemoteMediaTrack__on_stopped');

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

class RemoteMediaTrack {
Expand All @@ -57,6 +87,26 @@ class RemoteMediaTrack {
return MediaSourceKind.values[index];
}

void onEnabled(void Function() f) {
_onEnabled(ptr.getInnerPtr(), f);
}

void onDisabled(void Function() f) {
_onDisabled(ptr.getInnerPtr(), f);
}

void onMuted(void Function() f) {
_onMuted(ptr.getInnerPtr(), f);
}

void onUnmuted(void Function() f) {
_onUnmuted(ptr.getInnerPtr(), f);
}

void onStopped(void Function() f) {
_onStopped(ptr.getInnerPtr(), f);
}

@moveSemantics
void free() {
_free(ptr.getInnerPtr());
Expand Down
Loading

0 comments on commit e01d09c

Please sign in to comment.