From fbaf22ba7e870ccde9509cd2a6a6287a87c6ca9f Mon Sep 17 00:00:00 2001 From: Jasper Hugo Date: Fri, 13 Aug 2021 18:48:59 +0700 Subject: [PATCH] Initial --- .gitignore | 1 + Cargo.lock | 1790 +++++++++++++++++++++++ Cargo.toml | 5 + LICENSE-APACHE | 201 +++ LICENSE-MIT | 23 + README.md | 118 ++ deny.toml | 44 + gst-meet/Cargo.toml | 28 + gst-meet/LICENSE-APACHE | 201 +++ gst-meet/LICENSE-MIT | 23 + gst-meet/src/main.rs | 224 +++ lib-gst-meet/Cargo.toml | 34 + lib-gst-meet/LICENSE-APACHE | 201 +++ lib-gst-meet/LICENSE-MIT | 23 + lib-gst-meet/src/conference.rs | 579 ++++++++ lib-gst-meet/src/connection.rs | 315 ++++ lib-gst-meet/src/jingle.rs | 827 +++++++++++ lib-gst-meet/src/lib.rs | 14 + lib-gst-meet/src/pinger.rs | 34 + lib-gst-meet/src/source.rs | 21 + lib-gst-meet/src/stanza_filter.rs | 9 + lib-gst-meet/src/util.rs | 20 + lib-gst-meet/src/xmpp/extdisco.rs | 73 + lib-gst-meet/src/xmpp/jitsi.rs | 39 + lib-gst-meet/src/xmpp/mod.rs | 3 + lib-gst-meet/src/xmpp/ns.rs | 4 + nice-gst-meet-sys/Cargo.toml | 73 + nice-gst-meet-sys/Gir.toml | 8 + nice-gst-meet-sys/LICENSE-APACHE | 201 +++ nice-gst-meet-sys/LICENSE-MIT | 23 + nice-gst-meet-sys/build.rs | 17 + nice-gst-meet-sys/src/lib.rs | 683 +++++++++ nice-gst-meet-sys/tests/abi.rs | 418 ++++++ nice-gst-meet-sys/tests/constant.c | 96 ++ nice-gst-meet-sys/tests/layout.c | 30 + nice-gst-meet-sys/tests/manual.h | 2 + nice-gst-meet/Cargo.toml | 26 + nice-gst-meet/Gir.toml | 20 + nice-gst-meet/LICENSE-APACHE | 201 +++ nice-gst-meet/LICENSE-MIT | 23 + nice-gst-meet/src/agent.rs | 2130 ++++++++++++++++++++++++++++ nice-gst-meet/src/candidate.rs | 206 +++ nice-gst-meet/src/enums.rs | 328 +++++ nice-gst-meet/src/flags.rs | 65 + nice-gst-meet/src/lib.rs | 33 + rustfmt.toml | 13 + 46 files changed, 9450 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 LICENSE-APACHE create mode 100644 LICENSE-MIT create mode 100644 README.md create mode 100644 deny.toml create mode 100644 gst-meet/Cargo.toml create mode 100644 gst-meet/LICENSE-APACHE create mode 100644 gst-meet/LICENSE-MIT create mode 100644 gst-meet/src/main.rs create mode 100644 lib-gst-meet/Cargo.toml create mode 100644 lib-gst-meet/LICENSE-APACHE create mode 100644 lib-gst-meet/LICENSE-MIT create mode 100644 lib-gst-meet/src/conference.rs create mode 100644 lib-gst-meet/src/connection.rs create mode 100644 lib-gst-meet/src/jingle.rs create mode 100644 lib-gst-meet/src/lib.rs create mode 100644 lib-gst-meet/src/pinger.rs create mode 100644 lib-gst-meet/src/source.rs create mode 100644 lib-gst-meet/src/stanza_filter.rs create mode 100644 lib-gst-meet/src/util.rs create mode 100644 lib-gst-meet/src/xmpp/extdisco.rs create mode 100644 lib-gst-meet/src/xmpp/jitsi.rs create mode 100644 lib-gst-meet/src/xmpp/mod.rs create mode 100644 lib-gst-meet/src/xmpp/ns.rs create mode 100644 nice-gst-meet-sys/Cargo.toml create mode 100644 nice-gst-meet-sys/Gir.toml create mode 100644 nice-gst-meet-sys/LICENSE-APACHE create mode 100644 nice-gst-meet-sys/LICENSE-MIT create mode 100644 nice-gst-meet-sys/build.rs create mode 100644 nice-gst-meet-sys/src/lib.rs create mode 100644 nice-gst-meet-sys/tests/abi.rs create mode 100644 nice-gst-meet-sys/tests/constant.c create mode 100644 nice-gst-meet-sys/tests/layout.c create mode 100644 nice-gst-meet-sys/tests/manual.h create mode 100644 nice-gst-meet/Cargo.toml create mode 100644 nice-gst-meet/Gir.toml create mode 100644 nice-gst-meet/LICENSE-APACHE create mode 100644 nice-gst-meet/LICENSE-MIT create mode 100644 nice-gst-meet/src/agent.rs create mode 100644 nice-gst-meet/src/candidate.rs create mode 100644 nice-gst-meet/src/enums.rs create mode 100644 nice-gst-meet/src/flags.rs create mode 100644 nice-gst-meet/src/lib.rs create mode 100644 rustfmt.toml diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..1415702 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,1790 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "anyhow" +version = "1.0.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "595d3cfa7a60d4555cb5067b99f07142a08ea778de5cf993f7b75c7d8fabc486" + +[[package]] +name = "async-stream" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "171374e7e3b2504e0e5236e3b59260560f9fe94bfe9ac39ba5e4e929c5590625" +dependencies = [ + "async-stream-impl", + "futures-core", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "648ed8c8d2ce5409ccd57453d9d1b214b342a0d69376a6feda1fd6cae3299308" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "async-trait" +version = "0.1.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44318e776df68115a881de9a8fd1b9e53368d7a4a5ce4cc48517da3393233a5e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + +[[package]] +name = "base64" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "blake2" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10a5720225ef5daecf08657f23791354e1685a8c91a4c60c7f3d3b2892f978f4" +dependencies = [ + "crypto-mac", + "digest", + "opaque-debug", +] + +[[package]] +name = "block" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "block-padding", + "generic-array", +] + +[[package]] +name = "block-padding" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" + +[[package]] +name = "bumpalo" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c59e7af012c713f529e7a3ee57ce9b31ddd858d4b512923602f74608b009631" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bytes" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040" + +[[package]] +name = "cc" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e70cc2f62c6ce1868963827bd677764c62d07c3d9a3e1fb1177ee1a9ab199eb2" + +[[package]] +name = "cfg-expr" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b412e83326147c2bb881f8b40edfbf9905b9b8abaebd0e47ca190ba62fda8f0e" +dependencies = [ + "smallvec", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +dependencies = [ + "libc", + "num-integer", + "num-traits", + "winapi", +] + +[[package]] +name = "clap" +version = "2.33.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" +dependencies = [ + "bitflags", + "textwrap", + "unicode-width", +] + +[[package]] +name = "cocoa" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f63902e9223530efb4e26ccd0cf55ec30d592d3b42e21a28defc42a9586e832" +dependencies = [ + "bitflags", + "block", + "cocoa-foundation", + "core-foundation", + "core-graphics", + "foreign-types", + "libc", + "objc", +] + +[[package]] +name = "cocoa-foundation" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ade49b65d560ca58c403a479bb396592b155c0185eada742ee323d1d68d6318" +dependencies = [ + "bitflags", + "block", + "core-foundation", + "core-graphics-types", + "foreign-types", + "libc", + "objc", +] + +[[package]] +name = "core-foundation" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a89e2ae426ea83155dccf10c0fa6b1463ef6d5fcb44cee0b224a408fa640a62" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea221b5284a47e40033bf9b66f35f984ec0ea2931eb03505246cd27a963f981b" + +[[package]] +name = "core-graphics" +version = "0.22.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "269f35f69b542b80e736a20a89a05215c0ce80c2c03c514abb2e318b78379d86" +dependencies = [ + "bitflags", + "core-foundation", + "core-graphics-types", + "foreign-types", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a68b68b3446082644c91ac778bf50cd4104bfb002b5a6a7c44cca5a2c70788b" +dependencies = [ + "bitflags", + "core-foundation", + "foreign-types", + "libc", +] + +[[package]] +name = "cpufeatures" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66c99696f6c9dd7f35d486b9d04d7e6e202aa3e8c40d553f2fdf5e7e0c6a71ef" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-mac" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" +dependencies = [ + "generic-array", + "subtle", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "either" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" +dependencies = [ + "matches", + "percent-encoding", +] + +[[package]] +name = "futures" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1adc00f486adfc9ce99f77d717836f0c5aa84965eb0b4f051f4e83f7cab53f8b" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74ed2411805f6e4e3d9bc904c95d5d423b89b3b25dc0250aa74729de20629ff9" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af51b1b4a7fdff033703db39de8802c673eb91855f2e0d47dcf3bf2c0ef01f99" + +[[package]] +name = "futures-executor" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d0d535a57b87e1ae31437b892713aee90cd2d7b0ee48727cd11fc72ef54761c" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b0e06c393068f3a6ef246c75cdca793d6a46347e75286933e5e75fd2fd11582" + +[[package]] +name = "futures-sink" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0f30aaa67363d119812743aa5f33c201a7a66329f97d1a887022971feea4b53" + +[[package]] +name = "futures-task" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbe54a98670017f3be909561f6ad13e810d9a51f3f061b902062ca3da80799f2" + +[[package]] +name = "futures-util" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67eb846bfd58e44a8481a00049e82c43e0ccb5d61f8dc071057cb19249dd4d78" +dependencies = [ + "autocfg", + "futures-core", + "futures-sink", + "futures-task", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "gio" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86c6823b39d46d22cac2466de261f28d7f049ebc18f7b35296a42c7ed8a88325" +dependencies = [ + "bitflags", + "futures-channel", + "futures-core", + "futures-io", + "gio-sys", + "glib", + "libc", + "once_cell", + "thiserror", +] + +[[package]] +name = "gio-sys" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0a41df66e57fcc287c4bcf74fc26b884f31901ea9792ec75607289b456f48fa" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", + "winapi", +] + +[[package]] +name = "glib" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbecad7a3a898ee749d491ce2ae0decb0bce9e736f9747bc49159b1cea5d37f4" +dependencies = [ + "bitflags", + "futures-channel", + "futures-core", + "futures-executor", + "futures-task", + "glib-macros", + "glib-sys", + "gobject-sys", + "libc", + "log", + "once_cell", + "smallvec", +] + +[[package]] +name = "glib-macros" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2aad66361f66796bfc73f530c51ef123970eb895ffba991a234fcf7bea89e518" +dependencies = [ + "anyhow", + "heck", + "proc-macro-crate", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "glib-sys" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c1d60554a212445e2a858e42a0e48cece1bd57b311a19a9468f70376cf554ae" +dependencies = [ + "libc", + "system-deps", +] + +[[package]] +name = "gobject-sys" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa92cae29759dae34ab5921d73fff5ad54b3d794ab842c117e36cafc7994c3f5" +dependencies = [ + "glib-sys", + "libc", + "system-deps", +] + +[[package]] +name = "gst-meet" +version = "0.1.0" +dependencies = [ + "anyhow", + "cocoa", + "futures", + "glib", + "gstreamer", + "lib-gst-meet", + "structopt", + "tokio", + "tokio-stream", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "gstreamer" +version = "0.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4aa71d1b3f562645e2d3b96c8d5cf1f26174696e37781ef34193311da733f98c" +dependencies = [ + "bitflags", + "cfg-if", + "futures-channel", + "futures-core", + "futures-util", + "glib", + "gstreamer-sys", + "libc", + "muldiv", + "num-integer", + "num-rational", + "once_cell", + "paste", + "pretty-hex", + "thiserror", +] + +[[package]] +name = "gstreamer-sys" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8188ba998999a4a16005c3984812807ff882a87f5f3457c3d5bbbfcbdf631ebd" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "heck" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "http" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "527e8c9ac747e28542699a951517aa9a6945af506cd1f2e1b53a576c17b6cc11" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "httparse" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3a87b616e37e93c22fb19bcd386f02f3af5ea98a25670ad0fce773de23c5e68" + +[[package]] +name = "idna" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "input_buffer" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f97967975f448f1a7ddb12b0bc41069d09ed6a1c161a92687e057325db35d413" +dependencies = [ + "bytes", +] + +[[package]] +name = "instant" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bee0328b1209d157ef001c94dd85b4f8f64139adb0eac2659f4b08382b2f474d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "itertools" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69ddb889f9d0d08a67338271fa9b62996bc788c7796a5c18cf057420aaed5eaf" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" + +[[package]] +name = "jid" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aaded88f156caeca06f5849b6dc97336815c2be69691fad705dd42ced1a06de8" +dependencies = [ + "minidom", +] + +[[package]] +name = "js-sys" +version = "0.3.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce791b7ca6638aae45be056e068fc756d871eb3b3b10b8efa62d1c9cec616752" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "keccak" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67c21572b4949434e4fc1e1978b99c5f77064153c59d998bf13ecd96fb5ecba7" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "lib-gst-meet" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-stream", + "async-trait", + "bytes", + "futures", + "glib", + "gstreamer", + "hex", + "itertools", + "libc", + "maplit", + "nice-gst-meet", + "once_cell", + "pem", + "pkg-config", + "rand", + "rcgen", + "ring", + "tokio", + "tokio-stream", + "tokio-tungstenite", + "tracing", + "uuid", + "xmpp-parsers", +] + +[[package]] +name = "libc" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7f823d141fe0a24df1e23b4af4e3c7ba9e5966ec514ea068c93024aa7deb765" + +[[package]] +name = "lock_api" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0382880606dff6d15c9476c416d18690b72742aa7b605bb6dd6ec9030fbf07eb" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "malloc_buf" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +dependencies = [ + "libc", +] + +[[package]] +name = "maplit" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" + +[[package]] +name = "matchers" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f099785f7595cc4b4553a174ce30dd7589ef93391ff414dbb67f62392b9e0ce1" +dependencies = [ + "regex-automata", +] + +[[package]] +name = "matches" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" + +[[package]] +name = "memchr" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc" + +[[package]] +name = "memoffset" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9" +dependencies = [ + "autocfg", +] + +[[package]] +name = "minidom" +version = "0.13.0" +dependencies = [ + "quick-xml", +] + +[[package]] +name = "mio" +version = "0.7.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c2bdb6314ec10835cd3293dd268473a835c02b7b352e788be788b3c6ca6bb16" +dependencies = [ + "libc", + "log", + "miow", + "ntapi", + "winapi", +] + +[[package]] +name = "miow" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" +dependencies = [ + "winapi", +] + +[[package]] +name = "muldiv" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5136edda114182728ccdedb9f5eda882781f35fa6e80cc360af12a8932507f3" + +[[package]] +name = "nice-gst-meet" +version = "0.1.0" +dependencies = [ + "bitflags", + "glib", + "libc", + "nice-gst-meet-sys", + "nix", +] + +[[package]] +name = "nice-gst-meet-sys" +version = "0.0.1" +dependencies = [ + "gio", + "glib", + "libc", + "shell-words", + "system-deps", + "tempfile", +] + +[[package]] +name = "nix" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1e25ee6b412c2a1e3fcb6a4499a5c1bfe7f43e014bdce9a6b6666e5aa2d187" +dependencies = [ + "bitflags", + "cc", + "cfg-if", + "libc", + "memoffset", +] + +[[package]] +name = "ntapi" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44" +dependencies = [ + "winapi", +] + +[[package]] +name = "num-integer" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d41702bd167c2df5520b384281bc111a4b5efcf7fbc4c9c222c815b07e0a6a6a" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "objc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +dependencies = [ + "malloc_buf", +] + +[[package]] +name = "once_cell" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "parking_lot" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb" +dependencies = [ + "instant", + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7a782938e745763fe6907fc6ba86946d72f49fe7e21de074e08128a99fb018" +dependencies = [ + "cfg-if", + "instant", + "libc", + "redox_syscall", + "smallvec", + "winapi", +] + +[[package]] +name = "paste" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf547ad0c65e31259204bd90935776d1c693cec2f4ff7abb7a1bbbd40dfe58" + +[[package]] +name = "pem" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd56cbd21fea48d0c440b41cd69c589faacade08c992d9a54e471b79d0fd13eb" +dependencies = [ + "base64", + "once_cell", + "regex", +] + +[[package]] +name = "percent-encoding" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" + +[[package]] +name = "pin-project" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "576bc800220cc65dac09e99e97b08b358cfab6e17078de8dc5fee223bd2d0c08" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e8fe8163d14ce7f0cdac2e040116f22eac817edabff0be91e8aff7e9accf389" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d31d11c69a6b52a174b42bdc0c30e5e11670f90788b2c471c31c1d17d449443" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" + +[[package]] +name = "ppv-lite86" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" + +[[package]] +name = "pretty-hex" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc5c99d529f0d30937f6f4b8a86d988047327bb88d04d2c4afc356de74722131" + +[[package]] +name = "proc-macro-crate" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41fdbd1df62156fbc5945f4762632564d7d038153091c3fcf1067f6aef7cff92" +dependencies = [ + "thiserror", + "toml", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c7ed8b8c7b886ea3ed7dde405212185f423ab44682667c8c6dd14aa1d9f6612" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quick-xml" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26aab6b48e2590e4a64d1ed808749ba06257882b461d01ca71baeb747074a6dd" +dependencies = [ + "memchr", +] + +[[package]] +name = "quote" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", + "rand_hc", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_hc" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" +dependencies = [ + "rand_core", +] + +[[package]] +name = "rcgen" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48b4fc1b81d685fcd442a86da2e2c829d9e353142633a8159f42bf28e7e94428" +dependencies = [ + "chrono", + "ring", + "yasna", +] + +[[package]] +name = "redox_syscall" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" +dependencies = [ + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" + +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi", +] + +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin", + "untrusted", + "web-sys", + "winapi", +] + +[[package]] +name = "rustls" +version = "0.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35edb675feee39aec9c99fa5ff985081995a06d594114ae14cbe797ad7b7a6d7" +dependencies = [ + "base64", + "log", + "ring", + "sct", + "webpki", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "sct" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b362b83898e0e69f38515b82ee15aa80636befe47c3b6d3d89a911e78fc228ce" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "serde" +version = "1.0.127" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f03b9878abf6d14e6779d3f24f07b2cfa90352cfec4acc5aab8f1ac7f146fae8" + +[[package]] +name = "sha-1" +version = "0.9.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a0c8611594e2ab4ebbf06ec7cbbf0a99450b8570e96cbf5188b5d5f6ef18d81" +dependencies = [ + "block-buffer", + "cfg-if", + "cpufeatures", + "digest", + "opaque-debug", +] + +[[package]] +name = "sha2" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b362ae5752fd2137731f9fa25fd4d9058af34666ca1966fb969119cc35719f12" +dependencies = [ + "block-buffer", + "cfg-if", + "cpufeatures", + "digest", + "opaque-debug", +] + +[[package]] +name = "sha3" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f81199417d4e5de3f04b1e871023acea7389672c4135918f05aa9cbf2f2fa809" +dependencies = [ + "block-buffer", + "digest", + "keccak", + "opaque-debug", +] + +[[package]] +name = "sharded-slab" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "740223c51853f3145fe7c90360d2d4232f2b62e3449489c207eccde818979982" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shell-words" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fa3938c99da4914afedd13bf3d79bcb6c277d1b2c398d23257a304d9e1b074" + +[[package]] +name = "signal-hook-registry" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +dependencies = [ + "libc", +] + +[[package]] +name = "slab" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c307a32c1c5c437f38c7fd45d753050587732ba8628319fbdf12a7e289ccc590" + +[[package]] +name = "smallvec" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "structopt" +version = "0.3.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69b041cdcb67226aca307e6e7be44c8806423d83e018bd662360a93dabce4d71" +dependencies = [ + "clap", + "lazy_static", + "structopt-derive", +] + +[[package]] +name = "structopt-derive" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7813934aecf5f51a54775e00068c237de98489463968231a51746bbbc03f9c10" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "strum" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aaf86bbcfd1fa9670b7a129f64fc0c9fcbbfe4f1bc4210e9e98fe71ffc12cde2" + +[[package]] +name = "strum_macros" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d06aaeeee809dbc59eb4556183dd927df67db1540de5be8d3ec0b6636358a5ec" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "subtle" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" + +[[package]] +name = "syn" +version = "1.0.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1873d832550d4588c3dbc20f01361ab00bfe741048f71e3fecf145a7cc18b29c" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "system-deps" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "480c269f870722b3b08d2f13053ce0c2ab722839f472863c3e2d61ff3a1c2fa6" +dependencies = [ + "anyhow", + "cfg-expr", + "heck", + "itertools", + "pkg-config", + "strum", + "strum_macros", + "thiserror", + "toml", + "version-compare", +] + +[[package]] +name = "tempfile" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22" +dependencies = [ + "cfg-if", + "libc", + "rand", + "redox_syscall", + "remove_dir_all", + "winapi", +] + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "thiserror" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93119e4feac1cbe6c798c34d3a53ea0026b0b1de6a120deef895137c0529bfe2" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "060d69a0afe7796bf42e9e2ff91f5ee691fb15c53d38b4b62a9a53eb23164745" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thread_local" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8018d24e04c95ac8790716a5987d0fec4f8b27249ffa0f7d33f1369bdfb88cbd" +dependencies = [ + "once_cell", +] + +[[package]] +name = "tinyvec" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "848a1e1181b9f6753b5e96a092749e29b11d19ede67dfbbd6c7dc7e0f49b5338" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" + +[[package]] +name = "tokio" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b7b349f11a7047e6d1276853e612d152f5e8a352c61917887cc2169e2366b4c" +dependencies = [ + "autocfg", + "bytes", + "libc", + "memchr", + "mio", + "num_cpus", + "once_cell", + "pin-project-lite", + "signal-hook-registry", + "tokio-macros", + "winapi", +] + +[[package]] +name = "tokio-macros" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54473be61f4ebe4efd09cec9bd5d16fa51d70ea0192213d754d2d500457db110" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-rustls" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc6844de72e57df1980054b38be3a9f4702aba4858be64dd700181a8a6d0e1b6" +dependencies = [ + "rustls", + "tokio", + "webpki", +] + +[[package]] +name = "tokio-stream" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b2f3f698253f03119ac0102beaa64f67a67e08074d03a22d18784104543727f" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-tungstenite" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e96bb520beab540ab664bd5a9cfeaa1fcd846fa68c830b42e2c8963071251d2" +dependencies = [ + "futures-util", + "log", + "pin-project", + "rustls", + "tokio", + "tokio-rustls", + "tungstenite", + "webpki", + "webpki-roots", +] + +[[package]] +name = "toml" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" +dependencies = [ + "serde", +] + +[[package]] +name = "tracing" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09adeb8c97449311ccd28a427f96fb563e7fd31aabf994189879d9da2394b89d" +dependencies = [ + "cfg-if", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c42e6fa53307c8a17e4ccd4dc81cf5ec38db9209f59b222210375b54ee40d1e2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9ff14f98b1a4b289c6248a023c1c2fa1491062964e9fed67ab29c4e4da4a052" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "tracing-log" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6923477a48e41c1951f1999ef8bb5a3023eb723ceadafe78ffb65dc366761e3" +dependencies = [ + "lazy_static", + "log", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab69019741fca4d98be3c62d2b75254528b5432233fd8a4d2739fec20278de48" +dependencies = [ + "chrono", + "lazy_static", + "matchers", + "parking_lot", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "tungstenite" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fe8dada8c1a3aeca77d6b51a4f1314e0f4b8e438b7b1b71e3ddaca8080e4093" +dependencies = [ + "base64", + "byteorder", + "bytes", + "http", + "httparse", + "input_buffer", + "log", + "rand", + "rustls", + "sha-1", + "thiserror", + "url", + "utf-8", + "webpki", + "webpki-roots", +] + +[[package]] +name = "typenum" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879f6906492a7cd215bfa4cf595b600146ccfac0c79bcbd1f3000162af5e8b06" + +[[package]] +name = "unicode-bidi" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eeb8be209bb1c96b7c177c7420d26e04eccacb0eeae6b980e35fcb74678107e0" +dependencies = [ + "matches", +] + +[[package]] +name = "unicode-normalization" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-segmentation" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b" + +[[package]] +name = "unicode-width" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + +[[package]] +name = "url" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" +dependencies = [ + "form_urlencoded", + "idna", + "matches", + "percent-encoding", +] + +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[package]] +name = "uuid" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" +dependencies = [ + "getrandom", +] + +[[package]] +name = "version-compare" +version = "0.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c18c859eead79d8b95d09e4678566e8d70105c4e7b251f707a03df32442661b" + +[[package]] +name = "version_check" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" + +[[package]] +name = "wasi" +version = "0.10.2+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" + +[[package]] +name = "wasm-bindgen" +version = "0.2.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b608ecc8f4198fe8680e2ed18eccab5f0cd4caaf3d83516fa5fb2e927fda2586" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "580aa3a91a63d23aac5b6b267e2d13cb4f363e31dce6c352fca4752ae12e479f" +dependencies = [ + "bumpalo", + "lazy_static", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "171ebf0ed9e1458810dfcb31f2e766ad6b3a89dbda42d8901f2b268277e5f09c" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c2657dd393f03aa2a659c25c6ae18a13a4048cebd220e147933ea837efc589f" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e0c4a743a309662d45f4ede961d7afa4ba4131a59a639f29b0069c3798bbcc2" + +[[package]] +name = "web-sys" +version = "0.3.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01c70a82d842c9979078c772d4a1344685045f1a5628f677c2b2eab4dd7d2696" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki" +version = "0.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "webpki-roots" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aabe153544e473b775453675851ecc86863d2a81d786d741f6b76778f2a48940" +dependencies = [ + "webpki", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "xmpp-parsers" +version = "0.18.1" +dependencies = [ + "base64", + "blake2", + "chrono", + "digest", + "jid", + "minidom", + "sha-1", + "sha2", + "sha3", +] + +[[package]] +name = "yasna" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e262a29d0e61ccf2b6190d7050d4b237535fc76ce4c1210d9caa316f71dffa75" +dependencies = [ + "chrono", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..3fdf482 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] +members = ["lib-gst-meet", "gst-meet", "nice-gst-meet", "nice-gst-meet-sys"] + +[patch.crates-io] +minidom = { path = "../xmpp-rs/minidom" } diff --git a/LICENSE-APACHE b/LICENSE-APACHE new file mode 100644 index 0000000..f8e5e5e --- /dev/null +++ b/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. \ No newline at end of file diff --git a/LICENSE-MIT b/LICENSE-MIT new file mode 100644 index 0000000..468cd79 --- /dev/null +++ b/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..b8069f9 --- /dev/null +++ b/README.md @@ -0,0 +1,118 @@ +# gst-meet: Integrate Jitsi Meet conferences with GStreamer pipelines + +Note: gst-meet is in an **alpha** state and is under active development. The command-line options and the lib-gst-meet API are subject to change, and some important features (TURN, RTX, TCC, simulcast) are not yet fully functional. + +gst-meet provides a library and tool for integrating Jitsi Meet conferences with GStreamer pipelines. You can pipe audio and video into a conference as a participant, and pipe out other participants' audio and video streams. + +Thanks to GStreamer's flexibility and wide range of plugins, this enables many new possibilities. + +## Installation + +You will need the dependencies `glib`, `gstreamer` and `libnice`, as well as any GStreamer plugins you want to use in your pipelines, and a Rust toolchain ([rustup](https://rustup.rs/) is the easiest way to install one). + +Then: `cargo install gst-meet` + +To integrate gst-meet into your own application, look at `lib-gst-meet`. + +## Pipeline Structure + +You can pass two different pipeline fragments to gst-meet. + +`--send-pipeline` is for sending audio and video. If it contains an element named `audio`, this audio will be streamed to the conference. The audio codec must be 48kHz Opus. If it contains an element named `video`, this video will be streamed to the conference. The video codec must match the codec passed to `--video-codec`, which is VP8 by default. + +`--recv-pipeline-participant-template` is for receiving audio and video from other participants. This pipeline will be created once for each other participant in the conference. If it contains an element named `audio`, the participant's audio (48kHz Opus) will be sent to that element. If it contains an element named `video`, the participant's video (encoded with the codec selected by `--video-codec`) will be sent to that element. The strings `{jid}`, `{jid_user}`, `{participant_id}` and `{nick}` are replaced in the template with the participant's full JID, user part, MUC JID resource part (a.k.a. participant/occupant ID) and nickname respectively. + +## Examples + +A few examples of `gst-meet` usage are below. The GStreamer reference provides full details on available pipeline elements. + +`gst-meet --help` lists full usage information. + +Stream an Opus audio file to the conference. This is very efficient; the Opus data in the file is streamed directly without transcoding: + +``` +gst-meet --web-socket-url=wss://your.jitsi.domain/xmpp-websocket \ + --xmpp-domain=your.jitsi.domain \ + --room-name=roomname \ + --send-pipeline="filesrc location=sample.opus ! queue ! oggdemux name=audio" +``` + +Stream a FLAC audio file to the conference, transcoding it to Opus: + +``` +gst-meet --web-socket-url=wss://your.jitsi.domain/xmpp-websocket \ + --xmpp-domain=your.jitsi.domain \ + --room-name=roomname \ + --send-pipeline="filesrc location=shake-it-off.flac ! queue ! flacdec ! audioconvert ! audioresample ! opusenc name=audio" +``` + +Stream a .webm file containing VP8 video and Vorbis audio to the conference. This pipeline passes the VP8 stream through efficiently without transcoding, and transcodes the audio from Vorbis to Opus: + +``` +gst-meet --web-socket-url=wss://your.jitsi.domain/xmpp-websocket \ + --xmpp-domain=your.jitsi.domain \ + --room-name=roomname \ + --send-pipeline="filesrc location=big-buck-bunny_trailer.webm ! queue ! matroskademux name=demuxer + demuxer.video_0 ! queue name=video + demuxer.audio_0 ! queue ! vorbisdec ! audioconvert ! audioresample ! opusenc name=audio" +``` + +Stream the default macOS video & audio inputs to the conference, encoding as VP8 and Opus, display incoming video streams and play back incoming audio (a very basic, but completely native, Jitsi Meet conference!): + +``` +gst-meet --web-socket-url=wss://your.jitsi.domain/xmpp-websocket \ + --xmpp-domain=your.jitsi.domain \ + --room-name=roomname \ + --send-pipeline="avfvideosrc ! queue ! videoconvert ! vp8enc buffer-size=1000 deadline=1 name=video + osxaudiosrc ! queue ! audioconvert ! audioresample ! opusenc name=audio" \ + --recv-pipeline-participant-template="opusdec name=audio ! autoaudiosink + vp8dec name=video ! videoconvert ! autovideosink" +``` + +Record a .webm file for each other participant, containing VP8 video and Opus audio, without needing to do any transcoding: + +``` +gst-meet --web-socket-url=wss://your.jitsi.domain/xmpp-websocket \ + --xmpp-domain=your.jitsi.domain \ + --room-name=roomname \ + --recv-pipeline-participant-template="webmmux name=muxer ! filesink location={participant_id}.webm + opusparse name=audio ! muxer.audio_0 + capsfilter caps=video/x-vp8 name=video ! muxer.video_0" +``` + +Play a YouTube video in the conference. By requesting Opus audio and VP9 video from YouTube, and setting the Jitsi Meet video codec to VP9, no transcoding is necessary: + +``` +YOUTUBE_URL="https://www.youtube.com/watch?v=vjV_2Ri2rfE" +gst-meet --web-socket-url=wss://your.jitsi.domain/xmpp-websocket \ + --xmpp-domain=your.jitsi.domain \ + --room-name=roomname \ + --video-codec=vp9 \ + --send-pipeline="curlhttpsrc location=\"$(youtube-dl -g $YOUTUBE_URL -f 'bestaudio[acodec=opus]')\" ! queue ! matroskademux name=audiodemux + curlhttpsrc location=\"$(youtube-dl -g $YOUTUBE_URL -f 'bestvideo[vcodec=vp9]')\" ! queue ! matroskademux name=videodemux + audiodemux.audio_0 ! queue ! clocksync name=audio + videodemux.video_0 ! queue ! clocksync name=video" +``` + +## Debugging + +It can sometimes be tricky to get GStreamer pipeline syntax and structure correct. To help with this, you can try setting the `GST_DEBUG` environment variable (for example, `3` is modestly verbose, while `6` produces copious per-packet output). You can also set `GST_DEBUG_DUMP_DOT_DIR` to the relative path to a directory (which must already exist). `.dot` files containing the pipeline graph will be saved to this directory, and can be converted to `.png` with the `dot` tool from GraphViz; for example `dot filename.dot -Tpng > filename.png`. + +## License + +`gst-meet`, `lib-gst-meet`, `nice` and `nice-sys` are licensed under either of + + * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) + * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + +at your option. + +The dependency `xmpp-parsers` is licensed under the Mozilla Public License, Version 2.0, https://www.mozilla.org/en-US/MPL/2.0/ + +The dependency `gstreamer` is licensed under the GNU Lesser General Public License, Version 2.1, https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html. + +## Contribution + +Any kinds of contributions are welcome as a pull request. + +Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in these crates by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. \ No newline at end of file diff --git a/deny.toml b/deny.toml new file mode 100644 index 0000000..3aaade5 --- /dev/null +++ b/deny.toml @@ -0,0 +1,44 @@ +[advisories] +db-path = "~/.cargo/advisory-db" +db-urls = ["https://github.com/rustsec/advisory-db"] +vulnerability = "deny" +unmaintained = "deny" +yanked = "deny" +notice = "deny" +ignore = [] + +[licenses] +unlicensed = "deny" +allow = ["MPL-2.0"] +deny = [] +copyleft = "deny" +allow-osi-fsf-free = "either" +default = "deny" +confidence-threshold = 1.0 +exceptions = [] + +[[licenses.clarify]] +name = "ring" +version = "*" +expression = "MIT AND ISC AND OpenSSL" +license-files = [{ path = "LICENSE", hash = 0xbd0eed23 }] + +[[licenses.clarify]] +name = "webpki" +version = "*" +expression = "ISC" +license-files = [{ path = "LICENSE", hash = 0x001c7e6c }] + +[bans] +multiple-versions = "deny" +wildcards = "deny" +highlight = "all" +allow = [] +deny = [] +skip = [] +skip-tree = [] + +[sources] +unknown-registry = "deny" +unknown-git = "deny" +allow-registry = ["https://github.com/rust-lang/crates.io-index"] diff --git a/gst-meet/Cargo.toml b/gst-meet/Cargo.toml new file mode 100644 index 0000000..83b742a --- /dev/null +++ b/gst-meet/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "gst-meet" +version = "0.1.0" +edition = "2018" +license = "MIT/Apache-2.0" +authors = ["Jasper Hugo "] + +[dependencies] +anyhow = { version = "1", default-features = false, features = ["std"] } +futures = { version = "0.3", default-features = false } +glib = { version = "0.14", default-features = false, features = ["log"] } +gstreamer = { version = "0.17", default-features = false, features = ["v1_18"] } +lib-gst-meet = { path = "../lib-gst-meet" } +structopt = { version = "0.3", default-features = false } +tokio = { version = "1", default-features = false, features = ["macros", "rt-multi-thread", "signal", "sync", "time"] } +tokio-stream = { version = "0.1", default-features = false } +tracing = { version = "0.1", default-features = false, features = ["attributes", "std"] } +tracing-subscriber = { version = "0.2", default-features = false, features = [ + "chrono", + "env-filter", + "fmt", + "smallvec", + "parking_lot", + "tracing-log", +] } + +[target.'cfg(target_os = "macos")'.dependencies] +cocoa = { version = "0.24", default-features = false } \ No newline at end of file diff --git a/gst-meet/LICENSE-APACHE b/gst-meet/LICENSE-APACHE new file mode 100644 index 0000000..f8e5e5e --- /dev/null +++ b/gst-meet/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. \ No newline at end of file diff --git a/gst-meet/LICENSE-MIT b/gst-meet/LICENSE-MIT new file mode 100644 index 0000000..468cd79 --- /dev/null +++ b/gst-meet/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/gst-meet/src/main.rs b/gst-meet/src/main.rs new file mode 100644 index 0000000..f0fcf74 --- /dev/null +++ b/gst-meet/src/main.rs @@ -0,0 +1,224 @@ +use std::time::Duration; + +use anyhow::{Context, Result}; +#[cfg(target_os = "macos")] +use cocoa::appkit::NSApplication; +use gstreamer::{ + prelude::{ElementExt, GstBinExt}, + GhostPad, +}; +use lib_gst_meet::{JitsiConferenceConfig, JitsiConnection}; +use structopt::StructOpt; +use tokio::{signal::ctrl_c, task, time::timeout}; +use tracing::{info, warn}; + +#[derive(Debug, Clone, StructOpt)] +#[structopt( + name = "gst-meet", + about = "Connect a GStreamer pipeline to a Jitsi Meet conference." +)] +struct Opt { + #[structopt(long)] + web_socket_url: String, + #[structopt(long)] + xmpp_domain: String, + #[structopt(long)] + room_name: String, + #[structopt(long)] + muc_domain: Option, + #[structopt(long)] + focus_jid: Option, + #[structopt(long, default_value = "vp8")] + video_codec: String, + #[structopt(long, default_value = "gst-meet")] + nick: String, + #[structopt(long, default_value)] + region: String, + #[structopt(long)] + send_pipeline: Option, + #[structopt(long)] + recv_pipeline_participant_template: Option, +} + +#[cfg(not(target_os = "macos"))] +#[tokio::main] +async fn main() -> Result<()> { + main_inner().await +} + +#[cfg(target_os = "macos")] +fn main() { + // GStreamer requires an NSApp event loop in order for osxvideosink etc to work. + let app = unsafe { cocoa::appkit::NSApp() }; + + let rt = tokio::runtime::Builder::new_multi_thread() + .enable_all() + .build() + .unwrap(); + + rt.spawn(async move { + main_inner().await.unwrap(); + unsafe { + cocoa::appkit::NSApp().stop_(cocoa::base::nil); + } + std::process::exit(0); + }); + + unsafe { + app.run(); + } +} + +async fn main_inner() -> Result<()> { + init_tracing(); + + let opt = Opt::from_args(); + + glib::log_set_default_handler(glib::rust_log_handler); + let main_loop = glib::MainLoop::new(None, false); + gstreamer::init().unwrap(); + + let parsed_bin = opt + .send_pipeline + .as_ref() + .map(|pipeline| gstreamer::parse_bin_from_description(pipeline, false)) + .transpose()?; + + let (connection, background) = + JitsiConnection::new(&opt.web_socket_url, &opt.xmpp_domain).await?; + + tokio::spawn(background); + + connection.connect().await?; + + let room_jid = format!( + "{}@{}", + opt.room_name, + opt + .muc_domain + .clone() + .unwrap_or_else(|| { format!("conference.{}", opt.xmpp_domain) }), + ); + + let focus_jid = opt + .focus_jid + .clone() + .unwrap_or_else(|| format!("focus@auth.{}/focus", opt.xmpp_domain,)); + + let Opt { + nick, + region, + video_codec, + recv_pipeline_participant_template, + .. + } = opt; + + let config = JitsiConferenceConfig { + muc: room_jid.parse()?, + focus: focus_jid.parse()?, + nick, + region, + video_codec, + }; + + let conference = connection + .join_conference(main_loop.context(), config) + .await?; + + if let Some(bin) = parsed_bin { + conference.add_bin(&bin).await?; + + if let Some(audio) = bin.by_name("audio") { + info!("Found audio element in pipeline, linking..."); + let audio_sink = conference.audio_sink_element().await?; + audio.link(&audio_sink)?; + } + + if let Some(video) = bin.by_name("video") { + info!("Found video element in pipeline, linking..."); + let video_sink = conference.video_sink_element().await?; + video.link(&video_sink)?; + } + } + + conference + .on_participant(move |participant| { + let recv_pipeline_participant_template = recv_pipeline_participant_template.clone(); + Box::pin(async move { + info!("New participant: {:?}", participant); + + if let Some(template) = recv_pipeline_participant_template { + let pipeline_description = template + .replace("{jid}", &participant.jid.to_string()) + .replace( + "{jid_user}", + &participant.jid.node.context("jid missing node")?, + ) + .replace("{participant_id}", &participant.muc_jid.resource) + .replace("{nick}", &participant.nick.unwrap_or_default()); + + let bin = gstreamer::parse_bin_from_description(&pipeline_description, false) + .context("failed to parse recv pipeline participant template")?; + + if let Some(audio_sink_element) = bin.by_name("audio") { + let sink_pad = audio_sink_element.static_pad("sink").context( + "audio sink element in recv pipeline participant template has no sink pad", + )?; + bin.add_pad(&GhostPad::with_target(Some("audio"), &sink_pad)?)?; + } + else { + info!("No audio sink element found in recv pipeline participant template"); + } + + if let Some(video_sink_element) = bin.by_name("video") { + let sink_pad = video_sink_element.static_pad("sink").context( + "video sink element in recv pipeline participant template has no sink pad", + )?; + bin.add_pad(&GhostPad::with_target(Some("video"), &sink_pad)?)?; + } + else { + info!("No video sink element found in recv pipeline participant template"); + } + + Ok(Some(bin)) + } + else { + info!("No template for handling new participant"); + Ok(None) + } + }) + }) + .await; + + conference + .set_pipeline_state(gstreamer::State::Playing) + .await?; + + let conference_ = conference.clone(); + let main_loop_ = main_loop.clone(); + tokio::spawn(async move { + ctrl_c().await.unwrap(); + + info!("Exiting..."); + + match timeout(Duration::from_secs(10), conference_.leave()).await { + Ok(Ok(_)) => {}, + Ok(Err(e)) => warn!("Error leaving conference: {:?}", e), + Err(_) => warn!("Timed out leaving conference"), + } + + main_loop_.quit(); + }); + + task::spawn_blocking(move || main_loop.run()).await?; + + Ok(()) +} + +fn init_tracing() { + tracing_subscriber::fmt() + .with_env_filter(tracing_subscriber::EnvFilter::from_default_env()) + .with_span_events(tracing_subscriber::fmt::format::FmtSpan::CLOSE) + .with_target(false) + .init(); +} diff --git a/lib-gst-meet/Cargo.toml b/lib-gst-meet/Cargo.toml new file mode 100644 index 0000000..3e0049a --- /dev/null +++ b/lib-gst-meet/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "lib-gst-meet" +version = "0.1.0" +edition = "2018" +license = "MIT/Apache-2.0" +authors = ["Jasper Hugo "] + +[dependencies] +anyhow = { version = "1", default-features = false, features = ["std"] } +async-stream = { version = "0.3", default-features = false } +async-trait = { version = "0.1", default-features = false } +bytes = { version = "1", default-features = false, features = ["std"] } +futures = { version = "0.3", default-features = false } +glib = { version = "0.14", default-features = false } +gstreamer = { version = "0.17", default-features = false, features = ["v1_18"] } +hex = { version = "0.4", default-features = false, features = ["std"] } +itertools = { version = "0.10", default-features = false, features = ["use_std"] } +libc = { version = "0.2", default-features = false } +maplit = { version = "1", default-features = false } +nice-gst-meet = { path = "../nice-gst-meet", default-features = false, features = ["v0_1_18"] } +once_cell = { version = "1", default-features = false, features = ["std"] } +pem = { version = "0.8", default-features = false } +rand = { version = "0.8", default-features = false, features = ["std", "std_rng"] } +rcgen = { version = "0.8", default-features = false } +ring = { version = "0.16", default-features = false } +tokio = { version = "1", default-features = false, features = ["sync", "time"] } +tokio-stream = { version = "0.1", default-features = false, features = ["time"] } +tokio-tungstenite = { version = "0.14", default-features = false, features = ["connect", "rustls-tls"] } +tracing = { version = "0.1", default-features = false, features = ["attributes", "std"] } +uuid = { version = "0.8", default-features = false, features = ["v4"] } +xmpp-parsers = { path = "../../xmpp-rs/xmpp-parsers", default-features = false } + +[build-dependencies] +pkg-config = { version = "0.3", default-features = false } diff --git a/lib-gst-meet/LICENSE-APACHE b/lib-gst-meet/LICENSE-APACHE new file mode 100644 index 0000000..f8e5e5e --- /dev/null +++ b/lib-gst-meet/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. \ No newline at end of file diff --git a/lib-gst-meet/LICENSE-MIT b/lib-gst-meet/LICENSE-MIT new file mode 100644 index 0000000..468cd79 --- /dev/null +++ b/lib-gst-meet/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/lib-gst-meet/src/conference.rs b/lib-gst-meet/src/conference.rs new file mode 100644 index 0000000..5ac522e --- /dev/null +++ b/lib-gst-meet/src/conference.rs @@ -0,0 +1,579 @@ +use std::{collections::HashMap, convert::TryFrom, fmt, future::Future, pin::Pin, sync::Arc}; + +use anyhow::{anyhow, bail, Context, Result}; +use async_trait::async_trait; +use futures::stream::StreamExt; +use glib::ObjectExt; +use gstreamer::prelude::{ElementExt, ElementExtManual, GstBinExt}; +use once_cell::sync::Lazy; +use tokio::sync::{mpsc, oneshot, Mutex}; +use tokio_stream::wrappers::ReceiverStream; +use tracing::{debug, error, info, trace, warn}; +use xmpp_parsers::{ + disco::{DiscoInfoQuery, DiscoInfoResult, Feature}, + ecaps2::{self, ECaps2}, + hashes::Algo, + iq::{Iq, IqType}, + jingle::{Action, Jingle}, + muc::{Muc, MucUser}, + ns, + presence::{self, Presence}, + BareJid, Element, FullJid, Jid, +}; + +use crate::{ + jingle::JingleSession, + source::MediaType, + stanza_filter::StanzaFilter, + xmpp, +}; + +static DISCO_INFO: Lazy = Lazy::new(|| DiscoInfoResult { + node: None, + identities: vec![], + features: vec![ + Feature::new(ns::JINGLE_RTP_AUDIO), + Feature::new(ns::JINGLE_RTP_VIDEO), + Feature::new(ns::JINGLE_ICE_UDP), + Feature::new(ns::JINGLE_DTLS), + + // not supported yet: rtx + // Feature::new("urn:ietf:rfc:4588"), + + // not supported yet: rtcp remb + // Feature::new("http://jitsi.org/remb"), + + // not supported yet: transport-cc + // Feature::new("http://jitsi.org/tcc"), + + // rtcp-mux + Feature::new("urn:ietf:rfc:5761"), + // rtp-bundle + Feature::new("urn:ietf:rfc:5888"), + // opus red + Feature::new("http://jitsi.org/opus-red"), + ], + extensions: vec![], +}); + +#[derive(Debug, Clone, Copy)] +enum JitsiConferenceState { + Discovering, + JoiningMuc, + Idle, +} + +#[derive(Debug, Clone)] +pub struct JitsiConferenceConfig { + pub muc: BareJid, + pub focus: FullJid, + pub nick: String, + pub region: String, + pub video_codec: String, +} + +#[derive(Clone)] +pub struct JitsiConference { + pub(crate) glib_main_context: glib::MainContext, + pub(crate) jid: FullJid, + pub(crate) xmpp_tx: mpsc::Sender, + pub(crate) config: JitsiConferenceConfig, + pub(crate) external_services: Vec, + pub(crate) inner: Arc>, +} + +impl fmt::Debug for JitsiConference { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("JitsiConference") + .field("jid", &self.jid) + .field("config", &self.config) + .field("inner", &self.inner) + .finish() + } +} + +#[derive(Debug, Clone)] +pub struct Participant { + pub jid: FullJid, + pub muc_jid: FullJid, + pub nick: Option, + bin: Option, +} + +pub(crate) struct JitsiConferenceInner { + pub(crate) jingle_session: Option, + participants: HashMap, + on_participant: Option< + Arc< + dyn (Fn(Participant) -> Pin>> + Send>>) + + Send + + Sync, + >, + >, + state: JitsiConferenceState, + connected_tx: Option>, + connected_rx: Option>, +} + +impl fmt::Debug for JitsiConferenceInner { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("JitsiConferenceInner") + .field("state", &self.state) + .finish() + } +} + +impl JitsiConference { + #[tracing::instrument(level = "debug", skip(xmpp_tx), err)] + pub(crate) async fn new( + glib_main_context: glib::MainContext, + jid: FullJid, + xmpp_tx: mpsc::Sender, + config: JitsiConferenceConfig, + external_services: Vec, + ) -> Result { + let (tx, rx) = oneshot::channel(); + Ok(Self { + glib_main_context, + jid, + xmpp_tx, + config, + external_services, + inner: Arc::new(Mutex::new(JitsiConferenceInner { + state: JitsiConferenceState::Discovering, + participants: HashMap::new(), + on_participant: None, + jingle_session: None, + connected_tx: Some(tx), + connected_rx: Some(rx), + })), + }) + } + + pub async fn connected(&self) -> Result<()> { + let rx = { + let mut locked_inner = self.inner.lock().await; + locked_inner + .connected_rx + .take() + .context("connected() called twice")? + }; + rx.await?; + Ok(()) + } + + #[tracing::instrument(level = "debug", err)] + pub async fn leave(self) -> Result<()> { + let mut inner = self.inner.lock().await; + + if let Some(jingle_session) = inner.jingle_session.take() { + debug!("pausing all sinks"); + jingle_session.pause_all_sinks(); + + debug!("setting pipeline state to NULL"); + if let Err(e) = jingle_session.pipeline().set_state(gstreamer::State::Null) { + warn!("failed to set pipeline state to NULL: {:?}", e); + } + + debug!("waiting for state change to complete"); + let _ = jingle_session.pipeline_stopped().await; + } + + // should leave the XMPP muc gracefully, instead of just disconnecting + + Ok(()) + } + + fn jid_in_muc(&self) -> Result { + let resource = self + .jid + .node + .as_ref() + .ok_or_else(|| anyhow!("invalid jid"))? + .split('-') + .next() + .ok_or_else(|| anyhow!("invalid jid"))?; + Ok(self.config.muc.clone().with_resource(resource)) + } + + pub(crate) fn focus_jid_in_muc(&self) -> Result { + Ok(self.config.muc.clone().with_resource("focus")) + } + + #[tracing::instrument(level = "debug", err)] + async fn send_presence(&self, payloads: Vec) -> Result<()> { + let mut presence = Presence::new(presence::Type::None).with_to(self.jid_in_muc()?); + presence.payloads = payloads; + self.xmpp_tx.send(presence.into()).await?; + Ok(()) + } + + #[tracing::instrument(level = "debug", err)] + pub async fn set_muted(&self, media_type: MediaType, muted: bool) -> Result<()> { + self + .send_presence(vec![ + Element::builder(media_type.jitsi_muted_presence_element_name(), "") + .append(muted.to_string()) + .build(), + ]) + .await + } + + pub async fn pipeline(&self) -> Result { + Ok( + self + .inner + .lock() + .await + .jingle_session + .as_ref() + .context("not connected (no jingle session)")? + .pipeline(), + ) + } + + #[tracing::instrument(level = "debug", err)] + pub async fn add_bin(&self, bin: &gstreamer::Bin) -> Result<()> { + let pipeline = self.pipeline().await?; + pipeline.add(bin)?; + bin.sync_state_with_parent()?; + Ok(()) + } + + #[tracing::instrument(level = "debug", err)] + pub async fn set_pipeline_state(&self, state: gstreamer::State) -> Result<()> { + self.pipeline().await?.call_async(move |p| { + if let Err(e) = p.set_state(state) { + error!("pipeline set_state: {:?}", e); + } + }); + Ok(()) + } + + pub async fn audio_sink_element(&self) -> Result { + Ok( + self + .inner + .lock() + .await + .jingle_session + .as_ref() + .context("not connected (no jingle session)")? + .audio_sink_element(), + ) + } + + pub async fn video_sink_element(&self) -> Result { + Ok( + self + .inner + .lock() + .await + .jingle_session + .as_ref() + .context("not connected (no jingle session)")? + .video_sink_element(), + ) + } + + #[tracing::instrument(level = "trace", skip(f))] + pub async fn on_participant( + &self, + f: impl (Fn(Participant) -> Pin>> + Send>>) + + Send + + Sync + + 'static, + ) { + let f = Arc::new(f); + let f2 = f.clone(); + let existing_participants: Vec<_> = { + let mut locked_inner = self.inner.lock().await; + locked_inner.on_participant = Some(f2); + locked_inner.participants.values().cloned().collect() + }; + for participant in existing_participants { + debug!( + "calling on_participant with existing participant: {:?}", + participant + ); + match f(participant.clone()).await { + Ok(Some(bin)) => { + bin + .set_property( + "name", + format!("participant_{}", participant.muc_jid.resource), + ) + .unwrap(); + match self.add_bin(&bin).await { + Ok(_) => { + let mut locked_inner = self.inner.lock().await; + if let Some(p) = locked_inner + .participants + .get_mut(&participant.muc_jid.resource) + { + p.bin = Some(bin); + } + }, + Err(e) => warn!("failed to add participant bin: {:?}", e), + } + }, + Ok(None) => {}, + Err(e) => warn!("on_participant failed: {:?}", e), + } + } + } +} + +#[async_trait] +impl StanzaFilter for JitsiConference { + #[tracing::instrument(level = "trace")] + fn filter(&self, element: &Element) -> bool { + element.attr("from") == Some(self.config.focus.to_string().as_str()) + && element.is("iq", "jabber:client") + || element + .attr("from") + .and_then(|from| from.parse::().ok()) + .map(|jid| jid == self.config.muc) + .unwrap_or_default() + && (element.is("presence", "jabber:client") || element.is("iq", "jabber:client")) + } + + #[tracing::instrument(level = "trace", err)] + async fn take(&self, element: Element) -> Result<()> { + let mut locked_inner = self.inner.lock().await; + + use JitsiConferenceState::*; + match locked_inner.state { + Discovering => { + let iq = Iq::try_from(element)?; + if let IqType::Result(Some(element)) = iq.payload { + let ready: bool = element + .attr("ready") + .ok_or_else(|| anyhow!("missing ready attribute on conference IQ"))? + .parse()?; + if !ready { + bail!("focus reports room not ready"); + } + } + else { + bail!("focus IQ failed"); + }; + + let jitsi_disco_info = DiscoInfoResult { + node: Some("http://jitsi.org/jitsimeet".to_string()), + identities: vec![], + features: vec![], + extensions: vec![], + }; + + let jitsi_disco_hash = + ecaps2::hash_ecaps2(&ecaps2::compute_disco(&jitsi_disco_info)?, Algo::Sha_256)?; + self + .send_presence(vec![ + Muc::new().into(), + ECaps2::new(vec![jitsi_disco_hash]).into(), + Element::builder("stats-id", "").append("gst-meet").build(), + Element::builder("jitsi_participant_codecType", "") + .append(self.config.video_codec.as_str()) + .build(), + Element::builder("jitsi_participant_region", "") + .append(self.config.region.as_str()) + .build(), + Element::builder("audiomuted", "").append("false").build(), + Element::builder("videomuted", "").append("false").build(), + Element::builder("nick", "http://jabber.org/protocol/nick") + .append(self.config.nick.as_str()) + .build(), + Element::builder("region", "http://jitsi.org/jitsi-meet") + .attr("id", &self.config.region) + .build(), + ]) + .await?; + locked_inner.state = JoiningMuc; + }, + JoiningMuc => { + let presence = Presence::try_from(element)?; + if BareJid::from(presence.from.as_ref().unwrap().clone()) == self.config.muc { + debug!("Joined MUC: {}", self.config.muc); + locked_inner.state = Idle; + } + }, + Idle => { + if let Ok(iq) = Iq::try_from(element.clone()) { + match iq.payload { + IqType::Get(element) => { + if let Ok(query) = DiscoInfoQuery::try_from(element) { + debug!( + "Received disco info query from {} for node {:?}", + iq.from.as_ref().unwrap(), + query.node + ); + if query.node.is_none() { + let iq = Iq::from_result(iq.id, Some(DISCO_INFO.clone())) + .with_from(Jid::Full(self.jid.clone())) + .with_to(iq.from.unwrap()); + self.xmpp_tx.send(iq.into()).await?; + } + else { + panic!("don't know how to handle disco info node: {:?}", query.node); + } + } + }, + IqType::Set(element) => { + if let Ok(jingle) = Jingle::try_from(element) { + if let Some(Jid::Full(from_jid)) = iq.from { + if jingle.action == Action::SessionInitiate { + if from_jid.resource == "focus" { + // Acknowledge the IQ + let result_iq = Iq::empty_result(Jid::Full(from_jid.clone()), iq.id.clone()) + .with_from(Jid::Full(self.jid.clone())); + self.xmpp_tx.send(result_iq.into()).await?; + + locked_inner.jingle_session = + Some(JingleSession::initiate(self, jingle).await?); + } + else { + debug!("Ignored Jingle session-initiate from {}", from_jid); + } + } + else if jingle.action == Action::SourceAdd { + debug!("Received Jingle source-add"); + + // Acknowledge the IQ + let result_iq = Iq::empty_result(Jid::Full(from_jid.clone()), iq.id.clone()) + .with_from(Jid::Full(self.jid.clone())); + self.xmpp_tx.send(result_iq.into()).await?; + + locked_inner + .jingle_session + .as_mut() + .context("not connected (no jingle session")? + .source_add(jingle) + .await?; + } + } + else { + debug!("Received Jingle IQ from invalid JID: {:?}", iq.from); + } + } + else { + debug!("Received non-Jingle IQ"); + } + }, + IqType::Result(_) => { + if let Some(jingle_session) = locked_inner.jingle_session.as_ref() { + if Some(iq.id) == jingle_session.accept_iq_id { + let colibri_url = jingle_session.colibri_url.clone(); + + locked_inner.jingle_session.as_mut().unwrap().accept_iq_id = None; + + debug!("Focus acknowledged session-accept"); + + if let Some(colibri_url) = colibri_url { + info!("Connecting Colibri WebSocket to {}", colibri_url); + + let request = + tokio_tungstenite::tungstenite::http::Request::get(colibri_url).body(())?; + let (colibri_websocket, _response) = + tokio_tungstenite::connect_async(request).await?; + info!("Connected Colibri WebSocket"); + + let (colibri_sink, mut colibri_stream) = colibri_websocket.split(); + let colibri_receive_task = tokio::spawn(async move { + while let Some(msg) = colibri_stream.next().await { + debug!("colibri: {:?}", msg); + } + Ok::<_, anyhow::Error>(()) + }); + let (colibri_tx, colibri_rx) = mpsc::channel(8); + locked_inner.jingle_session.as_mut().unwrap().colibri_tx = Some(colibri_tx); + let colibri_transmit_task = tokio::spawn(async move { + let stream = ReceiverStream::new(colibri_rx); + stream.forward(colibri_sink).await?; + Ok::<_, anyhow::Error>(()) + }); + + tokio::spawn(async move { + tokio::select! { + res = colibri_receive_task => if let Ok(Err(e)) = res { + error!("colibri read loop: {:?}", e); + }, + res = colibri_transmit_task => if let Ok(Err(e)) = res { + error!("colibri write loop: {:?}", e); + }, + }; + }); + } + + if let Some(connected_tx) = locked_inner.connected_tx.take() { + connected_tx.send(()).unwrap(); + } + } + } + }, + _ => {}, + } + } + else if let Ok(presence) = Presence::try_from(element) { + if let Jid::Full(from) = presence + .from + .as_ref() + .context("missing from in presence")? + .clone() + { + let bare_from: BareJid = from.clone().into(); + if bare_from == self.config.muc && from.resource != "focus" { + trace!("received MUC presence from {}", from.resource); + for payload in presence.payloads { + if !payload.is("x", ns::MUC_USER) { + continue; + } + let muc_user = MucUser::try_from(payload)?; + debug!("MUC user presence: {:?}", muc_user); + for item in muc_user.items { + if let Some(jid) = &item.jid { + if jid == &self.jid { + continue; + } + let participant = Participant { + jid: jid.clone(), + muc_jid: from.clone(), + nick: item.nick, + bin: None, + }; + if locked_inner + .participants + .insert(from.resource.clone(), participant.clone()) + .is_none() + { + debug!("new participant: {:?}", jid); + if let Some(f) = &locked_inner.on_participant { + debug!("calling on_participant with new participant"); + match f(participant).await { + Ok(Some(bin)) => { + bin.set_property("name", format!("participant_{}", from.resource))?; + match self.add_bin(&bin).await { + Ok(_) => { + if let Some(p) = locked_inner.participants.get_mut(&from.resource) { + p.bin = Some(bin); + } + }, + Err(e) => warn!("failed to add participant bin: {:?}", e), + } + }, + Ok(None) => {}, + Err(e) => warn!("on_participant failed: {:?}", e), + } + } + } + } + } + } + } + } + } + }, + } + Ok(()) + } +} diff --git a/lib-gst-meet/src/connection.rs b/lib-gst-meet/src/connection.rs new file mode 100644 index 0000000..4dd6e18 --- /dev/null +++ b/lib-gst-meet/src/connection.rs @@ -0,0 +1,315 @@ +use std::{convert::TryFrom, fmt, future::Future, sync::Arc}; + +use anyhow::{anyhow, bail, Context, Result}; +use futures::{ + sink::{Sink, SinkExt}, + stream::{Stream, StreamExt, TryStreamExt}, +}; +use maplit::hashmap; +use tokio::sync::{mpsc, oneshot, Mutex}; +use tokio_stream::wrappers::ReceiverStream; +use tokio_tungstenite::tungstenite::{ + http::{Request, Uri}, + Message, +}; +use tracing::{debug, error, info, warn}; +use uuid::Uuid; +use xmpp_parsers::{ + bind::{BindQuery, BindResponse}, + disco::{DiscoInfoQuery, DiscoInfoResult}, + iq::{Iq, IqType}, + sasl::{Auth, Mechanism, Success}, + websocket::Open, + BareJid, Element, FullJid, Jid, +}; + +use crate::{ + conference::{JitsiConference, JitsiConferenceConfig}, + pinger::Pinger, + stanza_filter::StanzaFilter, + util::generate_id, + xmpp, +}; + +#[derive(Debug, Clone, Copy)] +enum JitsiConnectionState { + OpeningPreAuthentication, + ReceivingFeaturesPreAuthentication, + Authenticating, + OpeningPostAuthentication, + ReceivingFeaturesPostAuthentication, + Binding, + Discovering, + DiscoveringExternalServices, + Idle, +} + +struct JitsiConnectionInner { + state: JitsiConnectionState, + xmpp_domain: BareJid, + jid: Option, + external_services: Vec, + connected_tx: Option>>, + stanza_filters: Vec>, +} + +impl fmt::Debug for JitsiConnectionInner { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("JitsiConnectionInner") + .field("state", &self.state) + .field("xmpp_domain", &self.xmpp_domain) + .field("jid", &self.jid) + .finish() + } +} + +#[derive(Debug, Clone)] +pub struct JitsiConnection { + tx: mpsc::Sender, + inner: Arc>, +} + +impl JitsiConnection { + pub async fn new( + websocket_url: &str, + xmpp_domain: &str, + ) -> Result<(Self, impl Future)> { + let websocket_url: Uri = websocket_url.parse()?; + let xmpp_domain: BareJid = xmpp_domain.parse()?; + + info!("Connecting XMPP WebSocket to {}", websocket_url); + let request = Request::get(websocket_url) + .header("Sec-Websocket-Protocol", "xmpp") + .body(())?; + let (websocket, _response) = tokio_tungstenite::connect_async(request).await?; + let (sink, stream) = websocket.split(); + let (tx, rx) = mpsc::channel(64); + + let inner = Arc::new(Mutex::new(JitsiConnectionInner { + state: JitsiConnectionState::OpeningPreAuthentication, + xmpp_domain, + jid: None, + external_services: vec![], + connected_tx: None, + stanza_filters: vec![], + })); + + let connection = Self { + tx: tx.clone(), + inner: inner.clone(), + }; + + let writer = JitsiConnection::write_loop(rx, sink); + let reader = JitsiConnection::read_loop(inner, tx, stream); + + let background = async move { + tokio::select! { + res = reader => if let Err(e) = res { error!("fatal (in read loop): {:?}", e) }, + res = writer => if let Err(e) = res { error!("fatal (in write loop): {:?}", e) }, + } + }; + + Ok((connection, background)) + } + + pub async fn connect(&self) -> Result<()> { + let (tx, rx) = oneshot::channel(); + + { + let mut locked_inner = self.inner.lock().await; + locked_inner.connected_tx = Some(tx); + let open = Open::new(locked_inner.xmpp_domain.clone()); + self.tx.send(open.into()).await?; + } + + rx.await? + } + + pub async fn join_conference( + &self, + glib_main_context: glib::MainContext, + config: JitsiConferenceConfig, + ) -> Result { + let conference_stanza = xmpp::jitsi::Conference { + machine_uid: Uuid::new_v4().to_string(), + room: config.muc.to_string(), + properties: hashmap! { + // Disable voice processing + "stereo".to_string() => "true".to_string(), + "startBitrate".to_string() => "800".to_string(), + }, + }; + + let iq = + Iq::from_set(generate_id(), conference_stanza).with_to(Jid::Full(config.focus.clone())); + self.tx.send(iq.into()).await?; + + let conference = { + let mut locked_inner = self.inner.lock().await; + let conference = JitsiConference::new( + glib_main_context, + locked_inner + .jid + .as_ref() + .context("not connected (no jid)")? + .clone(), + self.tx.clone(), + config, + locked_inner.external_services.clone(), + ) + .await?; + locked_inner + .stanza_filters + .push(Box::new(conference.clone())); + conference + }; + + conference.connected().await?; + + Ok(conference) + } + + async fn write_loop(rx: mpsc::Receiver, mut sink: S) -> Result<()> + where + S: Sink + Unpin, + S::Error: std::error::Error + Send + Sync + 'static, + { + let mut rx = ReceiverStream::new(rx); + while let Some(element) = rx.next().await { + let mut bytes = Vec::new(); + element.write_to(&mut bytes)?; + let xml = String::from_utf8(bytes)?; + debug!("XMPP >>> {}", xml); + sink.send(Message::Text(xml)).await?; + } + Ok(()) + } + + async fn read_loop( + inner: Arc>, + tx: mpsc::Sender, + mut stream: S, + ) -> Result<()> + where + S: Stream> + Unpin, + { + loop { + let message = stream + .try_next() + .await? + .ok_or_else(|| anyhow!("unexpected EOF"))?; + let element: Element = match message { + Message::Text(xml) => { + debug!("XMPP <<< {}", xml); + xml.parse()? + }, + _ => { + warn!( + "unexpected non-text message on XMPP WebSocket stream: {:?}", + message + ); + continue; + }, + }; + + let mut locked_inner = inner.lock().await; + + use JitsiConnectionState::*; + match locked_inner.state { + OpeningPreAuthentication => { + Open::try_from(element)?; + info!("Connected XMPP WebSocket"); + locked_inner.state = ReceivingFeaturesPreAuthentication; + }, + ReceivingFeaturesPreAuthentication => { + let auth = Auth { + mechanism: Mechanism::Anonymous, + data: vec![], + }; + tx.send(auth.into()).await?; + locked_inner.state = Authenticating; + }, + Authenticating => { + Success::try_from(element)?; + + let open = Open::new(locked_inner.xmpp_domain.clone()); + tx.send(open.into()).await?; + locked_inner.state = OpeningPostAuthentication; + }, + OpeningPostAuthentication => { + Open::try_from(element)?; + info!("Logged in anonymously"); + + locked_inner.state = ReceivingFeaturesPostAuthentication; + }, + ReceivingFeaturesPostAuthentication => { + let iq = Iq::from_set(generate_id(), BindQuery::new(None)); + tx.send(iq.into()).await?; + locked_inner.state = Binding; + }, + Binding => { + let iq = Iq::try_from(element)?; + let jid = if let IqType::Result(Some(element)) = iq.payload { + let bind = BindResponse::try_from(element)?; + FullJid::try_from(bind)? + } + else { + bail!("bind failed"); + }; + info!("My JID: {}", jid); + locked_inner.jid = Some(jid.clone()); + + locked_inner.stanza_filters.push(Box::new(Pinger { + jid: jid.clone(), + tx: tx.clone(), + })); + + let iq = Iq::from_get(generate_id(), DiscoInfoQuery { node: None }) + .with_from(Jid::Full(jid.clone())) + .with_to(Jid::Bare(locked_inner.xmpp_domain.clone())); + tx.send(iq.into()).await?; + locked_inner.state = Discovering; + }, + Discovering => { + let iq = Iq::try_from(element)?; + if let IqType::Result(Some(element)) = iq.payload { + let _disco_info = DiscoInfoResult::try_from(element)?; + } + else { + bail!("disco failed"); + } + + let iq = Iq::from_get(generate_id(), xmpp::extdisco::ServicesQuery {}) + .with_from(Jid::Full(locked_inner.jid.as_ref().context("missing jid")?.clone())) + .with_to(Jid::Bare(locked_inner.xmpp_domain.clone())); + tx.send(iq.into()).await?; + locked_inner.state = DiscoveringExternalServices; + }, + DiscoveringExternalServices => { + let iq = Iq::try_from(element)?; + if let IqType::Result(Some(element)) = iq.payload { + let services = xmpp::extdisco::ServicesResult::try_from(element)?; + debug!("external services: {:?}", services.services); + locked_inner.external_services = services.services; + } + else { + bail!("extdisco failed"); + } + + if let Some(tx) = locked_inner.connected_tx.take() { + tx.send(Ok(())).map_err(|_| anyhow!("channel closed"))?; + } + locked_inner.state = Idle; + }, + Idle => { + for filter in &locked_inner.stanza_filters { + if filter.filter(&element) { + filter.take(element).await?; + break; + } + } + }, + } + } + } +} diff --git a/lib-gst-meet/src/jingle.rs b/lib-gst-meet/src/jingle.rs new file mode 100644 index 0000000..a2d33f8 --- /dev/null +++ b/lib-gst-meet/src/jingle.rs @@ -0,0 +1,827 @@ +use std::{collections::HashMap, fmt, net::SocketAddr}; + +use anyhow::{anyhow, bail, Context, Result}; +use futures::stream::StreamExt; +use glib::{Cast, ObjectExt, ToValue}; +use gstreamer::prelude::{ElementExt, GObjectExtManualGst, GstBinExt, PadExt}; +use nice_gst_meet as nice; +use pem::Pem; +use rand::random; +use rcgen::{Certificate, CertificateParams, PKCS_ECDSA_P256_SHA256}; +use ring::digest::{digest, SHA256}; +use tokio::{ + net::lookup_host, + runtime::Handle, + sync::{mpsc, oneshot}, +}; +use tracing::{debug, error, warn}; +use uuid::Uuid; +use xmpp_parsers::{ + hashes::Algo, + iq::Iq, + jingle::{Action, Content, Creator, Description, Jingle, Senders, Transport}, + jingle_dtls_srtp::{Fingerprint, Setup}, + jingle_ice_udp::{self, Transport as IceUdpTransport}, + jingle_rtp::{Description as RtpDescription, PayloadType, RtcpMux}, + jingle_ssma::{self, Parameter}, + Jid, +}; + +use crate::{ + conference::JitsiConference, + source::{MediaType, Source}, + util::generate_id, +}; + +const DEFAULT_STUN_PORT: u16 = 3478; + +pub(crate) struct JingleSession { + pipeline: gstreamer::Pipeline, + audio_sink_element: gstreamer::Element, + video_sink_element: gstreamer::Element, + remote_ssrc_map: HashMap, + ice_agent: nice::Agent, + ice_stream_id: u32, + ice_component_id: u32, + pub(crate) accept_iq_id: Option, + pub(crate) colibri_url: Option, + pub(crate) colibri_tx: Option< + mpsc::Sender< + Result, + >, + >, + pipeline_state_null_rx: oneshot::Receiver<()>, +} + +impl fmt::Debug for JingleSession { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("JingleSession").finish() + } +} + +impl JingleSession { + pub(crate) fn pipeline(&self) -> gstreamer::Pipeline { + self.pipeline.clone() + } + + pub(crate) fn audio_sink_element(&self) -> gstreamer::Element { + self.audio_sink_element.clone() + } + + pub(crate) fn video_sink_element(&self) -> gstreamer::Element { + self.video_sink_element.clone() + } + + pub(crate) fn pause_all_sinks(&self) { + if let Some(rtpbin) = self.pipeline.by_name("rtpbin") { + rtpbin.foreach_src_pad(|_, pad| { + let pad_name: String = pad.property("name").unwrap().get().unwrap(); + if pad_name.starts_with("recv_rtp_src_0_") { + if let Some(peer_pad) = pad.peer() { + if let Some(element) = peer_pad.parent_element() { + element.set_state(gstreamer::State::Paused).unwrap(); + } + } + } + true + }); + } + } + + pub(crate) async fn pipeline_stopped(self) -> Result<()> { + Ok(self.pipeline_state_null_rx.await?) + } + + pub(crate) async fn initiate(conference: &JitsiConference, jingle: Jingle) -> Result { + let initiator = jingle + .initiator + .as_ref() + .ok_or_else(|| anyhow!("session-initiate with no initiator"))? + .clone(); + + debug!("Received Jingle session-initiate from {}", initiator); + + let mut ice_remote_candidates = None; + let mut ice_remote_ufrag = None; + let mut ice_remote_pwd = None; + let mut dtls_fingerprint = None; + let mut opus_payload_type = None; + let mut h264_payload_type = None; + let mut vp8_payload_type = None; + let mut vp9_payload_type = None; + let mut colibri_url = None; + + let mut remote_ssrc_map = HashMap::new(); + + for content in &jingle.contents { + if let Some(Description::Rtp(description)) = &content.description { + if description.media == "audio" { + opus_payload_type = description + .payload_types + .iter() + .find(|pt| pt.name.as_deref() == Some("opus")) + .map(|pt| pt.id); + } + else if description.media == "video" { + h264_payload_type = description + .payload_types + .iter() + .find(|pt| pt.name.as_deref() == Some("H264")) + .map(|pt| pt.id); + vp8_payload_type = description + .payload_types + .iter() + .find(|pt| pt.name.as_deref() == Some("VP8")) + .map(|pt| pt.id); + vp9_payload_type = description + .payload_types + .iter() + .find(|pt| pt.name.as_deref() == Some("VP9")) + .map(|pt| pt.id); + } + else { + continue; + } + + for ssrc in &description.ssrcs { + let owner = ssrc + .info + .as_ref() + .context("missing ssrc-info")? + .owner + .clone(); + if owner == "jvb" { + debug!("skipping ssrc (owner = jvb)"); + continue; + } + + remote_ssrc_map.insert( + ssrc.id.parse()?, + Source { + ssrc: ssrc.id.parse()?, + participant_id: owner + .split('/') + .nth(1) + .context("invalid ssrc-info owner")? + .to_owned(), + media_type: if description.media == "audio" { + MediaType::Audio + } + else { + MediaType::Video + }, + }, + ); + } + } + + if let Some(Transport::IceUdp(transport)) = &content.transport { + if !transport.candidates.is_empty() { + ice_remote_candidates = Some(transport.candidates.clone()); + } + if let Some(ufrag) = &transport.ufrag { + ice_remote_ufrag = Some(ufrag.to_owned()); + } + if let Some(pwd) = &transport.pwd { + ice_remote_pwd = Some(pwd.to_owned()); + } + if let Some(fingerprint) = &transport.fingerprint { + if fingerprint.hash != Algo::Sha_256 { + bail!("unsupported fingerprint hash: {:?}", fingerprint.hash); + } + dtls_fingerprint = Some(fingerprint.value.clone()); + } + if let Some(websocket) = &transport.web_socket { + colibri_url = Some(websocket.url.clone()); + } + } + } + + if let Some(remote_fingerprint) = dtls_fingerprint { + warn!("Remote DTLS fingerprint (verification not implemented yet): {:?}", remote_fingerprint); + } + + let mut dtls_cert_params = CertificateParams::new(vec!["gst-meet".to_owned()]); + dtls_cert_params.alg = &PKCS_ECDSA_P256_SHA256; + let dtls_cert = Certificate::from_params(dtls_cert_params)?; + let dtls_cert_der = dtls_cert.serialize_der()?; + let fingerprint = digest(&SHA256, &dtls_cert_der).as_ref().to_vec(); + let fingerprint_str = + itertools::join(fingerprint.iter().map(|byte| format!("{:X}", byte)), ":"); + let dtls_cert_pem = pem::encode(&Pem { + tag: "CERTIFICATE".to_string(), + contents: dtls_cert_der, + }); + let dtls_private_key_pem = pem::encode(&Pem { + tag: "PRIVATE KEY".to_string(), + contents: dtls_cert.serialize_private_key_der(), + }); + debug!("Local DTLS certificate:\n{}", dtls_cert_pem); + debug!("Local DTLS fingerprint: {}", fingerprint_str); + + let audio_ssrc: u32 = random(); + let video_ssrc: u32 = random(); + + debug!("audio SSRC: {}", audio_ssrc); + debug!("video SSRC: {}", video_ssrc); + + let maybe_stun = conference + .external_services + .iter() + .find(|svc| svc.r#type == "stun"); + + let stun_addr = if let Some(stun) = maybe_stun { + lookup_host(format!("{}:{}", stun.host, stun.port.unwrap_or(DEFAULT_STUN_PORT))) + .await? + .next() + } + else { + None + }; + debug!("STUN address: {:?}", stun_addr); + + let ice_agent = nice::Agent::new(&conference.glib_main_context, nice::Compatibility::Rfc5245); + ice_agent.set_ice_tcp(false); + if let Some((stun_addr, stun_port)) = stun_addr.map(|sa| (sa.ip().to_string(), sa.port())) { + ice_agent.set_stun_server(Some(&stun_addr)); + ice_agent.set_stun_server_port(stun_port as u32); + } + ice_agent.set_upnp(false); + ice_agent.connect_component_state_changed(|_, a, b, c| { + debug!("ICE component-state-changed {} {} {}", a, b, c); + }); + ice_agent.connect_new_selected_pair(|_, a, b, c, d| { + debug!("ICE new-selected-pair {} {} {} {}", a, b, c, d); + }); + let ice_stream_id = ice_agent.add_stream(1); + let ice_component_id = 1; + + if !ice_agent.attach_recv( + ice_stream_id, + ice_component_id, + &conference.glib_main_context, + |_, _, _, s| debug!("ICE nice_agent_attach_recv cb: {}", s), + ) { + warn!("nice_agent_attach_recv failed"); + } + + debug!("ice_agent={:?}", ice_agent); + debug!("ice_stream_id={}", ice_stream_id); + debug!("ice_component_id={}", ice_component_id); + + let (ice_local_ufrag, ice_local_pwd) = ice_agent + .local_credentials(ice_stream_id) + .context("no local ICE credentials")?; + + if let (Some(ufrag), Some(pwd)) = (&ice_remote_ufrag, &ice_remote_pwd) { + debug!("setting ICE remote credentials"); + if !ice_agent.set_remote_credentials(ice_stream_id, ufrag, pwd) { + warn!("nice_agent_set_remote_candidates failed"); + } + } + + ice_agent.connect_candidate_gathering_done(move |ice_agent, a| { + debug!("ICE candidate-gathering-done {}", a); + }); + + debug!("gathering ICE candidates"); + if !ice_agent.gather_candidates(ice_stream_id) { + warn!("nice_agent_gather_candidates failed"); + } + + if let (Some(ufrag), Some(pwd), Some(remote_candidates)) = + (&ice_remote_ufrag, &ice_remote_pwd, &ice_remote_candidates) + { + debug!("setting ICE remote candidates: {:?}", remote_candidates); + let remote_candidates: Vec<_> = remote_candidates + .iter() + .map(|c| { + let mut candidate = nice::Candidate::new(match c.type_ { + jingle_ice_udp::Type::Host => nice::CandidateType::Host, + jingle_ice_udp::Type::Prflx => nice::CandidateType::PeerReflexive, + jingle_ice_udp::Type::Srflx => nice::CandidateType::ServerReflexive, + jingle_ice_udp::Type::Relay => nice::CandidateType::Relayed, + }); + candidate.set_stream_id(ice_stream_id); + candidate.set_component_id(c.component as u32); + candidate.set_foundation(&c.foundation); + candidate.set_addr(SocketAddr::new(c.ip, c.port)); + candidate.set_priority(c.priority); + candidate.set_transport(match c.protocol.as_str() { + "udp" => nice::CandidateTransport::Udp, + other => panic!("unsupported protocol: {}", other), + }); + candidate.set_username(ufrag); + candidate.set_password(pwd); + debug!("candidate: {:?}", candidate); + candidate + }) + .collect(); + let candidate_refs: Vec<_> = remote_candidates.iter().collect(); + let res = ice_agent.set_remote_candidates(ice_stream_id, ice_component_id, &candidate_refs); + if res < remote_candidates.len() as i32 { + warn!("some remote candidates failed to add: {}", res); + } + } + + let pipeline_spec = format!( + r#" + rtpbin rtp-profile=savpf name=rtpbin + + nicesrc stream={0} component={1} name=nicesrc ! dtlssrtpdec name=dtlssrtpdec connection-id=gst-meet + dtlssrtpenc name=dtlssrtpenc connection-id=gst-meet is-client=true ! nicesink stream={0} component={1} name=nicesink + + rtpbin.send_rtp_src_0 ! dtlssrtpenc.rtp_sink_0 + rtpbin.send_rtcp_src_0 ! dtlssrtpenc.rtcp_sink_0 + rtpbin.send_rtp_src_1 ! dtlssrtpenc.rtp_sink_1 + rtpbin.send_rtcp_src_1 ! dtlssrtpenc.rtcp_sink_1 + + dtlssrtpdec.rtp_src ! rtpbin.recv_rtp_sink_0 + dtlssrtpdec.rtcp_src ! rtpbin.recv_rtcp_sink_0 + "#, + ice_stream_id, ice_component_id, + ); + + debug!("building gstreamer pipeline:\n{}", pipeline_spec); + + let pipeline = gstreamer::parse_launch(&pipeline_spec)? + .downcast::() + .map_err(|_| anyhow!("pipeline did not parse as a pipeline"))?; + + let rtpbin = pipeline + .by_name("rtpbin") + .context("no rtpbin in pipeline")?; + + rtpbin.connect("request-pt-map", false, move |values| { + let f = || { + debug!("rtpbin request-pt-map {:?}", values); + let pt = values[2].get::()? as u8; + let maybe_caps = if Some(pt) == opus_payload_type { + Some(gstreamer::Caps::new_simple( + "application/x-rtp", + &[ + ("media", &"audio"), + ("encoding-name", &"OPUS"), + ("clock-rate", &48000), + ], + )) + } + else if Some(pt) == h264_payload_type { + Some(gstreamer::Caps::new_simple( + "application/x-rtp", + &[ + ("media", &"video"), + ("encoding-name", &"H264"), + ("clock-rate", &90000), + ], + )) + } + else if Some(pt) == vp8_payload_type { + Some(gstreamer::Caps::new_simple( + "application/x-rtp", + &[ + ("media", &"video"), + ("encoding-name", &"VP8"), + ("clock-rate", &90000), + ], + )) + } + else if Some(pt) == vp9_payload_type { + Some(gstreamer::Caps::new_simple( + "application/x-rtp", + &[ + ("media", &"video"), + ("encoding-name", &"VP9"), + ("clock-rate", &90000), + ], + )) + } + else { + warn!("unknown payload type: {}", pt); + None + }; + Ok::<_, anyhow::Error>(maybe_caps) + }; + match f() { + Ok(Some(caps)) => { + debug!("mapped pt to caps: {:?}", caps); + Some(caps.to_value()) + }, + Ok(None) => None, + Err(e) => { + error!("handling request-pt-map: {:?}", e); + None + }, + } + })?; + + let handle = Handle::current(); + let inner_ = conference.inner.clone(); + let pipeline_ = pipeline.clone(); + let rtpbin_ = rtpbin.clone(); + rtpbin.connect("pad-added", false, move |values| { + let inner_ = inner_.clone(); + let handle = handle.clone(); + let pipeline_ = pipeline_.clone(); + let rtpbin_ = rtpbin_.clone(); + let f = move || { + debug!("rtpbin pad-added {:?}", values); + let pad: gstreamer::Pad = values[1].get()?; + let pad_name: String = pad.property("name")?.get()?; + if pad_name.starts_with("recv_rtp_src_0_") { + let mut parts = pad_name.split('_').skip(4); + let ssrc: u32 = parts.next().context("malformed pad name")?.parse()?; + let pt: u8 = parts.next().context("malformed pad name")?.parse()?; + let source = handle.block_on(async move { + let locked_inner = inner_.lock().await; + let jingle_session = locked_inner + .jingle_session + .as_ref() + .context("not connected (no jingle session)")?; + Ok::<_, anyhow::Error>( + jingle_session + .remote_ssrc_map + .get(&ssrc) + .context(format!("unknown ssrc: {}", ssrc))? + .clone(), + ) + })?; + + debug!("pad added for remote source: {:?}", source); + + let element_name = match source.media_type { + MediaType::Audio => { + if Some(pt) == opus_payload_type { + "rtpopusdepay" + } + else { + bail!("received audio with unsupported PT {}", pt); + } + }, + MediaType::Video => { + if Some(pt) == h264_payload_type { + "rtph264depay" + } + else if Some(pt) == vp8_payload_type { + "rtpvp8depay" + } + else if Some(pt) == vp9_payload_type { + "rtpvp9depay" + } + else { + bail!("received video with unsupported PT {}", pt); + } + }, + }; + + let source_element = gstreamer::ElementFactory::make(element_name, None)?; + pipeline_ + .add(&source_element) + .context(format!("failed to add {} to pipeline", element_name))?; + source_element.sync_state_with_parent()?; + debug!("created {} element", element_name); + rtpbin_ + .link_pads(Some(&pad_name), &source_element, None) + .context(format!( + "failed to link rtpbin.{} to {}", + pad_name, element_name + ))?; + debug!("linked rtpbin.{} to {}", pad_name, element_name); + + let src_pad = source_element + .static_pad("src") + .context("depayloader has no src pad")?; + + if let Some(participant_bin) = + pipeline_.by_name(&format!("participant_{}", source.participant_id)) + { + let sink_pad_name = match source.media_type { + MediaType::Audio => "audio", + MediaType::Video => "video", + }; + if let Some(sink_pad) = participant_bin.static_pad(sink_pad_name) { + debug!("linking depayloader to participant bin"); + src_pad.link(&sink_pad)?; + } + else { + warn!( + "no {} sink pad in {} participant bin", + sink_pad_name, source.participant_id + ); + } + } + else { + debug!("no participant bin for {}", source.participant_id); + } + + if !src_pad.is_linked() { + debug!("nothing linked to {}, adding fakesink", element_name); + let fakesink = gstreamer::ElementFactory::make("fakesink", None)?; + pipeline_.add(&fakesink)?; + fakesink.sync_state_with_parent()?; + source_element.link(&fakesink)?; + } + + gstreamer::debug_bin_to_dot_file( + &pipeline_, + gstreamer::DebugGraphDetails::ALL, + &format!("ssrc-added-{}", ssrc), + ); + + Ok::<_, anyhow::Error>(()) + } + else { + Ok(()) + } + }; + if let Err(e) = f() { + error!("handling pad-added: {:?}", e); + } + None + })?; + + let audio_sink_element = gstreamer::ElementFactory::make("rtpopuspay", None)?; + audio_sink_element.set_property( + "pt", + opus_payload_type.context("no opus payload type in jingle session-initiate")? as u32, + )?; + audio_sink_element.set_property("min-ptime", 10i64 * 1000 * 1000)?; + audio_sink_element.set_property("ssrc", audio_ssrc)?; + pipeline.add(&audio_sink_element)?; + audio_sink_element.link_pads(None, &rtpbin, Some("send_rtp_sink_0"))?; + + let video_sink_element = match conference.config.video_codec.as_str() { + "h264" => { + let element = gstreamer::ElementFactory::make("rtph264pay", None)?; + element.set_property( + "pt", + h264_payload_type.context("no h264 payload type in jingle session-initiate")? as u32, + )?; + element.set_property_from_str("aggregate-mode", "zero-latency"); + element + }, + "vp8" => { + let element = gstreamer::ElementFactory::make("rtpvp8pay", None)?; + element.set_property( + "pt", + vp8_payload_type.context("no vp8 payload type in jingle session-initiate")? as u32, + )?; + element.set_property_from_str("picture-id-mode", "15-bit"); + element + }, + "vp9" => { + let element = gstreamer::ElementFactory::make("rtpvp9pay", None)?; + element.set_property( + "pt", + vp9_payload_type.context("no vp9 payload type in jingle session-initiate")? as u32, + )?; + element.set_property_from_str("picture-id-mode", "15-bit"); + element + }, + other => bail!("unsupported video codec: {}", other), + }; + video_sink_element.set_property("ssrc", video_ssrc)?; + pipeline.add(&video_sink_element)?; + video_sink_element.link_pads(None, &rtpbin, Some("send_rtp_sink_1"))?; + + let dtlssrtpdec = pipeline + .by_name("dtlssrtpdec") + .context("no dtlssrtpdec in pipeline")?; + dtlssrtpdec.set_property( + "pem", + format!("{}\n{}", dtls_cert_pem, dtls_private_key_pem), + )?; + + let nicesrc = pipeline + .by_name("nicesrc") + .context("no nicesrc in pipeline")?; + nicesrc.set_property("agent", &ice_agent)?; + + let nicesink = pipeline + .by_name("nicesink") + .context("no nicesink in pipeline")?; + nicesink.set_property("agent", &ice_agent)?; + + let bus = pipeline.bus().context("failed to get pipeline bus")?; + + let (pipeline_state_null_tx, pipeline_state_null_rx) = oneshot::channel(); + tokio::spawn(async move { + let mut stream = bus.stream(); + while let Some(msg) = stream.next().await { + match msg.view() { + gstreamer::MessageView::Error(e) => { + if let Some(d) = e.debug() { + error!("{}", d); + } + }, + gstreamer::MessageView::Warning(e) => { + if let Some(d) = e.debug() { + warn!("{}", d); + } + }, + gstreamer::MessageView::StateChanged(state) + if state.current() == gstreamer::State::Null => + { + debug!("pipeline state is null"); + pipeline_state_null_tx.send(()).unwrap(); + break; + } + _ => {}, + } + } + }); + + gstreamer::debug_bin_to_dot_file( + &pipeline, + gstreamer::DebugGraphDetails::ALL, + "session-initiate", + ); + + let local_candidates = ice_agent.local_candidates(ice_stream_id, ice_component_id); + debug!("local candidates: {:?}", local_candidates); + + debug!("building Jingle session-accept"); + let mut jingle_accept = Jingle::new(Action::SessionAccept, jingle.sid.clone()) + .with_initiator( + jingle + .initiator + .as_ref() + .context("jingle session-initiate with no initiator")? + .clone(), + ) + .with_responder(Jid::Full(conference.jid.clone())); + + for initiate_content in &jingle.contents { + let mut description = RtpDescription::new(initiate_content.name.0.clone()); + + description.payload_types = if initiate_content.name.0 == "audio" { + vec![PayloadType::new( + opus_payload_type.context("no opus payload type in jingle session-initiate")?, + "opus".to_owned(), + 48000, + 2, + )] + } + else { + match conference.config.video_codec.as_str() { + "h264" => vec![PayloadType::new( + h264_payload_type.context("no h264 payload type in jingle session-initiate")?, + "H264".to_owned(), + 90000, + 1, + )], + "vp8" => vec![PayloadType::new( + vp8_payload_type.context("no vp8 payload type in jingle session-initiate")?, + "VP8".to_owned(), + 90000, + 1, + )], + "vp9" => vec![PayloadType::new( + vp9_payload_type.context("no vp9 payload type in jingle session-initiate")?, + "VP9".to_owned(), + 90000, + 1, + )], + other => bail!("unsupported video codec: {}", other), + } + }; + + description.rtcp_mux = Some(RtcpMux); + + let mslabel = Uuid::new_v4().to_string(); + let label = Uuid::new_v4().to_string(); + let cname = Uuid::new_v4().to_string(); + + let mut ssrc = jingle_ssma::Source::new(if initiate_content.name.0 == "audio" { + audio_ssrc.to_string() + } + else { + video_ssrc.to_string() + }); + ssrc.parameters.push(Parameter { + name: "cname".to_owned(), + value: Some(cname), + }); + ssrc.parameters.push(Parameter { + name: "msid".to_owned(), + value: Some(format!("{} {}", mslabel, label)), + }); + ssrc.parameters.push(Parameter { + name: "mslabel".to_owned(), + value: Some(mslabel), + }); + ssrc.parameters.push(Parameter { + name: "label".to_owned(), + value: Some(label), + }); + description.ssrcs = vec![ssrc]; + + let mut transport = IceUdpTransport::new().with_fingerprint(Fingerprint { + hash: Algo::Sha_256, + setup: Some(Setup::Active), + value: fingerprint.clone(), + required: Some(true.to_string()), + }); + transport.ufrag = Some(ice_local_ufrag.clone()); + transport.pwd = Some(ice_local_pwd.clone()); + transport.candidates = vec![]; + for c in &local_candidates { + match c.transport() { + nice::CandidateTransport::Udp => { + let addr = c.addr(); + let foundation = c.foundation()?; + transport.candidates.push(jingle_ice_udp::Candidate { + component: c.component_id() as u8, + foundation: foundation.to_owned(), + generation: 0, + id: Uuid::new_v4().to_string(), + ip: addr.ip(), + port: addr.port(), + priority: c.priority(), + protocol: "udp".to_owned(), + type_: match c.type_() { + nice::CandidateType::Host => jingle_ice_udp::Type::Host, + nice::CandidateType::PeerReflexive => jingle_ice_udp::Type::Prflx, + nice::CandidateType::ServerReflexive => jingle_ice_udp::Type::Srflx, + nice::CandidateType::Relayed => jingle_ice_udp::Type::Relay, + other => bail!("unsupported candidate type: {:?}", other), + }, + rel_addr: None, + rel_port: None, + network: None, + }); + }, + other => { + warn!("skipping unsupported ICE transport: {:?}", other); + }, + } + } + + jingle_accept = jingle_accept.add_content( + Content::new(Creator::Responder, initiate_content.name.clone()) + .with_senders(Senders::Both) + .with_description(description) + .with_transport(transport), + ); + } + + let accept_iq_id = generate_id(); + let session_accept_iq = Iq::from_set(accept_iq_id.clone(), jingle_accept) + .with_to(Jid::Full(conference.focus_jid_in_muc()?)) + .with_from(Jid::Full(conference.jid.clone())); + conference.xmpp_tx.send(session_accept_iq.into()).await?; + + Ok(Self { + pipeline, + audio_sink_element, + video_sink_element, + remote_ssrc_map, + ice_agent, + ice_stream_id, + ice_component_id, + accept_iq_id: Some(accept_iq_id), + colibri_url, + colibri_tx: None, + pipeline_state_null_rx, + }) + } + + pub(crate) async fn source_add(&mut self, jingle: Jingle) -> Result<()> { + for content in &jingle.contents { + if let Some(Description::Rtp(description)) = &content.description { + for ssrc in &description.ssrcs { + let owner = ssrc + .info + .as_ref() + .context("missing ssrc-info")? + .owner + .clone(); + if owner == "jvb" { + debug!("skipping ssrc (owner = jvb)"); + continue; + } + + debug!("adding ssrc to remote_ssrc_map: {:?}", ssrc); + self.remote_ssrc_map.insert( + ssrc.id.parse()?, + Source { + ssrc: ssrc.id.parse()?, + participant_id: owner + .split('/') + .nth(1) + .context("invalid ssrc-info owner")? + .to_owned(), + media_type: if description.media == "audio" { + MediaType::Audio + } + else { + MediaType::Video + }, + }, + ); + } + } + } + Ok(()) + } +} diff --git a/lib-gst-meet/src/lib.rs b/lib-gst-meet/src/lib.rs new file mode 100644 index 0000000..c123912 --- /dev/null +++ b/lib-gst-meet/src/lib.rs @@ -0,0 +1,14 @@ +pub mod conference; +pub mod connection; +mod jingle; +mod xmpp; +mod pinger; +pub mod source; +mod stanza_filter; +mod util; + +pub use crate::{ + conference::{JitsiConference, JitsiConferenceConfig, Participant}, + connection::JitsiConnection, + source::{MediaType, Source}, +}; diff --git a/lib-gst-meet/src/pinger.rs b/lib-gst-meet/src/pinger.rs new file mode 100644 index 0000000..8e1e3d3 --- /dev/null +++ b/lib-gst-meet/src/pinger.rs @@ -0,0 +1,34 @@ +use std::convert::TryFrom; + +use anyhow::{anyhow, Result}; +use async_trait::async_trait; +use tokio::sync::mpsc; +use xmpp_parsers::{iq::Iq, Element, FullJid, Jid}; + +use crate::stanza_filter::StanzaFilter; + +#[derive(Debug)] +pub(crate) struct Pinger { + pub(crate) jid: FullJid, + pub(crate) tx: mpsc::Sender, +} + +#[async_trait] +impl StanzaFilter for Pinger { + #[tracing::instrument(level = "trace")] + fn filter(&self, element: &Element) -> bool { + element.is("iq", "jabber:client") && element.has_child("ping", "urn:xmpp:ping") + } + + #[tracing::instrument(level = "trace", err)] + async fn take(&self, element: Element) -> Result<()> { + let iq = Iq::try_from(element)?; + let result_iq = Iq::empty_result( + iq.from.ok_or_else(|| anyhow!("iq missing from"))?, + iq.id.clone(), + ) + .with_from(Jid::Full(self.jid.clone())); + self.tx.send(result_iq.into()).await?; + Ok(()) + } +} diff --git a/lib-gst-meet/src/source.rs b/lib-gst-meet/src/source.rs new file mode 100644 index 0000000..86ffba0 --- /dev/null +++ b/lib-gst-meet/src/source.rs @@ -0,0 +1,21 @@ +#[derive(Debug, Clone)] +pub struct Source { + pub(crate) ssrc: u32, + pub participant_id: String, + pub media_type: MediaType, +} + +#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] +pub enum MediaType { + Video, + Audio, +} + +impl MediaType { + pub(crate) fn jitsi_muted_presence_element_name(&self) -> &'static str { + match self { + MediaType::Video => "videomuted", + MediaType::Audio => "audiomuted", + } + } +} diff --git a/lib-gst-meet/src/stanza_filter.rs b/lib-gst-meet/src/stanza_filter.rs new file mode 100644 index 0000000..c66d6bd --- /dev/null +++ b/lib-gst-meet/src/stanza_filter.rs @@ -0,0 +1,9 @@ +use anyhow::Result; +use async_trait::async_trait; +use xmpp_parsers::Element; + +#[async_trait] +pub(crate) trait StanzaFilter { + fn filter(&self, element: &Element) -> bool; + async fn take(&self, element: Element) -> Result<()>; +} diff --git a/lib-gst-meet/src/util.rs b/lib-gst-meet/src/util.rs new file mode 100644 index 0000000..c27a97c --- /dev/null +++ b/lib-gst-meet/src/util.rs @@ -0,0 +1,20 @@ +use std::collections::hash_map::Entry; + +use uuid::Uuid; + +pub(crate) fn generate_id() -> String { + Uuid::new_v4().to_string() +} + +pub(crate) trait FallibleEntry<'a, V> { + fn or_try_insert_with Result>(self, default: F) -> Result<&'a mut V, E>; +} + +impl<'a, K, V> FallibleEntry<'a, V> for Entry<'a, K, V> { + fn or_try_insert_with Result>(self, default: F) -> Result<&'a mut V, E> { + Ok(match self { + Entry::Occupied(entry) => entry.into_mut(), + Entry::Vacant(entry) => entry.insert(default()?), + }) + } +} diff --git a/lib-gst-meet/src/xmpp/extdisco.rs b/lib-gst-meet/src/xmpp/extdisco.rs new file mode 100644 index 0000000..36e318b --- /dev/null +++ b/lib-gst-meet/src/xmpp/extdisco.rs @@ -0,0 +1,73 @@ +use std::convert::TryFrom; + +use anyhow::{bail, Context, Result}; +use xmpp_parsers::{ + Element, + iq::IqGetPayload, +}; + +use crate::xmpp::ns; + +#[derive(Debug)] +pub(crate) struct ServicesQuery {} + +impl TryFrom for ServicesQuery { + type Error = anyhow::Error; + + fn try_from(_elem: Element) -> Result { + unimplemented!() + } +} + +impl From for Element { + fn from(_services: ServicesQuery) -> Element { + let builder = Element::builder("services", ns::EXTDISCO); + builder.build() + } +} + +impl IqGetPayload for ServicesQuery {} + +#[derive(Debug, Clone)] +pub(crate) struct Service { + pub(crate) r#type: String, + pub(crate) name: Option, + pub(crate) host: String, + pub(crate) port: Option, + pub(crate) transport: Option, + pub(crate) restricted: Option, + pub(crate) username: Option, + pub(crate) password: Option, + pub(crate) expires: Option, +} + +#[derive(Debug)] +pub(crate) struct ServicesResult { + pub(crate) services: Vec, +} + +impl TryFrom for ServicesResult { + type Error = anyhow::Error; + + fn try_from(elem: Element) -> Result { + if !elem.is("services", ns::EXTDISCO) { + bail!("not a services element"); + } + Ok(ServicesResult { + services: elem + .children() + .map(|child| Ok(Service { + r#type: child.attr("type").context("missing type attr")?.to_owned(), + name: child.attr("name").map(ToOwned::to_owned), + host: child.attr("host").context("missing host attr")?.to_owned(), + port: child.attr("port").map(|p| p.parse()).transpose()?, + transport: child.attr("transport").map(ToOwned::to_owned), + restricted: child.attr("restricted").map(|b| b.to_lowercase() == "parse" || b == "1"), + username: child.attr("username").map(ToOwned::to_owned), + password: child.attr("password").map(ToOwned::to_owned), + expires: child.attr("expires").map(ToOwned::to_owned), + })) + .collect::>()?, + }) + } +} \ No newline at end of file diff --git a/lib-gst-meet/src/xmpp/jitsi.rs b/lib-gst-meet/src/xmpp/jitsi.rs new file mode 100644 index 0000000..8aae939 --- /dev/null +++ b/lib-gst-meet/src/xmpp/jitsi.rs @@ -0,0 +1,39 @@ +use std::{collections::HashMap, convert::TryFrom}; + +use anyhow::Result; +use xmpp_parsers::{iq::IqSetPayload, Element}; + +use crate::xmpp::ns; + +pub(crate) struct Conference { + pub(crate) machine_uid: String, + pub(crate) room: String, + pub(crate) properties: HashMap, +} + +impl IqSetPayload for Conference {} + +impl TryFrom for Conference { + type Error = anyhow::Error; + + fn try_from(_element: Element) -> Result { + unimplemented!() + } +} + +impl From for Element { + fn from(conference: Conference) -> Element { + let mut builder = Element::builder("conference", ns::JITSI_FOCUS) + .attr("machine-uid", conference.machine_uid) + .attr("room", conference.room); + for (name, value) in conference.properties { + builder = builder.append( + Element::builder("property", ns::JITSI_FOCUS) + .attr("name", name) + .attr("value", value) + .build(), + ); + } + builder.build() + } +} diff --git a/lib-gst-meet/src/xmpp/mod.rs b/lib-gst-meet/src/xmpp/mod.rs new file mode 100644 index 0000000..d690867 --- /dev/null +++ b/lib-gst-meet/src/xmpp/mod.rs @@ -0,0 +1,3 @@ +pub(crate) mod extdisco; +pub(crate) mod jitsi; +mod ns; \ No newline at end of file diff --git a/lib-gst-meet/src/xmpp/ns.rs b/lib-gst-meet/src/xmpp/ns.rs new file mode 100644 index 0000000..a8828bf --- /dev/null +++ b/lib-gst-meet/src/xmpp/ns.rs @@ -0,0 +1,4 @@ +/// XEP-0215: External Service Discovery +pub(crate) const EXTDISCO: &str = "urn:xmpp:extdisco:2"; + +pub(crate) const JITSI_FOCUS: &str = "http://jitsi.org/protocol/focus"; \ No newline at end of file diff --git a/nice-gst-meet-sys/Cargo.toml b/nice-gst-meet-sys/Cargo.toml new file mode 100644 index 0000000..a2f8ac3 --- /dev/null +++ b/nice-gst-meet-sys/Cargo.toml @@ -0,0 +1,73 @@ +[package] +name = "nice-gst-meet-sys" +version = "0.1.0" +links = "nice" +edition = "2018" +build = "build.rs" +license = "MIT/Apache-2.0" +authors = ["Jasper Hugo "] + +[package.metadata.system-deps.nice] +name = "nice" +version = "0.1" + +[package.metadata.system-deps.nice.v0_1_4] +version = "0.1.4" + +[package.metadata.system-deps.nice.v0_1_5] +version = "0.1.5" + +[package.metadata.system-deps.nice.v0_1_6] +version = "0.1.6" + +[package.metadata.system-deps.nice.v0_1_8] +version = "0.1.8" + +[package.metadata.system-deps.nice.v0_1_14] +version = "0.1.14" + +[package.metadata.system-deps.nice.v0_1_15] +version = "0.1.15" + +[package.metadata.system-deps.nice.v0_1_16] +version = "0.1.16" + +[package.metadata.system-deps.nice.v0_1_17] +version = "0.1.17" + +[package.metadata.system-deps.nice.v0_1_18] +version = "0.1.18" + +[package.metadata.system-deps.nice.v0_1_20] +version = "0.1.20" + +[package.metadata.docs.rs] +features = ["dox"] + +[lib] +name = "nice_sys" + +[dependencies] +libc = { version = "0.2", default-features = false } +glib = { version = "0.14", default-features = false } +gio = { version = "0.14", default-features = false } + +[build-dependencies] +system-deps = { version = "3", default-features = false } + +[dev-dependencies] +shell-words = { version = "1", default-features = false } +tempfile = { version = "3", default-features = false } + +[features] +v0_1_4 = [] +v0_1_5 = ["v0_1_4"] +v0_1_6 = ["v0_1_5"] +v0_1_8 = ["v0_1_6"] +v0_1_14 = ["v0_1_8"] +v0_1_15 = ["v0_1_14"] +v0_1_16 = ["v0_1_15"] +v0_1_17 = ["v0_1_16"] +v0_1_18 = ["v0_1_17"] +v0_1_20 = ["v0_1_18"] +dox = [] diff --git a/nice-gst-meet-sys/Gir.toml b/nice-gst-meet-sys/Gir.toml new file mode 100644 index 0000000..0611a9b --- /dev/null +++ b/nice-gst-meet-sys/Gir.toml @@ -0,0 +1,8 @@ +[options] +library = "Nice" +version = "0.1" +min_cfg_version = "0.1" +work_mode = "sys" +target_path = "." +external_libraries = [] + diff --git a/nice-gst-meet-sys/LICENSE-APACHE b/nice-gst-meet-sys/LICENSE-APACHE new file mode 100644 index 0000000..f8e5e5e --- /dev/null +++ b/nice-gst-meet-sys/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. \ No newline at end of file diff --git a/nice-gst-meet-sys/LICENSE-MIT b/nice-gst-meet-sys/LICENSE-MIT new file mode 100644 index 0000000..468cd79 --- /dev/null +++ b/nice-gst-meet-sys/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/nice-gst-meet-sys/build.rs b/nice-gst-meet-sys/build.rs new file mode 100644 index 0000000..db0435f --- /dev/null +++ b/nice-gst-meet-sys/build.rs @@ -0,0 +1,17 @@ +// Generated by gir (https://github.com/gtk-rs/gir @ 5bbf6cb) +// from ../../gir-files (@ 8e47c67) +// DO NOT EDIT + +#[cfg(not(feature = "dox"))] +use std::process; + +#[cfg(feature = "dox")] +fn main() {} // prevent linking libraries to avoid documentation failure + +#[cfg(not(feature = "dox"))] +fn main() { + if let Err(s) = system_deps::Config::new().probe() { + println!("cargo:warning={}", s); + process::exit(1); + } +} diff --git a/nice-gst-meet-sys/src/lib.rs b/nice-gst-meet-sys/src/lib.rs new file mode 100644 index 0000000..4290d22 --- /dev/null +++ b/nice-gst-meet-sys/src/lib.rs @@ -0,0 +1,683 @@ +// Generated by gir (https://github.com/gtk-rs/gir @ 5bbf6cb) +// from ../../gir-files (@ 8e47c67) +// DO NOT EDIT + +#![allow(non_camel_case_types, non_upper_case_globals, non_snake_case)] +#![allow( + clippy::approx_constant, + clippy::type_complexity, + clippy::unreadable_literal, + clippy::upper_case_acronyms +)] +#![cfg_attr(feature = "dox", feature(doc_cfg))] + +#[allow(unused_imports)] +use glib::ffi::{gboolean, gconstpointer, gpointer, GType}; +#[allow(unused_imports)] +use libc::{ + c_char, c_double, c_float, c_int, c_long, c_short, c_uchar, c_uint, c_ulong, c_ushort, c_void, + intptr_t, size_t, sockaddr, sockaddr_in, sockaddr_in6, ssize_t, time_t, uintptr_t, FILE, +}; + +// Enums +pub type NiceCandidateTransport = c_int; +pub const NICE_CANDIDATE_TRANSPORT_UDP: NiceCandidateTransport = 0; +pub const NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE: NiceCandidateTransport = 1; +pub const NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE: NiceCandidateTransport = 2; +pub const NICE_CANDIDATE_TRANSPORT_TCP_SO: NiceCandidateTransport = 3; + +pub type NiceCandidateType = c_int; +pub const NICE_CANDIDATE_TYPE_HOST: NiceCandidateType = 0; +pub const NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE: NiceCandidateType = 1; +pub const NICE_CANDIDATE_TYPE_PEER_REFLEXIVE: NiceCandidateType = 2; +pub const NICE_CANDIDATE_TYPE_RELAYED: NiceCandidateType = 3; + +pub type NiceCompatibility = c_int; +pub const NICE_COMPATIBILITY_RFC5245: NiceCompatibility = 0; +pub const NICE_COMPATIBILITY_DRAFT19: NiceCompatibility = 0; +pub const NICE_COMPATIBILITY_GOOGLE: NiceCompatibility = 1; +pub const NICE_COMPATIBILITY_MSN: NiceCompatibility = 2; +pub const NICE_COMPATIBILITY_WLM2009: NiceCompatibility = 3; +pub const NICE_COMPATIBILITY_OC2007: NiceCompatibility = 4; +pub const NICE_COMPATIBILITY_OC2007R2: NiceCompatibility = 5; +pub const NICE_COMPATIBILITY_LAST: NiceCompatibility = 5; + +pub type NiceComponentState = c_int; +pub const NICE_COMPONENT_STATE_DISCONNECTED: NiceComponentState = 0; +pub const NICE_COMPONENT_STATE_GATHERING: NiceComponentState = 1; +pub const NICE_COMPONENT_STATE_CONNECTING: NiceComponentState = 2; +pub const NICE_COMPONENT_STATE_CONNECTED: NiceComponentState = 3; +pub const NICE_COMPONENT_STATE_READY: NiceComponentState = 4; +pub const NICE_COMPONENT_STATE_FAILED: NiceComponentState = 5; +pub const NICE_COMPONENT_STATE_LAST: NiceComponentState = 6; + +pub type NiceComponentType = c_int; +pub const NICE_COMPONENT_TYPE_RTP: NiceComponentType = 1; +pub const NICE_COMPONENT_TYPE_RTCP: NiceComponentType = 2; + +pub type NiceNominationMode = c_int; +pub const NICE_NOMINATION_MODE_REGULAR: NiceNominationMode = 0; +pub const NICE_NOMINATION_MODE_AGGRESSIVE: NiceNominationMode = 1; + +pub type NiceProxyType = c_int; +pub const NICE_PROXY_TYPE_NONE: NiceProxyType = 0; +pub const NICE_PROXY_TYPE_SOCKS5: NiceProxyType = 1; +pub const NICE_PROXY_TYPE_HTTP: NiceProxyType = 2; +pub const NICE_PROXY_TYPE_LAST: NiceProxyType = 2; + +pub type NiceRelayType = c_int; +pub const NICE_RELAY_TYPE_TURN_UDP: NiceRelayType = 0; +pub const NICE_RELAY_TYPE_TURN_TCP: NiceRelayType = 1; +pub const NICE_RELAY_TYPE_TURN_TLS: NiceRelayType = 2; + +pub type PseudoTcpDebugLevel = c_int; +pub const PSEUDO_TCP_DEBUG_NONE: PseudoTcpDebugLevel = 0; +pub const PSEUDO_TCP_DEBUG_NORMAL: PseudoTcpDebugLevel = 1; +pub const PSEUDO_TCP_DEBUG_VERBOSE: PseudoTcpDebugLevel = 2; + +pub type PseudoTcpShutdown = c_int; +pub const PSEUDO_TCP_SHUTDOWN_RD: PseudoTcpShutdown = 0; +pub const PSEUDO_TCP_SHUTDOWN_WR: PseudoTcpShutdown = 1; +pub const PSEUDO_TCP_SHUTDOWN_RDWR: PseudoTcpShutdown = 2; + +pub type PseudoTcpState = c_int; +pub const PSEUDO_TCP_LISTEN: PseudoTcpState = 0; +pub const PSEUDO_TCP_SYN_SENT: PseudoTcpState = 1; +pub const PSEUDO_TCP_SYN_RECEIVED: PseudoTcpState = 2; +pub const PSEUDO_TCP_ESTABLISHED: PseudoTcpState = 3; +pub const PSEUDO_TCP_CLOSED: PseudoTcpState = 4; +pub const PSEUDO_TCP_FIN_WAIT_1: PseudoTcpState = 5; +pub const PSEUDO_TCP_FIN_WAIT_2: PseudoTcpState = 6; +pub const PSEUDO_TCP_CLOSING: PseudoTcpState = 7; +pub const PSEUDO_TCP_TIME_WAIT: PseudoTcpState = 8; +pub const PSEUDO_TCP_CLOSE_WAIT: PseudoTcpState = 9; +pub const PSEUDO_TCP_LAST_ACK: PseudoTcpState = 10; + +pub type PseudoTcpWriteResult = c_int; +pub const WR_SUCCESS: PseudoTcpWriteResult = 0; +pub const WR_TOO_LARGE: PseudoTcpWriteResult = 1; +pub const WR_FAIL: PseudoTcpWriteResult = 2; + +// Constants +pub const NICE_AGENT_MAX_REMOTE_CANDIDATES: c_int = 25; +pub const NICE_CANDIDATE_MAX_FOUNDATION: c_int = 33; +pub const NICE_CANDIDATE_MAX_LOCAL_ADDRESSES: c_int = 64; +pub const NICE_CANDIDATE_MAX_TURN_SERVERS: c_int = 8; + +// Flags +pub type NiceAgentOption = c_uint; +pub const NICE_AGENT_OPTION_REGULAR_NOMINATION: NiceAgentOption = 1; +pub const NICE_AGENT_OPTION_RELIABLE: NiceAgentOption = 2; +pub const NICE_AGENT_OPTION_LITE_MODE: NiceAgentOption = 4; +pub const NICE_AGENT_OPTION_ICE_TRICKLE: NiceAgentOption = 8; +pub const NICE_AGENT_OPTION_SUPPORT_RENOMINATION: NiceAgentOption = 16; +pub const NICE_AGENT_OPTION_CONSENT_FRESHNESS: NiceAgentOption = 32; + +// Unions +#[repr(C)] +#[derive(Copy, Clone)] +pub union NiceAddress_s { + pub addr: sockaddr, + pub ip4: sockaddr_in, + pub ip6: sockaddr_in6, +} + +impl ::std::fmt::Debug for NiceAddress_s { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + f.debug_struct(&format!("NiceAddress_s @ {:p}", self)) + .finish() + } +} + +// Callbacks +pub type NiceAgentRecvFunc = + Option; + +// Records +#[repr(C)] +#[derive(Copy, Clone)] +pub struct NiceAddress { + pub s: NiceAddress_s, +} + +impl ::std::fmt::Debug for NiceAddress { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + f.debug_struct(&format!("NiceAddress @ {:p}", self)) + .field("s", &self.s) + .finish() + } +} + +#[repr(C)] +#[derive(Copy, Clone)] +pub struct NiceAgentClass { + pub parent_class: glib::object::GObjectClass, +} + +impl ::std::fmt::Debug for NiceAgentClass { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + f.debug_struct(&format!("NiceAgentClass @ {:p}", self)) + .field("parent_class", &self.parent_class) + .finish() + } +} + +#[repr(C)] +#[derive(Copy, Clone)] +pub struct NiceCandidate { + pub type_: NiceCandidateType, + pub transport: NiceCandidateTransport, + pub addr: NiceAddress, + pub base_addr: NiceAddress, + pub priority: u32, + pub stream_id: c_uint, + pub component_id: c_uint, + pub foundation: [c_char; 33], + pub username: *mut c_char, + pub password: *mut c_char, +} + +impl ::std::fmt::Debug for NiceCandidate { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + f.debug_struct(&format!("NiceCandidate @ {:p}", self)) + .field("type_", &self.type_) + .field("transport", &self.transport) + .field("addr", &self.addr) + .field("base_addr", &self.base_addr) + .field("priority", &self.priority) + .field("stream_id", &self.stream_id) + .field("component_id", &self.component_id) + .field("username", &self.username) + .field("password", &self.password) + .finish() + } +} + +#[repr(C)] +#[derive(Copy, Clone)] +pub struct NiceInputMessage { + pub buffers: *mut gio::ffi::GInputVector, + pub n_buffers: c_int, + pub from: *mut NiceAddress, + pub length: size_t, +} + +impl ::std::fmt::Debug for NiceInputMessage { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + f.debug_struct(&format!("NiceInputMessage @ {:p}", self)) + .field("buffers", &self.buffers) + .field("n_buffers", &self.n_buffers) + .field("from", &self.from) + .field("length", &self.length) + .finish() + } +} + +#[repr(C)] +#[derive(Copy, Clone)] +pub struct NiceOutputMessage { + pub buffers: *mut gio::ffi::GOutputVector, + pub n_buffers: c_int, +} + +impl ::std::fmt::Debug for NiceOutputMessage { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + f.debug_struct(&format!("NiceOutputMessage @ {:p}", self)) + .field("buffers", &self.buffers) + .field("n_buffers", &self.n_buffers) + .finish() + } +} + +#[repr(C)] +#[derive(Copy, Clone)] +pub struct PseudoTcpCallbacks { + pub user_data: gpointer, + pub PseudoTcpOpened: Option, + pub PseudoTcpReadable: Option, + pub PseudoTcpWritable: Option, + pub PseudoTcpClosed: Option, + pub WritePacket: Option< + unsafe extern "C" fn( + *mut PseudoTcpSocket, + *const c_char, + u32, + gpointer, + ) -> PseudoTcpWriteResult, + >, +} + +impl ::std::fmt::Debug for PseudoTcpCallbacks { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + f.debug_struct(&format!("PseudoTcpCallbacks @ {:p}", self)) + .field("user_data", &self.user_data) + .field("PseudoTcpOpened", &self.PseudoTcpOpened) + .field("PseudoTcpReadable", &self.PseudoTcpReadable) + .field("PseudoTcpWritable", &self.PseudoTcpWritable) + .field("PseudoTcpClosed", &self.PseudoTcpClosed) + .field("WritePacket", &self.WritePacket) + .finish() + } +} + +#[repr(C)] +pub struct _PseudoTcpSocketClass(c_void); + +pub type PseudoTcpSocketClass = *mut _PseudoTcpSocketClass; + +// Classes +#[repr(C)] +pub struct NiceAgent(c_void); + +impl ::std::fmt::Debug for NiceAgent { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + f.debug_struct(&format!("NiceAgent @ {:p}", self)).finish() + } +} + +#[repr(C)] +pub struct PseudoTcpSocket(c_void); + +impl ::std::fmt::Debug for PseudoTcpSocket { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + f.debug_struct(&format!("PseudoTcpSocket @ {:p}", self)) + .finish() + } +} + +#[link(name = "nice")] +extern "C" { + + //========================================================================= + // NiceAddress + //========================================================================= + pub fn nice_address_copy_to_sockaddr(addr: *const NiceAddress, sin: *mut sockaddr); + pub fn nice_address_dup(addr: *const NiceAddress) -> *mut NiceAddress; + pub fn nice_address_equal(a: *const NiceAddress, b: *const NiceAddress) -> gboolean; + #[cfg(any(feature = "v0_1_8", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_8")))] + pub fn nice_address_equal_no_port(a: *const NiceAddress, b: *const NiceAddress) -> gboolean; + pub fn nice_address_free(addr: *mut NiceAddress); + pub fn nice_address_get_port(addr: *const NiceAddress) -> c_uint; + pub fn nice_address_init(addr: *mut NiceAddress); + pub fn nice_address_ip_version(addr: *const NiceAddress) -> c_int; + pub fn nice_address_is_private(addr: *const NiceAddress) -> gboolean; + pub fn nice_address_is_valid(addr: *const NiceAddress) -> gboolean; + pub fn nice_address_set_from_sockaddr(addr: *mut NiceAddress, sin: *const sockaddr); + pub fn nice_address_set_from_string(addr: *mut NiceAddress, str: *const c_char) -> gboolean; + pub fn nice_address_set_ipv4(addr: *mut NiceAddress, addr_ipv4: u32); + pub fn nice_address_set_ipv6(addr: *mut NiceAddress, addr_ipv6: *const u8); + pub fn nice_address_set_port(addr: *mut NiceAddress, port: c_uint); + pub fn nice_address_to_string(addr: *const NiceAddress, dst: *mut c_char); + pub fn nice_address_new() -> *mut NiceAddress; + + //========================================================================= + // NiceCandidate + //========================================================================= + pub fn nice_candidate_get_type() -> GType; + pub fn nice_candidate_new(type_: NiceCandidateType) -> *mut NiceCandidate; + pub fn nice_candidate_copy(candidate: *const NiceCandidate) -> *mut NiceCandidate; + #[cfg(any(feature = "v0_1_15", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_15")))] + pub fn nice_candidate_equal_target( + candidate1: *const NiceCandidate, + candidate2: *const NiceCandidate, + ) -> gboolean; + pub fn nice_candidate_free(candidate: *mut NiceCandidate); + #[cfg(any(feature = "v0_1_18", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_18")))] + pub fn nice_candidate_transport_to_string(transport: NiceCandidateTransport) -> *const c_char; + #[cfg(any(feature = "v0_1_18", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_18")))] + pub fn nice_candidate_type_to_string(type_: NiceCandidateType) -> *const c_char; + + //========================================================================= + // NiceAgent + //========================================================================= + pub fn nice_agent_get_type() -> GType; + pub fn nice_agent_new( + ctx: *mut glib::ffi::GMainContext, + compat: NiceCompatibility, + ) -> *mut NiceAgent; + #[cfg(any(feature = "v0_1_15", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_15")))] + pub fn nice_agent_new_full( + ctx: *mut glib::ffi::GMainContext, + compat: NiceCompatibility, + flags: NiceAgentOption, + ) -> *mut NiceAgent; + pub fn nice_agent_new_reliable( + ctx: *mut glib::ffi::GMainContext, + compat: NiceCompatibility, + ) -> *mut NiceAgent; + pub fn nice_agent_add_local_address(agent: *mut NiceAgent, addr: *mut NiceAddress) -> gboolean; + pub fn nice_agent_add_stream(agent: *mut NiceAgent, n_components: c_uint) -> c_uint; + pub fn nice_agent_attach_recv( + agent: *mut NiceAgent, + stream_id: c_uint, + component_id: c_uint, + ctx: *mut glib::ffi::GMainContext, + func: NiceAgentRecvFunc, + data: gpointer, + ) -> gboolean; + #[cfg(any(feature = "v0_1_16", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_16")))] + pub fn nice_agent_close_async( + agent: *mut NiceAgent, + callback: gio::ffi::GAsyncReadyCallback, + callback_data: gpointer, + ); + #[cfg(any(feature = "v0_1_20", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_20")))] + pub fn nice_agent_consent_lost( + agent: *mut NiceAgent, + stream_id: c_uint, + component_id: c_uint, + ) -> gboolean; + #[cfg(any(feature = "v0_1_6", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_6")))] + pub fn nice_agent_forget_relays( + agent: *mut NiceAgent, + stream_id: c_uint, + component_id: c_uint, + ) -> gboolean; + pub fn nice_agent_gather_candidates(agent: *mut NiceAgent, stream_id: c_uint) -> gboolean; + #[cfg(any(feature = "v0_1_4", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_4")))] + pub fn nice_agent_generate_local_candidate_sdp( + agent: *mut NiceAgent, + candidate: *mut NiceCandidate, + ) -> *mut c_char; + #[cfg(any(feature = "v0_1_4", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_4")))] + pub fn nice_agent_generate_local_sdp(agent: *mut NiceAgent) -> *mut c_char; + #[cfg(any(feature = "v0_1_4", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_4")))] + pub fn nice_agent_generate_local_stream_sdp( + agent: *mut NiceAgent, + stream_id: c_uint, + include_non_ice: gboolean, + ) -> *mut c_char; + #[cfg(any(feature = "v0_1_8", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_8")))] + pub fn nice_agent_get_component_state( + agent: *mut NiceAgent, + stream_id: c_uint, + component_id: c_uint, + ) -> NiceComponentState; + pub fn nice_agent_get_default_local_candidate( + agent: *mut NiceAgent, + stream_id: c_uint, + component_id: c_uint, + ) -> *mut NiceCandidate; + #[cfg(any(feature = "v0_1_5", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_5")))] + pub fn nice_agent_get_io_stream( + agent: *mut NiceAgent, + stream_id: c_uint, + component_id: c_uint, + ) -> *mut gio::ffi::GIOStream; + pub fn nice_agent_get_local_candidates( + agent: *mut NiceAgent, + stream_id: c_uint, + component_id: c_uint, + ) -> *mut glib::ffi::GSList; + pub fn nice_agent_get_local_credentials( + agent: *mut NiceAgent, + stream_id: c_uint, + ufrag: *mut *mut c_char, + pwd: *mut *mut c_char, + ) -> gboolean; + pub fn nice_agent_get_remote_candidates( + agent: *mut NiceAgent, + stream_id: c_uint, + component_id: c_uint, + ) -> *mut glib::ffi::GSList; + pub fn nice_agent_get_selected_pair( + agent: *mut NiceAgent, + stream_id: c_uint, + component_id: c_uint, + local: *mut *mut NiceCandidate, + remote: *mut *mut NiceCandidate, + ) -> gboolean; + #[cfg(any(feature = "v0_1_5", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_5")))] + pub fn nice_agent_get_selected_socket( + agent: *mut NiceAgent, + stream_id: c_uint, + component_id: c_uint, + ) -> *mut gio::ffi::GSocket; + #[cfg(any(feature = "v0_1_17", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_17")))] + pub fn nice_agent_get_sockets( + agent: *mut NiceAgent, + stream_id: c_uint, + component_id: c_uint, + ) -> *mut glib::ffi::GPtrArray; + #[cfg(any(feature = "v0_1_4", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_4")))] + pub fn nice_agent_get_stream_name(agent: *mut NiceAgent, stream_id: c_uint) -> *const c_char; + #[cfg(any(feature = "v0_1_4", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_4")))] + pub fn nice_agent_parse_remote_candidate_sdp( + agent: *mut NiceAgent, + stream_id: c_uint, + sdp: *const c_char, + ) -> *mut NiceCandidate; + #[cfg(any(feature = "v0_1_4", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_4")))] + pub fn nice_agent_parse_remote_sdp(agent: *mut NiceAgent, sdp: *const c_char) -> c_int; + #[cfg(any(feature = "v0_1_4", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_4")))] + pub fn nice_agent_parse_remote_stream_sdp( + agent: *mut NiceAgent, + stream_id: c_uint, + sdp: *const c_char, + ufrag: *mut *mut c_char, + pwd: *mut *mut c_char, + ) -> *mut glib::ffi::GSList; + #[cfg(any(feature = "v0_1_16", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_16")))] + pub fn nice_agent_peer_candidate_gathering_done( + agent: *mut NiceAgent, + stream_id: c_uint, + ) -> gboolean; + #[cfg(any(feature = "v0_1_5", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_5")))] + pub fn nice_agent_recv( + agent: *mut NiceAgent, + stream_id: c_uint, + component_id: c_uint, + buf: *mut u8, + buf_len: size_t, + cancellable: *mut gio::ffi::GCancellable, + error: *mut *mut glib::ffi::GError, + ) -> ssize_t; + #[cfg(any(feature = "v0_1_5", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_5")))] + pub fn nice_agent_recv_messages( + agent: *mut NiceAgent, + stream_id: c_uint, + component_id: c_uint, + messages: *mut NiceInputMessage, + n_messages: c_uint, + cancellable: *mut gio::ffi::GCancellable, + error: *mut *mut glib::ffi::GError, + ) -> c_int; + #[cfg(any(feature = "v0_1_5", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_5")))] + pub fn nice_agent_recv_messages_nonblocking( + agent: *mut NiceAgent, + stream_id: c_uint, + component_id: c_uint, + messages: *mut NiceInputMessage, + n_messages: c_uint, + cancellable: *mut gio::ffi::GCancellable, + error: *mut *mut glib::ffi::GError, + ) -> c_int; + #[cfg(any(feature = "v0_1_5", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_5")))] + pub fn nice_agent_recv_nonblocking( + agent: *mut NiceAgent, + stream_id: c_uint, + component_id: c_uint, + buf: *mut u8, + buf_len: size_t, + cancellable: *mut gio::ffi::GCancellable, + error: *mut *mut glib::ffi::GError, + ) -> ssize_t; + pub fn nice_agent_remove_stream(agent: *mut NiceAgent, stream_id: c_uint); + pub fn nice_agent_restart(agent: *mut NiceAgent) -> gboolean; + #[cfg(any(feature = "v0_1_6", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_6")))] + pub fn nice_agent_restart_stream(agent: *mut NiceAgent, stream_id: c_uint) -> gboolean; + pub fn nice_agent_send( + agent: *mut NiceAgent, + stream_id: c_uint, + component_id: c_uint, + len: c_uint, + buf: *const c_char, + ) -> c_int; + #[cfg(any(feature = "v0_1_5", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_5")))] + pub fn nice_agent_send_messages_nonblocking( + agent: *mut NiceAgent, + stream_id: c_uint, + component_id: c_uint, + messages: *const NiceOutputMessage, + n_messages: c_uint, + cancellable: *mut gio::ffi::GCancellable, + error: *mut *mut glib::ffi::GError, + ) -> c_int; + pub fn nice_agent_set_local_credentials( + agent: *mut NiceAgent, + stream_id: c_uint, + ufrag: *const c_char, + pwd: *const c_char, + ) -> gboolean; + pub fn nice_agent_set_port_range( + agent: *mut NiceAgent, + stream_id: c_uint, + component_id: c_uint, + min_port: c_uint, + max_port: c_uint, + ); + pub fn nice_agent_set_relay_info( + agent: *mut NiceAgent, + stream_id: c_uint, + component_id: c_uint, + server_ip: *const c_char, + server_port: c_uint, + username: *const c_char, + password: *const c_char, + type_: NiceRelayType, + ) -> gboolean; + pub fn nice_agent_set_remote_candidates( + agent: *mut NiceAgent, + stream_id: c_uint, + component_id: c_uint, + candidates: *const glib::ffi::GSList, + ) -> c_int; + pub fn nice_agent_set_remote_credentials( + agent: *mut NiceAgent, + stream_id: c_uint, + ufrag: *const c_char, + pwd: *const c_char, + ) -> gboolean; + pub fn nice_agent_set_selected_pair( + agent: *mut NiceAgent, + stream_id: c_uint, + component_id: c_uint, + lfoundation: *const c_char, + rfoundation: *const c_char, + ) -> gboolean; + pub fn nice_agent_set_selected_remote_candidate( + agent: *mut NiceAgent, + stream_id: c_uint, + component_id: c_uint, + candidate: *mut NiceCandidate, + ) -> gboolean; + pub fn nice_agent_set_software(agent: *mut NiceAgent, software: *const c_char); + #[cfg(any(feature = "v0_1_4", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_4")))] + pub fn nice_agent_set_stream_name( + agent: *mut NiceAgent, + stream_id: c_uint, + name: *const c_char, + ) -> gboolean; + pub fn nice_agent_set_stream_tos(agent: *mut NiceAgent, stream_id: c_uint, tos: c_int); + + //========================================================================= + // PseudoTcpSocket + //========================================================================= + pub fn pseudo_tcp_socket_get_type() -> GType; + pub fn pseudo_tcp_socket_new( + conversation: u32, + callbacks: *mut PseudoTcpCallbacks, + ) -> *mut PseudoTcpSocket; + #[cfg(any(feature = "v0_1_5", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_5")))] + pub fn pseudo_tcp_socket_can_send(self_: *mut PseudoTcpSocket) -> gboolean; + pub fn pseudo_tcp_socket_close(self_: *mut PseudoTcpSocket, force: gboolean); + pub fn pseudo_tcp_socket_connect(self_: *mut PseudoTcpSocket) -> gboolean; + #[cfg(any(feature = "v0_1_5", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_5")))] + pub fn pseudo_tcp_socket_get_available_bytes(self_: *mut PseudoTcpSocket) -> c_int; + #[cfg(any(feature = "v0_1_5", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_5")))] + pub fn pseudo_tcp_socket_get_available_send_space(self_: *mut PseudoTcpSocket) -> size_t; + pub fn pseudo_tcp_socket_get_error(self_: *mut PseudoTcpSocket) -> c_int; + pub fn pseudo_tcp_socket_get_next_clock( + self_: *mut PseudoTcpSocket, + timeout: *mut u64, + ) -> gboolean; + #[cfg(any(feature = "v0_1_8", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_8")))] + pub fn pseudo_tcp_socket_is_closed(self_: *mut PseudoTcpSocket) -> gboolean; + #[cfg(any(feature = "v0_1_8", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_8")))] + pub fn pseudo_tcp_socket_is_closed_remotely(self_: *mut PseudoTcpSocket) -> gboolean; + pub fn pseudo_tcp_socket_notify_clock(self_: *mut PseudoTcpSocket); + #[cfg(any(feature = "v0_1_5", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_5")))] + pub fn pseudo_tcp_socket_notify_message( + self_: *mut PseudoTcpSocket, + message: *mut NiceInputMessage, + ) -> gboolean; + pub fn pseudo_tcp_socket_notify_mtu(self_: *mut PseudoTcpSocket, mtu: u16); + pub fn pseudo_tcp_socket_notify_packet( + self_: *mut PseudoTcpSocket, + buffer: *const c_char, + len: u32, + ) -> gboolean; + pub fn pseudo_tcp_socket_recv( + self_: *mut PseudoTcpSocket, + buffer: *mut c_char, + len: size_t, + ) -> c_int; + pub fn pseudo_tcp_socket_send( + self_: *mut PseudoTcpSocket, + buffer: *const c_char, + len: u32, + ) -> c_int; + #[cfg(any(feature = "v0_1_8", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_8")))] + pub fn pseudo_tcp_socket_set_time(self_: *mut PseudoTcpSocket, current_time: u32); + #[cfg(any(feature = "v0_1_8", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_8")))] + pub fn pseudo_tcp_socket_shutdown(self_: *mut PseudoTcpSocket, how: PseudoTcpShutdown); + + //========================================================================= + // Other functions + //========================================================================= + #[cfg(any(feature = "v0_1_6", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_6")))] + pub fn nice_component_state_to_string(state: NiceComponentState) -> *const c_char; + pub fn nice_debug_disable(with_stun: gboolean); + pub fn nice_debug_enable(with_stun: gboolean); + pub fn nice_interfaces_get_ip_for_interface(interface_name: *mut c_char) -> *mut c_char; + pub fn nice_interfaces_get_local_interfaces() -> *mut glib::ffi::GList; + pub fn nice_interfaces_get_local_ips(include_loopback: gboolean) -> *mut glib::ffi::GList; + pub fn pseudo_tcp_set_debug_level(level: PseudoTcpDebugLevel); + +} diff --git a/nice-gst-meet-sys/tests/abi.rs b/nice-gst-meet-sys/tests/abi.rs new file mode 100644 index 0000000..0bcc87c --- /dev/null +++ b/nice-gst-meet-sys/tests/abi.rs @@ -0,0 +1,418 @@ +// Generated by gir (https://github.com/gtk-rs/gir @ 5bbf6cb) +// from ../../gir-files (@ 8e47c67) +// DO NOT EDIT + +use std::{ + env, + error::Error, + ffi::OsString, + mem::{align_of, size_of}, + path::Path, + process::Command, + str, +}; + +use nice_sys::*; +use tempfile::Builder; + +static PACKAGES: &[&str] = &["nice"]; + +#[derive(Clone, Debug)] +struct Compiler { + pub args: Vec, +} + +impl Compiler { + pub fn new() -> Result> { + let mut args = get_var("CC", "cc")?; + args.push("-Wno-deprecated-declarations".to_owned()); + // For _Generic + args.push("-std=c11".to_owned()); + // For %z support in printf when using MinGW. + args.push("-D__USE_MINGW_ANSI_STDIO".to_owned()); + args.extend(get_var("CFLAGS", "")?); + args.extend(get_var("CPPFLAGS", "")?); + args.extend(pkg_config_cflags(PACKAGES)?); + Ok(Self { args }) + } + + pub fn compile(&self, src: &Path, out: &Path) -> Result<(), Box> { + let mut cmd = self.to_command(); + cmd.arg(src); + cmd.arg("-o"); + cmd.arg(out); + let status = cmd.spawn()?.wait()?; + if !status.success() { + return Err(format!("compilation command {:?} failed, {}", &cmd, status).into()); + } + Ok(()) + } + + fn to_command(&self) -> Command { + let mut cmd = Command::new(&self.args[0]); + cmd.args(&self.args[1..]); + cmd + } +} + +fn get_var(name: &str, default: &str) -> Result, Box> { + match env::var(name) { + Ok(value) => Ok(shell_words::split(&value)?), + Err(env::VarError::NotPresent) => Ok(shell_words::split(default)?), + Err(err) => Err(format!("{} {}", name, err).into()), + } +} + +fn pkg_config_cflags(packages: &[&str]) -> Result, Box> { + if packages.is_empty() { + return Ok(Vec::new()); + } + let pkg_config = env::var_os("PKG_CONFIG").unwrap_or_else(|| OsString::from("pkg-config")); + let mut cmd = Command::new(pkg_config); + cmd.arg("--cflags"); + cmd.args(packages); + let out = cmd.output()?; + if !out.status.success() { + return Err(format!("command {:?} returned {}", &cmd, out.status).into()); + } + let stdout = str::from_utf8(&out.stdout)?; + Ok(shell_words::split(stdout.trim())?) +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +struct Layout { + size: usize, + alignment: usize, +} + +#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)] +struct Results { + /// Number of successfully completed tests. + passed: usize, + /// Total number of failed tests (including those that failed to compile). + failed: usize, +} + +impl Results { + fn record_passed(&mut self) { + self.passed += 1; + } + + fn record_failed(&mut self) { + self.failed += 1; + } + + fn summary(&self) -> String { + format!("{} passed; {} failed", self.passed, self.failed) + } + + fn expect_total_success(&self) { + if self.failed == 0 { + println!("OK: {}", self.summary()); + } + else { + panic!("FAILED: {}", self.summary()); + }; + } +} + +#[test] +fn cross_validate_constants_with_c() { + let mut c_constants: Vec<(String, String)> = Vec::new(); + + for l in get_c_output("constant").unwrap().lines() { + let mut words = l.trim().split(';'); + let name = words.next().expect("Failed to parse name").to_owned(); + let value = words + .next() + .and_then(|s| s.parse().ok()) + .expect("Failed to parse value"); + c_constants.push((name, value)); + } + + let mut results = Results::default(); + + for ((rust_name, rust_value), (c_name, c_value)) in RUST_CONSTANTS.iter().zip(c_constants.iter()) + { + if rust_name != c_name { + results.record_failed(); + eprintln!("Name mismatch:\nRust: {:?}\nC: {:?}", rust_name, c_name,); + continue; + } + + if rust_value != c_value { + results.record_failed(); + eprintln!( + "Constant value mismatch for {}\nRust: {:?}\nC: {:?}", + rust_name, rust_value, &c_value + ); + continue; + } + + results.record_passed(); + } + + results.expect_total_success(); +} + +#[test] +fn cross_validate_layout_with_c() { + let mut c_layouts = Vec::new(); + + for l in get_c_output("layout").unwrap().lines() { + let mut words = l.trim().split(';'); + let name = words.next().expect("Failed to parse name").to_owned(); + let size = words + .next() + .and_then(|s| s.parse().ok()) + .expect("Failed to parse size"); + let alignment = words + .next() + .and_then(|s| s.parse().ok()) + .expect("Failed to parse alignment"); + c_layouts.push((name, Layout { size, alignment })); + } + + let mut results = Results::default(); + + for ((rust_name, rust_layout), (c_name, c_layout)) in RUST_LAYOUTS.iter().zip(c_layouts.iter()) { + if rust_name != c_name { + results.record_failed(); + eprintln!("Name mismatch:\nRust: {:?}\nC: {:?}", rust_name, c_name,); + continue; + } + + if rust_layout != c_layout { + results.record_failed(); + eprintln!( + "Layout mismatch for {}\nRust: {:?}\nC: {:?}", + rust_name, rust_layout, &c_layout + ); + continue; + } + + results.record_passed(); + } + + results.expect_total_success(); +} + +fn get_c_output(name: &str) -> Result> { + let tmpdir = Builder::new().prefix("abi").tempdir()?; + let exe = tmpdir.path().join(name); + let c_file = Path::new("tests").join(name).with_extension("c"); + + let cc = Compiler::new().expect("configured compiler"); + cc.compile(&c_file, &exe)?; + + let mut abi_cmd = Command::new(exe); + let output = abi_cmd.output()?; + if !output.status.success() { + return Err(format!("command {:?} failed, {:?}", &abi_cmd, &output).into()); + } + + Ok(String::from_utf8(output.stdout)?) +} + +const RUST_LAYOUTS: &[(&str, Layout)] = &[ + ( + "NiceAddress", + Layout { + size: size_of::(), + alignment: align_of::(), + }, + ), + ( + "NiceAgentClass", + Layout { + size: size_of::(), + alignment: align_of::(), + }, + ), + ( + "NiceAgentOption", + Layout { + size: size_of::(), + alignment: align_of::(), + }, + ), + ( + "NiceCandidate", + Layout { + size: size_of::(), + alignment: align_of::(), + }, + ), + ( + "NiceCandidateTransport", + Layout { + size: size_of::(), + alignment: align_of::(), + }, + ), + ( + "NiceCandidateType", + Layout { + size: size_of::(), + alignment: align_of::(), + }, + ), + ( + "NiceCompatibility", + Layout { + size: size_of::(), + alignment: align_of::(), + }, + ), + ( + "NiceComponentState", + Layout { + size: size_of::(), + alignment: align_of::(), + }, + ), + ( + "NiceComponentType", + Layout { + size: size_of::(), + alignment: align_of::(), + }, + ), + ( + "NiceInputMessage", + Layout { + size: size_of::(), + alignment: align_of::(), + }, + ), + ( + "NiceNominationMode", + Layout { + size: size_of::(), + alignment: align_of::(), + }, + ), + ( + "NiceOutputMessage", + Layout { + size: size_of::(), + alignment: align_of::(), + }, + ), + ( + "NiceProxyType", + Layout { + size: size_of::(), + alignment: align_of::(), + }, + ), + ( + "NiceRelayType", + Layout { + size: size_of::(), + alignment: align_of::(), + }, + ), + ( + "PseudoTcpCallbacks", + Layout { + size: size_of::(), + alignment: align_of::(), + }, + ), + ( + "PseudoTcpDebugLevel", + Layout { + size: size_of::(), + alignment: align_of::(), + }, + ), + ( + "PseudoTcpShutdown", + Layout { + size: size_of::(), + alignment: align_of::(), + }, + ), + ( + "PseudoTcpState", + Layout { + size: size_of::(), + alignment: align_of::(), + }, + ), + ( + "PseudoTcpWriteResult", + Layout { + size: size_of::(), + alignment: align_of::(), + }, + ), +]; + +const RUST_CONSTANTS: &[(&str, &str)] = &[ + ("NICE_AGENT_MAX_REMOTE_CANDIDATES", "25"), + ("(guint) NICE_AGENT_OPTION_CONSENT_FRESHNESS", "32"), + ("(guint) NICE_AGENT_OPTION_ICE_TRICKLE", "8"), + ("(guint) NICE_AGENT_OPTION_LITE_MODE", "4"), + ("(guint) NICE_AGENT_OPTION_REGULAR_NOMINATION", "1"), + ("(guint) NICE_AGENT_OPTION_RELIABLE", "2"), + ("(guint) NICE_AGENT_OPTION_SUPPORT_RENOMINATION", "16"), + ("NICE_CANDIDATE_MAX_FOUNDATION", "33"), + ("NICE_CANDIDATE_MAX_LOCAL_ADDRESSES", "64"), + ("NICE_CANDIDATE_MAX_TURN_SERVERS", "8"), + ("(gint) NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE", "1"), + ("(gint) NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE", "2"), + ("(gint) NICE_CANDIDATE_TRANSPORT_TCP_SO", "3"), + ("(gint) NICE_CANDIDATE_TRANSPORT_UDP", "0"), + ("(gint) NICE_CANDIDATE_TYPE_HOST", "0"), + ("(gint) NICE_CANDIDATE_TYPE_PEER_REFLEXIVE", "2"), + ("(gint) NICE_CANDIDATE_TYPE_RELAYED", "3"), + ("(gint) NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE", "1"), + ("(gint) NICE_COMPATIBILITY_DRAFT19", "0"), + ("(gint) NICE_COMPATIBILITY_GOOGLE", "1"), + ("(gint) NICE_COMPATIBILITY_LAST", "5"), + ("(gint) NICE_COMPATIBILITY_MSN", "2"), + ("(gint) NICE_COMPATIBILITY_OC2007", "4"), + ("(gint) NICE_COMPATIBILITY_OC2007R2", "5"), + ("(gint) NICE_COMPATIBILITY_RFC5245", "0"), + ("(gint) NICE_COMPATIBILITY_WLM2009", "3"), + ("(gint) NICE_COMPONENT_STATE_CONNECTED", "3"), + ("(gint) NICE_COMPONENT_STATE_CONNECTING", "2"), + ("(gint) NICE_COMPONENT_STATE_DISCONNECTED", "0"), + ("(gint) NICE_COMPONENT_STATE_FAILED", "5"), + ("(gint) NICE_COMPONENT_STATE_GATHERING", "1"), + ("(gint) NICE_COMPONENT_STATE_LAST", "6"), + ("(gint) NICE_COMPONENT_STATE_READY", "4"), + ("(gint) NICE_COMPONENT_TYPE_RTCP", "2"), + ("(gint) NICE_COMPONENT_TYPE_RTP", "1"), + ("(gint) NICE_NOMINATION_MODE_AGGRESSIVE", "1"), + ("(gint) NICE_NOMINATION_MODE_REGULAR", "0"), + ("(gint) NICE_PROXY_TYPE_HTTP", "2"), + ("(gint) NICE_PROXY_TYPE_LAST", "2"), + ("(gint) NICE_PROXY_TYPE_NONE", "0"), + ("(gint) NICE_PROXY_TYPE_SOCKS5", "1"), + ("(gint) NICE_RELAY_TYPE_TURN_TCP", "1"), + ("(gint) NICE_RELAY_TYPE_TURN_TLS", "2"), + ("(gint) NICE_RELAY_TYPE_TURN_UDP", "0"), + ("(gint) PSEUDO_TCP_CLOSED", "4"), + ("(gint) PSEUDO_TCP_CLOSE_WAIT", "9"), + ("(gint) PSEUDO_TCP_CLOSING", "7"), + ("(gint) PSEUDO_TCP_DEBUG_NONE", "0"), + ("(gint) PSEUDO_TCP_DEBUG_NORMAL", "1"), + ("(gint) PSEUDO_TCP_DEBUG_VERBOSE", "2"), + ("(gint) PSEUDO_TCP_ESTABLISHED", "3"), + ("(gint) PSEUDO_TCP_FIN_WAIT_1", "5"), + ("(gint) PSEUDO_TCP_FIN_WAIT_2", "6"), + ("(gint) PSEUDO_TCP_LAST_ACK", "10"), + ("(gint) PSEUDO_TCP_LISTEN", "0"), + ("(gint) PSEUDO_TCP_SHUTDOWN_RD", "0"), + ("(gint) PSEUDO_TCP_SHUTDOWN_RDWR", "2"), + ("(gint) PSEUDO_TCP_SHUTDOWN_WR", "1"), + ("(gint) PSEUDO_TCP_SYN_RECEIVED", "2"), + ("(gint) PSEUDO_TCP_SYN_SENT", "1"), + ("(gint) PSEUDO_TCP_TIME_WAIT", "8"), + ("(gint) WR_FAIL", "2"), + ("(gint) WR_SUCCESS", "0"), + ("(gint) WR_TOO_LARGE", "1"), +]; diff --git a/nice-gst-meet-sys/tests/constant.c b/nice-gst-meet-sys/tests/constant.c new file mode 100644 index 0000000..acd00a2 --- /dev/null +++ b/nice-gst-meet-sys/tests/constant.c @@ -0,0 +1,96 @@ +// Generated by gir (https://github.com/gtk-rs/gir @ 5bbf6cb) +// from ../../gir-files (@ 8e47c67) +// DO NOT EDIT + +#include "manual.h" +#include + +#define PRINT_CONSTANT(CONSTANT_NAME) \ + printf("%s;", #CONSTANT_NAME); \ + printf(_Generic((CONSTANT_NAME), \ + char *: "%s", \ + const char *: "%s", \ + char: "%c", \ + signed char: "%hhd", \ + unsigned char: "%hhu", \ + short int: "%hd", \ + unsigned short int: "%hu", \ + int: "%d", \ + unsigned int: "%u", \ + long: "%ld", \ + unsigned long: "%lu", \ + long long: "%lld", \ + unsigned long long: "%llu", \ + float: "%f", \ + double: "%f", \ + long double: "%ld"), \ + CONSTANT_NAME); \ + printf("\n"); + +int main() { + PRINT_CONSTANT(NICE_AGENT_MAX_REMOTE_CANDIDATES); + PRINT_CONSTANT((guint) NICE_AGENT_OPTION_CONSENT_FRESHNESS); + PRINT_CONSTANT((guint) NICE_AGENT_OPTION_ICE_TRICKLE); + PRINT_CONSTANT((guint) NICE_AGENT_OPTION_LITE_MODE); + PRINT_CONSTANT((guint) NICE_AGENT_OPTION_REGULAR_NOMINATION); + PRINT_CONSTANT((guint) NICE_AGENT_OPTION_RELIABLE); + PRINT_CONSTANT((guint) NICE_AGENT_OPTION_SUPPORT_RENOMINATION); + PRINT_CONSTANT(NICE_CANDIDATE_MAX_FOUNDATION); + PRINT_CONSTANT(NICE_CANDIDATE_MAX_LOCAL_ADDRESSES); + PRINT_CONSTANT(NICE_CANDIDATE_MAX_TURN_SERVERS); + PRINT_CONSTANT((gint) NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE); + PRINT_CONSTANT((gint) NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE); + PRINT_CONSTANT((gint) NICE_CANDIDATE_TRANSPORT_TCP_SO); + PRINT_CONSTANT((gint) NICE_CANDIDATE_TRANSPORT_UDP); + PRINT_CONSTANT((gint) NICE_CANDIDATE_TYPE_HOST); + PRINT_CONSTANT((gint) NICE_CANDIDATE_TYPE_PEER_REFLEXIVE); + PRINT_CONSTANT((gint) NICE_CANDIDATE_TYPE_RELAYED); + PRINT_CONSTANT((gint) NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE); + PRINT_CONSTANT((gint) NICE_COMPATIBILITY_DRAFT19); + PRINT_CONSTANT((gint) NICE_COMPATIBILITY_GOOGLE); + PRINT_CONSTANT((gint) NICE_COMPATIBILITY_LAST); + PRINT_CONSTANT((gint) NICE_COMPATIBILITY_MSN); + PRINT_CONSTANT((gint) NICE_COMPATIBILITY_OC2007); + PRINT_CONSTANT((gint) NICE_COMPATIBILITY_OC2007R2); + PRINT_CONSTANT((gint) NICE_COMPATIBILITY_RFC5245); + PRINT_CONSTANT((gint) NICE_COMPATIBILITY_WLM2009); + PRINT_CONSTANT((gint) NICE_COMPONENT_STATE_CONNECTED); + PRINT_CONSTANT((gint) NICE_COMPONENT_STATE_CONNECTING); + PRINT_CONSTANT((gint) NICE_COMPONENT_STATE_DISCONNECTED); + PRINT_CONSTANT((gint) NICE_COMPONENT_STATE_FAILED); + PRINT_CONSTANT((gint) NICE_COMPONENT_STATE_GATHERING); + PRINT_CONSTANT((gint) NICE_COMPONENT_STATE_LAST); + PRINT_CONSTANT((gint) NICE_COMPONENT_STATE_READY); + PRINT_CONSTANT((gint) NICE_COMPONENT_TYPE_RTCP); + PRINT_CONSTANT((gint) NICE_COMPONENT_TYPE_RTP); + PRINT_CONSTANT((gint) NICE_NOMINATION_MODE_AGGRESSIVE); + PRINT_CONSTANT((gint) NICE_NOMINATION_MODE_REGULAR); + PRINT_CONSTANT((gint) NICE_PROXY_TYPE_HTTP); + PRINT_CONSTANT((gint) NICE_PROXY_TYPE_LAST); + PRINT_CONSTANT((gint) NICE_PROXY_TYPE_NONE); + PRINT_CONSTANT((gint) NICE_PROXY_TYPE_SOCKS5); + PRINT_CONSTANT((gint) NICE_RELAY_TYPE_TURN_TCP); + PRINT_CONSTANT((gint) NICE_RELAY_TYPE_TURN_TLS); + PRINT_CONSTANT((gint) NICE_RELAY_TYPE_TURN_UDP); + PRINT_CONSTANT((gint) PSEUDO_TCP_CLOSED); + PRINT_CONSTANT((gint) PSEUDO_TCP_CLOSE_WAIT); + PRINT_CONSTANT((gint) PSEUDO_TCP_CLOSING); + PRINT_CONSTANT((gint) PSEUDO_TCP_DEBUG_NONE); + PRINT_CONSTANT((gint) PSEUDO_TCP_DEBUG_NORMAL); + PRINT_CONSTANT((gint) PSEUDO_TCP_DEBUG_VERBOSE); + PRINT_CONSTANT((gint) PSEUDO_TCP_ESTABLISHED); + PRINT_CONSTANT((gint) PSEUDO_TCP_FIN_WAIT_1); + PRINT_CONSTANT((gint) PSEUDO_TCP_FIN_WAIT_2); + PRINT_CONSTANT((gint) PSEUDO_TCP_LAST_ACK); + PRINT_CONSTANT((gint) PSEUDO_TCP_LISTEN); + PRINT_CONSTANT((gint) PSEUDO_TCP_SHUTDOWN_RD); + PRINT_CONSTANT((gint) PSEUDO_TCP_SHUTDOWN_RDWR); + PRINT_CONSTANT((gint) PSEUDO_TCP_SHUTDOWN_WR); + PRINT_CONSTANT((gint) PSEUDO_TCP_SYN_RECEIVED); + PRINT_CONSTANT((gint) PSEUDO_TCP_SYN_SENT); + PRINT_CONSTANT((gint) PSEUDO_TCP_TIME_WAIT); + PRINT_CONSTANT((gint) WR_FAIL); + PRINT_CONSTANT((gint) WR_SUCCESS); + PRINT_CONSTANT((gint) WR_TOO_LARGE); + return 0; +} diff --git a/nice-gst-meet-sys/tests/layout.c b/nice-gst-meet-sys/tests/layout.c new file mode 100644 index 0000000..1109942 --- /dev/null +++ b/nice-gst-meet-sys/tests/layout.c @@ -0,0 +1,30 @@ +// Generated by gir (https://github.com/gtk-rs/gir @ 5bbf6cb) +// from ../../gir-files (@ 8e47c67) +// DO NOT EDIT + +#include "manual.h" +#include +#include + +int main() { + printf("%s;%zu;%zu\n", "NiceAddress", sizeof(NiceAddress), alignof(NiceAddress)); + printf("%s;%zu;%zu\n", "NiceAgentClass", sizeof(NiceAgentClass), alignof(NiceAgentClass)); + printf("%s;%zu;%zu\n", "NiceAgentOption", sizeof(NiceAgentOption), alignof(NiceAgentOption)); + printf("%s;%zu;%zu\n", "NiceCandidate", sizeof(NiceCandidate), alignof(NiceCandidate)); + printf("%s;%zu;%zu\n", "NiceCandidateTransport", sizeof(NiceCandidateTransport), alignof(NiceCandidateTransport)); + printf("%s;%zu;%zu\n", "NiceCandidateType", sizeof(NiceCandidateType), alignof(NiceCandidateType)); + printf("%s;%zu;%zu\n", "NiceCompatibility", sizeof(NiceCompatibility), alignof(NiceCompatibility)); + printf("%s;%zu;%zu\n", "NiceComponentState", sizeof(NiceComponentState), alignof(NiceComponentState)); + printf("%s;%zu;%zu\n", "NiceComponentType", sizeof(NiceComponentType), alignof(NiceComponentType)); + printf("%s;%zu;%zu\n", "NiceInputMessage", sizeof(NiceInputMessage), alignof(NiceInputMessage)); + printf("%s;%zu;%zu\n", "NiceNominationMode", sizeof(NiceNominationMode), alignof(NiceNominationMode)); + printf("%s;%zu;%zu\n", "NiceOutputMessage", sizeof(NiceOutputMessage), alignof(NiceOutputMessage)); + printf("%s;%zu;%zu\n", "NiceProxyType", sizeof(NiceProxyType), alignof(NiceProxyType)); + printf("%s;%zu;%zu\n", "NiceRelayType", sizeof(NiceRelayType), alignof(NiceRelayType)); + printf("%s;%zu;%zu\n", "PseudoTcpCallbacks", sizeof(PseudoTcpCallbacks), alignof(PseudoTcpCallbacks)); + printf("%s;%zu;%zu\n", "PseudoTcpDebugLevel", sizeof(PseudoTcpDebugLevel), alignof(PseudoTcpDebugLevel)); + printf("%s;%zu;%zu\n", "PseudoTcpShutdown", sizeof(PseudoTcpShutdown), alignof(PseudoTcpShutdown)); + printf("%s;%zu;%zu\n", "PseudoTcpState", sizeof(PseudoTcpState), alignof(PseudoTcpState)); + printf("%s;%zu;%zu\n", "PseudoTcpWriteResult", sizeof(PseudoTcpWriteResult), alignof(PseudoTcpWriteResult)); + return 0; +} diff --git a/nice-gst-meet-sys/tests/manual.h b/nice-gst-meet-sys/tests/manual.h new file mode 100644 index 0000000..33968c2 --- /dev/null +++ b/nice-gst-meet-sys/tests/manual.h @@ -0,0 +1,2 @@ +// Feel free to edit this file, it won't be regenerated by gir generator unless removed. + diff --git a/nice-gst-meet/Cargo.toml b/nice-gst-meet/Cargo.toml new file mode 100644 index 0000000..14b4ca5 --- /dev/null +++ b/nice-gst-meet/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "nice-gst-meet" +version = "0.1.0" +edition = "2018" +license = "MIT/Apache-2.0" +authors = ["Jasper Hugo "] + +[dependencies] +bitflags = { version = "1", default-features = false, optional = true } +glib = { version = "0.14", default-features = false } +libc = { version = "0.2", default-features = false } +nice-gst-meet-sys = { path = "../nice-gst-meet-sys", default-features = false } +nix = { version = "0.22", default-features = false } + +[features] +v0_1_4 = ["nice-gst-meet-sys/v0_1_4"] +v0_1_5 = ["nice-gst-meet-sys/v0_1_5", "v0_1_4"] +v0_1_6 = ["nice-gst-meet-sys/v0_1_6", "v0_1_5"] +v0_1_8 = ["nice-gst-meet-sys/v0_1_8", "v0_1_6"] +v0_1_14 = ["nice-gst-meet-sys/v0_1_14", "v0_1_8"] +v0_1_15 = ["nice-gst-meet-sys/v0_1_15", "v0_1_14", "bitflags"] +v0_1_16 = ["nice-gst-meet-sys/v0_1_16", "v0_1_15"] +v0_1_17 = ["nice-gst-meet-sys/v0_1_17", "v0_1_16"] +v0_1_18 = ["nice-gst-meet-sys/v0_1_18", "v0_1_17"] +v0_1_20 = ["nice-gst-meet-sys/v0_1_20", "v0_1_18"] +dox = [] diff --git a/nice-gst-meet/Gir.toml b/nice-gst-meet/Gir.toml new file mode 100644 index 0000000..9cbee25 --- /dev/null +++ b/nice-gst-meet/Gir.toml @@ -0,0 +1,20 @@ +[options] +library = "Nice" +version = "0.1" +min_cfg_version = "0.1" +work_mode = "normal" +auto_path = "src" +target_path = "." +external_libraries = [] +generate = [ + "Nice.Address", + "Nice.Agent", + "Nice.AgentOption", + "Nice.Candidate", + "Nice.CandidateTransport", + "Nice.CandidateType", + "Nice.Compatibility", + "Nice.ComponentState", + "Nice.RelayType", +] +manual = ["glib.MainContext"] diff --git a/nice-gst-meet/LICENSE-APACHE b/nice-gst-meet/LICENSE-APACHE new file mode 100644 index 0000000..f8e5e5e --- /dev/null +++ b/nice-gst-meet/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. \ No newline at end of file diff --git a/nice-gst-meet/LICENSE-MIT b/nice-gst-meet/LICENSE-MIT new file mode 100644 index 0000000..468cd79 --- /dev/null +++ b/nice-gst-meet/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/nice-gst-meet/src/agent.rs b/nice-gst-meet/src/agent.rs new file mode 100644 index 0000000..d2a8e0f --- /dev/null +++ b/nice-gst-meet/src/agent.rs @@ -0,0 +1,2130 @@ +// Generated by gir (https://github.com/gtk-rs/gir @ 5bbf6cb) +// from ../../gir-files (@ 8e47c67) +// DO NOT EDIT + +use std::{ + boxed::Box as Box_, + fmt, + mem::{self, transmute}, + ptr, slice, +}; + +use glib::{ + ffi::gpointer, + object::ObjectType as ObjectType_, + signal::{connect_raw, SignalHandlerId}, + translate::*, + StaticType, ToValue, +}; +use libc::{c_char, c_uint}; +use nice_sys as ffi; + +#[cfg(any(feature = "v0_1_15", feature = "dox"))] +#[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_15")))] +use crate::AgentOption; +#[cfg(any(feature = "v0_1_8", feature = "dox"))] +#[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_8")))] +use crate::ComponentState; +use crate::{Candidate, Compatibility, RelayType}; + +glib::wrapper! { + #[doc(alias = "NiceAgent")] + pub struct Agent(Object); + + match fn { + type_ => || ffi::nice_agent_get_type(), + } +} + +unsafe impl Send for Agent {} +unsafe impl Sync for Agent {} + +extern "C" fn attach_recv_cb( + agent: *mut ffi::NiceAgent, + stream_id: c_uint, + component_id: c_uint, + len: c_uint, + data: *mut c_char, + user_data: gpointer, +) { + if !user_data.is_null() { + let closure: &mut Box = unsafe { mem::transmute(user_data) }; + let slice = unsafe { slice::from_raw_parts(data, len as usize) }; + let bytes: Vec<_> = slice.iter().map(|b| *b as u8).collect(); + if let Ok(s) = std::str::from_utf8(&bytes) { + closure( + unsafe { Agent::from_glib_none(agent) }, + stream_id as u32, + component_id as u32, + s, + ); + } + } +} + +impl Agent { + #[doc(alias = "nice_agent_new")] + pub fn new(ctx: &glib::MainContext, compat: Compatibility) -> Agent { + unsafe { + Agent::from_glib_full(ffi::nice_agent_new( + ctx.to_glib_none().0, + compat.into_glib(), + )) + } + } + + #[cfg(any(feature = "v0_1_15", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_15")))] + #[doc(alias = "nice_agent_new_full")] + pub fn new_full(ctx: &glib::MainContext, compat: Compatibility, flags: AgentOption) -> Agent { + unsafe { + Agent::from_glib_full(ffi::nice_agent_new_full( + ctx.to_glib_none().0, + compat.into_glib(), + flags.bits(), + )) + } + } + + #[doc(alias = "nice_agent_new_reliable")] + pub fn new_reliable(ctx: &glib::MainContext, compat: Compatibility) -> Agent { + unsafe { + Agent::from_glib_full(ffi::nice_agent_new_reliable( + ctx.to_glib_none().0, + compat.into_glib(), + )) + } + } + + #[doc(alias = "nice_agent_add_stream")] + pub fn add_stream(&self, n_components: u32) -> u32 { + unsafe { ffi::nice_agent_add_stream(self.to_glib_none().0, n_components) } + } + + #[doc(alias = "nice_agent_attach_recv")] + pub fn attach_recv( + &self, + stream_id: u32, + component_id: u32, + ctx: &glib::MainContext, + cb: impl FnMut(Agent, u32, u32, &str), + ) -> bool { + let cb = Box::new(Box::new(cb)); + unsafe { + from_glib(ffi::nice_agent_attach_recv( + self.to_glib_none().0, + stream_id, + component_id, + ctx.to_glib_none().0, + Some(attach_recv_cb), + Box::into_raw(cb) as *mut _, + )) + } + } + + #[cfg(any(feature = "v0_1_20", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_20")))] + #[doc(alias = "nice_agent_consent_lost")] + pub fn consent_lost(&self, stream_id: u32, component_id: u32) -> bool { + unsafe { + from_glib(ffi::nice_agent_consent_lost( + self.to_glib_none().0, + stream_id, + component_id, + )) + } + } + + #[cfg(any(feature = "v0_1_6", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_6")))] + #[doc(alias = "nice_agent_forget_relays")] + pub fn forget_relays(&self, stream_id: u32, component_id: u32) -> bool { + unsafe { + from_glib(ffi::nice_agent_forget_relays( + self.to_glib_none().0, + stream_id, + component_id, + )) + } + } + + #[doc(alias = "nice_agent_gather_candidates")] + pub fn gather_candidates(&self, stream_id: u32) -> bool { + unsafe { + from_glib(ffi::nice_agent_gather_candidates( + self.to_glib_none().0, + stream_id, + )) + } + } + + #[cfg(any(feature = "v0_1_4", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_4")))] + #[doc(alias = "nice_agent_generate_local_candidate_sdp")] + pub fn generate_local_candidate_sdp(&self, candidate: &mut Candidate) -> Option { + unsafe { + from_glib_full(ffi::nice_agent_generate_local_candidate_sdp( + self.to_glib_none().0, + candidate.to_glib_none_mut().0, + )) + } + } + + #[cfg(any(feature = "v0_1_4", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_4")))] + #[doc(alias = "nice_agent_generate_local_sdp")] + pub fn generate_local_sdp(&self) -> Option { + unsafe { from_glib_full(ffi::nice_agent_generate_local_sdp(self.to_glib_none().0)) } + } + + #[cfg(any(feature = "v0_1_4", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_4")))] + #[doc(alias = "nice_agent_generate_local_stream_sdp")] + pub fn generate_local_stream_sdp(&self, stream_id: u32, include_non_ice: bool) -> Option { + unsafe { + from_glib_full(ffi::nice_agent_generate_local_stream_sdp( + self.to_glib_none().0, + stream_id, + include_non_ice.into_glib(), + )) + } + } + + #[cfg(any(feature = "v0_1_8", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_8")))] + #[doc(alias = "nice_agent_get_component_state")] + #[doc(alias = "get_component_state")] + pub fn component_state(&self, stream_id: u32, component_id: u32) -> ComponentState { + unsafe { + from_glib(ffi::nice_agent_get_component_state( + self.to_glib_none().0, + stream_id, + component_id, + )) + } + } + + #[doc(alias = "nice_agent_get_default_local_candidate")] + #[doc(alias = "get_default_local_candidate")] + pub fn default_local_candidate(&self, stream_id: u32, component_id: u32) -> Option { + unsafe { + from_glib_full(ffi::nice_agent_get_default_local_candidate( + self.to_glib_none().0, + stream_id, + component_id, + )) + } + } + + #[doc(alias = "nice_agent_get_local_candidates")] + #[doc(alias = "get_local_candidates")] + pub fn local_candidates(&self, stream_id: u32, component_id: u32) -> Vec { + unsafe { + FromGlibPtrContainer::from_glib_full(ffi::nice_agent_get_local_candidates( + self.to_glib_none().0, + stream_id, + component_id, + )) + } + } + + #[doc(alias = "nice_agent_get_local_credentials")] + #[doc(alias = "get_local_credentials")] + pub fn local_credentials(&self, stream_id: u32) -> Option<(String, String)> { + unsafe { + let mut ufrag = ptr::null_mut(); + let mut pwd = ptr::null_mut(); + let ret = from_glib(ffi::nice_agent_get_local_credentials( + self.to_glib_none().0, + stream_id, + &mut ufrag, + &mut pwd, + )); + if ret { + Some((from_glib_full(ufrag), from_glib_full(pwd))) + } + else { + None + } + } + } + + #[doc(alias = "nice_agent_get_remote_candidates")] + #[doc(alias = "get_remote_candidates")] + pub fn remote_candidates(&self, stream_id: u32, component_id: u32) -> Vec { + unsafe { + FromGlibPtrContainer::from_glib_full(ffi::nice_agent_get_remote_candidates( + self.to_glib_none().0, + stream_id, + component_id, + )) + } + } + + #[doc(alias = "nice_agent_get_selected_pair")] + pub fn get_selected_pair( + &self, + stream_id: u32, + component_id: u32, + ) -> Option<(Candidate, Candidate)> { + unsafe { + let mut local = ptr::null_mut(); + let mut remote = ptr::null_mut(); + let ret = from_glib(ffi::nice_agent_get_selected_pair( + self.to_glib_none().0, + stream_id, + component_id, + &mut local, + &mut remote, + )); + if ret { + Some((from_glib_full(local), from_glib_full(remote))) + } + else { + None + } + } + } + + #[cfg(any(feature = "v0_1_4", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_4")))] + #[doc(alias = "nice_agent_get_stream_name")] + #[doc(alias = "get_stream_name")] + pub fn stream_name(&self, stream_id: u32) -> Option { + unsafe { + from_glib_none(ffi::nice_agent_get_stream_name( + self.to_glib_none().0, + stream_id, + )) + } + } + + #[cfg(any(feature = "v0_1_4", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_4")))] + #[doc(alias = "nice_agent_parse_remote_candidate_sdp")] + pub fn parse_remote_candidate_sdp(&self, stream_id: u32, sdp: &str) -> Option { + unsafe { + from_glib_full(ffi::nice_agent_parse_remote_candidate_sdp( + self.to_glib_none().0, + stream_id, + sdp.to_glib_none().0, + )) + } + } + + #[cfg(any(feature = "v0_1_4", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_4")))] + #[doc(alias = "nice_agent_parse_remote_sdp")] + pub fn parse_remote_sdp(&self, sdp: &str) -> i32 { + unsafe { ffi::nice_agent_parse_remote_sdp(self.to_glib_none().0, sdp.to_glib_none().0) } + } + + #[cfg(any(feature = "v0_1_4", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_4")))] + #[doc(alias = "nice_agent_parse_remote_stream_sdp")] + pub fn parse_remote_stream_sdp( + &self, + stream_id: u32, + sdp: &str, + ) -> (Vec, String, String) { + unsafe { + let mut ufrag = ptr::null_mut(); + let mut pwd = ptr::null_mut(); + let candidates = ffi::nice_agent_parse_remote_stream_sdp( + self.to_glib_none().0, + stream_id, + sdp.to_glib_none().0, + &mut ufrag, + &mut pwd, + ); + ( + FromGlibPtrContainer::from_glib_full(candidates), + from_glib_full(ufrag), + from_glib_full(pwd), + ) + } + } + + #[cfg(any(feature = "v0_1_16", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_16")))] + #[doc(alias = "nice_agent_peer_candidate_gathering_done")] + pub fn peer_candidate_gathering_done(&self, stream_id: u32) -> bool { + unsafe { + from_glib(ffi::nice_agent_peer_candidate_gathering_done( + self.to_glib_none().0, + stream_id, + )) + } + } + + #[doc(alias = "nice_agent_remove_stream")] + pub fn remove_stream(&self, stream_id: u32) { + unsafe { + ffi::nice_agent_remove_stream(self.to_glib_none().0, stream_id); + } + } + + #[doc(alias = "nice_agent_restart")] + pub fn restart(&self) -> bool { + unsafe { from_glib(ffi::nice_agent_restart(self.to_glib_none().0)) } + } + + #[cfg(any(feature = "v0_1_6", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_6")))] + #[doc(alias = "nice_agent_restart_stream")] + pub fn restart_stream(&self, stream_id: u32) -> bool { + unsafe { + from_glib(ffi::nice_agent_restart_stream( + self.to_glib_none().0, + stream_id, + )) + } + } + + #[doc(alias = "nice_agent_send")] + pub fn send(&self, stream_id: u32, component_id: u32, len: u32, buf: &str) -> i32 { + unsafe { + ffi::nice_agent_send( + self.to_glib_none().0, + stream_id, + component_id, + len, + buf.to_glib_none().0, + ) + } + } + + #[doc(alias = "nice_agent_set_local_credentials")] + pub fn set_local_credentials(&self, stream_id: u32, ufrag: &str, pwd: &str) -> bool { + unsafe { + from_glib(ffi::nice_agent_set_local_credentials( + self.to_glib_none().0, + stream_id, + ufrag.to_glib_none().0, + pwd.to_glib_none().0, + )) + } + } + + #[doc(alias = "nice_agent_set_port_range")] + pub fn set_port_range(&self, stream_id: u32, component_id: u32, min_port: u32, max_port: u32) { + unsafe { + ffi::nice_agent_set_port_range( + self.to_glib_none().0, + stream_id, + component_id, + min_port, + max_port, + ); + } + } + + #[doc(alias = "nice_agent_set_relay_info")] + pub fn set_relay_info( + &self, + stream_id: u32, + component_id: u32, + server_ip: &str, + server_port: u32, + username: &str, + password: &str, + type_: RelayType, + ) -> bool { + unsafe { + from_glib(ffi::nice_agent_set_relay_info( + self.to_glib_none().0, + stream_id, + component_id, + server_ip.to_glib_none().0, + server_port, + username.to_glib_none().0, + password.to_glib_none().0, + type_.into_glib(), + )) + } + } + + #[doc(alias = "nice_agent_set_remote_candidates")] + pub fn set_remote_candidates( + &self, + stream_id: u32, + component_id: u32, + candidates: &[&Candidate], + ) -> i32 { + unsafe { + ffi::nice_agent_set_remote_candidates( + self.to_glib_none().0, + stream_id, + component_id, + candidates.to_glib_none().0, + ) + } + } + + #[doc(alias = "nice_agent_set_remote_credentials")] + pub fn set_remote_credentials(&self, stream_id: u32, ufrag: &str, pwd: &str) -> bool { + unsafe { + from_glib(ffi::nice_agent_set_remote_credentials( + self.to_glib_none().0, + stream_id, + ufrag.to_glib_none().0, + pwd.to_glib_none().0, + )) + } + } + + #[doc(alias = "nice_agent_set_selected_pair")] + pub fn set_selected_pair( + &self, + stream_id: u32, + component_id: u32, + lfoundation: &str, + rfoundation: &str, + ) -> bool { + unsafe { + from_glib(ffi::nice_agent_set_selected_pair( + self.to_glib_none().0, + stream_id, + component_id, + lfoundation.to_glib_none().0, + rfoundation.to_glib_none().0, + )) + } + } + + #[doc(alias = "nice_agent_set_selected_remote_candidate")] + pub fn set_selected_remote_candidate( + &self, + stream_id: u32, + component_id: u32, + candidate: &mut Candidate, + ) -> bool { + unsafe { + from_glib(ffi::nice_agent_set_selected_remote_candidate( + self.to_glib_none().0, + stream_id, + component_id, + candidate.to_glib_none_mut().0, + )) + } + } + + #[doc(alias = "nice_agent_set_software")] + pub fn set_software(&self, software: &str) { + unsafe { + ffi::nice_agent_set_software(self.to_glib_none().0, software.to_glib_none().0); + } + } + + #[cfg(any(feature = "v0_1_4", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_4")))] + #[doc(alias = "nice_agent_set_stream_name")] + pub fn set_stream_name(&self, stream_id: u32, name: &str) -> bool { + unsafe { + from_glib(ffi::nice_agent_set_stream_name( + self.to_glib_none().0, + stream_id, + name.to_glib_none().0, + )) + } + } + + #[doc(alias = "nice_agent_set_stream_tos")] + pub fn set_stream_tos(&self, stream_id: u32, tos: i32) { + unsafe { + ffi::nice_agent_set_stream_tos(self.to_glib_none().0, stream_id, tos); + } + } + + #[cfg(any(feature = "v0_1_8", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_8")))] + #[doc(alias = "bytestream-tcp")] + pub fn is_bytestream_tcp(&self) -> bool { + unsafe { + let mut value = glib::Value::from_type(::static_type()); + glib::gobject_ffi::g_object_get_property( + self.as_ptr() as *mut glib::gobject_ffi::GObject, + b"bytestream-tcp\0".as_ptr() as *const _, + value.to_glib_none_mut().0, + ); + value + .get() + .expect("Return Value for property `bytestream-tcp` getter") + } + } + + pub fn compatibility(&self) -> u32 { + unsafe { + let mut value = glib::Value::from_type(::static_type()); + glib::gobject_ffi::g_object_get_property( + self.as_ptr() as *mut glib::gobject_ffi::GObject, + b"compatibility\0".as_ptr() as *const _, + value.to_glib_none_mut().0, + ); + value + .get() + .expect("Return Value for property `compatibility` getter") + } + } + + #[cfg(any(feature = "v0_1_20", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_20")))] + #[doc(alias = "consent-freshness")] + pub fn is_consent_freshness(&self) -> bool { + unsafe { + let mut value = glib::Value::from_type(::static_type()); + glib::gobject_ffi::g_object_get_property( + self.as_ptr() as *mut glib::gobject_ffi::GObject, + b"consent-freshness\0".as_ptr() as *const _, + value.to_glib_none_mut().0, + ); + value + .get() + .expect("Return Value for property `consent-freshness` getter") + } + } + + #[doc(alias = "controlling-mode")] + pub fn is_controlling_mode(&self) -> bool { + unsafe { + let mut value = glib::Value::from_type(::static_type()); + glib::gobject_ffi::g_object_get_property( + self.as_ptr() as *mut glib::gobject_ffi::GObject, + b"controlling-mode\0".as_ptr() as *const _, + value.to_glib_none_mut().0, + ); + value + .get() + .expect("Return Value for property `controlling-mode` getter") + } + } + + #[doc(alias = "controlling-mode")] + pub fn set_controlling_mode(&self, controlling_mode: bool) { + unsafe { + glib::gobject_ffi::g_object_set_property( + self.as_ptr() as *mut glib::gobject_ffi::GObject, + b"controlling-mode\0".as_ptr() as *const _, + controlling_mode.to_value().to_glib_none().0, + ); + } + } + + #[cfg(any(feature = "v0_1_14", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_14")))] + #[doc(alias = "force-relay")] + pub fn is_force_relay(&self) -> bool { + unsafe { + let mut value = glib::Value::from_type(::static_type()); + glib::gobject_ffi::g_object_get_property( + self.as_ptr() as *mut glib::gobject_ffi::GObject, + b"force-relay\0".as_ptr() as *const _, + value.to_glib_none_mut().0, + ); + value + .get() + .expect("Return Value for property `force-relay` getter") + } + } + + #[cfg(any(feature = "v0_1_14", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_14")))] + #[doc(alias = "force-relay")] + pub fn set_force_relay(&self, force_relay: bool) { + unsafe { + glib::gobject_ffi::g_object_set_property( + self.as_ptr() as *mut glib::gobject_ffi::GObject, + b"force-relay\0".as_ptr() as *const _, + force_relay.to_value().to_glib_none().0, + ); + } + } + + #[doc(alias = "full-mode")] + pub fn is_full_mode(&self) -> bool { + unsafe { + let mut value = glib::Value::from_type(::static_type()); + glib::gobject_ffi::g_object_get_property( + self.as_ptr() as *mut glib::gobject_ffi::GObject, + b"full-mode\0".as_ptr() as *const _, + value.to_glib_none_mut().0, + ); + value + .get() + .expect("Return Value for property `full-mode` getter") + } + } + + #[cfg(any(feature = "v0_1_8", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_8")))] + #[doc(alias = "ice-tcp")] + pub fn is_ice_tcp(&self) -> bool { + unsafe { + let mut value = glib::Value::from_type(::static_type()); + glib::gobject_ffi::g_object_get_property( + self.as_ptr() as *mut glib::gobject_ffi::GObject, + b"ice-tcp\0".as_ptr() as *const _, + value.to_glib_none_mut().0, + ); + value + .get() + .expect("Return Value for property `ice-tcp` getter") + } + } + + #[cfg(any(feature = "v0_1_8", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_8")))] + #[doc(alias = "ice-tcp")] + pub fn set_ice_tcp(&self, ice_tcp: bool) { + unsafe { + glib::gobject_ffi::g_object_set_property( + self.as_ptr() as *mut glib::gobject_ffi::GObject, + b"ice-tcp\0".as_ptr() as *const _, + ice_tcp.to_value().to_glib_none().0, + ); + } + } + + #[cfg(any(feature = "v0_1_16", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_16")))] + #[doc(alias = "ice-trickle")] + pub fn is_ice_trickle(&self) -> bool { + unsafe { + let mut value = glib::Value::from_type(::static_type()); + glib::gobject_ffi::g_object_get_property( + self.as_ptr() as *mut glib::gobject_ffi::GObject, + b"ice-trickle\0".as_ptr() as *const _, + value.to_glib_none_mut().0, + ); + value + .get() + .expect("Return Value for property `ice-trickle` getter") + } + } + + #[cfg(any(feature = "v0_1_16", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_16")))] + #[doc(alias = "ice-trickle")] + pub fn set_ice_trickle(&self, ice_trickle: bool) { + unsafe { + glib::gobject_ffi::g_object_set_property( + self.as_ptr() as *mut glib::gobject_ffi::GObject, + b"ice-trickle\0".as_ptr() as *const _, + ice_trickle.to_value().to_glib_none().0, + ); + } + } + + #[cfg(any(feature = "v0_1_8", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_8")))] + #[doc(alias = "ice-udp")] + pub fn is_ice_udp(&self) -> bool { + unsafe { + let mut value = glib::Value::from_type(::static_type()); + glib::gobject_ffi::g_object_get_property( + self.as_ptr() as *mut glib::gobject_ffi::GObject, + b"ice-udp\0".as_ptr() as *const _, + value.to_glib_none_mut().0, + ); + value + .get() + .expect("Return Value for property `ice-udp` getter") + } + } + + #[cfg(any(feature = "v0_1_8", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_8")))] + #[doc(alias = "ice-udp")] + pub fn set_ice_udp(&self, ice_udp: bool) { + unsafe { + glib::gobject_ffi::g_object_set_property( + self.as_ptr() as *mut glib::gobject_ffi::GObject, + b"ice-udp\0".as_ptr() as *const _, + ice_udp.to_value().to_glib_none().0, + ); + } + } + + #[cfg(any(feature = "v0_1_17", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_17")))] + #[doc(alias = "idle-timeout")] + pub fn idle_timeout(&self) -> u32 { + unsafe { + let mut value = glib::Value::from_type(::static_type()); + glib::gobject_ffi::g_object_get_property( + self.as_ptr() as *mut glib::gobject_ffi::GObject, + b"idle-timeout\0".as_ptr() as *const _, + value.to_glib_none_mut().0, + ); + value + .get() + .expect("Return Value for property `idle-timeout` getter") + } + } + + #[cfg(any(feature = "v0_1_17", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_17")))] + #[doc(alias = "idle-timeout")] + pub fn set_idle_timeout(&self, idle_timeout: u32) { + unsafe { + glib::gobject_ffi::g_object_set_property( + self.as_ptr() as *mut glib::gobject_ffi::GObject, + b"idle-timeout\0".as_ptr() as *const _, + idle_timeout.to_value().to_glib_none().0, + ); + } + } + + #[cfg(any(feature = "v0_1_8", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_8")))] + #[doc(alias = "keepalive-conncheck")] + pub fn is_keepalive_conncheck(&self) -> bool { + unsafe { + let mut value = glib::Value::from_type(::static_type()); + glib::gobject_ffi::g_object_get_property( + self.as_ptr() as *mut glib::gobject_ffi::GObject, + b"keepalive-conncheck\0".as_ptr() as *const _, + value.to_glib_none_mut().0, + ); + value + .get() + .expect("Return Value for property `keepalive-conncheck` getter") + } + } + + #[cfg(any(feature = "v0_1_8", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_8")))] + #[doc(alias = "keepalive-conncheck")] + pub fn set_keepalive_conncheck(&self, keepalive_conncheck: bool) { + unsafe { + glib::gobject_ffi::g_object_set_property( + self.as_ptr() as *mut glib::gobject_ffi::GObject, + b"keepalive-conncheck\0".as_ptr() as *const _, + keepalive_conncheck.to_value().to_glib_none().0, + ); + } + } + + //#[doc(alias = "main-context")] + //pub fn main_context(&self) -> /*Unimplemented*/Fundamental: Pointer { + // unsafe { + // let mut value = glib::Value::from_type(::static_type()); + // glib::gobject_ffi::g_object_get_property(self.as_ptr() as *mut glib::gobject_ffi::GObject, b"main-context\0".as_ptr() as *const _, value.to_glib_none_mut().0); + // value.get().expect("Return Value for property `main-context` getter") + // } + //} + + #[doc(alias = "max-connectivity-checks")] + pub fn max_connectivity_checks(&self) -> u32 { + unsafe { + let mut value = glib::Value::from_type(::static_type()); + glib::gobject_ffi::g_object_get_property( + self.as_ptr() as *mut glib::gobject_ffi::GObject, + b"max-connectivity-checks\0".as_ptr() as *const _, + value.to_glib_none_mut().0, + ); + value + .get() + .expect("Return Value for property `max-connectivity-checks` getter") + } + } + + #[doc(alias = "max-connectivity-checks")] + pub fn set_max_connectivity_checks(&self, max_connectivity_checks: u32) { + unsafe { + glib::gobject_ffi::g_object_set_property( + self.as_ptr() as *mut glib::gobject_ffi::GObject, + b"max-connectivity-checks\0".as_ptr() as *const _, + max_connectivity_checks.to_value().to_glib_none().0, + ); + } + } + + #[doc(alias = "proxy-ip")] + pub fn proxy_ip(&self) -> Option { + unsafe { + let mut value = glib::Value::from_type(::static_type()); + glib::gobject_ffi::g_object_get_property( + self.as_ptr() as *mut glib::gobject_ffi::GObject, + b"proxy-ip\0".as_ptr() as *const _, + value.to_glib_none_mut().0, + ); + value + .get() + .expect("Return Value for property `proxy-ip` getter") + } + } + + #[doc(alias = "proxy-ip")] + pub fn set_proxy_ip(&self, proxy_ip: Option<&str>) { + unsafe { + glib::gobject_ffi::g_object_set_property( + self.as_ptr() as *mut glib::gobject_ffi::GObject, + b"proxy-ip\0".as_ptr() as *const _, + proxy_ip.to_value().to_glib_none().0, + ); + } + } + + #[doc(alias = "proxy-password")] + pub fn proxy_password(&self) -> Option { + unsafe { + let mut value = glib::Value::from_type(::static_type()); + glib::gobject_ffi::g_object_get_property( + self.as_ptr() as *mut glib::gobject_ffi::GObject, + b"proxy-password\0".as_ptr() as *const _, + value.to_glib_none_mut().0, + ); + value + .get() + .expect("Return Value for property `proxy-password` getter") + } + } + + #[doc(alias = "proxy-password")] + pub fn set_proxy_password(&self, proxy_password: Option<&str>) { + unsafe { + glib::gobject_ffi::g_object_set_property( + self.as_ptr() as *mut glib::gobject_ffi::GObject, + b"proxy-password\0".as_ptr() as *const _, + proxy_password.to_value().to_glib_none().0, + ); + } + } + + #[doc(alias = "proxy-port")] + pub fn proxy_port(&self) -> u32 { + unsafe { + let mut value = glib::Value::from_type(::static_type()); + glib::gobject_ffi::g_object_get_property( + self.as_ptr() as *mut glib::gobject_ffi::GObject, + b"proxy-port\0".as_ptr() as *const _, + value.to_glib_none_mut().0, + ); + value + .get() + .expect("Return Value for property `proxy-port` getter") + } + } + + #[doc(alias = "proxy-port")] + pub fn set_proxy_port(&self, proxy_port: u32) { + unsafe { + glib::gobject_ffi::g_object_set_property( + self.as_ptr() as *mut glib::gobject_ffi::GObject, + b"proxy-port\0".as_ptr() as *const _, + proxy_port.to_value().to_glib_none().0, + ); + } + } + + #[doc(alias = "proxy-type")] + pub fn proxy_type(&self) -> u32 { + unsafe { + let mut value = glib::Value::from_type(::static_type()); + glib::gobject_ffi::g_object_get_property( + self.as_ptr() as *mut glib::gobject_ffi::GObject, + b"proxy-type\0".as_ptr() as *const _, + value.to_glib_none_mut().0, + ); + value + .get() + .expect("Return Value for property `proxy-type` getter") + } + } + + #[doc(alias = "proxy-type")] + pub fn set_proxy_type(&self, proxy_type: u32) { + unsafe { + glib::gobject_ffi::g_object_set_property( + self.as_ptr() as *mut glib::gobject_ffi::GObject, + b"proxy-type\0".as_ptr() as *const _, + proxy_type.to_value().to_glib_none().0, + ); + } + } + + #[doc(alias = "proxy-username")] + pub fn proxy_username(&self) -> Option { + unsafe { + let mut value = glib::Value::from_type(::static_type()); + glib::gobject_ffi::g_object_get_property( + self.as_ptr() as *mut glib::gobject_ffi::GObject, + b"proxy-username\0".as_ptr() as *const _, + value.to_glib_none_mut().0, + ); + value + .get() + .expect("Return Value for property `proxy-username` getter") + } + } + + #[doc(alias = "proxy-username")] + pub fn set_proxy_username(&self, proxy_username: Option<&str>) { + unsafe { + glib::gobject_ffi::g_object_set_property( + self.as_ptr() as *mut glib::gobject_ffi::GObject, + b"proxy-username\0".as_ptr() as *const _, + proxy_username.to_value().to_glib_none().0, + ); + } + } + + pub fn is_reliable(&self) -> bool { + unsafe { + let mut value = glib::Value::from_type(::static_type()); + glib::gobject_ffi::g_object_get_property( + self.as_ptr() as *mut glib::gobject_ffi::GObject, + b"reliable\0".as_ptr() as *const _, + value.to_glib_none_mut().0, + ); + value + .get() + .expect("Return Value for property `reliable` getter") + } + } + + #[cfg(any(feature = "v0_1_15", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_15")))] + #[doc(alias = "stun-initial-timeout")] + pub fn stun_initial_timeout(&self) -> u32 { + unsafe { + let mut value = glib::Value::from_type(::static_type()); + glib::gobject_ffi::g_object_get_property( + self.as_ptr() as *mut glib::gobject_ffi::GObject, + b"stun-initial-timeout\0".as_ptr() as *const _, + value.to_glib_none_mut().0, + ); + value + .get() + .expect("Return Value for property `stun-initial-timeout` getter") + } + } + + #[cfg(any(feature = "v0_1_15", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_15")))] + #[doc(alias = "stun-initial-timeout")] + pub fn set_stun_initial_timeout(&self, stun_initial_timeout: u32) { + unsafe { + glib::gobject_ffi::g_object_set_property( + self.as_ptr() as *mut glib::gobject_ffi::GObject, + b"stun-initial-timeout\0".as_ptr() as *const _, + stun_initial_timeout.to_value().to_glib_none().0, + ); + } + } + + #[cfg(any(feature = "v0_1_15", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_15")))] + #[doc(alias = "stun-max-retransmissions")] + pub fn stun_max_retransmissions(&self) -> u32 { + unsafe { + let mut value = glib::Value::from_type(::static_type()); + glib::gobject_ffi::g_object_get_property( + self.as_ptr() as *mut glib::gobject_ffi::GObject, + b"stun-max-retransmissions\0".as_ptr() as *const _, + value.to_glib_none_mut().0, + ); + value + .get() + .expect("Return Value for property `stun-max-retransmissions` getter") + } + } + + #[cfg(any(feature = "v0_1_15", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_15")))] + #[doc(alias = "stun-max-retransmissions")] + pub fn set_stun_max_retransmissions(&self, stun_max_retransmissions: u32) { + unsafe { + glib::gobject_ffi::g_object_set_property( + self.as_ptr() as *mut glib::gobject_ffi::GObject, + b"stun-max-retransmissions\0".as_ptr() as *const _, + stun_max_retransmissions.to_value().to_glib_none().0, + ); + } + } + + #[doc(alias = "stun-pacing-timer")] + pub fn stun_pacing_timer(&self) -> u32 { + unsafe { + let mut value = glib::Value::from_type(::static_type()); + glib::gobject_ffi::g_object_get_property( + self.as_ptr() as *mut glib::gobject_ffi::GObject, + b"stun-pacing-timer\0".as_ptr() as *const _, + value.to_glib_none_mut().0, + ); + value + .get() + .expect("Return Value for property `stun-pacing-timer` getter") + } + } + + #[doc(alias = "stun-pacing-timer")] + pub fn set_stun_pacing_timer(&self, stun_pacing_timer: u32) { + unsafe { + glib::gobject_ffi::g_object_set_property( + self.as_ptr() as *mut glib::gobject_ffi::GObject, + b"stun-pacing-timer\0".as_ptr() as *const _, + stun_pacing_timer.to_value().to_glib_none().0, + ); + } + } + + #[cfg(any(feature = "v0_1_15", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_15")))] + #[doc(alias = "stun-reliable-timeout")] + pub fn stun_reliable_timeout(&self) -> u32 { + unsafe { + let mut value = glib::Value::from_type(::static_type()); + glib::gobject_ffi::g_object_get_property( + self.as_ptr() as *mut glib::gobject_ffi::GObject, + b"stun-reliable-timeout\0".as_ptr() as *const _, + value.to_glib_none_mut().0, + ); + value + .get() + .expect("Return Value for property `stun-reliable-timeout` getter") + } + } + + #[cfg(any(feature = "v0_1_15", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_15")))] + #[doc(alias = "stun-reliable-timeout")] + pub fn set_stun_reliable_timeout(&self, stun_reliable_timeout: u32) { + unsafe { + glib::gobject_ffi::g_object_set_property( + self.as_ptr() as *mut glib::gobject_ffi::GObject, + b"stun-reliable-timeout\0".as_ptr() as *const _, + stun_reliable_timeout.to_value().to_glib_none().0, + ); + } + } + + #[doc(alias = "stun-server")] + pub fn stun_server(&self) -> Option { + unsafe { + let mut value = glib::Value::from_type(::static_type()); + glib::gobject_ffi::g_object_get_property( + self.as_ptr() as *mut glib::gobject_ffi::GObject, + b"stun-server\0".as_ptr() as *const _, + value.to_glib_none_mut().0, + ); + value + .get() + .expect("Return Value for property `stun-server` getter") + } + } + + #[doc(alias = "stun-server")] + pub fn set_stun_server(&self, stun_server: Option<&str>) { + unsafe { + glib::gobject_ffi::g_object_set_property( + self.as_ptr() as *mut glib::gobject_ffi::GObject, + b"stun-server\0".as_ptr() as *const _, + stun_server.to_value().to_glib_none().0, + ); + } + } + + #[doc(alias = "stun-server-port")] + pub fn stun_server_port(&self) -> u32 { + unsafe { + let mut value = glib::Value::from_type(::static_type()); + glib::gobject_ffi::g_object_get_property( + self.as_ptr() as *mut glib::gobject_ffi::GObject, + b"stun-server-port\0".as_ptr() as *const _, + value.to_glib_none_mut().0, + ); + value + .get() + .expect("Return Value for property `stun-server-port` getter") + } + } + + #[doc(alias = "stun-server-port")] + pub fn set_stun_server_port(&self, stun_server_port: u32) { + unsafe { + glib::gobject_ffi::g_object_set_property( + self.as_ptr() as *mut glib::gobject_ffi::GObject, + b"stun-server-port\0".as_ptr() as *const _, + stun_server_port.to_value().to_glib_none().0, + ); + } + } + + #[doc(alias = "support-renomination")] + pub fn supports_renomination(&self) -> bool { + unsafe { + let mut value = glib::Value::from_type(::static_type()); + glib::gobject_ffi::g_object_get_property( + self.as_ptr() as *mut glib::gobject_ffi::GObject, + b"support-renomination\0".as_ptr() as *const _, + value.to_glib_none_mut().0, + ); + value + .get() + .expect("Return Value for property `support-renomination` getter") + } + } + + #[doc(alias = "support-renomination")] + pub fn set_support_renomination(&self, support_renomination: bool) { + unsafe { + glib::gobject_ffi::g_object_set_property( + self.as_ptr() as *mut glib::gobject_ffi::GObject, + b"support-renomination\0".as_ptr() as *const _, + support_renomination.to_value().to_glib_none().0, + ); + } + } + + pub fn is_upnp(&self) -> bool { + unsafe { + let mut value = glib::Value::from_type(::static_type()); + glib::gobject_ffi::g_object_get_property( + self.as_ptr() as *mut glib::gobject_ffi::GObject, + b"upnp\0".as_ptr() as *const _, + value.to_glib_none_mut().0, + ); + value + .get() + .expect("Return Value for property `upnp` getter") + } + } + + pub fn set_upnp(&self, upnp: bool) { + unsafe { + glib::gobject_ffi::g_object_set_property( + self.as_ptr() as *mut glib::gobject_ffi::GObject, + b"upnp\0".as_ptr() as *const _, + upnp.to_value().to_glib_none().0, + ); + } + } + + #[doc(alias = "upnp-timeout")] + pub fn upnp_timeout(&self) -> u32 { + unsafe { + let mut value = glib::Value::from_type(::static_type()); + glib::gobject_ffi::g_object_get_property( + self.as_ptr() as *mut glib::gobject_ffi::GObject, + b"upnp-timeout\0".as_ptr() as *const _, + value.to_glib_none_mut().0, + ); + value + .get() + .expect("Return Value for property `upnp-timeout` getter") + } + } + + #[doc(alias = "upnp-timeout")] + pub fn set_upnp_timeout(&self, upnp_timeout: u32) { + unsafe { + glib::gobject_ffi::g_object_set_property( + self.as_ptr() as *mut glib::gobject_ffi::GObject, + b"upnp-timeout\0".as_ptr() as *const _, + upnp_timeout.to_value().to_glib_none().0, + ); + } + } + + #[doc(alias = "candidate-gathering-done")] + pub fn connect_candidate_gathering_done( + &self, + f: F, + ) -> SignalHandlerId { + unsafe extern "C" fn candidate_gathering_done_trampoline( + this: *mut ffi::NiceAgent, + stream_id: libc::c_uint, + f: glib::ffi::gpointer, + ) { + let f: &F = &*(f as *const F); + f(&from_glib_borrow(this), stream_id) + } + unsafe { + let f: Box_ = Box_::new(f); + connect_raw( + self.as_ptr() as *mut _, + b"candidate-gathering-done\0".as_ptr() as *const _, + Some(transmute::<_, unsafe extern "C" fn()>( + candidate_gathering_done_trampoline:: as *const (), + )), + Box_::into_raw(f), + ) + } + } + + #[doc(alias = "component-state-changed")] + pub fn connect_component_state_changed( + &self, + f: F, + ) -> SignalHandlerId { + unsafe extern "C" fn component_state_changed_trampoline< + F: Fn(&Agent, u32, u32, u32) + 'static, + >( + this: *mut ffi::NiceAgent, + stream_id: libc::c_uint, + component_id: libc::c_uint, + state: libc::c_uint, + f: glib::ffi::gpointer, + ) { + let f: &F = &*(f as *const F); + f(&from_glib_borrow(this), stream_id, component_id, state) + } + unsafe { + let f: Box_ = Box_::new(f); + connect_raw( + self.as_ptr() as *mut _, + b"component-state-changed\0".as_ptr() as *const _, + Some(transmute::<_, unsafe extern "C" fn()>( + component_state_changed_trampoline:: as *const (), + )), + Box_::into_raw(f), + ) + } + } + + #[doc(alias = "initial-binding-request-received")] + pub fn connect_initial_binding_request_received( + &self, + f: F, + ) -> SignalHandlerId { + unsafe extern "C" fn initial_binding_request_received_trampoline< + F: Fn(&Agent, u32) + 'static, + >( + this: *mut ffi::NiceAgent, + stream_id: libc::c_uint, + f: glib::ffi::gpointer, + ) { + let f: &F = &*(f as *const F); + f(&from_glib_borrow(this), stream_id) + } + unsafe { + let f: Box_ = Box_::new(f); + connect_raw( + self.as_ptr() as *mut _, + b"initial-binding-request-received\0".as_ptr() as *const _, + Some(transmute::<_, unsafe extern "C" fn()>( + initial_binding_request_received_trampoline:: as *const (), + )), + Box_::into_raw(f), + ) + } + } + + #[cfg_attr(feature = "v0_1_8", deprecated = "Since 0.1.8")] + #[doc(alias = "new-candidate")] + pub fn connect_new_candidate( + &self, + f: F, + ) -> SignalHandlerId { + unsafe extern "C" fn new_candidate_trampoline( + this: *mut ffi::NiceAgent, + stream_id: libc::c_uint, + component_id: libc::c_uint, + foundation: *mut libc::c_char, + f: glib::ffi::gpointer, + ) { + let f: &F = &*(f as *const F); + f( + &from_glib_borrow(this), + stream_id, + component_id, + &glib::GString::from_glib_borrow(foundation), + ) + } + unsafe { + let f: Box_ = Box_::new(f); + connect_raw( + self.as_ptr() as *mut _, + b"new-candidate\0".as_ptr() as *const _, + Some(transmute::<_, unsafe extern "C" fn()>( + new_candidate_trampoline:: as *const (), + )), + Box_::into_raw(f), + ) + } + } + + #[cfg(any(feature = "v0_1_8", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_8")))] + #[doc(alias = "new-candidate-full")] + pub fn connect_new_candidate_full( + &self, + f: F, + ) -> SignalHandlerId { + unsafe extern "C" fn new_candidate_full_trampoline( + this: *mut ffi::NiceAgent, + candidate: *mut ffi::NiceCandidate, + f: glib::ffi::gpointer, + ) { + let f: &F = &*(f as *const F); + f(&from_glib_borrow(this), &from_glib_borrow(candidate)) + } + unsafe { + let f: Box_ = Box_::new(f); + connect_raw( + self.as_ptr() as *mut _, + b"new-candidate-full\0".as_ptr() as *const _, + Some(transmute::<_, unsafe extern "C" fn()>( + new_candidate_full_trampoline:: as *const (), + )), + Box_::into_raw(f), + ) + } + } + + #[cfg_attr(feature = "v0_1_8", deprecated = "Since 0.1.8")] + #[doc(alias = "new-remote-candidate")] + pub fn connect_new_remote_candidate( + &self, + f: F, + ) -> SignalHandlerId { + unsafe extern "C" fn new_remote_candidate_trampoline< + F: Fn(&Agent, u32, u32, &str) + 'static, + >( + this: *mut ffi::NiceAgent, + stream_id: libc::c_uint, + component_id: libc::c_uint, + foundation: *mut libc::c_char, + f: glib::ffi::gpointer, + ) { + let f: &F = &*(f as *const F); + f( + &from_glib_borrow(this), + stream_id, + component_id, + &glib::GString::from_glib_borrow(foundation), + ) + } + unsafe { + let f: Box_ = Box_::new(f); + connect_raw( + self.as_ptr() as *mut _, + b"new-remote-candidate\0".as_ptr() as *const _, + Some(transmute::<_, unsafe extern "C" fn()>( + new_remote_candidate_trampoline:: as *const (), + )), + Box_::into_raw(f), + ) + } + } + + #[cfg(any(feature = "v0_1_8", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_8")))] + #[doc(alias = "new-remote-candidate-full")] + pub fn connect_new_remote_candidate_full( + &self, + f: F, + ) -> SignalHandlerId { + unsafe extern "C" fn new_remote_candidate_full_trampoline< + F: Fn(&Agent, &Candidate) + 'static, + >( + this: *mut ffi::NiceAgent, + candidate: *mut ffi::NiceCandidate, + f: glib::ffi::gpointer, + ) { + let f: &F = &*(f as *const F); + f(&from_glib_borrow(this), &from_glib_borrow(candidate)) + } + unsafe { + let f: Box_ = Box_::new(f); + connect_raw( + self.as_ptr() as *mut _, + b"new-remote-candidate-full\0".as_ptr() as *const _, + Some(transmute::<_, unsafe extern "C" fn()>( + new_remote_candidate_full_trampoline:: as *const (), + )), + Box_::into_raw(f), + ) + } + } + + #[cfg_attr(feature = "v0_1_8", deprecated = "Since 0.1.8")] + #[doc(alias = "new-selected-pair")] + pub fn connect_new_selected_pair( + &self, + f: F, + ) -> SignalHandlerId { + unsafe extern "C" fn new_selected_pair_trampoline< + F: Fn(&Agent, u32, u32, &str, &str) + 'static, + >( + this: *mut ffi::NiceAgent, + stream_id: libc::c_uint, + component_id: libc::c_uint, + lfoundation: *mut libc::c_char, + rfoundation: *mut libc::c_char, + f: glib::ffi::gpointer, + ) { + let f: &F = &*(f as *const F); + f( + &from_glib_borrow(this), + stream_id, + component_id, + &glib::GString::from_glib_borrow(lfoundation), + &glib::GString::from_glib_borrow(rfoundation), + ) + } + unsafe { + let f: Box_ = Box_::new(f); + connect_raw( + self.as_ptr() as *mut _, + b"new-selected-pair\0".as_ptr() as *const _, + Some(transmute::<_, unsafe extern "C" fn()>( + new_selected_pair_trampoline:: as *const (), + )), + Box_::into_raw(f), + ) + } + } + + #[cfg(any(feature = "v0_1_8", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_8")))] + #[doc(alias = "new-selected-pair-full")] + pub fn connect_new_selected_pair_full< + F: Fn(&Self, u32, u32, &Candidate, &Candidate) + 'static, + >( + &self, + f: F, + ) -> SignalHandlerId { + unsafe extern "C" fn new_selected_pair_full_trampoline< + F: Fn(&Agent, u32, u32, &Candidate, &Candidate) + 'static, + >( + this: *mut ffi::NiceAgent, + stream_id: libc::c_uint, + component_id: libc::c_uint, + lcandidate: *mut ffi::NiceCandidate, + rcandidate: *mut ffi::NiceCandidate, + f: glib::ffi::gpointer, + ) { + let f: &F = &*(f as *const F); + f( + &from_glib_borrow(this), + stream_id, + component_id, + &from_glib_borrow(lcandidate), + &from_glib_borrow(rcandidate), + ) + } + unsafe { + let f: Box_ = Box_::new(f); + connect_raw( + self.as_ptr() as *mut _, + b"new-selected-pair-full\0".as_ptr() as *const _, + Some(transmute::<_, unsafe extern "C" fn()>( + new_selected_pair_full_trampoline:: as *const (), + )), + Box_::into_raw(f), + ) + } + } + + #[doc(alias = "reliable-transport-writable")] + pub fn connect_reliable_transport_writable( + &self, + f: F, + ) -> SignalHandlerId { + unsafe extern "C" fn reliable_transport_writable_trampoline< + F: Fn(&Agent, u32, u32) + 'static, + >( + this: *mut ffi::NiceAgent, + stream_id: libc::c_uint, + component_id: libc::c_uint, + f: glib::ffi::gpointer, + ) { + let f: &F = &*(f as *const F); + f(&from_glib_borrow(this), stream_id, component_id) + } + unsafe { + let f: Box_ = Box_::new(f); + connect_raw( + self.as_ptr() as *mut _, + b"reliable-transport-writable\0".as_ptr() as *const _, + Some(transmute::<_, unsafe extern "C" fn()>( + reliable_transport_writable_trampoline:: as *const (), + )), + Box_::into_raw(f), + ) + } + } + + //#[cfg(any(feature = "v0_1_5", feature = "dox"))] + //#[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_5")))] + //#[doc(alias = "streams-removed")] + //pub fn connect_streams_removed(&self, f: F) -> SignalHandlerId { + // Empty ctype stream_ids: *.CArray TypeId { ns_id: 0, id: 15 } + //} + + #[cfg(any(feature = "v0_1_8", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_8")))] + #[doc(alias = "bytestream-tcp")] + pub fn connect_bytestream_tcp_notify(&self, f: F) -> SignalHandlerId { + unsafe extern "C" fn notify_bytestream_tcp_trampoline( + this: *mut ffi::NiceAgent, + _param_spec: glib::ffi::gpointer, + f: glib::ffi::gpointer, + ) { + let f: &F = &*(f as *const F); + f(&from_glib_borrow(this)) + } + unsafe { + let f: Box_ = Box_::new(f); + connect_raw( + self.as_ptr() as *mut _, + b"notify::bytestream-tcp\0".as_ptr() as *const _, + Some(transmute::<_, unsafe extern "C" fn()>( + notify_bytestream_tcp_trampoline:: as *const (), + )), + Box_::into_raw(f), + ) + } + } + + #[doc(alias = "controlling-mode")] + pub fn connect_controlling_mode_notify(&self, f: F) -> SignalHandlerId { + unsafe extern "C" fn notify_controlling_mode_trampoline( + this: *mut ffi::NiceAgent, + _param_spec: glib::ffi::gpointer, + f: glib::ffi::gpointer, + ) { + let f: &F = &*(f as *const F); + f(&from_glib_borrow(this)) + } + unsafe { + let f: Box_ = Box_::new(f); + connect_raw( + self.as_ptr() as *mut _, + b"notify::controlling-mode\0".as_ptr() as *const _, + Some(transmute::<_, unsafe extern "C" fn()>( + notify_controlling_mode_trampoline:: as *const (), + )), + Box_::into_raw(f), + ) + } + } + + #[cfg(any(feature = "v0_1_14", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_14")))] + #[doc(alias = "force-relay")] + pub fn connect_force_relay_notify(&self, f: F) -> SignalHandlerId { + unsafe extern "C" fn notify_force_relay_trampoline( + this: *mut ffi::NiceAgent, + _param_spec: glib::ffi::gpointer, + f: glib::ffi::gpointer, + ) { + let f: &F = &*(f as *const F); + f(&from_glib_borrow(this)) + } + unsafe { + let f: Box_ = Box_::new(f); + connect_raw( + self.as_ptr() as *mut _, + b"notify::force-relay\0".as_ptr() as *const _, + Some(transmute::<_, unsafe extern "C" fn()>( + notify_force_relay_trampoline:: as *const (), + )), + Box_::into_raw(f), + ) + } + } + + #[cfg(any(feature = "v0_1_8", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_8")))] + #[doc(alias = "ice-tcp")] + pub fn connect_ice_tcp_notify(&self, f: F) -> SignalHandlerId { + unsafe extern "C" fn notify_ice_tcp_trampoline( + this: *mut ffi::NiceAgent, + _param_spec: glib::ffi::gpointer, + f: glib::ffi::gpointer, + ) { + let f: &F = &*(f as *const F); + f(&from_glib_borrow(this)) + } + unsafe { + let f: Box_ = Box_::new(f); + connect_raw( + self.as_ptr() as *mut _, + b"notify::ice-tcp\0".as_ptr() as *const _, + Some(transmute::<_, unsafe extern "C" fn()>( + notify_ice_tcp_trampoline:: as *const (), + )), + Box_::into_raw(f), + ) + } + } + + #[cfg(any(feature = "v0_1_16", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_16")))] + #[doc(alias = "ice-trickle")] + pub fn connect_ice_trickle_notify(&self, f: F) -> SignalHandlerId { + unsafe extern "C" fn notify_ice_trickle_trampoline( + this: *mut ffi::NiceAgent, + _param_spec: glib::ffi::gpointer, + f: glib::ffi::gpointer, + ) { + let f: &F = &*(f as *const F); + f(&from_glib_borrow(this)) + } + unsafe { + let f: Box_ = Box_::new(f); + connect_raw( + self.as_ptr() as *mut _, + b"notify::ice-trickle\0".as_ptr() as *const _, + Some(transmute::<_, unsafe extern "C" fn()>( + notify_ice_trickle_trampoline:: as *const (), + )), + Box_::into_raw(f), + ) + } + } + + #[cfg(any(feature = "v0_1_8", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_8")))] + #[doc(alias = "ice-udp")] + pub fn connect_ice_udp_notify(&self, f: F) -> SignalHandlerId { + unsafe extern "C" fn notify_ice_udp_trampoline( + this: *mut ffi::NiceAgent, + _param_spec: glib::ffi::gpointer, + f: glib::ffi::gpointer, + ) { + let f: &F = &*(f as *const F); + f(&from_glib_borrow(this)) + } + unsafe { + let f: Box_ = Box_::new(f); + connect_raw( + self.as_ptr() as *mut _, + b"notify::ice-udp\0".as_ptr() as *const _, + Some(transmute::<_, unsafe extern "C" fn()>( + notify_ice_udp_trampoline:: as *const (), + )), + Box_::into_raw(f), + ) + } + } + + #[cfg(any(feature = "v0_1_17", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_17")))] + #[doc(alias = "idle-timeout")] + pub fn connect_idle_timeout_notify(&self, f: F) -> SignalHandlerId { + unsafe extern "C" fn notify_idle_timeout_trampoline( + this: *mut ffi::NiceAgent, + _param_spec: glib::ffi::gpointer, + f: glib::ffi::gpointer, + ) { + let f: &F = &*(f as *const F); + f(&from_glib_borrow(this)) + } + unsafe { + let f: Box_ = Box_::new(f); + connect_raw( + self.as_ptr() as *mut _, + b"notify::idle-timeout\0".as_ptr() as *const _, + Some(transmute::<_, unsafe extern "C" fn()>( + notify_idle_timeout_trampoline:: as *const (), + )), + Box_::into_raw(f), + ) + } + } + + #[cfg(any(feature = "v0_1_8", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_8")))] + #[doc(alias = "keepalive-conncheck")] + pub fn connect_keepalive_conncheck_notify( + &self, + f: F, + ) -> SignalHandlerId { + unsafe extern "C" fn notify_keepalive_conncheck_trampoline( + this: *mut ffi::NiceAgent, + _param_spec: glib::ffi::gpointer, + f: glib::ffi::gpointer, + ) { + let f: &F = &*(f as *const F); + f(&from_glib_borrow(this)) + } + unsafe { + let f: Box_ = Box_::new(f); + connect_raw( + self.as_ptr() as *mut _, + b"notify::keepalive-conncheck\0".as_ptr() as *const _, + Some(transmute::<_, unsafe extern "C" fn()>( + notify_keepalive_conncheck_trampoline:: as *const (), + )), + Box_::into_raw(f), + ) + } + } + + #[doc(alias = "max-connectivity-checks")] + pub fn connect_max_connectivity_checks_notify( + &self, + f: F, + ) -> SignalHandlerId { + unsafe extern "C" fn notify_max_connectivity_checks_trampoline( + this: *mut ffi::NiceAgent, + _param_spec: glib::ffi::gpointer, + f: glib::ffi::gpointer, + ) { + let f: &F = &*(f as *const F); + f(&from_glib_borrow(this)) + } + unsafe { + let f: Box_ = Box_::new(f); + connect_raw( + self.as_ptr() as *mut _, + b"notify::max-connectivity-checks\0".as_ptr() as *const _, + Some(transmute::<_, unsafe extern "C" fn()>( + notify_max_connectivity_checks_trampoline:: as *const (), + )), + Box_::into_raw(f), + ) + } + } + + #[doc(alias = "proxy-ip")] + pub fn connect_proxy_ip_notify(&self, f: F) -> SignalHandlerId { + unsafe extern "C" fn notify_proxy_ip_trampoline( + this: *mut ffi::NiceAgent, + _param_spec: glib::ffi::gpointer, + f: glib::ffi::gpointer, + ) { + let f: &F = &*(f as *const F); + f(&from_glib_borrow(this)) + } + unsafe { + let f: Box_ = Box_::new(f); + connect_raw( + self.as_ptr() as *mut _, + b"notify::proxy-ip\0".as_ptr() as *const _, + Some(transmute::<_, unsafe extern "C" fn()>( + notify_proxy_ip_trampoline:: as *const (), + )), + Box_::into_raw(f), + ) + } + } + + #[doc(alias = "proxy-password")] + pub fn connect_proxy_password_notify(&self, f: F) -> SignalHandlerId { + unsafe extern "C" fn notify_proxy_password_trampoline( + this: *mut ffi::NiceAgent, + _param_spec: glib::ffi::gpointer, + f: glib::ffi::gpointer, + ) { + let f: &F = &*(f as *const F); + f(&from_glib_borrow(this)) + } + unsafe { + let f: Box_ = Box_::new(f); + connect_raw( + self.as_ptr() as *mut _, + b"notify::proxy-password\0".as_ptr() as *const _, + Some(transmute::<_, unsafe extern "C" fn()>( + notify_proxy_password_trampoline:: as *const (), + )), + Box_::into_raw(f), + ) + } + } + + #[doc(alias = "proxy-port")] + pub fn connect_proxy_port_notify(&self, f: F) -> SignalHandlerId { + unsafe extern "C" fn notify_proxy_port_trampoline( + this: *mut ffi::NiceAgent, + _param_spec: glib::ffi::gpointer, + f: glib::ffi::gpointer, + ) { + let f: &F = &*(f as *const F); + f(&from_glib_borrow(this)) + } + unsafe { + let f: Box_ = Box_::new(f); + connect_raw( + self.as_ptr() as *mut _, + b"notify::proxy-port\0".as_ptr() as *const _, + Some(transmute::<_, unsafe extern "C" fn()>( + notify_proxy_port_trampoline:: as *const (), + )), + Box_::into_raw(f), + ) + } + } + + #[doc(alias = "proxy-type")] + pub fn connect_proxy_type_notify(&self, f: F) -> SignalHandlerId { + unsafe extern "C" fn notify_proxy_type_trampoline( + this: *mut ffi::NiceAgent, + _param_spec: glib::ffi::gpointer, + f: glib::ffi::gpointer, + ) { + let f: &F = &*(f as *const F); + f(&from_glib_borrow(this)) + } + unsafe { + let f: Box_ = Box_::new(f); + connect_raw( + self.as_ptr() as *mut _, + b"notify::proxy-type\0".as_ptr() as *const _, + Some(transmute::<_, unsafe extern "C" fn()>( + notify_proxy_type_trampoline:: as *const (), + )), + Box_::into_raw(f), + ) + } + } + + #[doc(alias = "proxy-username")] + pub fn connect_proxy_username_notify(&self, f: F) -> SignalHandlerId { + unsafe extern "C" fn notify_proxy_username_trampoline( + this: *mut ffi::NiceAgent, + _param_spec: glib::ffi::gpointer, + f: glib::ffi::gpointer, + ) { + let f: &F = &*(f as *const F); + f(&from_glib_borrow(this)) + } + unsafe { + let f: Box_ = Box_::new(f); + connect_raw( + self.as_ptr() as *mut _, + b"notify::proxy-username\0".as_ptr() as *const _, + Some(transmute::<_, unsafe extern "C" fn()>( + notify_proxy_username_trampoline:: as *const (), + )), + Box_::into_raw(f), + ) + } + } + + #[cfg(any(feature = "v0_1_15", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_15")))] + #[doc(alias = "stun-initial-timeout")] + pub fn connect_stun_initial_timeout_notify( + &self, + f: F, + ) -> SignalHandlerId { + unsafe extern "C" fn notify_stun_initial_timeout_trampoline( + this: *mut ffi::NiceAgent, + _param_spec: glib::ffi::gpointer, + f: glib::ffi::gpointer, + ) { + let f: &F = &*(f as *const F); + f(&from_glib_borrow(this)) + } + unsafe { + let f: Box_ = Box_::new(f); + connect_raw( + self.as_ptr() as *mut _, + b"notify::stun-initial-timeout\0".as_ptr() as *const _, + Some(transmute::<_, unsafe extern "C" fn()>( + notify_stun_initial_timeout_trampoline:: as *const (), + )), + Box_::into_raw(f), + ) + } + } + + #[cfg(any(feature = "v0_1_15", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_15")))] + #[doc(alias = "stun-max-retransmissions")] + pub fn connect_stun_max_retransmissions_notify( + &self, + f: F, + ) -> SignalHandlerId { + unsafe extern "C" fn notify_stun_max_retransmissions_trampoline( + this: *mut ffi::NiceAgent, + _param_spec: glib::ffi::gpointer, + f: glib::ffi::gpointer, + ) { + let f: &F = &*(f as *const F); + f(&from_glib_borrow(this)) + } + unsafe { + let f: Box_ = Box_::new(f); + connect_raw( + self.as_ptr() as *mut _, + b"notify::stun-max-retransmissions\0".as_ptr() as *const _, + Some(transmute::<_, unsafe extern "C" fn()>( + notify_stun_max_retransmissions_trampoline:: as *const (), + )), + Box_::into_raw(f), + ) + } + } + + #[doc(alias = "stun-pacing-timer")] + pub fn connect_stun_pacing_timer_notify(&self, f: F) -> SignalHandlerId { + unsafe extern "C" fn notify_stun_pacing_timer_trampoline( + this: *mut ffi::NiceAgent, + _param_spec: glib::ffi::gpointer, + f: glib::ffi::gpointer, + ) { + let f: &F = &*(f as *const F); + f(&from_glib_borrow(this)) + } + unsafe { + let f: Box_ = Box_::new(f); + connect_raw( + self.as_ptr() as *mut _, + b"notify::stun-pacing-timer\0".as_ptr() as *const _, + Some(transmute::<_, unsafe extern "C" fn()>( + notify_stun_pacing_timer_trampoline:: as *const (), + )), + Box_::into_raw(f), + ) + } + } + + #[cfg(any(feature = "v0_1_15", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_15")))] + #[doc(alias = "stun-reliable-timeout")] + pub fn connect_stun_reliable_timeout_notify( + &self, + f: F, + ) -> SignalHandlerId { + unsafe extern "C" fn notify_stun_reliable_timeout_trampoline( + this: *mut ffi::NiceAgent, + _param_spec: glib::ffi::gpointer, + f: glib::ffi::gpointer, + ) { + let f: &F = &*(f as *const F); + f(&from_glib_borrow(this)) + } + unsafe { + let f: Box_ = Box_::new(f); + connect_raw( + self.as_ptr() as *mut _, + b"notify::stun-reliable-timeout\0".as_ptr() as *const _, + Some(transmute::<_, unsafe extern "C" fn()>( + notify_stun_reliable_timeout_trampoline:: as *const (), + )), + Box_::into_raw(f), + ) + } + } + + #[doc(alias = "stun-server")] + pub fn connect_stun_server_notify(&self, f: F) -> SignalHandlerId { + unsafe extern "C" fn notify_stun_server_trampoline( + this: *mut ffi::NiceAgent, + _param_spec: glib::ffi::gpointer, + f: glib::ffi::gpointer, + ) { + let f: &F = &*(f as *const F); + f(&from_glib_borrow(this)) + } + unsafe { + let f: Box_ = Box_::new(f); + connect_raw( + self.as_ptr() as *mut _, + b"notify::stun-server\0".as_ptr() as *const _, + Some(transmute::<_, unsafe extern "C" fn()>( + notify_stun_server_trampoline:: as *const (), + )), + Box_::into_raw(f), + ) + } + } + + #[doc(alias = "stun-server-port")] + pub fn connect_stun_server_port_notify(&self, f: F) -> SignalHandlerId { + unsafe extern "C" fn notify_stun_server_port_trampoline( + this: *mut ffi::NiceAgent, + _param_spec: glib::ffi::gpointer, + f: glib::ffi::gpointer, + ) { + let f: &F = &*(f as *const F); + f(&from_glib_borrow(this)) + } + unsafe { + let f: Box_ = Box_::new(f); + connect_raw( + self.as_ptr() as *mut _, + b"notify::stun-server-port\0".as_ptr() as *const _, + Some(transmute::<_, unsafe extern "C" fn()>( + notify_stun_server_port_trampoline:: as *const (), + )), + Box_::into_raw(f), + ) + } + } + + #[doc(alias = "support-renomination")] + pub fn connect_support_renomination_notify( + &self, + f: F, + ) -> SignalHandlerId { + unsafe extern "C" fn notify_support_renomination_trampoline( + this: *mut ffi::NiceAgent, + _param_spec: glib::ffi::gpointer, + f: glib::ffi::gpointer, + ) { + let f: &F = &*(f as *const F); + f(&from_glib_borrow(this)) + } + unsafe { + let f: Box_ = Box_::new(f); + connect_raw( + self.as_ptr() as *mut _, + b"notify::support-renomination\0".as_ptr() as *const _, + Some(transmute::<_, unsafe extern "C" fn()>( + notify_support_renomination_trampoline:: as *const (), + )), + Box_::into_raw(f), + ) + } + } + + #[doc(alias = "upnp")] + pub fn connect_upnp_notify(&self, f: F) -> SignalHandlerId { + unsafe extern "C" fn notify_upnp_trampoline( + this: *mut ffi::NiceAgent, + _param_spec: glib::ffi::gpointer, + f: glib::ffi::gpointer, + ) { + let f: &F = &*(f as *const F); + f(&from_glib_borrow(this)) + } + unsafe { + let f: Box_ = Box_::new(f); + connect_raw( + self.as_ptr() as *mut _, + b"notify::upnp\0".as_ptr() as *const _, + Some(transmute::<_, unsafe extern "C" fn()>( + notify_upnp_trampoline:: as *const (), + )), + Box_::into_raw(f), + ) + } + } + + #[doc(alias = "upnp-timeout")] + pub fn connect_upnp_timeout_notify(&self, f: F) -> SignalHandlerId { + unsafe extern "C" fn notify_upnp_timeout_trampoline( + this: *mut ffi::NiceAgent, + _param_spec: glib::ffi::gpointer, + f: glib::ffi::gpointer, + ) { + let f: &F = &*(f as *const F); + f(&from_glib_borrow(this)) + } + unsafe { + let f: Box_ = Box_::new(f); + connect_raw( + self.as_ptr() as *mut _, + b"notify::upnp-timeout\0".as_ptr() as *const _, + Some(transmute::<_, unsafe extern "C" fn()>( + notify_upnp_timeout_trampoline:: as *const (), + )), + Box_::into_raw(f), + ) + } + } +} + +impl fmt::Display for Agent { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str("Agent") + } +} diff --git a/nice-gst-meet/src/candidate.rs b/nice-gst-meet/src/candidate.rs new file mode 100644 index 0000000..9880c9a --- /dev/null +++ b/nice-gst-meet/src/candidate.rs @@ -0,0 +1,206 @@ +// Generated by gir (https://github.com/gtk-rs/gir @ 5bbf6cb) +// from ../../gir-files (@ 8e47c67) +// DO NOT EDIT + +use std::{ffi::CStr, net::SocketAddr}; + +use glib::translate::*; +use libc::c_char; +use nice_sys as ffi; +use nix::sys::socket::{AddressFamily, InetAddr}; + +#[cfg(any(feature = "v0_1_18", feature = "dox"))] +#[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_18")))] +use crate::CandidateTransport; +use crate::CandidateType; + +glib::wrapper! { + #[derive(PartialEq, Eq, PartialOrd, Ord, Hash)] + pub struct Candidate(Boxed); + + match fn { + copy => |ptr| ffi::nice_candidate_copy(ptr), + free => |ptr| ffi::nice_candidate_free(ptr), + type_ => || ffi::nice_candidate_get_type(), + } +} + +impl ::std::fmt::Debug for Candidate { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + f.debug_struct("Candidate") + .field("type_", &self.type_()) + .field("foundation", &self.foundation()) + .field("transport", &self.transport()) + .field("addr", &self.addr()) + .field("priority", &self.priority()) + .field("stream_id", &self.stream_id()) + .field("component_id", &self.component_id()) + .field("username", &self.username()) + .field("password", &self.password()) + .finish() + } +} + +unsafe impl Send for Candidate {} + +impl<'a> ToGlibPtr<'a, *mut ffi::NiceCandidate> for Candidate { + type Storage = &'a Self; + + #[inline] + fn to_glib_none(&'a self) -> Stash<'a, *mut ffi::NiceCandidate, Self> { + Stash(&*self.0 as *const _ as *mut _, self) + } +} + +impl Candidate { + #[doc(alias = "nice_candidate_new")] + pub fn new(type_: CandidateType) -> Candidate { + unsafe { from_glib_full(ffi::nice_candidate_new(type_.into_glib())) } + } + + pub fn type_(&self) -> CandidateType { + unsafe { CandidateType::from_glib(self.0.type_) } + } + + #[cfg(any(feature = "v0_1_18", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_18")))] + pub fn transport(&self) -> CandidateTransport { + unsafe { CandidateTransport::from_glib(self.0.transport) } + } + + #[cfg(any(feature = "v0_1_18", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_18")))] + pub fn set_transport(&mut self, transport: CandidateTransport) { + self.0.transport = transport.into_glib(); + } + + pub fn addr(&self) -> SocketAddr { + unsafe { + match AddressFamily::from_i32(self.0.addr.s.addr.sa_family as i32).unwrap() { + AddressFamily::Inet => InetAddr::V4(self.0.addr.s.ip4).to_std(), + AddressFamily::Inet6 => InetAddr::V6(self.0.addr.s.ip6).to_std(), + other => panic!("unsupported address family: {:?}", other), + } + } + } + + pub fn set_addr(&mut self, addr: SocketAddr) { + match InetAddr::from_std(&addr) { + InetAddr::V4(ip4) => unsafe { + ffi::nice_address_set_ipv4( + &mut self.0.addr as *mut _, + u32::from_be(ip4.sin_addr.s_addr), + ); + ffi::nice_address_set_port( + &mut self.0.addr as *mut _, + u16::from_be(ip4.sin_port) as u32, + ); + }, + InetAddr::V6(ip6) => unsafe { + ffi::nice_address_set_ipv6( + &mut self.0.addr as *mut _, + &ip6.sin6_addr.s6_addr as *const _, + ); + ffi::nice_address_set_port( + &mut self.0.addr as *mut _, + u16::from_be(ip6.sin6_port) as u32, + ); + }, + } + } + + pub fn priority(&self) -> u32 { + self.0.priority + } + + pub fn set_priority(&mut self, priority: u32) { + self.0.priority = priority; + } + + pub fn stream_id(&self) -> u32 { + self.0.stream_id + } + + pub fn set_stream_id(&mut self, stream_id: u32) { + self.0.stream_id = stream_id; + } + + pub fn component_id(&self) -> u32 { + self.0.component_id + } + + pub fn set_component_id(&mut self, component_id: u32) { + self.0.component_id = component_id; + } + + pub fn foundation(&self) -> Result<&str, std::str::Utf8Error> { + unsafe { CStr::from_ptr(&self.0.foundation as *const c_char).to_str() } + } + + pub fn set_foundation(&mut self, foundation: &str) { + let mut bytes: Vec<_> = foundation + .as_bytes() + .iter() + .take(32) + .map(|c| *c as i8) + .collect(); + bytes.resize(33, 0); + self.0.foundation.copy_from_slice(&bytes); + } + + pub fn username(&self) -> Result<&str, std::str::Utf8Error> { + if self.0.username.is_null() { + Ok("") + } + else { + unsafe { CStr::from_ptr(self.0.username).to_str() } + } + } + + pub fn set_username(&mut self, username: &str) { + self.0.username = username.to_owned().to_glib_full(); + } + + pub fn password(&self) -> Result<&str, std::str::Utf8Error> { + if self.0.password.is_null() { + Ok("") + } + else { + unsafe { CStr::from_ptr(self.0.password).to_str() } + } + } + + pub fn set_password(&mut self, password: &str) { + self.0.password = password.to_owned().to_glib_full(); + } + + #[cfg(any(feature = "v0_1_15", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_15")))] + #[doc(alias = "nice_candidate_equal_target")] + pub fn equal_target(&self, candidate2: &Candidate) -> bool { + unsafe { + from_glib(ffi::nice_candidate_equal_target( + self.to_glib_none().0, + candidate2.to_glib_none().0, + )) + } + } + + #[cfg(any(feature = "v0_1_18", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_18")))] + #[doc(alias = "nice_candidate_transport_to_string")] + pub fn transport_to_string(transport: CandidateTransport) -> Option { + unsafe { + from_glib_none(ffi::nice_candidate_transport_to_string( + transport.into_glib(), + )) + } + } + + #[cfg(any(feature = "v0_1_18", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_18")))] + #[doc(alias = "nice_candidate_type_to_string")] + pub fn type_to_string(type_: CandidateType) -> Option { + unsafe { from_glib_none(ffi::nice_candidate_type_to_string(type_.into_glib())) } + } +} diff --git a/nice-gst-meet/src/enums.rs b/nice-gst-meet/src/enums.rs new file mode 100644 index 0000000..6a75ed9 --- /dev/null +++ b/nice-gst-meet/src/enums.rs @@ -0,0 +1,328 @@ +// Generated by gir (https://github.com/gtk-rs/gir @ 5bbf6cb) +// from ../../gir-files (@ 8e47c67) +// DO NOT EDIT + +use std::fmt; + +use glib::translate::*; +use nice_sys as ffi; + +#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy)] +#[non_exhaustive] +#[doc(alias = "NiceCandidateTransport")] +pub enum CandidateTransport { + #[doc(alias = "NICE_CANDIDATE_TRANSPORT_UDP")] + Udp, + #[doc(alias = "NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE")] + TcpActive, + #[doc(alias = "NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE")] + TcpPassive, + #[doc(alias = "NICE_CANDIDATE_TRANSPORT_TCP_SO")] + TcpSo, + #[doc(hidden)] + __Unknown(i32), +} + +impl fmt::Display for CandidateTransport { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "CandidateTransport::{}", + match *self { + Self::Udp => "Udp", + Self::TcpActive => "TcpActive", + Self::TcpPassive => "TcpPassive", + Self::TcpSo => "TcpSo", + _ => "Unknown", + } + ) + } +} + +#[doc(hidden)] +impl IntoGlib for CandidateTransport { + type GlibType = ffi::NiceCandidateTransport; + + fn into_glib(self) -> ffi::NiceCandidateTransport { + match self { + Self::Udp => ffi::NICE_CANDIDATE_TRANSPORT_UDP, + Self::TcpActive => ffi::NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE, + Self::TcpPassive => ffi::NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE, + Self::TcpSo => ffi::NICE_CANDIDATE_TRANSPORT_TCP_SO, + Self::__Unknown(value) => value, + } + } +} + +#[doc(hidden)] +impl FromGlib for CandidateTransport { + unsafe fn from_glib(value: ffi::NiceCandidateTransport) -> Self { + match value { + ffi::NICE_CANDIDATE_TRANSPORT_UDP => Self::Udp, + ffi::NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE => Self::TcpActive, + ffi::NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE => Self::TcpPassive, + ffi::NICE_CANDIDATE_TRANSPORT_TCP_SO => Self::TcpSo, + value => Self::__Unknown(value), + } + } +} + +#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy)] +#[non_exhaustive] +#[doc(alias = "NiceCandidateType")] +pub enum CandidateType { + #[doc(alias = "NICE_CANDIDATE_TYPE_HOST")] + Host, + #[doc(alias = "NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE")] + ServerReflexive, + #[doc(alias = "NICE_CANDIDATE_TYPE_PEER_REFLEXIVE")] + PeerReflexive, + #[doc(alias = "NICE_CANDIDATE_TYPE_RELAYED")] + Relayed, + #[doc(hidden)] + __Unknown(i32), +} + +impl fmt::Display for CandidateType { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "CandidateType::{}", + match *self { + Self::Host => "Host", + Self::ServerReflexive => "ServerReflexive", + Self::PeerReflexive => "PeerReflexive", + Self::Relayed => "Relayed", + _ => "Unknown", + } + ) + } +} + +#[doc(hidden)] +impl IntoGlib for CandidateType { + type GlibType = ffi::NiceCandidateType; + + fn into_glib(self) -> ffi::NiceCandidateType { + match self { + Self::Host => ffi::NICE_CANDIDATE_TYPE_HOST, + Self::ServerReflexive => ffi::NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE, + Self::PeerReflexive => ffi::NICE_CANDIDATE_TYPE_PEER_REFLEXIVE, + Self::Relayed => ffi::NICE_CANDIDATE_TYPE_RELAYED, + Self::__Unknown(value) => value, + } + } +} + +#[doc(hidden)] +impl FromGlib for CandidateType { + unsafe fn from_glib(value: ffi::NiceCandidateType) -> Self { + match value { + ffi::NICE_CANDIDATE_TYPE_HOST => Self::Host, + ffi::NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE => Self::ServerReflexive, + ffi::NICE_CANDIDATE_TYPE_PEER_REFLEXIVE => Self::PeerReflexive, + ffi::NICE_CANDIDATE_TYPE_RELAYED => Self::Relayed, + value => Self::__Unknown(value), + } + } +} + +#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy)] +#[non_exhaustive] +#[doc(alias = "NiceCompatibility")] +pub enum Compatibility { + #[doc(alias = "NICE_COMPATIBILITY_RFC5245")] + Rfc5245, + #[doc(alias = "NICE_COMPATIBILITY_GOOGLE")] + Google, + #[doc(alias = "NICE_COMPATIBILITY_MSN")] + Msn, + #[doc(alias = "NICE_COMPATIBILITY_WLM2009")] + Wlm2009, + #[doc(alias = "NICE_COMPATIBILITY_OC2007")] + Oc2007, + #[doc(alias = "NICE_COMPATIBILITY_OC2007R2")] + Oc2007r2, + #[doc(hidden)] + __Unknown(i32), +} + +impl fmt::Display for Compatibility { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "Compatibility::{}", + match *self { + Self::Rfc5245 => "Rfc5245", + Self::Google => "Google", + Self::Msn => "Msn", + Self::Wlm2009 => "Wlm2009", + Self::Oc2007 => "Oc2007", + Self::Oc2007r2 => "Oc2007r2", + _ => "Unknown", + } + ) + } +} + +#[doc(hidden)] +impl IntoGlib for Compatibility { + type GlibType = ffi::NiceCompatibility; + + fn into_glib(self) -> ffi::NiceCompatibility { + match self { + Self::Rfc5245 => ffi::NICE_COMPATIBILITY_RFC5245, + Self::Google => ffi::NICE_COMPATIBILITY_GOOGLE, + Self::Msn => ffi::NICE_COMPATIBILITY_MSN, + Self::Wlm2009 => ffi::NICE_COMPATIBILITY_WLM2009, + Self::Oc2007 => ffi::NICE_COMPATIBILITY_OC2007, + Self::Oc2007r2 => ffi::NICE_COMPATIBILITY_OC2007R2, + Self::__Unknown(value) => value, + } + } +} + +#[doc(hidden)] +impl FromGlib for Compatibility { + unsafe fn from_glib(value: ffi::NiceCompatibility) -> Self { + match value { + ffi::NICE_COMPATIBILITY_RFC5245 => Self::Rfc5245, + ffi::NICE_COMPATIBILITY_GOOGLE => Self::Google, + ffi::NICE_COMPATIBILITY_MSN => Self::Msn, + ffi::NICE_COMPATIBILITY_WLM2009 => Self::Wlm2009, + ffi::NICE_COMPATIBILITY_OC2007 => Self::Oc2007, + ffi::NICE_COMPATIBILITY_OC2007R2 => Self::Oc2007r2, + value => Self::__Unknown(value), + } + } +} + +#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy)] +#[non_exhaustive] +#[doc(alias = "NiceComponentState")] +pub enum ComponentState { + #[doc(alias = "NICE_COMPONENT_STATE_DISCONNECTED")] + Disconnected, + #[doc(alias = "NICE_COMPONENT_STATE_GATHERING")] + Gathering, + #[doc(alias = "NICE_COMPONENT_STATE_CONNECTING")] + Connecting, + #[doc(alias = "NICE_COMPONENT_STATE_CONNECTED")] + Connected, + #[doc(alias = "NICE_COMPONENT_STATE_READY")] + Ready, + #[doc(alias = "NICE_COMPONENT_STATE_FAILED")] + Failed, + #[doc(alias = "NICE_COMPONENT_STATE_LAST")] + Last, + #[doc(hidden)] + __Unknown(i32), +} + +impl fmt::Display for ComponentState { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "ComponentState::{}", + match *self { + Self::Disconnected => "Disconnected", + Self::Gathering => "Gathering", + Self::Connecting => "Connecting", + Self::Connected => "Connected", + Self::Ready => "Ready", + Self::Failed => "Failed", + Self::Last => "Last", + _ => "Unknown", + } + ) + } +} + +#[doc(hidden)] +impl IntoGlib for ComponentState { + type GlibType = ffi::NiceComponentState; + + fn into_glib(self) -> ffi::NiceComponentState { + match self { + Self::Disconnected => ffi::NICE_COMPONENT_STATE_DISCONNECTED, + Self::Gathering => ffi::NICE_COMPONENT_STATE_GATHERING, + Self::Connecting => ffi::NICE_COMPONENT_STATE_CONNECTING, + Self::Connected => ffi::NICE_COMPONENT_STATE_CONNECTED, + Self::Ready => ffi::NICE_COMPONENT_STATE_READY, + Self::Failed => ffi::NICE_COMPONENT_STATE_FAILED, + Self::Last => ffi::NICE_COMPONENT_STATE_LAST, + Self::__Unknown(value) => value, + } + } +} + +#[doc(hidden)] +impl FromGlib for ComponentState { + unsafe fn from_glib(value: ffi::NiceComponentState) -> Self { + match value { + ffi::NICE_COMPONENT_STATE_DISCONNECTED => Self::Disconnected, + ffi::NICE_COMPONENT_STATE_GATHERING => Self::Gathering, + ffi::NICE_COMPONENT_STATE_CONNECTING => Self::Connecting, + ffi::NICE_COMPONENT_STATE_CONNECTED => Self::Connected, + ffi::NICE_COMPONENT_STATE_READY => Self::Ready, + ffi::NICE_COMPONENT_STATE_FAILED => Self::Failed, + ffi::NICE_COMPONENT_STATE_LAST => Self::Last, + value => Self::__Unknown(value), + } + } +} + +#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy)] +#[non_exhaustive] +#[doc(alias = "NiceRelayType")] +pub enum RelayType { + #[doc(alias = "NICE_RELAY_TYPE_TURN_UDP")] + Udp, + #[doc(alias = "NICE_RELAY_TYPE_TURN_TCP")] + Tcp, + #[doc(alias = "NICE_RELAY_TYPE_TURN_TLS")] + Tls, + #[doc(hidden)] + __Unknown(i32), +} + +impl fmt::Display for RelayType { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "RelayType::{}", + match *self { + Self::Udp => "Udp", + Self::Tcp => "Tcp", + Self::Tls => "Tls", + _ => "Unknown", + } + ) + } +} + +#[doc(hidden)] +impl IntoGlib for RelayType { + type GlibType = ffi::NiceRelayType; + + fn into_glib(self) -> ffi::NiceRelayType { + match self { + Self::Udp => ffi::NICE_RELAY_TYPE_TURN_UDP, + Self::Tcp => ffi::NICE_RELAY_TYPE_TURN_TCP, + Self::Tls => ffi::NICE_RELAY_TYPE_TURN_TLS, + Self::__Unknown(value) => value, + } + } +} + +#[doc(hidden)] +impl FromGlib for RelayType { + unsafe fn from_glib(value: ffi::NiceRelayType) -> Self { + match value { + ffi::NICE_RELAY_TYPE_TURN_UDP => Self::Udp, + ffi::NICE_RELAY_TYPE_TURN_TCP => Self::Tcp, + ffi::NICE_RELAY_TYPE_TURN_TLS => Self::Tls, + value => Self::__Unknown(value), + } + } +} diff --git a/nice-gst-meet/src/flags.rs b/nice-gst-meet/src/flags.rs new file mode 100644 index 0000000..4baebbd --- /dev/null +++ b/nice-gst-meet/src/flags.rs @@ -0,0 +1,65 @@ +// Generated by gir (https://github.com/gtk-rs/gir @ 5bbf6cb) +// from ../../gir-files (@ 8e47c67) +// DO NOT EDIT + +#[cfg(any(feature = "v0_1_15", feature = "dox"))] +#[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_15")))] +use std::fmt; + +#[cfg(any(feature = "v0_1_15", feature = "dox"))] +#[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_15")))] +use bitflags::bitflags; +#[cfg(any(feature = "v0_1_15", feature = "dox"))] +#[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_15")))] +use glib::translate::*; +#[cfg(any(feature = "v0_1_15", feature = "dox"))] +#[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_15")))] +use nice_sys as ffi; + +#[cfg(any(feature = "v0_1_15", feature = "dox"))] +#[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_15")))] +bitflags! { + #[doc(alias = "NiceAgentOption")] + pub struct AgentOption: u32 { + #[doc(alias = "NICE_AGENT_OPTION_REGULAR_NOMINATION")] + const REGULAR_NOMINATION = ffi::NICE_AGENT_OPTION_REGULAR_NOMINATION as u32; + #[doc(alias = "NICE_AGENT_OPTION_RELIABLE")] + const RELIABLE = ffi::NICE_AGENT_OPTION_RELIABLE as u32; + #[doc(alias = "NICE_AGENT_OPTION_LITE_MODE")] + const LITE_MODE = ffi::NICE_AGENT_OPTION_LITE_MODE as u32; + #[doc(alias = "NICE_AGENT_OPTION_ICE_TRICKLE")] + const ICE_TRICKLE = ffi::NICE_AGENT_OPTION_ICE_TRICKLE as u32; + #[doc(alias = "NICE_AGENT_OPTION_SUPPORT_RENOMINATION")] + const SUPPORT_RENOMINATION = ffi::NICE_AGENT_OPTION_SUPPORT_RENOMINATION as u32; + #[doc(alias = "NICE_AGENT_OPTION_CONSENT_FRESHNESS")] + const CONSENT_FRESHNESS = ffi::NICE_AGENT_OPTION_CONSENT_FRESHNESS as u32; + } +} + +#[cfg(any(feature = "v0_1_15", feature = "dox"))] +#[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_15")))] +impl fmt::Display for AgentOption { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + ::fmt(self, f) + } +} + +#[cfg(any(feature = "v0_1_15", feature = "dox"))] +#[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_15")))] +#[doc(hidden)] +impl IntoGlib for AgentOption { + type GlibType = ffi::NiceAgentOption; + + fn into_glib(self) -> ffi::NiceAgentOption { + self.bits() + } +} + +#[cfg(any(feature = "v0_1_15", feature = "dox"))] +#[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_15")))] +#[doc(hidden)] +impl FromGlib for AgentOption { + unsafe fn from_glib(value: ffi::NiceAgentOption) -> Self { + Self::from_bits_truncate(value) + } +} diff --git a/nice-gst-meet/src/lib.rs b/nice-gst-meet/src/lib.rs new file mode 100644 index 0000000..af4c860 --- /dev/null +++ b/nice-gst-meet/src/lib.rs @@ -0,0 +1,33 @@ +// Generated by gir (https://github.com/gtk-rs/gir @ 5bbf6cb) +// from ../../gir-files (@ 8e47c67) +// DO NOT EDIT + +mod agent; +pub use self::agent::Agent; + +mod candidate; +pub use self::candidate::Candidate; + +mod enums; +pub use self::enums::{ + CandidateTransport, CandidateType, Compatibility, ComponentState, RelayType, +}; + +mod flags; +use nice_sys as ffi; + +#[cfg(any(feature = "v0_1_15", feature = "dox"))] +#[cfg_attr(feature = "dox", doc(cfg(feature = "v0_1_15")))] +pub use self::flags::AgentOption; + +pub fn debug_enable(with_stun: bool) { + unsafe { + ffi::nice_debug_enable(with_stun as i32); + } +} + +pub fn debug_disable(with_stun: bool) { + unsafe { + ffi::nice_debug_disable(with_stun as i32); + } +} diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..3882951 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,13 @@ +tab_spaces = 2 +use_field_init_shorthand = true +use_try_shorthand = true +control_brace_style = "ClosingNextLine" +condense_wildcard_suffixes = true +match_block_trailing_comma = true +imports_granularity = "Crate" +newline_style = "Unix" +reorder_impl_items = true +group_imports = "StdExternalCrate" +report_fixme = "Unnumbered" +report_todo = "Unnumbered" +version = "Two"