diff --git a/.editorconfig b/.editorconfig index 60429d4e0..ec3ffa235 100644 --- a/.editorconfig +++ b/.editorconfig @@ -21,6 +21,10 @@ indent_size = 4 indent_style = space indent_size = 2 +[*.{c,h}] +indent_style = space +indent_size = 2 + [*.html] indent_style = space indent_size = 2 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 73ad2c4d6..ef3be8a4b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -275,6 +275,7 @@ jobs: - name: Compile shared object run: make cargo.build crate=medea-jason platform=android targets=x86 + args="--features mockable" - uses: reactivecircus/android-emulator-runner@v2 with: diff --git a/Cargo.lock b/Cargo.lock index 2cef1eb40..6d84bccad 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -140,7 +140,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4ca8ce00b267af8ccebbd647de0d61e0674b6e61185cc7a592ff88772bed655" dependencies = [ "quote 1.0.9", - "syn 1.0.70", + "syn 1.0.71", ] [[package]] @@ -325,7 +325,7 @@ checksum = "ad26f77093333e0e7c6ffe54ebe3582d908a104e448723eec6d43d08b07143fb" dependencies = [ "proc-macro2 1.0.26", "quote 1.0.9", - "syn 1.0.70", + "syn 1.0.71", ] [[package]] @@ -336,7 +336,7 @@ checksum = "b95aceadaf327f18f0df5962fedc1bde2f870566a0b9f65c89508a3b1f79334c" dependencies = [ "proc-macro2 1.0.26", "quote 1.0.9", - "syn 1.0.70", + "syn 1.0.71", ] [[package]] @@ -363,6 +363,24 @@ dependencies = [ "memchr", ] +[[package]] +name = "android_log-sys" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85965b6739a430150bdd138e2374a98af0c3ee0d030b3bb7fc3bddff58d0102e" + +[[package]] +name = "android_logger" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9ed09b18365ed295d722d0b5ed59c01b79a826ff2d2a8f73d5ecca8e6fb2f66" +dependencies = [ + "android_log-sys", + "env_logger", + "lazy_static", + "log", +] + [[package]] name = "ansi_term" version = "0.11.0" @@ -409,16 +427,16 @@ dependencies = [ [[package]] name = "async-executor" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb877970c7b440ead138f6321a3b5395d6061183af779340b65e20c0fede9146" +checksum = "871f9bb5e0a22eeb7e8cf16641feb87c9dc67032ccf8ff49e772eb9941d3a965" dependencies = [ "async-task", "concurrent-queue", "fastrand", "futures-lite", "once_cell", - "vec-arena", + "slab", ] [[package]] @@ -439,9 +457,9 @@ dependencies = [ [[package]] name = "async-io" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcb9af4888a70ad78ecb5efcb0ba95d66a3cf54a88b62ae81559954c7588c7a2" +checksum = "4bbfd5cf2794b1e908ea8457e6c45f8f8f1f6ec5f74617bf4662623f47503c3b" dependencies = [ "concurrent-queue", "fastrand", @@ -451,8 +469,8 @@ dependencies = [ "once_cell", "parking", "polling", + "slab", "socket2 0.4.0", - "vec-arena", "waker-fn", "winapi 0.3.9", ] @@ -483,7 +501,7 @@ checksum = "d7d78656ba01f1b93024b7c3a0467f1608e4be67d725749fdcd7d2c7678fd7a2" dependencies = [ "proc-macro2 1.0.26", "quote 1.0.9", - "syn 1.0.70", + "syn 1.0.71", ] [[package]] @@ -541,7 +559,7 @@ checksum = "25f9db3b38af870bf7e5cc649167533b493928e50744e2c30ae350230b414670" dependencies = [ "proc-macro2 1.0.26", "quote 1.0.9", - "syn 1.0.70", + "syn 1.0.71", ] [[package]] @@ -552,7 +570,7 @@ checksum = "db134ba52475c060f3329a8ef0f8786d6b872ed01515d4b79c162e5798da1340" dependencies = [ "proc-macro2 1.0.26", "quote 1.0.9", - "syn 1.0.70", + "syn 1.0.71", ] [[package]] @@ -569,7 +587,7 @@ checksum = "0b98e84bbb4cbcdd97da190ba0c58a1bb0de2c1fdf67d159e192ed766aeca722" dependencies = [ "proc-macro2 1.0.26", "quote 1.0.9", - "syn 1.0.70", + "syn 1.0.71", ] [[package]] @@ -621,11 +639,12 @@ dependencies = [ [[package]] name = "backtrace" -version = "0.3.57" +version = "0.3.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ed203b9ba68b242c62b3fb7480f589dd49829be1edb3fe8fc8b4ffda2dcb8d" +checksum = "88fb5a785d6b44fd9d6700935608639af1b8356de1e55d5f7c2740f4faa15d82" dependencies = [ "addr2line", + "cc", "cfg-if 1.0.0", "libc", "miniz_oxide", @@ -989,7 +1008,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e98e2ad1a782e33928b96fc3948e7c355e5af34ba4de7670fe8bac2a3b2006d" dependencies = [ "quote 1.0.9", - "syn 1.0.70", + "syn 1.0.71", ] [[package]] @@ -1027,7 +1046,7 @@ dependencies = [ "proc-macro2 1.0.26", "quote 1.0.9", "regex", - "syn 1.0.70", + "syn 1.0.71", ] [[package]] @@ -1037,7 +1056,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed431abf442833fd62ad7cc527a3833d969155c803f0dcf7f8a68db8adddc4c5" dependencies = [ "quote 1.0.9", - "syn 1.0.70", + "syn 1.0.71", ] [[package]] @@ -1071,7 +1090,7 @@ dependencies = [ "proc-macro2 1.0.26", "quote 1.0.9", "strsim 0.9.3", - "syn 1.0.70", + "syn 1.0.71", ] [[package]] @@ -1085,7 +1104,7 @@ dependencies = [ "proc-macro2 1.0.26", "quote 1.0.9", "strsim 0.10.0", - "syn 1.0.70", + "syn 1.0.71", ] [[package]] @@ -1096,7 +1115,7 @@ checksum = "d9b5a2f4ac4969822c62224815d069952656cadc7084fdca9751e6d959189b72" dependencies = [ "darling_core 0.10.2", "quote 1.0.9", - "syn 1.0.70", + "syn 1.0.71", ] [[package]] @@ -1107,9 +1126,15 @@ checksum = "29b5acf0dea37a7f66f7b25d2c5e93fd46f8f6968b1a5d7a3e02e97768afc95a" dependencies = [ "darling_core 0.12.4", "quote 1.0.9", - "syn 1.0.70", + "syn 1.0.71", ] +[[package]] +name = "dart-sys" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42fe64308f2072bb94ecd8f7557f9ac01b69d86e6199bac24be0f091ad74b092" + [[package]] name = "deadpool" version = "0.5.2" @@ -1147,7 +1172,7 @@ checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" dependencies = [ "proc-macro2 1.0.26", "quote 1.0.9", - "syn 1.0.70", + "syn 1.0.71", ] [[package]] @@ -1160,7 +1185,7 @@ dependencies = [ "derive_builder_core", "proc-macro2 1.0.26", "quote 1.0.9", - "syn 1.0.70", + "syn 1.0.71", ] [[package]] @@ -1172,7 +1197,7 @@ dependencies = [ "darling 0.10.2", "proc-macro2 1.0.26", "quote 1.0.9", - "syn 1.0.70", + "syn 1.0.71", ] [[package]] @@ -1184,7 +1209,7 @@ dependencies = [ "convert_case", "proc-macro2 1.0.26", "quote 1.0.9", - "syn 1.0.70", + "syn 1.0.71", ] [[package]] @@ -1271,7 +1296,17 @@ dependencies = [ "heck", "proc-macro2 1.0.26", "quote 1.0.9", - "syn 1.0.70", + "syn 1.0.71", +] + +[[package]] +name = "env_logger" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17392a012ea30ef05a610aa97dfb49496e71c9f676b27879922ea5bdf60d9d3f" +dependencies = [ + "log", + "regex", ] [[package]] @@ -1298,7 +1333,7 @@ checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4" dependencies = [ "proc-macro2 1.0.26", "quote 1.0.9", - "syn 1.0.70", + "syn 1.0.71", "synstructure", ] @@ -1313,7 +1348,7 @@ dependencies = [ "futures-core", "futures-util", "http", - "hyper 0.14.6", + "hyper 0.14.7", "hyper-tls", "mime", "serde 1.0.125", @@ -1325,9 +1360,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca5faf057445ce5c9d4329e382b2ce7ca38550ef3b73a5348362d5f24e0c7fe3" +checksum = "77b705829d1e87f762c2df6da140b26af5839e1033aa84aa5f56bb688e4e1bdb" dependencies = [ "instant", ] @@ -1516,7 +1551,7 @@ dependencies = [ "proc-macro-hack", "proc-macro2 1.0.26", "quote 1.0.9", - "syn 1.0.70", + "syn 1.0.71", ] [[package]] @@ -1616,7 +1651,7 @@ dependencies = [ "quote 1.0.9", "serde 1.0.125", "serde_json", - "syn 1.0.70", + "syn 1.0.71", "textwrap 0.12.1", "thiserror", "typed-builder", @@ -1630,7 +1665,7 @@ checksum = "1a5bcf1bbeab73aa4cf2fde60a846858dc036163c7c33bec309f8d17de785479" dependencies = [ "proc-macro2 1.0.26", "quote 1.0.9", - "syn 1.0.70", + "syn 1.0.71", ] [[package]] @@ -1867,9 +1902,9 @@ dependencies = [ [[package]] name = "hyper" -version = "0.14.6" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f006b8784cfb01fe7aa9c46f5f5cd4cf5c85a8c612a0653ec97642979062665" +checksum = "1e5f105c494081baa3bf9e200b279e27ec1623895cd504c7dbef8d0b080fcf54" dependencies = [ "bytes 1.0.1", "futures-channel", @@ -1896,7 +1931,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" dependencies = [ "bytes 1.0.1", - "hyper 0.14.6", + "hyper 0.14.7", "native-tls", "tokio 1.5.0", "tokio-native-tls", @@ -1981,7 +2016,7 @@ checksum = "75c094e94816723ab936484666968f5b58060492e880f3c8d00489a1e244fa51" dependencies = [ "proc-macro2 1.0.26", "quote 1.0.9", - "syn 1.0.70", + "syn 1.0.71", ] [[package]] @@ -2090,9 +2125,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.93" +version = "0.2.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9385f66bf6105b241aa65a61cb923ef20efc665cb9f9bb50ac2f0c4b7f378d41" +checksum = "18794a8ad5b29321f790b55d93dfba91e125cb1a9edbd4f8e3150acc771c1a5e" [[package]] name = "linked-hash-map" @@ -2183,7 +2218,7 @@ dependencies = [ "function_name", "futures 0.3.14", "humantime-serde", - "hyper 0.14.6", + "hyper 0.14.7", "lazy_static", "medea-client-api-proto 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "medea-control-api-mock", @@ -2330,16 +2365,21 @@ dependencies = [ name = "medea-jason" version = "0.2.0" dependencies = [ + "android_logger", "async-recursion", "async-trait", "bitflags", + "cc", + "cfg-if 1.0.0", "console_error_panic_hook", + "dart-sys", "derivative", "derive_more", "downcast", "fragile", "futures 0.3.14", "js-sys", + "libc", "log", "medea-client-api-proto 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "medea-macro 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2367,7 +2407,7 @@ dependencies = [ "medea-jason", "proc-macro2 1.0.26", "quote 1.0.9", - "syn 1.0.70", + "syn 1.0.71", "synstructure", ] @@ -2380,7 +2420,7 @@ dependencies = [ "Inflector", "proc-macro2 1.0.26", "quote 1.0.9", - "syn 1.0.70", + "syn 1.0.71", "synstructure", ] @@ -2527,7 +2567,7 @@ dependencies = [ "cfg-if 1.0.0", "proc-macro2 1.0.26", "quote 1.0.9", - "syn 1.0.70", + "syn 1.0.71", ] [[package]] @@ -2649,9 +2689,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "openssl" -version = "0.10.33" +version = "0.10.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a61075b62a23fef5a29815de7536d940aa35ce96d18ce0cc5076272db678a577" +checksum = "6d7830286ad6a3973c0f1d9b73738f69c76b739301d0229c4b96501695cbe4c8" dependencies = [ "bitflags", "cfg-if 1.0.0", @@ -2669,9 +2709,9 @@ checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" [[package]] name = "openssl-sys" -version = "0.9.61" +version = "0.9.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "313752393519e876837e09e1fa183ddef0be7735868dced3196f4472d536277f" +checksum = "fa52160d45fa2e7608d504b7c3a3355afed615e6d8b627a74458634ba21b69bd" dependencies = [ "autocfg", "cc", @@ -2786,7 +2826,7 @@ checksum = "3be26700300be6d9d23264c73211d8190e755b6b5ca7a1b28230025511b52a5e" dependencies = [ "proc-macro2 1.0.26", "quote 1.0.9", - "syn 1.0.70", + "syn 1.0.71", ] [[package]] @@ -2797,7 +2837,7 @@ checksum = "48c950132583b500556b1efd71d45b319029f2b71518d979fcc208e16b42426f" dependencies = [ "proc-macro2 1.0.26", "quote 1.0.9", - "syn 1.0.70", + "syn 1.0.71", ] [[package]] @@ -2845,9 +2885,9 @@ checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" [[package]] name = "predicates" -version = "1.0.7" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eeb433456c1a57cc93554dea3ce40b4c19c4057e41c55d4a0f3d84ea71c325aa" +checksum = "f49cfaf7fdaa3bfacc6fa3e7054e65148878354a5cfddcf661df4c851f8021df" dependencies = [ "difference", "float-cmp", @@ -2949,7 +2989,7 @@ dependencies = [ "itertools 0.8.2", "proc-macro2 1.0.26", "quote 1.0.9", - "syn 1.0.70", + "syn 1.0.71", ] [[package]] @@ -2964,9 +3004,9 @@ dependencies = [ [[package]] name = "protobuf" -version = "2.22.1" +version = "2.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b7f4a129bb3754c25a4e04032a90173c68f85168f77118ac4cb4936e7f06f92" +checksum = "45604fc7a88158e7d514d8e22e14ac746081e7a70d7690074dd0029ee37458d6" [[package]] name = "quick-error" @@ -3153,9 +3193,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8270314b5ccceb518e7e578952f0b72b88222d02e8f77f5ecf7abbb673539041" +checksum = "85dd92e586f7355c633911e11f77f3d12f04b1b1bd76a198bd34ae3af8341ef2" dependencies = [ "bitflags", ] @@ -3172,9 +3212,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.4.5" +version = "1.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "957056ecddbeba1b26965114e191d2e8589ce74db242b6ea25fc4062427a5c19" +checksum = "2a26af418b574bd56588335b3a3659a65725d4e636eb1016c2f9e3b38c7cc759" dependencies = [ "aho-corasick", "memchr", @@ -3209,7 +3249,7 @@ dependencies = [ "futures-util", "http", "http-body 0.4.1", - "hyper 0.14.6", + "hyper 0.14.7", "hyper-tls", "ipnet", "js-sys", @@ -3409,7 +3449,7 @@ checksum = "b093b7a2bb58203b5da3056c05b4ec1fed827dcfdb37347a8841695263b3d06d" dependencies = [ "proc-macro2 1.0.26", "quote 1.0.9", - "syn 1.0.70", + "syn 1.0.71", ] [[package]] @@ -3476,7 +3516,7 @@ dependencies = [ "darling 0.12.4", "proc-macro2 1.0.26", "quote 1.0.9", - "syn 1.0.70", + "syn 1.0.71", ] [[package]] @@ -3510,7 +3550,7 @@ checksum = "b2acd6defeddb41eb60bb468f8825d0cfd0c2a76bc03bfd235b6a1dc4f6a1ad5" dependencies = [ "proc-macro2 1.0.26", "quote 1.0.9", - "syn 1.0.70", + "syn 1.0.71", ] [[package]] @@ -3651,7 +3691,7 @@ checksum = "133659a15339456eeeb07572eb02a91c91e9815e9cbc89566944d2c8d3efdbf6" dependencies = [ "proc-macro2 1.0.26", "quote 1.0.9", - "syn 1.0.70", + "syn 1.0.71", ] [[package]] @@ -3714,7 +3754,7 @@ dependencies = [ "quote 1.0.9", "serde 1.0.125", "serde_derive", - "syn 1.0.70", + "syn 1.0.71", ] [[package]] @@ -3730,7 +3770,7 @@ dependencies = [ "serde_derive", "serde_json", "sha1", - "syn 1.0.70", + "syn 1.0.71", ] [[package]] @@ -3776,9 +3816,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.70" +version = "1.0.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9505f307c872bab8eb46f77ae357c8eba1fdacead58ee5a850116b1d7f82883" +checksum = "ad184cc9470f9117b2ac6817bfe297307418819ba40552f9b3846f05c33d5373" dependencies = [ "proc-macro2 1.0.26", "quote 1.0.9", @@ -3793,7 +3833,7 @@ checksum = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701" dependencies = [ "proc-macro2 1.0.26", "quote 1.0.9", - "syn 1.0.70", + "syn 1.0.71", "unicode-xid 0.2.1", ] @@ -3883,7 +3923,7 @@ checksum = "7765189610d8241a44529806d6fd1f2e0a08734313a35d5b3a556f92b381f3c0" dependencies = [ "proc-macro2 1.0.26", "quote 1.0.9", - "syn 1.0.70", + "syn 1.0.71", ] [[package]] @@ -3949,7 +3989,7 @@ dependencies = [ "proc-macro2 1.0.26", "quote 1.0.9", "standback", - "syn 1.0.70", + "syn 1.0.71", ] [[package]] @@ -4014,7 +4054,7 @@ checksum = "e44da00bfc73a25f814cd8d7e57a68a5c31b74b3152a0a1d1f590c97ed06265a" dependencies = [ "proc-macro2 1.0.26", "quote 1.0.9", - "syn 1.0.70", + "syn 1.0.71", ] [[package]] @@ -4025,7 +4065,7 @@ checksum = "caf7b11a536f46a809a8a9f0bb4237020f70ecbf115b842360afb127ea2fda57" dependencies = [ "proc-macro2 1.0.26", "quote 1.0.9", - "syn 1.0.70", + "syn 1.0.71", ] [[package]] @@ -4115,7 +4155,7 @@ dependencies = [ "proc-macro2 1.0.26", "prost-build", "quote 1.0.9", - "syn 1.0.70", + "syn 1.0.71", ] [[package]] @@ -4326,7 +4366,7 @@ checksum = "c42e6fa53307c8a17e4ccd4dc81cf5ec38db9209f59b222210375b54ee40d1e2" dependencies = [ "proc-macro2 1.0.26", "quote 1.0.9", - "syn 1.0.70", + "syn 1.0.71", ] [[package]] @@ -4407,7 +4447,7 @@ checksum = "f85f4270f4f449a3f2c0cf2aecc8415e388a597aeacc7d55fc749c5c968c8533" dependencies = [ "proc-macro2 1.0.26", "quote 1.0.9", - "syn 1.0.70", + "syn 1.0.71", ] [[package]] @@ -4509,12 +4549,6 @@ version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cbdbff6266a24120518560b5dc983096efb98462e51d0d68169895b237be3e5d" -[[package]] -name = "vec-arena" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34b2f665b594b07095e3ac3f718e13c2197143416fae4c5706cffb7b1af8d7f1" - [[package]] name = "vec_map" version = "0.8.2" @@ -4615,7 +4649,7 @@ dependencies = [ "log", "proc-macro2 1.0.26", "quote 1.0.9", - "syn 1.0.70", + "syn 1.0.71", "wasm-bindgen-shared", ] @@ -4649,7 +4683,7 @@ checksum = "d53739ff08c8a68b0fdbcd54c372b8ab800b1449ab3c9d706503bc7dd1621b2c" dependencies = [ "proc-macro2 1.0.26", "quote 1.0.9", - "syn 1.0.70", + "syn 1.0.71", "wasm-bindgen-backend", "wasm-bindgen-shared", ] diff --git a/Makefile b/Makefile index e3d8d24bb..a3cf11049 100644 --- a/Makefile +++ b/Makefile @@ -26,14 +26,10 @@ CHROME_VERSION := 89.0 FIREFOX_VERSION := 87.0 CARGO_NDK_VER := 2.3.0-ndkr22b-rust$(RUST_VER) -ANDROID_NDK_TARGETS := arm64-v8a \ - armeabi-v7a \ - x86 \ - x86_64 -ANDROID_RUST_TARGETS := aarch64-linux-android \ - armv7-linux-androideabi \ - i686-linux-android \ - x86_64-linux-android +ANDROID_TARGETS := aarch64-linux-android \ + armv7-linux-androideabi \ + i686-linux-android \ + x86_64-linux-android ANDROID_SDK_COMPILE_VERSION := $(strip \ $(shell grep compileSdkVersion jason/flutter/android/build.gradle \ | awk '{print $$2}')) @@ -232,12 +228,13 @@ cargo: # | crate=medea-jason [debug=(yes|no)] [dockerized=(no|yes)] # [( [platform=web] # | platform=android -# [targets=($(ANDROID_NDK_TARGETS)|[,...])] )] )] +# [targets=($(ANDROID_TARGETS)|[,...])] )] )] +# [args=] cargo-build-crate = $(if $(call eq,$(crate),),@all,$(crate)) cargo-build-platform = $(if $(call eq,$(platform),),web,$(platform)) cargo-build-targets = $(strip \ - $(if $(call eq,$(targets),),$(ANDROID_NDK_TARGETS),$(targets))) + $(if $(call eq,$(targets),),$(ANDROID_TARGETS),$(targets))) cargo.build: ifeq ($(cargo-build-crate),@all) @@ -254,7 +251,7 @@ ifeq ($(dockerized),yes) make cargo.build crate=$(cargo-build-crate) \ debug=$(debug) dockerized=no else - cargo build --bin medea $(if $(call eq,$(debug),no),--release,) + cargo build --bin medea $(if $(call eq,$(debug),no),--release,) $(args) endif endif ifeq ($(cargo-build-crate),medea-jason) @@ -287,7 +284,9 @@ ifeq ($(pre-install),yes) curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh endif @rm -rf $(crate-dir)/pkg/ - wasm-pack build -t web $(crate-dir) $(if $(call eq,$(debug),no),,--dev) + wasm-pack build -t web $(crate-dir) \ + $(if $(call eq,$(debug),no),,--dev) \ + $(args) endif ifeq ($(cargo-build-platform),android) $(foreach target,$(subst $(comma), ,$(cargo-build-targets)),\ @@ -295,15 +294,13 @@ ifeq ($(cargo-build-platform),android) endif endif endif -# TODO: Replace with actual `medea-jason` crate. define cargo.build.medea-jason.android $(eval target := $(strip $(1))) $(eval debug := $(strip $(2))) - cd jason/jason-dummy/ && \ cargo ndk -p $(ANDROID_SDK_COMPILE_VERSION) -t $(target) \ - -o ../flutter/android/src/main/jniLibs \ - --manifest-path=Cargo.toml \ - build $(if $(call eq,$(debug),no),--release,) + -o jason/flutter/android/src/main/jniLibs \ + --manifest-path=jason/Cargo.toml \ + build $(if $(call eq,$(debug),no),--release,) $(args) endef @@ -348,14 +345,13 @@ endif # make cargo.lint cargo.lint: - cargo clippy --all -- -D clippy::pedantic -D warnings - $(foreach target,$(subst $(comma), ,$(ANDROID_RUST_TARGETS)),\ + cargo clippy --workspace -- -D clippy::pedantic -D warnings + $(foreach target,$(subst $(comma), ,$(ANDROID_TARGETS)),\ $(call cargo.lint.medea-jason.android,$(target))) -# TODO: Replace with actual `medea-jason` crate. define cargo.lint.medea-jason.android $(eval target := $(strip $(1))) - cd jason/jason-dummy/ && \ - cargo clippy --target=$(target) -- -D clippy::pedantic -D warnings + cargo clippy --manifest-path jason/Cargo.toml --target=$(target) -- \ + -D clippy::pedantic -D warnings endef @@ -374,7 +370,7 @@ cargo.version: # make rustup.android rustup.android: - rustup target add $(ANDROID_RUST_TARGETS) + rustup target add $(ANDROID_TARGETS) diff --git a/jason/Cargo.toml b/jason/Cargo.toml index da6bf98dd..376f8acfe 100644 --- a/jason/Cargo.toml +++ b/jason/Cargo.toml @@ -12,7 +12,7 @@ repository = "https://github.com/instrumentisto/medea/tree/master/jason" readme = "README.md" keywords = ["medea", "jason", "webrtc", "client", "browser"] categories = ["multimedia", "api-bindings", "web-programming", "wasm"] -exclude = ["/demo/", "/e2e-demo/"] +exclude = ["/demo/", "/e2e-demo/", "/flutter/"] [lib] crate-type = ["cdylib", "rlib"] @@ -25,13 +25,12 @@ mockable = ["downcast", "fragile", "mockall", "predicates-tree"] async-recursion = "0.3" async-trait = "0.1" bitflags = "1.2" -console_error_panic_hook = { version = "0.1", optional = true } +cfg-if = "1.0" derivative = "2.1" derive_more = "0.99" downcast = { version = "0.10", optional = true } fragile = { version = "1.0", optional = true } futures = "0.3" -js-sys = "0.3" log = "0.4" medea-client-api-proto = { version = "0.3", features = ["jason"] } medea-macro = "0.2" @@ -42,11 +41,20 @@ serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" tracerr = "0.1" url = "2.1" + +[target.'cfg(target_os = "android")'.dependencies] +android_logger = "0.10" +dart-sys = "2.0" +libc = "0.2" + +[target.'cfg(not(target_os = "android"))'.dependencies] +console_error_panic_hook = { version = "0.1", optional = true } +js-sys = "0.3" wasm-bindgen = { version = "0.2", features = ["serde-serialize"] } wasm-bindgen-futures = "0.4" wasm-logger = "0.2" wee_alloc = { version = "0.4", optional = true } -[dependencies.web-sys] +[target.'cfg(not(target_os = "android"))'.dependencies.web-sys] version = "0.3.47" features = [ "console", @@ -78,5 +86,8 @@ wee_alloc = { version = "0.4", optional = true } "WebSocket", "Window", ] +[build-dependencies] +cc = "1.0" + [dev-dependencies] wasm-bindgen-test = "0.3" diff --git a/jason/build.rs b/jason/build.rs new file mode 100644 index 000000000..07c77c14c --- /dev/null +++ b/jason/build.rs @@ -0,0 +1,19 @@ +//! Compiles `trampoline.c` and links it into the final library. + +use std::env; + +fn main() { + println!("cargo:rerun-if-env-changed=CLIPPY_ARGS"); + if let Ok("cargo-clippy") = env::var("CARGO_CFG_FEATURE").as_deref() { + return; + } + + if let Ok("wasm32-unknown-unknown") = env::var("TARGET").as_deref() { + return; + } + + println!("cargo:rerun-if-changed=src/platform/dart/api_dl/trampoline.c"); + cc::Build::new() + .file("src/platform/dart/api_dl/trampoline.c") + .compile("trampoline"); +} diff --git a/jason/flutter/example/integration_test/jason.dart b/jason/flutter/example/integration_test/jason.dart index 49bc290ab..eb56fc8e2 100644 --- a/jason/flutter/example/integration_test/jason.dart +++ b/jason/flutter/example/integration_test/jason.dart @@ -7,7 +7,7 @@ import 'package:medea_jason/connection_handle.dart'; import 'package:medea_jason/device_video_track_constraints.dart'; import 'package:medea_jason/display_video_track_constraints.dart'; import 'package:medea_jason/jason.dart'; -import 'package:medea_jason/kind.dart'; +import 'package:medea_jason/track_kinds.dart'; import 'package:medea_jason/media_stream_settings.dart'; import 'package:medea_jason/remote_media_track.dart'; import 'package:medea_jason/room_close_reason.dart'; @@ -140,7 +140,7 @@ void main() { var reason = await reasonFut.future.timeout(Duration(seconds: 1)); - expect(reason.reason(), equals('RoomCloseReason.reason')); + expect(reason.reason(), equals('RpcClientUnexpectedlyDropped')); expect(reason.isClosedByServer(), equals(false)); expect(reason.isErr(), equals(true)); reason.free(); diff --git a/jason/flutter/lib/audio_track_constraints.dart b/jason/flutter/lib/audio_track_constraints.dart index 66f857d9e..20d3e1f41 100644 --- a/jason/flutter/lib/audio_track_constraints.dart +++ b/jason/flutter/lib/audio_track_constraints.dart @@ -1,4 +1,5 @@ import 'dart:ffi'; + import 'package:ffi/ffi.dart'; import 'jason.dart'; @@ -22,9 +23,14 @@ final _deviceId = dl.lookupFunction<_deviceId_C, _deviceId_Dart>( final _free = dl.lookupFunction<_free_C, _free_Dart>('AudioTrackConstraints__free'); +/// Constraints applicable to audio tracks. class AudioTrackConstraints { + /// [Pointer] to the Rust struct backing this object. final NullablePointer ptr = NullablePointer(_new()); + /// Sets an exact [`deviceId`][1] constraint. + /// + /// [1]: https://w3.org/TR/mediacapture-streams#def-constraint-deviceId void deviceId(String deviceId) { var deviceIdPtr = deviceId.toNativeUtf8(); try { @@ -34,6 +40,7 @@ class AudioTrackConstraints { } } + /// Drops the associated Rust struct and nulls the local [Pointer] to it. @moveSemantics void free() { _free(ptr.getInnerPtr()); diff --git a/jason/flutter/lib/connection_handle.dart b/jason/flutter/lib/connection_handle.dart index fe1fe8dfb..fde7a08ac 100644 --- a/jason/flutter/lib/connection_handle.dart +++ b/jason/flutter/lib/connection_handle.dart @@ -41,29 +41,40 @@ final _onQualityScoreUpdate = dl.lookupFunction<_onQualityScoreUpdate_C, _onQualityScoreUpdate_Dart>( 'ConnectionHandle__on_quality_score_update'); +/// External handler to a `Connection` with a remote `Member`. class ConnectionHandle { + /// [Pointer] to the Rust struct backing this object. late NullablePointer ptr; + /// Constructs a new [ConnectionHandle] backed by a Rust struct behind the + /// provided [Pointer]. ConnectionHandle(this.ptr); + /// Returns ID of the remote `Member`. String getRemoteMemberId() { return _getRemoteMemberId(ptr.getInnerPtr()).nativeStringToDartString(); } + /// Sets callback, invoked when this `Connection` is closed. void onClose(void Function() f) { _onClose(ptr.getInnerPtr(), f); } + /// Sets callback, invoked when a new [RemoteMediaTrack] is added to this + /// `Connection`. void onRemoteTrackAdded(void Function(RemoteMediaTrack) f) { _onRemoteTrackAdded(ptr.getInnerPtr(), (t) { f(RemoteMediaTrack(NullablePointer(t))); }); } + /// Sets callback, invoked when a connection quality score is updated by a + /// server. void onQualityScoreUpdate(void Function(int) f) { _onQualityScoreUpdate(ptr.getInnerPtr(), f); } + /// Drops the associated Rust struct and nulls the local [Pointer] to it. @moveSemantics void free() { _free(ptr.getInnerPtr()); diff --git a/jason/flutter/lib/device_video_track_constraints.dart b/jason/flutter/lib/device_video_track_constraints.dart index 91e0e5095..9407ded71 100644 --- a/jason/flutter/lib/device_video_track_constraints.dart +++ b/jason/flutter/lib/device_video_track_constraints.dart @@ -1,4 +1,5 @@ import 'dart:ffi'; + import 'package:ffi/ffi.dart'; import 'jason.dart'; @@ -73,16 +74,34 @@ final _widthInRange = dl.lookupFunction<_widthInRange_C, _widthInRange_Dart>( final _free = dl.lookupFunction<_free_C, _free_Dart>('DeviceVideoTrackConstraints__free'); +/// Describes the directions that the camera can face, as seen from a user's +/// perspective. +/// +/// Representation of the [`VideoFacingModeEnum`][1]. +/// +/// [1]: https://w3.org/TR/mediacapture-streams#dom-videofacingmodeenum enum FacingMode { + /// Facing towards the user (a self-view camera). User, + + /// Facing away from the user (viewing an environment). Environment, + + /// Facing to the left of the user. Left, + + /// Facing to the right of the user. Right, } +/// Constraints applicable to video tracks sourced from some media device. class DeviceVideoTrackConstraints { + /// [Pointer] to the Rust struct backing this object. final NullablePointer ptr = NullablePointer(_new()); + /// Sets an exact [`deviceId`][1] constraint. + /// + /// [1]: https://w3.org/TR/mediacapture-streams#def-constraint-deviceId void deviceId(String deviceId) { var deviceIdPtr = deviceId.toNativeUtf8(); try { @@ -92,38 +111,63 @@ class DeviceVideoTrackConstraints { } } + /// Sets an exact [`facingMode`][1] constraint. + /// + /// [1]: https://w3.org/TR/mediacapture-streams#dom-constraindomstring void exactFacingMode(FacingMode facingMode) { _exactFacingMode(ptr.getInnerPtr(), facingMode.index); } + /// Sets an ideal [`facingMode`][1] constraint. + /// + /// [1]: https://w3.org/TR/mediacapture-streams#dom-constraindomstring void idealFacingMode(FacingMode facingMode) { _idealFacingMode(ptr.getInnerPtr(), facingMode.index); } + /// Sets an exact [`height`][1] constraint. + /// + /// [1]: https://tinyurl.com/w3-streams#def-constraint-height void exactHeight(int height) { _exactHeight(ptr.getInnerPtr(), height); } + /// Sets an ideal [`height`][1] constraint. + /// + /// [1]: https://tinyurl.com/w3-streams#def-constraint-height void idealHeight(int height) { _idealHeight(ptr.getInnerPtr(), height); } + /// Sets a range of a [`height`][1] constraint. + /// + /// [1]: https://tinyurl.com/w3-streams#def-constraint-height void heightInRange(int min, int max) { _heightInRange(ptr.getInnerPtr(), min, max); } + /// Sets an exact [`width`][1] constraint. + /// + /// [1]: https://tinyurl.com/w3-streams#def-constraint-width void exactWidth(int width) { _exactWidth(ptr.getInnerPtr(), width); } + /// Sets an ideal [`width`][1] constraint. + /// + /// [1]: https://tinyurl.com/w3-streams#def-constraint-width void idealWidth(int width) { _idealWidth(ptr.getInnerPtr(), width); } + /// Sets a range of a [`width`][1] constraint. + /// + /// [1]: https://tinyurl.com/w3-streams#def-constraint-width void widthInRange(int min, int max) { _widthInRange(ptr.getInnerPtr(), min, max); } + /// Drops the associated Rust struct and nulls the local [Pointer] to it. @moveSemantics void free() { _free(ptr.getInnerPtr()); diff --git a/jason/flutter/lib/display_video_track_constraints.dart b/jason/flutter/lib/display_video_track_constraints.dart index 9bf20cea2..2d8322867 100644 --- a/jason/flutter/lib/display_video_track_constraints.dart +++ b/jason/flutter/lib/display_video_track_constraints.dart @@ -16,9 +16,12 @@ final _new = final _free_Dart _free = dl .lookupFunction<_free_C, _free_Dart>('DisplayVideoTrackConstraints__free'); +/// Constraints applicable to video tracks sourced from a screen capturing. class DisplayVideoTrackConstraints { + /// [Pointer] to the Rust struct backing this object. final NullablePointer ptr = NullablePointer(_new()); + /// Drops the associated Rust struct and nulls the local [Pointer] to it. @moveSemantics void free() { _free(ptr.getInnerPtr()); diff --git a/jason/flutter/lib/input_device_info.dart b/jason/flutter/lib/input_device_info.dart index 81c6b9a3b..bc440149a 100644 --- a/jason/flutter/lib/input_device_info.dart +++ b/jason/flutter/lib/input_device_info.dart @@ -3,7 +3,7 @@ import 'dart:ffi'; import 'package:ffi/ffi.dart'; import 'jason.dart'; -import 'kind.dart'; +import 'track_kinds.dart'; import 'util/move_semantic.dart'; import 'util/nullable_pointer.dart'; import 'util/native_string.dart'; @@ -36,28 +36,53 @@ final _deviceId = dl final _free = dl.lookupFunction<_free_C, _free_Dart>('InputDeviceInfo__free'); +/// [`MediaDeviceInfo`][1] interface. +/// +/// [1]: https://w3.org/TR/mediacapture-streams/#device-info class InputDeviceInfo { + /// [Pointer] to the Rust struct backing this object. late NullablePointer ptr; + /// Constructs a new [InputDeviceInfo] backed by a Rust struct behind the + /// provided [Pointer]. InputDeviceInfo(this.ptr); + /// Returns an unique identifier of the represented device. String deviceId() { return _deviceId(ptr.getInnerPtr()).nativeStringToDartString(); } + /// Returns label describing the represented device (for example "External USB + /// Webcam"). + /// + /// If the device has no associated label, then returns an empty string. String label() { return _label(ptr.getInnerPtr()).nativeStringToDartString(); } + /// Returns kind of the represented device. + /// + /// This representation of a [`MediaDeviceInfo`][1] is ONLY for input devices. + /// + /// [1]: https://w3.org/TR/mediacapture-streams/#device-info MediaKind kind() { var index = _kind(ptr.getInnerPtr()); return MediaKind.values[index]; } + /// Returns a group identifier of the represented device. + /// + /// Two devices have the same group identifier if they belong to the same + /// physical device. For example, the audio input and output devices + /// representing the speaker and microphone of the same headset have the + /// same [`groupId`][1]. + /// + /// [1]: https://w3.org/TR/mediacapture-streams/#dom-mediadeviceinfo-groupid String groupId() { return _nativeGroupId(ptr.getInnerPtr()).nativeStringToDartString(); } + /// Drops the associated Rust struct and nulls the local [Pointer] to it. @moveSemantics void free() { _free(ptr.getInnerPtr()); diff --git a/jason/flutter/lib/jason.dart b/jason/flutter/lib/jason.dart index 938e51287..07c5cd39e 100644 --- a/jason/flutter/lib/jason.dart +++ b/jason/flutter/lib/jason.dart @@ -50,7 +50,7 @@ DynamicLibrary _dl_load() { throw 'You are running unsupported NativeApi version.'; } - var dl = DynamicLibrary.open('libjason.so'); + var dl = DynamicLibrary.open('libmedea_jason.so'); var initResult = dl.lookupFunction< IntPtr Function(Pointer), @@ -65,22 +65,32 @@ DynamicLibrary _dl_load() { return dl; } +/// General library interface. +/// +/// Responsible for managing shared transports, local media and room +/// initialization. class Jason { + /// [Pointer] to the Rust struct backing this object. final NullablePointer ptr = NullablePointer(_new()); - MediaManager mediaManager() { - return MediaManager(NullablePointer(_media_manager(ptr.getInnerPtr()))); + /// Returns a [MediaManagerHandle] to the `MediaManager` of this [Jason]. + MediaManagerHandle mediaManager() { + return MediaManagerHandle( + NullablePointer(_media_manager(ptr.getInnerPtr()))); } + /// Creates a new `Room` and returns its [RoomHandle]. RoomHandle initRoom() { return RoomHandle(NullablePointer(_initRoom(ptr.getInnerPtr()))); } + /// Closes the `Room` by the provided [RoomHandle]. void closeRoom(@moveSemantics RoomHandle room) { _close_room(ptr.getInnerPtr(), room.ptr.getInnerPtr()); room.ptr.free(); } + /// Drops the associated Rust struct and nulls the local [Pointer] to it. @moveSemantics void free() { _free(ptr.getInnerPtr()); diff --git a/jason/flutter/lib/kind.dart b/jason/flutter/lib/kind.dart deleted file mode 100644 index 4467dd03f..000000000 --- a/jason/flutter/lib/kind.dart +++ /dev/null @@ -1,9 +0,0 @@ -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 index e39f3c0ad..6c2a2ae7a 100644 --- a/jason/flutter/lib/local_media_track.dart +++ b/jason/flutter/lib/local_media_track.dart @@ -1,7 +1,7 @@ import 'dart:ffi'; import 'jason.dart'; -import 'kind.dart'; +import 'track_kinds.dart'; import 'util/move_semantic.dart'; import 'util/nullable_pointer.dart'; @@ -22,21 +22,41 @@ final _sourceKind = final _free = dl.lookupFunction<_free_C, _free_Dart>('LocalMediaTrack__free'); +/// Strongly referenced media track received from a +/// [`getUserMedia()`][1]/[`getDisplayMedia()`][2] request. +/// +/// [1]: https://w3.org/TR/mediacapture-streams#dom-mediadevices-getusermedia +/// [2]: https://w3.org/TR/screen-capture/#dom-mediadevices-getdisplaymedia class LocalMediaTrack { + /// [Pointer] to the Rust struct backing this object. late NullablePointer ptr; + /// Constructs a new [LocalMediaTrack] backed by the Rust struct behind the + /// provided [Pointer]. LocalMediaTrack(this.ptr); + /// Returns the [MediaKind.Audio] if this [LocalMediaTrack] represents an + /// audio track, or the [MediaKind.Video] if it represents a video track. MediaKind kind() { var index = _kind(ptr.getInnerPtr()); return MediaKind.values[index]; } + /// Returns the [MediaSourceKind.Device] if this [LocalMediaTrack] is sourced + /// from some device (webcam/microphone), or the [MediaSourceKind.Display] + /// if it's captured via [`MediaDevices.getDisplayMedia()`][1]. + /// + /// [1]: https://w3.org/TR/screen-capture/#dom-mediadevices-getdisplaymedia MediaSourceKind mediaSourceKind() { var index = _sourceKind(ptr.getInnerPtr()); return MediaSourceKind.values[index]; } + /// Drops the associated Rust struct and nulls the local [Pointer] to it. + /// + /// Note, that this is a strong reference, so freeing it will stop underlying + /// track if there are no other strong references (i.e., not used in local + /// peer's senders). @moveSemantics void free() { _free(ptr.getInnerPtr()); diff --git a/jason/flutter/lib/media_manager.dart b/jason/flutter/lib/media_manager.dart index 691d2c313..2ef2fbbf3 100644 --- a/jason/flutter/lib/media_manager.dart +++ b/jason/flutter/lib/media_manager.dart @@ -28,11 +28,24 @@ final _enumerateDevices = final _free = dl.lookupFunction<_free_C, _free_Dart>('MediaManagerHandle__free'); -class MediaManager { +/// External handle to a `MediaManager`. +/// +/// `MediaManager` performs all media acquisition requests +/// ([`getUserMedia()`][1]/[`getDisplayMedia()`][2]) and stores all received +/// tracks for further re-usage. +/// +/// [1]: https://w3.org/TR/mediacapture-streams#dom-mediadevices-getusermedia +/// [2]: https://w3.org/TR/screen-capture/#dom-mediadevices-getdisplaymedia +class MediaManagerHandle { + /// [Pointer] to the Rust struct backing this object. late NullablePointer ptr; - MediaManager(this.ptr); + /// Creates a new [MediaManagerHandle] backed by the Rust struct behind the + /// provided [Pointer]. + MediaManagerHandle(this.ptr); + /// Obtains [LocalMediaTrack]s objects from local media devices (or screen + /// capture) basing on the provided [MediaStreamSettings]. List initLocalTracks(MediaStreamSettings caps) { return _initLocalTracks(ptr.getInnerPtr(), caps.ptr.getInnerPtr()) .intoPointerList() @@ -40,6 +53,8 @@ class MediaManager { .toList(); } + /// Returns a list of [InputDeviceInfo] objects representing available media + /// input devices, such as microphones, cameras, and so forth. List enumerateDevices() { return _enumerateDevices(ptr.getInnerPtr()) .intoPointerList() @@ -47,6 +62,7 @@ class MediaManager { .toList(); } + /// Drops the associated Rust struct and nulls the local [Pointer] to it. @moveSemantics void free() { _free(ptr.getInnerPtr()); diff --git a/jason/flutter/lib/media_stream_settings.dart b/jason/flutter/lib/media_stream_settings.dart index 8fc29f5c4..868e7d5c7 100644 --- a/jason/flutter/lib/media_stream_settings.dart +++ b/jason/flutter/lib/media_stream_settings.dart @@ -36,24 +36,32 @@ final _displayVideo = dl.lookupFunction<_displayVideo_C, _displayVideo_Dart>( final _free = dl.lookupFunction<_free_C, _free_Dart>('MediaStreamSettings__free'); +/// Representation of [`MediaStreamConstraints`][1]. +/// +/// [1]: https://w3.org/TR/mediacapture-streams#dom-mediastreamconstraints class MediaStreamSettings { + /// [Pointer] to the Rust struct backing this object. final NullablePointer ptr = NullablePointer(_new()); + /// Specifies a nature and settings of the audio `LocalMediaTrack`. void audio(@moveSemantics AudioTrackConstraints constraints) { _audio(ptr.getInnerPtr(), constraints.ptr.getInnerPtr()); constraints.ptr.free(); } + /// Sets constraints for obtaining a local video, sourced from a media device. void deviceVideo(@moveSemantics DeviceVideoTrackConstraints constraints) { _deviceVideo(ptr.getInnerPtr(), constraints.ptr.getInnerPtr()); constraints.ptr.free(); } + /// Set constraints for capturing a local video from user's display. void displayVideo(@moveSemantics DisplayVideoTrackConstraints constraints) { _displayVideo(ptr.getInnerPtr(), constraints.ptr.getInnerPtr()); constraints.ptr.free(); } + /// Drops the associated Rust struct and nulls the local [Pointer] to it. @moveSemantics void free() { _free(ptr.getInnerPtr()); diff --git a/jason/flutter/lib/reconnect_handle.dart b/jason/flutter/lib/reconnect_handle.dart index f8b8d4c2d..380bb787c 100644 --- a/jason/flutter/lib/reconnect_handle.dart +++ b/jason/flutter/lib/reconnect_handle.dart @@ -9,11 +9,18 @@ typedef _free_Dart = void Function(Pointer); final _free = dl.lookupFunction<_free_C, _free_Dart>('ReconnectHandle__free'); +/// External handle used to reconnect to a media server when connection is lost. +/// +/// This handle is passed to the `RoomHandle.onConnectionLoss()` callback. class ReconnectHandle { + /// [Pointer] to the Rust struct backing this object. late NullablePointer ptr; + /// Constructs a new [ReconnectHandle] backed by the Rust struct behind the + /// provided [Pointer]. ReconnectHandle(this.ptr); + /// Drops the associated Rust struct and nulls the local [Pointer] to it. @moveSemantics void free() { _free(ptr.getInnerPtr()); diff --git a/jason/flutter/lib/remote_media_track.dart b/jason/flutter/lib/remote_media_track.dart index 222bc1324..718cc8bb0 100644 --- a/jason/flutter/lib/remote_media_track.dart +++ b/jason/flutter/lib/remote_media_track.dart @@ -1,7 +1,7 @@ import 'dart:ffi'; import 'jason.dart'; -import 'kind.dart'; +import 'track_kinds.dart'; import 'util/move_semantic.dart'; import 'util/nullable_pointer.dart'; @@ -64,49 +64,65 @@ final _onStopped = dl.lookupFunction<_onStopped_C, _onStopped_Dart>( final _free = dl.lookupFunction<_free_C, _free_Dart>('RemoteMediaTrack__free'); +/// Representation of a received remote [`MediaStreamTrack`][1]. +/// +/// [1]: https://w3.org/TR/mediacapture-streams#dom-mediastreamtrack class RemoteMediaTrack { + /// [Pointer] to the Rust struct that backing this object. late NullablePointer ptr; + /// Constructs a new [RemoteMediaTrack] backed by the Rust struct behind the + /// provided [Pointer]. RemoteMediaTrack(this.ptr); + /// Indicates whether this [RemoteMediaTrack] is enabled. bool enabled() { return _enabled(ptr.getInnerPtr()) > 0; } + /// Indicate whether this [RemoteMediaTrack] is muted. bool muted() { return _muted(ptr.getInnerPtr()) > 0; } + /// Returns this [RemoteMediaTrack]'s kind (audio/video). MediaKind kind() { var index = _kind(ptr.getInnerPtr()); return MediaKind.values[index]; } + /// Returns this [RemoteMediaTrack]'s media source kind (device/display). MediaSourceKind mediaSourceKind() { var index = _mediaSourceKind(ptr.getInnerPtr()); return MediaSourceKind.values[index]; } + /// Sets callback, invoked when this [RemoteMediaTrack] is enabled. void onEnabled(void Function() f) { _onEnabled(ptr.getInnerPtr(), f); } + /// Sets callback, invoked when this [RemoteMediaTrack] is disabled. void onDisabled(void Function() f) { _onDisabled(ptr.getInnerPtr(), f); } + /// Sets callback to invoke when this [RemoteMediaTrack] is muted. void onMuted(void Function() f) { _onMuted(ptr.getInnerPtr(), f); } + /// Sets callback to invoke when this [RemoteMediaTrack] is unmuted. void onUnmuted(void Function() f) { _onUnmuted(ptr.getInnerPtr(), f); } + /// Sets callback to invoke when this [RemoteMediaTrack] is stopped. void onStopped(void Function() f) { _onStopped(ptr.getInnerPtr(), f); } + /// Drops the associated Rust struct and nulls the local [Pointer] to it. @moveSemantics void free() { _free(ptr.getInnerPtr()); diff --git a/jason/flutter/lib/room_close_reason.dart b/jason/flutter/lib/room_close_reason.dart index 3f5d9f9ff..7bc85343e 100644 --- a/jason/flutter/lib/room_close_reason.dart +++ b/jason/flutter/lib/room_close_reason.dart @@ -30,23 +30,33 @@ final _isErr = final _free = dl.lookupFunction<_free_C, _free_Dart>('RoomCloseReason__free'); +/// Reason of why a `Room` has been closed. +/// +/// This struct is passed into the `RoomHandle.onClose()` callback. class RoomCloseReason { + /// [Pointer] to the Rust struct that backing this object. late NullablePointer ptr; + /// Constructs a new [RoomCloseReason] backed by the Rust struct behind the + /// provided [Pointer]. RoomCloseReason(this.ptr); + /// Returns a close reason of the `Room`. String reason() { return _reason(ptr.getInnerPtr()).nativeStringToDartString(); } + /// Indicates whether the `Room` was closed by server. bool isClosedByServer() { return _isClosedByServer(ptr.getInnerPtr()) > 0; } + /// Indicates whether the `Room`'s close reason is considered as an error. bool isErr() { return _isErr(ptr.getInnerPtr()) > 0; } + /// Drops the associated Rust struct and nulls the local [Pointer] to it. @moveSemantics void free() { _free(ptr.getInnerPtr()); diff --git a/jason/flutter/lib/room_handle.dart b/jason/flutter/lib/room_handle.dart index b2f012bda..823f98753 100644 --- a/jason/flutter/lib/room_handle.dart +++ b/jason/flutter/lib/room_handle.dart @@ -1,13 +1,12 @@ import 'dart:ffi'; -import 'package:medea_jason/local_media_track.dart'; -import 'package:medea_jason/util/nullable_pointer.dart'; - import 'connection_handle.dart'; import 'jason.dart'; +import 'local_media_track.dart'; import 'reconnect_handle.dart'; import 'room_close_reason.dart'; import 'util/move_semantic.dart'; +import 'util/nullable_pointer.dart'; typedef _free_C = Void Function(Pointer); typedef _free_Dart = void Function(Pointer); @@ -40,35 +39,54 @@ final _onConnectionLoss = dl.lookupFunction<_onConnectionLoss_C, _onConnectionLoss_Dart>( 'RoomHandle__on_connection_loss'); +/// External handle to a `Room`. class RoomHandle { + /// [Pointer] to the Rust struct that backing this object. late NullablePointer ptr; + /// Constructs a new [RoomHandle] backed by the Rust struct behind the + /// provided [Pointer]. RoomHandle(this.ptr); + /// Sets callback, invoked when a new `Connection` with some remote `Peer` + /// is established. void onNewConnection(void Function(ConnectionHandle) f) { _onNewConnection(ptr.getInnerPtr(), (t) { f(ConnectionHandle(NullablePointer(t))); }); } + /// Sets callback, invoked when this `Room` is closed, providing a + /// [RoomCloseReason]. void onClose(void Function(RoomCloseReason) f) { _onClose(ptr.getInnerPtr(), (t) { f(RoomCloseReason(NullablePointer(t))); }); } + /// Sets callback, invoked when a new [LocalMediaTrack] is added to this + /// `Room`. + /// + /// This might happen in the following cases: + /// 1. Media server initiates a media request. + /// 2. [RoomHandle.enableAudio()]/[RoomHandle.enableVideo()] is called. + /// 3. [MediaStreamSettings] were updated via + /// [RoomHandle.setLocalMediaSettings()] method. void onLocalTrack(void Function(LocalMediaTrack) f) { _onLocalTrack(ptr.getInnerPtr(), (t) { f(LocalMediaTrack(NullablePointer(t))); }); } + /// Sets callback, invoked when a connection with a media server is lost, + /// providing a [ReconnectHandle]. void onConnectionLoss(void Function(ReconnectHandle) f) { _onConnectionLoss(ptr.getInnerPtr(), (t) { f(ReconnectHandle(NullablePointer(t))); }); } + /// Drops the associated Rust struct and nulls the local [Pointer] to it. @moveSemantics void free() { _free(ptr.getInnerPtr()); diff --git a/jason/flutter/lib/track_kinds.dart b/jason/flutter/lib/track_kinds.dart new file mode 100644 index 000000000..72875620d --- /dev/null +++ b/jason/flutter/lib/track_kinds.dart @@ -0,0 +1,19 @@ +/// Representation of a [`MediaStreamTrack.kind`][1]. +/// +/// [1]: https://w3.org/TR/mediacapture-streams#dom-mediastreamtrack-kind +enum MediaKind { + /// Audio track. + Audio, + + /// Video track. + Video, +} + +/// Media source type. +enum MediaSourceKind { + /// Media is sourced from some media device (webcam or microphone). + Device, + + /// Media is obtained via screen capturing. + Display, +} diff --git a/jason/flutter/lib/util/move_semantic.dart b/jason/flutter/lib/util/move_semantic.dart index 910e99b15..6180c2fd4 100644 --- a/jason/flutter/lib/util/move_semantic.dart +++ b/jason/flutter/lib/util/move_semantic.dart @@ -1,5 +1,15 @@ -class MoveSemantics { - const MoveSemantics(); +class _MoveSemantics { + const _MoveSemantics(); } -const MoveSemantics moveSemantics = MoveSemantics(); +/// Marker annotation signalling that current operation on an item (it's applied +/// to) has move semantics. +/// +/// Move semantics means that the item will be moved and can not be used after. +/// +/// When applied to an object method, means that the object should not be used +/// after that method is called. +/// +/// When applied to method arguments, means that the argument is moved into that +/// method. +const _MoveSemantics moveSemantics = _MoveSemantics(); diff --git a/jason/flutter/lib/util/native_string.dart b/jason/flutter/lib/util/native_string.dart index ba9b219b8..fcba90455 100644 --- a/jason/flutter/lib/util/native_string.dart +++ b/jason/flutter/lib/util/native_string.dart @@ -3,13 +3,17 @@ import 'dart:ffi'; import 'package:ffi/ffi.dart'; import 'package:medea_jason/jason.dart'; +import 'move_semantic.dart'; + typedef _free_C = Void Function(Pointer); typedef _free_Dart = void Function(Pointer); +/// Frees [String] returned from Rust. 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. + /// Converts this [RustStringPointer] to a Dart's [String]. + @moveSemantics String nativeStringToDartString() { try { return toDartString(); diff --git a/jason/flutter/lib/util/nullable_pointer.dart b/jason/flutter/lib/util/nullable_pointer.dart index e641fdd53..67591c433 100644 --- a/jason/flutter/lib/util/nullable_pointer.dart +++ b/jason/flutter/lib/util/nullable_pointer.dart @@ -1,8 +1,15 @@ import 'dart:ffi'; +/// Wrapper around a [Pointer] that can be null when its pointed memory is +/// freed. +/// +/// Accessing the pointer after it's freed will throw [StateError]. class NullablePointer { Pointer? _ptr; + /// Constructs [NullablePointer] from the provided [Pointer]. + /// + /// Provided [Pointer] should not have a zero address. NullablePointer(Pointer ptr) { if (ptr.address == 0) { throw ArgumentError.notNull('ptr'); @@ -10,6 +17,9 @@ class NullablePointer { _ptr = ptr; } + /// Returns the underlying [Pointer]. + /// + /// Throws [StateError] if the underlying [Pointer] has been freed. Pointer getInnerPtr() { if (_ptr == null) { throw StateError('NullablePointer cannot be used after free.'); @@ -18,6 +28,10 @@ class NullablePointer { } } + /// Nulls the underlying [Pointer]. + /// + /// This doesn't affect the pointed memory, but Dart won't be able to access + /// it, so it's expected that native memory has been freed at this point. void free() { _ptr = null; } diff --git a/jason/flutter/lib/util/ptrarray.dart b/jason/flutter/lib/util/ptrarray.dart index 9ba21156c..5ef36567d 100644 --- a/jason/flutter/lib/util/ptrarray.dart +++ b/jason/flutter/lib/util/ptrarray.dart @@ -1,19 +1,26 @@ import 'dart:ffi'; import '../jason.dart'; +import 'move_semantic.dart'; typedef _free_C = Void Function(PtrArray); typedef _free_Dart = void Function(PtrArray); +/// Frees [PtrArray] returned from Rust. final _free_Dart _free = dl.lookupFunction<_free_C, _free_Dart>('PtrArray_free'); +/// Array of [Pointer]s to Rust objects. class PtrArray extends Struct { + /// [Pointer] to the first array element. external Pointer _ptr; + + /// Length of this [PtrArray]. @Uint64() external int _len; - /// Frees an underlying native memory, so it can only be called once. + /// Converts this [PtrArray] to a Dart's [List] of [Pointer]s. + @moveSemantics List intoPointerList() { try { var out = List.empty(growable: true); diff --git a/jason/jason-dummy/.editorconfig b/jason/jason-dummy/.editorconfig deleted file mode 100644 index 7f9845bb1..000000000 --- a/jason/jason-dummy/.editorconfig +++ /dev/null @@ -1,3 +0,0 @@ -[*.{c,h}] -indent_style = space -indent_size = 2 diff --git a/jason/jason-dummy/.gitignore b/jason/jason-dummy/.gitignore deleted file mode 100644 index 042776aad..000000000 --- a/jason/jason-dummy/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/Cargo.lock -/target/ diff --git a/jason/jason-dummy/Cargo.toml b/jason/jason-dummy/Cargo.toml deleted file mode 100644 index 625e175a4..000000000 --- a/jason/jason-dummy/Cargo.toml +++ /dev/null @@ -1,18 +0,0 @@ -[package] -name = "jason-dummy" -version = "0.0.1" -edition = "2018" -description = "Temp crate to help us bootstrap flutter + rust builds" - -[lib] -name = "jason" -crate-type = ["staticlib", "cdylib"] - -[dependencies] -dart-sys = "2.0" -libc = "0.2" - -[build-dependencies] -cc = "1.0" - -[workspace] diff --git a/jason/jason-dummy/build.rs b/jason/jason-dummy/build.rs deleted file mode 100644 index 6b5f4d771..000000000 --- a/jason/jason-dummy/build.rs +++ /dev/null @@ -1,13 +0,0 @@ -//! Compiles `trampoline.c` and links it into the final library. - -fn main() { - println!("cargo:rerun-if-env-changed=CLIPPY_ARGS"); - if let Ok("cargo-clippy") = std::env::var("CARGO_CFG_FEATURE").as_deref() { - return; - } - - println!("cargo:rerun-if-changed=src/dart_api_dl/trampoline.c"); - cc::Build::new() - .file("src/dart_api_dl/trampoline.c") - .compile("trampoline"); -} diff --git a/jason/jason-dummy/src/audio_track_constraints.rs b/jason/jason-dummy/src/audio_track_constraints.rs deleted file mode 100644 index 6f78584da..000000000 --- a/jason/jason-dummy/src/audio_track_constraints.rs +++ /dev/null @@ -1,35 +0,0 @@ -use crate::{utils::c_str_into_string, ForeignClass}; - -pub struct AudioTrackConstraints; - -impl ForeignClass for 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 { - AudioTrackConstraints::new().into_ptr() -} - -#[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, -) { - AudioTrackConstraints::from_ptr(this); -} diff --git a/jason/jason-dummy/src/connection_handle.rs b/jason/jason-dummy/src/connection_handle.rs deleted file mode 100644 index 020b05e02..000000000 --- a/jason/jason-dummy/src/connection_handle.rs +++ /dev/null @@ -1,74 +0,0 @@ -use dart_sys::Dart_Handle; - -use crate::{ - remote_media_track::RemoteMediaTrack, - utils::{string_into_c_str, DartClosure}, - ForeignClass, -}; - -pub struct ConnectionHandle; - -impl ForeignClass for 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: DartClosure<()>) { - // Result<(), JasonError> - f.call0(); - } - - pub fn on_remote_track_added(&self, f: DartClosure) { - // Result<(), JasonError> - f.call1(RemoteMediaTrack); - } - - pub fn on_quality_score_update(&self, f: DartClosure) { - // Result<(), JasonError> - f.call1(4); - } -} - -#[no_mangle] -pub unsafe extern "C" fn ConnectionHandle__on_close( - this: *const ConnectionHandle, - f: Dart_Handle, -) { - let this = this.as_ref().unwrap(); - this.on_close(DartClosure::new(f)); -} - -#[no_mangle] -pub unsafe extern "C" fn ConnectionHandle__on_remote_track_added( - this: *const ConnectionHandle, - f: Dart_Handle, -) { - let this = this.as_ref().unwrap(); - this.on_remote_track_added(DartClosure::new(f)); -} - -#[no_mangle] -pub unsafe extern "C" fn ConnectionHandle__on_quality_score_update( - this: *const ConnectionHandle, - f: Dart_Handle, -) { - let this = this.as_ref().unwrap(); - this.on_quality_score_update(DartClosure::new(f)); -} - -#[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) { - ConnectionHandle::from_ptr(this); -} diff --git a/jason/jason-dummy/src/display_video_track_constraints.rs b/jason/jason-dummy/src/display_video_track_constraints.rs deleted file mode 100644 index e2c5c21ca..000000000 --- a/jason/jason-dummy/src/display_video_track_constraints.rs +++ /dev/null @@ -1,24 +0,0 @@ -use crate::ForeignClass; - -pub struct DisplayVideoTrackConstraints; - -impl ForeignClass for DisplayVideoTrackConstraints {} - -impl DisplayVideoTrackConstraints { - fn new() -> Self { - Self - } -} - -#[no_mangle] -pub extern "C" fn DisplayVideoTrackConstraints__new( -) -> *const DisplayVideoTrackConstraints { - DisplayVideoTrackConstraints::new().into_ptr() -} - -#[no_mangle] -pub unsafe extern "C" fn DisplayVideoTrackConstraints__free( - this: *mut DisplayVideoTrackConstraints, -) { - DisplayVideoTrackConstraints::from_ptr(this); -} diff --git a/jason/jason-dummy/src/input_device_info.rs b/jason/jason-dummy/src/input_device_info.rs deleted file mode 100644 index f552f1032..000000000 --- a/jason/jason-dummy/src/input_device_info.rs +++ /dev/null @@ -1,64 +0,0 @@ -use crate::{utils::string_into_c_str, ForeignClass, MediaKind}; - -pub struct InputDeviceInfo {} - -impl ForeignClass for 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) { - InputDeviceInfo::from_ptr(this); -} diff --git a/jason/jason-dummy/src/jason.rs b/jason/jason-dummy/src/jason.rs deleted file mode 100644 index 7225cbe53..000000000 --- a/jason/jason-dummy/src/jason.rs +++ /dev/null @@ -1,61 +0,0 @@ -use crate::{ - media_manager::MediaManagerHandle, room_handle::RoomHandle, ForeignClass, -}; - -pub struct Jason; - -impl ForeignClass for 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 { - Jason::new().into_ptr() -} - -#[no_mangle] -pub unsafe extern "C" fn Jason__init_room( - this: *const Jason, -) -> *const RoomHandle { - let this = this.as_ref().unwrap(); - - this.init_room().into_ptr() -} - -#[no_mangle] -pub unsafe extern "C" fn Jason__media_manager( - this: *const Jason, -) -> *const MediaManagerHandle { - let this = this.as_ref().unwrap(); - - this.media_manager().into_ptr() -} - -#[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(RoomHandle::from_ptr(room_to_delete)); -} - -#[no_mangle] -pub unsafe extern "C" fn Jason__free(this: *mut Jason) { - Jason::from_ptr(this); -} diff --git a/jason/jason-dummy/src/lib.rs b/jason/jason-dummy/src/lib.rs deleted file mode 100644 index 69432a3ae..000000000 --- a/jason/jason-dummy/src/lib.rs +++ /dev/null @@ -1,55 +0,0 @@ -#![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; -pub mod utils; - -/// Rust structure that has wrapper class in Dart. Such structures are passed -/// through FFI boundaries as thin pointers. -pub trait ForeignClass { - /// Consumes `Self` returning a wrapped raw pointer via [`Box::into_raw`]. - fn into_ptr(self) -> *const Self - where - Self: Sized, - { - Box::into_raw(Box::new(self)) - } - - /// Constructs `Self` from a raw pointer via [`Box::from_raw`]. - unsafe fn from_ptr(this: *mut Self) -> Self - where - Self: Sized, - { - *Box::from_raw(this) - } -} - -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 deleted file mode 100644 index 7417e4796..000000000 --- a/jason/jason-dummy/src/local_media_track.rs +++ /dev/null @@ -1,40 +0,0 @@ -use crate::{ForeignClass, MediaKind, MediaSourceKind}; - -pub struct LocalMediaTrack; - -impl ForeignClass for 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) { - LocalMediaTrack::from_ptr(this); -} diff --git a/jason/jason-dummy/src/media_manager.rs b/jason/jason-dummy/src/media_manager.rs deleted file mode 100644 index 91a66b794..000000000 --- a/jason/jason-dummy/src/media_manager.rs +++ /dev/null @@ -1,50 +0,0 @@ -use crate::{ - input_device_info::InputDeviceInfo, local_media_track::LocalMediaTrack, - media_stream_settings::MediaStreamSettings, utils::PtrArray, ForeignClass, -}; - -pub struct MediaManagerHandle; - -impl ForeignClass for 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, -) { - MediaManagerHandle::from_ptr(this); -} diff --git a/jason/jason-dummy/src/reconnect_handle.rs b/jason/jason-dummy/src/reconnect_handle.rs deleted file mode 100644 index 9ab59531e..000000000 --- a/jason/jason-dummy/src/reconnect_handle.rs +++ /dev/null @@ -1,17 +0,0 @@ -use crate::ForeignClass; - -pub struct ReconnectHandle; - -impl ForeignClass for 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) { - ReconnectHandle::from_ptr(this); -} diff --git a/jason/jason-dummy/src/remote_media_track.rs b/jason/jason-dummy/src/remote_media_track.rs deleted file mode 100644 index f571c6bae..000000000 --- a/jason/jason-dummy/src/remote_media_track.rs +++ /dev/null @@ -1,133 +0,0 @@ -use dart_sys::Dart_Handle; - -use crate::{utils::DartClosure, ForeignClass, MediaKind, MediaSourceKind}; - -pub struct RemoteMediaTrack; - -impl ForeignClass for 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 get_track(&self) -> sys::MediaStreamTrack - - pub fn on_enabled(&self, cb: DartClosure<()>) { - cb.call0(); - } - - pub fn on_disabled(&self, cb: DartClosure<()>) { - cb.call0(); - } - - pub fn on_muted(&self, cb: DartClosure<()>) { - cb.call0(); - } - - pub fn on_unmuted(&self, cb: DartClosure<()>) { - cb.call0(); - } - - pub fn on_stopped(&self, cb: DartClosure<()>) { - cb.call0(); - } -} - -#[no_mangle] -pub unsafe extern "C" fn RemoteMediaTrack__on_enabled( - this: *const RemoteMediaTrack, - f: Dart_Handle, -) { - let this = this.as_ref().unwrap(); - this.on_enabled(DartClosure::new(f)); -} - -#[no_mangle] -pub unsafe extern "C" fn RemoteMediaTrack__on_disabled( - this: *const RemoteMediaTrack, - f: Dart_Handle, -) { - let this = this.as_ref().unwrap(); - this.on_disabled(DartClosure::new(f)); -} - -#[no_mangle] -pub unsafe extern "C" fn RemoteMediaTrack__on_muted( - this: *const RemoteMediaTrack, - f: Dart_Handle, -) { - let this = this.as_ref().unwrap(); - this.on_muted(DartClosure::new(f)); -} - -#[no_mangle] -pub unsafe extern "C" fn RemoteMediaTrack__on_unmuted( - this: *const RemoteMediaTrack, - f: Dart_Handle, -) { - let this = this.as_ref().unwrap(); - this.on_unmuted(DartClosure::new(f)); -} - -#[no_mangle] -pub unsafe extern "C" fn RemoteMediaTrack__on_stopped( - this: *const RemoteMediaTrack, - f: Dart_Handle, -) { - let this = this.as_ref().unwrap(); - this.on_stopped(DartClosure::new(f)); -} - -#[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) { - RemoteMediaTrack::from_ptr(this); -} diff --git a/jason/jason-dummy/src/room_handle.rs b/jason/jason-dummy/src/room_handle.rs deleted file mode 100644 index 5f05b1021..000000000 --- a/jason/jason-dummy/src/room_handle.rs +++ /dev/null @@ -1,98 +0,0 @@ -use dart_sys::Dart_Handle; - -use crate::{ - connection_handle::ConnectionHandle, local_media_track::LocalMediaTrack, - reconnect_handle::ReconnectHandle, room_close_reason::RoomCloseReason, - utils::DartClosure, ForeignClass, -}; - -pub struct RoomHandle; - -impl ForeignClass for RoomHandle {} - -impl RoomHandle { - pub fn on_new_connection(&self, cb: DartClosure) { - // Result<(), JasonError> - cb.call1(ConnectionHandle); - } - - pub fn on_close(&self, cb: DartClosure) { - // Result<(), JasonError> - cb.call1(RoomCloseReason); - } - - pub fn on_local_track(&self, cb: DartClosure) { - // Result<(), JasonError> - cb.call1(LocalMediaTrack); - } - - pub fn on_connection_loss(&self, cb: DartClosure) { - // Result<(), JasonError> - cb.call1(ReconnectHandle); - } - - // pub async fn join(&self, token: String) -> Result<(), JasonError> - // pub fn on_failed_local_media( - // &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__on_new_connection( - this: *mut RoomHandle, - cb: Dart_Handle, -) { - let this = this.as_ref().unwrap(); - this.on_new_connection(DartClosure::new(cb)); -} - -#[no_mangle] -pub unsafe extern "C" fn RoomHandle__on_close( - this: *mut RoomHandle, - cb: Dart_Handle, -) { - let this = this.as_ref().unwrap(); - this.on_close(DartClosure::new(cb)); -} - -#[no_mangle] -pub unsafe extern "C" fn RoomHandle__on_local_track( - this: *mut RoomHandle, - cb: Dart_Handle, -) { - let this = this.as_ref().unwrap(); - this.on_local_track(DartClosure::new(cb)); -} - -#[no_mangle] -pub unsafe extern "C" fn RoomHandle__on_connection_loss( - this: *mut RoomHandle, - cb: Dart_Handle, -) { - let this = this.as_ref().unwrap(); - this.on_connection_loss(DartClosure::new(cb)); -} - -#[no_mangle] -pub unsafe extern "C" fn RoomHandle__free(this: *mut RoomHandle) { - RoomHandle::from_ptr(this); -} diff --git a/jason/jason-dummy/src/utils/string.rs b/jason/jason-dummy/src/utils/string.rs deleted file mode 100644 index 7835cef03..000000000 --- a/jason/jason-dummy/src/utils/string.rs +++ /dev/null @@ -1,14 +0,0 @@ -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); -} diff --git a/jason/src/api/dart/audio_track_constraints.rs b/jason/src/api/dart/audio_track_constraints.rs new file mode 100644 index 000000000..df75c1f7c --- /dev/null +++ b/jason/src/api/dart/audio_track_constraints.rs @@ -0,0 +1,41 @@ +//! Constraints applicable to audio tracks. + +use std::os::raw::c_char; + +use super::{utils::c_str_into_string, ForeignClass}; + +pub use crate::media::AudioTrackConstraints; + +impl ForeignClass for AudioTrackConstraints {} + +/// Creates new [`AudioTrackConstraints`] with none constraints configured. +#[no_mangle] +pub extern "C" fn AudioTrackConstraints__new() -> *const AudioTrackConstraints { + AudioTrackConstraints::new().into_ptr() +} + +/// Sets an exact [deviceId][1] constraint. +/// +/// [1]: https://w3.org/TR/mediacapture-streams#def-constraint-deviceId +#[no_mangle] +pub unsafe extern "C" fn AudioTrackConstraints__device_id( + this: *mut AudioTrackConstraints, + device_id: *const c_char, +) { + let this = this.as_mut().unwrap(); + + this.device_id(c_str_into_string(device_id)) +} + +/// Frees the data behind the provided pointer. +/// +/// # Safety +/// +/// Should be called when object is no longer needed. Calling this more than +/// once for the same pointer is equivalent to double free. +#[no_mangle] +pub unsafe extern "C" fn AudioTrackConstraints__free( + this: *mut AudioTrackConstraints, +) { + let _ = AudioTrackConstraints::from_ptr(this); +} diff --git a/jason/src/api/dart/connection_handle.rs b/jason/src/api/dart/connection_handle.rs new file mode 100644 index 000000000..10d2f7dec --- /dev/null +++ b/jason/src/api/dart/connection_handle.rs @@ -0,0 +1,124 @@ +use std::os::raw::c_char; + +use dart_sys::Dart_Handle; + +use crate::platform; + +use super::{utils::string_into_c_str, ForeignClass}; + +#[cfg(feature = "mockable")] +pub use self::mock::ConnectionHandle; +#[cfg(not(feature = "mockable"))] +pub use crate::connection::ConnectionHandle; + +impl ForeignClass for ConnectionHandle {} + +/// Sets callback, invoked when this `Connection` will close. +#[no_mangle] +pub unsafe extern "C" fn ConnectionHandle__on_close( + this: *const ConnectionHandle, + f: Dart_Handle, +) { + let this = this.as_ref().unwrap(); + + // TODO: Remove unwrap when propagating errors from Rust to Dart is + // implemented. + this.on_close(platform::Function::new(f)).unwrap(); +} + +/// Sets callback, invoked when a new [`remote::Track`] is added to this +/// [`Connection`]. +/// +/// [`remote::Track`]: crate::media::track::remote::Track +/// [`Connection`]: crate::connection::Connection +#[no_mangle] +pub unsafe extern "C" fn ConnectionHandle__on_remote_track_added( + this: *const ConnectionHandle, + f: Dart_Handle, +) { + let this = this.as_ref().unwrap(); + + // TODO: Remove unwrap when propagating errors from Rust to Dart is + // implemented. + this.on_remote_track_added(platform::Function::new(f)) + .unwrap(); +} + +/// Sets callback, invoked when a connection quality score is updated by +/// a server. +#[no_mangle] +pub unsafe extern "C" fn ConnectionHandle__on_quality_score_update( + this: *const ConnectionHandle, + f: Dart_Handle, +) { + let this = this.as_ref().unwrap(); + + // TODO: Remove unwrap when propagating errors from Rust to Dart is + // implemented. + this.on_quality_score_update(platform::Function::new(f)) + .unwrap(); +} + +/// Returns remote `Member` ID. +#[no_mangle] +pub unsafe extern "C" fn ConnectionHandle__get_remote_member_id( + this: *const ConnectionHandle, +) -> *const c_char { + let this = this.as_ref().unwrap(); + + // TODO: Remove unwrap when propagating errors from Rust to Dart is + // implemented. + string_into_c_str(this.get_remote_member_id().unwrap()) +} + +/// Frees the data behind the provided pointer. +/// +/// # Safety +/// +/// Should be called when object is no longer needed. Calling this more than +/// once for the same pointer is equivalent to double free. +#[no_mangle] +pub unsafe extern "C" fn ConnectionHandle__free(this: *mut ConnectionHandle) { + let _ = ConnectionHandle::from_ptr(this); +} + +#[cfg(feature = "mockable")] +mod mock { + use crate::{ + api::{JasonError, RemoteMediaTrack}, + platform, + }; + + pub struct ConnectionHandle; + + #[allow(clippy::missing_errors_doc)] + impl ConnectionHandle { + pub fn get_remote_member_id(&self) -> Result { + Ok(String::from("ConnectionHandle.get_remote_member_id")) + } + + pub fn on_close( + &self, + f: platform::Function<()>, + ) -> Result<(), JasonError> { + f.call0(); + Ok(()) + } + + pub fn on_remote_track_added( + &self, + f: platform::Function, + ) -> Result<(), JasonError> { + f.call1(RemoteMediaTrack); + Ok(()) + } + + pub fn on_quality_score_update( + &self, + f: platform::Function, + ) -> Result<(), JasonError> { + f.call1(4); + Ok(()) + } + } +} diff --git a/jason/jason-dummy/src/device_video_track_constraints.rs b/jason/src/api/dart/device_video_track_constraints.rs similarity index 63% rename from jason/jason-dummy/src/device_video_track_constraints.rs rename to jason/src/api/dart/device_video_track_constraints.rs index 1f981b193..da181aec8 100644 --- a/jason/jason-dummy/src/device_video_track_constraints.rs +++ b/jason/src/api/dart/device_video_track_constraints.rs @@ -1,41 +1,12 @@ -use std::convert::TryFrom; +use std::{convert::TryFrom, os::raw::c_char}; -use crate::{utils::c_str_into_string, ForeignClass}; +use crate::media::FacingMode; -pub struct DeviceVideoTrackConstraints; +use super::{utils::c_str_into_string, ForeignClass}; -impl ForeignClass for 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 use crate::media::DeviceVideoTrackConstraints; - 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 ForeignClass for DeviceVideoTrackConstraints {} impl From for FacingMode { fn from(value: u8) -> Self { @@ -51,22 +22,30 @@ impl From for FacingMode { } } +/// Creates new [`DeviceVideoTrackConstraints`] with none constraints +/// configured. #[no_mangle] pub extern "C" fn DeviceVideoTrackConstraints__new( ) -> *const DeviceVideoTrackConstraints { DeviceVideoTrackConstraints::new().into_ptr() } +/// Sets an exact [deviceId][1] constraint. +/// +/// [1]: https://w3.org/TR/mediacapture-streams#def-constraint-deviceId #[no_mangle] pub unsafe extern "C" fn DeviceVideoTrackConstraints__device_id( this: *mut DeviceVideoTrackConstraints, - device_id: *const libc::c_char, + device_id: *const c_char, ) { let this = this.as_mut().unwrap(); this.device_id(c_str_into_string(device_id)); } +/// Sets an exact [facingMode][1] constraint. +/// +/// [1]: https://w3.org/TR/mediacapture-streams#dom-constraindomstring #[no_mangle] pub unsafe extern "C" fn DeviceVideoTrackConstraints__exact_facing_mode( this: *mut DeviceVideoTrackConstraints, @@ -77,6 +56,9 @@ pub unsafe extern "C" fn DeviceVideoTrackConstraints__exact_facing_mode( this.exact_facing_mode(FacingMode::try_from(facing_mode).unwrap()); } +/// Sets an ideal [facingMode][1] constraint. +/// +/// [1]: https://w3.org/TR/mediacapture-streams#dom-constraindomstring #[no_mangle] pub unsafe extern "C" fn DeviceVideoTrackConstraints__ideal_facing_mode( this: *mut DeviceVideoTrackConstraints, @@ -87,6 +69,9 @@ pub unsafe extern "C" fn DeviceVideoTrackConstraints__ideal_facing_mode( this.ideal_facing_mode(FacingMode::try_from(facing_mode).unwrap()); } +/// Sets an exact [height][1] constraint. +/// +/// [1]: https://tinyurl.com/w3-streams#def-constraint-height #[no_mangle] pub unsafe extern "C" fn DeviceVideoTrackConstraints__exact_height( this: *mut DeviceVideoTrackConstraints, @@ -97,6 +82,9 @@ pub unsafe extern "C" fn DeviceVideoTrackConstraints__exact_height( this.exact_height(height); } +/// Sets an ideal [height][1] constraint. +/// +/// [1]: https://tinyurl.com/w3-streams#def-constraint-height #[no_mangle] pub unsafe extern "C" fn DeviceVideoTrackConstraints__ideal_height( this: *mut DeviceVideoTrackConstraints, @@ -107,6 +95,9 @@ pub unsafe extern "C" fn DeviceVideoTrackConstraints__ideal_height( this.ideal_height(height); } +/// Sets a range of a [height][1] constraint. +/// +/// [1]: https://tinyurl.com/w3-streams#def-constraint-height #[no_mangle] pub unsafe extern "C" fn DeviceVideoTrackConstraints__height_in_range( this: *mut DeviceVideoTrackConstraints, @@ -118,6 +109,9 @@ pub unsafe extern "C" fn DeviceVideoTrackConstraints__height_in_range( this.height_in_range(min, max); } +/// Sets an exact [width][1] constraint. +/// +/// [1]: https://tinyurl.com/w3-streams#def-constraint-width #[no_mangle] pub unsafe extern "C" fn DeviceVideoTrackConstraints__exact_width( this: *mut DeviceVideoTrackConstraints, @@ -128,6 +122,9 @@ pub unsafe extern "C" fn DeviceVideoTrackConstraints__exact_width( this.exact_width(width); } +/// Sets an ideal [width][1] constraint. +/// +/// [1]: https://tinyurl.com/w3-streams#def-constraint-width #[no_mangle] pub unsafe extern "C" fn DeviceVideoTrackConstraints__ideal_width( this: *mut DeviceVideoTrackConstraints, @@ -138,6 +135,9 @@ pub unsafe extern "C" fn DeviceVideoTrackConstraints__ideal_width( this.ideal_width(width); } +/// Sets a range of a [width][1] constraint. +/// +/// [1]: https://tinyurl.com/w3-streams#def-constraint-width #[no_mangle] pub unsafe extern "C" fn DeviceVideoTrackConstraints__width_in_range( this: *mut DeviceVideoTrackConstraints, @@ -149,9 +149,15 @@ pub unsafe extern "C" fn DeviceVideoTrackConstraints__width_in_range( this.width_in_range(min, max); } +/// Frees the data behind the provided pointer. +/// +/// # Safety +/// +/// Should be called when object is no longer needed. Calling this more than +/// once for the same pointer is equivalent to double free. #[no_mangle] pub unsafe extern "C" fn DeviceVideoTrackConstraints__free( this: *mut DeviceVideoTrackConstraints, ) { - DeviceVideoTrackConstraints::from_ptr(this); + let _ = DeviceVideoTrackConstraints::from_ptr(this); } diff --git a/jason/src/api/dart/display_video_track_constraints.rs b/jason/src/api/dart/display_video_track_constraints.rs new file mode 100644 index 000000000..25f4f925b --- /dev/null +++ b/jason/src/api/dart/display_video_track_constraints.rs @@ -0,0 +1,26 @@ +use super::ForeignClass; + +pub use crate::media::DisplayVideoTrackConstraints; + +impl ForeignClass for DisplayVideoTrackConstraints {} + +/// Creates new [`DisplayVideoTrackConstraints`] with none constraints +/// configured. +#[no_mangle] +pub extern "C" fn DisplayVideoTrackConstraints__new( +) -> *const DisplayVideoTrackConstraints { + DisplayVideoTrackConstraints::new().into_ptr() +} + +/// Frees the data behind the provided pointer. +/// +/// # Safety +/// +/// Should be called when object is no longer needed. Calling this more than +/// once for the same pointer is equivalent to double free. +#[no_mangle] +pub unsafe extern "C" fn DisplayVideoTrackConstraints__free( + this: *mut DisplayVideoTrackConstraints, +) { + let _ = DisplayVideoTrackConstraints::from_ptr(this); +} diff --git a/jason/src/api/dart/input_device_info.rs b/jason/src/api/dart/input_device_info.rs new file mode 100644 index 000000000..a294eaa29 --- /dev/null +++ b/jason/src/api/dart/input_device_info.rs @@ -0,0 +1,100 @@ +use std::os::raw::c_char; + +use super::{utils::string_into_c_str, ForeignClass}; + +#[cfg(feature = "mockable")] +pub use self::mock::InputDeviceInfo; +#[cfg(not(feature = "mockable"))] +pub use crate::platform::InputDeviceInfo; + +impl ForeignClass for InputDeviceInfo {} + +/// Returns unique identifier of the represented device. +#[no_mangle] +pub unsafe extern "C" fn InputDeviceInfo__device_id( + this: *const InputDeviceInfo, +) -> *const c_char { + let this = this.as_ref().unwrap(); + + string_into_c_str(this.device_id()) +} + +/// Returns kind of the represented device. +/// +/// This representation of [MediaDeviceInfo][1] ONLY for input device. +/// +/// [1]: https://w3.org/TR/mediacapture-streams/#device-info +#[no_mangle] +pub unsafe extern "C" fn InputDeviceInfo__kind( + this: *const InputDeviceInfo, +) -> u8 { + let this = this.as_ref().unwrap(); + + this.kind() as u8 // TODO: .into() +} + +/// Returns label describing the represented device (for example "External USB +/// Webcam"). +/// +/// If the device has no associated label, then returns an empty string. +#[no_mangle] +pub unsafe extern "C" fn InputDeviceInfo__label( + this: *const InputDeviceInfo, +) -> *const c_char { + let this = this.as_ref().unwrap(); + + string_into_c_str(this.label()) +} + +/// Returns group identifier of the represented device. +/// +/// Two devices have the same group identifier if they belong to the same +/// physical device. For example, the audio input and output devices +/// representing the speaker and microphone of the same headset have the +/// same [groupId][1]. +/// +/// [1]: https://w3.org/TR/mediacapture-streams/#dom-mediadeviceinfo-groupid +#[no_mangle] +pub unsafe extern "C" fn InputDeviceInfo__group_id( + this: *const InputDeviceInfo, +) -> *const c_char { + let this = this.as_ref().unwrap(); + + string_into_c_str(this.group_id()) +} + +/// Frees the data behind the provided pointer. +/// +/// # Safety +/// +/// Should be called when object is no longer needed. Calling this more than +/// once for the same pointer is equivalent to double free. +#[no_mangle] +pub unsafe extern "C" fn InputDeviceInfo__free(this: *mut InputDeviceInfo) { + let _ = InputDeviceInfo::from_ptr(this); +} + +#[cfg(feature = "mockable")] +mod mock { + use crate::media::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") + } + } +} diff --git a/jason/src/api/dart/jason.rs b/jason/src/api/dart/jason.rs new file mode 100644 index 000000000..677569589 --- /dev/null +++ b/jason/src/api/dart/jason.rs @@ -0,0 +1,85 @@ +use super::{ + media_manager_handle::MediaManagerHandle, room_handle::RoomHandle, + ForeignClass, +}; + +#[cfg(feature = "mockable")] +pub use self::mock::Jason; +#[cfg(not(feature = "mockable"))] +pub use crate::jason::Jason; + +impl ForeignClass for Jason {} + +/// Instantiates a new [`Jason`] interface to interact with this library. +#[no_mangle] +pub extern "C" fn Jason__new() -> *const Jason { + Jason::new().into_ptr() +} + +/// Creates a new [`Room`] and returns its [`RoomHandle`]. +/// +/// [`Room`]: crate::room::Room +#[no_mangle] +pub unsafe extern "C" fn Jason__init_room( + this: *const Jason, +) -> *const RoomHandle { + let this = this.as_ref().unwrap(); + + this.init_room().into_ptr() +} + +/// Returns a [`MediaManagerHandle`]. +#[no_mangle] +pub unsafe extern "C" fn Jason__media_manager( + this: *const Jason, +) -> *const MediaManagerHandle { + let this = this.as_ref().unwrap(); + + this.media_manager().into_ptr() +} + +/// Closes the provided [`RoomHandle`]. +#[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(RoomHandle::from_ptr(room_to_delete)); +} + +/// Frees the data behind the provided pointer. +/// +/// # Safety +/// +/// Should be called when object is no longer needed. Calling this more than +/// once for the same pointer is equivalent to double free. +#[no_mangle] +pub unsafe extern "C" fn Jason__free(this: *mut Jason) { + let _ = Jason::from_ptr(this); +} + +#[cfg(feature = "mockable")] +mod mock { + use crate::api::{MediaManagerHandle, RoomHandle}; + + pub struct Jason; + + impl Jason { + pub fn new() -> Self { + crate::platform::init_logger(); + Self + } + + pub fn init_room(&self) -> RoomHandle { + RoomHandle + } + + pub fn media_manager(&self) -> MediaManagerHandle { + MediaManagerHandle + } + + pub fn close_room(&self, _: RoomHandle) {} + } +} diff --git a/jason/src/api/dart/jason_error.rs b/jason/src/api/dart/jason_error.rs new file mode 100644 index 000000000..a6219b96e --- /dev/null +++ b/jason/src/api/dart/jason_error.rs @@ -0,0 +1,54 @@ +//! App error exported to JS side. +// TODO: This is just a copy of wasm version, Rust to Dart error propagation +// will be implemented later. + +use std::fmt::{Debug, Display}; + +use derive_more::{Display, From}; +use tracerr::{Trace, Traced}; + +use crate::{platform, utils::JsCaused}; + +/// Representation of an app error exported to JS side. +/// +/// Contains JS side error if it's the cause, and a trace information. +#[derive(From, Clone, Debug, Display)] +#[display(fmt = "{}: {}\n{}", name, message, trace)] +pub struct JasonError { + /// Name of this [`JasonError`]. + name: &'static str, + + /// Message describing this [`JasonError`]. + message: String, + + /// [`Trace`] information of this [`JasonError`]. + trace: Trace, + + /// Optional cause of this [`JasonError`] as a JS side error. + source: Option, +} + +impl From<(E, Trace)> for JasonError +where + E::Error: Into, +{ + #[inline] + fn from((err, trace): (E, Trace)) -> Self { + Self { + name: err.name(), + message: err.to_string(), + trace, + source: err.js_cause().map(Into::into), + } + } +} + +impl From> for JasonError +where + E::Error: Into, +{ + #[inline] + fn from(traced: Traced) -> Self { + Self::from(traced.into_parts()) + } +} diff --git a/jason/src/api/dart/local_media_track.rs b/jason/src/api/dart/local_media_track.rs new file mode 100644 index 000000000..34baf0bd0 --- /dev/null +++ b/jason/src/api/dart/local_media_track.rs @@ -0,0 +1,69 @@ +use super::ForeignClass; + +#[cfg(feature = "mockable")] +pub use self::mock::LocalMediaTrack; +#[cfg(not(feature = "mockable"))] +pub use crate::media::track::local::LocalMediaTrack; + +impl ForeignClass for LocalMediaTrack {} + +/// Returns a [`MediaKind::Audio`] if this [`LocalMediaTrack`] represents an +/// audio track, or a [`MediaKind::Video`] if it represents a video track. +/// +/// [`MediaKind::Audio`]: crate::media::MediaKind::Audio +/// [`MediaKind::Video`]: crate::media::MediaKind::Video +#[no_mangle] +pub unsafe extern "C" fn LocalMediaTrack__kind( + this: *const LocalMediaTrack, +) -> u8 { + let this = this.as_ref().unwrap(); + + this.kind() as u8 +} + +/// Returns a [`MediaSourceKind::Device`] if this [`LocalMediaTrack`] is +/// sourced from some device (webcam/microphone), or a +/// [`MediaSourceKind::Display`] if it's captured via +/// [MediaDevices.getDisplayMedia()][1]. +/// +/// [1]: https://w3.org/TR/screen-capture/#dom-mediadevices-getdisplaymedia +/// [`MediaSourceKind::Device`]: crate::media::MediaSourceKind::Device +/// [`MediaSourceKind::Display`]: crate::media::MediaSourceKind::Display +#[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 +} + +/// Frees the data behind the provided pointer. +/// +/// # Safety +/// +/// Should be called when object is no longer needed. Calling this more than +/// once for the same pointer is equivalent to double free. +#[no_mangle] +pub unsafe extern "C" fn LocalMediaTrack__free(this: *mut LocalMediaTrack) { + let _ = LocalMediaTrack::from_ptr(this); +} + +#[cfg(feature = "mockable")] +mod mock { + use crate::media::{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 + } +} diff --git a/jason/src/api/dart/media_manager_handle.rs b/jason/src/api/dart/media_manager_handle.rs new file mode 100644 index 000000000..e17e0656d --- /dev/null +++ b/jason/src/api/dart/media_manager_handle.rs @@ -0,0 +1,94 @@ +use futures::FutureExt as _; + +use super::{ + input_device_info::InputDeviceInfo, local_media_track::LocalMediaTrack, + media_stream_settings::MediaStreamSettings, utils::PtrArray, ForeignClass, +}; + +#[cfg(feature = "mockable")] +pub use self::mock::MediaManagerHandle; +#[cfg(not(feature = "mockable"))] +pub use crate::media::MediaManagerHandle; + +impl ForeignClass for MediaManagerHandle {} + +/// Returns [`LocalMediaTrack`]s objects, built from the provided +/// [`MediaStreamSettings`]. +#[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(); + + // TODO: Remove now_or_never when polling from Dart is implemented. + // Remove unwrap when propagating errors from Rust to Dart is + // implemented. + PtrArray::new( + this.init_local_tracks(caps.clone()) + .now_or_never() + .unwrap() + .unwrap(), + ) +} + +/// Returns a list of [`InputDeviceInfo`] objects representing available media +/// input and devices, such as microphones, cameras, and so forth. +#[no_mangle] +pub unsafe extern "C" fn MediaManagerHandle__enumerate_devices( + this: *const MediaManagerHandle, +) -> PtrArray { + let this = this.as_ref().unwrap(); + + // TODO: Remove now_or_never when polling from Dart is implemented. + // Remove unwrap when propagating errors from Rust to Dart is + // implemented. + PtrArray::new(this.enumerate_devices().now_or_never().unwrap().unwrap()) +} + +/// Frees the data behind the provided pointer. +/// +/// # Safety +/// +/// Should be called when object is no longer needed. Calling this more than +/// once for the same pointer is equivalent to double free. +#[no_mangle] +pub unsafe extern "C" fn MediaManagerHandle__free( + this: *mut MediaManagerHandle, +) { + let _ = MediaManagerHandle::from_ptr(this); +} + +#[cfg(feature = "mockable")] +mod mock { + use crate::api::{ + InputDeviceInfo, JasonError, LocalMediaTrack, MediaStreamSettings, + }; + + pub struct MediaManagerHandle; + + #[allow(clippy::missing_errors_doc)] + impl MediaManagerHandle { + pub async fn enumerate_devices( + &self, + ) -> Result, JasonError> { + Ok(vec![ + InputDeviceInfo {}, + InputDeviceInfo {}, + InputDeviceInfo {}, + ]) + } + + pub async fn init_local_tracks( + &self, + _caps: MediaStreamSettings, + ) -> Result, JasonError> { + Ok(vec![ + LocalMediaTrack {}, + LocalMediaTrack {}, + LocalMediaTrack {}, + ]) + } + } +} diff --git a/jason/jason-dummy/src/media_stream_settings.rs b/jason/src/api/dart/media_stream_settings.rs similarity index 66% rename from jason/jason-dummy/src/media_stream_settings.rs rename to jason/src/api/dart/media_stream_settings.rs index befcaad39..1689a6035 100644 --- a/jason/jason-dummy/src/media_stream_settings.rs +++ b/jason/src/api/dart/media_stream_settings.rs @@ -1,31 +1,23 @@ -use crate::{ +use super::{ audio_track_constraints::AudioTrackConstraints, device_video_track_constraints::DeviceVideoTrackConstraints, display_video_track_constraints::DisplayVideoTrackConstraints, ForeignClass, }; -pub struct MediaStreamSettings; +pub use crate::media::MediaStreamSettings; impl ForeignClass for 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) {} -} - +/// Creates new [`MediaStreamSettings`] with none constraints configured. #[no_mangle] pub extern "C" fn MediaStreamSettings__new() -> *const MediaStreamSettings { MediaStreamSettings::new().into_ptr() } +/// Specifies a nature and settings of an audio [`MediaStreamTrack`]. +/// +/// [`MediaStreamTrack`]: crate::platform::MediaStreamTrack #[no_mangle] pub unsafe extern "C" fn MediaStreamSettings__audio( this: *mut MediaStreamSettings, @@ -36,6 +28,7 @@ pub unsafe extern "C" fn MediaStreamSettings__audio( this.audio(AudioTrackConstraints::from_ptr(constraints)); } +/// Set constraints for obtaining a local video sourced from a media device. #[no_mangle] pub unsafe extern "C" fn MediaStreamSettings__device_video( this: *mut MediaStreamSettings, @@ -46,6 +39,7 @@ pub unsafe extern "C" fn MediaStreamSettings__device_video( this.device_video(DeviceVideoTrackConstraints::from_ptr(constraints)); } +/// Set constraints for capturing a local video from user's display. #[no_mangle] pub unsafe extern "C" fn MediaStreamSettings__display_video( this: *mut MediaStreamSettings, @@ -56,9 +50,15 @@ pub unsafe extern "C" fn MediaStreamSettings__display_video( this.display_video(DisplayVideoTrackConstraints::from_ptr(constraints)); } +/// Frees the data behind the provided pointer. +/// +/// # Safety +/// +/// Should be called when object is no longer needed. Calling this more than +/// once for the same pointer is equivalent to double free. #[no_mangle] pub unsafe extern "C" fn MediaStreamSettings__free( this: *mut MediaStreamSettings, ) { - MediaStreamSettings::from_ptr(this); + let _ = MediaStreamSettings::from_ptr(this); } diff --git a/jason/src/api/dart/mod.rs b/jason/src/api/dart/mod.rs new file mode 100644 index 000000000..0f39f6c5b --- /dev/null +++ b/jason/src/api/dart/mod.rs @@ -0,0 +1,63 @@ +//! External [`Jason`] API exposing functions that can be called via FFI and +//! designed to be integrated into a [Flutter] plugin. +//! +//! [`Jason`]: crate::api::Jason +//! [Flutter]: https://flutter.dev + +// TODO: Improve documentation in this module. +#![allow(clippy::missing_safety_doc, clippy::missing_panics_doc, missing_docs)] + +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 jason_error; +pub mod local_media_track; +pub mod media_manager_handle; +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; +pub mod utils; + +pub use self::{ + audio_track_constraints::AudioTrackConstraints, + connection_handle::ConnectionHandle, + device_video_track_constraints::DeviceVideoTrackConstraints, + display_video_track_constraints::DisplayVideoTrackConstraints, + input_device_info::InputDeviceInfo, jason::Jason, jason_error::JasonError, + local_media_track::LocalMediaTrack, + media_manager_handle::MediaManagerHandle, + media_stream_settings::MediaStreamSettings, + reconnect_handle::ReconnectHandle, remote_media_track::RemoteMediaTrack, + room_close_reason::RoomCloseReason, room_handle::RoomHandle, +}; + +/// Rust structure having wrapper class in Dart. +/// +/// Intended to be passed through FFI boundaries as thin pointers. +pub trait ForeignClass: Sized { + /// Consumes itself returning a wrapped raw pointer obtained via + /// [`Box::into_raw()`]. + #[inline] + #[must_use] + fn into_ptr(self) -> *const Self { + Box::into_raw(Box::new(self)) + } + + /// Constructs a [`ForeignClass`] from the given raw pointer via + /// [`Box::from_raw()`]. + /// + /// # Safety + /// + /// Same as for [`Box::from_raw()`]. + #[inline] + #[must_use] + unsafe fn from_ptr(this: *mut Self) -> Self { + *Box::from_raw(this) + } +} diff --git a/jason/src/api/dart/reconnect_handle.rs b/jason/src/api/dart/reconnect_handle.rs new file mode 100644 index 000000000..9c2b65192 --- /dev/null +++ b/jason/src/api/dart/reconnect_handle.rs @@ -0,0 +1,31 @@ +use super::ForeignClass; + +#[cfg(feature = "mockable")] +pub use self::mock::ReconnectHandle; +#[cfg(not(feature = "mockable"))] +pub use crate::rpc::ReconnectHandle; + +impl ForeignClass for ReconnectHandle {} + +/// Frees the data behind the provided pointer. +/// +/// # Safety +/// +/// Should be called when object is no longer needed. Calling this more than +/// once for the same pointer is equivalent to double free. +#[no_mangle] +pub unsafe extern "C" fn ReconnectHandle__free(this: *mut ReconnectHandle) { + let _ = ReconnectHandle::from_ptr(this); +} + +#[cfg(feature = "mockable")] +mod mock { + 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> + } +} diff --git a/jason/src/api/dart/remote_media_track.rs b/jason/src/api/dart/remote_media_track.rs new file mode 100644 index 000000000..f9052e0da --- /dev/null +++ b/jason/src/api/dart/remote_media_track.rs @@ -0,0 +1,163 @@ +use dart_sys::Dart_Handle; + +use crate::platform; + +use super::ForeignClass; + +#[cfg(feature = "mockable")] +pub use self::mock::RemoteMediaTrack; +#[cfg(not(feature = "mockable"))] +pub use crate::media::track::remote::Track as RemoteMediaTrack; + +impl ForeignClass for RemoteMediaTrack {} + +/// Sets callback, invoked when this [`RemoteMediaTrack`] is enabled. +#[no_mangle] +pub unsafe extern "C" fn RemoteMediaTrack__on_enabled( + this: *const RemoteMediaTrack, + f: Dart_Handle, +) { + let this = this.as_ref().unwrap(); + this.on_enabled(platform::Function::new(f)); +} + +/// Sets callback, invoked when this [`RemoteMediaTrack`] is disabled. +#[no_mangle] +pub unsafe extern "C" fn RemoteMediaTrack__on_disabled( + this: *const RemoteMediaTrack, + f: Dart_Handle, +) { + let this = this.as_ref().unwrap(); + this.on_disabled(platform::Function::new(f)); +} + +/// Sets callback to invoke when this [`RemoteMediaTrack`] is muted. +#[no_mangle] +pub unsafe extern "C" fn RemoteMediaTrack__on_muted( + this: *const RemoteMediaTrack, + f: Dart_Handle, +) { + let this = this.as_ref().unwrap(); + this.on_muted(platform::Function::new(f)); +} + +/// Sets callback to invoke when this [`RemoteMediaTrack`] is unmuted. +#[no_mangle] +pub unsafe extern "C" fn RemoteMediaTrack__on_unmuted( + this: *const RemoteMediaTrack, + f: Dart_Handle, +) { + let this = this.as_ref().unwrap(); + this.on_unmuted(platform::Function::new(f)); +} + +/// Sets callback to invoke when this [`RemoteMediaTrack`] is stopped. +#[no_mangle] +pub unsafe extern "C" fn RemoteMediaTrack__on_stopped( + this: *const RemoteMediaTrack, + f: Dart_Handle, +) { + let this = this.as_ref().unwrap(); + this.on_stopped(platform::Function::new(f)); +} + +/// Indicates whether this [`RemoteMediaTrack`] is enabled. +#[no_mangle] +pub unsafe extern "C" fn RemoteMediaTrack__enabled( + this: *const RemoteMediaTrack, +) -> u8 { + let this = this.as_ref().unwrap(); + + this.enabled() as u8 +} + +/// Indicate whether this [`RemoteMediaTrack`] is muted. +#[no_mangle] +pub unsafe extern "C" fn RemoteMediaTrack__muted( + this: *const RemoteMediaTrack, +) -> u8 { + let this = this.as_ref().unwrap(); + + this.muted() as u8 +} + +/// Returns this [`RemoteMediaTrack`]'s kind (audio/video). +#[no_mangle] +pub unsafe extern "C" fn RemoteMediaTrack__kind( + this: *const RemoteMediaTrack, +) -> u8 { + let this = this.as_ref().unwrap(); + + this.kind() as u8 +} + +/// Returns this [`RemoteMediaTrack`]'s media source kind. +#[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 +} + +/// Frees the data behind the provided pointer. +/// +/// # Safety +/// +/// Should be called when object is no longer needed. Calling this more than +/// once for the same pointer is equivalent to double free. +#[no_mangle] +pub unsafe extern "C" fn RemoteMediaTrack__free(this: *mut RemoteMediaTrack) { + let _ = RemoteMediaTrack::from_ptr(this); +} + +#[cfg(feature = "mockable")] +mod mock { + use crate::{ + media::{MediaKind, MediaSourceKind}, + platform, + }; + + 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 get_track(&self) -> sys::MediaStreamTrack + + pub fn on_enabled(&self, cb: platform::Function<()>) { + cb.call0(); + } + + pub fn on_disabled(&self, cb: platform::Function<()>) { + cb.call0(); + } + + pub fn on_muted(&self, cb: platform::Function<()>) { + cb.call0(); + } + + pub fn on_unmuted(&self, cb: platform::Function<()>) { + cb.call0(); + } + + pub fn on_stopped(&self, cb: platform::Function<()>) { + cb.call0(); + } + } +} diff --git a/jason/jason-dummy/src/room_close_reason.rs b/jason/src/api/dart/room_close_reason.rs similarity index 50% rename from jason/jason-dummy/src/room_close_reason.rs rename to jason/src/api/dart/room_close_reason.rs index 29a9eb355..9ca8469fa 100644 --- a/jason/jason-dummy/src/room_close_reason.rs +++ b/jason/src/api/dart/room_close_reason.rs @@ -1,32 +1,26 @@ -use crate::{utils::string_into_c_str, ForeignClass}; +use std::os::raw::c_char; -pub struct RoomCloseReason; +use super::{utils::string_into_c_str, ForeignClass}; -impl ForeignClass for RoomCloseReason {} - -impl RoomCloseReason { - pub fn reason(&self) -> String { - String::from("RoomCloseReason.reason") - } - - pub fn is_closed_by_server(&self) -> bool { - false - } +pub use crate::room::RoomCloseReason; - pub fn is_err(&self) -> bool { - true - } -} +impl ForeignClass for RoomCloseReason {} +/// Returns a close reason of a [`Room`]. +/// +/// [`Room`]: crate::room::Room #[no_mangle] pub unsafe extern "C" fn RoomCloseReason__reason( this: *const RoomCloseReason, -) -> *const libc::c_char { +) -> *const c_char { let this = this.as_ref().unwrap(); string_into_c_str(this.reason()) } +/// Indicates whether a [`Room`] was closed by server. +/// +/// [`Room`]: crate::room::Room #[no_mangle] pub unsafe extern "C" fn RoomCloseReason__is_closed_by_server( this: *const RoomCloseReason, @@ -36,6 +30,9 @@ pub unsafe extern "C" fn RoomCloseReason__is_closed_by_server( this.is_closed_by_server() as u8 } +/// Indicates whether a [`Room`]'s close reason is considered as an error. +/// +/// [`Room`]: crate::room::Room #[no_mangle] pub unsafe extern "C" fn RoomCloseReason__is_err( this: *const RoomCloseReason, @@ -45,7 +42,13 @@ pub unsafe extern "C" fn RoomCloseReason__is_err( this.is_err() as u8 } +/// Frees the data behind the provided pointer. +/// +/// # Safety +/// +/// Should be called when object is no longer needed. Calling this more than +/// once for the same pointer is equivalent to double free. #[no_mangle] pub unsafe extern "C" fn RoomCloseReason__free(this: *mut RoomCloseReason) { - RoomCloseReason::from_ptr(this); + let _ = RoomCloseReason::from_ptr(this); } diff --git a/jason/src/api/dart/room_handle.rs b/jason/src/api/dart/room_handle.rs new file mode 100644 index 000000000..599919323 --- /dev/null +++ b/jason/src/api/dart/room_handle.rs @@ -0,0 +1,166 @@ +use dart_sys::Dart_Handle; + +use crate::{api::dart::ForeignClass, platform}; + +#[cfg(feature = "mockable")] +pub use self::mock::RoomHandle; +#[cfg(not(feature = "mockable"))] +pub use crate::room::RoomHandle; + +impl ForeignClass for RoomHandle {} + +/// Sets callback, invoked when a new [`Connection`] with some remote `Peer` +/// is established. +/// +/// [`Connection`]: crate::connection::Connection +#[no_mangle] +pub unsafe extern "C" fn RoomHandle__on_new_connection( + this: *mut RoomHandle, + cb: Dart_Handle, +) { + let this = this.as_ref().unwrap(); + + // TODO: Remove unwrap when propagating errors from Rust to Dart is + // implemented. + this.on_new_connection(platform::Function::new(cb)).unwrap(); +} + +/// Sets callback, invoked on this [`Room`] close, providing a +/// [`RoomCloseReason`]. +/// +/// [`Room`]: crate::room::Room +/// [`RoomCloseReason`]: crate::room::RoomCloseReason +#[no_mangle] +pub unsafe extern "C" fn RoomHandle__on_close( + this: *mut RoomHandle, + cb: Dart_Handle, +) { + let this = this.as_ref().unwrap(); + + // TODO: Remove unwrap when propagating errors from Rust to Dart is + // implemented. + this.on_close(platform::Function::new(cb)).unwrap(); +} + +/// Sets callback, invoked when a new [`LocalMediaTrack`] is added to this +/// [`Room`]. +/// +/// This might happen in such cases: +/// 1. Media server initiates a media request. +/// 2. `enable_audio`/`enable_video` is called. +/// 3. [`MediaStreamSettings`] updated via `set_local_media_settings`. +/// +/// [`Room`]: crate::room::Room +/// [`MediaStreamSettings`]: crate::media::MediaStreamSettings +/// [`LocalMediaTrack`]: crate::media::track::local::LocalMediaTrack +#[no_mangle] +pub unsafe extern "C" fn RoomHandle__on_local_track( + this: *mut RoomHandle, + cb: Dart_Handle, +) { + let this = this.as_ref().unwrap(); + + // TODO: Remove unwrap when propagating errors from Rust to Dart is + // implemented. + this.on_local_track(platform::Function::new(cb)).unwrap(); +} + +/// Sets callback, invoked when a connection with server is lost. +#[no_mangle] +pub unsafe extern "C" fn RoomHandle__on_connection_loss( + this: *mut RoomHandle, + cb: Dart_Handle, +) { + let this = this.as_ref().unwrap(); + + // TODO: Remove unwrap when propagating errors from Rust to Dart is + // implemented. + this.on_connection_loss(platform::Function::new(cb)) + .unwrap(); +} + +/// Frees the data behind the provided pointer. +/// +/// # Safety +/// +/// Should be called when object is no longer needed. Calling this more than +/// once for the same pointer is equivalent to double free. +#[no_mangle] +pub unsafe extern "C" fn RoomHandle__free(this: *mut RoomHandle) { + let _ = RoomHandle::from_ptr(this); +} + +#[cfg(feature = "mockable")] +mod mock { + use crate::{ + api::{ConnectionHandle, JasonError, LocalMediaTrack, ReconnectHandle}, + platform, + room::RoomCloseReason, + rpc::{ClientDisconnect, CloseReason}, + }; + + pub struct RoomHandle; + + #[allow(clippy::missing_errors_doc)] + impl RoomHandle { + pub fn on_new_connection( + &self, + cb: platform::Function, + ) -> Result<(), JasonError> { + cb.call1(ConnectionHandle); + Ok(()) + } + + pub fn on_close( + &self, + cb: platform::Function, + ) -> Result<(), JasonError> { + cb.call1(RoomCloseReason::new(CloseReason::ByClient { + is_err: true, + reason: ClientDisconnect::RpcClientUnexpectedlyDropped, + })); + Ok(()) + } + + pub fn on_local_track( + &self, + cb: platform::Function, + ) -> Result<(), JasonError> { + cb.call1(LocalMediaTrack); + Ok(()) + } + + pub fn on_connection_loss( + &self, + cb: platform::Function, + ) -> Result<(), JasonError> { + cb.call1(ReconnectHandle); + Ok(()) + } + + // pub async fn join(&self, token: String) -> Result<(), JasonError> + // pub fn on_failed_local_media( + // &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> + } +} diff --git a/jason/jason-dummy/src/unimplemented.rs b/jason/src/api/dart/unimplemented.rs similarity index 100% rename from jason/jason-dummy/src/unimplemented.rs rename to jason/src/api/dart/unimplemented.rs diff --git a/jason/jason-dummy/src/utils/arrays.rs b/jason/src/api/dart/utils/arrays.rs similarity index 98% rename from jason/jason-dummy/src/utils/arrays.rs rename to jason/src/api/dart/utils/arrays.rs index 51246b0ef..d864304d0 100644 --- a/jason/jason-dummy/src/utils/arrays.rs +++ b/jason/src/api/dart/utils/arrays.rs @@ -2,7 +2,7 @@ use std::{ffi::c_void, marker::PhantomData, slice}; -use crate::ForeignClass; +use crate::api::ForeignClass; /// Array of pointers to [`ForeignClass`] structs. /// diff --git a/jason/jason-dummy/src/utils/mod.rs b/jason/src/api/dart/utils/mod.rs similarity index 69% rename from jason/jason-dummy/src/utils/mod.rs rename to jason/src/api/dart/utils/mod.rs index fee318f0c..a340ca232 100644 --- a/jason/jason-dummy/src/utils/mod.rs +++ b/jason/src/api/dart/utils/mod.rs @@ -1,10 +1,7 @@ mod arrays; -mod closure; -mod dart_api; mod string; pub use self::{ arrays::PtrArray, - closure::DartClosure, string::{c_str_into_string, string_into_c_str}, }; diff --git a/jason/src/api/dart/utils/string.rs b/jason/src/api/dart/utils/string.rs new file mode 100644 index 000000000..c292bac84 --- /dev/null +++ b/jason/src/api/dart/utils/string.rs @@ -0,0 +1,47 @@ +//! Helper functionality for passing [`String`]s through FFI boundaries. + +use std::{ + ffi::{CStr, CString}, + os::raw::c_char, +}; + +/// Constructs a Rust [`String`] from the provided raw C string. +/// +/// # Panics +/// +/// If the provided C string UTF-8 validation fails. +/// +/// # Safety +/// +/// Same as for [`CStr::from_ptr()`]. +#[inline] +#[must_use] +pub unsafe fn c_str_into_string(string: *const c_char) -> String { + CStr::from_ptr(string).to_str().unwrap().to_owned() +} + +/// Leaks the given [`String`] returning a raw C string that can be passed +/// through FFI boundaries. +/// +/// The pointer (returned by this function) must be returned to Rust and +/// reconstituted via [`CString::from_raw()`] for proper deallocating. +/// +/// # Panics +/// +/// If the provided [`String`] contains an internal `0x0` byte. +#[inline] +#[must_use] +pub fn string_into_c_str(string: String) -> *const c_char { + CString::new(string).unwrap().into_raw() +} + +/// Retakes ownership over a [`CString`] previously transferred to Dart via +/// [`CString::into_raw()`]. +/// +/// # Safety +/// +/// Same as for [`CString::from_raw()`]. +#[no_mangle] +pub unsafe extern "C" fn String_free(s: *mut c_char) { + CString::from_raw(s); +} diff --git a/jason/src/api/mod.rs b/jason/src/api/mod.rs index bed53b2c7..7a8d3b5dc 100644 --- a/jason/src/api/mod.rs +++ b/jason/src/api/mod.rs @@ -1,88 +1,11 @@ //! External [`Jason`] API. -pub mod wasm; - -use crate::media; - -pub use self::wasm::{ - connection_handle::ConnectionHandle, - constraints_update_exception::ConstraintsUpdateException, - input_device_info::InputDeviceInfo, - jason::Jason, - jason_error::JasonError, - local_media_track::LocalMediaTrack, - media_manager_handle::MediaManagerHandle, - media_stream_settings::{ - AudioTrackConstraints, DeviceVideoTrackConstraints, - DisplayVideoTrackConstraints, MediaStreamSettings, - }, - reconnect_handle::ReconnectHandle, - remote_media_track::RemoteMediaTrack, - room_close_reason::RoomCloseReason, - room_handle::RoomHandle, - FacingMode, MediaKind, MediaSourceKind, -}; - -impl From for MediaKind { - #[inline] - fn from(that: media::MediaKind) -> Self { - match that { - media::MediaKind::Audio => Self::Audio, - media::MediaKind::Video => Self::Video, - } - } -} - -impl From for media::MediaKind { - #[inline] - fn from(that: MediaKind) -> Self { - match that { - MediaKind::Audio => Self::Audio, - MediaKind::Video => Self::Video, - } - } -} - -impl From for MediaSourceKind { - #[inline] - fn from(that: media::MediaSourceKind) -> Self { - match that { - media::MediaSourceKind::Device => Self::Device, - media::MediaSourceKind::Display => Self::Display, - } - } -} - -impl From for media::MediaSourceKind { - #[inline] - fn from(that: MediaSourceKind) -> Self { - match that { - MediaSourceKind::Device => Self::Device, - MediaSourceKind::Display => Self::Display, - } - } -} - -impl From for FacingMode { - #[inline] - fn from(that: media::FacingMode) -> Self { - match that { - media::FacingMode::User => Self::User, - media::FacingMode::Environment => Self::Environment, - media::FacingMode::Left => Self::Left, - media::FacingMode::Right => Self::Right, - } - } -} - -impl From for media::FacingMode { - #[inline] - fn from(val: FacingMode) -> Self { - match val { - FacingMode::User => Self::User, - FacingMode::Environment => Self::Environment, - FacingMode::Left => Self::Left, - FacingMode::Right => Self::Right, - } +cfg_if::cfg_if! { + if #[cfg(target_os = "android")] { + mod dart; + pub use self::dart::*; + } else { + mod wasm; + pub use self::wasm::*; } } diff --git a/jason/src/api/wasm/mod.rs b/jason/src/api/wasm/mod.rs index 3867a498a..d3c2f72bd 100644 --- a/jason/src/api/wasm/mod.rs +++ b/jason/src/api/wasm/mod.rs @@ -19,7 +19,25 @@ pub mod room_handle; use derive_more::Display; use wasm_bindgen::prelude::*; -pub use self::jason_error::JasonError; +use crate::media; + +pub use self::{ + connection_handle::ConnectionHandle, + constraints_update_exception::ConstraintsUpdateException, + input_device_info::InputDeviceInfo, + jason::Jason, + jason_error::JasonError, + local_media_track::LocalMediaTrack, + media_manager_handle::MediaManagerHandle, + media_stream_settings::{ + AudioTrackConstraints, DeviceVideoTrackConstraints, + DisplayVideoTrackConstraints, MediaStreamSettings, + }, + reconnect_handle::ReconnectHandle, + remote_media_track::RemoteMediaTrack, + room_close_reason::RoomCloseReason, + room_handle::RoomHandle, +}; /// [MediaStreamTrack.kind][1] representation. /// @@ -64,3 +82,67 @@ pub enum FacingMode { /// Facing to the right of a user. Right, } + +impl From for MediaKind { + #[inline] + fn from(that: media::MediaKind) -> Self { + match that { + media::MediaKind::Audio => Self::Audio, + media::MediaKind::Video => Self::Video, + } + } +} + +impl From for media::MediaKind { + #[inline] + fn from(that: MediaKind) -> Self { + match that { + MediaKind::Audio => Self::Audio, + MediaKind::Video => Self::Video, + } + } +} + +impl From for MediaSourceKind { + #[inline] + fn from(that: media::MediaSourceKind) -> Self { + match that { + media::MediaSourceKind::Device => Self::Device, + media::MediaSourceKind::Display => Self::Display, + } + } +} + +impl From for media::MediaSourceKind { + #[inline] + fn from(that: MediaSourceKind) -> Self { + match that { + MediaSourceKind::Device => Self::Device, + MediaSourceKind::Display => Self::Display, + } + } +} + +impl From for FacingMode { + #[inline] + fn from(that: media::FacingMode) -> Self { + match that { + media::FacingMode::User => Self::User, + media::FacingMode::Environment => Self::Environment, + media::FacingMode::Left => Self::Left, + media::FacingMode::Right => Self::Right, + } + } +} + +impl From for media::FacingMode { + #[inline] + fn from(val: FacingMode) -> Self { + match val { + FacingMode::User => Self::User, + FacingMode::Environment => Self::Environment, + FacingMode::Left => Self::Left, + FacingMode::Right => Self::Right, + } + } +} diff --git a/jason/src/api/wasm/room_handle.rs b/jason/src/api/wasm/room_handle.rs index 76cd2069d..1dc7678c2 100644 --- a/jason/src/api/wasm/room_handle.rs +++ b/jason/src/api/wasm/room_handle.rs @@ -84,7 +84,7 @@ impl RoomHandle { /// /// This might happen in such cases: /// 1. Media server initiates a media request. - /// 2. `disable_audio`/`enable_video` is called. + /// 2. `enable_audio`/`enable_video` is called. /// 3. [`MediaStreamSettings`] is updated via `set_local_media_settings`. /// /// [`Room`]: room::Room diff --git a/jason/src/connection.rs b/jason/src/connection.rs index 2568c1d16..76568201e 100644 --- a/jason/src/connection.rs +++ b/jason/src/connection.rs @@ -148,8 +148,8 @@ impl ConnectionHandle { .map(|inner| inner.remote_id.0.clone()) } - /// Sets callback, invoked when a new [`remote::Track`] will is added to - /// this [`Connection`]. + /// Sets callback, invoked when a new [`remote::Track`] is added to this + /// [`Connection`]. /// /// # Errors /// diff --git a/jason/src/media/manager.rs b/jason/src/media/manager.rs index 9ae49903d..fd166214f 100644 --- a/jason/src/media/manager.rs +++ b/jason/src/media/manager.rs @@ -322,8 +322,8 @@ pub struct MediaManagerHandle(Weak); #[allow(clippy::unused_self)] impl MediaManagerHandle { /// Returns a list of [`platform::InputDeviceInfo`] objects representing - /// available media input and output devices, such as microphones, cameras, - /// and so forth. + /// available media input and devices, such as microphones, cameras, and so + /// forth. /// /// # Errors /// diff --git a/jason/src/peer/media/mod.rs b/jason/src/peer/media/mod.rs index 694bd6927..82b4df6da 100644 --- a/jason/src/peer/media/mod.rs +++ b/jason/src/peer/media/mod.rs @@ -370,7 +370,7 @@ impl InnerMediaConnections { kind: MediaKind, direction: platform::TransceiverDirection, ) -> platform::Transceiver { - platform::Transceiver::from(self.peer.add_transceiver(kind, direction)) + self.peer.add_transceiver(kind, direction) } /// Lookups a [`platform::Transceiver`] by the provided [`mid`]. @@ -380,9 +380,7 @@ impl InnerMediaConnections { &self, mid: &str, ) -> Option { - self.peer - .get_transceiver_by_mid(mid) - .map(platform::Transceiver::from) + self.peer.get_transceiver_by_mid(mid) } } @@ -394,6 +392,7 @@ impl MediaConnections { /// Instantiates a new [`MediaConnections`] storage for the given /// [`platform::RtcPeerConnection`]. #[inline] + #[must_use] pub fn new( peer: Rc, peer_events_sender: mpsc::UnboundedSender, @@ -708,7 +707,7 @@ impl MediaConnections { { if let Some(mid) = receiver.mid() { if let Some(trnscvr) = inner.peer.get_transceiver_by_mid(&mid) { - receiver.replace_transceiver(trnscvr.into()) + receiver.replace_transceiver(trnscvr) } } } diff --git a/jason/src/peer/media/sender/mod.rs b/jason/src/peer/media/sender/mod.rs index 29474b9cb..6525d0163 100644 --- a/jason/src/peer/media/sender/mod.rs +++ b/jason/src/peer/media/sender/mod.rs @@ -6,7 +6,6 @@ use std::{cell::Cell, rc::Rc}; use futures::channel::mpsc; use medea_client_api_proto::TrackId; -use wasm_bindgen_futures::spawn_local; use crate::{ media::{ @@ -177,7 +176,6 @@ impl Sender { self.transceiver .set_send_track(Rc::new(new_track)) .await - .map_err(Into::into) .map_err(MediaConnectionsError::CouldNotInsertLocalTrack) .map_err(tracerr::wrap!())?; @@ -270,7 +268,7 @@ impl Drop for Sender { if !self.transceiver.is_stopped() { self.transceiver .sub_direction(platform::TransceiverDirection::SEND); - spawn_local(self.transceiver.drop_send_track()); + platform::spawn(self.transceiver.drop_send_track()); } } } diff --git a/jason/jason-dummy/src/dart_api_dl/README.md b/jason/src/platform/dart/api_dl/README.md similarity index 100% rename from jason/jason-dummy/src/dart_api_dl/README.md rename to jason/src/platform/dart/api_dl/README.md diff --git a/jason/jason-dummy/src/dart_api_dl/include/bin/dart_io_api.h b/jason/src/platform/dart/api_dl/include/bin/dart_io_api.h similarity index 100% rename from jason/jason-dummy/src/dart_api_dl/include/bin/dart_io_api.h rename to jason/src/platform/dart/api_dl/include/bin/dart_io_api.h diff --git a/jason/jason-dummy/src/dart_api_dl/include/dart_api.h b/jason/src/platform/dart/api_dl/include/dart_api.h similarity index 100% rename from jason/jason-dummy/src/dart_api_dl/include/dart_api.h rename to jason/src/platform/dart/api_dl/include/dart_api.h diff --git a/jason/jason-dummy/src/dart_api_dl/include/dart_api_dl.c b/jason/src/platform/dart/api_dl/include/dart_api_dl.c similarity index 100% rename from jason/jason-dummy/src/dart_api_dl/include/dart_api_dl.c rename to jason/src/platform/dart/api_dl/include/dart_api_dl.c diff --git a/jason/jason-dummy/src/dart_api_dl/include/dart_api_dl.h b/jason/src/platform/dart/api_dl/include/dart_api_dl.h similarity index 100% rename from jason/jason-dummy/src/dart_api_dl/include/dart_api_dl.h rename to jason/src/platform/dart/api_dl/include/dart_api_dl.h diff --git a/jason/jason-dummy/src/dart_api_dl/include/dart_embedder_api.h b/jason/src/platform/dart/api_dl/include/dart_embedder_api.h similarity index 100% rename from jason/jason-dummy/src/dart_api_dl/include/dart_embedder_api.h rename to jason/src/platform/dart/api_dl/include/dart_embedder_api.h diff --git a/jason/jason-dummy/src/dart_api_dl/include/dart_native_api.h b/jason/src/platform/dart/api_dl/include/dart_native_api.h similarity index 100% rename from jason/jason-dummy/src/dart_api_dl/include/dart_native_api.h rename to jason/src/platform/dart/api_dl/include/dart_native_api.h diff --git a/jason/jason-dummy/src/dart_api_dl/include/dart_tools_api.h b/jason/src/platform/dart/api_dl/include/dart_tools_api.h similarity index 100% rename from jason/jason-dummy/src/dart_api_dl/include/dart_tools_api.h rename to jason/src/platform/dart/api_dl/include/dart_tools_api.h diff --git a/jason/jason-dummy/src/dart_api_dl/include/dart_version.h b/jason/src/platform/dart/api_dl/include/dart_version.h similarity index 100% rename from jason/jason-dummy/src/dart_api_dl/include/dart_version.h rename to jason/src/platform/dart/api_dl/include/dart_version.h diff --git a/jason/jason-dummy/src/dart_api_dl/include/internal/dart_api_dl_impl.h b/jason/src/platform/dart/api_dl/include/internal/dart_api_dl_impl.h similarity index 100% rename from jason/jason-dummy/src/dart_api_dl/include/internal/dart_api_dl_impl.h rename to jason/src/platform/dart/api_dl/include/internal/dart_api_dl_impl.h diff --git a/jason/jason-dummy/src/dart_api_dl/trampoline.c b/jason/src/platform/dart/api_dl/trampoline.c similarity index 100% rename from jason/jason-dummy/src/dart_api_dl/trampoline.c rename to jason/src/platform/dart/api_dl/trampoline.c diff --git a/jason/src/platform/dart/constraints.rs b/jason/src/platform/dart/constraints.rs new file mode 100644 index 000000000..cbf7cf9cb --- /dev/null +++ b/jason/src/platform/dart/constraints.rs @@ -0,0 +1,78 @@ +//! Media tracks and streams constraints functionality. + +use derive_more::{AsRef, Into}; + +use crate::media::{ + AudioTrackConstraints, DeviceVideoTrackConstraints, + DisplayVideoTrackConstraints, +}; + +/// [MediaStreamConstraints][1] wrapper. +/// +/// [1]: https://w3.org/TR/mediacapture-streams/#dom-mediastreamconstraints +#[derive(AsRef, Debug, Into)] +pub struct MediaStreamConstraints; + +impl MediaStreamConstraints { + /// Creates a new [`MediaStreamConstraints`] with none constraints + /// configured. + #[inline] + #[must_use] + pub fn new() -> Self { + unimplemented!() + } + + /// Specifies the nature and settings of the `audio` [MediaStreamTrack][1]. + /// + /// [1]: https://w3.org/TR/mediacapture-streams/#mediastreamtrack + #[inline] + pub fn audio(&mut self, audio: AudioTrackConstraints) { + unimplemented!() + } + + /// Specifies the nature and settings of the `video` [MediaStreamTrack][1]. + /// + /// [1]: https://w3.org/TR/mediacapture-streams/#mediastreamtrack + #[inline] + pub fn video(&mut self, video: DeviceVideoTrackConstraints) { + unimplemented!() + } +} + +impl Default for MediaStreamConstraints { + #[inline] + fn default() -> Self { + Self::new() + } +} + +/// [DisplayMediaStreamConstraints][1] wrapper. +/// +/// [1]: https://w3.org/TR/screen-capture/#dom-displaymediastreamconstraints +#[derive(AsRef, Debug, Into)] +pub struct DisplayMediaStreamConstraints(); + +impl Default for DisplayMediaStreamConstraints { + #[inline] + fn default() -> Self { + unimplemented!() + } +} + +impl DisplayMediaStreamConstraints { + /// Creates a new [`DisplayMediaStreamConstraints`] with none constraints + /// configured. + #[inline] + #[must_use] + pub fn new() -> Self { + unimplemented!() + } + + /// Specifies the nature and settings of the `video` [MediaStreamTrack][1]. + /// + /// [1]: https://w3.org/TR/mediacapture-streams/#mediastreamtrack + #[inline] + pub fn video(&mut self, video: DisplayVideoTrackConstraints) { + unimplemented!() + } +} diff --git a/jason/src/platform/dart/error.rs b/jason/src/platform/dart/error.rs new file mode 100644 index 000000000..9cf0fffc7 --- /dev/null +++ b/jason/src/platform/dart/error.rs @@ -0,0 +1,7 @@ +//! Wrapper for Dart exceptions. + +use derive_more::Display; + +/// Wrapper for Dart exception thrown when calling Dart code. +#[derive(Clone, Debug, Display, PartialEq)] +pub struct Error; diff --git a/jason/src/platform/dart/ice_server.rs b/jason/src/platform/dart/ice_server.rs new file mode 100644 index 000000000..81288b7d4 --- /dev/null +++ b/jason/src/platform/dart/ice_server.rs @@ -0,0 +1,20 @@ +//! Collection of [`RtcIceServer`][1]s. +//! +//! [1]: https://w3.org/TR/webrtc/#rtciceserver-dictionary + +use medea_client_api_proto::IceServer; + +/// Collection of [RTCIceServer][1]s. +/// +/// [1]: https://w3.org/TR/webrtc/#rtciceserver-dictionary +#[derive(Debug)] +pub struct RtcIceServers; + +impl From for RtcIceServers +where + I: IntoIterator, +{ + fn from(servers: I) -> Self { + unimplemented!() + } +} diff --git a/jason/src/platform/dart/input_device_info.rs b/jason/src/platform/dart/input_device_info.rs new file mode 100644 index 000000000..a0b459bef --- /dev/null +++ b/jason/src/platform/dart/input_device_info.rs @@ -0,0 +1,65 @@ +//! [MediaDeviceInfo][1] related objects. +//! +//! [1]: https://w3.org/TR/mediacapture-streams/#device-info + +use derive_more::Display; + +use crate::media::MediaKind; + +/// Errors that may occur when parsing [MediaDeviceInfo][1]. +/// +/// [1]: https://w3.org/TR/mediacapture-streams/#device-info +#[derive(Debug, Display)] +pub enum Error { + /// Occurs when kind of media device is not an input device. + #[display(fmt = "Not an input device")] + NotInputDevice, +} + +/// Representation of [MediaDeviceInfo][1]. +/// +/// [1]: https://w3.org/TR/mediacapture-streams/#device-info +pub struct InputDeviceInfo; + +impl InputDeviceInfo { + /// Returns unique identifier for the represented device. + #[inline] + #[must_use] + pub fn device_id(&self) -> String { + unimplemented!() + } + + /// Returns kind of the represented device. + /// + /// This representation of [MediaDeviceInfo][1] ONLY for input device. + /// + /// [1]: https://w3.org/TR/mediacapture-streams/#device-info + #[inline] + #[must_use] + pub fn kind(&self) -> MediaKind { + unimplemented!() + } + + /// Returns label describing the represented device (for example + /// "External USB Webcam"). + /// If the device has no associated label, then returns an empty string. + #[inline] + #[must_use] + pub fn label(&self) -> String { + unimplemented!() + } + + /// Returns group identifier of the represented device. + /// + /// Two devices have the same group identifier if they belong to the same + /// physical device. For example, the audio input and output devices + /// representing the speaker and microphone of the same headset have the + /// same [groupId][1]. + /// + /// [1]: https://w3.org/TR/mediacapture-streams/#dom-mediadeviceinfo-groupid + #[inline] + #[must_use] + pub fn group_id(&self) -> String { + unimplemented!() + } +} diff --git a/jason/src/platform/dart/media_devices.rs b/jason/src/platform/dart/media_devices.rs new file mode 100644 index 000000000..c4546c0fc --- /dev/null +++ b/jason/src/platform/dart/media_devices.rs @@ -0,0 +1,75 @@ +//! [MediaDevices][1] functionality. +//! +//! [1]: https://w3.org/TR/mediacapture-streams#mediadevices + +use tracerr::Traced; + +use crate::{ + media::MediaManagerError, + platform::{ + DisplayMediaStreamConstraints, InputDeviceInfo, MediaStreamConstraints, + MediaStreamTrack, + }, +}; + +/// Collects information about the User Agent's available media input devices. +/// +/// Adapter for a [MediaDevices.enumerateDevices()][1] function. +/// +/// # Errors +/// +/// With [`MediaManagerError::CouldNotGetMediaDevices`] if couldn't get +/// [MediaDevices][2]. +/// +/// With [`MediaManagerError::EnumerateDevicesFailed`] if +/// [MediaDevices.enumerateDevices()][1] returns error. +/// +/// [1]: https://tinyurl.com/w3-streams#dom-mediadevices-enumeratedevices +/// [2]: https://w3.org/TR/mediacapture-streams#mediadevices +pub async fn enumerate_devices( +) -> Result, Traced> { + unimplemented!() +} + +/// Prompts a user for a permission to use a media input which produces vector +/// of [`MediaStreamTrack`]s containing the requested types of media. +/// +/// Adapter for a [MediaDevices.getUserMedia()][1] function. +/// +/// # Errors +/// +/// With [`MediaManagerError::CouldNotGetMediaDevices`] if couldn't get +/// [MediaDevices][2]. +/// +/// With [`MediaManagerError::GetUserMediaFailed`] if +/// [MediaDevices.getUserMedia()][1] returns error. +/// +/// [1]: https://w3.org/TR/mediacapture-streams#dom-mediadevices-getusermedia +/// [2]: https://w3.org/TR/mediacapture-streams#mediadevices +pub async fn get_user_media( + caps: MediaStreamConstraints, +) -> Result, Traced> { + unimplemented!() +} + +/// Prompts a user to select and grant a permission to capture contents of a +/// display or portion thereof (such as a single window) as vector of +/// [`MediaStreamTrack`]. +/// +/// Adapter for a [MediaDevices.getDisplayMedia()][1] function. +/// +/// # Errors +/// +/// With [`MediaManagerError::CouldNotGetMediaDevices`] if couldn't get +/// [MediaDevices][2]. +/// +/// With [`MediaManagerError::GetUserMediaFailed`] if +/// [MediaDevices.getDisplayMedia()][1] returns error. +/// +/// [1]: https://w3.org/TR/screen-capture/#dom-mediadevices-getdisplaymedia +/// [2]: https://w3.org/TR/mediacapture-streams#mediadevices +pub async fn get_display_media( + caps: DisplayMediaStreamConstraints, +) -> Result, Traced> { + unimplemented!() +} diff --git a/jason/src/platform/dart/media_track.rs b/jason/src/platform/dart/media_track.rs new file mode 100644 index 000000000..7c2d15087 --- /dev/null +++ b/jason/src/platform/dart/media_track.rs @@ -0,0 +1,150 @@ +//! Wrapper around [MediaStreamTrack][1]. +//! +//! [1]: https://w3.org/TR/mediacapture-streams#mediastreamtrack + +use derive_more::AsRef; + +use crate::media::{track::MediaStreamTrackState, FacingMode, MediaKind}; + +/// Wrapper around [MediaStreamTrack][1] received from a +/// [getUserMedia()][2]/[getDisplayMedia()][3] request. +/// +/// [1]: https://w3.org/TR/mediacapture-streams#mediastreamtrack +/// [2]: https://w3.org/TR/mediacapture-streams#dom-mediadevices-getusermedia +/// [3]: https://w3.org/TR/screen-capture/#dom-mediadevices-getdisplaymedia +#[derive(AsRef, Debug)] +pub struct MediaStreamTrack; + +impl MediaStreamTrack { + /// Returns [`id`] of the underlying [MediaStreamTrack][2]. + /// + /// [`id`]: https://w3.org/TR/mediacapture-streams#dom-mediastreamtrack-id + /// [2]: https://w3.org/TR/mediacapture-streams#mediastreamtrack + #[inline] + #[must_use] + pub fn id(&self) -> String { + unimplemented!() + } + + /// Returns this [`MediaStreamTrack`]'s kind (audio/video). + #[inline] + #[must_use] + pub fn kind(&self) -> MediaKind { + unimplemented!() + } + + /// Returns [MediaStreamTrackState][1] of the underlying + /// [MediaStreamTrack][2]. + /// + /// [1]: https://w3.org/TR/mediacapture-streams#dom-mediastreamtrackstate + /// [2]: https://w3.org/TR/mediacapture-streams#mediastreamtrack + #[must_use] + pub fn ready_state(&self) -> MediaStreamTrackState { + unimplemented!() + } + + /// Returns a [`deviceId`][1] of the underlying [MediaStreamTrack][2]. + /// + /// [1]: https://tinyurl.com/w3-streams#dom-mediatracksettings-deviceid + /// [2]: https://w3.org/TR/mediacapture-streams#mediastreamtrack + #[inline] + #[must_use] + pub fn device_id(&self) -> Option { + unimplemented!() + } + + /// Return a [`facingMode`][1] of the underlying [MediaStreamTrack][2]. + /// + /// [1]: https://tinyurl.com/w3-streams#dom-mediatracksettings-facingmode + /// [2]: https://w3.org/TR/mediacapture-streams#mediastreamtrack + #[must_use] + pub fn facing_mode(&self) -> Option { + unimplemented!() + } + + /// Returns a [`height`][1] of the underlying [MediaStreamTrack][2]. + /// + /// [1]: https://tinyurl.com/w3-streams#dom-mediatracksettings-height + /// [2]: https://w3.org/TR/mediacapture-streams#mediastreamtrack + #[inline] + #[must_use] + pub fn height(&self) -> Option { + unimplemented!() + } + + /// Return a [`width`][1] of the underlying [MediaStreamTrack][2]. + /// + /// [1]: https://w3.org/TR/mediacapture-streams#dom-mediatracksettings-width + /// [2]: https://w3.org/TR/mediacapture-streams#mediastreamtrack + #[inline] + #[must_use] + pub fn width(&self) -> Option { + unimplemented!() + } + + /// Changes an [`enabled`][1] attribute in the underlying + /// [MediaStreamTrack][2]. + /// + /// [1]: https://w3.org/TR/mediacapture-streams#dom-mediastreamtrack-enabled + /// [2]: https://w3.org/TR/mediacapture-streams#mediastreamtrack + #[inline] + pub fn set_enabled(&self, enabled: bool) { + unimplemented!() + } + + /// Changes a [`readyState`][1] attribute in the underlying + /// [MediaStreamTrack][2] to [`ended`][3]. + /// + /// [1]: https://tinyurl.com/w3-streams#dom-mediastreamtrack-readystate + /// [2]: https://w3.org/TR/mediacapture-streams#mediastreamtrack + /// [3]: https://tinyurl.com/w3-streams#idl-def-MediaStreamTrackState.ended + #[inline] + pub fn stop(&self) { + unimplemented!() + } + + /// Returns an [`enabled`][1] attribute of the underlying + /// [MediaStreamTrack][2]. + /// + /// [1]: https://w3.org/TR/mediacapture-streams#dom-mediastreamtrack-enabled + /// [2]: https://w3.org/TR/mediacapture-streams#mediastreamtrack + #[inline] + #[must_use] + pub fn enabled(&self) -> bool { + unimplemented!() + } + + /// Detects whether a video track captured from display searching + /// [specific fields][1] in its settings. + /// + /// Only works in Chrome browser at the moment. + /// + /// [1]: https://w3.org/TR/screen-capture/#extensions-to-mediatracksettings + #[must_use] + pub fn guess_is_from_display(&self) -> bool { + unimplemented!() + } + + /// Forks this [`MediaStreamTrack`]. + /// + /// Creates a new [`MediaStreamTrack`] from this [`MediaStreamTrack`] using + /// a [`clone()`][1] method. It won't clone current [`MediaStreamTrack`]'s + /// callbacks. + /// + /// [1]: https://w3.org/TR/mediacapture-streams#dom-mediastreamtrack-clone + #[must_use] + pub fn fork(&self) -> Self { + unimplemented!() + } + + /// Sets handler for the [`ended`][1] event. + /// + /// [1]: https://tinyurl.com/w3-streams#event-mediastreamtrack-ended + #[allow(clippy::unused_self, clippy::needless_pass_by_value)] + pub fn on_ended(&self, f: Option) + where + F: 'static + FnOnce(), + { + unimplemented!() + } +} diff --git a/jason/src/platform/dart/mod.rs b/jason/src/platform/dart/mod.rs new file mode 100644 index 000000000..317c51dac --- /dev/null +++ b/jason/src/platform/dart/mod.rs @@ -0,0 +1,67 @@ +//! Multiplatform Dart runtime specific functionality. + +// TODO: Remove allows when implementing platform code. +#![allow( + unused_variables, + clippy::missing_panics_doc, + clippy::unused_self, + clippy::needless_pass_by_value +)] + +pub mod constraints; +pub mod error; +pub mod ice_server; +pub mod input_device_info; +pub mod media_devices; +pub mod media_track; +pub mod peer_connection; +pub mod rtc_stats; +pub mod transceiver; +pub mod transport; +pub mod utils; + +use std::time::Duration; + +use futures::Future; + +pub use self::{ + constraints::{DisplayMediaStreamConstraints, MediaStreamConstraints}, + error::Error, + input_device_info::InputDeviceInfo, + media_devices::{enumerate_devices, get_display_media, get_user_media}, + media_track::MediaStreamTrack, + peer_connection::RtcPeerConnection, + rtc_stats::RtcStats, + transceiver::{Transceiver, TransceiverDirection}, + transport::WebSocketRpcTransport, + utils::{Callback, Function}, +}; + +/// TODO: Implement panic hook. +pub fn set_panic_hook() {} + +/// Initialize [`android_logger`] as default application logger with min log +/// level set to [`log::Level::Debug`]. +/// +/// [`android_logger`]: https://docs.rs/android_logger +pub fn init_logger() { + android_logger::init_once( + android_logger::Config::default().with_min_level(log::Level::Debug), + ); +} + +/// Runs a Rust [`Future`] on the current thread. +#[inline] +pub fn spawn(task: F) +where + F: Future + 'static, +{ + unimplemented!() +} + +/// [`Future`] which resolves after the provided [`Duration`]. +/// +/// [`Future`]: std::future::Future +pub async fn delay_for(delay: Duration) { + unimplemented!() +} diff --git a/jason/src/platform/dart/peer_connection.rs b/jason/src/platform/dart/peer_connection.rs new file mode 100644 index 000000000..f173e89ec --- /dev/null +++ b/jason/src/platform/dart/peer_connection.rs @@ -0,0 +1,256 @@ +//! Wrapper around [RTCPeerConnection][1]. +//! +//! [1]: https://w3.org/TR/webrtc/#dom-rtcpeerconnection + +use medea_client_api_proto::{ + IceConnectionState, IceServer, PeerConnectionState, +}; +use tracerr::Traced; + +use crate::{ + media::{MediaKind, TrackConstraints}, + platform::{ + IceCandidate, MediaStreamTrack, RtcPeerConnectionError, RtcStats, + SdpType, Transceiver, TransceiverDirection, + }, +}; + +impl From<&TrackConstraints> for MediaKind { + fn from(media_type: &TrackConstraints) -> Self { + match media_type { + TrackConstraints::Audio(_) => Self::Audio, + TrackConstraints::Video(_) => Self::Video, + } + } +} + +type Result = std::result::Result>; + +/// Representation of [RTCPeerConnection][1]. +/// +/// [1]: https://w3.org/TR/webrtc/#dom-rtcpeerconnection +pub struct RtcPeerConnection; + +impl RtcPeerConnection { + /// Instantiates new [`RtcPeerConnection`]. + /// + /// # Errors + /// + /// Errors with [`RtcPeerConnectionError::PeerCreationError`] if + /// [`RtcPeerConnection`] creation fails. + pub fn new(ice_servers: I, is_force_relayed: bool) -> Result + where + I: IntoIterator, + { + unimplemented!() + } + + /// Returns [`RtcStats`] of this [`RtcPeerConnection`]. + /// + /// # Errors + /// + /// Errors with [`RtcPeerConnectionError::RtcStatsError`] if getting or + /// parsing of [`RtcStats`] fails. + /// + /// Errors with [`RtcPeerConnectionError::GetStatsException`] when + /// [PeerConnection.getStats][1] promise throws exception. + /// + /// [1]: https://tinyurl.com/w6hmt5f + pub async fn get_stats(&self) -> Result { + unimplemented!() + } + + /// Sets handler for a [RTCTrackEvent][1] (see [`ontrack` callback][2]). + /// + /// [1]: https://w3.org/TR/webrtc/#rtctrackevent + /// [2]: https://w3.org/TR/webrtc/#dom-rtcpeerconnection-ontrack + pub fn on_track(&self, f: Option) + where + F: 'static + FnMut(MediaStreamTrack, Transceiver), + { + unimplemented!() + } + + /// Sets handler for a [RTCPeerConnectionIceEvent][1] (see + /// [`onicecandidate` callback][2]). + /// + /// [1]: https://w3.org/TR/webrtc/#dom-rtcpeerconnectioniceevent + /// [2]: https://w3.org/TR/webrtc/#dom-rtcpeerconnection-onicecandidate + pub fn on_ice_candidate(&self, f: Option) + where + F: 'static + FnMut(IceCandidate), + { + unimplemented!() + } + + /// Returns [`IceConnectionState`] of this [`RtcPeerConnection`]. + #[inline] + #[must_use] + pub fn ice_connection_state(&self) -> IceConnectionState { + unimplemented!() + } + + /// Returns [`PeerConnectionState`] of this [`RtcPeerConnection`]. + /// + /// Returns [`None`] if failed to parse a [`PeerConnectionState`]. + #[inline] + #[must_use] + pub fn connection_state(&self) -> Option { + unimplemented!() + } + + /// Sets handler for an [`iceconnectionstatechange`][1] event. + /// + /// [1]: https://w3.org/TR/webrtc/#event-iceconnectionstatechange + pub fn on_ice_connection_state_change(&self, f: Option) + where + F: 'static + FnMut(IceConnectionState), + { + unimplemented!() + } + + /// Sets handler for a [`connectionstatechange`][1] event. + /// + /// [1]: https://w3.org/TR/webrtc/#event-connectionstatechange + pub fn on_connection_state_change(&self, f: Option) + where + F: 'static + FnMut(PeerConnectionState), + { + unimplemented!() + } + + /// Adds remote [RTCPeerConnection][1]'s [ICE candidate][2] to this + /// [`RtcPeerConnection`]. + /// + /// # Errors + /// + /// With [`RtcPeerConnectionError::AddIceCandidateFailed`] if + /// [RtcPeerConnection.addIceCandidate()][3] fails. + /// + /// [1]: https://w3.org/TR/webrtc/#rtcpeerconnection-interface + /// [2]: https://tools.ietf.org/html/rfc5245#section-2 + /// [3]: https://w3.org/TR/webrtc/#dom-peerconnection-addicecandidate + pub async fn add_ice_candidate( + &self, + candidate: &str, + sdp_m_line_index: Option, + sdp_mid: &Option, + ) -> Result<()> { + unimplemented!() + } + + /// Marks [`RtcPeerConnection`] to trigger ICE restart. + /// + /// After this function returns, the offer returned by the next call to + /// [`RtcPeerConnection::create_offer`] is automatically configured + /// to trigger ICE restart. + #[inline] + pub fn restart_ice(&self) { + unimplemented!() + } + + /// Sets provided [SDP offer][`SdpType::Offer`] as local description. + /// + /// # Errors + /// + /// With [`RtcPeerConnectionError::SetLocalDescriptionFailed`] if + /// [RtcPeerConnection.setLocalDescription()][1] fails. + /// + /// [1]: https://w3.org/TR/webrtc/#dom-peerconnection-setlocaldescription + pub async fn set_offer(&self, offer: &str) -> Result<()> { + unimplemented!() + } + + /// Sets provided [SDP answer][`SdpType::Answer`] as local description. + /// + /// # Errors + /// + /// With [`RtcPeerConnectionError::SetLocalDescriptionFailed`] if + /// [RtcPeerConnection.setLocalDescription()][1] fails. + /// + /// [1]: https://w3.org/TR/webrtc/#dom-peerconnection-setlocaldescription + pub async fn set_answer(&self, answer: &str) -> Result<()> { + unimplemented!() + } + + /// Obtains [SDP answer][`SdpType::Answer`] from the [`RtcPeerConnection`]. + /// + /// Should be called whenever remote description has been changed. + /// + /// # Errors + /// + /// With [`RtcPeerConnectionError::CreateAnswerFailed`] if + /// [RtcPeerConnection.createAnswer()][1] fails. + /// + /// [1]: https://w3.org/TR/webrtc/#dom-rtcpeerconnection-createanswer + pub async fn create_answer(&self) -> Result { + unimplemented!() + } + + /// Rollbacks the [`RtcPeerConnection`] to the previous stable state. + /// + /// # Errors + /// + /// With [`RtcPeerConnectionError::SetLocalDescriptionFailed`] if + /// [RtcPeerConnection.setLocalDescription()][1] fails. + /// + /// [1]: https://w3.org/TR/webrtc/#dom-peerconnection-setlocaldescription + pub async fn rollback(&self) -> Result<()> { + unimplemented!() + } + + /// Obtains [SDP offer][`SdpType::Offer`] from the [`RtcPeerConnection`]. + /// + /// Should be called after local tracks changes, which require + /// (re)negotiation. + /// + /// # Errors + /// + /// With [`RtcPeerConnectionError::CreateOfferFailed`] if + /// [RtcPeerConnection.createOffer()][1] fails. + /// + /// [1]: https://w3.org/TR/webrtc/#dom-rtcpeerconnection-createoffer + pub async fn create_offer(&self) -> Result { + unimplemented!() + } + + /// Instructs the [`RtcPeerConnection`] to apply the supplied + /// [SDP][`SdpType`] as the remote [offer][`SdpType::Offer`] or + /// [answer][`SdpType::Answer`]. + /// + /// Changes the local media state. + /// + /// # Errors + /// + /// With [`RtcPeerConnectionError::SetRemoteDescriptionFailed`] if + /// [RTCPeerConnection.setRemoteDescription()][1] fails. + /// + /// [1]: https://w3.org/TR/webrtc/#dom-peerconnection-setremotedescription + pub async fn set_remote_description(&self, sdp: SdpType) -> Result<()> { + unimplemented!() + } + + /// Creates a new [`Transceiver`] (see [RTCRtpTransceiver][1]) and adds it + /// to the [set of this RTCPeerConnection's transceivers][2]. + /// + /// [1]: https://w3.org/TR/webrtc/#dom-rtcrtptransceiver + /// [2]: https://w3.org/TR/webrtc/#transceivers-set + #[must_use] + pub fn add_transceiver( + &self, + kind: MediaKind, + direction: TransceiverDirection, + ) -> Transceiver { + unimplemented!() + } + + /// Returns [`Transceiver`] (see [RTCRtpTransceiver][1]) from a + /// [set of this RTCPeerConnection's transceivers][2] by provided `mid`. + /// + /// [1]: https://w3.org/TR/webrtc/#dom-rtcrtptransceiver + /// [2]: https://w3.org/TR/webrtc/#transceivers-set + #[must_use] + pub fn get_transceiver_by_mid(&self, mid: &str) -> Option { + unimplemented!() + } +} diff --git a/jason/src/platform/dart/rtc_stats.rs b/jason/src/platform/dart/rtc_stats.rs new file mode 100644 index 000000000..da3459f4c --- /dev/null +++ b/jason/src/platform/dart/rtc_stats.rs @@ -0,0 +1,10 @@ +//! Deserialization of [`RtcStats`]. + +use medea_client_api_proto::stats::RtcStat; + +/// All available [`RtcStatsType`]s of a [`RtcPeerConnection`]. +/// +/// [`RtcStatsType`]: medea_client_api_proto::stats::RtcStatsType +/// [`RtcPeerConnection`]: crate::platform::RtcPeerConnection +#[derive(Clone, Debug)] +pub struct RtcStats(pub Vec); diff --git a/jason/src/platform/dart/transceiver.rs b/jason/src/platform/dart/transceiver.rs new file mode 100644 index 000000000..0fcd5073b --- /dev/null +++ b/jason/src/platform/dart/transceiver.rs @@ -0,0 +1,127 @@ +//! [RTCRtpTransceiver] wrapper. +//! +//! [RTCRtpTransceiver]: https://w3.org/TR/webrtc/#dom-rtcrtptransceiver + +use std::rc::Rc; + +use bitflags::bitflags; +use futures::future::LocalBoxFuture; + +use crate::{media::track::local, platform::Error}; + +/// Wrapper around [RTCRtpTransceiver] which provides handy methods for +/// direction changes. +/// +/// [RTCRtpTransceiver]: https://w3.org/TR/webrtc/#dom-rtcrtptransceiver +#[derive(Clone)] +pub struct Transceiver; + +impl Transceiver { + /// Disables provided [`TransceiverDirection`] of this [`Transceiver`]. + #[inline] + pub fn sub_direction(&self, disabled_direction: TransceiverDirection) { + unimplemented!() + } + + /// Enables provided [`TransceiverDirection`] of this [`Transceiver`]. + #[inline] + pub fn add_direction(&self, enabled_direction: TransceiverDirection) { + unimplemented!() + } + + /// Indicates whether the provided [`TransceiverDirection`] is enabled for + /// this [`Transceiver`]. + #[inline] + #[must_use] + pub fn has_direction(&self, direction: TransceiverDirection) -> bool { + unimplemented!() + } + + /// Replaces [`TransceiverDirection::SEND`] [`local::Track`] of this + /// [`Transceiver`]. + /// + /// # Errors + /// + /// Errors with [`Error`] if the underlying [`replaceTrack`][1] call fails. + /// + /// [1]: https://w3.org/TR/webrtc/#dom-rtcrtpsender-replacetrack + pub async fn set_send_track( + &self, + new_track: Rc, + ) -> Result<(), Error> { + unimplemented!() + } + + /// Sets a [`TransceiverDirection::SEND`] [`local::Track`] of this + /// [`Transceiver`] to [`None`]. + #[must_use] + pub fn drop_send_track(&self) -> LocalBoxFuture<'static, ()> { + unimplemented!() + } + + /// Returns [`mid`] of this [`Transceiver`]. + /// + /// [`mid`]: https://w3.org/TR/webrtc/#dom-rtptransceiver-mid + #[inline] + #[must_use] + pub fn mid(&self) -> Option { + unimplemented!() + } + + /// Returns [`local::Track`] that is being send to remote, if any. + #[inline] + #[must_use] + pub fn send_track(&self) -> Option> { + unimplemented!() + } + + /// Indicates whether this [`Transceiver`] has [`local::Track`]. + #[inline] + #[must_use] + pub fn has_send_track(&self) -> bool { + unimplemented!() + } + + /// Sets the underlying [`local::Track`]'s `enabled` field to the provided + /// value, if any. + #[inline] + pub fn set_send_track_enabled(&self, enabled: bool) { + unimplemented!() + } + + /// Indicates whether the underlying [RTCRtpTransceiver] is stopped. + /// + /// [RTCRtpTransceiver]: https://w3.org/TR/webrtc/#dom-rtcrtptransceiver + #[inline] + #[must_use] + pub fn is_stopped(&self) -> bool { + unimplemented!() + } +} + +// TODO: Probably should be shared between `wasm` and `dart` platforms. +bitflags! { + /// Representation of [RTCRtpTransceiverDirection][1]. + /// + /// [`sendrecv` direction][2] is represented by a + /// [`TransceiverDirection::all`] bitflag. + /// + /// [1]: https://w3.org/TR/webrtc/#dom-rtcrtptransceiverdirection + /// [2]: https://w3.org/TR/webrtc/#dom-rtcrtptransceiverdirection-sendrecv + pub struct TransceiverDirection: u8 { + /// [`inactive` direction][1] of transceiver. + /// + /// [1]: https://tinyurl.com/y2zslyw2 + const INACTIVE = 0b00; + + /// [`sendonly` direction][1] of transceiver. + /// + /// [1]: https://tinyurl.com/y6y2ye97 + const SEND = 0b01; + + /// [`recvonly` direction][1] of transceiver. + /// + /// [1]: https://tinyurl.com/y2nlxpzf + const RECV = 0b10; + } +} diff --git a/jason/src/platform/dart/transport.rs b/jason/src/platform/dart/transport.rs new file mode 100644 index 000000000..92c031092 --- /dev/null +++ b/jason/src/platform/dart/transport.rs @@ -0,0 +1,68 @@ +//! [WebSocket] transport wrapper. +//! +//! [WebSocket]: https://developer.mozilla.org/docs/WebSockets + +use futures::stream::LocalBoxStream; +use medea_client_api_proto::{ClientMsg, ServerMsg}; +use tracerr::Traced; + +use crate::{ + platform::transport::{RpcTransport, TransportError, TransportState}, + rpc::{websocket::ClientDisconnect, ApiUrl}, +}; + +type Result> = std::result::Result; + +/// [WebSocket] [`RpcTransport`] between a client and a server. +/// +/// # Drop +/// +/// This structure has __cyclic references__, which are freed in its [`Drop`] +/// implementation. +/// +/// If you're adding new cyclic dependencies, then don't forget to drop them in +/// the [`Drop`]. +/// +/// [WebSocket]: https://developer.mozilla.org/docs/WebSockets +pub struct WebSocketRpcTransport; + +impl WebSocketRpcTransport { + /// Initiates a new [WebSocket] connection. Resolves only once an underlying + /// connection becomes active. + /// + /// # Errors + /// + /// With [`TransportError::CreateSocket`] if cannot establish [WebSocket] to + /// specified URL. + /// + /// With [`TransportError::InitSocket`] if [WebSocket.onclose][1] callback + /// fired before [WebSocket.onopen][2] callback. + /// + /// [WebSocket]: https://developer.mozilla.org/docs/WebSockets + /// [1]: https://developer.mozilla.org/docs/Web/API/WebSocket/onclose + /// [2]: https://developer.mozilla.org/docs/Web/API/WebSocket/onopen + pub async fn new(url: ApiUrl) -> Result { + unimplemented!() + } +} + +impl RpcTransport for WebSocketRpcTransport { + #[inline] + fn on_message(&self) -> LocalBoxStream<'static, ServerMsg> { + unimplemented!() + } + + #[inline] + fn set_close_reason(&self, close_reason: ClientDisconnect) { + unimplemented!() + } + + fn send(&self, msg: &ClientMsg) -> Result<()> { + unimplemented!() + } + + #[inline] + fn on_state_change(&self) -> LocalBoxStream<'static, TransportState> { + unimplemented!() + } +} diff --git a/jason/jason-dummy/src/utils/closure.rs b/jason/src/platform/dart/utils/callback.rs similarity index 70% rename from jason/jason-dummy/src/utils/closure.rs rename to jason/src/platform/dart/utils/callback.rs index fbfa4a65e..512a72a76 100644 --- a/jason/jason-dummy/src/utils/closure.rs +++ b/jason/src/platform/dart/utils/callback.rs @@ -10,11 +10,11 @@ //! initialization phase: after Dart DL API is initialized and before any other //! exported Rust function is called. -use std::{ffi::c_void, marker::PhantomData}; +use std::{cell::RefCell, ffi::c_void, marker::PhantomData}; use dart_sys::{Dart_Handle, Dart_PersistentHandle}; -use crate::ForeignClass; +use crate::api::ForeignClass; use super::dart_api::{ Dart_DeletePersistentHandle_DL_Trampolined, @@ -76,47 +76,95 @@ pub unsafe extern "C" fn register_int_arg_fn_caller(f: IntArgFnCaller) { INT_ARG_FN_CALLER = Some(f); } +// TODO: Probably should be shared between `wasm` and `dart` platforms. +/// Wrapper for a single argument Dart callback function. +pub struct Callback(RefCell>>); + +impl Callback { + /// Sets the inner [`Function`]. + #[inline] + pub fn set_func(&self, f: Function) { + self.0.borrow_mut().replace(f); + } + + /// Indicates whether this [`Callback`]'s inner [`Function`] is set. + #[inline] + #[must_use] + pub fn is_set(&self) -> bool { + self.0.borrow().as_ref().is_some() + } +} + +impl Callback<()> { + /// Invokes the underlying [`Function`] (if any) passing no arguments to it. + #[inline] + pub fn call0(&self) { + if let Some(f) = self.0.borrow().as_ref() { + f.call0() + }; + } +} + +impl Default for Callback { + #[inline] + fn default() -> Self { + Self(RefCell::new(None)) + } +} + +impl Callback { + /// Invokes the underlying [`Function`] (if any) passing the single provided + /// argument to it. + #[inline] + pub fn call1(&self, arg: T) { + unimplemented!() + } +} + // TODO: Print exception if Dart closure throws. /// Dart closure that can be called from Rust. -pub struct DartClosure { +pub struct Function { /// [`Dart_PersistentHandle`] to the Dart closure that should be called. - cb: Dart_PersistentHandle, + dart_fn: Dart_PersistentHandle, /// Type of this closure argument. _arg: PhantomData, } -impl DartClosure { - /// Creates a new [`DartClosure`] from the provided [`Dart_Handle`] to a - /// Dart closure, and persists the provided [`Dart_Handle`] so it won't be - /// moved by the Dart VM GC. +impl Function { + /// Creates a new [`Function`] from the provided [`Dart_Handle`] to a Dart + /// closure, and persists the provided [`Dart_Handle`] so it won't be moved + /// by the Dart VM GC. #[inline] + #[must_use] pub fn new(cb: Dart_Handle) -> Self { Self { - cb: unsafe { Dart_NewPersistentHandle_DL_Trampolined(cb) }, + dart_fn: unsafe { Dart_NewPersistentHandle_DL_Trampolined(cb) }, _arg: PhantomData, } } } -impl DartClosure<()> { +impl Function<()> { /// Calls the underlying Dart closure. #[inline] pub fn call0(&self) { unsafe { - let fn_handle = Dart_HandleFromPersistent_DL_Trampolined(self.cb); + let fn_handle = + Dart_HandleFromPersistent_DL_Trampolined(self.dart_fn); NO_ARGS_FN_CALLER.unwrap()(fn_handle); } } } -impl DartClosure { +impl Function { /// Calls the underlying Dart closure with the provided [`ForeignClass`] /// argument. #[inline] pub fn call1(&self, arg: T) { unsafe { - let fn_handle = Dart_HandleFromPersistent_DL_Trampolined(self.cb); + let fn_handle = + Dart_HandleFromPersistent_DL_Trampolined(self.dart_fn); PTR_ARG_FN_CALLER.unwrap()( fn_handle, arg.into_ptr().cast::(), @@ -129,12 +177,12 @@ impl DartClosure { /// called for all integer types that fit into `2^63`. macro_rules! impl_dart_closure_for_int { ($arg:ty) => { - impl DartClosure<$arg> { + impl Function<$arg> { /// Calls the underlying Dart closure with the provided argument. pub fn call1(&self, arg: $arg) { unsafe { let fn_handle = - Dart_HandleFromPersistent_DL_Trampolined(self.cb); + Dart_HandleFromPersistent_DL_Trampolined(self.dart_fn); INT_ARG_FN_CALLER.unwrap()(fn_handle, arg as i64); } } @@ -152,9 +200,9 @@ impl_dart_closure_for_int!(u16); impl_dart_closure_for_int!(u32); impl_dart_closure_for_int!(bool); -impl Drop for DartClosure { +impl Drop for Function { /// Manually deallocates saved [`Dart_PersistentHandle`] so it won't leak. fn drop(&mut self) { - unsafe { Dart_DeletePersistentHandle_DL_Trampolined(self.cb) }; + unsafe { Dart_DeletePersistentHandle_DL_Trampolined(self.dart_fn) }; } } diff --git a/jason/jason-dummy/src/utils/dart_api.rs b/jason/src/platform/dart/utils/dart_api.rs similarity index 90% rename from jason/jason-dummy/src/utils/dart_api.rs rename to jason/src/platform/dart/utils/dart_api.rs index cf43fb141..4ddec3a1e 100644 --- a/jason/jason-dummy/src/utils/dart_api.rs +++ b/jason/src/platform/dart/utils/dart_api.rs @@ -2,6 +2,8 @@ //! //! [`Dart DL API`]: https://tinyurl.com/32e7fudh +use std::ffi::c_void; + use dart_sys::{Dart_Handle, Dart_PersistentHandle}; #[link(name = "trampoline")] @@ -11,7 +13,7 @@ extern "C" { /// code. Must be called before calling any other Dart API method. /// /// [1]: https://api.dart.dev/dart-ffi/NativeApi/initializeApiDLData.html - pub fn Dart_InitializeApiDL(obj: *mut libc::c_void) -> libc::intptr_t; + pub fn Dart_InitializeApiDL(obj: *mut c_void) -> libc::intptr_t; /// Allocates a [`Dart_PersistentHandle`] for provided [`Dart_Handle`]. /// @@ -44,8 +46,6 @@ extern "C" { /// /// [1]: https://api.dart.dev/dart-ffi/NativeApi/initializeApiDLData.html #[no_mangle] -pub unsafe extern "C" fn init_dart_api_dl( - obj: *mut libc::c_void, -) -> libc::intptr_t { +pub unsafe extern "C" fn init_dart_api_dl(obj: *mut c_void) -> libc::intptr_t { Dart_InitializeApiDL(obj) } diff --git a/jason/src/platform/dart/utils/event_listener.rs b/jason/src/platform/dart/utils/event_listener.rs new file mode 100644 index 000000000..e3ffe9652 --- /dev/null +++ b/jason/src/platform/dart/utils/event_listener.rs @@ -0,0 +1,52 @@ +use std::{marker::PhantomData, rc::Rc}; + +use derive_more::{Display, From}; +use tracerr::Traced; + +use crate::{platform, utils::JsCaused}; + +/// Failure to bind a listener to some event. +#[derive(Clone, Debug, Display, From, JsCaused, PartialEq)] +#[js(error = "platform::Error")] +pub struct EventListenerBindError(platform::Error); + +/// Wrapper for the closure that handles some event. +#[derive(Debug)] +pub struct EventListener { + t: PhantomData, + a: PhantomData, +} + +impl EventListener { + /// Creates a new [`EventListener`] from the given [`FnMut`] `closure`. + /// + /// # Errors + /// + /// Errors if [`EventListener`] bound fails. + pub fn new_mut( + target: Rc, + event_name: &'static str, + closure: F, + ) -> Result> + where + F: FnMut(A) + 'static, + { + unimplemented!() + } + + /// Creates a new [`EventListener`] from the given [`FnOnce`] `closure`. + /// + /// # Errors + /// + /// Errors if [`EventListener`] bound fails. + pub fn new_once( + target: Rc, + event_name: &'static str, + closure: F, + ) -> Result> + where + F: FnOnce(A) + 'static, + { + unimplemented!() + } +} diff --git a/jason/src/platform/dart/utils/mod.rs b/jason/src/platform/dart/utils/mod.rs new file mode 100644 index 000000000..9598e0293 --- /dev/null +++ b/jason/src/platform/dart/utils/mod.rs @@ -0,0 +1,11 @@ +//! Multiplatform Dart runtime specific utility structs and functions. + +mod callback; +pub mod dart_api; +mod event_listener; + +#[doc(inline)] +pub use self::{ + callback::{Callback, Function}, + event_listener::{EventListener, EventListenerBindError}, +}; diff --git a/jason/src/platform/mod.rs b/jason/src/platform/mod.rs index 3bddbe95e..b0eb61c54 100644 --- a/jason/src/platform/mod.rs +++ b/jason/src/platform/mod.rs @@ -3,28 +3,21 @@ pub mod peer_connection; pub mod rtc_stats; pub mod transport; -pub mod wasm; + +cfg_if::cfg_if! { + if #[cfg(target_os = "android")] { + mod dart; + pub use self::dart::*; + } else { + mod wasm; + pub use self::wasm::*; + } +} pub use self::{ peer_connection::{IceCandidate, RtcPeerConnectionError, SdpType}, rtc_stats::RtcStatsError, - transport::{ - RpcTransport, TransportError, TransportState, WebSocketRpcTransport, - }, - wasm::{ - constraints::{DisplayMediaStreamConstraints, MediaStreamConstraints}, - delay_for, - error::Error, - get_property_by_name, init_logger, - input_device_info::InputDeviceInfo, - media_devices::{enumerate_devices, get_display_media, get_user_media}, - media_track::MediaStreamTrack, - peer_connection::RtcPeerConnection, - rtc_stats::RtcStats, - set_panic_hook, spawn, - transceiver::{Transceiver, TransceiverDirection}, - utils::{Callback, Function}, - }, + transport::{RpcTransport, TransportError, TransportState}, }; #[cfg(feature = "mockable")] diff --git a/jason/src/platform/transport.rs b/jason/src/platform/transport.rs index e08229921..e8dd9cee7 100644 --- a/jason/src/platform/transport.rs +++ b/jason/src/platform/transport.rs @@ -11,8 +11,6 @@ use crate::{ utils::{JsCaused, JsonParseError}, }; -pub use super::wasm::transport::WebSocketRpcTransport; - /// Possible states of a [`RpcTransport`]. #[derive(Clone, Copy, Debug, PartialEq)] pub enum TransportState { diff --git a/jason/src/platform/wasm/media_track.rs b/jason/src/platform/wasm/media_track.rs index 3aaad04d4..07b086761 100644 --- a/jason/src/platform/wasm/media_track.rs +++ b/jason/src/platform/wasm/media_track.rs @@ -8,7 +8,7 @@ use derive_more::AsRef; use crate::{ media::{track::MediaStreamTrackState, FacingMode, MediaKind}, - platform::{get_property_by_name, wasm::utils::EventListener}, + platform::wasm::{get_property_by_name, utils::EventListener}, }; /// Wrapper around [MediaStreamTrack][1] received from a diff --git a/jason/src/platform/wasm/mod.rs b/jason/src/platform/wasm/mod.rs index a6b3f2c39..7b30a6816 100644 --- a/jason/src/platform/wasm/mod.rs +++ b/jason/src/platform/wasm/mod.rs @@ -1,7 +1,5 @@ //! `wasm32`-platform-specific functionality. -use std::{convert::TryInto as _, time::Duration}; - pub mod constraints; pub mod error; pub mod ice_server; @@ -14,24 +12,39 @@ pub mod transceiver; pub mod transport; pub mod utils; +use std::{convert::TryInto as _, time::Duration}; + use futures::Future; use js_sys::{Promise, Reflect}; use wasm_bindgen::JsValue; use wasm_bindgen_futures::JsFuture; use web_sys::Window; +pub use self::{ + constraints::{DisplayMediaStreamConstraints, MediaStreamConstraints}, + error::Error, + input_device_info::InputDeviceInfo, + media_devices::{enumerate_devices, get_display_media, get_user_media}, + media_track::MediaStreamTrack, + peer_connection::RtcPeerConnection, + rtc_stats::RtcStats, + transceiver::{Transceiver, TransceiverDirection}, + transport::WebSocketRpcTransport, + utils::{Callback, Function}, +}; + // When the `wee_alloc` feature is enabled, use `wee_alloc` as the global // allocator. #[cfg(feature = "wee_alloc")] #[global_allocator] static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; -// When the `console_error_panic_hook` feature is enabled, we can call the -// `set_panic_hook` function at least once during initialization, and then -// we will get better error messages if our code ever panics. -// -// For more details see: -// https://github.com/rustwasm/console_error_panic_hook#readme +/// When the `console_error_panic_hook` feature is enabled, we can call the +/// `set_panic_hook` function at least once during initialization, and then +/// we will get better error messages if our code ever panics. +/// +/// For more details see: +/// #[cfg(feature = "console_error_panic_hook")] pub use console_error_panic_hook::set_once as set_panic_hook; diff --git a/jason/src/platform/wasm/peer_connection.rs b/jason/src/platform/wasm/peer_connection.rs index 63b241edf..7a88d0e06 100644 --- a/jason/src/platform/wasm/peer_connection.rs +++ b/jason/src/platform/wasm/peer_connection.rs @@ -24,9 +24,10 @@ use web_sys::{ use crate::{ media::{MediaKind, TrackConstraints}, platform::{ - self, get_property_by_name, wasm::utils::EventListener, IceCandidate, - MediaStreamTrack, RtcPeerConnectionError, RtcStats, SdpType, - Transceiver, TransceiverDirection, + self, + wasm::{get_property_by_name, utils::EventListener}, + IceCandidate, MediaStreamTrack, RtcPeerConnectionError, RtcStats, + SdpType, Transceiver, TransceiverDirection, }, }; @@ -577,11 +578,13 @@ impl RtcPeerConnection { &self, kind: MediaKind, direction: TransceiverDirection, - ) -> RtcRtpTransceiver { + ) -> Transceiver { let mut init = RtcRtpTransceiverInit::new(); init.direction(direction.into()); - self.peer - .add_transceiver_with_str_and_init(kind.as_str(), &init) + let transceiver = self + .peer + .add_transceiver_with_str_and_init(kind.as_str(), &init); + Transceiver::from(transceiver) } /// Returns [`RtcRtpTransceiver`] (see [RTCRtpTransceiver][1]) from a @@ -594,10 +597,7 @@ impl RtcPeerConnection { /// /// [1]: https://w3.org/TR/webrtc/#dom-rtcrtptransceiver /// [2]: https://w3.org/TR/webrtc/#transceivers-set - pub fn get_transceiver_by_mid( - &self, - mid: &str, - ) -> Option { + pub fn get_transceiver_by_mid(&self, mid: &str) -> Option { let mut transceiver = None; let transceivers = js_sys::try_iter(&self.peer.get_transceivers()) @@ -613,7 +613,7 @@ impl RtcPeerConnection { } } - transceiver + transceiver.map(Transceiver::from) } } diff --git a/jason/src/platform/wasm/rtc_stats.rs b/jason/src/platform/wasm/rtc_stats.rs index 2151c92f0..088740fbb 100644 --- a/jason/src/platform/wasm/rtc_stats.rs +++ b/jason/src/platform/wasm/rtc_stats.rs @@ -11,7 +11,7 @@ use medea_client_api_proto::stats::{RtcStat, RtcStatsType}; use tracerr::Traced; use wasm_bindgen::{prelude::*, JsCast}; -use crate::platform::{self, get_property_by_name, RtcStatsError}; +use crate::platform::{self, wasm::get_property_by_name, RtcStatsError}; /// All available [`RtcStatsType`]s of a [`platform::RtcPeerConnection`]. #[derive(Clone, Debug)] diff --git a/jason/src/platform/wasm/transceiver.rs b/jason/src/platform/wasm/transceiver.rs index b553e90e2..7f99e5956 100644 --- a/jason/src/platform/wasm/transceiver.rs +++ b/jason/src/platform/wasm/transceiver.rs @@ -5,11 +5,10 @@ use std::{cell::RefCell, rc::Rc}; use bitflags::bitflags; use futures::future::LocalBoxFuture; use medea_client_api_proto::Direction as DirectionProto; -use wasm_bindgen::JsValue; use wasm_bindgen_futures::JsFuture; use web_sys::{RtcRtpTransceiver, RtcRtpTransceiverDirection}; -use crate::media::track::local; +use crate::{media::track::local, platform::Error}; /// Wrapper around [`RtcRtpTransceiver`] which provides handy methods for /// direction changes. @@ -62,7 +61,7 @@ impl Transceiver { pub async fn set_send_track( &self, new_track: Rc, - ) -> Result<(), JsValue> { + ) -> Result<(), Error> { let sys_track: &web_sys::MediaStreamTrack = (*new_track).as_ref().as_ref(); JsFuture::from( diff --git a/jason/src/room.rs b/jason/src/room.rs index abf80115a..5894567e4 100644 --- a/jason/src/room.rs +++ b/jason/src/room.rs @@ -355,7 +355,7 @@ impl RoomHandle { /// /// This might happen in such cases: /// 1. Media server initiates a media request. - /// 2. `disable_audio`/`enable_video` is called. + /// 2. `enable_audio`/`enable_video` is called. /// 3. [`MediaStreamSettings`] updated via `set_local_media_settings`. /// /// # Errors