diff --git a/examples/Cargo.lock b/examples/Cargo.lock index 4e0647a5c..352819acd 100644 --- a/examples/Cargo.lock +++ b/examples/Cargo.lock @@ -139,7 +139,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c494134f746c14dc653a35a4ea5aca24ac368529da5370ecf41fe0341c35772f" dependencies = [ "android_log-sys", - "env_logger", + "env_logger 0.10.2", "log", "once_cell", ] @@ -440,8 +440,8 @@ dependencies = [ "bytes", "futures-util", "http 0.2.12", - "http-body", - "hyper", + "http-body 0.4.6", + "hyper 0.14.32", "itoa", "matchit", "memchr", @@ -450,8 +450,8 @@ dependencies = [ "pin-project-lite", "rustversion", "serde", - "sync_wrapper", - "tower", + "sync_wrapper 0.1.2", + "tower 0.4.13", "tower-layer", "tower-service", ] @@ -466,7 +466,7 @@ dependencies = [ "bytes", "futures-util", "http 0.2.12", - "http-body", + "http-body 0.4.6", "mime", "rustversion", "tower-layer", @@ -510,7 +510,7 @@ checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" name = "basic_room" version = "0.1.0" dependencies = [ - "env_logger", + "env_logger 0.10.2", "livekit", "livekit-api", "log", @@ -735,6 +735,16 @@ dependencies = [ "nom", ] +[[package]] +name = "cfg-expr" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a2c5f3bf25ec225351aa1c8e230d04d880d3bd89dea133537dafad4ae291e5c" +dependencies = [ + "smallvec", + "target-lexicon", +] + [[package]] name = "cfg-if" version = "1.0.3" @@ -1366,15 +1376,6 @@ dependencies = [ "serde", ] -[[package]] -name = "encoding_rs" -version = "0.8.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" -dependencies = [ - "cfg-if", -] - [[package]] name = "enumn" version = "0.1.14" @@ -1386,6 +1387,15 @@ dependencies = [ "syn 2.0.106", ] +[[package]] +name = "env_filter" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bf3c259d255ca70051b30e2e95b5446cdb8949ac4cd22c0d7fd634d89f568e2" +dependencies = [ + "log", +] + [[package]] name = "env_logger" version = "0.10.2" @@ -1399,6 +1409,18 @@ dependencies = [ "termcolor", ] +[[package]] +name = "env_logger" +version = "0.11.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c863f0904021b108aa8b2f55046443e6b1ebde8fd4a15c399893aae4fa069f" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "log", +] + [[package]] name = "epaint" version = "0.31.1" @@ -1766,9 +1788,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" dependencies = [ "cfg-if", + "js-sys", "libc", "r-efi", "wasi 0.14.7+wasi-0.2.4", + "wasm-bindgen", ] [[package]] @@ -1787,6 +1811,19 @@ version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" +[[package]] +name = "gio-sys" +version = "0.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "171ed2f6dd927abbe108cfd9eebff2052c335013f5879d55bab0dc1dee19b706" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", + "windows-sys 0.61.0", +] + [[package]] name = "gl_generator" version = "0.14.0" @@ -1798,6 +1835,50 @@ dependencies = [ "xml-rs", ] +[[package]] +name = "glib" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1f2cbc4577536c849335878552f42086bfd25a8dcd6f54a18655cf818b20c8f" +dependencies = [ + "bitflags 2.9.4", + "futures-channel", + "futures-core", + "futures-executor", + "futures-task", + "futures-util", + "gio-sys", + "glib-macros", + "glib-sys", + "gobject-sys", + "libc", + "memchr", + "smallvec", +] + +[[package]] +name = "glib-macros" +version = "0.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55eda916eecdae426d78d274a17b48137acdca6fba89621bd3705f2835bc719f" +dependencies = [ + "heck 0.5.0", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "glib-sys" +version = "0.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d09d3d0fddf7239521674e57b0465dfbd844632fec54f059f7f56112e3f927e1" +dependencies = [ + "libc", + "system-deps", +] + [[package]] name = "glob" version = "0.3.3" @@ -1837,6 +1918,17 @@ dependencies = [ "gl_generator", ] +[[package]] +name = "gobject-sys" +version = "0.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "538e41d8776173ec107e7b0f2aceced60abc368d7e1d81c1f0e2ecd35f59080d" +dependencies = [ + "glib-sys", + "libc", + "system-deps", +] + [[package]] name = "gpu-alloc" version = "0.6.0" @@ -2021,6 +2113,29 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http 1.3.1", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http 1.3.1", + "http-body 1.0.1", + "pin-project-lite", +] + [[package]] name = "httparse" version = "1.10.1" @@ -2051,7 +2166,7 @@ dependencies = [ "futures-util", "h2", "http 0.2.12", - "http-body", + "http-body 0.4.6", "httparse", "httpdate", "itoa", @@ -2063,18 +2178,43 @@ dependencies = [ "want", ] +[[package]] +name = "hyper" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb3aa54a13a0dfe7fbe3a59e0c76093041720fdc77b110cc0fc260fafb4dc51e" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "http 1.3.1", + "http-body 1.0.1", + "httparse", + "itoa", + "pin-project-lite", + "pin-utils", + "smallvec", + "tokio", + "want", +] + [[package]] name = "hyper-rustls" -version = "0.24.2" +version = "0.27.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" +checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" dependencies = [ - "futures-util", - "http 0.2.12", - "hyper", - "rustls", + "http 1.3.1", + "hyper 1.7.0", + "hyper-util", + "rustls 0.23.32", + "rustls-native-certs 0.8.1", + "rustls-pki-types", "tokio", - "tokio-rustls", + "tokio-rustls 0.26.4", + "tower-service", + "webpki-roots 1.0.3", ] [[package]] @@ -2083,7 +2223,7 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" dependencies = [ - "hyper", + "hyper 0.14.32", "pin-project-lite", "tokio", "tokio-io-timeout", @@ -2091,15 +2231,42 @@ dependencies = [ [[package]] name = "hyper-tls" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ "bytes", - "hyper", + "http-body-util", + "hyper 1.7.0", + "hyper-util", "native-tls", "tokio", "tokio-native-tls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c6995591a8f1380fcb4ba966a252a4b29188d51d2b89e3a252f5305be65aea8" +dependencies = [ + "base64 0.22.1", + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "http 1.3.1", + "http-body 1.0.1", + "hyper 1.7.0", + "ipnet", + "libc", + "percent-encoding", + "pin-project-lite", + "socket2 0.6.0", + "tokio", + "tower-service", + "tracing", ] [[package]] @@ -2311,6 +2478,16 @@ version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" +[[package]] +name = "iri-string" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbc5ebe9c3a1a7a5127f920a418f7585e9e758e911d0466ed004f393b0e380b2" +dependencies = [ + "memchr", + "serde", +] + [[package]] name = "is-terminal" version = "0.4.16" @@ -2507,7 +2684,7 @@ dependencies = [ [[package]] name = "libwebrtc" -version = "0.3.14" +version = "0.3.17" dependencies = [ "cxx", "jni", @@ -2562,7 +2739,7 @@ checksum = "f5e54036fe321fd421e10d732f155734c4e4afd610dd556d9a82833ab3ee0bed" [[package]] name = "livekit" -version = "0.7.18" +version = "0.7.21" dependencies = [ "bmrng", "bytes", @@ -2580,18 +2757,19 @@ dependencies = [ "semver", "serde", "serde_json", + "test-log", "thiserror 1.0.69", "tokio", ] [[package]] name = "livekit-api" -version = "0.4.6" +version = "0.4.8" dependencies = [ "async-tungstenite", "base64 0.21.7", "futures-util", - "http 0.2.12", + "http 1.3.1", "jsonwebtoken", "livekit-protocol", "livekit-runtime", @@ -2601,21 +2779,21 @@ dependencies = [ "prost 0.12.6", "rand 0.9.2", "reqwest", - "rustls-native-certs", + "rustls-native-certs 0.6.3", "scopeguard", "serde", "serde_json", "sha2", "thiserror 1.0.69", "tokio", - "tokio-rustls", + "tokio-rustls 0.24.1", "tokio-tungstenite", "url", ] [[package]] name = "livekit-protocol" -version = "0.4.0" +version = "0.5.0" dependencies = [ "futures-util", "livekit-runtime", @@ -2644,7 +2822,7 @@ dependencies = [ "anyhow", "clap", "cpal", - "env_logger", + "env_logger 0.10.2", "futures-util", "libwebrtc", "livekit", @@ -2672,6 +2850,12 @@ dependencies = [ "value-bag", ] +[[package]] +name = "lru-slab" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" + [[package]] name = "mach2" version = "0.4.3" @@ -2856,7 +3040,7 @@ dependencies = [ "openssl-probe", "openssl-sys", "schannel", - "security-framework", + "security-framework 2.11.1", "security-framework-sys", "tempfile", ] @@ -2930,6 +3114,15 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "nu-ansi-term" +version = "0.50.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" +dependencies = [ + "windows-sys 0.61.0", +] + [[package]] name = "num-conv" version = "0.1.0" @@ -3634,7 +3827,7 @@ version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" dependencies = [ - "toml_edit", + "toml_edit 0.23.5", ] [[package]] @@ -3770,6 +3963,61 @@ dependencies = [ "memchr", ] +[[package]] +name = "quinn" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" +dependencies = [ + "bytes", + "cfg_aliases", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash 2.1.1", + "rustls 0.23.32", + "socket2 0.6.0", + "thiserror 2.0.16", + "tokio", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-proto" +version = "0.11.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" +dependencies = [ + "bytes", + "getrandom 0.3.3", + "lru-slab", + "rand 0.9.2", + "ring", + "rustc-hash 2.1.1", + "rustls 0.23.32", + "rustls-pki-types", + "slab", + "thiserror 2.0.16", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2 0.6.0", + "tracing", + "windows-sys 0.60.2", +] + [[package]] name = "quote" version = "1.0.40" @@ -3931,47 +4179,46 @@ checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832" [[package]] name = "reqwest" -version = "0.11.27" +version = "0.12.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" +checksum = "9d0946410b9f7b082a427e4ef5c8ff541a88b357bc6c637c40db3a68ac70a36f" dependencies = [ - "base64 0.21.7", + "base64 0.22.1", "bytes", - "encoding_rs", + "futures-channel", "futures-core", "futures-util", - "h2", - "http 0.2.12", - "http-body", - "hyper", + "http 1.3.1", + "http-body 1.0.1", + "http-body-util", + "hyper 1.7.0", "hyper-rustls", "hyper-tls", - "ipnet", + "hyper-util", "js-sys", "log", - "mime", "native-tls", - "once_cell", "percent-encoding", "pin-project-lite", - "rustls", - "rustls-native-certs", - "rustls-pemfile", + "quinn", + "rustls 0.23.32", + "rustls-native-certs 0.8.1", + "rustls-pki-types", "serde", "serde_json", "serde_urlencoded", - "sync_wrapper", - "system-configuration", + "sync_wrapper 1.0.2", "tokio", "tokio-native-tls", - "tokio-rustls", + "tokio-rustls 0.26.4", + "tower 0.5.2", + "tower-http", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "webpki-roots", - "winreg", + "webpki-roots 1.0.3", ] [[package]] @@ -4004,7 +4251,7 @@ dependencies = [ name = "rpc_example" version = "0.1.0" dependencies = [ - "env_logger", + "env_logger 0.10.2", "livekit", "livekit-api", "log", @@ -4065,10 +4312,24 @@ checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" dependencies = [ "log", "ring", - "rustls-webpki", + "rustls-webpki 0.101.7", "sct", ] +[[package]] +name = "rustls" +version = "0.23.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd3c25631629d034ce7cd9940adc9d45762d46de2b0f57193c4443b92c6d4d40" +dependencies = [ + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki 0.103.7", + "subtle", + "zeroize", +] + [[package]] name = "rustls-native-certs" version = "0.6.3" @@ -4078,7 +4339,19 @@ dependencies = [ "openssl-probe", "rustls-pemfile", "schannel", - "security-framework", + "security-framework 2.11.1", +] + +[[package]] +name = "rustls-native-certs" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcff2dd52b58a8d98a70243663a0d234c4e2b79235637849d15913394a247d3" +dependencies = [ + "openssl-probe", + "rustls-pki-types", + "schannel", + "security-framework 3.5.1", ] [[package]] @@ -4090,6 +4363,16 @@ dependencies = [ "base64 0.21.7", ] +[[package]] +name = "rustls-pki-types" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" +dependencies = [ + "web-time", + "zeroize", +] + [[package]] name = "rustls-webpki" version = "0.101.7" @@ -4100,6 +4383,17 @@ dependencies = [ "untrusted", ] +[[package]] +name = "rustls-webpki" +version = "0.103.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10b3f4191e8a80e6b43eebabfac91e5dcecebb27a71f04e820c47ec41d314bf" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + [[package]] name = "rustversion" version = "1.0.22" @@ -4159,6 +4453,19 @@ version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d68f2ec51b097e4c1a75b681a8bec621909b5e91f15bb7b840c4f2f7b01148b2" +[[package]] +name = "screensharing" +version = "0.1.0" +dependencies = [ + "clap", + "env_logger 0.10.2", + "glib", + "livekit", + "livekit-api", + "log", + "tokio", +] + [[package]] name = "sct" version = "0.7.1" @@ -4195,6 +4502,19 @@ dependencies = [ "security-framework-sys", ] +[[package]] +name = "security-framework" +version = "3.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3297343eaf830f66ede390ea39da1d462b6b0c1b000f420d0a83f898bbbe6ef" +dependencies = [ + "bitflags 2.9.4", + "core-foundation 0.10.1", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + [[package]] name = "security-framework-sys" version = "2.15.0" @@ -4254,6 +4574,15 @@ dependencies = [ "serde_core", ] +[[package]] +name = "serde_spanned" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" +dependencies = [ + "serde", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -4493,6 +4822,15 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + [[package]] name = "synstructure" version = "0.13.2" @@ -4505,25 +4843,23 @@ dependencies = [ ] [[package]] -name = "system-configuration" -version = "0.5.1" +name = "system-deps" +version = "7.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +checksum = "e4be53aa0cba896d2dc615bd42bbc130acdcffa239e0a2d965ea5b3b2a86ffdb" dependencies = [ - "bitflags 1.3.2", - "core-foundation 0.9.4", - "system-configuration-sys", + "cfg-expr", + "heck 0.5.0", + "pkg-config", + "toml", + "version-compare", ] [[package]] -name = "system-configuration-sys" -version = "0.5.0" +name = "target-lexicon" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" -dependencies = [ - "core-foundation-sys", - "libc", -] +checksum = "e502f78cdbb8ba4718f566c418c52bc729126ffd16baee5baa718cf25dd5a69a" [[package]] name = "tempfile" @@ -4547,6 +4883,28 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "test-log" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e33b98a582ea0be1168eba097538ee8dd4bbe0f2b01b22ac92ea30054e5be7b" +dependencies = [ + "env_logger 0.11.8", + "test-log-macros", + "tracing-subscriber", +] + +[[package]] +name = "test-log-macros" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "451b374529930d7601b1eef8d32bc79ae870b6079b069401709c2a8bf9e75f36" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + [[package]] name = "thiserror" version = "1.0.69" @@ -4685,6 +5043,21 @@ dependencies = [ "zerovec", ] +[[package]] +name = "tinyvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + [[package]] name = "tokio" version = "1.47.1" @@ -4743,7 +5116,17 @@ version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" dependencies = [ - "rustls", + "rustls 0.21.12", + "tokio", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" +dependencies = [ + "rustls 0.23.32", "tokio", ] @@ -4767,13 +5150,13 @@ dependencies = [ "futures-util", "log", "native-tls", - "rustls", - "rustls-native-certs", + "rustls 0.21.12", + "rustls-native-certs 0.6.3", "tokio", "tokio-native-tls", - "tokio-rustls", + "tokio-rustls 0.24.1", "tungstenite 0.20.1", - "webpki-roots", + "webpki-roots 0.25.4", ] [[package]] @@ -4789,6 +5172,27 @@ dependencies = [ "tokio", ] +[[package]] +name = "toml" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime 0.6.11", + "toml_edit 0.22.27", +] + +[[package]] +name = "toml_datetime" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" +dependencies = [ + "serde", +] + [[package]] name = "toml_datetime" version = "0.7.1" @@ -4798,6 +5202,19 @@ dependencies = [ "serde_core", ] +[[package]] +name = "toml_edit" +version = "0.22.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" +dependencies = [ + "indexmap 2.11.3", + "serde", + "serde_spanned", + "toml_datetime 0.6.11", + "winnow", +] + [[package]] name = "toml_edit" version = "0.23.5" @@ -4805,7 +5222,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2ad0b7ae9cfeef5605163839cb9221f453399f15cfb5c10be9885fcf56611f9" dependencies = [ "indexmap 2.11.3", - "toml_datetime", + "toml_datetime 0.7.1", "toml_parser", "winnow", ] @@ -4833,15 +5250,15 @@ dependencies = [ "futures-util", "h2", "http 0.2.12", - "http-body", - "hyper", + "http-body 0.4.6", + "hyper 0.14.32", "hyper-timeout", "percent-encoding", "pin-project", "prost 0.11.9", "tokio", "tokio-stream", - "tower", + "tower 0.4.13", "tower-layer", "tower-service", "tracing", @@ -4867,6 +5284,39 @@ dependencies = [ "tracing", ] +[[package]] +name = "tower" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper 1.0.2", + "tokio", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-http" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" +dependencies = [ + "bitflags 2.9.4", + "bytes", + "futures-util", + "http 1.3.1", + "http-body 1.0.1", + "iri-string", + "pin-project-lite", + "tower 0.5.2", + "tower-layer", + "tower-service", +] + [[package]] name = "tower-layer" version = "0.3.3" @@ -4911,6 +5361,17 @@ dependencies = [ "valuable", ] +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + [[package]] name = "tracing-subscriber" version = "0.3.20" @@ -4918,6 +5379,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5" dependencies = [ "matchers", + "nu-ansi-term", "once_cell", "parking_lot", "regex-automata", @@ -4925,6 +5387,7 @@ dependencies = [ "thread_local", "tracing", "tracing-core", + "tracing-log", ] [[package]] @@ -4953,7 +5416,7 @@ dependencies = [ "log", "native-tls", "rand 0.8.5", - "rustls", + "rustls 0.21.12", "sha1", "thiserror 1.0.69", "url", @@ -5079,6 +5542,12 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" +[[package]] +name = "version-compare" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" + [[package]] name = "version_check" version = "0.9.5" @@ -5349,7 +5818,7 @@ dependencies = [ name = "webhooks" version = "0.1.0" dependencies = [ - "hyper", + "hyper 0.14.32", "livekit-api", "tokio", ] @@ -5360,21 +5829,31 @@ version = "0.25.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" +[[package]] +name = "webpki-roots" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32b130c0d2d49f8b6889abc456e795e82525204f27c42cf767cf0d7734e089b8" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "webrtc-sys" -version = "0.3.11" +version = "0.3.14" dependencies = [ "cc", "cxx", "cxx-build", "glob", "log", + "pkg-config", "webrtc-sys-build", ] [[package]] name = "webrtc-sys-build" -version = "0.3.7" +version = "0.3.9" dependencies = [ "anyhow", "fs2", @@ -5651,7 +6130,7 @@ dependencies = [ "eframe", "egui", "egui-wgpu", - "env_logger", + "env_logger 0.10.2", "futures", "image 0.24.9", "livekit", @@ -5863,15 +6342,6 @@ dependencies = [ "windows-targets 0.42.2", ] -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.5", -] - [[package]] name = "windows-sys" version = "0.52.0" @@ -5923,21 +6393,6 @@ dependencies = [ "windows_x86_64_msvc 0.42.2", ] -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", -] - [[package]] name = "windows-targets" version = "0.52.6" @@ -5977,12 +6432,6 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" @@ -6001,12 +6450,6 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - [[package]] name = "windows_aarch64_msvc" version = "0.52.6" @@ -6025,12 +6468,6 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -6061,12 +6498,6 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - [[package]] name = "windows_i686_msvc" version = "0.52.6" @@ -6085,12 +6516,6 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - [[package]] name = "windows_x86_64_gnu" version = "0.52.6" @@ -6109,12 +6534,6 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" @@ -6133,12 +6552,6 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - [[package]] name = "windows_x86_64_msvc" version = "0.52.6" @@ -6212,16 +6625,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "winreg" -version = "0.50.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" -dependencies = [ - "cfg-if", - "windows-sys 0.48.0", -] - [[package]] name = "wit-bindgen" version = "0.46.0" @@ -6362,6 +6765,12 @@ dependencies = [ "synstructure", ] +[[package]] +name = "zeroize" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" + [[package]] name = "zerotrie" version = "0.2.2" diff --git a/examples/Cargo.toml b/examples/Cargo.toml index 200bc0228..f2800e93c 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -8,4 +8,5 @@ members = [ "webhooks", "api", "rpc", + "screensharing", ] diff --git a/examples/screensharing/Cargo.toml b/examples/screensharing/Cargo.toml new file mode 100644 index 000000000..1ae0787a2 --- /dev/null +++ b/examples/screensharing/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "screensharing" +version = "0.1.0" +edition = "2021" + +[dependencies] +tokio = { version = "1", features = ["full"] } +env_logger = "0.10" +livekit = { path = "../../livekit", features = ["native-tls"]} +livekit-api = { path = "../../livekit-api"} +log = "0.4" +clap = { version = "4.0", features = ["derive"] } + +[target.'cfg(target_os = "linux")'.dependencies] +glib = "0.21.1" diff --git a/examples/screensharing/src/main.rs b/examples/screensharing/src/main.rs new file mode 100644 index 000000000..841c25606 --- /dev/null +++ b/examples/screensharing/src/main.rs @@ -0,0 +1,168 @@ +use clap::Parser; +use livekit::options::{TrackPublishOptions, VideoCodec}; +use livekit::prelude::*; +use livekit::track::{LocalTrack, LocalVideoTrack, TrackSource}; +use livekit::webrtc::desktop_capturer::{ + CaptureResult, DesktopCapturer, DesktopCapturerOptions, DesktopFrame, +}; +use livekit::webrtc::native::yuv_helper; +use livekit::webrtc::prelude::{ + I420Buffer, RtcVideoSource, VideoBuffer, VideoFrame, VideoResolution, VideoRotation, +}; +use livekit::webrtc::video_source::native::NativeVideoSource; +use livekit_api::access_token; +use std::env; +use std::sync::Arc; +use std::sync::Mutex; + +#[derive(Parser, Debug)] +#[command(author, version, about, long_about = None)] +struct Args { + /// Capture the mouse cursor + #[arg(long)] + capture_cursor: bool, + + /// Capture a specific window (requires window ID) + #[arg(long)] + capture_window: bool, + + /// Use system screen picker (macOS only) + #[cfg(target_os = "macos")] + #[arg(long)] + use_system_picker: bool, +} + +#[tokio::main] +async fn main() { + env_logger::init(); + let args = Args::parse(); + + #[cfg(target_os = "linux")] + { + /* This is needed for getting the system picker for screen sharing. */ + use glib::MainLoop; + let main_loop = MainLoop::new(None, false); + let _handle = std::thread::spawn(move || { + main_loop.run(); + }); + } + + let url = env::var("LIVEKIT_URL").expect("LIVEKIT_URL is not set"); + let api_key = env::var("LIVEKIT_API_KEY").expect("LIVEKIT_API_KEY is not set"); + let api_secret = env::var("LIVEKIT_API_SECRET").expect("LIVEKIT_API_SECRET is not set"); + + let token = access_token::AccessToken::with_api_key(&api_key, &api_secret) + .with_identity("rust-bot") + .with_name("Rust Bot") + .with_grants(access_token::VideoGrants { + room_join: true, + room: "dev_room".to_string(), + ..Default::default() + }) + .to_jwt() + .unwrap(); + + let (room, _) = Room::connect(&url, &token, RoomOptions::default()).await.unwrap(); + log::info!("Connected to room: {} - {}", room.name(), String::from(room.sid().await)); + + let stream_width = 1920; + let stream_height = 1080; + let buffer_source = + NativeVideoSource::new(VideoResolution { width: stream_width, height: stream_height }); + let track = LocalVideoTrack::create_video_track( + "screen_share", + RtcVideoSource::Native(buffer_source.clone()), + ); + + room.local_participant() + .publish_track( + LocalTrack::Video(track), + TrackPublishOptions { + source: TrackSource::Screenshare, + video_codec: VideoCodec::VP9, + ..Default::default() + }, + ) + .await + .unwrap(); + + let buffer_source_clone = buffer_source.clone(); + let video_frame = Arc::new(Mutex::new(VideoFrame { + rotation: VideoRotation::VideoRotation0, + buffer: I420Buffer::new(stream_width, stream_height), + timestamp_us: 0, + })); + let capture_buffer = Arc::new(Mutex::new(I420Buffer::new(stream_width, stream_height))); + let callback = move |result: CaptureResult, frame: DesktopFrame| { + match result { + CaptureResult::ErrorTemporary => { + log::info!("Error temporary"); + return; + } + CaptureResult::ErrorPermanent => { + log::info!("Error permanent"); + return; + } + _ => {} + } + let video_frame = video_frame.clone(); + let height = frame.height(); + let width = frame.width(); + + { + let mut capture_buffer = capture_buffer.lock().unwrap(); + let capture_buffer_width = capture_buffer.width() as i32; + let capture_buffer_height = capture_buffer.height() as i32; + if height != capture_buffer_height || width != capture_buffer_width { + *capture_buffer = I420Buffer::new(width as u32, height as u32); + } + } + + let stride = frame.stride(); + let data = frame.data(); + + let mut capture_buffer = capture_buffer.lock().unwrap(); + let (s_y, s_u, s_v) = capture_buffer.strides(); + let (y, u, v) = capture_buffer.data_mut(); + yuv_helper::argb_to_i420(data, stride, y, s_y, u, s_u, v, s_v, width, height); + + let scaled_buffer = capture_buffer.scale(stream_width as i32, stream_height as i32); + let (scaled_y, scaled_u, scaled_v) = scaled_buffer.data(); + + let mut framebuffer = video_frame.lock().unwrap(); + let buffer = &mut framebuffer.buffer; + let (y, u, v) = buffer.data_mut(); + y.copy_from_slice(scaled_y); + u.copy_from_slice(scaled_u); + v.copy_from_slice(scaled_v); + + buffer_source_clone.capture_frame(&*framebuffer); + }; + let mut options = DesktopCapturerOptions::new(); + #[cfg(target_os = "macos")] + { + options.set_sck_system_picker(args.use_system_picker); + } + options.set_window_capturer(args.capture_window); + options.set_include_cursor(args.capture_cursor); + #[cfg(target_os = "linux")] + { + if std::env::var("WAYLAND_DISPLAY").is_ok() { + options.set_pipewire_capturer(true); + } + } + + let mut capturer = + DesktopCapturer::new(callback, options).expect("Failed to create desktop capturer"); + let sources = capturer.get_source_list(); + log::info!("Found {} sources", sources.len()); + + let selected_source = sources.first().cloned(); + capturer.start_capture(selected_source); + + let now = tokio::time::Instant::now(); + while now.elapsed() < tokio::time::Duration::from_secs(30) { + capturer.capture_frame(); + tokio::time::sleep(tokio::time::Duration::from_millis(16)).await; + } +} diff --git a/libwebrtc/src/desktop_capturer.rs b/libwebrtc/src/desktop_capturer.rs new file mode 100644 index 000000000..34c0ff874 --- /dev/null +++ b/libwebrtc/src/desktop_capturer.rs @@ -0,0 +1,239 @@ +// Copyright 2025 LiveKit, Inc. +// +// 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. + +use crate::imp::desktop_capturer as imp_dc; + +/// Configuration options for creating a desktop capturer. +/// +/// It contains a subset of libwertc's DesktopCaptureOptions. +/// +/// By default, it captures the entire screen and does not include the cursor. +/// +/// # Example +/// ```no_run +/// use libwebrtc::desktop_capturer::DesktopCapturerOptions; +/// +/// let mut options = DesktopCapturerOptions::new(); +/// options.set_include_cursor(true); +/// ``` +pub struct DesktopCapturerOptions { + pub(crate) sys_handle: imp_dc::DesktopCapturerOptions, +} + +impl DesktopCapturerOptions { + /// Creates a new `DesktopCapturerOptions` with default values. + pub fn new() -> Self { + Self { sys_handle: imp_dc::DesktopCapturerOptions::new() } + } + + /// Sets whether to include the cursor in captured frames. + pub fn set_include_cursor(&mut self, include: bool) { + self.sys_handle = self.sys_handle.with_cursor(include); + } + + /// Sets whether to capture windows instead of the entire screen. + pub fn set_window_capturer(&mut self, window_capturer: bool) { + self.sys_handle = self.sys_handle.with_window_capturer(window_capturer); + } + + /// Sets whether to allow the ScreenCaptureKit (SCK) capturer on macOS. + /// + /// This is enabled by default. + #[cfg(target_os = "macos")] + pub fn set_sck_capturer(&mut self, allow_sck_capturer: bool) { + self.sys_handle = self.sys_handle.with_sck_capturer(allow_sck_capturer); + } + + /// Sets whether to allow the ScreenCaptureKit system picker on macOS. + /// + /// This is enabled by default. + /// + /// When disabled, for capturing displays the client should get the source id + /// via a different way as [`DesktopCapturer::get_source_list`] returns an empty vector. + #[cfg(target_os = "macos")] + pub fn set_sck_system_picker(&mut self, allow_sck_system_picker: bool) { + self.sys_handle = self.sys_handle.with_sck_system_picker(allow_sck_system_picker); + } + + /// Sets whether to allow the Windows Graphics Capture (WGC) capturer on Windows. + /// + /// This is enabled by default. + #[cfg(target_os = "windows")] + pub fn set_wgc_capturer(&mut self, allow_wgc_capturer: bool) { + self.sys_handle = self.sys_handle.with_wgc_capturer(allow_wgc_capturer); + } + + /// Sets whether to allow the DirectX capturer on Windows. + /// + /// This is enabled by default. + #[cfg(target_os = "windows")] + pub fn set_directx_capturer(&mut self, allow_directx_capturer: bool) { + self.sys_handle = self.sys_handle.with_directx_capturer(allow_directx_capturer); + } + + /// Sets whether to allow the PipeWire capturer on Linux. + /// + /// It should be enabled when using Wayland. + #[cfg(target_os = "linux")] + pub fn set_pipewire_capturer(&mut self, allow_pipewire_capturer: bool) { + self.sys_handle = self.sys_handle.with_pipewire_capturer(allow_pipewire_capturer); + } +} + +/// A desktop capturer for capturing screens or windows. +pub struct DesktopCapturer { + handle: imp_dc::DesktopCapturer, +} + +impl DesktopCapturer { + /// Creates a new `DesktopCapturer` with the specified callback and options. + /// + /// # Arguments + /// + /// * `callback` - A function that will be called for each captured frame. The callback + /// receives a [`CaptureResult`] indicating success or error, and a [`DesktopFrame`] + /// containing the captured image data. + /// * `options` - Configuration options for the capturer + /// + /// # Returns + /// + /// Returns `Some(DesktopCapturer)` if the capturer was created successfully, + /// or `None` if creation failed (e.g., due to platform limitations or permissions). + pub fn new(callback: T, options: DesktopCapturerOptions) -> Option + where + T: Fn(CaptureResult, DesktopFrame) + Send + 'static, + { + let inner_callback = move |result: imp_dc::CaptureResult, frame: imp_dc::DesktopFrame| { + callback(capture_result_from_sys(result), DesktopFrame::new(frame)); + }; + let desktop_capturer = imp_dc::DesktopCapturer::new(inner_callback, options.sys_handle); + if desktop_capturer.is_none() { + return None; + } + Some(Self { handle: desktop_capturer.unwrap() }) + } + + /// Starts capturing from the specified source. + /// + /// # Arguments + /// + /// * `source` - The capture source to use. It should be None when the capturer + /// is configured to use the system picker (on platforms that support it). + /// + /// # Note + /// + /// After calling this method, you must call [`capture_frame`](Self::capture_frame) + /// to actually capture frames. This method only initializes the capture session. + pub fn start_capture(&mut self, source: Option) { + if let Some(source) = source { + self.handle.select_source(source.sys_handle.id()); + } + self.handle.start(); + } + + /// Captures a single frame. + /// + /// You must call [`start_capture`](Self::start_capture) before calling this method. + pub fn capture_frame(&mut self) { + self.handle.capture_frame(); + } + + /// Retrieves a list of available capture sources. + /// + /// Returns a list of screens or windows that can be captured, depending + /// on whether the capturer was configured for window or screen capture. + /// + /// # Returns + /// + /// A vector of [`CaptureSource`] objects representing available capture sources. + pub fn get_source_list(&self) -> Vec { + let source_list = self.handle.get_source_list(); + source_list.into_iter().map(|source| CaptureSource { sys_handle: source }).collect() + } +} + +pub struct DesktopFrame { + pub(crate) sys_handle: imp_dc::DesktopFrame, +} + +impl DesktopFrame { + pub fn new(sys_handle: imp_dc::DesktopFrame) -> Self { + Self { sys_handle } + } + + pub fn width(&self) -> i32 { + self.sys_handle.width() as i32 + } + + pub fn height(&self) -> i32 { + self.sys_handle.height() as i32 + } + + pub fn stride(&self) -> u32 { + self.sys_handle.stride() as u32 + } + + pub fn left(&self) -> i32 { + self.sys_handle.left() + } + + pub fn top(&self) -> i32 { + self.sys_handle.top() + } + + pub fn data(&self) -> &[u8] { + &self.sys_handle.data() + } +} + +#[derive(Clone)] +pub struct CaptureSource { + pub(crate) sys_handle: imp_dc::CaptureSource, +} + +impl CaptureSource { + pub fn id(&self) -> u64 { + self.sys_handle.id() + } + pub fn title(&self) -> String { + self.sys_handle.title() + } + pub fn display_id(&self) -> i64 { + self.sys_handle.display_id() + } +} + +impl std::fmt::Display for CaptureSource { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("CaptureSource") + .field("id", &self.id()) + .field("title", &self.title()) + .field("display_id", &self.display_id()) + .finish() + } +} + +pub enum CaptureResult { + Success, + ErrorTemporary, + ErrorPermanent, +} + +fn capture_result_from_sys(result: imp_dc::CaptureResult) -> CaptureResult { + match result { + imp_dc::CaptureResult::Success => CaptureResult::Success, + imp_dc::CaptureResult::ErrorTemporary => CaptureResult::ErrorTemporary, + imp_dc::CaptureResult::ErrorPermanent => CaptureResult::ErrorPermanent, + } +} diff --git a/libwebrtc/src/lib.rs b/libwebrtc/src/lib.rs index f13be654d..f84233173 100644 --- a/libwebrtc/src/lib.rs +++ b/libwebrtc/src/lib.rs @@ -45,6 +45,7 @@ pub mod audio_source; pub mod audio_stream; pub mod audio_track; pub mod data_channel; +pub mod desktop_capturer; pub mod ice_candidate; pub mod media_stream; pub mod media_stream_track; diff --git a/libwebrtc/src/native/desktop_capturer.rs b/libwebrtc/src/native/desktop_capturer.rs new file mode 100644 index 000000000..1c48de710 --- /dev/null +++ b/libwebrtc/src/native/desktop_capturer.rs @@ -0,0 +1,261 @@ +// Copyright 2025 LiveKit, Inc. +// +// 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. + +use cxx::UniquePtr; +use webrtc_sys::desktop_capturer::{self as sys_dc, ffi::new_desktop_capturer}; + +#[derive(Copy, Clone, Debug)] +pub struct DesktopCapturerOptions { + pub window_capturer: bool, + pub include_cursor: bool, + #[cfg(target_os = "macos")] + pub allow_sck_capturer: bool, + #[cfg(target_os = "macos")] + pub allow_sck_system_picker: bool, + #[cfg(target_os = "windows")] + pub allow_wgc_capturer: bool, + #[cfg(target_os = "windows")] + pub allow_directx_capturer: bool, + #[cfg(target_os = "linux")] + pub allow_pipewire_capturer: bool, +} + +impl Default for DesktopCapturerOptions { + fn default() -> Self { + Self { + window_capturer: false, + include_cursor: false, + #[cfg(target_os = "macos")] + allow_sck_capturer: true, + #[cfg(target_os = "macos")] + allow_sck_system_picker: true, + #[cfg(target_os = "windows")] + allow_wgc_capturer: true, + #[cfg(target_os = "windows")] + allow_directx_capturer: true, + #[cfg(target_os = "linux")] + allow_pipewire_capturer: false, + } + } +} + +impl DesktopCapturerOptions { + pub(crate) fn new() -> Self { + Self { window_capturer: false, include_cursor: false, ..Default::default() } + } + + pub(crate) fn with_cursor(mut self, include: bool) -> Self { + self.include_cursor = include; + self + } + + pub(crate) fn with_window_capturer(mut self, window_capturer: bool) -> Self { + self.window_capturer = window_capturer; + self + } + + #[cfg(target_os = "macos")] + pub(crate) fn with_sck_capturer(mut self, allow_sck_capturer: bool) -> Self { + self.allow_sck_capturer = allow_sck_capturer; + self + } + + #[cfg(target_os = "macos")] + pub(crate) fn with_sck_system_picker(mut self, allow_sck_system_picker: bool) -> Self { + self.allow_sck_system_picker = allow_sck_system_picker; + self + } + + #[cfg(target_os = "windows")] + pub(crate) fn with_wgc_capturer(mut self, allow_wgc_capturer: bool) -> Self { + self.allow_wgc_capturer = allow_wgc_capturer; + self + } + + #[cfg(target_os = "windows")] + pub(crate) fn with_directx_capturer(mut self, allow_directx_capturer: bool) -> Self { + self.allow_directx_capturer = allow_directx_capturer; + self + } + + #[cfg(target_os = "linux")] + pub(crate) fn with_pipewire_capturer(mut self, allow_pipewire_capturer: bool) -> Self { + self.allow_pipewire_capturer = allow_pipewire_capturer; + self + } + + pub(crate) fn to_sys_handle(&self) -> sys_dc::ffi::DesktopCapturerOptions { + let mut sys_handle = sys_dc::ffi::DesktopCapturerOptions { + window_capturer: self.window_capturer, + include_cursor: self.include_cursor, + allow_sck_capturer: false, + allow_sck_system_picker: false, + allow_wgc_capturer: false, + allow_directx_capturer: false, + allow_pipewire_capturer: false, + }; + #[cfg(target_os = "macos")] + { + sys_handle.allow_sck_capturer = self.allow_sck_capturer; + sys_handle.allow_sck_system_picker = self.allow_sck_system_picker; + } + #[cfg(target_os = "windows")] + { + sys_handle.allow_wgc_capturer = self.allow_wgc_capturer; + sys_handle.allow_directx_capturer = self.allow_directx_capturer; + } + #[cfg(target_os = "linux")] + { + sys_handle.allow_pipewire_capturer = self.allow_pipewire_capturer; + } + sys_handle + } +} + +pub struct DesktopCapturer { + pub(crate) sys_handle: UniquePtr, +} + +impl DesktopCapturer { + pub fn new(callback: T, options: DesktopCapturerOptions) -> Option + where + T: Fn(CaptureResult, DesktopFrame) + Send + 'static, + { + let callback = DesktopCallback::new(callback); + let callback_wrapper = sys_dc::DesktopCapturerCallbackWrapper::new(Box::new(callback)); + let sys_handle = new_desktop_capturer(Box::new(callback_wrapper), options.to_sys_handle()); + if sys_handle.is_null() { + None + } else { + Some(Self { sys_handle }) + } + } + + pub fn capture_frame(&self) { + self.sys_handle.capture_frame(); + } + + pub fn start(&mut self) { + let pin_handle = self.sys_handle.pin_mut(); + pin_handle.start(); + } + + pub fn select_source(&self, id: u64) -> bool { + self.sys_handle.select_source(id) + } + + pub fn get_source_list(&self) -> Vec { + let mut sources = Vec::new(); + let source_list = self.sys_handle.get_source_list(); + for source in source_list.iter() { + sources.push(CaptureSource { sys_handle: source.clone() }); + } + sources + } +} + +pub struct DesktopFrame { + pub(crate) sys_handle: UniquePtr, +} + +impl DesktopFrame { + pub fn new(sys_handle: UniquePtr) -> Self { + Self { sys_handle } + } + + pub fn width(&self) -> i32 { + self.sys_handle.width() + } + + pub fn height(&self) -> i32 { + self.sys_handle.height() + } + + pub fn stride(&self) -> u32 { + self.sys_handle.stride() as u32 + } + + pub fn left(&self) -> i32 { + self.sys_handle.left() + } + + pub fn top(&self) -> i32 { + self.sys_handle.top() + } + + pub fn data(&self) -> &[u8] { + let data = self.sys_handle.data(); + unsafe { std::slice::from_raw_parts(data, self.stride() as usize * self.height() as usize) } + } +} + +pub struct DesktopCallback { + callback: T, +} + +impl DesktopCallback +where + T: Fn(CaptureResult, DesktopFrame) + Send, +{ + pub fn new(callback: T) -> Self { + Self { callback } + } +} + +impl sys_dc::DesktopCapturerCallback for DesktopCallback +where + T: Fn(CaptureResult, DesktopFrame) + Send, +{ + fn on_capture_result( + &self, + result: sys_dc::ffi::CaptureResult, + frame: UniquePtr, + ) { + (self.callback)(capture_result_from_sys(result), DesktopFrame::new(frame)); + } +} + +#[derive(Clone)] +pub struct CaptureSource { + pub(crate) sys_handle: sys_dc::ffi::Source, +} + +impl CaptureSource { + pub fn id(&self) -> u64 { + self.sys_handle.id + } + + pub fn title(&self) -> String { + self.sys_handle.title.clone() + } + + pub fn display_id(&self) -> i64 { + self.sys_handle.display_id + } +} + +pub(crate) enum CaptureResult { + Success, + ErrorTemporary, + ErrorPermanent, +} + +fn capture_result_from_sys(result: sys_dc::ffi::CaptureResult) -> CaptureResult { + match result { + sys_dc::ffi::CaptureResult::Success => CaptureResult::Success, + sys_dc::ffi::CaptureResult::ErrorTemporary => CaptureResult::ErrorTemporary, + sys_dc::ffi::CaptureResult::ErrorPermanent => CaptureResult::ErrorPermanent, + _ => CaptureResult::ErrorPermanent, + } +} diff --git a/libwebrtc/src/native/mod.rs b/libwebrtc/src/native/mod.rs index 8ad65145b..4497bbe1f 100644 --- a/libwebrtc/src/native/mod.rs +++ b/libwebrtc/src/native/mod.rs @@ -20,6 +20,7 @@ pub mod audio_source; pub mod audio_stream; pub mod audio_track; pub mod data_channel; +pub mod desktop_capturer; pub mod frame_cryptor; pub mod ice_candidate; pub mod media_stream; diff --git a/webrtc-sys/Cargo.toml b/webrtc-sys/Cargo.toml index f34c155ab..2dc85d2c2 100644 --- a/webrtc-sys/Cargo.toml +++ b/webrtc-sys/Cargo.toml @@ -22,5 +22,8 @@ cxx-build = "1.0" glob = "0.3" cc = "1.0" +[target.'cfg(target_os = "linux")'.build-dependencies] +pkg-config = "0.3.22" + [dev-dependencies] env_logger = "0.10" diff --git a/webrtc-sys/build.rs b/webrtc-sys/build.rs index 84fa8588d..f44f41b3f 100644 --- a/webrtc-sys/build.rs +++ b/webrtc-sys/build.rs @@ -49,6 +49,7 @@ fn main() { "src/android.rs", "src/prohibit_libsrtp_initialization.rs", "src/apm.rs", + "src/desktop_capturer.rs", ]); builder.files(&[ @@ -77,6 +78,7 @@ fn main() { "src/global_task_queue.cpp", "src/prohibit_libsrtp_initialization.cpp", "src/apm.cpp", + "src/desktop_capturer.cpp", ]); let webrtc_dir = webrtc_sys_build::webrtc_dir(); @@ -153,6 +155,24 @@ fn main() { println!("cargo:rustc-link-lib=dylib=pthread"); println!("cargo:rustc-link-lib=dylib=m"); + if std::env::var("LK_CUSTOM_WEBRTC").is_ok() { + for lib_name in ["glib-2.0", "gobject-2.0", "gio-2.0"] { + let lib = pkg_config::probe_library(lib_name).unwrap(); + builder.includes(lib.include_paths); + } + } else { + println!("cargo:rustc-link-lib=dylib=glib-2.0"); + println!("cargo:rustc-link-lib=dylib=gobject-2.0"); + println!("cargo:rustc-link-lib=dylib=gio-2.0"); + add_gio_headers(&mut builder); + } + + for lib_name in + ["libdrm", "gbm", "x11", "xfixes", "xdamage", "xrandr", "xcomposite", "xext"] + { + pkg_config::probe_library(lib_name).unwrap(); + } + match target_arch.as_str() { "x86_64" => { #[cfg(feature = "use_vaapi")] @@ -348,3 +368,29 @@ fn configure_android_sysroot(builder: &mut cc::Build) { builder.flag(format!("-isysroot{}", sysroot.display()).as_str()); } + +fn add_gio_headers(builder: &mut cc::Build) { + let webrtc_dir = webrtc_sys_build::webrtc_dir(); + let target_arch = webrtc_sys_build::target_arch(); + let target_arch_sysroot = match target_arch.as_str() { + "arm64" => "arm64", + "x64" => "amd64", + _ => panic!("unsupported arch"), + }; + let sysroot_path = format!("include/build/linux/debian_bullseye_{target_arch_sysroot}-sysroot"); + let sysroot = webrtc_dir.join(sysroot_path); + let glib_path = sysroot.join("usr/include/glib-2.0"); + println!("cargo:info=add_gio_headers {}", glib_path.display()); + + builder.include(&glib_path); + let arch_specific_path = match target_arch.as_str() { + "x64" => "x86_64-linux-gnu", + "arm64" => "aarch64-linux-gnu", + _ => panic!("unsupported target"), + }; + + let glib_path_config = sysroot.join("usr/lib"); + let glib_path_config = glib_path_config.join(arch_specific_path); + let glib_path_config = glib_path_config.join("glib-2.0/include"); + builder.include(&glib_path_config); +} diff --git a/webrtc-sys/build/src/lib.rs b/webrtc-sys/build/src/lib.rs index ef5ed794a..b3ef904ce 100644 --- a/webrtc-sys/build/src/lib.rs +++ b/webrtc-sys/build/src/lib.rs @@ -14,6 +14,7 @@ use std::path::PathBuf; use std::{ + collections::HashSet, env, fs::{self, File}, io::{self, BufRead, Write}, @@ -117,20 +118,28 @@ pub fn webrtc_dir() -> path::PathBuf { pub fn webrtc_defines() -> Vec<(String, Option)> { // read preprocessor definitions from webrtc.ninja let defines_re = Regex::new(r"-D(\w+)(?:=([^\s]+))?").unwrap(); - let webrtc_gni = fs::File::open(webrtc_dir().join("webrtc.ninja")).unwrap(); + let files = [webrtc_dir().join("webrtc.ninja"), webrtc_dir().join("desktop_capture.ninja")]; - let mut defines_line = String::default(); - io::BufReader::new(webrtc_gni).read_line(&mut defines_line).unwrap(); + let mut seen = HashSet::new(); + let mut vec = Vec::new(); - let mut vec = Vec::default(); - for cap in defines_re.captures_iter(&defines_line) { - let define_name = &cap[1]; - let define_value = cap.get(2).map(|m| m.as_str()); - if IGNORE_DEFINES.contains(&define_name) { - continue; - } + for path in files { + let gni = fs::File::open(path).unwrap(); - vec.push((define_name.to_owned(), define_value.map(str::to_string))); + let mut defines_line = String::default(); + io::BufReader::new(gni).read_line(&mut defines_line).unwrap(); + for cap in defines_re.captures_iter(&defines_line) { + let define_name = &cap[1]; + let define_value = cap.get(2).map(|m| m.as_str()); + if IGNORE_DEFINES.contains(&define_name) { + continue; + } + let value = define_value.map(str::to_string); + let name = define_name.to_owned(); + if seen.insert((name.clone(), value.clone())) { + vec.push((name, value)); + } + } } vec diff --git a/webrtc-sys/include/livekit/desktop_capturer.h b/webrtc-sys/include/livekit/desktop_capturer.h new file mode 100644 index 000000000..7c5bcfa2b --- /dev/null +++ b/webrtc-sys/include/livekit/desktop_capturer.h @@ -0,0 +1,76 @@ +/* + * Copyright 2025 LiveKit + * + * 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. + */ + +#pragma once +#include + +#include "modules/desktop_capture/desktop_capture_options.h" +#include "modules/desktop_capture/desktop_capturer.h" +#include "rust/cxx.h" + +namespace livekit { +class DesktopFrame; +class DesktopCapturer; +class DesktopCapturerOptions; +class Source; +} // namespace livekit + +#include "webrtc-sys/src/desktop_capturer.rs.h" + +namespace livekit { + +class DesktopCapturer : public webrtc::DesktopCapturer::Callback { + public: + explicit DesktopCapturer(rust::Box callback, + std::unique_ptr capturer); + + void OnCaptureResult(webrtc::DesktopCapturer::Result result, + std::unique_ptr frame) final; + + rust::Vec get_source_list() const; + bool select_source(uint64_t id) const { return capturer->SelectSource(id); }; + void start() { capturer->Start(this); }; + void capture_frame() const { capturer->CaptureFrame(); }; + + private: + std::unique_ptr capturer; + rust::Box callback; +}; + +class DesktopFrame { + public: + DesktopFrame(std::unique_ptr frame) + : frame(std::move(frame)) {}; + int32_t width() const { return frame->size().width(); } + + int32_t height() const { return frame->size().height(); } + + int32_t left() const { return frame->rect().left(); } + + int32_t top() const { return frame->rect().top(); } + + int32_t stride() const { return frame->stride(); } + + const uint8_t* data() const { return frame->data(); } + + private: + std::unique_ptr frame; +}; + +std::unique_ptr new_desktop_capturer( + rust::Box callback, + DesktopCapturerOptions options); +} // namespace livekit \ No newline at end of file diff --git a/webrtc-sys/libwebrtc/build_linux.sh b/webrtc-sys/libwebrtc/build_linux.sh index a1d84fe1f..99c42a303 100755 --- a/webrtc-sys/libwebrtc/build_linux.sh +++ b/webrtc-sys/libwebrtc/build_linux.sh @@ -115,12 +115,12 @@ args="is_debug=$debug \ ffmpeg_branding=\"Chrome\" \ rtc_use_h264=true \ rtc_use_h265=true \ - rtc_use_pipewire=false \ + rtc_use_pipewire=true \ symbol_level=0 \ enable_iterator_debugging=false \ use_rtti=true \ is_clang=false \ - rtc_use_x11=false" + rtc_use_x11=true" # generate ninja files gn gen "$OUTPUT_DIR" --root="src" --args="${args}" @@ -137,6 +137,7 @@ python3 "./src/tools_webrtc/libs/generate_licenses.py" \ --target :default "$OUTPUT_DIR" "$OUTPUT_DIR" cp "$OUTPUT_DIR/obj/webrtc.ninja" "$ARTIFACTS_DIR" +cp "$OUTPUT_DIR/obj/modules/desktop_capture/desktop_capture.ninja" "$ARTIFACTS_DIR" cp "$OUTPUT_DIR/args.gn" "$ARTIFACTS_DIR" cp "$OUTPUT_DIR/LICENSE.md" "$ARTIFACTS_DIR" diff --git a/webrtc-sys/src/desktop_capturer.cpp b/webrtc-sys/src/desktop_capturer.cpp new file mode 100644 index 000000000..88c94b83c --- /dev/null +++ b/webrtc-sys/src/desktop_capturer.cpp @@ -0,0 +1,99 @@ +/* + * Copyright 2025 LiveKit + * + * 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. + */ + +#include "livekit/desktop_capturer.h" + +using SourceList = webrtc::DesktopCapturer::SourceList; + +namespace livekit { + +std::unique_ptr new_desktop_capturer( + rust::Box callback, + DesktopCapturerOptions options) { + webrtc::DesktopCaptureOptions webrtc_options = + webrtc::DesktopCaptureOptions::CreateDefault(); +#ifdef __APPLE__ + webrtc_options.set_allow_sck_capturer(options.allow_sck_capturer); + webrtc_options.set_allow_sck_system_picker(options.allow_sck_system_picker); +#endif +#ifdef _WIN64 + if (options.window_capturer) { + webrtc_options.set_allow_wgc_screen_capturer(options.allow_wgc_capturer); + } else { + webrtc_options.set_allow_wgc_window_capturer(options.allow_wgc_capturer); + // https://github.com/webrtc-sdk/webrtc/blob/m137_release/modules/desktop_capture/desktop_capture_options.h#L133-L142 + webrtc_options.set_enumerate_current_process_windows(false); + } + webrtc_options.set_allow_directx_capturer(options.allow_directx_capturer); +#endif +#ifdef __linux__ + webrtc_options.set_allow_pipewire(options.allow_pipewire_capturer); +#endif + + webrtc_options.set_prefer_cursor_embedded(options.include_cursor); + + std::unique_ptr capturer = nullptr; + if (options.window_capturer) { + capturer = webrtc::DesktopCapturer::CreateWindowCapturer(webrtc_options); + } else { + capturer = webrtc::DesktopCapturer::CreateScreenCapturer(webrtc_options); + } + if (!capturer) { + return nullptr; + } + return std::make_unique(std::move(callback), + std::move(capturer)); +} + +DesktopCapturer::DesktopCapturer( + rust::Box callback, + std::unique_ptr capturer) + : callback(std::move(callback)), capturer(std::move(capturer)) {} + +void DesktopCapturer::OnCaptureResult( + webrtc::DesktopCapturer::Result result, + std::unique_ptr frame) { + CaptureResult ret_result = CaptureResult::Success; + switch (result) { + case webrtc::DesktopCapturer::Result::SUCCESS: + ret_result = CaptureResult::Success; + break; + case webrtc::DesktopCapturer::Result::ERROR_PERMANENT: + ret_result = CaptureResult::ErrorPermanent; + break; + case webrtc::DesktopCapturer::Result::ERROR_TEMPORARY: + ret_result = CaptureResult::ErrorTemporary; + break; + default: + break; + } + callback->on_capture_result(ret_result, + std::make_unique(std::move(frame))); +} + +rust::Vec DesktopCapturer::get_source_list() const { + SourceList list{}; + bool res = capturer->GetSourceList(&list); + rust::Vec source_list{}; + if (res) { + for (auto& source : list) { + source_list.push_back(Source{static_cast(source.id), + source.title, source.display_id}); + } + } + return source_list; +} +} // namespace livekit \ No newline at end of file diff --git a/webrtc-sys/src/desktop_capturer.rs b/webrtc-sys/src/desktop_capturer.rs new file mode 100644 index 000000000..a3a06960e --- /dev/null +++ b/webrtc-sys/src/desktop_capturer.rs @@ -0,0 +1,98 @@ +// Copyright 2025 LiveKit, Inc. +// +// 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. + +use cxx::UniquePtr; +use ffi::CaptureResult; + +use crate::{desktop_capturer::ffi::DesktopFrame, impl_thread_safety}; + +#[cxx::bridge(namespace = "livekit")] +pub mod ffi { + #[derive(Clone)] + struct Source { + id: u64, + title: String, + display_id: i64, + } + + #[derive(Clone, Debug)] + struct DesktopCapturerOptions { + window_capturer: bool, + include_cursor: bool, + allow_sck_capturer: bool, + allow_sck_system_picker: bool, + allow_wgc_capturer: bool, + allow_directx_capturer: bool, + allow_pipewire_capturer: bool, + } + + enum CaptureResult { + Success, + ErrorTemporary, + ErrorPermanent, + } + + unsafe extern "C++" { + include!("livekit/desktop_capturer.h"); + + type DesktopCapturer; + type DesktopFrame; + + fn new_desktop_capturer( + callback: Box, + options: DesktopCapturerOptions, + ) -> UniquePtr; + fn capture_frame(self: &DesktopCapturer); + fn get_source_list(self: &DesktopCapturer) -> Vec; + fn select_source(self: &DesktopCapturer, id: u64) -> bool; + fn start(self: Pin<&mut DesktopCapturer>); + + fn width(self: &DesktopFrame) -> i32; + fn height(self: &DesktopFrame) -> i32; + fn stride(self: &DesktopFrame) -> i32; + fn left(self: &DesktopFrame) -> i32; + fn top(self: &DesktopFrame) -> i32; + fn data(self: &DesktopFrame) -> *const u8; + } + + extern "Rust" { + type DesktopCapturerCallbackWrapper; + + fn on_capture_result( + self: &DesktopCapturerCallbackWrapper, + result: CaptureResult, + frame: UniquePtr, + ); + } +} + +impl_thread_safety!(ffi::DesktopCapturer, Send + Sync); + +pub trait DesktopCapturerCallback: Send { + fn on_capture_result(&self, result: CaptureResult, frame: UniquePtr); +} + +pub struct DesktopCapturerCallbackWrapper { + callback: Box, +} + +impl DesktopCapturerCallbackWrapper { + pub fn new(callback: Box) -> Self { + Self { callback } + } + + fn on_capture_result(&self, result: CaptureResult, frame: UniquePtr) { + self.callback.on_capture_result(result, frame); + } +} diff --git a/webrtc-sys/src/lib.rs b/webrtc-sys/src/lib.rs index 6baf7e2f6..af1762a93 100644 --- a/webrtc-sys/src/lib.rs +++ b/webrtc-sys/src/lib.rs @@ -19,6 +19,7 @@ pub mod audio_resampler; pub mod audio_track; pub mod candidate; pub mod data_channel; +pub mod desktop_capturer; pub mod frame_cryptor; pub mod helper; pub mod jsep;