diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b1b908bf..f91a4a8e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,15 +6,21 @@ All user visible changes to this project will be documented in this file. This p -## [0.4.1] · 2024-??-?? (unreleased) -[0.4.1]: /../../tree/medea-jason-0.4.1 +## [0.5.0] · 2024-??-?? (unreleased) +[0.5.0]: /../../tree/medea-jason-0.5.0 -[Diff](https://github.com/instrumentisto/medea-jason/compare/medea-jason-0.4.0...medea-jason-0.4.1) +[Diff](https://github.com/instrumentisto/medea-jason/compare/medea-jason-0.4.0...medea-jason-0.5.0) + +### BC Breaks + +- Minimal supported version of `medea-client-api-proto` is `0.6.0` ([#151]). ### Added - Logging: - Exceptions thrown from Dart callbacks called by Rust ([#138]). +- Monitoring: + - `IceCandidateError` metric sending to server ([#151]). ### Fixed @@ -22,6 +28,7 @@ All user visible changes to this project will be documented in this file. This p [#135]: /../../pull/135 [#138]: /../../pull/138 +[#151]: /../../pull/151 diff --git a/Cargo.lock b/Cargo.lock index 7dcfa9f28..1bacb98fb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -277,9 +277,9 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "ahash" -version = "0.8.7" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01" +checksum = "42cd52102d3df161c77a887b608d7a4897d7cc112886a9537b738a887a03aaff" dependencies = [ "cfg-if 1.0.0", "getrandom", @@ -683,9 +683,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.33" +version = "0.4.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f13690e35a5e4ace198e7beea2895d29f3a9cc55015fcebe6336bd2010af9eb" +checksum = "5bc015644b92d5890fab7489e49d21f879d5c990186827d42ec511919404f38b" dependencies = [ "android-tzdata", "iana-time-zone", @@ -818,9 +818,9 @@ dependencies = [ [[package]] name = "crc32fast" -version = "1.3.2" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" dependencies = [ "cfg-if 1.0.0", ] @@ -1060,9 +1060,9 @@ checksum = "669a445ee724c5c69b1b06fe0b63e70a1c84bc9bb7d9696cd4f4e3ec45050408" [[package]] name = "either" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" [[package]] name = "encode_unicode" @@ -1386,7 +1386,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap 2.2.2", + "indexmap 2.2.3", "slab", "tokio", "tokio-util", @@ -1431,9 +1431,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0c62115964e08cb8039170eb33c1d0e2388a256930279edca206fff675f82c3" +checksum = "bd5256b483761cd23699d0da46cc6fd2ee3be420bbe6d020ae4a091e70b7e9fd" [[package]] name = "hex" @@ -1632,9 +1632,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.2.2" +version = "2.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "824b2ae422412366ba479e8111fd301f7b5faece8149317bb81925979a53f520" +checksum = "233cf39063f058ea2caae4091bf4a3ef70a653afbc026f5c4a4135d114e3c177" dependencies = [ "equivalent", "hashbrown 0.14.3", @@ -1707,8 +1707,7 @@ dependencies = [ [[package]] name = "js-sys" version = "0.3.68" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "406cda4b368d531c842222cf9d2600a9a4acce8d29423695379c6868a143a9ee" +source = "git+https://github.com/rustwasm/wasm-bindgen.git?rev=12889ef666f9b355585602e268be2b70d7f3ec80#12889ef666f9b355585602e268be2b70d7f3ec80" dependencies = [ "wasm-bindgen", ] @@ -1818,7 +1817,7 @@ checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" [[package]] name = "medea-client-api-proto" -version = "0.5.0" +version = "0.6.0-dev" dependencies = [ "async-trait", "derive_more", @@ -1853,7 +1852,7 @@ dependencies = [ [[package]] name = "medea-control-api-proto" -version = "0.11.0" +version = "0.12.0-dev" dependencies = [ "async-trait", "derive_more", @@ -1892,7 +1891,7 @@ dependencies = [ [[package]] name = "medea-jason" -version = "0.4.1-dev" +version = "0.5.0-dev" dependencies = [ "android_logger", "async-recursion", @@ -2087,7 +2086,7 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi 0.3.5", + "hermit-abi 0.3.6", "libc", ] @@ -2228,7 +2227,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" dependencies = [ "fixedbitset", - "indexmap 2.2.2", + "indexmap 2.2.3", ] [[package]] @@ -2265,9 +2264,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2900ede94e305130c13ddd391e0ab7cbaeb783945ae07a279c268cb05109c6cb" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" [[package]] name = "powerfmt" @@ -2782,7 +2781,7 @@ dependencies = [ "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.2.2", + "indexmap 2.2.3", "serde", "serde_derive", "serde_json", @@ -2808,7 +2807,7 @@ version = "0.9.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adf8a49373e98a4c5f0ceb5d05aa7c648d75f63774981ed95b7c7443bbd50c6e" dependencies = [ - "indexmap 2.2.2", + "indexmap 2.2.3", "itoa", "ryu", "serde", @@ -3132,18 +3131,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.56" +version = "1.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad" +checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.56" +version = "1.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" +checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81" dependencies = [ "proc-macro2", "quote", @@ -3573,8 +3572,7 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" version = "0.2.91" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1e124130aee3fb58c5bdd6b639a0509486b0338acaaae0c84a5124b0f588b7f" +source = "git+https://github.com/rustwasm/wasm-bindgen.git?rev=12889ef666f9b355585602e268be2b70d7f3ec80#12889ef666f9b355585602e268be2b70d7f3ec80" dependencies = [ "cfg-if 1.0.0", "wasm-bindgen-macro", @@ -3583,8 +3581,7 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" version = "0.2.91" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e7e1900c352b609c8488ad12639a311045f40a35491fb69ba8c12f758af70b" +source = "git+https://github.com/rustwasm/wasm-bindgen.git?rev=12889ef666f9b355585602e268be2b70d7f3ec80#12889ef666f9b355585602e268be2b70d7f3ec80" dependencies = [ "bumpalo", "log", @@ -3610,8 +3607,7 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" version = "0.2.91" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b30af9e2d358182b5c7449424f017eba305ed32a7010509ede96cdc4696c46ed" +source = "git+https://github.com/rustwasm/wasm-bindgen.git?rev=12889ef666f9b355585602e268be2b70d7f3ec80#12889ef666f9b355585602e268be2b70d7f3ec80" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3620,8 +3616,7 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" version = "0.2.91" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66" +source = "git+https://github.com/rustwasm/wasm-bindgen.git?rev=12889ef666f9b355585602e268be2b70d7f3ec80#12889ef666f9b355585602e268be2b70d7f3ec80" dependencies = [ "proc-macro2", "quote", @@ -3633,8 +3628,7 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" version = "0.2.91" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f186bd2dcf04330886ce82d6f33dd75a7bfcf69ecf5763b89fcde53b6ac9838" +source = "git+https://github.com/rustwasm/wasm-bindgen.git?rev=12889ef666f9b355585602e268be2b70d7f3ec80#12889ef666f9b355585602e268be2b70d7f3ec80" [[package]] name = "wasm-bindgen-test" @@ -3675,8 +3669,7 @@ dependencies = [ [[package]] name = "web-sys" version = "0.3.68" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96565907687f7aceb35bc5fc03770a8a0471d82e479f25832f54a0e3f4b28446" +source = "git+https://github.com/rustwasm/wasm-bindgen.git?rev=12889ef666f9b355585602e268be2b70d7f3ec80#12889ef666f9b355585602e268be2b70d7f3ec80" dependencies = [ "js-sys", "wasm-bindgen", diff --git a/Cargo.toml b/Cargo.toml index 9b87a482a..0df80bd00 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "medea-jason" -version = "0.4.1-dev" +version = "0.5.0-dev" edition = "2021" rust-version = "1.65" description = "Client library for Medea media server." @@ -44,7 +44,7 @@ derivative = "2.2" derive_more = "0.99" futures = "0.3" log = "0.4" -medea-client-api-proto = { version = "0.5", path = "proto/client-api" } +medea-client-api-proto = { version = "0.6.0-dev", path = "proto/client-api" } medea-macro = { version = "0.3", path = "crates/medea-macro" } medea-reactive = { version = "0.1", path = "crates/medea-reactive" } mockall = { version = "0.12", optional = true } @@ -95,6 +95,7 @@ wee_alloc = { version = "0.4", optional = true } "RtcIceTransportPolicy", "RtcOfferOptions", "RtcPeerConnection", "RtcPeerConnectionIceEvent", + "RtcPeerConnectionIceErrorEvent", "RtcRtpReceiver", "RtcRtpSender", "RtcRtpTransceiver", "RtcRtpTransceiverDirection", "RtcRtpTransceiverInit", @@ -105,6 +106,12 @@ wee_alloc = { version = "0.4", optional = true } "WebSocket", "Window", ] +[patch.crates-io] +web-sys = { git = "https://github.com/rustwasm/wasm-bindgen.git", rev = "12889ef666f9b355585602e268be2b70d7f3ec80" } +js-sys = { git = "https://github.com/rustwasm/wasm-bindgen.git", rev = "12889ef666f9b355585602e268be2b70d7f3ec80" } +wasm-bindgen = { git = "https://github.com/rustwasm/wasm-bindgen.git", rev = "12889ef666f9b355585602e268be2b70d7f3ec80" } +wasm-bindgen-shared = { git = "https://github.com/rustwasm/wasm-bindgen.git", rev = "12889ef666f9b355585602e268be2b70d7f3ec80" } + [build-dependencies] cc = "1.0" diff --git a/flutter/example/pubspec.lock b/flutter/example/pubspec.lock index 3098bb2b6..e93ee460d 100644 --- a/flutter/example/pubspec.lock +++ b/flutter/example/pubspec.lock @@ -352,7 +352,7 @@ packages: path: ".." relative: true source: path - version: "0.4.1-dev" + version: "0.5.0-dev" meta: dependency: transitive description: diff --git a/flutter/lib/src/native/ffi/jason_api.g.dart b/flutter/lib/src/native/ffi/jason_api.g.dart index b658a737a..5065812c3 100644 --- a/flutter/lib/src/native/ffi/jason_api.g.dart +++ b/flutter/lib/src/native/ffi/jason_api.g.dart @@ -648,7 +648,7 @@ abstract class MedeaJason { FlutterRustBridgeTaskConstMeta get kRoomHandleOnFailedLocalMediaConstMeta; - /// Log Dart exception. + /// Logs Dart exception. void logDartException( {required String message, required String stackTrace, dynamic hint}); @@ -5215,6 +5215,72 @@ class MedeaJasonWire implements FlutterRustBridgeWireBase { late final _sdp_mid = _sdp_midPtr.asFunction Function(Object)>(); + ffi.Pointer address( + Object error, + ) { + return _address( + error, + ); + } + + late final _addressPtr = + _lookup Function(ffi.Handle)>>( + 'address'); + late final _address = + _addressPtr.asFunction Function(Object)>(); + + int port( + Object error, + ) { + return _port( + error, + ); + } + + late final _portPtr = + _lookup>('port'); + late final _port = _portPtr.asFunction(); + + ffi.Pointer url( + Object error, + ) { + return _url( + error, + ); + } + + late final _urlPtr = + _lookup Function(ffi.Handle)>>( + 'url'); + late final _url = + _urlPtr.asFunction Function(Object)>(); + + int error_code( + Object error, + ) { + return _error_code( + error, + ); + } + + late final _error_codePtr = + _lookup>('error_code'); + late final _error_code = _error_codePtr.asFunction(); + + ffi.Pointer error_text( + Object error, + ) { + return _error_text( + error, + ); + } + + late final _error_textPtr = + _lookup Function(ffi.Handle)>>( + 'error_text'); + late final _error_text = + _error_textPtr.asFunction Function(Object)>(); + void add( Object list, ffi.Pointer url, @@ -5691,6 +5757,22 @@ class MedeaJasonWire implements FlutterRustBridgeWireBase { late final _on_ice_candidate = _on_ice_candidatePtr.asFunction(); + void on_ice_candidate_error( + Object peer, + Object cb, + ) { + return _on_ice_candidate_error( + peer, + cb, + ); + } + + late final _on_ice_candidate_errorPtr = + _lookup>( + 'on_ice_candidate_error'); + late final _on_ice_candidate_error = + _on_ice_candidate_errorPtr.asFunction(); + Object get_transceiver_by_mid( Object peer, ffi.Pointer mid, diff --git a/flutter/lib/src/native/ffi/jason_api.g.freezed.dart b/flutter/lib/src/native/ffi/jason_api.g.freezed.dart index 8169cc27a..677c87cff 100644 --- a/flutter/lib/src/native/ffi/jason_api.g.freezed.dart +++ b/flutter/lib/src/native/ffi/jason_api.g.freezed.dart @@ -146,7 +146,7 @@ class _$ApiConstrainFacingMode_ExactImpl } @override - bool operator ==(Object other) { + bool operator ==(dynamic other) { return identical(this, other) || (other.runtimeType == runtimeType && other is _$ApiConstrainFacingMode_ExactImpl && @@ -291,7 +291,7 @@ class _$ApiConstrainFacingMode_IdealImpl } @override - bool operator ==(Object other) { + bool operator ==(dynamic other) { return identical(this, other) || (other.runtimeType == runtimeType && other is _$ApiConstrainFacingMode_IdealImpl && @@ -518,7 +518,7 @@ class _$ConstrainU32_ExactImpl implements ConstrainU32_Exact { } @override - bool operator ==(Object other) { + bool operator ==(dynamic other) { return identical(this, other) || (other.runtimeType == runtimeType && other is _$ConstrainU32_ExactImpl && @@ -662,7 +662,7 @@ class _$ConstrainU32_IdealImpl implements ConstrainU32_Ideal { } @override - bool operator ==(Object other) { + bool operator ==(dynamic other) { return identical(this, other) || (other.runtimeType == runtimeType && other is _$ConstrainU32_IdealImpl && @@ -813,7 +813,7 @@ class _$ConstrainU32_RangeImpl implements ConstrainU32_Range { } @override - bool operator ==(Object other) { + bool operator ==(dynamic other) { return identical(this, other) || (other.runtimeType == runtimeType && other is _$ConstrainU32_RangeImpl && diff --git a/flutter/lib/src/native/platform/functions_registerer.dart b/flutter/lib/src/native/platform/functions_registerer.dart index d2b6c622a..3c0e696cc 100644 --- a/flutter/lib/src/native/platform/functions_registerer.dart +++ b/flutter/lib/src/native/platform/functions_registerer.dart @@ -2,6 +2,7 @@ import 'dart:ffi'; import 'constraints.dart' as constraints; import 'ice_candidate.dart' as ice_candidate; +import 'ice_candidate_error.dart' as ice_candidate_error; import 'ice_servers.dart' as ice_servers; import 'media_device_info.dart' as media_device_info; import 'media_devices.dart' as media_devices; @@ -25,4 +26,5 @@ void registerFunctions(DynamicLibrary dl) { media_device_info.registerFunctions(dl); media_display_info.registerFunctions(dl); ice_candidate.registerFunctions(dl); + ice_candidate_error.registerFunctions(dl); } diff --git a/flutter/lib/src/native/platform/ice_candidate_error.dart b/flutter/lib/src/native/platform/ice_candidate_error.dart new file mode 100644 index 000000000..907cf19ad --- /dev/null +++ b/flutter/lib/src/native/platform/ice_candidate_error.dart @@ -0,0 +1,68 @@ +import 'dart:ffi'; + +import 'package:ffi/ffi.dart'; +import 'package:medea_flutter_webrtc/medea_flutter_webrtc.dart' as webrtc; + +import 'ice_candidate_error.g.dart' as bridge; + +/// Registers functions allowing Rust to create Dart [IceCandidateErrorEvent]s. +void registerFunctions(DynamicLibrary dl) { + bridge.registerFunction( + dl, + address: Pointer.fromFunction(_address), + port: Pointer.fromFunction(_port, 0), + url: Pointer.fromFunction(_url), + errorCode: Pointer.fromFunction(_errorCode, 0), + errorText: Pointer.fromFunction(_errorText), + ); +} + +/// Returns the local IP address used to communicate with a [STUN]/[TURN] +/// server. +/// +/// [STUN]: https://webrtcglossary.com/stun +/// [TURN]: https://webrtcglossary.com/turn +Pointer _address(webrtc.IceCandidateErrorEvent error) { + return error.address.toNativeUtf8(); +} + +/// Returns the port used to communicate with a [STUN]/[TURN] server. +/// +/// [STUN]: https://webrtcglossary.com/stun +/// [TURN]: https://webrtcglossary.com/turn +int _port(webrtc.IceCandidateErrorEvent error) { + return error.port; +} + +/// Returns the URL identifying the [STUN]/[TURN] server for which the failure +/// occurred. +/// +/// [STUN]: https://webrtcglossary.com/stun +/// [TURN]: https://webrtcglossary.com/turn +Pointer _url(webrtc.IceCandidateErrorEvent error) { + return error.url.toNativeUtf8(); +} + +/// Returns the Numeric [STUN] error code returned by the [STUN]/[TURN] server. +/// +/// If no host candidate can reach the server, this error code will be set to +/// the value `701`, which is outside the [STUN] error code range. This error is +/// only fired once per server URL while in the `RTCIceGatheringState` of +/// "gathering". +/// +/// [STUN]: https://webrtcglossary.com/stun +/// [TURN]: https://webrtcglossary.com/turn +int _errorCode(webrtc.IceCandidateErrorEvent error) { + return error.errorCode; +} + +/// [STUN] reason text returned by the [STUN]/[TURN] server. +/// +/// If the server could not be reached, this reason test will be set to an +/// implementation-specific value providing details about the error. +/// +/// [STUN]: https://webrtcglossary.com/stun +/// [TURN]: https://webrtcglossary.com/turn +Pointer _errorText(webrtc.IceCandidateErrorEvent error) { + return error.errorText.toNativeUtf8(); +} diff --git a/flutter/lib/src/native/platform/ice_candidate_error.g.dart b/flutter/lib/src/native/platform/ice_candidate_error.g.dart new file mode 100644 index 000000000..d75d90e58 --- /dev/null +++ b/flutter/lib/src/native/platform/ice_candidate_error.g.dart @@ -0,0 +1,25 @@ +import 'dart:ffi'; + +import 'package:ffi/ffi.dart'; + +import 'package:medea_jason/src/native/ffi/foreign_value.dart'; + +void registerFunction( + DynamicLibrary dl, { + required Pointer Function(Handle)>> address, + required Pointer> port, + required Pointer Function(Handle)>> url, + required Pointer> errorCode, + required Pointer Function(Handle)>> errorText, +}) { + dl.lookupFunction< + Void Function(Pointer, Pointer, Pointer, Pointer, Pointer), + void Function(Pointer, Pointer, Pointer, Pointer, + Pointer)>('register_ice_candidate_error')( + address, + port, + url, + errorCode, + errorText, + ); +} diff --git a/flutter/lib/src/native/platform/peer_connection.dart b/flutter/lib/src/native/platform/peer_connection.dart index 05e651a05..2079d4f99 100644 --- a/flutter/lib/src/native/platform/peer_connection.dart +++ b/flutter/lib/src/native/platform/peer_connection.dart @@ -20,6 +20,7 @@ void registerFunctions(DynamicLibrary dl) { rollback: Pointer.fromFunction(_rollback), onTrack: Pointer.fromFunction(_onTrack), onIceCandidate: Pointer.fromFunction(_onIceCandidate), + onIceCandidateError: Pointer.fromFunction(_onIceCandidateError), onIceConnectionStateChange: Pointer.fromFunction(_onIceConnectionStateChange), newPeer: Pointer.fromFunction(_newPeer), @@ -63,6 +64,13 @@ void _onIceCandidate(PeerConnection conn, Function f) { }); } +/// Sets the provided [f] to the [PeerConnection.onIceCandidateError] callback. +void _onIceCandidateError(PeerConnection conn, Function f) { + conn.onIceCandidateError((e) { + f(e); + }); +} + /// Sets the provided [f] to the [PeerConnection.onIceConnectionStateChange] /// callback. void _onIceConnectionStateChange(PeerConnection conn, Function f) { diff --git a/flutter/lib/src/native/platform/peer_connection.g.dart b/flutter/lib/src/native/platform/peer_connection.g.dart index 501aaa7cb..120c0cda4 100644 --- a/flutter/lib/src/native/platform/peer_connection.g.dart +++ b/flutter/lib/src/native/platform/peer_connection.g.dart @@ -15,6 +15,8 @@ void registerFunction( required Pointer> onTrack, required Pointer> onIceCandidate, + required Pointer> + onIceCandidateError, required Pointer)>> getTransceiverByMid, required Pointer> @@ -52,6 +54,7 @@ void registerFunction( Pointer, Pointer, Pointer, + Pointer, Pointer), void Function( Pointer, @@ -70,6 +73,7 @@ void registerFunction( Pointer, Pointer, Pointer, + Pointer, Pointer)>('register_peer_connection')( iceConnectionState, onConnectionStateChange, @@ -78,6 +82,7 @@ void registerFunction( rollback, onTrack, onIceCandidate, + onIceCandidateError, getTransceiverByMid, addIceCandidate, onIceConnectionStateChange, diff --git a/flutter/pubspec.yaml b/flutter/pubspec.yaml index 81697f19e..de95dd6ce 100644 --- a/flutter/pubspec.yaml +++ b/flutter/pubspec.yaml @@ -1,6 +1,6 @@ name: medea_jason description: Cross-platform client library of Medea media server for Flutter. -version: 0.4.1-dev +version: 0.5.0-dev homepage: https://github.com/instrumentisto/medea-jason/blob/master/flutter environment: @@ -24,7 +24,7 @@ dependencies: dev_dependencies: build_runner: ^2.4.5 - ffigen: 11.0.0 + ffigen: 9.0.0 flutter_gherkin: 3.0.0-rc.17 flutter_lints: ^3.0.1 flutter_test: @@ -43,6 +43,9 @@ dependency_overrides: # Pinned until gql-dart updates to v4 # https://github.com/gql-dart/gql/blob/master/links/gql_websocket_link/pubspec.yaml#L13 uuid: ^3.0.7 + # Pinned until flutter_rust_bridge update to v11 + # https://github.com/fzyzcjy/flutter_rust_bridge/pull/1757 + ffigen: 9.0.0 flutter: diff --git a/mock/control-api/Cargo.toml b/mock/control-api/Cargo.toml index c1d50f041..b6890bba4 100644 --- a/mock/control-api/Cargo.toml +++ b/mock/control-api/Cargo.toml @@ -20,7 +20,7 @@ clap = { version = "4.0", features = ["derive", "wrap_help"] } derive_more = "0.99" dotenv = "0.15" humantime-serde = "1.0" -medea-control-api-proto = { version = "0.11", path = "../../proto/control-api", features = ["grpc", "client"] } +medea-control-api-proto = { version = "0.12.0-dev", path = "../../proto/control-api", features = ["grpc", "client"] } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" slog = "2.5" diff --git a/proto/client-api/CHANGELOG.md b/proto/client-api/CHANGELOG.md index 5e9fc9e0a..98fdc2730 100644 --- a/proto/client-api/CHANGELOG.md +++ b/proto/client-api/CHANGELOG.md @@ -6,6 +6,23 @@ All user visible changes to this project will be documented in this file. This p +## [0.6.0] · 2024-??-?? (unreleased) +[0.6.0]: /../../tree/medea-client-api-proto-0.6.0/proto/client-api + +## BC Breaks + +- `PeerMetrics::PeerConnectionError` variant ([#151]). + +## Added + +- `PeerConnectionError` type ([#151]). +- `IceCandidateError` type ([#151]). + +[#151]: /../../pull/151 + + + + ## [0.5.0] · 2023-07-11 [0.5.0]: /../../tree/medea-client-api-proto-0.5.0/proto/client-api diff --git a/proto/client-api/Cargo.toml b/proto/client-api/Cargo.toml index f2fa3d72f..eab731988 100644 --- a/proto/client-api/Cargo.toml +++ b/proto/client-api/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "medea-client-api-proto" -version = "0.5.0" +version = "0.6.0-dev" edition = "2021" rust-version = "1.65" description = "Client API protocol implementation for Medea media server." diff --git a/proto/client-api/src/lib.rs b/proto/client-api/src/lib.rs index 311f52cb5..9822427d4 100644 --- a/proto/client-api/src/lib.rs +++ b/proto/client-api/src/lib.rs @@ -372,10 +372,69 @@ pub enum PeerMetrics { /// `PeerConnection`'s connection state. PeerConnectionState(PeerConnectionState), + /// `PeerConnection` related error occurred. + PeerConnectionError(PeerConnectionError), + /// `PeerConnection`'s RTC stats. RtcStats(Vec), } +/// Possible errors related to a `PeerConnection`. +#[cfg_attr(feature = "client", derive(Serialize))] +#[cfg_attr(feature = "server", derive(Deserialize))] +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum PeerConnectionError { + /// Error occurred with ICE candidate from a `PeerConnection`. + IceCandidate(IceCandidateError), +} + +/// Error occurred with an [ICE] candidate from a `PeerConnection`. +/// +/// [ICE]: https://webrtcglossary.com/ice +#[cfg_attr(feature = "client", derive(Serialize))] +#[cfg_attr(feature = "server", derive(Deserialize))] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct IceCandidateError { + /// Local IP address used to communicate with a [STUN]/[TURN] server. + /// + /// [STUN]: https://webrtcglossary.com/stun + /// [TURN]: https://webrtcglossary.com/turn + pub address: Option, + + /// Port used to communicate with a [STUN]/[TURN] server. + /// + /// [STUN]: https://webrtcglossary.com/stun + /// [TURN]: https://webrtcglossary.com/turn + pub port: Option, + + /// URL identifying the [STUN]/[TURN] server for which the failure + /// occurred. + /// + /// [STUN]: https://webrtcglossary.com/stun + /// [TURN]: https://webrtcglossary.com/turn + pub url: String, + + /// Numeric [STUN] error code returned by the [STUN]/[TURN] server. + /// + /// If no host candidate can reach the server, this error code will be set + /// to the value `701`, which is outside the [STUN] error code range. This + /// error is only fired once per server URL while in the + /// `RTCIceGatheringState` of "gathering". + /// + /// [STUN]: https://webrtcglossary.com/stun + /// [TURN]: https://webrtcglossary.com/turn + pub error_code: i32, + + /// [STUN] reason text returned by the [STUN]/[TURN] server. + /// + /// If the server could not be reached, this reason test will be set to an + /// implementation-specific value providing details about the error. + /// + /// [STUN]: https://webrtcglossary.com/stun + /// [TURN]: https://webrtcglossary.com/turn + pub error_text: String, +} + /// `PeerConnection`'s ICE connection state. #[cfg_attr(feature = "client", derive(Serialize))] #[cfg_attr(feature = "server", derive(Deserialize))] diff --git a/proto/control-api/CHANGELOG.md b/proto/control-api/CHANGELOG.md index 37e8179a1..4d5c2b78b 100644 --- a/proto/control-api/CHANGELOG.md +++ b/proto/control-api/CHANGELOG.md @@ -6,6 +6,21 @@ All user visible changes to this project will be documented in this file. This p +## [0.12.0] · 2024-??-?? (unreleased) +[0.12.0]: /../../tree/medea-control-api-proto-0.12.0/proto/control-api + +[Diff](/../../compare/medea-control-api-proto-0.11.0...medea-control-api-proto-0.12.0) + +### Upgraded + +- Dependencies: + - [`medea-client-api-proto`] to `0.6` ([#151]). + +[#151]: /../../pull/151 + + + + ## [0.11.0] · 2024-02-12 [0.11.0]: /../../tree/medea-control-api-proto-0.11.0/proto/control-api diff --git a/proto/control-api/Cargo.toml b/proto/control-api/Cargo.toml index c22231543..8333f0485 100644 --- a/proto/control-api/Cargo.toml +++ b/proto/control-api/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "medea-control-api-proto" -version = "0.11.0" +version = "0.12.0-dev" edition = "2021" rust-version = "1.62" description = "Control API protocol implementation for Medea media server." @@ -27,7 +27,7 @@ async-trait = "0.1.34" derive_more = { version = "0.99.17", features = ["as_ref", "display", "error", "from", "from_str", "into"], default-features = false } futures = { version = "0.3.21", optional = true } humantime-serde = { version = "1.1", optional = true } -medea-client-api-proto = { version = "0.5", path = "../client-api", optional = true } +medea-client-api-proto = { version = "0.6.0-dev", path = "../client-api", optional = true } prost = { version = "0.12", optional = true } prost-types = { version = "0.12", optional = true } rand = "0.8" diff --git a/src/peer/mod.rs b/src/peer/mod.rs index 1184e798c..26545127c 100644 --- a/src/peer/mod.rs +++ b/src/peer/mod.rs @@ -136,6 +136,55 @@ pub enum PeerEvent { sdp_mid: Option, }, + /// Error occurred with an [ICE] candidate from a [`PeerConnection`]. + /// + /// [ICE]: https://webrtcglossary.com/ice + IceCandidateError { + /// ID of the [`PeerConnection`] that errored. + peer_id: Id, + + /// Local IP address used to communicate with a [STUN]/[TURN] + /// server. + /// + /// [STUN]: https://webrtcglossary.com/stun + /// [TURN]: https://webrtcglossary.com/turn + address: Option, + + /// Port used to communicate with a [STUN]/[TURN] server. + /// + /// [STUN]: https://webrtcglossary.com/stun + /// [TURN]: https://webrtcglossary.com/turn + port: Option, + + /// URL identifying the [STUN]/[TURN] server for which the failure + /// occurred. + /// + /// [STUN]: https://webrtcglossary.com/stun + /// [TURN]: https://webrtcglossary.com/turn + url: String, + + /// Numeric [STUN] error code returned by the [STUN]/[TURN] server. + /// + /// If no host candidate can reach the server, this error code will be + /// set to the value `701`, which is outside the [STUN] error code + /// range. This error is only fired once per server URL while in the + /// `RTCIceGatheringState` of "gathering". + /// + /// [STUN]: https://webrtcglossary.com/stun + /// [TURN]: https://webrtcglossary.com/turn + error_code: i32, + + /// [STUN] reason text returned by the [STUN]/[TURN] server. + /// + /// If the server could not be reached, this reason test will be set to + /// an implementation-specific value providing details about + /// the error. + /// + /// [STUN]: https://webrtcglossary.com/stun + /// [TURN]: https://webrtcglossary.com/turn + error_text: String, + }, + /// [`platform::RtcPeerConnection`] received a new [`remote::Track`] from /// a remote sender. NewRemoteTrack { @@ -348,22 +397,40 @@ impl PeerConnection { recv_constraints, }; + peer.bind_event_listeners(state); + + Ok(Rc::new(peer)) + } + + /// Binds all the necessary event listeners to this [`PeerConnection`]. + fn bind_event_listeners(&self, state: &State) { // Bind to `icecandidate` event. { - let id = peer.id; - let weak_sender = Rc::downgrade(&peer.peer_events_sender); - peer.peer.on_ice_candidate(Some(move |candidate| { + let id = self.id; + let weak_sender = Rc::downgrade(&self.peer_events_sender); + self.peer.on_ice_candidate(Some(move |candidate| { if let Some(sender) = weak_sender.upgrade() { Self::on_ice_candidate(id, &sender, candidate); } })); } + // Bind to `icecandidateerror` event. + { + let id = self.id; + let weak_sender = Rc::downgrade(&self.peer_events_sender); + self.peer.on_ice_candidate_error(Some(move |error| { + if let Some(sender) = weak_sender.upgrade() { + Self::on_ice_candidate_error(id, &sender, error); + } + })); + } + // Bind to `iceconnectionstatechange` event. { - let id = peer.id; - let weak_sender = Rc::downgrade(&peer.peer_events_sender); - peer.peer.on_ice_connection_state_change(Some( + let id = self.id; + let weak_sender = Rc::downgrade(&self.peer_events_sender); + self.peer.on_ice_connection_state_change(Some( move |ice_connection_state| { if let Some(sender) = weak_sender.upgrade() { Self::on_ice_connection_state_changed( @@ -378,9 +445,9 @@ impl PeerConnection { // Bind to `connectionstatechange` event. { - let id = peer.id; - let weak_sender = Rc::downgrade(&peer.peer_events_sender); - peer.peer.on_connection_state_change(Some( + let id = self.id; + let weak_sender = Rc::downgrade(&self.peer_events_sender); + self.peer.on_connection_state_change(Some( move |peer_connection_state| { if let Some(sender) = weak_sender.upgrade() { Self::on_connection_state_changed( @@ -395,9 +462,9 @@ impl PeerConnection { // Bind to `track` event. { - let media_conns = Rc::downgrade(&peer.media_connections); + let media_conns = Rc::downgrade(&self.media_connections); let connection_mode = state.connection_mode(); - peer.peer.on_track(Some(move |track, transceiver| { + self.peer.on_track(Some(move |track, transceiver| { if let Some(c) = media_conns.upgrade() { platform::spawn(async move { if let (Err(mid), ConnectionMode::Mesh) = ( @@ -412,8 +479,6 @@ impl PeerConnection { } })); } - - Ok(Rc::new(peer)) } /// Handles [`TrackEvent`]s emitted from a [`Sender`] or a [`Receiver`]. @@ -564,6 +629,24 @@ impl PeerConnection { })); } + /// Handle `icecandidateerror` event from underlying peer emitting + /// [`PeerEvent::IceCandidateError`] event into this peers + /// `peer_events_sender`. + fn on_ice_candidate_error( + id: Id, + sender: &mpsc::UnboundedSender, + error: platform::IceCandidateError, + ) { + drop(sender.unbounded_send(PeerEvent::IceCandidateError { + peer_id: id, + address: error.address, + port: error.port, + url: error.url, + error_code: error.error_code, + error_text: error.error_text, + })); + } + /// Handle `iceconnectionstatechange` event from underlying peer emitting /// [`PeerEvent::IceConnectionStateChanged`] event into this peers /// `peer_events_sender`. @@ -1064,5 +1147,9 @@ impl Drop for PeerConnection { >>(None); self.peer .on_ice_candidate::>(None); + self.peer + .on_ice_candidate_error::>(None); } } diff --git a/src/platform/dart/ice_candidate.rs b/src/platform/dart/ice_candidate.rs index 013d5c646..119cda1c4 100644 --- a/src/platform/dart/ice_candidate.rs +++ b/src/platform/dart/ice_candidate.rs @@ -35,6 +35,125 @@ mod ice_candidate { } } +#[dart_bridge("flutter/lib/src/native/platform/ice_candidate_error.g.dart")] +mod ice_candidate_error { + use std::{os::raw::c_char, ptr}; + + use dart_sys::Dart_Handle; + + extern "C" { + /// Returns the local IP address used to communicate with a + /// [STUN]/[TURN] server. + /// + /// [STUN]: https://webrtcglossary.com/stun + /// [TURN]: https://webrtcglossary.com/turn + pub fn address(error: Dart_Handle) -> ptr::NonNull; + + /// Returns the port used to communicate with a [STUN]/[TURN] server. + /// + /// [STUN]: https://webrtcglossary.com/stun + /// [TURN]: https://webrtcglossary.com/turn + pub fn port(error: Dart_Handle) -> u32; + + /// Returns the URL identifying the [STUN]/[TURN] server for which the + /// failure occurred. + /// + /// [STUN]: https://webrtcglossary.com/stun + /// [TURN]: https://webrtcglossary.com/turn + pub fn url(error: Dart_Handle) -> ptr::NonNull; + + /// Returns the Numeric [STUN] error code returned by the [STUN]/[TURN] + /// server. + /// + /// If no host candidate can reach the server, this error code will be + /// set to the value `701`, which is outside the [STUN] error code + /// range. This error is only fired once per server URL while in the + /// `RTCIceGatheringState` of "gathering". + /// + /// [STUN]: https://webrtcglossary.com/stun + /// [TURN]: https://webrtcglossary.com/turn + pub fn error_code(error: Dart_Handle) -> i32; + + /// [STUN] reason text returned by the [STUN]/[TURN] server. + /// + /// If the server could not be reached, this reason test will be set to + /// an implementation-specific value providing details about the error. + /// + /// [STUN]: https://webrtcglossary.com/stun + /// [TURN]: https://webrtcglossary.com/turn + pub fn error_text(error: Dart_Handle) -> ptr::NonNull; + } +} + +/// Error occurred with an [ICE] candidate from a [`PeerConnection`]. +/// +/// [`PeerConnection`]: crate::peer::PeerConnection +/// [ICE]: https://webrtcglossary.com/ice +#[derive(Debug, From)] +pub struct IceCandidateError(DartHandle); + +impl IceCandidateError { + /// Returns the local IP address used to communicate with a [STUN]/[TURN] + /// server. + /// + /// [STUN]: https://webrtcglossary.com/stun + /// [TURN]: https://webrtcglossary.com/turn + #[must_use] + pub fn address(&self) -> String { + let address = unsafe { ice_candidate_error::address(self.0.get()) }; + unsafe { dart_string_into_rust(address) } + } + + /// Returns the port used to communicate with a [STUN]/[TURN] server. + /// + /// [STUN]: https://webrtcglossary.com/stun + /// [TURN]: https://webrtcglossary.com/turn + #[must_use] + pub fn port(&self) -> u32 { + unsafe { ice_candidate_error::port(self.0.get()) } + } + + /// Returns the URL identifying the [STUN]/[TURN] server for which the + /// failure occurred. + /// + /// [STUN]: https://webrtcglossary.com/stun + /// [TURN]: https://webrtcglossary.com/turn + #[must_use] + pub fn url(&self) -> String { + let url = unsafe { ice_candidate_error::url(self.0.get()) }; + unsafe { dart_string_into_rust(url) } + } + + /// Returns the Numeric [STUN] error code returned by the [STUN]/[TURN] + /// server. + /// + /// If no host candidate can reach the server, this error code will be set + /// to the value `701`, which is outside the [STUN] error code range. This + /// error is only fired once per server URL while in the + /// `RTCIceGatheringState` of "gathering". + /// + /// [STUN]: https://webrtcglossary.com/stun + /// [TURN]: https://webrtcglossary.com/turn + #[must_use] + pub fn error_code(&self) -> i32 { + unsafe { ice_candidate_error::error_code(self.0.get()) } + } + + /// [STUN] reason text returned by the [STUN]/[TURN] server. + /// + /// If the server could not be reached, this reason test will be set to an + /// implementation-specific value providing details about the error. + /// + /// [STUN]: https://webrtcglossary.com/stun + /// [TURN]: https://webrtcglossary.com/turn + #[must_use] + pub fn error_text(&self) -> String { + let error_text = + unsafe { ice_candidate_error::error_text(self.0.get()) }; + unsafe { dart_string_into_rust(error_text) } + } +} + /// Wrapper around a [`DartHandle`] representing an ICE candidate of a /// [RTCPeerConnection][1]. /// diff --git a/src/platform/dart/peer_connection.rs b/src/platform/dart/peer_connection.rs index f9f070eaf..c26604b1e 100644 --- a/src/platform/dart/peer_connection.rs +++ b/src/platform/dart/peer_connection.rs @@ -23,14 +23,18 @@ use crate::{ peer_connection_state_from_int, }, }, - IceCandidate, RtcPeerConnectionError, RtcStats, SdpType, - TransceiverDirection, + IceCandidate, IceCandidateError, RtcPeerConnectionError, RtcStats, + SdpType, TransceiverDirection, }, }; use super::{ - ice_candidate::IceCandidate as PlatformIceCandidate, - media_track::MediaStreamTrack, utils::string_into_c_str, + ice_candidate::{ + IceCandidate as PlatformIceCandidate, + IceCandidateError as PlatformIceCandidateError, + }, + media_track::MediaStreamTrack, + utils::string_into_c_str, }; type RtcPeerConnectionResult = Result>; @@ -71,6 +75,10 @@ mod peer_connection { /// Sets `onIceCandidate` callback of the provided [`PeerConnection`]. pub fn on_ice_candidate(peer: Dart_Handle, cb: Dart_Handle); + /// Sets `onIceCandidateError` callback of the provided + /// [`PeerConnection`]. + pub fn on_ice_candidate_error(peer: Dart_Handle, cb: Dart_Handle); + /// Looks ups [`Transceiver`] in the provided [`PeerConnection`] by the /// provided [`String`]. pub fn get_transceiver_by_mid( @@ -230,6 +238,35 @@ impl RtcPeerConnection { } } + /// Sets `handler` for an [RTCPeerConnectionIceEvent][1] (see the + /// [`onicecandidateerror` callback][2]). + /// + /// [1]: https://w3.org/TR/webrtc#dom-rtcpeerconnectioniceevent + /// [2]: https://w3.org/TR/webrtc#dom-rtcpeerconnection-onicecandidateerror + pub fn on_ice_candidate_error(&self, handler: Option) + where + F: 'static + FnMut(IceCandidateError), + { + if let Some(mut h) = handler { + unsafe { + peer_connection::on_ice_candidate_error( + self.handle.get(), + Callback::from_fn_mut(move |handle: DartHandle| { + let candidate = PlatformIceCandidateError::from(handle); + h(IceCandidateError { + address: Some(candidate.address()), + port: Some(candidate.port()), + url: candidate.url(), + error_code: candidate.error_code(), + error_text: candidate.error_text(), + }); + }) + .into_dart(), + ); + } + } + } + /// Returns [`IceConnectionState`] of this [`RtcPeerConnection`]. #[must_use] pub fn ice_connection_state(&self) -> IceConnectionState { diff --git a/src/platform/mod.rs b/src/platform/mod.rs index 2d0b38fa6..298b6588e 100644 --- a/src/platform/mod.rs +++ b/src/platform/mod.rs @@ -22,7 +22,9 @@ use crate::utils::Caused; pub use self::{ callback::Callback, - peer_connection::{IceCandidate, RtcPeerConnectionError, SdpType}, + peer_connection::{ + IceCandidate, IceCandidateError, RtcPeerConnectionError, SdpType, + }, rtc_stats::RtcStatsError, transceiver::Direction as TransceiverDirection, transport::{RpcTransport, TransportError, TransportState}, diff --git a/src/platform/peer_connection.rs b/src/platform/peer_connection.rs index a8a5f95e9..934a7c9bb 100644 --- a/src/platform/peer_connection.rs +++ b/src/platform/peer_connection.rs @@ -47,6 +47,51 @@ pub struct IceCandidate { pub sdp_mid: Option, } +/// Error occurred with an [ICE] candidate from a `PeerConnection`. +/// +/// [ICE]: https://webrtcglossary.com/ice +#[derive(Debug)] +pub struct IceCandidateError { + /// Local IP address used to communicate with a [STUN]/[TURN] server. + /// + /// [STUN]: https://webrtcglossary.com/stun + /// [TURN]: https://webrtcglossary.com/turn + pub address: Option, + + /// Port used to communicate with a [STUN]/[TURN] server. + /// + /// [STUN]: https://webrtcglossary.com/stun + /// [TURN]: https://webrtcglossary.com/turn + pub port: Option, + + /// URL identifying the [STUN]/[TURN] server for which the failure + /// occurred. + /// + /// [STUN]: https://webrtcglossary.com/stun + /// [TURN]: https://webrtcglossary.com/turn + pub url: String, + + /// Numeric [STUN] error code returned by the [STUN]/[TURN] server. + /// + /// If no host candidate can reach the server, this error code will be set + /// to the value `701`, which is outside the [STUN] error code range. This + /// error is only fired once per server URL while in the + /// `RTCIceGatheringState` of "gathering". + /// + /// [STUN]: https://webrtcglossary.com/stun + /// [TURN]: https://webrtcglossary.com/turn + pub error_code: i32, + + /// [STUN] reason text returned by the [STUN]/[TURN] server. + /// + /// If the server could not be reached, this reason test will be set to an + /// implementation-specific value providing details about the error. + /// + /// [STUN]: https://webrtcglossary.com/stun + /// [TURN]: https://webrtcglossary.com/turn + pub error_text: String, +} + /// Errors that may occur during signaling between this and remote /// [RTCPeerConnection][1] and event handlers setting errors. /// diff --git a/src/platform/wasm/peer_connection.rs b/src/platform/wasm/peer_connection.rs index f4f14da78..b8b40af45 100644 --- a/src/platform/wasm/peer_connection.rs +++ b/src/platform/wasm/peer_connection.rs @@ -18,9 +18,10 @@ use wasm_bindgen_futures::JsFuture; use web_sys::{ Event, RtcBundlePolicy, RtcConfiguration, RtcIceCandidateInit, RtcIceConnectionState, RtcIceTransportPolicy, RtcOfferOptions, - RtcPeerConnection as SysRtcPeerConnection, RtcPeerConnectionIceEvent, - RtcRtpTransceiver, RtcRtpTransceiverInit, RtcSdpType, - RtcSessionDescription, RtcSessionDescriptionInit, RtcTrackEvent, + RtcPeerConnection as SysRtcPeerConnection, RtcPeerConnectionIceErrorEvent, + RtcPeerConnectionIceEvent, RtcRtpTransceiver, RtcRtpTransceiverInit, + RtcSdpType, RtcSessionDescription, RtcSessionDescriptionInit, + RtcTrackEvent, }; use crate::{ @@ -28,8 +29,9 @@ use crate::{ platform::{ self, wasm::{get_property_by_name, utils::EventListener}, - IceCandidate, MediaStreamTrack, RtcPeerConnectionError, RtcStats, - SdpType, Transceiver, TransceiverDirection, + IceCandidate, IceCandidateError, MediaStreamTrack, + RtcPeerConnectionError, RtcStats, SdpType, Transceiver, + TransceiverDirection, }, }; @@ -64,6 +66,22 @@ pub struct RtcPeerConnection { Option>, >, + /// [`onicecandidateerror`][2] callback of an [RTCPeerConnection][1] to + /// handle the [`icecandidateerror`][3] event. + /// + /// It fires when an [RTCPeerConnectionIceErrorEvent][4] occurs on an + /// [RTCPeerConnection][1]. + /// + /// [1]: https://w3.org/TR/webrtc#rtcpeerconnection-interface + /// [2]: https://w3.org/TR/webrtc#dom-rtcpeerconnection-onicecandidateerror + /// [3]: https://w3.org/TR/webrtc#event-icecandidateerror + /// [4]: https://w3.org/TR/webrtc#dom-rtcpeerconnectioniceerrorevent + on_ice_candidate_error: RefCell< + Option< + EventListener, + >, + >, + /// [`iceconnectionstatechange`][2] callback of [RTCPeerConnection][1], /// fires whenever [ICE connection state][3] changes. /// @@ -132,6 +150,7 @@ impl RtcPeerConnection { peer: Rc::new(peer), ice_restart: Cell::new(false), on_ice_candidate: RefCell::new(None), + on_ice_candidate_error: RefCell::new(None), on_ice_connection_state_changed: RefCell::new(None), on_connection_state_changed: RefCell::new(None), on_track: RefCell::new(None), @@ -197,6 +216,49 @@ impl RtcPeerConnection { }); } + /// Sets handler for an [`RtcPeerConnectionIceErrorEvent`] (see the + /// [RTCPeerConnectionIceErrorEvent][1] and the + /// [`onicecandidateerror` callback][2]). + /// + /// # Panics + /// + /// If binding to the [`icecandidateerror`][3] event fails. Not supposed to + /// ever happen. + /// + /// [1]: https://w3.org/TR/webrtc#dom-rtcpeerconnectioniceerrorevent + /// [2]: https://w3.org/TR/webrtc#dom-rtcpeerconnection-onicecandidateerror + /// [3]: https://w3.org/TR/webrtc#event-icecandidateerror + pub fn on_ice_candidate_error(&self, f: Option) + where + F: 'static + FnMut(IceCandidateError), + { + let mut on_ice_candidate_error = + self.on_ice_candidate_error.borrow_mut(); + drop(match f { + None => on_ice_candidate_error.take(), + Some(mut f) => { + on_ice_candidate_error.replace( + // Unwrapping is OK here, because this function shouldn't + // error ever. + EventListener::new_mut( + Rc::clone(&self.peer), + "icecandidateerror", + move |msg: RtcPeerConnectionIceErrorEvent| { + f(IceCandidateError { + address: msg.address(), + port: msg.port().map(u32::from), + url: msg.url(), + error_code: i32::from(msg.error_code()), + error_text: msg.error_text(), + }); + }, + ) + .unwrap(), + ) + } + }); + } + /// Sets handler for a [`RtcPeerConnectionIceEvent`] (see /// [RTCPeerConnectionIceEvent][1] and [`onicecandidate` callback][2]). /// @@ -631,6 +693,7 @@ impl Drop for RtcPeerConnection { fn drop(&mut self) { drop(self.on_track.borrow_mut().take()); drop(self.on_ice_candidate.borrow_mut().take()); + drop(self.on_ice_candidate_error.borrow_mut().take()); drop(self.on_ice_connection_state_changed.borrow_mut().take()); drop(self.on_connection_state_changed.borrow_mut().take()); self.peer.close(); diff --git a/src/room.rs b/src/room.rs index 2caccc7ce..180cb43da 100644 --- a/src/room.rs +++ b/src/room.rs @@ -18,10 +18,10 @@ use futures::{ use medea_client_api_proto::{ self as proto, Command, ConnectionQualityScore, Event as RpcEvent, EventHandler, IceCandidate, IceConnectionState, IceServer, MemberId, - NegotiationRole, PeerConnectionState, PeerId, PeerMetrics, PeerUpdate, - Track, TrackId, + NegotiationRole, PeerConnectionError, PeerConnectionState, PeerId, + PeerMetrics, PeerUpdate, Track, TrackId, }; -use proto::ConnectionMode; +use proto::{ConnectionMode, IceCandidateError}; use tracerr::Traced; use crate::{ @@ -1777,6 +1777,32 @@ impl PeerEventHandler for InnerRoom { Ok(()) } + /// Handles [`PeerEvent::IceCandidateError`] event and sends the received + /// error to RPC server. + async fn on_ice_candidate_error( + &self, + peer_id: PeerId, + address: Option, + port: Option, + url: String, + error_code: i32, + error_text: String, + ) -> Self::Output { + self.rpc.send_command(Command::AddPeerConnectionMetrics { + peer_id, + metrics: PeerMetrics::PeerConnectionError( + PeerConnectionError::IceCandidate(IceCandidateError { + address, + port, + url, + error_code, + error_text, + }), + ), + }); + Ok(()) + } + /// Handles [`PeerEvent::NewRemoteTrack`] event and passes received /// [`remote::Track`] to the related [`Connection`]. ///