Skip to content

Commit

Permalink
Basic Jason integration with flutter_webrtc library (#10)
Browse files Browse the repository at this point in the history
- add bindings for `PeerConnection`, `MediaStreamTrack`, `IceCandidate` and `Transceiver`
- add ability to return `ForeignValue` from Dart without memory leaking
- add support for Dart callbacks with two arguments
- adapt internal Jason implementations for working with async `flutter_webrtc` methods

Additionally:
- use `instrumentisto/flutter-webrtc` fork instead of upstream
  • Loading branch information
evdokimovs authored Nov 10, 2021
1 parent 2ffa445 commit 23ca7a5
Show file tree
Hide file tree
Showing 38 changed files with 2,953 additions and 453 deletions.
10 changes: 6 additions & 4 deletions flutter/example/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -103,10 +103,12 @@ packages:
flutter_webrtc:
dependency: transitive
description:
name: flutter_webrtc
url: "https://pub.dartlang.org"
source: hosted
version: "0.6.8"
path: "."
ref: HEAD
resolved-ref: "1cf5f5919eb1a8dde62a8fd84d640078b6ed8e59"
url: "https://github.com/instrumentisto/flutter-webrtc.git"
source: git
version: "0.7.0+hotfix.2"
fuchsia_remote_debug_protocol:
dependency: transitive
description: flutter
Expand Down
22 changes: 22 additions & 0 deletions flutter/lib/src/native/ffi/callback.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,16 @@ import 'foreign_value.dart';

typedef _callbackCall_C = Void Function(Pointer, ForeignValue);
typedef _callbackCall_Dart = void Function(Pointer, ForeignValue);
typedef _callbackTwoArgCall_C = Void Function(
Pointer, ForeignValue, ForeignValue);
typedef _callbackTwoArgCall_Dart = void Function(
Pointer, ForeignValue, ForeignValue);

final _callbackCall =
dl.lookupFunction<_callbackCall_C, _callbackCall_Dart>('Callback__call');
final _callbackTwoArgCall =
dl.lookupFunction<_callbackTwoArgCall_C, _callbackTwoArgCall_Dart>(
'Callback__call_two_arg');

/// Registers the closure callers functions in Rust.
void registerFunctions(DynamicLibrary dl) {
Expand All @@ -18,6 +25,10 @@ void registerFunctions(DynamicLibrary dl) {
dl.lookupFunction<Void Function(Pointer), void Function(Pointer)>(
'register_Callback__call_proxy')(
Pointer.fromFunction<Handle Function(Pointer)>(callback));

dl.lookupFunction<Void Function(Pointer), void Function(Pointer)>(
'register_Callback__call_two_arg_proxy')(
Pointer.fromFunction<Handle Function(Pointer)>(callbackTwoArg));
}

/// Function used by Rust to call closures with a single [ForeignValue]
Expand All @@ -39,3 +50,14 @@ Object callback(Pointer cb) {
arg.free();
};
}

/// Returns a two args closure calling the provided Rust function [Pointer].
Object callbackTwoArg(Pointer cb) {
return (first, second) {
var firstArg = ForeignValue.fromDart(first);
var secondArg = ForeignValue.fromDart(second);
_callbackTwoArgCall(cb, firstArg.ref, secondArg.ref);
firstArg.free();
secondArg.free();
};
}
10 changes: 10 additions & 0 deletions flutter/lib/src/native/ffi/completer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,16 @@ void registerFunctions(DynamicLibrary dl) {
'register_completer_complete_error_caller')(
Pointer.fromFunction<Void Function(Handle, Pointer<Handle>)>(
_Completer_completeError_Pointer));

dl.lookupFunction<Void Function(Pointer), void Function(Pointer)>(
'register_delayed_future_function')(
Pointer.fromFunction<Handle Function(Int32)>(delayedFuture));
}

/// Returns closure returning a [Future.delayed] with the provided amount of
/// milliseconds.
Object delayedFuture(int delayMs) {
return () => Future.delayed(Duration(milliseconds: delayMs));
}

/// Returns a new [Completer].
Expand Down
17 changes: 17 additions & 0 deletions flutter/lib/src/native/ffi/foreign_value.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@ import 'package:ffi/ffi.dart';
import '../../util/move_semantic.dart';
import 'nullable_pointer.dart';
import 'box_handle.dart';
import '../jason.dart';

typedef _boxForeignValue_C = Pointer Function(ForeignValue);
typedef _boxForeignValue_Dart = Pointer Function(ForeignValue);

final _boxForeignValue_Dart _boxForeignValue =
dl.lookupFunction<_boxForeignValue_C, _boxForeignValue_Dart>(
'box_foreign_value');

/// Type-erased value that can be transferred via FFI boundaries to/from Rust.
class ForeignValue extends Struct {
Expand Down Expand Up @@ -101,6 +109,15 @@ class ForeignValue extends Struct {
}

extension ForeignValuePointer on Pointer<ForeignValue> {
/// Transfers [ForeignValue] ownership to Rust.
///
/// Frees Dart side [ForeignValue].
Pointer intoRustOwned() {
var out = _boxForeignValue(ref);
free();
return out;
}

/// Releases the memory allocated on a native heap.
@moveSemantics
void free() {
Expand Down
10 changes: 10 additions & 0 deletions flutter/lib/src/native/platform/functions_registerer.dart
Original file line number Diff line number Diff line change
@@ -1,8 +1,18 @@
import 'dart:ffi';

import 'object.dart' as object;
import 'media_track.dart' as media_track;
import 'peer_connection.dart' as peer_connection;
import 'transceiver.dart' as transceiver;
import 'ice_servers.dart' as ice_servers;
import 'ice_candidate.dart' as ice_candidate;

/// Registers functions needed for platform utils working.
void registerFunctions(DynamicLibrary dl) {
object.registerFunctions(dl);
media_track.registerFunctions(dl);
peer_connection.registerFunctions(dl);
transceiver.registerFunctions(dl);
ice_servers.registerFunctions(dl);
ice_candidate.registerFunctions(dl);
}
58 changes: 58 additions & 0 deletions flutter/lib/src/native/platform/ice_candidate.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import 'dart:ffi';

import 'package:flutter_webrtc/flutter_webrtc.dart';
import 'package:medea_jason/src/native/ffi/foreign_value.dart';

/// Registers functions allowing Rust to create Dart [RTCIceCandidate]s.
void registerFunctions(DynamicLibrary dl) {
dl.lookupFunction<Void Function(Pointer), void Function(Pointer)>(
'register_IceCandidate__new')(
Pointer.fromFunction<
Handle Function(
ForeignValue, ForeignValue, ForeignValue)>(newRtcIceCandidate));
dl.lookupFunction<Void Function(Pointer), void Function(Pointer)>(
'register_IceCandidate__candidate')(
Pointer.fromFunction<Pointer Function(Handle)>(candidate));
dl.lookupFunction<Void Function(Pointer), void Function(Pointer)>(
'register_IceCandidate__sdp_m_line_index')(
Pointer.fromFunction<Pointer Function(Handle)>(sdpMLineIndex));
dl.lookupFunction<Void Function(Pointer), void Function(Pointer)>(
'register_IceCandidate__sdp_mid')(
Pointer.fromFunction<Pointer Function(Handle)>(sdpMid));
}

/// Returns the provided [RTCIceCandidate] [String].
Pointer candidate(RTCIceCandidate iceCandidate) {
if (iceCandidate.candidate != null) {
return ForeignValue.fromString(iceCandidate.candidate!).intoRustOwned();
} else {
return ForeignValue.none().intoRustOwned();
}
}

/// Returns SDP M line index of the provided [RTCIceCandidate].
Pointer sdpMLineIndex(RTCIceCandidate iceCandidate) {
if (iceCandidate.sdpMlineIndex != null) {
return ForeignValue.fromInt(iceCandidate.sdpMlineIndex!).intoRustOwned();
} else {
return ForeignValue.none().intoRustOwned();
}
}

/// Returns SDP MID of the provided [RTCIceCandidate].
Pointer sdpMid(RTCIceCandidate iceCandidate) {
if (iceCandidate.sdpMid != null) {
return ForeignValue.fromString(iceCandidate.sdpMid!).intoRustOwned();
} else {
return ForeignValue.none().intoRustOwned();
}
}

/// Creates a new [RTCIceCandidate] with the provided values.
Object newRtcIceCandidate(
ForeignValue candidate, ForeignValue sdpMid, ForeignValue sdpMlineIndex) {
var candidateArg = candidate.toDart();
var sdpMidArg = sdpMid.toDart();
var sdpMLineIndexArg = sdpMlineIndex.toDart();
return RTCIceCandidate(candidateArg, sdpMidArg, sdpMLineIndexArg);
}
36 changes: 36 additions & 0 deletions flutter/lib/src/native/platform/ice_servers.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import 'dart:ffi';
import 'package:ffi/ffi.dart';
import 'package:flutter_webrtc/flutter_webrtc.dart';
import 'package:medea_jason/src/native/ffi/foreign_value.dart';

/// Registers [RTCPeerConnection] ICE servers related functions in Rust.
void registerFunctions(DynamicLibrary dl) {
dl.lookupFunction<Void Function(Pointer), void Function(Pointer)>(
'register_IceServers__new')(
Pointer.fromFunction<Handle Function()>(newIceServers));
dl.lookupFunction<Void Function(Pointer), void Function(Pointer)>(
'register_IceServers__add')(Pointer.fromFunction<
Void Function(Handle, Pointer<Utf8>, ForeignValue, ForeignValue)>(
addIceServer));
}

/// Returns a new empty `IceServer`s [List].
Object newIceServers() {
return List.empty(growable: true);
}

/// Adds an `IceServer` with the provided data to the provided [List].
void addIceServer(Object servers, Pointer<Utf8> url, ForeignValue username,
ForeignValue credentials) {
servers as List;
var iceServer = {'url': url.toDartString()};
username = username.toDart();
if (username is String) {
iceServer['username'] = username as String;
}
credentials = credentials.toDart();
if (credentials is String) {
iceServer['credentials'] = credentials as String;
}
servers.add(iceServer);
}
98 changes: 98 additions & 0 deletions flutter/lib/src/native/platform/media_track.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import 'package:flutter_webrtc/flutter_webrtc.dart';
import 'dart:ffi';
import 'package:ffi/ffi.dart';

/// Registers [MediaStreamTrack] related functions in Rust.
void registerFunctions(DynamicLibrary dl) {
dl.lookupFunction<Void Function(Pointer), void Function(Pointer)>(
'register_MediaStreamTrack__id')(
Pointer.fromFunction<Pointer<Utf8> Function(Handle)>(id));
dl.lookupFunction<Void Function(Pointer), void Function(Pointer)>(
'register_MediaStreamTrack__device_id')(
Pointer.fromFunction<Pointer<Utf8> Function(Handle)>(deviceId));
dl.lookupFunction<Void Function(Pointer), void Function(Pointer)>(
'register_MediaStreamTrack__facing_mode')(
Pointer.fromFunction<Int64 Function(Handle)>(facingMode, 0));
dl.lookupFunction<Void Function(Pointer), void Function(Pointer)>(
'register_MediaStreamTrack__kind')(
Pointer.fromFunction<Int64 Function(Handle)>(kind, 0));
dl.lookupFunction<Void Function(Pointer), void Function(Pointer)>(
'register_MediaStreamTrack__height')(
Pointer.fromFunction<Int64 Function(Handle)>(height, 0));
dl.lookupFunction<Void Function(Pointer), void Function(Pointer)>(
'register_MediaStreamTrack__width')(
Pointer.fromFunction<Int64 Function(Handle)>(width, 0));
dl.lookupFunction<Void Function(Pointer), void Function(Pointer)>(
'register_MediaStreamTrack__set_enabled')(
Pointer.fromFunction<Void Function(Handle, Int8)>(setEnabled));
dl.lookupFunction<Void Function(Pointer), void Function(Pointer)>(
'register_MediaStreamTrack__enabled')(
Pointer.fromFunction<Int8 Function(Handle)>(enabled, 0));
dl.lookupFunction<Void Function(Pointer), void Function(Pointer)>(
'register_MediaStreamTrack__stop')(
Pointer.fromFunction<Void Function(Handle)>(stop));
dl.lookupFunction<Void Function(Pointer), void Function(Pointer)>(
'register_MediaStreamTrack__on_ended')(
Pointer.fromFunction<Void Function(Handle, Handle)>(onEnded));
}

/// Returns ID of the provided [MediaStreamTrack].
Pointer<Utf8> id(MediaStreamTrack track) {
return track.id!.toNativeUtf8();
}

/// Returns kind of the provided [MediaStreamTrack].
int kind(MediaStreamTrack track) {
if (track.kind == 'audio') {
return 0;
} else {
return 1;
}
}

/// Subscribes on the [MediaStreamTrack.onEnded] of the provided
/// [MediaStreamTrack].
void onEnded(MediaStreamTrack track, Function f) {
track.onEnded = () {
f();
};
}

/// Returns device ID of the provided [MediaStreamTrack].
Pointer<Utf8> deviceId(MediaStreamTrack track) {
// TODO: Correct implementation requires flutter_webrtc-side fixes.
return id(track);
}

/// Returns facingMode of the provided [MediaStreamTrack].
int facingMode(MediaStreamTrack track) {
// TODO: Correct implementation requires flutter_webrtc-side fixes.
return 0;
}

/// Returns height of the video of the provided [MediaStreamTrack].
int height(MediaStreamTrack track) {
// TODO: Correct implementation requires flutter_webrtc-side fixes.
return 1600;
}

/// Returns width of the video of the provided [MediaStreamTrack].
int width(MediaStreamTrack track) {
// TODO: Correct implementation requires flutter_webrtc-side fixes.
return 1300;
}

/// Sets [MediaStreamTrack.enabled] state of the provided [MediaStreamTrack].
void setEnabled(MediaStreamTrack track, int enabled) {
track.enabled = enabled == 1;
}

/// Stops provided [MediaStreamTrack].
void stop(MediaStreamTrack track) {
track.stop();
}

/// Returns `1` if the provided [MediaStreamTrack] is enabled and `0` otherwise.
int enabled(MediaStreamTrack track) {
return track.enabled ? 1 : 0;
}
Loading

0 comments on commit 23ca7a5

Please sign in to comment.