diff --git a/jason/flutter/example/integration_test/jason.dart b/jason/flutter/example/integration_test/jason.dart index 916609c8f..366c15563 100644 --- a/jason/flutter/example/integration_test/jason.dart +++ b/jason/flutter/example/integration_test/jason.dart @@ -1,11 +1,96 @@ 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/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'; void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); - testWidgets('add test', (WidgetTester tester) async { - expect(add(2, 2), equals(4)); + // 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(); + var mediaManager = jason.mediaManager(); + + var devices = mediaManager.enumerateDevices(); + var tracks = mediaManager.initLocalTracks(MediaStreamSettings()); + + expect(devices.length, equals(3)); + expect(tracks.length, equals(3)); + + expect(devices.first.ptr.getInnerPtr(), + isNot(equals(devices.last.ptr.getInnerPtr()))); + expect(tracks.first.ptr.getInnerPtr(), + isNot(equals(tracks.last.ptr.getInnerPtr()))); + + expect(devices.first.deviceId(), equals('InputDeviceInfo.device_id')); + expect(devices.first.groupId(), equals('InputDeviceInfo.group_id')); + expect(devices.first.kind(), equals(MediaKind.Audio)); + expect(devices.first.label(), equals('InputDeviceInfo.label')); + + devices.first.free(); + expect(() => devices.first.label(), throwsStateError); + + expect(tracks.first.kind(), equals(MediaKind.Video)); + expect(tracks.first.mediaSourceKind(), equals(MediaSourceKind.Display)); + + tracks.first.free(); + expect(() => tracks.first.kind(), throwsStateError); + }); + + testWidgets('DeviceVideoTrackConstraints', (WidgetTester tester) async { + var constraints = DeviceVideoTrackConstraints(); + constraints.deviceId('deviceId'); + constraints.exactFacingMode(FacingMode.User); + constraints.idealFacingMode(FacingMode.Right); + constraints.exactHeight(444); + constraints.idealHeight(111); + constraints.heightInRange(55, 66); + constraints.exactWidth(444); + constraints.idealWidth(111); + constraints.widthInRange(55, 66); + constraints.free(); + expect(() => constraints.deviceId('deviceId'), throwsStateError); + + var constraints2 = DeviceVideoTrackConstraints(); + var settings = MediaStreamSettings(); + constraints2.deviceId('deviceId'); + settings.deviceVideo(constraints2); + expect(() => constraints2.deviceId('deviceId'), throwsStateError); + }); + + testWidgets('DisplayVideoTrackConstraints', (WidgetTester tester) async { + var constraints = DisplayVideoTrackConstraints(); + constraints.free(); + expect(() => constraints.free(), throwsStateError); + + var constraints2 = DisplayVideoTrackConstraints(); + var settings = MediaStreamSettings(); + settings.displayVideo(constraints2); + expect(() => settings.displayVideo(constraints2), throwsStateError); + }); + + testWidgets('AudioTrackConstraints', (WidgetTester tester) async { + var constraints = AudioTrackConstraints(); + constraints.deviceId('deviceId'); + constraints.free(); + expect(() => constraints.deviceId('deviceId'), throwsStateError); + + var constraints2 = AudioTrackConstraints(); + var settings = MediaStreamSettings(); + constraints2.deviceId('deviceId'); + settings.audio(constraints2); + expect(() => constraints2.deviceId('deviceId'), throwsStateError); }); } diff --git a/jason/flutter/example/lib/main.dart b/jason/flutter/example/lib/main.dart index dd1d21ffc..4209104a3 100644 --- a/jason/flutter/example/lib/main.dart +++ b/jason/flutter/example/lib/main.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:medea_jason/jason.dart'; void main() { runApp(MyApp()); @@ -11,12 +10,9 @@ class MyApp extends StatefulWidget { } class _MyAppState extends State { - int _sum = 0; - @override void initState() { super.initState(); - _sum = add(2, 2); } @override @@ -27,7 +23,7 @@ class _MyAppState extends State { title: const Text('Plugin example app'), ), body: Center( - child: Text('2 + 2 = $_sum\n'), + child: Text(''), ), ), ); diff --git a/jason/flutter/lib/audio_track_constraints.dart b/jason/flutter/lib/audio_track_constraints.dart new file mode 100644 index 000000000..66f857d9e --- /dev/null +++ b/jason/flutter/lib/audio_track_constraints.dart @@ -0,0 +1,42 @@ +import 'dart:ffi'; +import 'package:ffi/ffi.dart'; + +import 'jason.dart'; +import 'util/move_semantic.dart'; +import 'util/nullable_pointer.dart'; + +typedef _new_C = Pointer Function(); +typedef _new_Dart = Pointer Function(); + +typedef _deviceId_C = Void Function(Pointer, Pointer); +typedef _deviceId_Dart = void Function(Pointer, Pointer); + +typedef _free_C = Void Function(Pointer); +typedef _free_Dart = void Function(Pointer); + +final _new = dl.lookupFunction<_new_C, _new_Dart>('AudioTrackConstraints__new'); + +final _deviceId = dl.lookupFunction<_deviceId_C, _deviceId_Dart>( + 'AudioTrackConstraints__device_id'); + +final _free = + dl.lookupFunction<_free_C, _free_Dart>('AudioTrackConstraints__free'); + +class AudioTrackConstraints { + final NullablePointer ptr = NullablePointer(_new()); + + void deviceId(String deviceId) { + var deviceIdPtr = deviceId.toNativeUtf8(); + try { + _deviceId(ptr.getInnerPtr(), deviceIdPtr); + } finally { + calloc.free(deviceIdPtr); + } + } + + @moveSemantics + void free() { + _free(ptr.getInnerPtr()); + ptr.free(); + } +} diff --git a/jason/flutter/lib/connection_handle.dart b/jason/flutter/lib/connection_handle.dart new file mode 100644 index 000000000..71ae1755a --- /dev/null +++ b/jason/flutter/lib/connection_handle.dart @@ -0,0 +1,36 @@ +import 'dart:ffi'; + +import 'package:ffi/ffi.dart'; + +import 'jason.dart'; +import 'util/move_semantic.dart'; +import 'util/native_string.dart'; +import 'util/nullable_pointer.dart'; + +typedef _getRemoteMemberId_C = Pointer Function(Pointer); +typedef _getRemoteMemberId_Dart = Pointer Function(Pointer); + +typedef _free_C = Void Function(Pointer); +typedef _free_Dart = void Function(Pointer); + +final _getRemoteMemberId = + dl.lookupFunction<_getRemoteMemberId_C, _getRemoteMemberId_Dart>( + 'ConnectionHandle__get_remote_member_id'); + +final _free = dl.lookupFunction<_free_C, _free_Dart>('ConnectionHandle__free'); + +class ConnectionHandle { + late NullablePointer ptr; + + ConnectionHandle(this.ptr); + + String getRemoteMemberId() { + return _getRemoteMemberId(ptr.getInnerPtr()).nativeStringToDartString(); + } + + @moveSemantics + void free() { + _free(ptr.getInnerPtr()); + ptr.free(); + } +} diff --git a/jason/flutter/lib/device_video_track_constraints.dart b/jason/flutter/lib/device_video_track_constraints.dart new file mode 100644 index 000000000..91e0e5095 --- /dev/null +++ b/jason/flutter/lib/device_video_track_constraints.dart @@ -0,0 +1,132 @@ +import 'dart:ffi'; +import 'package:ffi/ffi.dart'; + +import 'jason.dart'; +import 'util/move_semantic.dart'; +import 'util/nullable_pointer.dart'; + +typedef _new_C = Pointer Function(); +typedef _new_Dart = Pointer Function(); + +typedef _deviceId_C = Void Function(Pointer, Pointer); +typedef _deviceId_Dart = void Function(Pointer, Pointer); + +typedef _exactFacingMode_C = Void Function(Pointer, Uint8); +typedef _exactFacingMode_Dart = void Function(Pointer, int); + +typedef _idealFacingMode_C = Void Function(Pointer, Uint8); +typedef _idealFacingMode_Dart = void Function(Pointer, int); + +typedef _exactHeight_C = Void Function(Pointer, Uint32); +typedef _exactHeight_Dart = void Function(Pointer, int); + +typedef _idealHeight_C = Void Function(Pointer, Uint32); +typedef _idealHeight_Dart = void Function(Pointer, int); + +typedef _heightInRange_C = Void Function(Pointer, Uint32, Uint32); +typedef _heightInRange_Dart = void Function(Pointer, int, int); + +typedef _exactWidth_C = Void Function(Pointer, Uint32); +typedef _exactWidth_Dart = void Function(Pointer, int); + +typedef _idealWidth_C = Void Function(Pointer, Uint32); +typedef _idealWidth_Dart = void Function(Pointer, int); + +typedef _widthInRange_C = Void Function(Pointer, Uint32, Uint32); +typedef _widthInRange_Dart = void Function(Pointer, int, int); + +typedef _free_C = Void Function(Pointer); +typedef _free_Dart = void Function(Pointer); + +final _new = + dl.lookupFunction<_new_C, _new_Dart>('DeviceVideoTrackConstraints__new'); + +final _deviceId = dl.lookupFunction<_deviceId_C, _deviceId_Dart>( + 'DeviceVideoTrackConstraints__device_id'); + +final _exactFacingMode = + dl.lookupFunction<_exactFacingMode_C, _exactFacingMode_Dart>( + 'DeviceVideoTrackConstraints__exact_facing_mode'); + +final _idealFacingMode = + dl.lookupFunction<_idealFacingMode_C, _idealFacingMode_Dart>( + 'DeviceVideoTrackConstraints__ideal_facing_mode'); + +final _exactHeight = dl.lookupFunction<_exactHeight_C, _exactHeight_Dart>( + 'DeviceVideoTrackConstraints__exact_height'); + +final _idealHeight = dl.lookupFunction<_idealHeight_C, _idealHeight_Dart>( + 'DeviceVideoTrackConstraints__ideal_height'); + +final _heightInRange = dl.lookupFunction<_heightInRange_C, _heightInRange_Dart>( + 'DeviceVideoTrackConstraints__height_in_range'); + +final _exactWidth = dl.lookupFunction<_exactWidth_C, _exactWidth_Dart>( + 'DeviceVideoTrackConstraints__exact_width'); + +final _idealWidth = dl.lookupFunction<_idealWidth_C, _idealWidth_Dart>( + 'DeviceVideoTrackConstraints__ideal_width'); + +final _widthInRange = dl.lookupFunction<_widthInRange_C, _widthInRange_Dart>( + 'DeviceVideoTrackConstraints__width_in_range'); + +final _free = + dl.lookupFunction<_free_C, _free_Dart>('DeviceVideoTrackConstraints__free'); + +enum FacingMode { + User, + Environment, + Left, + Right, +} + +class DeviceVideoTrackConstraints { + final NullablePointer ptr = NullablePointer(_new()); + + void deviceId(String deviceId) { + var deviceIdPtr = deviceId.toNativeUtf8(); + try { + _deviceId(ptr.getInnerPtr(), deviceIdPtr); + } finally { + calloc.free(deviceIdPtr); + } + } + + void exactFacingMode(FacingMode facingMode) { + _exactFacingMode(ptr.getInnerPtr(), facingMode.index); + } + + void idealFacingMode(FacingMode facingMode) { + _idealFacingMode(ptr.getInnerPtr(), facingMode.index); + } + + void exactHeight(int height) { + _exactHeight(ptr.getInnerPtr(), height); + } + + void idealHeight(int height) { + _idealHeight(ptr.getInnerPtr(), height); + } + + void heightInRange(int min, int max) { + _heightInRange(ptr.getInnerPtr(), min, max); + } + + void exactWidth(int width) { + _exactWidth(ptr.getInnerPtr(), width); + } + + void idealWidth(int width) { + _idealWidth(ptr.getInnerPtr(), width); + } + + void widthInRange(int min, int max) { + _widthInRange(ptr.getInnerPtr(), min, max); + } + + @moveSemantics + void free() { + _free(ptr.getInnerPtr()); + ptr.free(); + } +} diff --git a/jason/flutter/lib/display_video_track_constraints.dart b/jason/flutter/lib/display_video_track_constraints.dart new file mode 100644 index 000000000..9bf20cea2 --- /dev/null +++ b/jason/flutter/lib/display_video_track_constraints.dart @@ -0,0 +1,27 @@ +import 'dart:ffi'; + +import 'jason.dart'; +import 'util/move_semantic.dart'; +import 'util/nullable_pointer.dart'; + +typedef _new_C = Pointer Function(); +typedef _new_Dart = Pointer Function(); + +typedef _free_C = Void Function(Pointer); +typedef _free_Dart = void Function(Pointer); + +final _new = + dl.lookupFunction<_new_C, _new_Dart>('DisplayVideoTrackConstraints__new'); + +final _free_Dart _free = dl + .lookupFunction<_free_C, _free_Dart>('DisplayVideoTrackConstraints__free'); + +class DisplayVideoTrackConstraints { + final NullablePointer ptr = NullablePointer(_new()); + + @moveSemantics + void free() { + _free(ptr.getInnerPtr()); + ptr.free(); + } +} diff --git a/jason/flutter/lib/input_device_info.dart b/jason/flutter/lib/input_device_info.dart new file mode 100644 index 000000000..81c6b9a3b --- /dev/null +++ b/jason/flutter/lib/input_device_info.dart @@ -0,0 +1,66 @@ +import 'dart:ffi'; + +import 'package:ffi/ffi.dart'; + +import 'jason.dart'; +import 'kind.dart'; +import 'util/move_semantic.dart'; +import 'util/nullable_pointer.dart'; +import 'util/native_string.dart'; + +typedef _deviceId_C = Pointer Function(Pointer); +typedef _deviceId_Dart = Pointer Function(Pointer); + +typedef _label_C = Pointer Function(Pointer); +typedef _label_Dart = Pointer Function(Pointer); + +typedef _kind_C = Uint8 Function(Pointer); +typedef _kind_Dart = int Function(Pointer); + +typedef _nativeGroupId_C = Pointer Function(Pointer); +typedef _nativeGroupId_Dart = Pointer Function(Pointer); + +typedef _free_C = Void Function(Pointer); +typedef _free_Dart = void Function(Pointer); + +final _nativeGroupId = dl.lookupFunction<_nativeGroupId_C, _nativeGroupId_Dart>( + 'InputDeviceInfo__group_id'); + +final _kind = dl.lookupFunction<_kind_C, _kind_Dart>('InputDeviceInfo__kind'); + +final _label = + dl.lookupFunction<_label_C, _label_Dart>('InputDeviceInfo__label'); + +final _deviceId = dl + .lookupFunction<_deviceId_C, _deviceId_Dart>('InputDeviceInfo__device_id'); + +final _free = dl.lookupFunction<_free_C, _free_Dart>('InputDeviceInfo__free'); + +class InputDeviceInfo { + late NullablePointer ptr; + + InputDeviceInfo(this.ptr); + + String deviceId() { + return _deviceId(ptr.getInnerPtr()).nativeStringToDartString(); + } + + String label() { + return _label(ptr.getInnerPtr()).nativeStringToDartString(); + } + + MediaKind kind() { + var index = _kind(ptr.getInnerPtr()); + return MediaKind.values[index]; + } + + String groupId() { + return _nativeGroupId(ptr.getInnerPtr()).nativeStringToDartString(); + } + + @moveSemantics + void free() { + _free(ptr.getInnerPtr()); + ptr.free(); + } +} diff --git a/jason/flutter/lib/jason.dart b/jason/flutter/lib/jason.dart index 0c4bc9746..a5cb518c0 100644 --- a/jason/flutter/lib/jason.dart +++ b/jason/flutter/lib/jason.dart @@ -1,18 +1,66 @@ +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'; + +typedef _new_C = Pointer Function(); +typedef _new_Dart = Pointer Function(); + +typedef _mediaManager_C = Pointer Function(Pointer); +typedef _mediaManager_Dart = Pointer Function(Pointer); + +typedef _closeRoom_C = Void Function(Pointer, Pointer); +typedef _closeRoom_Dart = void Function(Pointer, Pointer); + +typedef _initRoom_C = Pointer Function(Pointer); +typedef _initRoom_Dart = Pointer Function(Pointer); + +typedef _free_C = Void Function(Pointer); +typedef _free_Dart = void Function(Pointer); + +final DynamicLibrary dl = _dl_load(); -typedef _add_C = Int64 Function(Int64 a, Int64 b); -typedef _add_Dart = int Function(int a, int b); +final _new = dl.lookupFunction<_new_C, _new_Dart>('Jason__new'); -final DynamicLibrary _dl = _load(); +final _media_manager = dl.lookupFunction<_mediaManager_C, _mediaManager_Dart>( + 'Jason__media_manager'); -final _add_Dart _add = _dl.lookupFunction<_add_C, _add_Dart>('add'); +final _initRoom = + dl.lookupFunction<_initRoom_C, _initRoom_Dart>('Jason__init_room'); -DynamicLibrary _load() { +final _close_room = + dl.lookupFunction<_closeRoom_C, _closeRoom_Dart>('Jason__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.'); } -int add(int a, int b) { - return _add(a, b); +class Jason { + final NullablePointer ptr = NullablePointer(_new()); + + MediaManager mediaManager() { + return MediaManager(NullablePointer(_media_manager(ptr.getInnerPtr()))); + } + + RoomHandle initRoom() { + return RoomHandle(NullablePointer(_initRoom(ptr.getInnerPtr()))); + } + + void closeRoom(@moveSemantics RoomHandle room) { + _close_room(ptr.getInnerPtr(), room.ptr.getInnerPtr()); + room.ptr.free(); + } + + @moveSemantics + void free() { + _free(ptr.getInnerPtr()); + ptr.free(); + } } diff --git a/jason/flutter/lib/kind.dart b/jason/flutter/lib/kind.dart new file mode 100644 index 000000000..4467dd03f --- /dev/null +++ b/jason/flutter/lib/kind.dart @@ -0,0 +1,9 @@ +enum MediaKind { + Audio, + Video, +} + +enum MediaSourceKind { + Device, + Display, +} diff --git a/jason/flutter/lib/local_media_track.dart b/jason/flutter/lib/local_media_track.dart new file mode 100644 index 000000000..e39f3c0ad --- /dev/null +++ b/jason/flutter/lib/local_media_track.dart @@ -0,0 +1,45 @@ +import 'dart:ffi'; + +import 'jason.dart'; +import 'kind.dart'; +import 'util/move_semantic.dart'; +import 'util/nullable_pointer.dart'; + +typedef _kind_C = Uint8 Function(Pointer); +typedef _kind_Dart = int Function(Pointer); + +typedef _mediaSourceKind_C = Uint8 Function(Pointer); +typedef _mediaSourceKind_Dart = int Function(Pointer); + +typedef _free_C = Void Function(Pointer); +typedef _free_Dart = void Function(Pointer); + +final _kind = dl.lookupFunction<_kind_C, _kind_Dart>('LocalMediaTrack__kind'); + +final _sourceKind = + dl.lookupFunction<_mediaSourceKind_C, _mediaSourceKind_Dart>( + 'LocalMediaTrack__media_source_kind'); + +final _free = dl.lookupFunction<_free_C, _free_Dart>('LocalMediaTrack__free'); + +class LocalMediaTrack { + late NullablePointer ptr; + + LocalMediaTrack(this.ptr); + + MediaKind kind() { + var index = _kind(ptr.getInnerPtr()); + return MediaKind.values[index]; + } + + MediaSourceKind mediaSourceKind() { + var index = _sourceKind(ptr.getInnerPtr()); + return MediaSourceKind.values[index]; + } + + @moveSemantics + void free() { + _free(ptr.getInnerPtr()); + ptr.free(); + } +} diff --git a/jason/flutter/lib/media_manager.dart b/jason/flutter/lib/media_manager.dart new file mode 100644 index 000000000..691d2c313 --- /dev/null +++ b/jason/flutter/lib/media_manager.dart @@ -0,0 +1,55 @@ +import 'dart:ffi'; + +import 'input_device_info.dart'; +import 'jason.dart'; +import 'local_media_track.dart'; +import 'media_stream_settings.dart'; +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 _enumerateDevices_C = PtrArray Function(Pointer); +typedef _enumerateDevices_Dart = PtrArray Function(Pointer); + +typedef _free_C = Void Function(Pointer); +typedef _free_Dart = void Function(Pointer); + +final _initLocalTracks = + dl.lookupFunction<_initLocalTracks_C, _initLocalTracks_Dart>( + 'MediaManagerHandle__init_local_tracks'); + +final _enumerateDevices = + dl.lookupFunction<_enumerateDevices_C, _enumerateDevices_Dart>( + 'MediaManagerHandle__enumerate_devices'); + +final _free = + dl.lookupFunction<_free_C, _free_Dart>('MediaManagerHandle__free'); + +class MediaManager { + late NullablePointer ptr; + + MediaManager(this.ptr); + + List initLocalTracks(MediaStreamSettings caps) { + return _initLocalTracks(ptr.getInnerPtr(), caps.ptr.getInnerPtr()) + .intoPointerList() + .map((e) => LocalMediaTrack(NullablePointer(e))) + .toList(); + } + + List enumerateDevices() { + return _enumerateDevices(ptr.getInnerPtr()) + .intoPointerList() + .map((e) => InputDeviceInfo(NullablePointer(e))) + .toList(); + } + + @moveSemantics + void free() { + _free(ptr.getInnerPtr()); + ptr.free(); + } +} diff --git a/jason/flutter/lib/media_stream_settings.dart b/jason/flutter/lib/media_stream_settings.dart new file mode 100644 index 000000000..8fc29f5c4 --- /dev/null +++ b/jason/flutter/lib/media_stream_settings.dart @@ -0,0 +1,62 @@ +import 'dart:ffi'; + +import 'audio_track_constraints.dart'; +import 'device_video_track_constraints.dart'; +import 'display_video_track_constraints.dart'; +import 'jason.dart'; +import 'util/move_semantic.dart'; +import 'util/nullable_pointer.dart'; + +typedef _new_C = Pointer Function(); +typedef _new_Dart = Pointer Function(); + +typedef _audio_C = Void Function(Pointer, Pointer); +typedef _audio_Dart = void Function(Pointer, Pointer); + +typedef _deviceVideo_C = Void Function(Pointer, Pointer); +typedef _deviceVideo_Dart = void Function(Pointer, Pointer); + +typedef _displayVideo_C = Void Function(Pointer, Pointer); +typedef _displayVideo_Dart = void Function(Pointer, Pointer); + +typedef _free_C = Void Function(Pointer); +typedef _free_Dart = void Function(Pointer); + +final _new = dl.lookupFunction<_new_C, _new_Dart>('MediaStreamSettings__new'); + +final _audio = + dl.lookupFunction<_audio_C, _audio_Dart>('MediaStreamSettings__audio'); + +final _deviceVideo = dl.lookupFunction<_deviceVideo_C, _deviceVideo_Dart>( + 'MediaStreamSettings__device_video'); + +final _displayVideo = dl.lookupFunction<_displayVideo_C, _displayVideo_Dart>( + 'MediaStreamSettings__display_video'); + +final _free = + dl.lookupFunction<_free_C, _free_Dart>('MediaStreamSettings__free'); + +class MediaStreamSettings { + final NullablePointer ptr = NullablePointer(_new()); + + void audio(@moveSemantics AudioTrackConstraints constraints) { + _audio(ptr.getInnerPtr(), constraints.ptr.getInnerPtr()); + constraints.ptr.free(); + } + + void deviceVideo(@moveSemantics DeviceVideoTrackConstraints constraints) { + _deviceVideo(ptr.getInnerPtr(), constraints.ptr.getInnerPtr()); + constraints.ptr.free(); + } + + void displayVideo(@moveSemantics DisplayVideoTrackConstraints constraints) { + _displayVideo(ptr.getInnerPtr(), constraints.ptr.getInnerPtr()); + constraints.ptr.free(); + } + + @moveSemantics + void free() { + _free(ptr.getInnerPtr()); + ptr.free(); + } +} diff --git a/jason/flutter/lib/reconnect_handle.dart b/jason/flutter/lib/reconnect_handle.dart new file mode 100644 index 000000000..f8b8d4c2d --- /dev/null +++ b/jason/flutter/lib/reconnect_handle.dart @@ -0,0 +1,22 @@ +import 'dart:ffi'; + +import 'jason.dart'; +import 'util/move_semantic.dart'; +import 'util/nullable_pointer.dart'; + +typedef _free_C = Void Function(Pointer); +typedef _free_Dart = void Function(Pointer); + +final _free = dl.lookupFunction<_free_C, _free_Dart>('ReconnectHandle__free'); + +class ReconnectHandle { + late NullablePointer ptr; + + ReconnectHandle(this.ptr); + + @moveSemantics + void free() { + _free(ptr.getInnerPtr()); + ptr.free(); + } +} diff --git a/jason/flutter/lib/remote_media_track.dart b/jason/flutter/lib/remote_media_track.dart new file mode 100644 index 000000000..1ee22ce31 --- /dev/null +++ b/jason/flutter/lib/remote_media_track.dart @@ -0,0 +1,65 @@ +import 'dart:ffi'; + +import 'jason.dart'; +import 'kind.dart'; +import 'util/move_semantic.dart'; +import 'util/nullable_pointer.dart'; + +typedef _enabled_C = Uint8 Function(Pointer); +typedef _enabled_Dart = int Function(Pointer); + +typedef _muted_C = Uint8 Function(Pointer); +typedef _muted_Dart = int Function(Pointer); + +typedef _kind_C = Uint8 Function(Pointer); +typedef _kind_Dart = int Function(Pointer); + +typedef _mediaSourceKind_C = Uint8 Function(Pointer); +typedef _mediaSourceKind_Dart = int Function(Pointer); + +typedef _free_C = Void Function(Pointer); +typedef _free_Dart = void Function(Pointer); + +final _enabled = + dl.lookupFunction<_enabled_C, _enabled_Dart>('RemoteMediaTrack__enabled'); + +final _muted = + dl.lookupFunction<_muted_C, _muted_Dart>('RemoteMediaTrack__muted'); + +final _kind = dl.lookupFunction<_kind_C, _kind_Dart>('RemoteMediaTrack__kind'); + +final _mediaSourceKind = + dl.lookupFunction<_mediaSourceKind_C, _mediaSourceKind_Dart>( + 'RemoteMediaTrack__media_source_kind'); + +final _free = dl.lookupFunction<_free_C, _free_Dart>('RemoteMediaTrack__free'); + +class RemoteMediaTrack { + late NullablePointer ptr; + + RemoteMediaTrack(this.ptr); + + bool enabled() { + return _enabled(ptr.getInnerPtr()) > 0; + } + + bool muted() { + return _muted(ptr.getInnerPtr()) > 0; + } + + MediaKind kind() { + var index = _kind(ptr.getInnerPtr()); + return MediaKind.values[index]; + } + + MediaSourceKind mediaSourceKind() { + var index = _mediaSourceKind(ptr.getInnerPtr()); + return MediaSourceKind.values[index]; + } + + @moveSemantics + void free() { + _free(ptr.getInnerPtr()); + ptr.free(); + } +} diff --git a/jason/flutter/lib/room_close_reason.dart b/jason/flutter/lib/room_close_reason.dart new file mode 100644 index 000000000..3f5d9f9ff --- /dev/null +++ b/jason/flutter/lib/room_close_reason.dart @@ -0,0 +1,55 @@ +import 'dart:ffi'; +import 'package:ffi/ffi.dart'; + +import 'jason.dart'; +import 'util/move_semantic.dart'; +import 'util/native_string.dart'; +import 'util/nullable_pointer.dart'; + +typedef _reason_C = Pointer Function(Pointer); +typedef _reason_Dart = Pointer Function(Pointer); + +typedef _isClosedByServer_C = Int8 Function(Pointer); +typedef _isClosedByServer_Dart = int Function(Pointer); + +typedef _isErr_C = Int8 Function(Pointer); +typedef _isErr_Dart = int Function(Pointer); + +typedef _free_C = Void Function(Pointer); +typedef _free_Dart = void Function(Pointer); + +final _reason = + dl.lookupFunction<_reason_C, _reason_Dart>('RoomCloseReason__reason'); + +final _isClosedByServer = + dl.lookupFunction<_isClosedByServer_C, _isClosedByServer_Dart>( + 'RoomCloseReason__is_closed_by_server'); + +final _isErr = + dl.lookupFunction<_isErr_C, _isErr_Dart>('RoomCloseReason__is_err'); + +final _free = dl.lookupFunction<_free_C, _free_Dart>('RoomCloseReason__free'); + +class RoomCloseReason { + late NullablePointer ptr; + + RoomCloseReason(this.ptr); + + String reason() { + return _reason(ptr.getInnerPtr()).nativeStringToDartString(); + } + + bool isClosedByServer() { + return _isClosedByServer(ptr.getInnerPtr()) > 0; + } + + bool isErr() { + return _isErr(ptr.getInnerPtr()) > 0; + } + + @moveSemantics + void free() { + _free(ptr.getInnerPtr()); + ptr.free(); + } +} diff --git a/jason/flutter/lib/room_handle.dart b/jason/flutter/lib/room_handle.dart new file mode 100644 index 000000000..3314a198f --- /dev/null +++ b/jason/flutter/lib/room_handle.dart @@ -0,0 +1,23 @@ +import 'dart:ffi'; + +import 'package:medea_jason/util/nullable_pointer.dart'; + +import 'jason.dart'; +import 'util/move_semantic.dart'; + +typedef _free_C = Void Function(Pointer); +typedef _free_Dart = void Function(Pointer); + +final _free = dl.lookupFunction<_free_C, _free_Dart>('RoomHandle__free'); + +class RoomHandle { + late NullablePointer ptr; + + RoomHandle(this.ptr); + + @moveSemantics + void free() { + _free(ptr.getInnerPtr()); + ptr.free(); + } +} diff --git a/jason/flutter/lib/util/move_semantic.dart b/jason/flutter/lib/util/move_semantic.dart new file mode 100644 index 000000000..910e99b15 --- /dev/null +++ b/jason/flutter/lib/util/move_semantic.dart @@ -0,0 +1,5 @@ +class MoveSemantics { + const MoveSemantics(); +} + +const MoveSemantics moveSemantics = MoveSemantics(); diff --git a/jason/flutter/lib/util/native_string.dart b/jason/flutter/lib/util/native_string.dart new file mode 100644 index 000000000..ba9b219b8 --- /dev/null +++ b/jason/flutter/lib/util/native_string.dart @@ -0,0 +1,20 @@ +import 'dart:ffi'; + +import 'package:ffi/ffi.dart'; +import 'package:medea_jason/jason.dart'; + +typedef _free_C = Void Function(Pointer); +typedef _free_Dart = void Function(Pointer); + +final _free = dl.lookupFunction<_free_C, _free_Dart>('String_free'); + +extension RustStringPointer on Pointer { + /// Frees an underlying native memory, so it can only be called once. + String nativeStringToDartString() { + try { + return toDartString(); + } finally { + _free(this); + } + } +} diff --git a/jason/flutter/lib/util/nullable_pointer.dart b/jason/flutter/lib/util/nullable_pointer.dart new file mode 100644 index 000000000..e641fdd53 --- /dev/null +++ b/jason/flutter/lib/util/nullable_pointer.dart @@ -0,0 +1,24 @@ +import 'dart:ffi'; + +class NullablePointer { + Pointer? _ptr; + + NullablePointer(Pointer ptr) { + if (ptr.address == 0) { + throw ArgumentError.notNull('ptr'); + } + _ptr = ptr; + } + + Pointer getInnerPtr() { + if (_ptr == null) { + throw StateError('NullablePointer cannot be used after free.'); + } else { + return Pointer.fromAddress(_ptr!.address); + } + } + + void free() { + _ptr = null; + } +} diff --git a/jason/flutter/lib/util/ptrarray.dart b/jason/flutter/lib/util/ptrarray.dart new file mode 100644 index 000000000..436ce41eb --- /dev/null +++ b/jason/flutter/lib/util/ptrarray.dart @@ -0,0 +1,28 @@ +import 'dart:ffi'; + +import '../jason.dart'; + +typedef _free_C = Void Function(PtrArray); +typedef _free_Dart = void Function(PtrArray); + +final _free_Dart _free = + dl.lookupFunction<_free_C, _free_Dart>('PtrArray_free'); + +class PtrArray extends Struct { + @Uint64() + external int _len; + external Pointer _arr; + + /// Frees an underlying native memory, so it can only be called once. + List intoPointerList() { + try { + var out = List.empty(growable: true); + for (var i = 0; i < _len; i++) { + out.add(_arr.elementAt(i)); + } + return out; + } finally { + _free(this); + } + } +} diff --git a/jason/jason-dummy/Cargo.toml b/jason/jason-dummy/Cargo.toml index 890c625ca..2c006ddaf 100644 --- a/jason/jason-dummy/Cargo.toml +++ b/jason/jason-dummy/Cargo.toml @@ -9,5 +9,6 @@ name = "jason" crate-type = ["staticlib", "cdylib"] [dependencies] +libc = "0.2" [workspace] diff --git a/jason/jason-dummy/src/audio_track_constraints.rs b/jason/jason-dummy/src/audio_track_constraints.rs new file mode 100644 index 000000000..e5714a019 --- /dev/null +++ b/jason/jason-dummy/src/audio_track_constraints.rs @@ -0,0 +1,33 @@ +use crate::utils::c_str_into_string; + +pub struct AudioTrackConstraints; + +impl AudioTrackConstraints { + pub fn new() -> Self { + Self + } + + pub fn device_id(&mut self, _: String) {} +} + +#[no_mangle] +pub extern "C" fn AudioTrackConstraints__new() -> *const AudioTrackConstraints { + Box::into_raw(Box::new(AudioTrackConstraints::new())) +} + +#[no_mangle] +pub unsafe extern "C" fn AudioTrackConstraints__device_id( + this: *mut AudioTrackConstraints, + device_id: *const libc::c_char, +) { + let this = this.as_mut().unwrap(); + + this.device_id(c_str_into_string(device_id)) +} + +#[no_mangle] +pub unsafe extern "C" fn AudioTrackConstraints__free( + this: *mut AudioTrackConstraints, +) { + Box::from_raw(this); +} diff --git a/jason/jason-dummy/src/connection_handle.rs b/jason/jason-dummy/src/connection_handle.rs new file mode 100644 index 000000000..549e66147 --- /dev/null +++ b/jason/jason-dummy/src/connection_handle.rs @@ -0,0 +1,29 @@ +use crate::utils::string_into_c_str; + +pub struct ConnectionHandle; + +impl ConnectionHandle { + pub fn get_remote_member_id(&self) -> String { + // Result + String::from("ConnectionHandle.get_remote_member_id") + } + + // pub fn on_close(&self, f: Callback<()>) -> Result<(), JasonError> { } + // pub fn on_remote_track_added(&self, f: Callback) -> + // Result<(), JasonError> { } pub fn on_quality_score_update(&self, f: + // Callback) -> Result<(), JasonError> {} +} + +#[no_mangle] +pub unsafe extern "C" fn ConnectionHandle__get_remote_member_id( + this: *const ConnectionHandle, +) -> *const libc::c_char { + let this = this.as_ref().unwrap(); + + string_into_c_str(this.get_remote_member_id()) +} + +#[no_mangle] +pub unsafe extern "C" fn ConnectionHandle__free(this: *mut ConnectionHandle) { + Box::from_raw(this); +} diff --git a/jason/jason-dummy/src/device_video_track_constraints.rs b/jason/jason-dummy/src/device_video_track_constraints.rs new file mode 100644 index 000000000..d0080b2b6 --- /dev/null +++ b/jason/jason-dummy/src/device_video_track_constraints.rs @@ -0,0 +1,155 @@ +use std::convert::TryFrom; + +use crate::utils::c_str_into_string; + +pub struct DeviceVideoTrackConstraints; + +impl DeviceVideoTrackConstraints { + pub fn new() -> Self { + Self + } + + pub fn device_id(&mut self, _device_id: String) {} + + pub fn exact_facing_mode(&mut self, _facing_mode: FacingMode) {} + + pub fn ideal_facing_mode(&mut self, _facing_mode: FacingMode) {} + + pub fn exact_height(&mut self, _height: u32) {} + + pub fn ideal_height(&mut self, _height: u32) {} + + pub fn height_in_range(&mut self, _min: u32, _max: u32) {} + + pub fn exact_width(&mut self, _width: u32) {} + + pub fn ideal_width(&mut self, _width: u32) {} + + pub fn width_in_range(&mut self, _min: u32, _max: u32) {} +} + +pub enum FacingMode { + User, + Environment, + Left, + Right, +} + +impl From for FacingMode { + fn from(value: u8) -> Self { + match value { + 0 => FacingMode::User, + 1 => FacingMode::Environment, + 2 => FacingMode::Left, + 3 => FacingMode::Right, + _ => { + unreachable!() + } + } + } +} + +#[no_mangle] +pub extern "C" fn DeviceVideoTrackConstraints__new( +) -> *const DeviceVideoTrackConstraints { + Box::into_raw(Box::new(DeviceVideoTrackConstraints::new())) +} + +#[no_mangle] +pub unsafe extern "C" fn DeviceVideoTrackConstraints__device_id( + this: *mut DeviceVideoTrackConstraints, + device_id: *const libc::c_char, +) { + let this = this.as_mut().unwrap(); + + this.device_id(c_str_into_string(device_id)); +} + +#[no_mangle] +pub unsafe extern "C" fn DeviceVideoTrackConstraints__exact_facing_mode( + this: *mut DeviceVideoTrackConstraints, + facing_mode: u8, +) { + let this = this.as_mut().unwrap(); + + this.exact_facing_mode(FacingMode::try_from(facing_mode).unwrap()); +} + +#[no_mangle] +pub unsafe extern "C" fn DeviceVideoTrackConstraints__ideal_facing_mode( + this: *mut DeviceVideoTrackConstraints, + facing_mode: u8, +) { + let this = this.as_mut().unwrap(); + + this.ideal_facing_mode(FacingMode::try_from(facing_mode).unwrap()); +} + +#[no_mangle] +pub unsafe extern "C" fn DeviceVideoTrackConstraints__exact_height( + this: *mut DeviceVideoTrackConstraints, + height: u32, +) { + let this = this.as_mut().unwrap(); + + this.exact_height(height); +} + +#[no_mangle] +pub unsafe extern "C" fn DeviceVideoTrackConstraints__ideal_height( + this: *mut DeviceVideoTrackConstraints, + height: u32, +) { + let this = this.as_mut().unwrap(); + + this.ideal_height(height); +} + +#[no_mangle] +pub unsafe extern "C" fn DeviceVideoTrackConstraints__height_in_range( + this: *mut DeviceVideoTrackConstraints, + min: u32, + max: u32, +) { + let this = this.as_mut().unwrap(); + + this.height_in_range(min, max); +} + +#[no_mangle] +pub unsafe extern "C" fn DeviceVideoTrackConstraints__exact_width( + this: *mut DeviceVideoTrackConstraints, + width: u32, +) { + let this = this.as_mut().unwrap(); + + this.exact_width(width); +} + +#[no_mangle] +pub unsafe extern "C" fn DeviceVideoTrackConstraints__ideal_width( + this: *mut DeviceVideoTrackConstraints, + width: u32, +) { + let this = this.as_mut().unwrap(); + + this.ideal_width(width); +} + +#[no_mangle] +pub unsafe extern "C" fn DeviceVideoTrackConstraints__width_in_range( + this: *mut DeviceVideoTrackConstraints, + min: u32, + max: u32, +) { + let this = this.as_mut().unwrap(); + + this.width_in_range(min, max); +} + +#[no_mangle] +pub unsafe extern "C" fn DeviceVideoTrackConstraints__free( + this: *mut DeviceVideoTrackConstraints, +) { + Box::from_raw(this); +} diff --git a/jason/jason-dummy/src/display_video_track_constraints.rs b/jason/jason-dummy/src/display_video_track_constraints.rs new file mode 100644 index 000000000..ace2c91b4 --- /dev/null +++ b/jason/jason-dummy/src/display_video_track_constraints.rs @@ -0,0 +1,20 @@ +pub struct DisplayVideoTrackConstraints; + +impl DisplayVideoTrackConstraints { + fn new() -> Self { + Self + } +} + +#[no_mangle] +pub extern "C" fn DisplayVideoTrackConstraints__new( +) -> *const DisplayVideoTrackConstraints { + Box::into_raw(Box::new(DisplayVideoTrackConstraints::new())) +} + +#[no_mangle] +pub unsafe extern "C" fn DisplayVideoTrackConstraints__free( + this: *mut DisplayVideoTrackConstraints, +) { + Box::from_raw(this); +} diff --git a/jason/jason-dummy/src/input_device_info.rs b/jason/jason-dummy/src/input_device_info.rs new file mode 100644 index 000000000..93d5bbf79 --- /dev/null +++ b/jason/jason-dummy/src/input_device_info.rs @@ -0,0 +1,62 @@ +use crate::{utils::string_into_c_str, MediaKind}; + +pub struct InputDeviceInfo {} + +impl InputDeviceInfo { + pub fn device_id(&self) -> String { + String::from("InputDeviceInfo.device_id") + } + + pub fn kind(&self) -> MediaKind { + MediaKind::Audio + } + + pub fn label(&self) -> String { + String::from("InputDeviceInfo.label") + } + + pub fn group_id(&self) -> String { + String::from("InputDeviceInfo.group_id") + } +} + +#[no_mangle] +pub unsafe extern "C" fn InputDeviceInfo__device_id( + this: *const InputDeviceInfo, +) -> *const libc::c_char { + let this = this.as_ref().unwrap(); + + string_into_c_str(this.device_id()) +} + +#[no_mangle] +pub unsafe extern "C" fn InputDeviceInfo__kind( + this: *const InputDeviceInfo, +) -> u8 { + let this = this.as_ref().unwrap(); + + this.kind() as u8 +} + +#[no_mangle] +pub unsafe extern "C" fn InputDeviceInfo__label( + this: *const InputDeviceInfo, +) -> *const libc::c_char { + let this = this.as_ref().unwrap(); + + string_into_c_str(this.label()) +} + +#[no_mangle] +pub unsafe extern "C" fn InputDeviceInfo__group_id( + this: *const InputDeviceInfo, +) -> *const libc::c_char { + let this = this.as_ref().unwrap(); + + string_into_c_str(this.group_id()) +} + +#[no_mangle] +pub unsafe extern "C" fn InputDeviceInfo__free(this: *mut InputDeviceInfo) { + Box::from_raw(this); +} diff --git a/jason/jason-dummy/src/jason.rs b/jason/jason-dummy/src/jason.rs new file mode 100644 index 000000000..8bf4b61ce --- /dev/null +++ b/jason/jason-dummy/src/jason.rs @@ -0,0 +1,57 @@ +use crate::{media_manager::MediaManagerHandle, room_handle::RoomHandle}; + +pub struct Jason; + +impl Jason { + pub fn new() -> Self { + Self + } + + pub fn init_room(&self) -> RoomHandle { + RoomHandle + } + + pub fn media_manager(&self) -> MediaManagerHandle { + MediaManagerHandle + } + + pub fn close_room(&self, _: RoomHandle) {} +} + +#[no_mangle] +pub extern "C" fn Jason__new() -> *const Jason { + Box::into_raw(Box::new(Jason::new())) +} + +#[no_mangle] +pub unsafe extern "C" fn Jason__init_room( + this: *const Jason, +) -> *mut RoomHandle { + let this = this.as_ref().unwrap(); + + Box::into_raw(Box::new(this.init_room())) +} + +#[no_mangle] +pub unsafe extern "C" fn Jason__media_manager( + this: *const Jason, +) -> *mut MediaManagerHandle { + let this = this.as_ref().unwrap(); + + Box::into_raw(Box::new(this.media_manager())) +} + +#[no_mangle] +pub unsafe extern "C" fn Jason__close_room( + this: *const Jason, + room_to_delete: *mut RoomHandle, +) { + let this = this.as_ref().unwrap(); + + this.close_room(*Box::from_raw(room_to_delete)); +} + +#[no_mangle] +pub unsafe extern "C" fn Jason__free(this: *mut Jason) { + Box::from_raw(this); +} diff --git a/jason/jason-dummy/src/lib.rs b/jason/jason-dummy/src/lib.rs index d793cdba3..b30589037 100644 --- a/jason/jason-dummy/src/lib.rs +++ b/jason/jason-dummy/src/lib.rs @@ -1,4 +1,38 @@ +#![allow( + clippy::module_name_repetitions, + clippy::unused_self, + clippy::needless_pass_by_value, + clippy::missing_safety_doc, + clippy::must_use_candidate, + clippy::missing_panics_doc, + clippy::new_without_default +)] + +pub mod audio_track_constraints; +pub mod connection_handle; +pub mod device_video_track_constraints; +pub mod display_video_track_constraints; +pub mod input_device_info; +pub mod jason; +pub mod local_media_track; +pub mod media_manager; +pub mod media_stream_settings; +pub mod reconnect_handle; +pub mod remote_media_track; +pub mod room_close_reason; +pub mod room_handle; +mod unimplemented; +mod utils; + #[no_mangle] -pub extern "C" fn add(a: i64, b: i64) -> i64 { - a + b +pub extern "C" fn dummy_function() {} + +pub enum MediaKind { + Audio = 0, + Video = 1, +} + +pub enum MediaSourceKind { + Device = 0, + Display = 1, } diff --git a/jason/jason-dummy/src/local_media_track.rs b/jason/jason-dummy/src/local_media_track.rs new file mode 100644 index 000000000..9c3b71558 --- /dev/null +++ b/jason/jason-dummy/src/local_media_track.rs @@ -0,0 +1,38 @@ +use crate::{MediaKind, MediaSourceKind}; + +pub struct LocalMediaTrack; + +impl LocalMediaTrack { + pub fn kind(&self) -> MediaKind { + MediaKind::Video + } + + pub fn media_source_kind(&self) -> MediaSourceKind { + MediaSourceKind::Display + } + + // pub fn get_track(&self) -> sys::MediaStreamTrack +} + +#[no_mangle] +pub unsafe extern "C" fn LocalMediaTrack__kind( + this: *const LocalMediaTrack, +) -> u8 { + let this = this.as_ref().unwrap(); + + this.kind() as u8 +} + +#[no_mangle] +pub unsafe extern "C" fn LocalMediaTrack__media_source_kind( + this: *const LocalMediaTrack, +) -> u8 { + let this = this.as_ref().unwrap(); + + this.media_source_kind() as u8 +} + +#[no_mangle] +pub unsafe extern "C" fn LocalMediaTrack__free(this: *mut LocalMediaTrack) { + Box::from_raw(this); +} diff --git a/jason/jason-dummy/src/media_manager.rs b/jason/jason-dummy/src/media_manager.rs new file mode 100644 index 000000000..6298dc59e --- /dev/null +++ b/jason/jason-dummy/src/media_manager.rs @@ -0,0 +1,48 @@ +use crate::{ + input_device_info::InputDeviceInfo, local_media_track::LocalMediaTrack, + media_stream_settings::MediaStreamSettings, utils::PtrArray, +}; + +pub struct MediaManagerHandle; + +impl MediaManagerHandle { + pub fn enumerate_devices(&self) -> Vec { + // async && Result + vec![InputDeviceInfo {}, InputDeviceInfo {}, InputDeviceInfo {}] + } + + pub fn init_local_tracks( + &self, + _caps: &MediaStreamSettings, + ) -> Vec { + // async && Result + vec![LocalMediaTrack {}, LocalMediaTrack {}, LocalMediaTrack {}] + } +} + +#[no_mangle] +pub unsafe extern "C" fn MediaManagerHandle__init_local_tracks( + this: *const MediaManagerHandle, + caps: *const MediaStreamSettings, +) -> PtrArray { + let this = this.as_ref().unwrap(); + let caps = caps.as_ref().unwrap(); + + PtrArray::new(this.init_local_tracks(caps)) +} + +#[no_mangle] +pub unsafe extern "C" fn MediaManagerHandle__enumerate_devices( + this: *const MediaManagerHandle, +) -> PtrArray { + let this = this.as_ref().unwrap(); + + PtrArray::new(this.enumerate_devices()) +} + +#[no_mangle] +pub unsafe extern "C" fn MediaManagerHandle__free( + this: *mut MediaManagerHandle, +) { + Box::from_raw(this); +} diff --git a/jason/jason-dummy/src/media_stream_settings.rs b/jason/jason-dummy/src/media_stream_settings.rs new file mode 100644 index 000000000..9091c3a2f --- /dev/null +++ b/jason/jason-dummy/src/media_stream_settings.rs @@ -0,0 +1,61 @@ +use crate::{ + audio_track_constraints::AudioTrackConstraints, + device_video_track_constraints::DeviceVideoTrackConstraints, + display_video_track_constraints::DisplayVideoTrackConstraints, +}; + +pub struct MediaStreamSettings; + +impl MediaStreamSettings { + pub fn new() -> Self { + Self + } + + pub fn audio(&mut self, _: AudioTrackConstraints) {} + + pub fn device_video(&mut self, _: DeviceVideoTrackConstraints) {} + + pub fn display_video(&mut self, _: DisplayVideoTrackConstraints) {} +} + +#[no_mangle] +pub extern "C" fn MediaStreamSettings__new() -> *const MediaStreamSettings { + Box::into_raw(Box::new(MediaStreamSettings::new())) +} + +#[no_mangle] +pub unsafe extern "C" fn MediaStreamSettings__audio( + this: *mut MediaStreamSettings, + constraints: *mut AudioTrackConstraints, +) { + let this = this.as_mut().unwrap(); + + this.audio(*Box::from_raw(constraints)); +} + +#[no_mangle] +pub unsafe extern "C" fn MediaStreamSettings__device_video( + this: *mut MediaStreamSettings, + constraints: *mut DeviceVideoTrackConstraints, +) { + let this = this.as_mut().unwrap(); + + this.device_video(*Box::from_raw(constraints)); +} + +#[no_mangle] +pub unsafe extern "C" fn MediaStreamSettings__display_video( + this: *mut MediaStreamSettings, + constraints: *mut DisplayVideoTrackConstraints, +) { + let this = this.as_mut().unwrap(); + + this.display_video(*Box::from_raw(constraints)); +} + +#[no_mangle] +pub unsafe extern "C" fn MediaStreamSettings__free( + this: *mut MediaStreamSettings, +) { + Box::from_raw(this); +} diff --git a/jason/jason-dummy/src/reconnect_handle.rs b/jason/jason-dummy/src/reconnect_handle.rs new file mode 100644 index 000000000..76001cffb --- /dev/null +++ b/jason/jason-dummy/src/reconnect_handle.rs @@ -0,0 +1,13 @@ +pub struct ReconnectHandle; + +impl ReconnectHandle { + // pub async fn reconnect_with_delay(&self, delay_ms: u32) -> Result<(), + // JasonError> pub async fn reconnect_with_backoff(&self, + // starting_delay_ms: u32, multiplier: f32, max_delay: u32) -> Result<(), + // JasonError> +} + +#[no_mangle] +pub unsafe extern "C" fn ReconnectHandle__free(this: *mut ReconnectHandle) { + Box::from_raw(this); +} diff --git a/jason/jason-dummy/src/remote_media_track.rs b/jason/jason-dummy/src/remote_media_track.rs new file mode 100644 index 000000000..0d3d6e99e --- /dev/null +++ b/jason/jason-dummy/src/remote_media_track.rs @@ -0,0 +1,68 @@ +use crate::{MediaKind, MediaSourceKind}; + +pub struct RemoteMediaTrack; + +impl RemoteMediaTrack { + pub fn enabled(&self) -> bool { + true + } + + pub fn kind(&self) -> MediaKind { + MediaKind::Video + } + + pub fn media_source_kind(&self) -> MediaSourceKind { + MediaSourceKind::Device + } + + pub fn muted(&self) -> bool { + false + } + // pub fn on_enabled(&self, callback: Callback<()>) + // pub fn on_disabled(&self, callback: Callback<()>) + // pub fn get_track(&self) -> sys::MediaStreamTrack + // pub fn on_muted(&self, cb: Callback<()>) + // pub fn on_unmuted(&self, cb: Callback<()>) + // pub fn on_stopped(&self, cb: Callback<()>) +} + +#[no_mangle] +pub unsafe extern "C" fn RemoteMediaTrack__enabled( + this: *const RemoteMediaTrack, +) -> u8 { + let this = this.as_ref().unwrap(); + + this.enabled() as u8 +} + +#[no_mangle] +pub unsafe extern "C" fn RemoteMediaTrack__muted( + this: *const RemoteMediaTrack, +) -> u8 { + let this = this.as_ref().unwrap(); + + this.muted() as u8 +} + +#[no_mangle] +pub unsafe extern "C" fn RemoteMediaTrack__kind( + this: *const RemoteMediaTrack, +) -> u8 { + let this = this.as_ref().unwrap(); + + this.kind() as u8 +} + +#[no_mangle] +pub unsafe extern "C" fn RemoteMediaTrack__media_source_kind( + this: *const RemoteMediaTrack, +) -> u8 { + let this = this.as_ref().unwrap(); + + this.media_source_kind() as u8 +} + +#[no_mangle] +pub unsafe extern "C" fn RemoteMediaTrack__free(this: *mut RemoteMediaTrack) { + Box::from_raw(this); +} diff --git a/jason/jason-dummy/src/room_close_reason.rs b/jason/jason-dummy/src/room_close_reason.rs new file mode 100644 index 000000000..67c0243c7 --- /dev/null +++ b/jason/jason-dummy/src/room_close_reason.rs @@ -0,0 +1,49 @@ +use crate::utils::string_into_c_str; + +pub struct RoomCloseReason; + +impl RoomCloseReason { + pub fn reason(&self) -> String { + String::from("RoomCloseReason.reason") + } + + pub fn is_closed_by_server(&self) -> bool { + false + } + + pub fn is_err(&self) -> bool { + true + } +} + +#[no_mangle] +pub unsafe extern "C" fn RoomCloseReason__reason( + this: *const RoomCloseReason, +) -> *const libc::c_char { + let this = this.as_ref().unwrap(); + + string_into_c_str(this.reason()) +} + +#[no_mangle] +pub unsafe extern "C" fn RoomCloseReason__is_closed_by_server( + this: *const RoomCloseReason, +) -> u8 { + let this = this.as_ref().unwrap(); + + this.is_closed_by_server() as u8 +} + +#[no_mangle] +pub unsafe extern "C" fn RoomCloseReason__is_err( + this: *const RoomCloseReason, +) -> u8 { + let this = this.as_ref().unwrap(); + + this.is_err() as u8 +} + +#[no_mangle] +pub unsafe extern "C" fn RoomCloseReason__free(this: *mut RoomCloseReason) { + Box::from_raw(this); +} diff --git a/jason/jason-dummy/src/room_handle.rs b/jason/jason-dummy/src/room_handle.rs new file mode 100644 index 000000000..9476e4c5f --- /dev/null +++ b/jason/jason-dummy/src/room_handle.rs @@ -0,0 +1,34 @@ +pub struct RoomHandle; + +impl RoomHandle { + // pub async fn join(&self, token: String) -> Result<(), JasonError> + // pub fn on_new_connection(&self, f: Callback) -> + // Result<(), JasonError> {} pub fn on_close(&mut self, f: + // Callback) -> Result<(), JasonError> + // pub fn on_local_track(&self, f: Callback) -> Result<(), + // JasonError> pub fn on_failed_local_media(&self, f: + // Callback) -> Result<(), JasonError> + // pub fn on_connection_loss(&self, f: Callback) -> + // Result<(), JasonError> pub async fn set_local_media_settings(&self, + // settings: &MediaStreamSettings, stop_first: bool, rollback_on_fail: bool) + // -> Result<(), ConstraintsUpdateException> pub async fn + // mute_audio(&self) -> Result<(), JasonError> pub async fn + // unmute_audio(&self) -> Result<(), JasonError> pub async fn + // mute_video(&self, source_kind: Option) -> Result<(), + // JasonError> pub async fn unmute_video(&self, source_kind: + // Option) -> Result<(), JasonError> pub async fn + // disable_audio(&self) -> Result<(), JasonError> pub async fn + // enable_audio(&self) -> Result<(), JasonError> pub async fn + // disable_video(&self, source_kind: Option) -> Result<(), + // JasonError> pub async fn enable_video(&self,source_kind: + // Option) -> Result<(), JasonError> pub async fn + // disable_remote_audio(&self) -> Result<(), JasonError> pub async fn + // disable_remote_video(&self) -> Result<(), JasonError> pub async fn + // enable_remote_audio(&self) -> Result<(), JasonError> pub async fn + // enable_remote_video(&self) -> Result<(), JasonError> +} + +#[no_mangle] +pub unsafe extern "C" fn RoomHandle__free(this: *mut RoomHandle) { + Box::from_raw(this); +} diff --git a/jason/jason-dummy/src/unimplemented.rs b/jason/jason-dummy/src/unimplemented.rs new file mode 100644 index 000000000..e4365b613 --- /dev/null +++ b/jason/jason-dummy/src/unimplemented.rs @@ -0,0 +1,17 @@ +// struct ConstrainsUpdateException {} +// +// impl ConstrainsUpdateException { +// pub fn name(&self) -> String +// pub fn recover_reason(&self) -> Option +// pub fn recover_fail_reasons(&self) -> Option +// pub fn error(&self) -> Option +// } +// +// struct JasonError {} +// +// impl JasonError { +// pub fn name(&self) -> String +// pub fn message(&self) -> String +// pub fn trace(&self) -> String +// pub fn source(&self) -> Option +// } diff --git a/jason/jason-dummy/src/utils/arrays.rs b/jason/jason-dummy/src/utils/arrays.rs new file mode 100644 index 000000000..f469de97b --- /dev/null +++ b/jason/jason-dummy/src/utils/arrays.rs @@ -0,0 +1,42 @@ +use std::{marker::PhantomData, slice}; + +#[repr(C)] +pub struct PtrArray { + len: u64, + arr: *const *mut (), + marker: PhantomData, +} + +impl PtrArray { + pub fn new(arr: Vec) -> Self { + let out: Vec<_> = arr + .into_iter() + .map(|e| Box::into_raw(Box::new(e))) + .collect(); + Self { + len: out.len() as u64, + arr: Box::leak(out.into_boxed_slice()).as_ptr().cast::<*mut ()>(), + marker: PhantomData::default(), + } + } +} + +impl Drop for PtrArray { + #[allow(clippy::cast_possible_truncation)] + fn drop(&mut self) { + // Only dropping boxed slice. Elements are leaked and must be + // explicitly freed in a foreign code. + unsafe { + let slice = slice::from_raw_parts_mut( + self.arr as *mut *mut (), + self.len as usize, + ); + Box::from_raw(slice); + }; + } +} + +#[no_mangle] +pub unsafe extern "C" fn PtrArray_free(arr: PtrArray) { + drop(arr); +} diff --git a/jason/jason-dummy/src/utils/mod.rs b/jason/jason-dummy/src/utils/mod.rs new file mode 100644 index 000000000..a340ca232 --- /dev/null +++ b/jason/jason-dummy/src/utils/mod.rs @@ -0,0 +1,7 @@ +mod arrays; +mod string; + +pub use self::{ + arrays::PtrArray, + string::{c_str_into_string, string_into_c_str}, +}; diff --git a/jason/jason-dummy/src/utils/string.rs b/jason/jason-dummy/src/utils/string.rs new file mode 100644 index 000000000..7835cef03 --- /dev/null +++ b/jason/jason-dummy/src/utils/string.rs @@ -0,0 +1,14 @@ +use std::ffi::{CStr, CString}; + +pub unsafe fn c_str_into_string(string: *const libc::c_char) -> String { + CStr::from_ptr(string).to_str().unwrap().to_owned() +} + +pub unsafe fn string_into_c_str(string: String) -> *const libc::c_char { + CString::new(string).unwrap().into_raw() +} + +#[no_mangle] +pub unsafe extern "C" fn String_free(s: *mut libc::c_char) { + CString::from_raw(s); +}