From 8a307e7b89f50a22fc497df0ff06736eb704837c Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Fri, 28 Mar 2025 10:58:23 -0700 Subject: [PATCH] Switch fully to Rust Livekit (redux) (#27126) Swift bindings BEGONE Release Notes: - Switched from using the Swift LiveKit bindings, to the Rust bindings, fixing https://github.com/zed-industries/zed/issues/9396, a crash when leaving a collaboration session, and making Zed easier to build. --------- Co-authored-by: Conrad Irwin Co-authored-by: Michael Sloan --- .github/workflows/ci.yml | 1 - Cargo.lock | 282 ++- Cargo.toml | 15 +- change-sophie.wav | Bin 0 -> 570298 bytes crates/assistant_eval/build.rs | 9 - crates/call/Cargo.toml | 12 +- crates/call/src/call.rs | 12 +- .../src/{cross_platform => call_impl}/mod.rs | 8 +- .../src/{macos => call_impl}/participant.rs | 16 +- .../src/{cross_platform => call_impl}/room.rs | 192 +- crates/call/src/cross_platform/participant.rs | 84 - crates/call/src/macos/mod.rs | 521 ----- crates/call/src/macos/room.rs | 1707 ----------------- crates/cli/src/main.rs | 36 +- crates/collab/Cargo.toml | 10 +- crates/collab/src/tests/channel_tests.rs | 10 +- crates/collab/src/tests/following_tests.rs | 6 +- crates/collab/src/tests/integration_tests.rs | 108 +- crates/collab/src/tests/test_server.rs | 5 +- crates/evals/build.rs | 9 - crates/gpui/Cargo.toml | 14 +- crates/gpui/src/elements/surface.rs | 10 +- .../gpui/src/platform/blade/blade_renderer.rs | 12 +- crates/gpui/src/platform/mac.rs | 2 +- .../gpui/src/platform/mac/metal_renderer.rs | 82 +- .../gpui/src/platform/mac/screen_capture.rs | 19 +- crates/gpui/src/scene.rs | 2 +- crates/gpui/src/window.rs | 6 +- crates/gpui_tokio/src/gpui_tokio.rs | 4 +- crates/livekit_client/Cargo.toml | 19 +- crates/livekit_client/examples/test_app.rs | 121 +- crates/livekit_client/src/lib.rs | 165 ++ crates/livekit_client/src/livekit_client.rs | 1124 +++++------ .../src/livekit_client/playback.rs | 763 ++++++++ crates/livekit_client/src/mock_client.rs | 38 + .../src/{test => mock_client}/participant.rs | 88 +- .../src/{test => mock_client}/publication.rs | 48 +- .../livekit_client/src/mock_client/track.rs | 75 + .../src/remote_video_track_view.rs | 53 +- crates/livekit_client/src/test.rs | 248 +-- crates/livekit_client/src/test/track.rs | 201 -- crates/livekit_client/src/test/webrtc.rs | 136 -- .../livekit_client_macos/.cargo/config.toml | 2 - crates/livekit_client_macos/Cargo.toml | 67 - crates/livekit_client_macos/LICENSE-GPL | 1 - .../LiveKitBridge/Package.resolved | 52 - .../LiveKitBridge/Package.swift | 27 - .../LiveKitBridge/README.md | 3 - .../Sources/LiveKitBridge/LiveKitBridge.swift | 383 ---- crates/livekit_client_macos/build.rs | 185 -- .../examples/test_app_macos.rs | 172 -- .../src/livekit_client.rs | 37 - crates/livekit_client_macos/src/prod.rs | 981 ---------- crates/livekit_client_macos/src/test.rs | 882 --------- crates/media/Cargo.toml | 1 + crates/media/src/media.rs | 398 +--- crates/storybook/build.rs | 7 - crates/workspace/src/shared_screen.rs | 138 +- .../src/shared_screen/cross_platform.rs | 121 -- crates/workspace/src/shared_screen/macos.rs | 132 -- crates/workspace/src/workspace.rs | 6 +- crates/zed/Cargo.toml | 2 +- crates/zed/build.rs | 9 - nix/build.nix | 6 - script/bundle-mac | 7 +- script/check-rust-livekit-macos | 19 - .../patches/use-cross-platform-livekit.patch | 59 - typos.toml | 2 - 68 files changed, 2393 insertions(+), 7579 deletions(-) create mode 100644 change-sophie.wav rename crates/call/src/{cross_platform => call_impl}/mod.rs (98%) rename crates/call/src/{macos => call_impl}/participant.rs (83%) rename crates/call/src/{cross_platform => call_impl}/room.rs (90%) delete mode 100644 crates/call/src/cross_platform/participant.rs delete mode 100644 crates/call/src/macos/mod.rs delete mode 100644 crates/call/src/macos/room.rs create mode 100644 crates/livekit_client/src/lib.rs create mode 100644 crates/livekit_client/src/livekit_client/playback.rs create mode 100644 crates/livekit_client/src/mock_client.rs rename crates/livekit_client/src/{test => mock_client}/participant.rs (55%) rename crates/livekit_client/src/{test => mock_client}/publication.rs (66%) create mode 100644 crates/livekit_client/src/mock_client/track.rs delete mode 100644 crates/livekit_client/src/test/track.rs delete mode 100644 crates/livekit_client/src/test/webrtc.rs delete mode 100644 crates/livekit_client_macos/.cargo/config.toml delete mode 100644 crates/livekit_client_macos/Cargo.toml delete mode 120000 crates/livekit_client_macos/LICENSE-GPL delete mode 100644 crates/livekit_client_macos/LiveKitBridge/Package.resolved delete mode 100644 crates/livekit_client_macos/LiveKitBridge/Package.swift delete mode 100644 crates/livekit_client_macos/LiveKitBridge/README.md delete mode 100644 crates/livekit_client_macos/LiveKitBridge/Sources/LiveKitBridge/LiveKitBridge.swift delete mode 100644 crates/livekit_client_macos/build.rs delete mode 100644 crates/livekit_client_macos/examples/test_app_macos.rs delete mode 100644 crates/livekit_client_macos/src/livekit_client.rs delete mode 100644 crates/livekit_client_macos/src/prod.rs delete mode 100644 crates/livekit_client_macos/src/test.rs delete mode 100644 crates/workspace/src/shared_screen/cross_platform.rs delete mode 100644 crates/workspace/src/shared_screen/macos.rs delete mode 100755 script/check-rust-livekit-macos delete mode 100644 script/patches/use-cross-platform-livekit.patch diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 853011d8e2..0814350712 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -209,7 +209,6 @@ jobs: cargo check -p workspace cargo build -p remote_server cargo check -p gpui --examples - script/check-rust-livekit-macos # Since the macOS runners are stateful, so we need to remove the config file to prevent potential bug. - name: Clean CI config file diff --git a/Cargo.lock b/Cargo.lock index f001aaa2d5..71c50e148e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1881,7 +1881,7 @@ dependencies = [ "bitflags 2.9.0", "cexpr", "clang-sys", - "itertools 0.10.5", + "itertools 0.12.1", "lazy_static", "lazycell", "log", @@ -1904,7 +1904,7 @@ dependencies = [ "bitflags 2.9.0", "cexpr", "clang-sys", - "itertools 0.10.5", + "itertools 0.12.1", "log", "prettyplease", "proc-macro2", @@ -1993,7 +1993,7 @@ dependencies = [ "ash-window", "bitflags 2.9.0", "bytemuck", - "codespan-reporting", + "codespan-reporting 0.11.1", "glow", "gpu-alloc", "gpu-alloc-ash", @@ -2279,12 +2279,11 @@ dependencies = [ [[package]] name = "bzip2-sys" -version = "0.1.11+1.0.8" +version = "0.1.13+1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc" +checksum = "225bff33b2141874fe80d71e07d6eec4f85c5c216453dd96388240f96e1acc14" dependencies = [ "cc", - "libc", "pkg-config", ] @@ -2299,10 +2298,10 @@ dependencies = [ "fs", "futures 0.3.31", "gpui", + "gpui_tokio", "http_client", "language", "livekit_client", - "livekit_client_macos", "log", "postage", "project", @@ -2438,8 +2437,7 @@ dependencies = [ [[package]] name = "cargo_metadata" version = "0.19.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd5eb614ed4c27c5d706420e4320fbe3216ab31fa1c33cd8246ac36dae4479ba" +source = "git+https://github.com/zed-industries/cargo_metadata?rev=ce8171bad673923d61a77b6761d0dc4aff63398a#ce8171bad673923d61a77b6761d0dc4aff63398a" dependencies = [ "camino", "cargo-platform", @@ -2565,6 +2563,15 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" +[[package]] +name = "cgl" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ced0551234e87afee12411d535648dd89d2e7f34c78b753395567aff3d447ff" +dependencies = [ + "libc", +] + [[package]] name = "channel" version = "0.1.0" @@ -2721,7 +2728,7 @@ dependencies = [ "anyhow", "clap", "collections", - "core-foundation 0.9.4", + "core-foundation 0.10.0", "core-services", "exec", "fork", @@ -2874,6 +2881,17 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "codespan-reporting" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe6d2e5af09e8c8ad56c969f2157a3d4238cebc7c55f0a517728c38f7b200f81" +dependencies = [ + "serde", + "termcolor", + "unicode-width", +] + [[package]] name = "collab" version = "0.44.0" @@ -2921,6 +2939,7 @@ dependencies = [ "git_ui", "google_ai", "gpui", + "gpui_tokio", "hex", "http_client", "hyper 0.14.32", @@ -2930,7 +2949,6 @@ dependencies = [ "language_model", "livekit_api", "livekit_client", - "livekit_client_macos", "log", "lsp", "menu", @@ -3354,6 +3372,19 @@ dependencies = [ "libc", ] +[[package]] +name = "core-graphics2" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e4583956b9806b69f73fcb23aee05eb3620efc282972f08f6a6db7504f8334d" +dependencies = [ + "bitflags 2.9.0", + "block", + "cfg-if", + "core-foundation 0.10.0", + "libc", +] + [[package]] name = "core-services" version = "0.2.1" @@ -3365,16 +3396,30 @@ dependencies = [ [[package]] name = "core-text" -version = "20.1.0" +version = "21.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9d2790b5c08465d49f8dc05c8bcae9fea467855947db39b0f8145c091aaced5" +checksum = "a593227b66cbd4007b2a050dfdd9e1d1318311409c8d600dc82ba1b15ca9c130" dependencies = [ - "core-foundation 0.9.4", - "core-graphics 0.23.2", + "core-foundation 0.10.0", + "core-graphics 0.24.0", "foreign-types 0.5.0", "libc", ] +[[package]] +name = "core-video" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d45e71d5be22206bed53c3c3cb99315fc4c3d31b8963808c6bc4538168c4f8ef" +dependencies = [ + "block", + "core-foundation 0.10.0", + "core-graphics2", + "io-surface", + "libc", + "metal", +] + [[package]] name = "core_maths" version = "0.1.1" @@ -3793,9 +3838,9 @@ checksum = "96a6ac251f4a2aca6b3f91340350eab87ae57c3f127ffeb585e92bd336717991" [[package]] name = "cxx" -version = "1.0.134" +version = "1.0.151" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5a32d755fe20281b46118ee4b507233311fb7a48a0cfd42f554b93640521a2f" +checksum = "fdb3e596b379180315d2f934231e233a2fc745041f88231807774093d8de45f2" dependencies = [ "cc", "cxxbridge-cmd", @@ -3807,12 +3852,12 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.134" +version = "1.0.151" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11645536ada5d1c8804312cbffc9ab950f2216154de431de930da47ca6955199" +checksum = "3743fae7f47620cd34ec23bab819db9ee52da93166a058f87ab0ad99d777dc9b" dependencies = [ "cc", - "codespan-reporting", + "codespan-reporting 0.12.0", "proc-macro2", "quote", "scratch", @@ -3821,12 +3866,12 @@ dependencies = [ [[package]] name = "cxxbridge-cmd" -version = "1.0.134" +version = "1.0.151" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebcc9c78e3c7289665aab921a2b394eaffe8bdb369aa18d81ffc0f534fd49385" +checksum = "aaea0273c049b126a3918df88a1670c9c0168e0738df9370a988ff69070d4fff" dependencies = [ "clap", - "codespan-reporting", + "codespan-reporting 0.12.0", "proc-macro2", "quote", "syn 2.0.100", @@ -3834,15 +3879,15 @@ dependencies = [ [[package]] name = "cxxbridge-flags" -version = "1.0.134" +version = "1.0.151" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a22a87bd9e78d7204d793261470a4c9d585154fddd251828d8aefbb5f74c3bf" +checksum = "020a9a3d6b792aab7f30f6e323893ad7f45052e572cde5d014c47fe67c89495f" [[package]] name = "cxxbridge-macro" -version = "1.0.134" +version = "1.0.151" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dfdb020ff8787c5daf6e0dca743005cc8782868faeadfbabb8824ede5cb1c72" +checksum = "ee54cd01f94db0328c4c73036d38bd8c3bb88927e953d05ffefe743edbf4eb68" dependencies = [ "proc-macro2", "quote", @@ -5073,12 +5118,12 @@ checksum = "f81ec6369c545a7d40e4589b5597581fa1c441fe1cce96dd1de43159910a36a2" [[package]] name = "font-kit" version = "0.14.1" -source = "git+https://github.com/zed-industries/font-kit?rev=40391b7#40391b7c0041d8a8572af2afa3de32ae088f0120" +source = "git+https://github.com/zed-industries/font-kit?rev=5474cfad4b719a72ec8ed2cb7327b2b01fd10568#5474cfad4b719a72ec8ed2cb7327b2b01fd10568" dependencies = [ "bitflags 2.9.0", "byteorder", - "core-foundation 0.9.4", - "core-graphics 0.23.2", + "core-foundation 0.10.0", + "core-graphics 0.24.0", "core-text", "dirs 5.0.1", "dwrote", @@ -5276,7 +5321,7 @@ name = "fsevent" version = "0.1.0" dependencies = [ "bitflags 2.9.0", - "core-foundation 0.9.4", + "core-foundation 0.10.0", "fsevent-sys 3.1.0", "parking_lot", "tempfile", @@ -5813,10 +5858,11 @@ dependencies = [ "cbindgen 0.28.0", "cocoa 0.26.0", "collections", - "core-foundation 0.9.4", + "core-foundation 0.10.0", "core-foundation-sys", - "core-graphics 0.23.2", + "core-graphics 0.24.0", "core-text", + "core-video", "cosmic-text", "ctor", "derive_more", @@ -6918,6 +6964,19 @@ version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06432fb54d3be7964ecd3649233cddf80db2832f47fec34c01f65b3d9d774983" +[[package]] +name = "io-surface" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8283575d5f0b2e7447ec0840363879d71c0fa325d4c699d5b45208ea4a51f45e" +dependencies = [ + "cgl", + "core-foundation 0.10.0", + "core-foundation-sys", + "leaky-cow", + "libc", +] + [[package]] name = "iovec" version = "0.1.4" @@ -7498,6 +7557,21 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" +[[package]] +name = "leak" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd100e01f1154f2908dfa7d02219aeab25d0b9c7fa955164192e3245255a0c73" + +[[package]] +name = "leaky-cow" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40a8225d44241fd324a8af2806ba635fc7c8a7e9a7de4d5cf3ef54e71f5926fc" +dependencies = [ + "leak", +] + [[package]] name = "leb128" version = "0.2.5" @@ -7598,8 +7672,8 @@ dependencies = [ [[package]] name = "libwebrtc" -version = "0.3.7" -source = "git+https://github.com/zed-industries/livekit-rust-sdks?rev=811ceae29fabee455f110c56cd66b3f49a7e5003#811ceae29fabee455f110c56cd66b3f49a7e5003" +version = "0.3.10" +source = "git+https://github.com/zed-industries/livekit-rust-sdks?rev=102ebbb1ccfbdbcb7332d86dc30b1b1c8c01e4f8#102ebbb1ccfbdbcb7332d86dc30b1b1c8c01e4f8" dependencies = [ "cxx", "jni", @@ -7633,9 +7707,9 @@ dependencies = [ [[package]] name = "link-cplusplus" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d240c6f7e1ba3a28b0249f774e6a9dd0175054b52dfbb61b16eb8505c3785c9" +checksum = "4a6f6da007f968f9def0d65a05b187e2960183de70c160204ecfccf0ee330212" dependencies = [ "cc", ] @@ -7683,12 +7757,13 @@ checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" [[package]] name = "livekit" -version = "0.7.0" -source = "git+https://github.com/zed-industries/livekit-rust-sdks?rev=811ceae29fabee455f110c56cd66b3f49a7e5003#811ceae29fabee455f110c56cd66b3f49a7e5003" +version = "0.7.8" +source = "git+https://github.com/zed-industries/livekit-rust-sdks?rev=102ebbb1ccfbdbcb7332d86dc30b1b1c8c01e4f8#102ebbb1ccfbdbcb7332d86dc30b1b1c8c01e4f8" dependencies = [ "chrono", "futures-util", "lazy_static", + "libloading", "libwebrtc", "livekit-api", "livekit-protocol", @@ -7705,10 +7780,10 @@ dependencies = [ [[package]] name = "livekit-api" -version = "0.4.1" -source = "git+https://github.com/zed-industries/livekit-rust-sdks?rev=811ceae29fabee455f110c56cd66b3f49a7e5003#811ceae29fabee455f110c56cd66b3f49a7e5003" +version = "0.4.2" +source = "git+https://github.com/zed-industries/livekit-rust-sdks?rev=102ebbb1ccfbdbcb7332d86dc30b1b1c8c01e4f8#102ebbb1ccfbdbcb7332d86dc30b1b1c8c01e4f8" dependencies = [ - "async-tungstenite", + "base64 0.21.7", "futures-util", "http 0.2.12", "jsonwebtoken", @@ -7716,7 +7791,9 @@ dependencies = [ "livekit-runtime", "log", "parking_lot", + "pbjson-types", "prost 0.12.6", + "rand 0.9.0", "reqwest 0.11.27", "scopeguard", "serde", @@ -7724,14 +7801,14 @@ dependencies = [ "sha2", "thiserror 1.0.69", "tokio", - "tokio-tungstenite 0.20.1", + "tokio-tungstenite 0.26.2", "url", ] [[package]] name = "livekit-protocol" -version = "0.3.6" -source = "git+https://github.com/zed-industries/livekit-rust-sdks?rev=811ceae29fabee455f110c56cd66b3f49a7e5003#811ceae29fabee455f110c56cd66b3f49a7e5003" +version = "0.3.9" +source = "git+https://github.com/zed-industries/livekit-rust-sdks?rev=102ebbb1ccfbdbcb7332d86dc30b1b1c8c01e4f8#102ebbb1ccfbdbcb7332d86dc30b1b1c8c01e4f8" dependencies = [ "futures-util", "livekit-runtime", @@ -7747,13 +7824,11 @@ dependencies = [ [[package]] name = "livekit-runtime" -version = "0.3.1" -source = "git+https://github.com/zed-industries/livekit-rust-sdks?rev=811ceae29fabee455f110c56cd66b3f49a7e5003#811ceae29fabee455f110c56cd66b3f49a7e5003" +version = "0.4.0" +source = "git+https://github.com/zed-industries/livekit-rust-sdks?rev=102ebbb1ccfbdbcb7332d86dc30b1b1c8c01e4f8#102ebbb1ccfbdbcb7332d86dc30b1b1c8c01e4f8" dependencies = [ - "async-io", - "async-std", - "async-task", - "futures 0.3.31", + "tokio", + "tokio-stream", ] [[package]] @@ -7778,19 +7853,21 @@ dependencies = [ "anyhow", "async-trait", "collections", - "core-foundation 0.9.4", + "core-foundation 0.10.0", + "core-video", "coreaudio-rs 0.12.1", "cpal", "futures 0.3.31", "gpui", - "http 0.2.12", - "http_client", + "gpui_tokio", + "http_client_tls", "image", + "libwebrtc", "livekit", "livekit_api", "log", - "media", "nanoid", + "objc", "parking_lot", "postage", "serde", @@ -7798,32 +7875,10 @@ dependencies = [ "sha2", "simplelog", "smallvec", + "tokio-tungstenite 0.26.2", "util", ] -[[package]] -name = "livekit_client_macos" -version = "0.1.0" -dependencies = [ - "anyhow", - "async-broadcast", - "async-trait", - "collections", - "core-foundation 0.9.4", - "futures 0.3.31", - "gpui", - "livekit_api", - "log", - "media", - "nanoid", - "parking_lot", - "postage", - "serde", - "serde_json", - "sha2", - "simplelog", -] - [[package]] name = "lmdb-master-sys" version = "0.2.4" @@ -8165,7 +8220,8 @@ version = "0.1.0" dependencies = [ "anyhow", "bindgen 0.70.1", - "core-foundation 0.9.4", + "core-foundation 0.10.0", + "core-video", "ctor", "foreign-types 0.5.0", "metal", @@ -8215,9 +8271,9 @@ dependencies = [ [[package]] name = "metal" -version = "0.31.0" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f569fb946490b5743ad69813cb19629130ce9374034abe31614a36402d18f99e" +checksum = "7ecfd3296f8c56b7c1f6fbac3c71cefa9d78ce009850c45000015f206dc7fa21" dependencies = [ "bitflags 2.9.0", "block", @@ -8370,12 +8426,6 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" -[[package]] -name = "multimap" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "defc4c55412d89136f966bbb339008b474350e5e6e78d2714439c386b3137a03" - [[package]] name = "naga" version = "23.1.0" @@ -8386,7 +8436,7 @@ dependencies = [ "bit-set 0.8.0", "bitflags 2.9.0", "cfg_aliases 0.1.1", - "codespan-reporting", + "codespan-reporting 0.11.1", "hexf-parse", "indexmap", "log", @@ -10694,7 +10744,7 @@ dependencies = [ "itertools 0.10.5", "lazy_static", "log", - "multimap 0.8.3", + "multimap", "petgraph", "prost 0.9.0", "prost-types 0.9.0", @@ -10713,7 +10763,7 @@ dependencies = [ "heck 0.4.1", "itertools 0.10.5", "log", - "multimap 0.10.0", + "multimap", "once_cell", "petgraph", "prettyplease", @@ -12132,9 +12182,9 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "scratch" -version = "1.0.7" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3cf7c11c38cb994f3d40e8a8cde3bbd1f72a435e4c49e85d6553d8312306152" +checksum = "9f6280af86e5f559536da57a45ebc84948833b3bee313a7dd25232e09c878a52" [[package]] name = "scrypt" @@ -14093,7 +14143,7 @@ dependencies = [ name = "time_format" version = "0.1.0" dependencies = [ - "core-foundation 0.9.4", + "core-foundation 0.10.0", "core-foundation-sys", "sys-locale", "time", @@ -14317,10 +14367,7 @@ checksum = "212d5dcb2a1ce06d81107c3d0ffa3121fe974b73f068c8282cb1c32328113b6c" dependencies = [ "futures-util", "log", - "rustls 0.21.12", - "rustls-native-certs 0.6.3", "tokio", - "tokio-rustls 0.24.1", "tungstenite 0.20.1", ] @@ -14336,6 +14383,21 @@ dependencies = [ "tungstenite 0.21.0", ] +[[package]] +name = "tokio-tungstenite" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a9daff607c6d2bf6c16fd681ccb7eecc83e4e2cdc1ca067ffaadfca5de7f084" +dependencies = [ + "futures-util", + "log", + "rustls 0.23.25", + "rustls-pki-types", + "tokio", + "tokio-rustls 0.26.1", + "tungstenite 0.26.2", +] + [[package]] name = "tokio-util" version = "0.7.13" @@ -14840,7 +14902,6 @@ dependencies = [ "httparse", "log", "rand 0.8.5", - "rustls 0.21.12", "sha1", "thiserror 1.0.69", "url", @@ -14884,6 +14945,25 @@ dependencies = [ "utf-8", ] +[[package]] +name = "tungstenite" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4793cb5e56680ecbb1d843515b23b6de9a75eb04b66643e256a396d43be33c13" +dependencies = [ + "bytes 1.10.1", + "data-encoding", + "http 1.2.0", + "httparse", + "log", + "rand 0.9.0", + "rustls 0.23.25", + "rustls-pki-types", + "sha1", + "thiserror 2.0.12", + "utf-8", +] + [[package]] name = "typeid" version = "1.0.2" @@ -16020,8 +16100,8 @@ dependencies = [ [[package]] name = "webrtc-sys" -version = "0.3.5" -source = "git+https://github.com/zed-industries/livekit-rust-sdks?rev=811ceae29fabee455f110c56cd66b3f49a7e5003#811ceae29fabee455f110c56cd66b3f49a7e5003" +version = "0.3.7" +source = "git+https://github.com/zed-industries/livekit-rust-sdks?rev=102ebbb1ccfbdbcb7332d86dc30b1b1c8c01e4f8#102ebbb1ccfbdbcb7332d86dc30b1b1c8c01e4f8" dependencies = [ "cc", "cxx", @@ -16033,8 +16113,8 @@ dependencies = [ [[package]] name = "webrtc-sys-build" -version = "0.3.5" -source = "git+https://github.com/zed-industries/livekit-rust-sdks?rev=811ceae29fabee455f110c56cd66b3f49a7e5003#811ceae29fabee455f110c56cd66b3f49a7e5003" +version = "0.3.6" +source = "git+https://github.com/zed-industries/livekit-rust-sdks?rev=102ebbb1ccfbdbcb7332d86dc30b1b1c8c01e4f8#102ebbb1ccfbdbcb7332d86dc30b1b1c8c01e4f8" dependencies = [ "fs2", "regex", diff --git a/Cargo.toml b/Cargo.toml index 6b01ae7988..12a03a37fb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -87,7 +87,6 @@ members = [ "crates/languages", "crates/livekit_api", "crates/livekit_client", - "crates/livekit_client_macos", "crates/lmstudio", "crates/lsp", "crates/markdown", @@ -292,7 +291,6 @@ language_tools = { path = "crates/language_tools" } languages = { path = "crates/languages" } livekit_api = { path = "crates/livekit_api" } livekit_client = { path = "crates/livekit_client" } -livekit_client_macos = { path = "crates/livekit_client_macos" } lmstudio = { path = "crates/lmstudio" } lsp = { path = "crates/lsp" } markdown = { path = "crates/markdown" } @@ -413,15 +411,16 @@ blade-util = { git = "https://github.com/kvark/blade", rev = "b16f5c7bd873c7126f naga = { version = "23.1.0", features = ["wgsl-in"] } blake3 = "1.5.3" bytes = "1.0" -cargo_metadata = "0.19" +cargo_metadata = { git = "https://github.com/zed-industries/cargo_metadata", rev = "ce8171bad673923d61a77b6761d0dc4aff63398a"} cargo_toml = "0.21" chrono = { version = "0.4", features = ["serde"] } circular-buffer = "1.0" clap = { version = "4.4", features = ["derive"] } cocoa = "0.26" cocoa-foundation = "0.2.0" +core-video = { version = "0.4.3", features = ["metal"] } convert_case = "0.8.0" -core-foundation = "0.9.3" +core-foundation = "0.10.0" core-foundation-sys = "0.8.6" ctor = "0.4.0" dashmap = "6.0" @@ -459,11 +458,6 @@ libc = "0.2" libsqlite3-sys = { version = "0.30.1", features = ["bundled"] } linkify = "0.10.0" linkme = "0.3.31" -livekit = { git = "https://github.com/zed-industries/livekit-rust-sdks", rev = "811ceae29fabee455f110c56cd66b3f49a7e5003", features = [ - "dispatcher", - "services-dispatcher", - "rustls-tls-native-roots", -], default-features = false } log = { version = "0.4.16", features = ["kv_unstable_serde", "serde"] } markup5ever_rcdom = "0.3.0" mlua = { version = "0.10", features = ["lua54", "vendored", "async", "send"] } @@ -552,6 +546,7 @@ time = { version = "0.3", features = [ tiny_http = "0.8" toml = "0.8" tokio = { version = "1" } +tokio-tungstenite = { version = "0.26", features = ["__rustls-tls"]} tower-http = "0.4.4" tree-sitter = { version = "0.25.3", features = ["wasm"] } tree-sitter-bash = "0.23" @@ -597,7 +592,7 @@ which = "6.0.0" wit-component = "0.221" zed_llm_client = "0.4" zstd = "0.11" -metal = "0.31" +metal = "0.29" [workspace.dependencies.async-stripe] git = "https://github.com/zed-industries/async-stripe" diff --git a/change-sophie.wav b/change-sophie.wav new file mode 100644 index 0000000000000000000000000000000000000000..1f0a0bebc82dc59f845ce9f24287194bb3f7f534 GIT binary patch literal 570298 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�d~KYM=I{_y+}`{Unt+3yzL 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

s!IgsB#t^k<^QUlThG7p47wu8zL(0Car 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 impl Global for GlobalActiveCall {} pub fn init(client: Arc, user_store: Entity, cx: &mut App) { - livekit_client::init( - cx.background_executor().dispatcher.clone(), - cx.http_client(), - ); CallSettings::register(cx); let active_call = cx.new(|cx| ActiveCall::new(client, user_store, cx)); diff --git a/crates/call/src/macos/participant.rs b/crates/call/src/call_impl/participant.rs similarity index 83% rename from crates/call/src/macos/participant.rs rename to crates/call/src/call_impl/participant.rs index 1f1a63a10e..e887aeb329 100644 --- a/crates/call/src/macos/participant.rs +++ b/crates/call/src/call_impl/participant.rs @@ -1,13 +1,14 @@ use anyhow::{anyhow, Result}; -use client::ParticipantIndex; -use client::{proto, User}; +use client::{proto, ParticipantIndex, User}; use collections::HashMap; use gpui::WeakEntity; -pub use livekit_client_macos::Frame; -pub use livekit_client_macos::{RemoteAudioTrack, RemoteVideoTrack}; +use livekit_client::AudioStream; use project::Project; use std::sync::Arc; +pub use livekit_client::TrackSid; +pub use livekit_client::{RemoteAudioTrack, RemoteVideoTrack}; + #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum ParticipantLocation { SharedProject { project_id: u64 }, @@ -48,7 +49,6 @@ impl LocalParticipant { } } -#[derive(Clone, Debug)] pub struct RemoteParticipant { pub user: Arc, pub peer_id: proto::PeerId, @@ -58,13 +58,13 @@ pub struct RemoteParticipant { pub participant_index: ParticipantIndex, pub muted: bool, pub speaking: bool, - pub video_tracks: HashMap>, - pub audio_tracks: HashMap>, + pub video_tracks: HashMap, + pub audio_tracks: HashMap, } impl RemoteParticipant { pub fn has_video_tracks(&self) -> bool { - !self.video_tracks.is_empty() + return !self.video_tracks.is_empty(); } pub fn can_write(&self) -> bool { diff --git a/crates/call/src/cross_platform/room.rs b/crates/call/src/call_impl/room.rs similarity index 90% rename from crates/call/src/cross_platform/room.rs rename to crates/call/src/call_impl/room.rs index ebed7439cc..44294f8360 100644 --- a/crates/call/src/cross_platform/room.rs +++ b/crates/call/src/call_impl/room.rs @@ -1,5 +1,3 @@ -#![cfg_attr(all(target_os = "windows", target_env = "gnu"), allow(unused))] - use crate::{ call_settings::CallSettings, participant::{LocalParticipant, ParticipantLocation, RemoteParticipant}, @@ -14,20 +12,10 @@ use collections::{BTreeMap, HashMap, HashSet}; use fs::Fs; use futures::{FutureExt, StreamExt}; use gpui::{App, AppContext as _, AsyncApp, Context, Entity, EventEmitter, Task, WeakEntity}; +use gpui_tokio::Tokio; use language::LanguageRegistry; -#[cfg(not(all(target_os = "windows", target_env = "gnu")))] -use livekit::{ - capture_local_audio_track, capture_local_video_track, - id::ParticipantIdentity, - options::{TrackPublishOptions, VideoCodec}, - play_remote_audio_track, - publication::LocalTrackPublication, - track::{TrackKind, TrackSource}, - RoomEvent, RoomOptions, -}; -#[cfg(all(target_os = "windows", target_env = "gnu"))] -use livekit::{publication::LocalTrackPublication, RoomEvent}; -use livekit_client as livekit; +use livekit::{LocalTrackPublication, ParticipantIdentity, RoomEvent}; +use livekit_client::{self as livekit, TrackSid}; use postage::{sink::Sink, stream::Stream, watch}; use project::Project; use settings::Settings as _; @@ -47,6 +35,9 @@ pub enum Event { RemoteVideoTracksChanged { participant_id: proto::PeerId, }, + RemoteVideoTrackUnsubscribed { + sid: TrackSid, + }, RemoteAudioTracksChanged { participant_id: proto::PeerId, }, @@ -104,11 +95,7 @@ impl Room { !self.shared_projects.is_empty() } - #[cfg(all( - any(test, feature = "test-support"), - not(all(target_os = "windows", target_env = "gnu")) - ))] - pub fn is_connected(&self) -> bool { + pub fn is_connected(&self, _: &App) -> bool { if let Some(live_kit) = self.live_kit.as_ref() { live_kit.room.connection_state() == livekit::ConnectionState::Connected } else { @@ -477,13 +464,15 @@ impl Room { id: worktree.id().to_proto(), scan_id: worktree.completed_scan_id() as u64, }); - for repository in worktree.repositories().iter() { - repositories.push(proto::RejoinRepository { - id: repository.work_directory_id().to_proto(), - scan_id: worktree.completed_scan_id() as u64, - }); - } } + for (entry_id, repository) in project.repositories(cx) { + let repository = repository.read(cx); + repositories.push(proto::RejoinRepository { + id: entry_id.to_proto(), + scan_id: repository.completed_scan_id as u64, + }); + } + rejoined_projects.push(proto::RejoinProject { id: project_id, worktrees, @@ -687,12 +676,6 @@ impl Room { } } - #[cfg(all(target_os = "windows", target_env = "gnu"))] - fn start_room_connection(&self, mut room: proto::Room, cx: &mut Context) -> Task<()> { - Task::ready(()) - } - - #[cfg(not(all(target_os = "windows", target_env = "gnu")))] fn start_room_connection(&self, mut room: proto::Room, cx: &mut Context) -> Task<()> { // Filter ourselves out from the room's participants. let local_participant_ix = room @@ -845,7 +828,6 @@ impl Room { muted: true, speaking: false, video_tracks: Default::default(), - #[cfg(not(all(target_os = "windows", target_env = "gnu")))] audio_tracks: Default::default(), }, ); @@ -948,7 +930,6 @@ impl Room { ); match event { - #[cfg(not(all(target_os = "windows", target_env = "gnu")))] RoomEvent::TrackSubscribed { track, participant, @@ -963,18 +944,22 @@ impl Room { ) })?; if self.live_kit.as_ref().map_or(true, |kit| kit.deafened) { - track.rtc_track().set_enabled(false); + if matches!(track, livekit_client::RemoteTrack::Audio(_)) { + track.set_enabled(false, cx); + } } match track { - livekit::track::RemoteTrack::Audio(track) => { + livekit_client::RemoteTrack::Audio(track) => { cx.emit(Event::RemoteAudioTracksChanged { participant_id: participant.peer_id, }); - let stream = play_remote_audio_track(&track, cx.background_executor())?; - participant.audio_tracks.insert(track_id, (track, stream)); - participant.muted = publication.is_muted(); + if let Some(live_kit) = self.live_kit.as_ref() { + let stream = live_kit.room.play_remote_audio_track(&track, cx)?; + participant.audio_tracks.insert(track_id, (track, stream)); + participant.muted = publication.is_muted(); + } } - livekit::track::RemoteTrack::Video(track) => { + livekit_client::RemoteTrack::Video(track) => { cx.emit(Event::RemoteVideoTracksChanged { participant_id: participant.peer_id, }); @@ -983,7 +968,6 @@ impl Room { } } - #[cfg(not(all(target_os = "windows", target_env = "gnu")))] RoomEvent::TrackUnsubscribed { track, participant, .. } => { @@ -995,23 +979,23 @@ impl Room { ) })?; match track { - livekit::track::RemoteTrack::Audio(track) => { + livekit_client::RemoteTrack::Audio(track) => { participant.audio_tracks.remove(&track.sid()); participant.muted = true; cx.emit(Event::RemoteAudioTracksChanged { participant_id: participant.peer_id, }); } - livekit::track::RemoteTrack::Video(track) => { + livekit_client::RemoteTrack::Video(track) => { participant.video_tracks.remove(&track.sid()); cx.emit(Event::RemoteVideoTracksChanged { participant_id: participant.peer_id, }); + cx.emit(Event::RemoteVideoTrackUnsubscribed { sid: track.sid() }); } } } - #[cfg(not(all(target_os = "windows", target_env = "gnu")))] RoomEvent::ActiveSpeakersChanged { speakers } => { let mut speaker_ids = speakers .into_iter() @@ -1028,7 +1012,6 @@ impl Room { } } - #[cfg(not(all(target_os = "windows", target_env = "gnu")))] RoomEvent::TrackMuted { participant, publication, @@ -1053,7 +1036,6 @@ impl Room { } } - #[cfg(not(all(target_os = "windows", target_env = "gnu")))] RoomEvent::LocalTrackUnpublished { publication, .. } => { log::info!("unpublished track {}", publication.sid()); if let Some(room) = &mut self.live_kit { @@ -1076,12 +1058,10 @@ impl Room { } } - #[cfg(not(all(target_os = "windows", target_env = "gnu")))] RoomEvent::LocalTrackPublished { publication, .. } => { log::info!("published track {:?}", publication.sid()); } - #[cfg(not(all(target_os = "windows", target_env = "gnu")))] RoomEvent::Disconnected { reason } => { log::info!("disconnected from room: {reason:?}"); self.leave(cx).detach_and_log_err(cx); @@ -1309,13 +1289,6 @@ impl Room { pub fn can_use_microphone(&self) -> bool { use proto::ChannelRole::*; - #[cfg(not(any(test, feature = "test-support")))] - { - if cfg!(all(target_os = "windows", target_env = "gnu")) { - return false; - } - } - match self.local_participant.role { Admin | Member | Talker => true, Guest | Banned => false, @@ -1330,40 +1303,23 @@ impl Room { } } - #[cfg(all(target_os = "windows", target_env = "gnu"))] - pub fn share_microphone(&mut self, cx: &mut Context) -> Task> { - Task::ready(Err(anyhow!("MinGW is not supported yet"))) - } - - #[cfg(not(all(target_os = "windows", target_env = "gnu")))] #[track_caller] pub fn share_microphone(&mut self, cx: &mut Context) -> Task> { if self.status.is_offline() { return Task::ready(Err(anyhow!("room is offline"))); } - let (participant, publish_id) = if let Some(live_kit) = self.live_kit.as_mut() { + let (room, publish_id) = if let Some(live_kit) = self.live_kit.as_mut() { let publish_id = post_inc(&mut live_kit.next_publish_id); live_kit.microphone_track = LocalTrack::Pending { publish_id }; cx.notify(); - (live_kit.room.local_participant(), publish_id) + (live_kit.room.clone(), publish_id) } else { return Task::ready(Err(anyhow!("live-kit was not initialized"))); }; cx.spawn(async move |this, cx| { - let (track, stream) = capture_local_audio_track(cx.background_executor())?.await; - - let publication = participant - .publish_track( - livekit::track::LocalTrack::Audio(track), - TrackPublishOptions { - source: TrackSource::Microphone, - ..Default::default() - }, - ) - .await - .map_err(|error| anyhow!("failed to publish track: {error}")); + let publication = room.publish_local_microphone_track(cx).await; this.update(cx, |this, cx| { let live_kit = this .live_kit @@ -1380,15 +1336,15 @@ impl Room { }; match publication { - Ok(publication) => { + Ok((publication, stream)) => { if canceled { - cx.background_spawn(async move { - participant.unpublish_track(&publication.sid()).await + cx.spawn(async move |_, cx| { + room.unpublish_local_track(publication.sid(), cx).await }) .detach_and_log_err(cx) } else { if live_kit.muted_by_user || live_kit.deafened { - publication.mute(); + publication.mute(cx); } live_kit.microphone_track = LocalTrack::Published { track_publication: publication, @@ -1412,12 +1368,6 @@ impl Room { }) } - #[cfg(all(target_os = "windows", target_env = "gnu"))] - pub fn share_screen(&mut self, cx: &mut Context) -> Task> { - Task::ready(Err(anyhow!("MinGW is not supported yet"))) - } - - #[cfg(not(all(target_os = "windows", target_env = "gnu")))] pub fn share_screen(&mut self, cx: &mut Context) -> Task> { if self.status.is_offline() { return Task::ready(Err(anyhow!("room is offline"))); @@ -1441,19 +1391,7 @@ impl Room { let sources = sources.await??; let source = sources.first().ok_or_else(|| anyhow!("no display found"))?; - let (track, stream) = capture_local_video_track(&**source).await?; - - let publication = participant - .publish_track( - livekit::track::LocalTrack::Video(track), - TrackPublishOptions { - source: TrackSource::Screenshare, - video_codec: VideoCodec::H264, - ..Default::default() - }, - ) - .await - .map_err(|error| anyhow!("error publishing screen track {error:?}")); + let publication = participant.publish_screenshare_track(&**source, cx).await; this.update(cx, |this, cx| { let live_kit = this @@ -1471,10 +1409,10 @@ impl Room { }; match publication { - Ok(publication) => { + Ok((publication, stream)) => { if canceled { - cx.background_spawn(async move { - participant.unpublish_track(&publication.sid()).await + cx.spawn(async move |_, cx| { + participant.unpublish_track(publication.sid(), cx).await }) .detach() } else { @@ -1564,14 +1502,11 @@ impl Room { LocalTrack::Published { track_publication, .. } => { - #[cfg(not(all(target_os = "windows", target_env = "gnu")))] { let local_participant = live_kit.room.local_participant(); let sid = track_publication.sid(); - cx.background_spawn( - async move { local_participant.unpublish_track(&sid).await }, - ) - .detach_and_log_err(cx); + cx.spawn(async move |_, cx| local_participant.unpublish_track(sid, cx).await) + .detach_and_log_err(cx); cx.notify(); } @@ -1582,14 +1517,13 @@ impl Room { } fn set_deafened(&mut self, deafened: bool, cx: &mut Context) -> Option<()> { - #[cfg(not(all(target_os = "windows", target_env = "gnu")))] { let live_kit = self.live_kit.as_mut()?; cx.notify(); for (_, participant) in live_kit.room.remote_participants() { for (_, publication) in participant.track_publications() { - if publication.kind() == TrackKind::Audio { - publication.set_enabled(!deafened); + if publication.is_audio() { + publication.set_enabled(!deafened, cx); } } } @@ -1620,14 +1554,13 @@ impl Room { LocalTrack::Published { track_publication, .. } => { - #[cfg(not(all(target_os = "windows", target_env = "gnu")))] - { - if should_mute { - track_publication.mute() - } else { - track_publication.unmute() - } + let guard = Tokio::handle(cx); + if should_mute { + track_publication.mute(cx) + } else { + track_publication.unmute(cx) } + drop(guard); None } @@ -1635,30 +1568,19 @@ impl Room { } } -#[cfg(all(target_os = "windows", target_env = "gnu"))] -fn spawn_room_connection( - livekit_connection_info: Option, - cx: &mut Context<'_, Room>, -) { -} - -#[cfg(not(all(target_os = "windows", target_env = "gnu")))] fn spawn_room_connection( livekit_connection_info: Option, cx: &mut Context<'_, Room>, ) { if let Some(connection_info) = livekit_connection_info { cx.spawn(async move |this, cx| { - let (room, mut events) = livekit::Room::connect( - &connection_info.server_url, - &connection_info.token, - RoomOptions::default(), - ) - .await?; + let (room, mut events) = + livekit::Room::connect(connection_info.server_url, connection_info.token, cx) + .await?; this.update(cx, |this, cx| { let _handle_updates = cx.spawn(async move |this, cx| { - while let Some(event) = events.recv().await { + while let Some(event) = events.next().await { if this .update(cx, |this, cx| { this.livekit_room_updated(event, cx).warn_on_err(); @@ -1707,10 +1629,6 @@ struct LiveKitRoom { } impl LiveKitRoom { - #[cfg(all(target_os = "windows", target_env = "gnu"))] - fn stop_publishing(&mut self, _cx: &mut Context) {} - - #[cfg(not(all(target_os = "windows", target_env = "gnu")))] fn stop_publishing(&mut self, cx: &mut Context) { let mut tracks_to_unpublish = Vec::new(); if let LocalTrack::Published { @@ -1730,9 +1648,9 @@ impl LiveKitRoom { } let participant = self.room.local_participant(); - cx.background_spawn(async move { + cx.spawn(async move |_, cx| { for sid in tracks_to_unpublish { - participant.unpublish_track(&sid).await.log_err(); + participant.unpublish_track(sid, cx).await.log_err(); } }) .detach(); diff --git a/crates/call/src/cross_platform/participant.rs b/crates/call/src/cross_platform/participant.rs deleted file mode 100644 index de001ea9ca..0000000000 --- a/crates/call/src/cross_platform/participant.rs +++ /dev/null @@ -1,84 +0,0 @@ -#![cfg_attr(all(target_os = "windows", target_env = "gnu"), allow(unused))] - -use anyhow::{anyhow, Result}; -use client::{proto, ParticipantIndex, User}; -use collections::HashMap; -use gpui::WeakEntity; -use livekit_client::AudioStream; -use project::Project; -use std::sync::Arc; - -#[cfg(not(all(target_os = "windows", target_env = "gnu")))] -pub use livekit_client::id::TrackSid; -pub use livekit_client::track::{RemoteAudioTrack, RemoteVideoTrack}; - -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub enum ParticipantLocation { - SharedProject { project_id: u64 }, - UnsharedProject, - External, -} - -impl ParticipantLocation { - pub fn from_proto(location: Option) -> Result { - match location.and_then(|l| l.variant) { - Some(proto::participant_location::Variant::SharedProject(project)) => { - Ok(Self::SharedProject { - project_id: project.id, - }) - } - Some(proto::participant_location::Variant::UnsharedProject(_)) => { - Ok(Self::UnsharedProject) - } - Some(proto::participant_location::Variant::External(_)) => Ok(Self::External), - None => Err(anyhow!("participant location was not provided")), - } - } -} - -#[derive(Clone, Default)] -pub struct LocalParticipant { - pub projects: Vec, - pub active_project: Option>, - pub role: proto::ChannelRole, -} - -impl LocalParticipant { - pub fn can_write(&self) -> bool { - matches!( - self.role, - proto::ChannelRole::Admin | proto::ChannelRole::Member - ) - } -} - -pub struct RemoteParticipant { - pub user: Arc, - pub peer_id: proto::PeerId, - pub role: proto::ChannelRole, - pub projects: Vec, - pub location: ParticipantLocation, - pub participant_index: ParticipantIndex, - pub muted: bool, - pub speaking: bool, - #[cfg(not(all(target_os = "windows", target_env = "gnu")))] - pub video_tracks: HashMap, - #[cfg(not(all(target_os = "windows", target_env = "gnu")))] - pub audio_tracks: HashMap, -} - -impl RemoteParticipant { - pub fn has_video_tracks(&self) -> bool { - #[cfg(not(all(target_os = "windows", target_env = "gnu")))] - return !self.video_tracks.is_empty(); - #[cfg(all(target_os = "windows", target_env = "gnu"))] - return false; - } - - pub fn can_write(&self) -> bool { - matches!( - self.role, - proto::ChannelRole::Admin | proto::ChannelRole::Member - ) - } -} diff --git a/crates/call/src/macos/mod.rs b/crates/call/src/macos/mod.rs deleted file mode 100644 index 49ff6f3ef6..0000000000 --- a/crates/call/src/macos/mod.rs +++ /dev/null @@ -1,521 +0,0 @@ -pub mod participant; -pub mod room; - -use crate::call_settings::CallSettings; -use anyhow::{anyhow, Result}; -use audio::Audio; -use client::{proto, ChannelId, Client, TypedEnvelope, User, UserStore, ZED_ALWAYS_ACTIVE}; -use collections::HashSet; -use futures::{channel::oneshot, future::Shared, Future, FutureExt}; -use gpui::{ - App, AppContext as _, AsyncApp, Context, Entity, EventEmitter, Global, Subscription, Task, - WeakEntity, -}; -use postage::watch; -use project::Project; -use room::Event; -use settings::Settings; -use std::sync::Arc; - -pub use participant::ParticipantLocation; -pub use room::Room; - -struct GlobalActiveCall(Entity); - -impl Global for GlobalActiveCall {} - -pub fn init(client: Arc, user_store: Entity, cx: &mut App) { - CallSettings::register(cx); - - let active_call = cx.new(|cx| ActiveCall::new(client, user_store, cx)); - cx.set_global(GlobalActiveCall(active_call)); -} - -pub struct OneAtATime { - cancel: Option>, -} - -impl OneAtATime { - /// spawn a task in the given context. - /// if another task is spawned before that resolves, or if the OneAtATime itself is dropped, the first task will be cancelled and return Ok(None) - /// otherwise you'll see the result of the task. - fn spawn(&mut self, cx: &mut App, f: F) -> Task>> - where - F: 'static + FnOnce(AsyncApp) -> Fut, - Fut: Future>, - R: 'static, - { - let (tx, rx) = oneshot::channel(); - self.cancel.replace(tx); - cx.spawn(async move |cx| { - futures::select_biased! { - _ = rx.fuse() => Ok(None), - result = f(cx.clone()).fuse() => result.map(Some), - } - }) - } - - fn running(&self) -> bool { - self.cancel - .as_ref() - .is_some_and(|cancel| !cancel.is_canceled()) - } -} - -#[derive(Clone)] -pub struct IncomingCall { - pub room_id: u64, - pub calling_user: Arc, - pub participants: Vec>, - pub initial_project: Option, -} - -/// Singleton global maintaining the user's participation in a room across workspaces. -pub struct ActiveCall { - room: Option<(Entity, Vec)>, - pending_room_creation: Option, Arc>>>>, - location: Option>, - _join_debouncer: OneAtATime, - pending_invites: HashSet, - incoming_call: ( - watch::Sender>, - watch::Receiver>, - ), - client: Arc, - user_store: Entity, - _subscriptions: Vec, -} - -impl EventEmitter for ActiveCall {} - -impl ActiveCall { - fn new(client: Arc, user_store: Entity, cx: &mut Context) -> Self { - Self { - room: None, - pending_room_creation: None, - location: None, - pending_invites: Default::default(), - incoming_call: watch::channel(), - _join_debouncer: OneAtATime { cancel: None }, - _subscriptions: vec![ - client.add_request_handler(cx.weak_entity(), Self::handle_incoming_call), - client.add_message_handler(cx.weak_entity(), Self::handle_call_canceled), - ], - client, - user_store, - } - } - - pub fn channel_id(&self, cx: &App) -> Option { - self.room()?.read(cx).channel_id() - } - - async fn handle_incoming_call( - this: Entity, - envelope: TypedEnvelope, - mut cx: AsyncApp, - ) -> Result { - let user_store = this.update(&mut cx, |this, _| this.user_store.clone())?; - let call = IncomingCall { - room_id: envelope.payload.room_id, - participants: user_store - .update(&mut cx, |user_store, cx| { - user_store.get_users(envelope.payload.participant_user_ids, cx) - })? - .await?, - calling_user: user_store - .update(&mut cx, |user_store, cx| { - user_store.get_user(envelope.payload.calling_user_id, cx) - })? - .await?, - initial_project: envelope.payload.initial_project, - }; - this.update(&mut cx, |this, _| { - *this.incoming_call.0.borrow_mut() = Some(call); - })?; - - Ok(proto::Ack {}) - } - - async fn handle_call_canceled( - this: Entity, - envelope: TypedEnvelope, - mut cx: AsyncApp, - ) -> Result<()> { - this.update(&mut cx, |this, _| { - let mut incoming_call = this.incoming_call.0.borrow_mut(); - if incoming_call - .as_ref() - .map_or(false, |call| call.room_id == envelope.payload.room_id) - { - incoming_call.take(); - } - })?; - Ok(()) - } - - pub fn global(cx: &App) -> Entity { - cx.global::().0.clone() - } - - pub fn try_global(cx: &App) -> Option> { - cx.try_global::() - .map(|call| call.0.clone()) - } - - pub fn invite( - &mut self, - called_user_id: u64, - initial_project: Option>, - cx: &mut Context, - ) -> Task> { - if !self.pending_invites.insert(called_user_id) { - return Task::ready(Err(anyhow!("user was already invited"))); - } - cx.notify(); - - if self._join_debouncer.running() { - return Task::ready(Ok(())); - } - - let room = if let Some(room) = self.room().cloned() { - Some(Task::ready(Ok(room)).shared()) - } else { - self.pending_room_creation.clone() - }; - - let invite = if let Some(room) = room { - cx.spawn(async move |_, cx| { - let room = room.await.map_err(|err| anyhow!("{:?}", err))?; - - let initial_project_id = if let Some(initial_project) = initial_project { - Some( - room.update(cx, |room, cx| room.share_project(initial_project, cx))? - .await?, - ) - } else { - None - }; - - room.update(cx, move |room, cx| { - room.call(called_user_id, initial_project_id, cx) - })? - .await?; - - anyhow::Ok(()) - }) - } else { - let client = self.client.clone(); - let user_store = self.user_store.clone(); - let room = cx - .spawn(async move |this, cx| { - let create_room = async { - let room = cx - .update(|cx| { - Room::create( - called_user_id, - initial_project, - client, - user_store, - cx, - ) - })? - .await?; - - this.update(cx, |this, cx| this.set_room(Some(room.clone()), cx))? - .await?; - - anyhow::Ok(room) - }; - - let room = create_room.await; - this.update(cx, |this, _| this.pending_room_creation = None)?; - room.map_err(Arc::new) - }) - .shared(); - self.pending_room_creation = Some(room.clone()); - cx.background_spawn(async move { - room.await.map_err(|err| anyhow!("{:?}", err))?; - anyhow::Ok(()) - }) - }; - - cx.spawn(async move |this, cx| { - let result = invite.await; - if result.is_ok() { - this.update(cx, |this, cx| { - this.report_call_event("Participant Invited", cx) - })?; - } else { - //TODO: report collaboration error - log::error!("invite failed: {:?}", result); - } - - this.update(cx, |this, cx| { - this.pending_invites.remove(&called_user_id); - cx.notify(); - })?; - result - }) - } - - pub fn cancel_invite( - &mut self, - called_user_id: u64, - cx: &mut Context, - ) -> Task> { - let room_id = if let Some(room) = self.room() { - room.read(cx).id() - } else { - return Task::ready(Err(anyhow!("no active call"))); - }; - - let client = self.client.clone(); - cx.background_spawn(async move { - client - .request(proto::CancelCall { - room_id, - called_user_id, - }) - .await?; - anyhow::Ok(()) - }) - } - - pub fn incoming(&self) -> watch::Receiver> { - self.incoming_call.1.clone() - } - - pub fn accept_incoming(&mut self, cx: &mut Context) -> Task> { - if self.room.is_some() { - return Task::ready(Err(anyhow!("cannot join while on another call"))); - } - - let call = if let Some(call) = self.incoming_call.0.borrow_mut().take() { - call - } else { - return Task::ready(Err(anyhow!("no incoming call"))); - }; - - if self.pending_room_creation.is_some() { - return Task::ready(Ok(())); - } - - let room_id = call.room_id; - let client = self.client.clone(); - let user_store = self.user_store.clone(); - let join = self._join_debouncer.spawn(cx, move |mut cx| async move { - Room::join(room_id, client, user_store, &mut cx).await - }); - - cx.spawn(async move |this, cx| { - let room = join.await?; - this.update(cx, |this, cx| this.set_room(room.clone(), cx))? - .await?; - this.update(cx, |this, cx| { - this.report_call_event("Incoming Call Accepted", cx) - })?; - Ok(()) - }) - } - - pub fn decline_incoming(&mut self, _: &mut Context) -> Result<()> { - let call = self - .incoming_call - .0 - .borrow_mut() - .take() - .ok_or_else(|| anyhow!("no incoming call"))?; - telemetry::event!("Incoming Call Declined", room_id = call.room_id); - self.client.send(proto::DeclineCall { - room_id: call.room_id, - })?; - Ok(()) - } - - pub fn join_channel( - &mut self, - channel_id: ChannelId, - cx: &mut Context, - ) -> Task>>> { - if let Some(room) = self.room().cloned() { - if room.read(cx).channel_id() == Some(channel_id) { - return Task::ready(Ok(Some(room))); - } else { - room.update(cx, |room, cx| room.clear_state(cx)); - } - } - - if self.pending_room_creation.is_some() { - return Task::ready(Ok(None)); - } - - let client = self.client.clone(); - let user_store = self.user_store.clone(); - let join = self._join_debouncer.spawn(cx, move |mut cx| async move { - Room::join_channel(channel_id, client, user_store, &mut cx).await - }); - - cx.spawn(async move |this, cx| { - let room = join.await?; - this.update(cx, |this, cx| this.set_room(room.clone(), cx))? - .await?; - this.update(cx, |this, cx| this.report_call_event("Channel Joined", cx))?; - Ok(room) - }) - } - - pub fn hang_up(&mut self, cx: &mut Context) -> Task> { - cx.notify(); - self.report_call_event("Call Ended", cx); - - Audio::end_call(cx); - - let channel_id = self.channel_id(cx); - if let Some((room, _)) = self.room.take() { - cx.emit(Event::RoomLeft { channel_id }); - room.update(cx, |room, cx| room.leave(cx)) - } else { - Task::ready(Ok(())) - } - } - - pub fn share_project( - &mut self, - project: Entity, - cx: &mut Context, - ) -> Task> { - if let Some((room, _)) = self.room.as_ref() { - self.report_call_event("Project Shared", cx); - room.update(cx, |room, cx| room.share_project(project, cx)) - } else { - Task::ready(Err(anyhow!("no active call"))) - } - } - - pub fn unshare_project( - &mut self, - project: Entity, - cx: &mut Context, - ) -> Result<()> { - if let Some((room, _)) = self.room.as_ref() { - self.report_call_event("Project Unshared", cx); - room.update(cx, |room, cx| room.unshare_project(project, cx)) - } else { - Err(anyhow!("no active call")) - } - } - - pub fn location(&self) -> Option<&WeakEntity> { - self.location.as_ref() - } - - pub fn set_location( - &mut self, - project: Option<&Entity>, - cx: &mut Context, - ) -> Task> { - if project.is_some() || !*ZED_ALWAYS_ACTIVE { - self.location = project.map(|project| project.downgrade()); - if let Some((room, _)) = self.room.as_ref() { - return room.update(cx, |room, cx| room.set_location(project, cx)); - } - } - Task::ready(Ok(())) - } - - fn set_room(&mut self, room: Option>, cx: &mut Context) -> Task> { - if room.as_ref() == self.room.as_ref().map(|room| &room.0) { - Task::ready(Ok(())) - } else { - cx.notify(); - if let Some(room) = room { - if room.read(cx).status().is_offline() { - self.room = None; - Task::ready(Ok(())) - } else { - let subscriptions = vec![ - cx.observe(&room, |this, room, cx| { - if room.read(cx).status().is_offline() { - this.set_room(None, cx).detach_and_log_err(cx); - } - - cx.notify(); - }), - cx.subscribe(&room, |_, _, event, cx| cx.emit(event.clone())), - ]; - self.room = Some((room.clone(), subscriptions)); - let location = self - .location - .as_ref() - .and_then(|location| location.upgrade()); - let channel_id = room.read(cx).channel_id(); - cx.emit(Event::RoomJoined { channel_id }); - room.update(cx, |room, cx| room.set_location(location.as_ref(), cx)) - } - } else { - self.room = None; - Task::ready(Ok(())) - } - } - } - - pub fn room(&self) -> Option<&Entity> { - self.room.as_ref().map(|(room, _)| room) - } - - pub fn client(&self) -> Arc { - self.client.clone() - } - - pub fn pending_invites(&self) -> &HashSet { - &self.pending_invites - } - - pub fn report_call_event(&self, operation: &'static str, cx: &mut App) { - if let Some(room) = self.room() { - let room = room.read(cx); - telemetry::event!( - operation, - room_id = room.id(), - channel_id = room.channel_id() - ); - } - } -} - -#[cfg(test)] -mod test { - use gpui::TestAppContext; - - use crate::OneAtATime; - - #[gpui::test] - async fn test_one_at_a_time(cx: &mut TestAppContext) { - let mut one_at_a_time = OneAtATime { cancel: None }; - - assert_eq!( - cx.update(|cx| one_at_a_time.spawn(cx, |_| async { Ok(1) })) - .await - .unwrap(), - Some(1) - ); - - let (a, b) = cx.update(|cx| { - ( - one_at_a_time.spawn(cx, |_| async { - panic!(""); - }), - one_at_a_time.spawn(cx, |_| async { Ok(3) }), - ) - }); - - assert_eq!(a.await.unwrap(), None::); - assert_eq!(b.await.unwrap(), Some(3)); - - let promise = cx.update(|cx| one_at_a_time.spawn(cx, |_| async { Ok(4) })); - drop(one_at_a_time); - - assert_eq!(promise.await.unwrap(), None); - } -} diff --git a/crates/call/src/macos/room.rs b/crates/call/src/macos/room.rs deleted file mode 100644 index bfddd624fc..0000000000 --- a/crates/call/src/macos/room.rs +++ /dev/null @@ -1,1707 +0,0 @@ -use crate::{ - call_settings::CallSettings, - participant::{LocalParticipant, ParticipantLocation, RemoteParticipant}, -}; -use anyhow::{anyhow, Result}; -use audio::{Audio, Sound}; -use client::{ - proto::{self, PeerId}, - ChannelId, Client, ParticipantIndex, TypedEnvelope, User, UserStore, -}; -use collections::{BTreeMap, HashMap, HashSet}; -use fs::Fs; -use futures::{FutureExt, StreamExt}; -use gpui::{App, AppContext as _, AsyncApp, Context, Entity, EventEmitter, Task, WeakEntity}; -use language::LanguageRegistry; -use livekit_client_macos::{LocalAudioTrack, LocalTrackPublication, LocalVideoTrack, RoomUpdate}; -use postage::{sink::Sink, stream::Stream, watch}; -use project::Project; -use settings::Settings as _; -use std::{future::Future, mem, sync::Arc, time::Duration}; -use util::{post_inc, ResultExt, TryFutureExt}; - -pub const RECONNECT_TIMEOUT: Duration = Duration::from_secs(30); - -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum Event { - RoomJoined { - channel_id: Option, - }, - ParticipantLocationChanged { - participant_id: proto::PeerId, - }, - RemoteVideoTracksChanged { - participant_id: proto::PeerId, - }, - RemoteAudioTracksChanged { - participant_id: proto::PeerId, - }, - RemoteProjectShared { - owner: Arc, - project_id: u64, - worktree_root_names: Vec, - }, - RemoteProjectUnshared { - project_id: u64, - }, - RemoteProjectJoined { - project_id: u64, - }, - RemoteProjectInvitationDiscarded { - project_id: u64, - }, - RoomLeft { - channel_id: Option, - }, -} - -pub struct Room { - id: u64, - channel_id: Option, - live_kit: Option, - status: RoomStatus, - shared_projects: HashSet>, - joined_projects: HashSet>, - local_participant: LocalParticipant, - remote_participants: BTreeMap, - pending_participants: Vec>, - participant_user_ids: HashSet, - pending_call_count: usize, - leave_when_empty: bool, - client: Arc, - user_store: Entity, - follows_by_leader_id_project_id: HashMap<(PeerId, u64), Vec>, - client_subscriptions: Vec, - _subscriptions: Vec, - room_update_completed_tx: watch::Sender>, - room_update_completed_rx: watch::Receiver>, - pending_room_update: Option>, - maintain_connection: Option>>, -} - -impl EventEmitter for Room {} - -impl Room { - pub fn channel_id(&self) -> Option { - self.channel_id - } - - pub fn is_sharing_project(&self) -> bool { - !self.shared_projects.is_empty() - } - - #[cfg(any(test, feature = "test-support"))] - pub fn is_connected(&self) -> bool { - if let Some(live_kit) = self.live_kit.as_ref() { - matches!( - *live_kit.room.status().borrow(), - livekit_client_macos::ConnectionState::Connected { .. } - ) - } else { - false - } - } - - fn new( - id: u64, - channel_id: Option, - live_kit_connection_info: Option, - client: Arc, - user_store: Entity, - cx: &mut Context, - ) -> Self { - let live_kit_room = if let Some(connection_info) = live_kit_connection_info { - let room = livekit_client_macos::Room::new(); - let mut status = room.status(); - // Consume the initial status of the room. - let _ = status.try_recv(); - let _maintain_room = cx.spawn(async move |this, cx| { - while let Some(status) = status.next().await { - let this = if let Some(this) = this.upgrade() { - this - } else { - break; - }; - - if status == livekit_client_macos::ConnectionState::Disconnected { - this.update(cx, |this, cx| this.leave(cx).log_err()).ok(); - break; - } - } - }); - - let _handle_updates = cx.spawn({ - let room = room.clone(); - async move |this, cx| { - let mut updates = room.updates(); - while let Some(update) = updates.next().await { - let this = if let Some(this) = this.upgrade() { - this - } else { - break; - }; - - this.update(cx, |this, cx| { - this.live_kit_room_updated(update, cx).log_err() - }) - .ok(); - } - } - }); - - let connect = room.connect(&connection_info.server_url, &connection_info.token); - cx.spawn(async move |this, cx| { - connect.await?; - this.update(cx, |this, cx| { - if this.can_use_microphone() { - if let Some(live_kit) = &this.live_kit { - if !live_kit.muted_by_user && !live_kit.deafened { - return this.share_microphone(cx); - } - } - } - Task::ready(Ok(())) - })? - .await - }) - .detach_and_log_err(cx); - - Some(LiveKitRoom { - room, - screen_track: LocalTrack::None, - microphone_track: LocalTrack::None, - next_publish_id: 0, - muted_by_user: Self::mute_on_join(cx), - deafened: false, - speaking: false, - _maintain_room, - _handle_updates, - }) - } else { - None - }; - - let maintain_connection = cx.spawn({ - let client = client.clone(); - async move |this, cx| { - Self::maintain_connection(this, client.clone(), cx) - .log_err() - .await - } - }); - - Audio::play_sound(Sound::Joined, cx); - - let (room_update_completed_tx, room_update_completed_rx) = watch::channel(); - - Self { - id, - channel_id, - live_kit: live_kit_room, - status: RoomStatus::Online, - shared_projects: Default::default(), - joined_projects: Default::default(), - participant_user_ids: Default::default(), - local_participant: Default::default(), - remote_participants: Default::default(), - pending_participants: Default::default(), - pending_call_count: 0, - client_subscriptions: vec![ - client.add_message_handler(cx.weak_entity(), Self::handle_room_updated) - ], - _subscriptions: vec![ - cx.on_release(Self::released), - cx.on_app_quit(Self::app_will_quit), - ], - leave_when_empty: false, - pending_room_update: None, - client, - user_store, - follows_by_leader_id_project_id: Default::default(), - maintain_connection: Some(maintain_connection), - room_update_completed_tx, - room_update_completed_rx, - } - } - - pub(crate) fn create( - called_user_id: u64, - initial_project: Option>, - client: Arc, - user_store: Entity, - cx: &mut App, - ) -> Task>> { - cx.spawn(async move |cx| { - let response = client.request(proto::CreateRoom {}).await?; - let room_proto = response.room.ok_or_else(|| anyhow!("invalid room"))?; - let room = cx.new(|cx| { - let mut room = Self::new( - room_proto.id, - None, - response.live_kit_connection_info, - client, - user_store, - cx, - ); - if let Some(participant) = room_proto.participants.first() { - room.local_participant.role = participant.role() - } - room - })?; - - let initial_project_id = if let Some(initial_project) = initial_project { - let initial_project_id = room - .update(cx, |room, cx| { - room.share_project(initial_project.clone(), cx) - })? - .await?; - Some(initial_project_id) - } else { - None - }; - - let did_join = room - .update(cx, |room, cx| { - room.leave_when_empty = true; - room.call(called_user_id, initial_project_id, cx) - })? - .await; - match did_join { - Ok(()) => Ok(room), - Err(error) => Err(error.context("room creation failed")), - } - }) - } - - pub(crate) async fn join_channel( - channel_id: ChannelId, - client: Arc, - user_store: Entity, - cx: &mut AsyncApp, - ) -> Result> { - Self::from_join_response( - client - .request(proto::JoinChannel { - channel_id: channel_id.0, - }) - .await?, - client, - user_store, - cx, - ) - } - - pub(crate) async fn join( - room_id: u64, - client: Arc, - user_store: Entity, - cx: &mut AsyncApp, - ) -> Result> { - Self::from_join_response( - client.request(proto::JoinRoom { id: room_id }).await?, - client, - user_store, - cx, - ) - } - - fn released(&mut self, cx: &mut App) { - if self.status.is_online() { - self.leave_internal(cx).detach_and_log_err(cx); - } - } - - fn app_will_quit(&mut self, cx: &mut Context) -> impl Future { - let task = if self.status.is_online() { - let leave = self.leave_internal(cx); - Some(cx.background_spawn(async move { - leave.await.log_err(); - })) - } else { - None - }; - - async move { - if let Some(task) = task { - task.await; - } - } - } - - pub fn mute_on_join(cx: &App) -> bool { - CallSettings::get_global(cx).mute_on_join || client::IMPERSONATE_LOGIN.is_some() - } - - fn from_join_response( - response: proto::JoinRoomResponse, - client: Arc, - user_store: Entity, - cx: &mut AsyncApp, - ) -> Result> { - let room_proto = response.room.ok_or_else(|| anyhow!("invalid room"))?; - let room = cx.new(|cx| { - Self::new( - room_proto.id, - response.channel_id.map(ChannelId), - response.live_kit_connection_info, - client, - user_store, - cx, - ) - })?; - room.update(cx, |room, cx| { - room.leave_when_empty = room.channel_id.is_none(); - room.apply_room_update(room_proto, cx)?; - anyhow::Ok(()) - })??; - Ok(room) - } - - fn should_leave(&self) -> bool { - self.leave_when_empty - && self.pending_room_update.is_none() - && self.pending_participants.is_empty() - && self.remote_participants.is_empty() - && self.pending_call_count == 0 - } - - pub(crate) fn leave(&mut self, cx: &mut Context) -> Task> { - cx.notify(); - self.leave_internal(cx) - } - - fn leave_internal(&mut self, cx: &mut App) -> Task> { - if self.status.is_offline() { - return Task::ready(Err(anyhow!("room is offline"))); - } - - log::info!("leaving room"); - Audio::play_sound(Sound::Leave, cx); - - self.clear_state(cx); - - let leave_room = self.client.request(proto::LeaveRoom {}); - cx.background_spawn(async move { - leave_room.await?; - anyhow::Ok(()) - }) - } - - pub(crate) fn clear_state(&mut self, cx: &mut App) { - for project in self.shared_projects.drain() { - if let Some(project) = project.upgrade() { - project.update(cx, |project, cx| { - project.unshare(cx).log_err(); - }); - } - } - for project in self.joined_projects.drain() { - if let Some(project) = project.upgrade() { - project.update(cx, |project, cx| { - project.disconnected_from_host(cx); - project.close(cx); - }); - } - } - - self.status = RoomStatus::Offline; - self.remote_participants.clear(); - self.pending_participants.clear(); - self.participant_user_ids.clear(); - self.client_subscriptions.clear(); - self.live_kit.take(); - self.pending_room_update.take(); - self.maintain_connection.take(); - } - - async fn maintain_connection( - this: WeakEntity, - client: Arc, - cx: &mut AsyncApp, - ) -> Result<()> { - let mut client_status = client.status(); - loop { - let _ = client_status.try_recv(); - let is_connected = client_status.borrow().is_connected(); - // Even if we're initially connected, any future change of the status means we momentarily disconnected. - if !is_connected || client_status.next().await.is_some() { - log::info!("detected client disconnection"); - - this.upgrade() - .ok_or_else(|| anyhow!("room was dropped"))? - .update(cx, |this, cx| { - this.status = RoomStatus::Rejoining; - cx.notify(); - })?; - - // Wait for client to re-establish a connection to the server. - { - let mut reconnection_timeout = - cx.background_executor().timer(RECONNECT_TIMEOUT).fuse(); - let client_reconnection = async { - let mut remaining_attempts = 3; - while remaining_attempts > 0 { - if client_status.borrow().is_connected() { - log::info!("client reconnected, attempting to rejoin room"); - - let Some(this) = this.upgrade() else { break }; - match this.update(cx, |this, cx| this.rejoin(cx)) { - Ok(task) => { - if task.await.log_err().is_some() { - return true; - } else { - remaining_attempts -= 1; - } - } - Err(_app_dropped) => return false, - } - } else if client_status.borrow().is_signed_out() { - return false; - } - - log::info!( - "waiting for client status change, remaining attempts {}", - remaining_attempts - ); - client_status.next().await; - } - false - } - .fuse(); - futures::pin_mut!(client_reconnection); - - futures::select_biased! { - reconnected = client_reconnection => { - if reconnected { - log::info!("successfully reconnected to room"); - // If we successfully joined the room, go back around the loop - // waiting for future connection status changes. - continue; - } - } - _ = reconnection_timeout => { - log::info!("room reconnection timeout expired"); - } - } - } - - break; - } - } - - // The client failed to re-establish a connection to the server - // or an error occurred while trying to re-join the room. Either way - // we leave the room and return an error. - if let Some(this) = this.upgrade() { - log::info!("reconnection failed, leaving room"); - this.update(cx, |this, cx| this.leave(cx))?.await?; - } - Err(anyhow!( - "can't reconnect to room: client failed to re-establish connection" - )) - } - - fn rejoin(&mut self, cx: &mut Context) -> Task> { - let mut projects = HashMap::default(); - let mut reshared_projects = Vec::new(); - let mut rejoined_projects = Vec::new(); - self.shared_projects.retain(|project| { - if let Some(handle) = project.upgrade() { - let project = handle.read(cx); - if let Some(project_id) = project.remote_id() { - projects.insert(project_id, handle.clone()); - reshared_projects.push(proto::UpdateProject { - project_id, - worktrees: project.worktree_metadata_protos(cx), - }); - return true; - } - } - false - }); - self.joined_projects.retain(|project| { - if let Some(handle) = project.upgrade() { - let project = handle.read(cx); - if let Some(project_id) = project.remote_id() { - projects.insert(project_id, handle.clone()); - let mut worktrees = Vec::new(); - let mut repositories = Vec::new(); - for worktree in project.worktrees(cx) { - let worktree = worktree.read(cx); - worktrees.push(proto::RejoinWorktree { - id: worktree.id().to_proto(), - scan_id: worktree.completed_scan_id() as u64, - }); - } - for (entry_id, repository) in project.repositories(cx) { - let repository = repository.read(cx); - repositories.push(proto::RejoinRepository { - id: entry_id.to_proto(), - scan_id: repository.completed_scan_id as u64, - }); - } - - rejoined_projects.push(proto::RejoinProject { - id: project_id, - worktrees, - repositories, - }); - } - return true; - } - false - }); - - let response = self.client.request_envelope(proto::RejoinRoom { - id: self.id, - reshared_projects, - rejoined_projects, - }); - - cx.spawn(async move |this, cx| { - let response = response.await?; - let message_id = response.message_id; - let response = response.payload; - let room_proto = response.room.ok_or_else(|| anyhow!("invalid room"))?; - this.update(cx, |this, cx| { - this.status = RoomStatus::Online; - this.apply_room_update(room_proto, cx)?; - - for reshared_project in response.reshared_projects { - if let Some(project) = projects.get(&reshared_project.id) { - project.update(cx, |project, cx| { - project.reshared(reshared_project, cx).log_err(); - }); - } - } - - for rejoined_project in response.rejoined_projects { - if let Some(project) = projects.get(&rejoined_project.id) { - project.update(cx, |project, cx| { - project.rejoined(rejoined_project, message_id, cx).log_err(); - }); - } - } - - anyhow::Ok(()) - })? - }) - } - - pub fn id(&self) -> u64 { - self.id - } - - pub fn status(&self) -> RoomStatus { - self.status - } - - pub fn local_participant(&self) -> &LocalParticipant { - &self.local_participant - } - - pub fn local_participant_user(&self, cx: &App) -> Option> { - self.user_store.read(cx).current_user() - } - - pub fn remote_participants(&self) -> &BTreeMap { - &self.remote_participants - } - - pub fn remote_participant_for_peer_id(&self, peer_id: PeerId) -> Option<&RemoteParticipant> { - self.remote_participants - .values() - .find(|p| p.peer_id == peer_id) - } - - pub fn role_for_user(&self, user_id: u64) -> Option { - self.remote_participants - .get(&user_id) - .map(|participant| participant.role) - } - - pub fn contains_guests(&self) -> bool { - self.local_participant.role == proto::ChannelRole::Guest - || self - .remote_participants - .values() - .any(|p| p.role == proto::ChannelRole::Guest) - } - - pub fn local_participant_is_admin(&self) -> bool { - self.local_participant.role == proto::ChannelRole::Admin - } - - pub fn local_participant_is_guest(&self) -> bool { - self.local_participant.role == proto::ChannelRole::Guest - } - - pub fn set_participant_role( - &mut self, - user_id: u64, - role: proto::ChannelRole, - cx: &Context, - ) -> Task> { - let client = self.client.clone(); - let room_id = self.id; - let role = role.into(); - cx.spawn(async move |_, _| { - client - .request(proto::SetRoomParticipantRole { - room_id, - user_id, - role, - }) - .await - .map(|_| ()) - }) - } - - pub fn pending_participants(&self) -> &[Arc] { - &self.pending_participants - } - - pub fn contains_participant(&self, user_id: u64) -> bool { - self.participant_user_ids.contains(&user_id) - } - - pub fn followers_for(&self, leader_id: PeerId, project_id: u64) -> &[PeerId] { - self.follows_by_leader_id_project_id - .get(&(leader_id, project_id)) - .map_or(&[], |v| v.as_slice()) - } - - /// Returns the most 'active' projects, defined as most people in the project - pub fn most_active_project(&self, cx: &App) -> Option<(u64, u64)> { - let mut project_hosts_and_guest_counts = HashMap::, u32)>::default(); - for participant in self.remote_participants.values() { - match participant.location { - ParticipantLocation::SharedProject { project_id } => { - project_hosts_and_guest_counts - .entry(project_id) - .or_default() - .1 += 1; - } - ParticipantLocation::External | ParticipantLocation::UnsharedProject => {} - } - for project in &participant.projects { - project_hosts_and_guest_counts - .entry(project.id) - .or_default() - .0 = Some(participant.user.id); - } - } - - if let Some(user) = self.user_store.read(cx).current_user() { - for project in &self.local_participant.projects { - project_hosts_and_guest_counts - .entry(project.id) - .or_default() - .0 = Some(user.id); - } - } - - project_hosts_and_guest_counts - .into_iter() - .filter_map(|(id, (host, guest_count))| Some((id, host?, guest_count))) - .max_by_key(|(_, _, guest_count)| *guest_count) - .map(|(id, host, _)| (id, host)) - } - - async fn handle_room_updated( - this: Entity, - envelope: TypedEnvelope, - mut cx: AsyncApp, - ) -> Result<()> { - let room = envelope - .payload - .room - .ok_or_else(|| anyhow!("invalid room"))?; - this.update(&mut cx, |this, cx| this.apply_room_update(room, cx))? - } - - fn apply_room_update(&mut self, mut room: proto::Room, cx: &mut Context) -> Result<()> { - // Filter ourselves out from the room's participants. - let local_participant_ix = room - .participants - .iter() - .position(|participant| Some(participant.user_id) == self.client.user_id()); - let local_participant = local_participant_ix.map(|ix| room.participants.swap_remove(ix)); - - let pending_participant_user_ids = room - .pending_participants - .iter() - .map(|p| p.user_id) - .collect::>(); - - let remote_participant_user_ids = room - .participants - .iter() - .map(|p| p.user_id) - .collect::>(); - - let (remote_participants, pending_participants) = - self.user_store.update(cx, move |user_store, cx| { - ( - user_store.get_users(remote_participant_user_ids, cx), - user_store.get_users(pending_participant_user_ids, cx), - ) - }); - - self.pending_room_update = Some(cx.spawn(async move |this, cx| { - let (remote_participants, pending_participants) = - futures::join!(remote_participants, pending_participants); - - this.update(cx, |this, cx| { - this.participant_user_ids.clear(); - - if let Some(participant) = local_participant { - let role = participant.role(); - this.local_participant.projects = participant.projects; - if this.local_participant.role != role { - this.local_participant.role = role; - - if role == proto::ChannelRole::Guest { - for project in mem::take(&mut this.shared_projects) { - if let Some(project) = project.upgrade() { - this.unshare_project(project, cx).log_err(); - } - } - this.local_participant.projects.clear(); - if let Some(live_kit_room) = &mut this.live_kit { - live_kit_room.stop_publishing(cx); - } - } - - this.joined_projects.retain(|project| { - if let Some(project) = project.upgrade() { - project.update(cx, |project, cx| project.set_role(role, cx)); - true - } else { - false - } - }); - } - } else { - this.local_participant.projects.clear(); - } - - if let Some(participants) = remote_participants.log_err() { - for (participant, user) in room.participants.into_iter().zip(participants) { - let Some(peer_id) = participant.peer_id else { - continue; - }; - let participant_index = ParticipantIndex(participant.participant_index); - this.participant_user_ids.insert(participant.user_id); - - let old_projects = this - .remote_participants - .get(&participant.user_id) - .into_iter() - .flat_map(|existing| &existing.projects) - .map(|project| project.id) - .collect::>(); - let new_projects = participant - .projects - .iter() - .map(|project| project.id) - .collect::>(); - - for project in &participant.projects { - if !old_projects.contains(&project.id) { - cx.emit(Event::RemoteProjectShared { - owner: user.clone(), - project_id: project.id, - worktree_root_names: project.worktree_root_names.clone(), - }); - } - } - - for unshared_project_id in old_projects.difference(&new_projects) { - this.joined_projects.retain(|project| { - if let Some(project) = project.upgrade() { - project.update(cx, |project, cx| { - if project.remote_id() == Some(*unshared_project_id) { - project.disconnected_from_host(cx); - false - } else { - true - } - }) - } else { - false - } - }); - cx.emit(Event::RemoteProjectUnshared { - project_id: *unshared_project_id, - }); - } - - let role = participant.role(); - let location = ParticipantLocation::from_proto(participant.location) - .unwrap_or(ParticipantLocation::External); - if let Some(remote_participant) = - this.remote_participants.get_mut(&participant.user_id) - { - remote_participant.peer_id = peer_id; - remote_participant.projects = participant.projects; - remote_participant.participant_index = participant_index; - if location != remote_participant.location - || role != remote_participant.role - { - remote_participant.location = location; - remote_participant.role = role; - cx.emit(Event::ParticipantLocationChanged { - participant_id: peer_id, - }); - } - } else { - this.remote_participants.insert( - participant.user_id, - RemoteParticipant { - user: user.clone(), - participant_index, - peer_id, - projects: participant.projects, - location, - role, - muted: true, - speaking: false, - video_tracks: Default::default(), - audio_tracks: Default::default(), - }, - ); - - Audio::play_sound(Sound::Joined, cx); - - if let Some(live_kit) = this.live_kit.as_ref() { - let video_tracks = - live_kit.room.remote_video_tracks(&user.id.to_string()); - let audio_tracks = - live_kit.room.remote_audio_tracks(&user.id.to_string()); - let publications = live_kit - .room - .remote_audio_track_publications(&user.id.to_string()); - - for track in video_tracks { - this.live_kit_room_updated( - RoomUpdate::SubscribedToRemoteVideoTrack(track), - cx, - ) - .log_err(); - } - - for (track, publication) in - audio_tracks.iter().zip(publications.iter()) - { - this.live_kit_room_updated( - RoomUpdate::SubscribedToRemoteAudioTrack( - track.clone(), - publication.clone(), - ), - cx, - ) - .log_err(); - } - } - } - } - - this.remote_participants.retain(|user_id, participant| { - if this.participant_user_ids.contains(user_id) { - true - } else { - for project in &participant.projects { - cx.emit(Event::RemoteProjectUnshared { - project_id: project.id, - }); - } - false - } - }); - } - - if let Some(pending_participants) = pending_participants.log_err() { - this.pending_participants = pending_participants; - for participant in &this.pending_participants { - this.participant_user_ids.insert(participant.id); - } - } - - this.follows_by_leader_id_project_id.clear(); - for follower in room.followers { - let project_id = follower.project_id; - let (leader, follower) = match (follower.leader_id, follower.follower_id) { - (Some(leader), Some(follower)) => (leader, follower), - - _ => { - log::error!("Follower message {follower:?} missing some state"); - continue; - } - }; - - let list = this - .follows_by_leader_id_project_id - .entry((leader, project_id)) - .or_default(); - if !list.contains(&follower) { - list.push(follower); - } - } - - this.pending_room_update.take(); - if this.should_leave() { - log::info!("room is empty, leaving"); - this.leave(cx).detach(); - } - - this.user_store.update(cx, |user_store, cx| { - let participant_indices_by_user_id = this - .remote_participants - .iter() - .map(|(user_id, participant)| (*user_id, participant.participant_index)) - .collect(); - user_store.set_participant_indices(participant_indices_by_user_id, cx); - }); - - this.check_invariants(); - this.room_update_completed_tx.try_send(Some(())).ok(); - cx.notify(); - }) - .ok(); - })); - - cx.notify(); - Ok(()) - } - - pub fn room_update_completed(&mut self) -> impl Future { - let mut done_rx = self.room_update_completed_rx.clone(); - async move { - while let Some(result) = done_rx.next().await { - if result.is_some() { - break; - } - } - } - } - - fn live_kit_room_updated(&mut self, update: RoomUpdate, cx: &mut Context) -> Result<()> { - match update { - RoomUpdate::SubscribedToRemoteVideoTrack(track) => { - let user_id = track.publisher_id().parse()?; - let track_id = track.sid().to_string(); - let participant = self - .remote_participants - .get_mut(&user_id) - .ok_or_else(|| anyhow!("subscribed to track by unknown participant"))?; - participant.video_tracks.insert(track_id.clone(), track); - cx.emit(Event::RemoteVideoTracksChanged { - participant_id: participant.peer_id, - }); - } - - RoomUpdate::UnsubscribedFromRemoteVideoTrack { - publisher_id, - track_id, - } => { - let user_id = publisher_id.parse()?; - let participant = self - .remote_participants - .get_mut(&user_id) - .ok_or_else(|| anyhow!("unsubscribed from track by unknown participant"))?; - participant.video_tracks.remove(&track_id); - cx.emit(Event::RemoteVideoTracksChanged { - participant_id: participant.peer_id, - }); - } - - RoomUpdate::ActiveSpeakersChanged { speakers } => { - let mut speaker_ids = speakers - .into_iter() - .filter_map(|speaker_sid| speaker_sid.parse().ok()) - .collect::>(); - speaker_ids.sort_unstable(); - for (sid, participant) in &mut self.remote_participants { - participant.speaking = speaker_ids.binary_search(sid).is_ok(); - } - if let Some(id) = self.client.user_id() { - if let Some(room) = &mut self.live_kit { - room.speaking = speaker_ids.binary_search(&id).is_ok(); - } - } - } - - RoomUpdate::RemoteAudioTrackMuteChanged { track_id, muted } => { - let mut found = false; - for participant in &mut self.remote_participants.values_mut() { - for track in participant.audio_tracks.values() { - if track.sid() == track_id { - found = true; - break; - } - } - if found { - participant.muted = muted; - break; - } - } - } - - RoomUpdate::SubscribedToRemoteAudioTrack(track, publication) => { - if let Some(live_kit) = &self.live_kit { - if live_kit.deafened { - track.stop(); - cx.foreground_executor() - .spawn(publication.set_enabled(false)) - .detach(); - } - } - - let user_id = track.publisher_id().parse()?; - let track_id = track.sid().to_string(); - let participant = self - .remote_participants - .get_mut(&user_id) - .ok_or_else(|| anyhow!("subscribed to track by unknown participant"))?; - participant.audio_tracks.insert(track_id.clone(), track); - participant.muted = publication.is_muted(); - - cx.emit(Event::RemoteAudioTracksChanged { - participant_id: participant.peer_id, - }); - } - - RoomUpdate::UnsubscribedFromRemoteAudioTrack { - publisher_id, - track_id, - } => { - let user_id = publisher_id.parse()?; - let participant = self - .remote_participants - .get_mut(&user_id) - .ok_or_else(|| anyhow!("unsubscribed from track by unknown participant"))?; - participant.audio_tracks.remove(&track_id); - cx.emit(Event::RemoteAudioTracksChanged { - participant_id: participant.peer_id, - }); - } - - RoomUpdate::LocalAudioTrackUnpublished { publication } => { - log::info!("unpublished audio track {}", publication.sid()); - if let Some(room) = &mut self.live_kit { - room.microphone_track = LocalTrack::None; - } - } - - RoomUpdate::LocalVideoTrackUnpublished { publication } => { - log::info!("unpublished video track {}", publication.sid()); - if let Some(room) = &mut self.live_kit { - room.screen_track = LocalTrack::None; - } - } - - RoomUpdate::LocalAudioTrackPublished { publication } => { - log::info!("published audio track {}", publication.sid()); - } - - RoomUpdate::LocalVideoTrackPublished { publication } => { - log::info!("published video track {}", publication.sid()); - } - } - - cx.notify(); - Ok(()) - } - - fn check_invariants(&self) { - #[cfg(any(test, feature = "test-support"))] - { - for participant in self.remote_participants.values() { - assert!(self.participant_user_ids.contains(&participant.user.id)); - assert_ne!(participant.user.id, self.client.user_id().unwrap()); - } - - for participant in &self.pending_participants { - assert!(self.participant_user_ids.contains(&participant.id)); - assert_ne!(participant.id, self.client.user_id().unwrap()); - } - - assert_eq!( - self.participant_user_ids.len(), - self.remote_participants.len() + self.pending_participants.len() - ); - } - } - - pub(crate) fn call( - &mut self, - called_user_id: u64, - initial_project_id: Option, - cx: &mut Context, - ) -> Task> { - if self.status.is_offline() { - return Task::ready(Err(anyhow!("room is offline"))); - } - - cx.notify(); - let client = self.client.clone(); - let room_id = self.id; - self.pending_call_count += 1; - cx.spawn(async move |this, cx| { - let result = client - .request(proto::Call { - room_id, - called_user_id, - initial_project_id, - }) - .await; - this.update(cx, |this, cx| { - this.pending_call_count -= 1; - if this.should_leave() { - this.leave(cx).detach_and_log_err(cx); - } - })?; - result?; - Ok(()) - }) - } - - pub fn join_project( - &mut self, - id: u64, - language_registry: Arc, - fs: Arc, - cx: &mut Context, - ) -> Task>> { - let client = self.client.clone(); - let user_store = self.user_store.clone(); - cx.emit(Event::RemoteProjectJoined { project_id: id }); - cx.spawn(async move |this, cx| { - let project = - Project::in_room(id, client, user_store, language_registry, fs, cx.clone()).await?; - - this.update(cx, |this, cx| { - this.joined_projects.retain(|project| { - if let Some(project) = project.upgrade() { - !project.read(cx).is_disconnected(cx) - } else { - false - } - }); - this.joined_projects.insert(project.downgrade()); - })?; - Ok(project) - }) - } - - pub fn share_project( - &mut self, - project: Entity, - cx: &mut Context, - ) -> Task> { - if let Some(project_id) = project.read(cx).remote_id() { - return Task::ready(Ok(project_id)); - } - - let request = self.client.request(proto::ShareProject { - room_id: self.id(), - worktrees: project.read(cx).worktree_metadata_protos(cx), - is_ssh_project: project.read(cx).is_via_ssh(), - }); - - cx.spawn(async move |this, cx| { - let response = request.await?; - - project.update(cx, |project, cx| project.shared(response.project_id, cx))??; - - // If the user's location is in this project, it changes from UnsharedProject to SharedProject. - this.update(cx, |this, cx| { - this.shared_projects.insert(project.downgrade()); - let active_project = this.local_participant.active_project.as_ref(); - if active_project.map_or(false, |location| *location == project) { - this.set_location(Some(&project), cx) - } else { - Task::ready(Ok(())) - } - })? - .await?; - - Ok(response.project_id) - }) - } - - pub(crate) fn unshare_project( - &mut self, - project: Entity, - cx: &mut Context, - ) -> Result<()> { - let project_id = match project.read(cx).remote_id() { - Some(project_id) => project_id, - None => return Ok(()), - }; - - self.client.send(proto::UnshareProject { project_id })?; - project.update(cx, |this, cx| this.unshare(cx))?; - - if self.local_participant.active_project == Some(project.downgrade()) { - self.set_location(Some(&project), cx).detach_and_log_err(cx); - } - Ok(()) - } - - pub(crate) fn set_location( - &mut self, - project: Option<&Entity>, - cx: &mut Context, - ) -> Task> { - if self.status.is_offline() { - return Task::ready(Err(anyhow!("room is offline"))); - } - - let client = self.client.clone(); - let room_id = self.id; - let location = if let Some(project) = project { - self.local_participant.active_project = Some(project.downgrade()); - if let Some(project_id) = project.read(cx).remote_id() { - proto::participant_location::Variant::SharedProject( - proto::participant_location::SharedProject { id: project_id }, - ) - } else { - proto::participant_location::Variant::UnsharedProject( - proto::participant_location::UnsharedProject {}, - ) - } - } else { - self.local_participant.active_project = None; - proto::participant_location::Variant::External(proto::participant_location::External {}) - }; - - cx.notify(); - cx.background_spawn(async move { - client - .request(proto::UpdateParticipantLocation { - room_id, - location: Some(proto::ParticipantLocation { - variant: Some(location), - }), - }) - .await?; - Ok(()) - }) - } - - pub fn is_screen_sharing(&self) -> bool { - self.live_kit.as_ref().map_or(false, |live_kit| { - !matches!(live_kit.screen_track, LocalTrack::None) - }) - } - - pub fn is_sharing_mic(&self) -> bool { - self.live_kit.as_ref().map_or(false, |live_kit| { - !matches!(live_kit.microphone_track, LocalTrack::None) - }) - } - - pub fn is_muted(&self) -> bool { - self.live_kit.as_ref().map_or(false, |live_kit| { - matches!(live_kit.microphone_track, LocalTrack::None) - || live_kit.muted_by_user - || live_kit.deafened - }) - } - - pub fn muted_by_user(&self) -> bool { - self.live_kit - .as_ref() - .map_or(false, |live_kit| live_kit.muted_by_user) - } - - pub fn is_speaking(&self) -> bool { - self.live_kit - .as_ref() - .map_or(false, |live_kit| live_kit.speaking) - } - - pub fn is_deafened(&self) -> Option { - self.live_kit.as_ref().map(|live_kit| live_kit.deafened) - } - - pub fn can_use_microphone(&self) -> bool { - use proto::ChannelRole::*; - match self.local_participant.role { - Admin | Member | Talker => true, - Guest | Banned => false, - } - } - - pub fn can_share_projects(&self) -> bool { - use proto::ChannelRole::*; - match self.local_participant.role { - Admin | Member => true, - Guest | Banned | Talker => false, - } - } - - #[track_caller] - pub fn share_microphone(&mut self, cx: &mut Context) -> Task> { - if self.status.is_offline() { - return Task::ready(Err(anyhow!("room is offline"))); - } - - let publish_id = if let Some(live_kit) = self.live_kit.as_mut() { - let publish_id = post_inc(&mut live_kit.next_publish_id); - live_kit.microphone_track = LocalTrack::Pending { publish_id }; - cx.notify(); - publish_id - } else { - return Task::ready(Err(anyhow!("live-kit was not initialized"))); - }; - - cx.spawn(async move |this, cx| { - let publish_track = async { - let track = LocalAudioTrack::create(); - this.upgrade() - .ok_or_else(|| anyhow!("room was dropped"))? - .update(cx, |this, _| { - this.live_kit - .as_ref() - .map(|live_kit| live_kit.room.publish_audio_track(track)) - })? - .ok_or_else(|| anyhow!("live-kit was not initialized"))? - .await - }; - let publication = publish_track.await; - this.upgrade() - .ok_or_else(|| anyhow!("room was dropped"))? - .update(cx, |this, cx| { - let live_kit = this - .live_kit - .as_mut() - .ok_or_else(|| anyhow!("live-kit was not initialized"))?; - - let canceled = if let LocalTrack::Pending { - publish_id: cur_publish_id, - } = &live_kit.microphone_track - { - *cur_publish_id != publish_id - } else { - true - }; - - match publication { - Ok(publication) => { - if canceled { - live_kit.room.unpublish_track(publication); - } else { - if live_kit.muted_by_user || live_kit.deafened { - cx.background_spawn(publication.set_mute(true)).detach(); - } - live_kit.microphone_track = LocalTrack::Published { - track_publication: publication, - }; - cx.notify(); - } - Ok(()) - } - Err(error) => { - if canceled { - Ok(()) - } else { - live_kit.microphone_track = LocalTrack::None; - cx.notify(); - Err(error) - } - } - } - })? - }) - } - - pub fn share_screen(&mut self, cx: &mut Context) -> Task> { - if self.status.is_offline() { - return Task::ready(Err(anyhow!("room is offline"))); - } else if self.is_screen_sharing() { - return Task::ready(Err(anyhow!("screen was already shared"))); - } - - let (displays, publish_id) = if let Some(live_kit) = self.live_kit.as_mut() { - let publish_id = post_inc(&mut live_kit.next_publish_id); - live_kit.screen_track = LocalTrack::Pending { publish_id }; - cx.notify(); - (live_kit.room.display_sources(), publish_id) - } else { - return Task::ready(Err(anyhow!("live-kit was not initialized"))); - }; - - cx.spawn(async move |this, cx| { - let publish_track = async { - let displays = displays.await?; - let display = displays - .first() - .ok_or_else(|| anyhow!("no display found"))?; - let track = LocalVideoTrack::screen_share_for_display(display); - this.upgrade() - .ok_or_else(|| anyhow!("room was dropped"))? - .update(cx, |this, _| { - this.live_kit - .as_ref() - .map(|live_kit| live_kit.room.publish_video_track(track)) - })? - .ok_or_else(|| anyhow!("live-kit was not initialized"))? - .await - }; - - let publication = publish_track.await; - this.upgrade() - .ok_or_else(|| anyhow!("room was dropped"))? - .update(cx, |this, cx| { - let live_kit = this - .live_kit - .as_mut() - .ok_or_else(|| anyhow!("live-kit was not initialized"))?; - - let canceled = if let LocalTrack::Pending { - publish_id: cur_publish_id, - } = &live_kit.screen_track - { - *cur_publish_id != publish_id - } else { - true - }; - - match publication { - Ok(publication) => { - if canceled { - live_kit.room.unpublish_track(publication); - } else { - live_kit.screen_track = LocalTrack::Published { - track_publication: publication, - }; - cx.notify(); - } - - Audio::play_sound(Sound::StartScreenshare, cx); - - Ok(()) - } - Err(error) => { - if canceled { - Ok(()) - } else { - live_kit.screen_track = LocalTrack::None; - cx.notify(); - Err(error) - } - } - } - })? - }) - } - - pub fn toggle_mute(&mut self, cx: &mut Context) { - if let Some(live_kit) = self.live_kit.as_mut() { - // When unmuting, undeafen if the user was deafened before. - let was_deafened = live_kit.deafened; - if live_kit.muted_by_user - || live_kit.deafened - || matches!(live_kit.microphone_track, LocalTrack::None) - { - live_kit.muted_by_user = false; - live_kit.deafened = false; - } else { - live_kit.muted_by_user = true; - } - let muted = live_kit.muted_by_user; - let should_undeafen = was_deafened && !live_kit.deafened; - - if let Some(task) = self.set_mute(muted, cx) { - task.detach_and_log_err(cx); - } - - if should_undeafen { - if let Some(task) = self.set_deafened(false, cx) { - task.detach_and_log_err(cx); - } - } - } - } - - pub fn toggle_deafen(&mut self, cx: &mut Context) { - if let Some(live_kit) = self.live_kit.as_mut() { - // When deafening, mute the microphone if it was not already muted. - // When un-deafening, unmute the microphone, unless it was explicitly muted. - let deafened = !live_kit.deafened; - live_kit.deafened = deafened; - let should_change_mute = !live_kit.muted_by_user; - - if let Some(task) = self.set_deafened(deafened, cx) { - task.detach_and_log_err(cx); - } - - if should_change_mute { - if let Some(task) = self.set_mute(deafened, cx) { - task.detach_and_log_err(cx); - } - } - } - } - - pub fn unshare_screen(&mut self, cx: &mut Context) -> Result<()> { - if self.status.is_offline() { - return Err(anyhow!("room is offline")); - } - - let live_kit = self - .live_kit - .as_mut() - .ok_or_else(|| anyhow!("live-kit was not initialized"))?; - match mem::take(&mut live_kit.screen_track) { - LocalTrack::None => Err(anyhow!("screen was not shared")), - LocalTrack::Pending { .. } => { - cx.notify(); - Ok(()) - } - LocalTrack::Published { - track_publication, .. - } => { - live_kit.room.unpublish_track(track_publication); - cx.notify(); - - Audio::play_sound(Sound::StopScreenshare, cx); - Ok(()) - } - } - } - - fn set_deafened(&mut self, deafened: bool, cx: &mut Context) -> Option>> { - let live_kit = self.live_kit.as_mut()?; - cx.notify(); - - let mut track_updates = Vec::new(); - for participant in self.remote_participants.values() { - for publication in live_kit - .room - .remote_audio_track_publications(&participant.user.id.to_string()) - { - track_updates.push(publication.set_enabled(!deafened)); - } - - for track in participant.audio_tracks.values() { - if deafened { - track.stop(); - } else { - track.start(); - } - } - } - - Some(cx.foreground_executor().spawn(async move { - for result in futures::future::join_all(track_updates).await { - result?; - } - Ok(()) - })) - } - - fn set_mute(&mut self, should_mute: bool, cx: &mut Context) -> Option>> { - let live_kit = self.live_kit.as_mut()?; - cx.notify(); - - if should_mute { - Audio::play_sound(Sound::Mute, cx); - } else { - Audio::play_sound(Sound::Unmute, cx); - } - - match &mut live_kit.microphone_track { - LocalTrack::None => { - if should_mute { - None - } else { - Some(self.share_microphone(cx)) - } - } - LocalTrack::Pending { .. } => None, - LocalTrack::Published { track_publication } => Some( - cx.foreground_executor() - .spawn(track_publication.set_mute(should_mute)), - ), - } - } - - #[cfg(any(test, feature = "test-support"))] - pub fn set_display_sources(&self, sources: Vec) { - self.live_kit - .as_ref() - .unwrap() - .room - .set_display_sources(sources); - } -} - -struct LiveKitRoom { - room: Arc, - screen_track: LocalTrack, - microphone_track: LocalTrack, - /// Tracks whether we're currently in a muted state due to auto-mute from deafening or manual mute performed by user. - muted_by_user: bool, - deafened: bool, - speaking: bool, - next_publish_id: usize, - _maintain_room: Task<()>, - _handle_updates: Task<()>, -} - -impl LiveKitRoom { - fn stop_publishing(&mut self, cx: &mut Context) { - if let LocalTrack::Published { - track_publication, .. - } = mem::replace(&mut self.microphone_track, LocalTrack::None) - { - self.room.unpublish_track(track_publication); - cx.notify(); - } - - if let LocalTrack::Published { - track_publication, .. - } = mem::replace(&mut self.screen_track, LocalTrack::None) - { - self.room.unpublish_track(track_publication); - cx.notify(); - } - } -} - -enum LocalTrack { - None, - Pending { - publish_id: usize, - }, - Published { - track_publication: LocalTrackPublication, - }, -} - -impl Default for LocalTrack { - fn default() -> Self { - Self::None - } -} - -#[derive(Copy, Clone, PartialEq, Eq)] -pub enum RoomStatus { - Online, - Rejoining, - Offline, -} - -impl RoomStatus { - pub fn is_offline(&self) -> bool { - matches!(self, RoomStatus::Offline) - } - - pub fn is_online(&self) -> bool { - matches!(self, RoomStatus::Online) - } -} diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index f18db8bac9..3914b4ae2f 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -729,10 +729,11 @@ mod mac_os { use anyhow::{anyhow, Context as _, Result}; use core_foundation::{ array::{CFArray, CFIndex}, + base::TCFType as _, string::kCFStringEncodingUTF8, url::{CFURLCreateWithBytes, CFURL}, }; - use core_services::{kLSLaunchDefaults, LSLaunchURLSpec, LSOpenFromURLSpec, TCFType}; + use core_services::{kLSLaunchDefaults, LSLaunchURLSpec, LSOpenFromURLSpec}; use serde::Deserialize; use std::{ ffi::OsStr, @@ -759,7 +760,6 @@ mod mac_os { }, LocalPath { executable: PathBuf, - plist: InfoPlist, }, } @@ -796,34 +796,16 @@ mod mac_os { plist, }) } - _ => { - println!("Bundle path {bundle_path:?} has no *.app extension, attempting to locate a dev build"); - let plist_path = bundle_path - .parent() - .with_context(|| format!("Bundle path {bundle_path:?} has no parent"))? - .join("WebRTC.framework/Resources/Info.plist"); - let plist = - plist::from_file::<_, InfoPlist>(&plist_path).with_context(|| { - format!("Reading dev bundle plist file at {plist_path:?}") - })?; - Ok(Bundle::LocalPath { - executable: bundle_path, - plist, - }) - } + _ => Ok(Bundle::LocalPath { + executable: bundle_path, + }), } } } impl InstalledApp for Bundle { fn zed_version_string(&self) -> String { - let is_dev = matches!(self, Self::LocalPath { .. }); - format!( - "Zed {}{} – {}", - self.plist().bundle_short_version_string, - if is_dev { " (dev)" } else { "" }, - self.path().display(), - ) + format!("Zed {} – {}", self.version(), self.path().display(),) } fn launch(&self, url: String) -> anyhow::Result<()> { @@ -909,10 +891,10 @@ mod mac_os { } impl Bundle { - fn plist(&self) -> &InfoPlist { + fn version(&self) -> String { match self { - Self::App { plist, .. } => plist, - Self::LocalPath { plist, .. } => plist, + Self::App { plist, .. } => plist.bundle_short_version_string.clone(), + Self::LocalPath { .. } => "".to_string(), } } diff --git a/crates/collab/Cargo.toml b/crates/collab/Cargo.toml index eb65e992a1..0f14afae97 100644 --- a/crates/collab/Cargo.toml +++ b/crates/collab/Cargo.toml @@ -100,13 +100,15 @@ extension.workspace = true file_finder.workspace = true fs = { workspace = true, features = ["test-support"] } git = { workspace = true, features = ["test-support"] } -git_ui = { workspace = true, features = ["test-support"] } git_hosting_providers.workspace = true +git_ui = { workspace = true, features = ["test-support"] } gpui = { workspace = true, features = ["test-support"] } +gpui_tokio.workspace = true hyper.workspace = true indoc.workspace = true language = { workspace = true, features = ["test-support"] } language_model = { workspace = true, features = ["test-support"] } +livekit_client = { workspace = true, features = ["test-support"] } lsp = { workspace = true, features = ["test-support"] } menu.workspace = true multi_buffer = { workspace = true, features = ["test-support"] } @@ -131,11 +133,5 @@ util.workspace = true workspace = { workspace = true, features = ["test-support"] } worktree = { workspace = true, features = ["test-support"] } -[target.'cfg(target_os = "macos")'.dev-dependencies] -livekit_client_macos = { workspace = true, features = ["test-support"] } - -[target.'cfg(not(target_os = "macos"))'.dev-dependencies] -livekit_client = { workspace = true, features = ["test-support"] } - [package.metadata.cargo-machete] ignored = ["async-stripe"] diff --git a/crates/collab/src/tests/channel_tests.rs b/crates/collab/src/tests/channel_tests.rs index 74b3f79d64..43f616dc57 100644 --- a/crates/collab/src/tests/channel_tests.rs +++ b/crates/collab/src/tests/channel_tests.rs @@ -387,7 +387,7 @@ async fn test_channel_room( executor.run_until_parked(); let room_a = cx_a.read(|cx| active_call_a.read_with(cx, |call, _| call.room().unwrap().clone())); - cx_a.read(|cx| room_a.read_with(cx, |room, _| assert!(room.is_connected()))); + cx_a.read(|cx| room_a.read_with(cx, |room, cx| assert!(room.is_connected(cx)))); cx_a.read(|cx| { client_a.channel_store().read_with(cx, |channels, _| { @@ -461,7 +461,7 @@ async fn test_channel_room( let room_a = cx_a.read(|cx| active_call_a.read_with(cx, |call, _| call.room().unwrap().clone())); - cx_a.read(|cx| room_a.read_with(cx, |room, _| assert!(room.is_connected()))); + cx_a.read(|cx| room_a.read_with(cx, |room, cx| assert!(room.is_connected(cx)))); assert_eq!( room_participants(&room_a, cx_a), RoomParticipants { @@ -472,7 +472,7 @@ async fn test_channel_room( let room_b = cx_b.read(|cx| active_call_b.read_with(cx, |call, _| call.room().unwrap().clone())); - cx_b.read(|cx| room_b.read_with(cx, |room, _| assert!(room.is_connected()))); + cx_b.read(|cx| room_b.read_with(cx, |room, cx| assert!(room.is_connected(cx)))); assert_eq!( room_participants(&room_b, cx_b), RoomParticipants { @@ -556,7 +556,7 @@ async fn test_channel_room( let room_a = cx_a.read(|cx| active_call_a.read_with(cx, |call, _| call.room().unwrap().clone())); - cx_a.read(|cx| room_a.read_with(cx, |room, _| assert!(room.is_connected()))); + cx_a.read(|cx| room_a.read_with(cx, |room, cx| assert!(room.is_connected(cx)))); assert_eq!( room_participants(&room_a, cx_a), RoomParticipants { @@ -567,7 +567,7 @@ async fn test_channel_room( let room_b = cx_b.read(|cx| active_call_b.read_with(cx, |call, _| call.room().unwrap().clone())); - cx_b.read(|cx| room_b.read_with(cx, |room, _| assert!(room.is_connected()))); + cx_b.read(|cx| room_b.read_with(cx, |room, cx| assert!(room.is_connected(cx)))); assert_eq!( room_participants(&room_b, cx_b), RoomParticipants { diff --git a/crates/collab/src/tests/following_tests.rs b/crates/collab/src/tests/following_tests.rs index a9ee27bff1..bc372d16fe 100644 --- a/crates/collab/src/tests/following_tests.rs +++ b/crates/collab/src/tests/following_tests.rs @@ -436,9 +436,6 @@ async fn test_basic_following( editor_a1.item_id() ); - // TODO: Re-enable this test once we can replace our swift Livekit SDK with the rust SDK - // todo(windows) - // Fix this on Windows #[cfg(all(not(target_os = "macos"), not(target_os = "windows")))] { use crate::rpc::RECONNECT_TIMEOUT; @@ -463,8 +460,9 @@ async fn test_basic_following( .update(cx, |room, cx| room.share_screen(cx)) }) .await - .unwrap(); // This is what breaks + .unwrap(); executor.run_until_parked(); + let shared_screen = workspace_a.update(cx_a, |workspace, cx| { workspace .active_item(cx) diff --git a/crates/collab/src/tests/integration_tests.rs b/crates/collab/src/tests/integration_tests.rs index ee4c7cb3ec..c21b1a8dca 100644 --- a/crates/collab/src/tests/integration_tests.rs +++ b/crates/collab/src/tests/integration_tests.rs @@ -244,60 +244,56 @@ async fn test_basic_calls( } ); - // TODO: Re-enable this test once we can replace our swift Livekit SDK with the rust SDK - #[cfg(not(target_os = "macos"))] - { - // User A shares their screen - let display = gpui::TestScreenCaptureSource::new(); - let events_b = active_call_events(cx_b); - let events_c = active_call_events(cx_c); - cx_a.set_screen_capture_sources(vec![display]); - active_call_a - .update(cx_a, |call, cx| { - call.room() - .unwrap() - .update(cx, |room, cx| room.share_screen(cx)) - }) - .await - .unwrap(); + // User A shares their screen + let display = gpui::TestScreenCaptureSource::new(); + let events_b = active_call_events(cx_b); + let events_c = active_call_events(cx_c); + cx_a.set_screen_capture_sources(vec![display]); + active_call_a + .update(cx_a, |call, cx| { + call.room() + .unwrap() + .update(cx, |room, cx| room.share_screen(cx)) + }) + .await + .unwrap(); - executor.run_until_parked(); + executor.run_until_parked(); - // User B observes the remote screen sharing track. - assert_eq!(events_b.borrow().len(), 1); - let event_b = events_b.borrow().first().unwrap().clone(); - if let call::room::Event::RemoteVideoTracksChanged { participant_id } = event_b { - assert_eq!(participant_id, client_a.peer_id().unwrap()); + // User B observes the remote screen sharing track. + assert_eq!(events_b.borrow().len(), 1); + let event_b = events_b.borrow().first().unwrap().clone(); + if let call::room::Event::RemoteVideoTracksChanged { participant_id } = event_b { + assert_eq!(participant_id, client_a.peer_id().unwrap()); - room_b.read_with(cx_b, |room, _| { - assert_eq!( - room.remote_participants()[&client_a.user_id().unwrap()] - .video_tracks - .len(), - 1 - ); - }); - } else { - panic!("unexpected event") - } + room_b.read_with(cx_b, |room, _| { + assert_eq!( + room.remote_participants()[&client_a.user_id().unwrap()] + .video_tracks + .len(), + 1 + ); + }); + } else { + panic!("unexpected event") + } - // User C observes the remote screen sharing track. - assert_eq!(events_c.borrow().len(), 1); - let event_c = events_c.borrow().first().unwrap().clone(); - if let call::room::Event::RemoteVideoTracksChanged { participant_id } = event_c { - assert_eq!(participant_id, client_a.peer_id().unwrap()); + // User C observes the remote screen sharing track. + assert_eq!(events_c.borrow().len(), 1); + let event_c = events_c.borrow().first().unwrap().clone(); + if let call::room::Event::RemoteVideoTracksChanged { participant_id } = event_c { + assert_eq!(participant_id, client_a.peer_id().unwrap()); - room_c.read_with(cx_c, |room, _| { - assert_eq!( - room.remote_participants()[&client_a.user_id().unwrap()] - .video_tracks - .len(), - 1 - ); - }); - } else { - panic!("unexpected event") - } + room_c.read_with(cx_c, |room, _| { + assert_eq!( + room.remote_participants()[&client_a.user_id().unwrap()] + .video_tracks + .len(), + 1 + ); + }); + } else { + panic!("unexpected event") } // User A leaves the room. @@ -2091,17 +2087,7 @@ async fn test_mute_deafen( audio_tracks_playing: participant .audio_tracks .values() - .map({ - #[cfg(target_os = "macos")] - { - |track| track.is_playing() - } - - #[cfg(not(target_os = "macos"))] - { - |(track, _)| track.rtc_track().enabled() - } - }) + .map(|(track, _)| track.enabled()) .collect(), }) .collect::>() @@ -6238,8 +6224,6 @@ async fn test_contact_requests( } } -// TODO: Re-enable this test once we can replace our swift Livekit SDK with the rust SDK -#[cfg(not(target_os = "macos"))] #[gpui::test(iterations = 10)] async fn test_join_call_after_screen_was_shared( executor: BackgroundExecutor, diff --git a/crates/collab/src/tests/test_server.rs b/crates/collab/src/tests/test_server.rs index 1653782dd9..f139beaa09 100644 --- a/crates/collab/src/tests/test_server.rs +++ b/crates/collab/src/tests/test_server.rs @@ -47,12 +47,8 @@ use std::{ use util::path; use workspace::{Workspace, WorkspaceStore}; -#[cfg(not(target_os = "macos"))] use livekit_client::test::TestServer as LivekitTestServer; -#[cfg(target_os = "macos")] -use livekit_client_macos::TestServer as LivekitTestServer; - pub struct TestServer { pub app_state: Arc, pub test_livekit_server: Arc, @@ -167,6 +163,7 @@ impl TestServer { let fs = FakeFs::new(cx.executor()); cx.update(|cx| { + gpui_tokio::init(cx); if cx.has_global::() { panic!("Same cx used to create two test clients") } diff --git a/crates/evals/build.rs b/crates/evals/build.rs index 8175d90494..8c93f4fb27 100644 --- a/crates/evals/build.rs +++ b/crates/evals/build.rs @@ -1,14 +1,5 @@ fn main() { if cfg!(target_os = "macos") { println!("cargo:rustc-env=MACOSX_DEPLOYMENT_TARGET=10.15.7"); - - println!("cargo:rerun-if-env-changed=ZED_BUNDLE"); - if std::env::var("ZED_BUNDLE").ok().as_deref() == Some("true") { - // Find WebRTC.framework in the Frameworks folder when running as part of an application bundle. - println!("cargo:rustc-link-arg=-Wl,-rpath,@executable_path/../Frameworks"); - } else { - // Find WebRTC.framework as a sibling of the executable when running outside of an application bundle. - println!("cargo:rustc-link-arg=-Wl,-rpath,@executable_path"); - } } } diff --git a/crates/gpui/Cargo.toml b/crates/gpui/Cargo.toml index d3201d61d2..c1de8d9945 100644 --- a/crates/gpui/Cargo.toml +++ b/crates/gpui/Cargo.toml @@ -12,7 +12,7 @@ license = "Apache-2.0" workspace = true [features] -default = ["http_client", "font-kit", "wayland", "x11"] +default = ["macos-blade", "http_client", "font-kit", "wayland", "x11"] test-support = [ "leak-detection", "collections/test-support", @@ -123,10 +123,11 @@ lyon = "1.0" block = "0.1" cocoa.workspace = true core-foundation.workspace = true -core-foundation-sys = "0.8" -core-graphics = "0.23" -core-text = "20.1" -font-kit = { git = "https://github.com/zed-industries/font-kit", rev = "40391b7", optional = true } +core-foundation-sys.workspace = true +core-graphics = "0.24" +core-video.workspace = true +core-text = "21" +font-kit = { git = "https://github.com/zed-industries/font-kit", rev = "5474cfad4b719a72ec8ed2cb7327b2b01fd10568", optional = true } foreign-types = "0.5" log.workspace = true media.workspace = true @@ -154,9 +155,10 @@ blade-macros = { workspace = true, optional = true } blade-util = { workspace = true, optional = true } bytemuck = { version = "1", optional = true } cosmic-text = { version = "0.13.2", optional = true } -font-kit = { git = "https://github.com/zed-industries/font-kit", rev = "40391b7", features = [ +font-kit = { git = "https://github.com/zed-industries/font-kit", rev = "5474cfad4b719a72ec8ed2cb7327b2b01fd10568", features = [ "source-fontconfig-dlopen", ], optional = true } + calloop = { version = "0.13.0" } filedescriptor = { version = "0.8.2", optional = true } open = { version = "5.2.0", optional = true } diff --git a/crates/gpui/src/elements/surface.rs b/crates/gpui/src/elements/surface.rs index 2d25de2c93..707271f2ec 100644 --- a/crates/gpui/src/elements/surface.rs +++ b/crates/gpui/src/elements/surface.rs @@ -3,7 +3,7 @@ use crate::{ Style, StyleRefinement, Styled, Window, }; #[cfg(target_os = "macos")] -use media::core_video::CVImageBuffer; +use core_video::pixel_buffer::CVPixelBuffer; use refineable::Refineable; /// A source of a surface's content. @@ -11,12 +11,12 @@ use refineable::Refineable; pub enum SurfaceSource { /// A macOS image buffer from CoreVideo #[cfg(target_os = "macos")] - Surface(CVImageBuffer), + Surface(CVPixelBuffer), } #[cfg(target_os = "macos")] -impl From for SurfaceSource { - fn from(value: CVImageBuffer) -> Self { +impl From for SurfaceSource { + fn from(value: CVPixelBuffer) -> Self { SurfaceSource::Surface(value) } } @@ -87,7 +87,7 @@ impl Element for Surface { match &self.source { #[cfg(target_os = "macos")] SurfaceSource::Surface(surface) => { - let size = crate::size(surface.width().into(), surface.height().into()); + let size = crate::size(surface.get_width().into(), surface.get_height().into()); let new_bounds = self.object_fit.get_bounds(bounds, size); // TODO: Add support for corner_radii window.paint_surface(new_bounds, surface.clone()); diff --git a/crates/gpui/src/platform/blade/blade_renderer.rs b/crates/gpui/src/platform/blade/blade_renderer.rs index 63b1b6d65e..f8129449b5 100644 --- a/crates/gpui/src/platform/blade/blade_renderer.rs +++ b/crates/gpui/src/platform/blade/blade_renderer.rs @@ -725,8 +725,8 @@ impl BladeRenderer { use std::ptr; assert_eq!( - surface.image_buffer.pixel_format_type(), - media::core_video::kCVPixelFormatType_420YpCbCr8BiPlanarFullRange + surface.image_buffer.get_pixel_format(), + core_video::pixel_buffer::kCVPixelFormatType_420YpCbCr8BiPlanarFullRange ); let y_texture = self @@ -735,8 +735,8 @@ impl BladeRenderer { surface.image_buffer.as_concrete_TypeRef(), ptr::null(), metal::MTLPixelFormat::R8Unorm, - surface.image_buffer.plane_width(0), - surface.image_buffer.plane_height(0), + surface.image_buffer.get_width_of_plane(0), + surface.image_buffer.get_height_of_plane(0), 0, ) .unwrap(); @@ -746,8 +746,8 @@ impl BladeRenderer { surface.image_buffer.as_concrete_TypeRef(), ptr::null(), metal::MTLPixelFormat::RG8Unorm, - surface.image_buffer.plane_width(1), - surface.image_buffer.plane_height(1), + surface.image_buffer.get_width_of_plane(1), + surface.image_buffer.get_height_of_plane(1), 1, ) .unwrap(); diff --git a/crates/gpui/src/platform/mac.rs b/crates/gpui/src/platform/mac.rs index bd3d8f35ac..145e6c9edf 100644 --- a/crates/gpui/src/platform/mac.rs +++ b/crates/gpui/src/platform/mac.rs @@ -11,7 +11,7 @@ mod metal_atlas; #[cfg(not(feature = "macos-blade"))] pub mod metal_renderer; -use media::core_video::CVImageBuffer; +use core_video::image_buffer::CVImageBuffer; #[cfg(not(feature = "macos-blade"))] use metal_renderer as renderer; diff --git a/crates/gpui/src/platform/mac/metal_renderer.rs b/crates/gpui/src/platform/mac/metal_renderer.rs index 56109d2ff6..389f97fd99 100644 --- a/crates/gpui/src/platform/mac/metal_renderer.rs +++ b/crates/gpui/src/platform/mac/metal_renderer.rs @@ -13,8 +13,11 @@ use cocoa::{ }; use collections::HashMap; use core_foundation::base::TCFType; -use foreign_types::ForeignType; -use media::core_video::CVMetalTextureCache; +use core_video::{ + metal_texture::CVMetalTextureGetTexture, metal_texture_cache::CVMetalTextureCache, + pixel_buffer::kCVPixelFormatType_420YpCbCr8BiPlanarFullRange, +}; +use foreign_types::{ForeignType, ForeignTypeRef}; use metal::{CAMetalLayer, CommandQueue, MTLPixelFormat, MTLResourceOptions, NSRange}; use objc::{self, msg_send, sel, sel_impl}; use parking_lot::Mutex; @@ -107,7 +110,7 @@ pub(crate) struct MetalRenderer { #[allow(clippy::arc_with_non_send_sync)] instance_buffer_pool: Arc>, sprite_atlas: Arc, - core_video_texture_cache: CVMetalTextureCache, + core_video_texture_cache: core_video::metal_texture_cache::CVMetalTextureCache, } impl MetalRenderer { @@ -235,7 +238,7 @@ impl MetalRenderer { let command_queue = device.new_command_queue(); let sprite_atlas = Arc::new(MetalAtlas::new(device.clone(), PATH_SAMPLE_COUNT)); let core_video_texture_cache = - unsafe { CVMetalTextureCache::new(device.as_ptr()).unwrap() }; + CVMetalTextureCache::new(None, device.clone(), None).unwrap(); Self { device, @@ -1054,39 +1057,37 @@ impl MetalRenderer { for surface in surfaces { let texture_size = size( - DevicePixels::from(surface.image_buffer.width() as i32), - DevicePixels::from(surface.image_buffer.height() as i32), + DevicePixels::from(surface.image_buffer.get_width() as i32), + DevicePixels::from(surface.image_buffer.get_height() as i32), ); assert_eq!( - surface.image_buffer.pixel_format_type(), - media::core_video::kCVPixelFormatType_420YpCbCr8BiPlanarFullRange + surface.image_buffer.get_pixel_format(), + kCVPixelFormatType_420YpCbCr8BiPlanarFullRange ); - let y_texture = unsafe { - self.core_video_texture_cache - .create_texture_from_image( - surface.image_buffer.as_concrete_TypeRef(), - ptr::null(), - MTLPixelFormat::R8Unorm, - surface.image_buffer.plane_width(0), - surface.image_buffer.plane_height(0), - 0, - ) - .unwrap() - }; - let cb_cr_texture = unsafe { - self.core_video_texture_cache - .create_texture_from_image( - surface.image_buffer.as_concrete_TypeRef(), - ptr::null(), - MTLPixelFormat::RG8Unorm, - surface.image_buffer.plane_width(1), - surface.image_buffer.plane_height(1), - 1, - ) - .unwrap() - }; + let y_texture = self + .core_video_texture_cache + .create_texture_from_image( + surface.image_buffer.as_concrete_TypeRef(), + None, + MTLPixelFormat::R8Unorm, + surface.image_buffer.get_width_of_plane(0), + surface.image_buffer.get_height_of_plane(0), + 0, + ) + .unwrap(); + let cb_cr_texture = self + .core_video_texture_cache + .create_texture_from_image( + surface.image_buffer.as_concrete_TypeRef(), + None, + MTLPixelFormat::RG8Unorm, + surface.image_buffer.get_width_of_plane(1), + surface.image_buffer.get_height_of_plane(1), + 1, + ) + .unwrap(); align_offset(instance_offset); let next_offset = *instance_offset + mem::size_of::(); @@ -1104,14 +1105,15 @@ impl MetalRenderer { mem::size_of_val(&texture_size) as u64, &texture_size as *const Size as *const _, ); - command_encoder.set_fragment_texture( - SurfaceInputIndex::YTexture as u64, - Some(y_texture.as_texture_ref()), - ); - command_encoder.set_fragment_texture( - SurfaceInputIndex::CbCrTexture as u64, - Some(cb_cr_texture.as_texture_ref()), - ); + // let y_texture = y_texture.get_texture().unwrap(). + command_encoder.set_fragment_texture(SurfaceInputIndex::YTexture as u64, unsafe { + let texture = CVMetalTextureGetTexture(y_texture.as_concrete_TypeRef()); + Some(metal::TextureRef::from_ptr(texture as *mut _)) + }); + command_encoder.set_fragment_texture(SurfaceInputIndex::CbCrTexture as u64, unsafe { + let texture = CVMetalTextureGetTexture(cb_cr_texture.as_concrete_TypeRef()); + Some(metal::TextureRef::from_ptr(texture as *mut _)) + }); unsafe { let buffer_contents = (instance_buffer.metal_buffer.contents() as *mut u8) diff --git a/crates/gpui/src/platform/mac/screen_capture.rs b/crates/gpui/src/platform/mac/screen_capture.rs index a2b535996f..eaf58ae972 100644 --- a/crates/gpui/src/platform/mac/screen_capture.rs +++ b/crates/gpui/src/platform/mac/screen_capture.rs @@ -9,6 +9,10 @@ use cocoa::{ foundation::NSArray, }; use core_foundation::base::TCFType; +use core_graphics::display::{ + CGDirectDisplayID, CGDisplayCopyDisplayMode, CGDisplayModeGetPixelHeight, + CGDisplayModeGetPixelWidth, CGDisplayModeRelease, +}; use ctor::ctor; use futures::channel::oneshot; use media::core_media::{CMSampleBuffer, CMSampleBufferRef}; @@ -45,8 +49,12 @@ const SCStreamOutputTypeScreen: NSInteger = 0; impl ScreenCaptureSource for MacScreenCaptureSource { fn resolution(&self) -> Result> { unsafe { - let width: i64 = msg_send![self.sc_display, width]; - let height: i64 = msg_send![self.sc_display, height]; + let display_id: CGDirectDisplayID = msg_send![self.sc_display, displayID]; + let display_mode_ref = CGDisplayCopyDisplayMode(display_id); + let width = CGDisplayModeGetPixelWidth(display_mode_ref); + let height = CGDisplayModeGetPixelHeight(display_mode_ref); + CGDisplayModeRelease(display_mode_ref); + Ok(size(px(width as f32), px(height as f32))) } } @@ -65,6 +73,10 @@ impl ScreenCaptureSource for MacScreenCaptureSource { let excluded_windows = NSArray::array(nil); let filter: id = msg_send![filter, initWithDisplay:self.sc_display excludingWindows:excluded_windows]; let configuration: id = msg_send![configuration, init]; + let _: id = msg_send![configuration, setScalesToFit: true]; + let _: id = msg_send![configuration, setPixelFormat: 0x42475241]; + // let _: id = msg_send![configuration, setShowsCursor: false]; + // let _: id = msg_send![configuration, setCaptureResolution: 3]; let delegate: id = msg_send![delegate, init]; let output: id = msg_send![output, init]; @@ -73,6 +85,9 @@ impl ScreenCaptureSource for MacScreenCaptureSource { Box::into_raw(Box::new(frame_callback)) as *mut c_void, ); + let resolution = self.resolution().unwrap(); + let _: id = msg_send![configuration, setWidth: resolution.width.0 as i64]; + let _: id = msg_send![configuration, setHeight: resolution.height.0 as i64]; let stream: id = msg_send![stream, initWithFilter:filter configuration:configuration delegate:delegate]; let (mut tx, rx) = oneshot::channel(); diff --git a/crates/gpui/src/scene.rs b/crates/gpui/src/scene.rs index 03862bc149..6271d6e88b 100644 --- a/crates/gpui/src/scene.rs +++ b/crates/gpui/src/scene.rs @@ -662,7 +662,7 @@ pub(crate) struct PaintSurface { pub bounds: Bounds, pub content_mask: ContentMask, #[cfg(target_os = "macos")] - pub image_buffer: media::core_video::CVImageBuffer, + pub image_buffer: core_video::pixel_buffer::CVPixelBuffer, } impl From for Primitive { diff --git a/crates/gpui/src/window.rs b/crates/gpui/src/window.rs index 4f2d9fb01e..4c9e968237 100644 --- a/crates/gpui/src/window.rs +++ b/crates/gpui/src/window.rs @@ -17,11 +17,11 @@ use crate::{ }; use anyhow::{anyhow, Context as _, Result}; use collections::{FxHashMap, FxHashSet}; +#[cfg(target_os = "macos")] +use core_video::pixel_buffer::CVPixelBuffer; use derive_more::{Deref, DerefMut}; use futures::channel::oneshot; use futures::FutureExt; -#[cfg(target_os = "macos")] -use media::core_video::CVImageBuffer; use parking_lot::RwLock; use raw_window_handle::{HandleError, HasWindowHandle}; use refineable::Refineable; @@ -2658,7 +2658,7 @@ impl Window { /// /// This method should only be called as part of the paint phase of element drawing. #[cfg(target_os = "macos")] - pub fn paint_surface(&mut self, bounds: Bounds, image_buffer: CVImageBuffer) { + pub fn paint_surface(&mut self, bounds: Bounds, image_buffer: CVPixelBuffer) { use crate::PaintSurface; self.invalidator.debug_assert_paint(); diff --git a/crates/gpui_tokio/src/gpui_tokio.rs b/crates/gpui_tokio/src/gpui_tokio.rs index d6fc4868b7..fffe18a616 100644 --- a/crates/gpui_tokio/src/gpui_tokio.rs +++ b/crates/gpui_tokio/src/gpui_tokio.rs @@ -32,7 +32,7 @@ pub struct Tokio {} impl Tokio { /// Spawns the given future on Tokio's thread pool, and returns it via a GPUI task /// Note that the Tokio task will be cancelled if the GPUI task is dropped - pub fn spawn(cx: &mut C, f: Fut) -> C::Result>> + pub fn spawn(cx: &C, f: Fut) -> C::Result>> where C: AppContext, Fut: Future + Send + 'static, @@ -52,7 +52,7 @@ impl Tokio { }) } - pub fn handle(cx: &mut App) -> tokio::runtime::Handle { + pub fn handle(cx: &App) -> tokio::runtime::Handle { GlobalTokio::global(cx).runtime.handle().clone() } } diff --git a/crates/livekit_client/Cargo.toml b/crates/livekit_client/Cargo.toml index 8d492984ba..674f6900fa 100644 --- a/crates/livekit_client/Cargo.toml +++ b/crates/livekit_client/Cargo.toml @@ -10,46 +10,47 @@ license = "GPL-3.0-or-later" workspace = true [lib] -path = "src/livekit_client.rs" +path = "src/lib.rs" doctest = false [[example]] name = "test_app" [features] -no-webrtc = [] -test-support = ["collections/test-support", "gpui/test-support", "nanoid"] +test-support = ["collections/test-support", "gpui/test-support"] [dependencies] +gpui_tokio.workspace = true anyhow.workspace = true async-trait.workspace = true collections.workspace = true cpal = "0.15" futures.workspace = true gpui.workspace = true -http_2 = { package = "http", version = "0.2.1" } livekit_api.workspace = true log.workspace = true -media.workspace = true -nanoid = { workspace = true, optional = true } +nanoid.workspace = true parking_lot.workspace = true postage.workspace = true util.workspace = true -http_client.workspace = true smallvec.workspace = true image.workspace = true +tokio-tungstenite.workspace = true +http_client_tls.workspace = true [target.'cfg(not(all(target_os = "windows", target_env = "gnu")))'.dependencies] -livekit.workspace = true +livekit = { rev = "102ebbb1ccfbdbcb7332d86dc30b1b1c8c01e4f8", git = "https://github.com/zed-industries/livekit-rust-sdks", features = ["__rustls-tls"]} +libwebrtc = { rev = "102ebbb1ccfbdbcb7332d86dc30b1b1c8c01e4f8", git = "https://github.com/zed-industries/livekit-rust-sdks"} [target.'cfg(target_os = "macos")'.dependencies] core-foundation.workspace = true coreaudio-rs = "0.12.1" +objc = "0.2" +core-video.workspace = true [dev-dependencies] collections = { workspace = true, features = ["test-support"] } gpui = { workspace = true, features = ["test-support"] } -nanoid.workspace = true sha2.workspace = true simplelog.workspace = true diff --git a/crates/livekit_client/examples/test_app.rs b/crates/livekit_client/examples/test_app.rs index ff680cad19..c04d442592 100644 --- a/crates/livekit_client/examples/test_app.rs +++ b/crates/livekit_client/examples/test_app.rs @@ -1,8 +1,6 @@ -#![cfg_attr(windows, allow(unused))] -// TODO: For some reason mac build complains about import of postage::stream::Stream, but removal of -// it causes compile errors. -#![cfg_attr(target_os = "macos", allow(unused_imports))] +use std::sync::Arc; +use futures::StreamExt; use gpui::{ actions, bounds, div, point, prelude::{FluentBuilder as _, IntoElement}, @@ -11,26 +9,9 @@ use gpui::{ StatefulInteractiveElement as _, Styled, Task, Window, WindowBounds, WindowHandle, WindowOptions, }; -#[cfg(not(target_os = "windows"))] use livekit_client::{ - capture_local_audio_track, capture_local_video_track, - id::ParticipantIdentity, - options::{TrackPublishOptions, VideoCodec}, - participant::{Participant, RemoteParticipant}, - play_remote_audio_track, - publication::{LocalTrackPublication, RemoteTrackPublication}, - track::{LocalTrack, RemoteTrack, RemoteVideoTrack, TrackSource}, - AudioStream, RemoteVideoTrackView, Room, RoomEvent, RoomOptions, -}; -#[cfg(not(target_os = "windows"))] -use postage::stream::Stream; - -#[cfg(target_os = "windows")] -use livekit_client::{ - participant::{Participant, RemoteParticipant}, - publication::{LocalTrackPublication, RemoteTrackPublication}, - track::{LocalTrack, RemoteTrack, RemoteVideoTrack}, - AudioStream, RemoteVideoTrackView, Room, RoomEvent, + AudioStream, LocalTrackPublication, Participant, ParticipantIdentity, RemoteParticipant, + RemoteTrackPublication, RemoteVideoTrack, RemoteVideoTrackView, Room, RoomEvent, }; use livekit_api::token::{self, VideoGrant}; @@ -39,25 +20,18 @@ use simplelog::SimpleLogger; actions!(livekit_client, [Quit]); -#[cfg(windows)] -fn main() {} - -#[cfg(not(windows))] fn main() { SimpleLogger::init(LevelFilter::Info, Default::default()).expect("could not initialize logger"); gpui::Application::new().run(|cx| { - livekit_client::init( - cx.background_executor().dispatcher.clone(), - cx.http_client(), - ); - #[cfg(any(test, feature = "test-support"))] println!("USING TEST LIVEKIT"); #[cfg(not(any(test, feature = "test-support")))] println!("USING REAL LIVEKIT"); + gpui_tokio::init(cx); + cx.activate(true); cx.on_action(quit); cx.bind_keys([KeyBinding::new("cmd-q", Quit, None)]); @@ -83,14 +57,12 @@ fn main() { &livekit_key, &livekit_secret, Some(&format!("test-participant-{i}")), - VideoGrant::to_join("test-room"), + VideoGrant::to_join("wtej-trty"), ) .unwrap(); let bounds = bounds(point(width * i, px(0.0)), size(width, height)); - let window = - LivekitWindow::new(livekit_url.as_str(), token.as_str(), bounds, cx.clone()) - .await; + let window = LivekitWindow::new(livekit_url.clone(), token, bounds, cx).await; windows.push(window); } }) @@ -103,12 +75,11 @@ fn quit(_: &Quit, cx: &mut gpui::App) { } struct LivekitWindow { - room: Room, + room: Arc, microphone_track: Option, screen_share_track: Option, - microphone_stream: Option, + microphone_stream: Option, screen_share_stream: Option>, - #[cfg(not(target_os = "windows"))] remote_participants: Vec<(ParticipantIdentity, ParticipantState)>, _events_task: Task<()>, } @@ -121,17 +92,23 @@ struct ParticipantState { speaking: bool, } -#[cfg(not(windows))] impl LivekitWindow { async fn new( - url: &str, - token: &str, + url: String, + token: String, bounds: Bounds, - cx: AsyncApp, + cx: &mut AsyncApp, ) -> WindowHandle { - let (room, mut events) = Room::connect(url, token, RoomOptions::default()) - .await - .unwrap(); + let (room, mut events) = + Room::connect(url.clone(), token, cx) + .await + .unwrap_or_else(|err| { + eprintln!( + "Failed to connect to {url}: {err}.\nTry `foreman start` to run the livekit server" + ); + + std::process::exit(1) + }); cx.update(|cx| { cx.open_window( @@ -142,7 +119,7 @@ impl LivekitWindow { |window, cx| { cx.new(|cx| { let _events_task = cx.spawn_in(window, async move |this, cx| { - while let Some(event) = events.recv().await { + while let Some(event) = events.next().await { cx.update(|window, cx| { this.update(cx, |this: &mut LivekitWindow, cx| { this.handle_room_event(event, window, cx) @@ -153,7 +130,7 @@ impl LivekitWindow { }); Self { - room, + room: Arc::new(room), microphone_track: None, microphone_stream: None, screen_share_track: None, @@ -201,15 +178,16 @@ impl LivekitWindow { participant, track, } => { + let room = self.room.clone(); let output = self.remote_participant(participant); match track { - RemoteTrack::Audio(track) => { + livekit_client::RemoteTrack::Audio(track) => { output.audio_output_stream = Some(( publication.clone(), - play_remote_audio_track(&track, cx.background_executor()).unwrap(), + room.play_remote_audio_track(&track, cx).unwrap(), )); } - RemoteTrack::Video(track) => { + livekit_client::RemoteTrack::Video(track) => { output.screen_share_output_view = Some(( track.clone(), cx.new(|cx| RemoteVideoTrackView::new(track, window, cx)), @@ -269,25 +247,15 @@ impl LivekitWindow { fn toggle_mute(&mut self, window: &mut Window, cx: &mut Context) { if let Some(track) = &self.microphone_track { if track.is_muted() { - track.unmute(); + track.unmute(cx); } else { - track.mute(); + track.mute(cx); } cx.notify(); } else { - let participant = self.room.local_participant(); + let room = self.room.clone(); cx.spawn_in(window, async move |this, cx| { - let (track, stream) = capture_local_audio_track(cx.background_executor())?.await; - let publication = participant - .publish_track( - LocalTrack::Audio(track), - TrackPublishOptions { - source: TrackSource::Microphone, - ..Default::default() - }, - ) - .await - .unwrap(); + let (publication, stream) = room.publish_local_microphone_track(cx).await.unwrap(); this.update(cx, |this, cx| { this.microphone_track = Some(publication); this.microphone_stream = Some(stream); @@ -302,8 +270,8 @@ impl LivekitWindow { if let Some(track) = self.screen_share_track.take() { self.screen_share_stream.take(); let participant = self.room.local_participant(); - cx.background_spawn(async move { - participant.unpublish_track(&track.sid()).await.unwrap(); + cx.spawn(async move |_, cx| { + participant.unpublish_track(track.sid(), cx).await.unwrap(); }) .detach(); cx.notify(); @@ -313,16 +281,9 @@ impl LivekitWindow { cx.spawn_in(window, async move |this, cx| { let sources = sources.await.unwrap()?; let source = sources.into_iter().next().unwrap(); - let (track, stream) = capture_local_video_track(&*source).await?; - let publication = participant - .publish_track( - LocalTrack::Video(track), - TrackPublishOptions { - source: TrackSource::Screenshare, - video_codec: VideoCodec::H264, - ..Default::default() - }, - ) + + let (publication, stream) = participant + .publish_screenshare_track(&*source, cx) .await .unwrap(); this.update(cx, |this, cx| { @@ -338,7 +299,6 @@ impl LivekitWindow { fn toggle_remote_audio_for_participant( &mut self, identity: &ParticipantIdentity, - cx: &mut Context, ) -> Option<()> { let participant = self.remote_participants.iter().find_map(|(id, state)| { @@ -349,13 +309,12 @@ impl LivekitWindow { } })?; let publication = &participant.audio_output_stream.as_ref()?.0; - publication.set_enabled(!publication.is_enabled()); + publication.set_enabled(!publication.is_enabled(), cx); cx.notify(); Some(()) } } -#[cfg(not(windows))] impl Render for LivekitWindow { fn render(&mut self, _window: &mut Window, cx: &mut Context) -> impl IntoElement { fn button() -> gpui::Div { @@ -407,7 +366,7 @@ impl Render for LivekitWindow { .flex_grow() .children(self.remote_participants.iter().map(|(identity, state)| { div() - .h(px(300.0)) + .h(px(1080.0)) .flex() .flex_col() .m_2() diff --git a/crates/livekit_client/src/lib.rs b/crates/livekit_client/src/lib.rs new file mode 100644 index 0000000000..c35c83f228 --- /dev/null +++ b/crates/livekit_client/src/lib.rs @@ -0,0 +1,165 @@ +use collections::HashMap; + +mod remote_video_track_view; +pub use remote_video_track_view::{RemoteVideoTrackView, RemoteVideoTrackViewEvent}; + +#[cfg(not(any( + test, + feature = "test-support", + all(target_os = "windows", target_env = "gnu") +)))] +mod livekit_client; +#[cfg(not(any( + test, + feature = "test-support", + all(target_os = "windows", target_env = "gnu") +)))] +pub use livekit_client::*; + +#[cfg(any( + test, + feature = "test-support", + all(target_os = "windows", target_env = "gnu") +))] +mod mock_client; +#[cfg(any( + test, + feature = "test-support", + all(target_os = "windows", target_env = "gnu") +))] +pub mod test; +#[cfg(any( + test, + feature = "test-support", + all(target_os = "windows", target_env = "gnu") +))] +pub use mock_client::*; + +#[derive(Debug, Clone)] +pub enum Participant { + Local(LocalParticipant), + Remote(RemoteParticipant), +} + +#[derive(Debug, Clone)] +pub enum TrackPublication { + Local(LocalTrackPublication), + Remote(RemoteTrackPublication), +} + +impl TrackPublication { + pub fn sid(&self) -> TrackSid { + match self { + TrackPublication::Local(local) => local.sid(), + TrackPublication::Remote(remote) => remote.sid(), + } + } + + pub fn is_muted(&self) -> bool { + match self { + TrackPublication::Local(local) => local.is_muted(), + TrackPublication::Remote(remote) => remote.is_muted(), + } + } +} + +#[derive(Clone, Debug)] +pub enum RemoteTrack { + Audio(RemoteAudioTrack), + Video(RemoteVideoTrack), +} + +impl RemoteTrack { + pub fn sid(&self) -> TrackSid { + match self { + RemoteTrack::Audio(remote_audio_track) => remote_audio_track.sid(), + RemoteTrack::Video(remote_video_track) => remote_video_track.sid(), + } + } +} + +#[derive(Clone, Debug)] +pub enum LocalTrack { + Audio(LocalAudioTrack), + Video(LocalVideoTrack), +} + +#[derive(Clone, Debug)] +#[non_exhaustive] +pub enum RoomEvent { + ParticipantConnected(RemoteParticipant), + ParticipantDisconnected(RemoteParticipant), + LocalTrackPublished { + publication: LocalTrackPublication, + track: LocalTrack, + participant: LocalParticipant, + }, + LocalTrackUnpublished { + publication: LocalTrackPublication, + participant: LocalParticipant, + }, + LocalTrackSubscribed { + track: LocalTrack, + }, + TrackSubscribed { + track: RemoteTrack, + publication: RemoteTrackPublication, + participant: RemoteParticipant, + }, + TrackUnsubscribed { + track: RemoteTrack, + publication: RemoteTrackPublication, + participant: RemoteParticipant, + }, + TrackSubscriptionFailed { + participant: RemoteParticipant, + // error: livekit::track::TrackError, + track_sid: TrackSid, + }, + TrackPublished { + publication: RemoteTrackPublication, + participant: RemoteParticipant, + }, + TrackUnpublished { + publication: RemoteTrackPublication, + participant: RemoteParticipant, + }, + TrackMuted { + participant: Participant, + publication: TrackPublication, + }, + TrackUnmuted { + participant: Participant, + publication: TrackPublication, + }, + RoomMetadataChanged { + old_metadata: String, + metadata: String, + }, + ParticipantMetadataChanged { + participant: Participant, + old_metadata: String, + metadata: String, + }, + ParticipantNameChanged { + participant: Participant, + old_name: String, + name: String, + }, + ParticipantAttributesChanged { + participant: Participant, + changed_attributes: HashMap, + }, + ActiveSpeakersChanged { + speakers: Vec, + }, + ConnectionStateChanged(ConnectionState), + Connected { + participants_with_tracks: Vec<(RemoteParticipant, Vec)>, + }, + Disconnected { + reason: &'static str, + }, + Reconnecting, + Reconnected, +} diff --git a/crates/livekit_client/src/livekit_client.rs b/crates/livekit_client/src/livekit_client.rs index c47cd48fa9..ddab660678 100644 --- a/crates/livekit_client/src/livekit_client.rs +++ b/crates/livekit_client/src/livekit_client.rs @@ -1,679 +1,497 @@ -#![cfg_attr(all(target_os = "windows", target_env = "gnu"), allow(unused))] +use std::sync::Arc; -mod remote_video_track_view; -#[cfg(any( - test, - feature = "test-support", - all(target_os = "windows", target_env = "gnu") -))] -pub mod test; +use anyhow::Result; +use collections::HashMap; +use futures::{channel::mpsc, SinkExt}; +use gpui::{App, AsyncApp, ScreenCaptureSource, ScreenCaptureStream, Task}; +use gpui_tokio::Tokio; +use playback::capture_local_video_track; -use anyhow::{anyhow, Context as _, Result}; -use cpal::traits::{DeviceTrait, HostTrait, StreamTrait as _}; -use futures::{io, Stream, StreamExt as _}; -use gpui::{ - BackgroundExecutor, ScreenCaptureFrame, ScreenCaptureSource, ScreenCaptureStream, Task, -}; -use parking_lot::Mutex; -use std::{borrow::Cow, collections::VecDeque, future::Future, pin::Pin, sync::Arc, thread}; -use util::{debug_panic, ResultExt as _}; -#[cfg(not(all(target_os = "windows", target_env = "gnu")))] -use webrtc::{ - audio_frame::AudioFrame, - audio_source::{native::NativeAudioSource, AudioSourceOptions, RtcAudioSource}, - audio_stream::native::NativeAudioStream, - video_frame::{VideoBuffer, VideoFrame, VideoRotation}, - video_source::{native::NativeVideoSource, RtcVideoSource, VideoResolution}, - video_stream::native::NativeVideoStream, -}; +mod playback; -#[cfg(all( - not(any(test, feature = "test-support")), - not(all(target_os = "windows", target_env = "gnu")) -))] -use livekit::track::RemoteAudioTrack; -#[cfg(all( - not(any(test, feature = "test-support")), - not(all(target_os = "windows", target_env = "gnu")) -))] -pub use livekit::*; -#[cfg(any( - test, - feature = "test-support", - all(target_os = "windows", target_env = "gnu") -))] -use test::track::RemoteAudioTrack; -#[cfg(any( - test, - feature = "test-support", - all(target_os = "windows", target_env = "gnu") -))] -pub use test::*; +use crate::{LocalTrack, Participant, RemoteTrack, RoomEvent, TrackPublication}; +pub use playback::AudioStream; +pub(crate) use playback::{play_remote_video_track, RemoteVideoFrame}; -pub use remote_video_track_view::{RemoteVideoTrackView, RemoteVideoTrackViewEvent}; +#[derive(Clone, Debug)] +pub struct RemoteVideoTrack(livekit::track::RemoteVideoTrack); +#[derive(Clone, Debug)] +pub struct RemoteAudioTrack(livekit::track::RemoteAudioTrack); +#[derive(Clone, Debug)] +pub struct RemoteTrackPublication(livekit::publication::RemoteTrackPublication); +#[derive(Clone, Debug)] +pub struct RemoteParticipant(livekit::participant::RemoteParticipant); -pub enum AudioStream { - Input { - _thread_handle: std::sync::mpsc::Sender<()>, - _transmit_task: Task<()>, - }, - Output { - _task: Task<()>, - }, +#[derive(Clone, Debug)] +pub struct LocalVideoTrack(livekit::track::LocalVideoTrack); +#[derive(Clone, Debug)] +pub struct LocalAudioTrack(livekit::track::LocalAudioTrack); +#[derive(Clone, Debug)] +pub struct LocalTrackPublication(livekit::publication::LocalTrackPublication); +#[derive(Clone, Debug)] +pub struct LocalParticipant(livekit::participant::LocalParticipant); + +pub struct Room { + room: livekit::Room, + _task: Task<()>, + playback: playback::AudioStack, } -struct Dispatcher(Arc); +pub type TrackSid = livekit::id::TrackSid; +pub type ConnectionState = livekit::ConnectionState; +#[derive(Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)] +pub struct ParticipantIdentity(pub String); -#[cfg(not(all(target_os = "windows", target_env = "gnu")))] -impl livekit::dispatcher::Dispatcher for Dispatcher { - fn dispatch(&self, runnable: livekit::dispatcher::Runnable) { - self.0.dispatch(runnable, None); - } - - fn dispatch_after( - &self, - duration: std::time::Duration, - runnable: livekit::dispatcher::Runnable, - ) { - self.0.dispatch_after(duration, runnable); - } -} - -struct HttpClientAdapter(Arc); - -fn http_2_status(status: http_client::http::StatusCode) -> http_2::StatusCode { - http_2::StatusCode::from_u16(status.as_u16()) - .expect("valid status code to status code conversion") -} - -#[cfg(not(all(target_os = "windows", target_env = "gnu")))] -impl livekit::dispatcher::HttpClient for HttpClientAdapter { - fn get( - &self, - url: &str, - ) -> Pin> + Send>> { - let http_client = self.0.clone(); - let url = url.to_string(); - Box::pin(async move { - let response = http_client - .get(&url, http_client::AsyncBody::empty(), false) - .await - .map_err(io::Error::other)?; - Ok(livekit::dispatcher::Response { - status: http_2_status(response.status()), - body: Box::pin(response.into_body()), - }) - }) - } - - fn send_async( - &self, - request: http_2::Request>, - ) -> Pin> + Send>> { - let http_client = self.0.clone(); - let mut builder = http_client::http::Request::builder() - .method(request.method().as_str()) - .uri(request.uri().to_string()); - - for (key, value) in request.headers().iter() { - builder = builder.header(key.as_str(), value.as_bytes()); - } - - if !request.extensions().is_empty() { - debug_panic!( - "Livekit sent an HTTP request with a protocol extension that Zed doesn't support!" - ); - } - - let request = builder - .body(http_client::AsyncBody::from_bytes( - request.into_body().into(), - )) - .unwrap(); - - Box::pin(async move { - let response = http_client.send(request).await.map_err(io::Error::other)?; - Ok(livekit::dispatcher::Response { - status: http_2_status(response.status()), - body: Box::pin(response.into_body()), - }) - }) - } -} - -#[cfg(all(target_os = "windows", target_env = "gnu"))] -pub fn init( - dispatcher: Arc, - http_client: Arc, -) { -} - -#[cfg(not(all(target_os = "windows", target_env = "gnu")))] -pub fn init( - dispatcher: Arc, - http_client: Arc, -) { - livekit::dispatcher::set_dispatcher(Dispatcher(dispatcher)); - livekit::dispatcher::set_http_client(HttpClientAdapter(http_client)); -} - -#[cfg(not(all(target_os = "windows", target_env = "gnu")))] -pub async fn capture_local_video_track( - capture_source: &dyn ScreenCaptureSource, -) -> Result<(track::LocalVideoTrack, Box)> { - let resolution = capture_source.resolution()?; - let track_source = NativeVideoSource::new(VideoResolution { - width: resolution.width.0 as u32, - height: resolution.height.0 as u32, - }); - - let capture_stream = capture_source - .stream({ - let track_source = track_source.clone(); - Box::new(move |frame| { - if let Some(buffer) = video_frame_buffer_to_webrtc(frame) { - track_source.capture_frame(&VideoFrame { - rotation: VideoRotation::VideoRotation0, - timestamp_us: 0, - buffer, - }); - } - }) - }) +impl Room { + pub async fn connect( + url: String, + token: String, + cx: &mut AsyncApp, + ) -> Result<(Self, mpsc::UnboundedReceiver)> { + let connector = + tokio_tungstenite::Connector::Rustls(Arc::new(http_client_tls::tls_config())); + let mut config = livekit::RoomOptions::default(); + config.connector = Some(connector); + let (room, mut events) = Tokio::spawn(cx, async move { + livekit::Room::connect(&url, &token, config).await + })? .await??; - Ok(( - track::LocalVideoTrack::create_video_track( - "screen share", - RtcVideoSource::Native(track_source), - ), - capture_stream, - )) -} - -#[cfg(not(all(target_os = "windows", target_env = "gnu")))] -pub fn capture_local_audio_track( - background_executor: &BackgroundExecutor, -) -> Result> { - use util::maybe; - - let (frame_tx, mut frame_rx) = futures::channel::mpsc::unbounded(); - let (thread_handle, thread_kill_rx) = std::sync::mpsc::channel::<()>(); - let sample_rate; - let channels; - - if cfg!(any(test, feature = "test-support")) { - sample_rate = 2; - channels = 1; - } else { - let (device, config) = default_device(true)?; - sample_rate = config.sample_rate().0; - channels = config.channels() as u32; - thread::spawn(move || { - maybe!({ - if let Some(name) = device.name().ok() { - log::info!("Using microphone: {}", name) - } else { - log::info!("Using microphone: "); + let (mut tx, rx) = mpsc::unbounded(); + let task = cx.background_executor().spawn(async move { + while let Some(event) = events.recv().await { + if let Some(event) = room_event_from_livekit(event) { + tx.send(event.into()).await.ok(); } - - let stream = device - .build_input_stream_raw( - &config.config(), - cpal::SampleFormat::I16, - move |data, _: &_| { - frame_tx - .unbounded_send(AudioFrame { - data: Cow::Owned(data.as_slice::().unwrap().to_vec()), - sample_rate, - num_channels: channels, - samples_per_channel: data.len() as u32 / channels, - }) - .ok(); - }, - |err| log::error!("error capturing audio track: {:?}", err), - None, - ) - .context("failed to build input stream")?; - - stream.play()?; - // Keep the thread alive and holding onto the `stream` - thread_kill_rx.recv().ok(); - anyhow::Ok(Some(())) - }) - .log_err(); - }); - } - - Ok(background_executor.spawn({ - let background_executor = background_executor.clone(); - async move { - let source = NativeAudioSource::new( - AudioSourceOptions { - echo_cancellation: true, - noise_suppression: true, - auto_gain_control: true, - }, - sample_rate, - channels, - 100, - ); - let transmit_task = background_executor.spawn({ - let source = source.clone(); - async move { - while let Some(frame) = frame_rx.next().await { - source.capture_frame(&frame).await.log_err(); - } - } - }); - - let track = track::LocalAudioTrack::create_audio_track( - "microphone", - RtcAudioSource::Native(source), - ); - - ( - track, - AudioStream::Input { - _thread_handle: thread_handle, - _transmit_task: transmit_task, - }, - ) - } - })) -} - -#[cfg(not(all(target_os = "windows", target_env = "gnu")))] -pub fn play_remote_audio_track( - track: &RemoteAudioTrack, - background_executor: &BackgroundExecutor, -) -> Result { - let track = track.clone(); - // We track device changes in our output because Livekit has a resampler built in, - // and it's easy to create a new native audio stream when the device changes. - if cfg!(any(test, feature = "test-support")) { - Ok(AudioStream::Output { - _task: background_executor.spawn(async {}), - }) - } else { - let mut default_change_listener = DeviceChangeListener::new(false)?; - let (output_device, output_config) = default_device(false)?; - - let _task = background_executor.spawn({ - let background_executor = background_executor.clone(); - async move { - let (mut _receive_task, mut _thread) = - start_output_stream(output_config, output_device, &track, &background_executor); - - while let Some(_) = default_change_listener.next().await { - let Some((output_device, output_config)) = get_default_output().log_err() - else { - continue; - }; - - if let Ok(name) = output_device.name() { - log::info!("Using speaker: {}", name) - } else { - log::info!("Using speaker: ") - } - - (_receive_task, _thread) = start_output_stream( - output_config, - output_device, - &track, - &background_executor, - ); - } - - futures::future::pending::<()>().await; } }); - Ok(AudioStream::Output { _task }) - } -} - -fn default_device(input: bool) -> anyhow::Result<(cpal::Device, cpal::SupportedStreamConfig)> { - let device; - let config; - if input { - device = cpal::default_host() - .default_input_device() - .ok_or_else(|| anyhow!("no audio input device available"))?; - config = device - .default_input_config() - .context("failed to get default input config")?; - } else { - device = cpal::default_host() - .default_output_device() - .ok_or_else(|| anyhow!("no audio output device available"))?; - config = device - .default_output_config() - .context("failed to get default output config")?; - } - Ok((device, config)) -} - -#[cfg(not(all(target_os = "windows", target_env = "gnu")))] -fn get_default_output() -> anyhow::Result<(cpal::Device, cpal::SupportedStreamConfig)> { - let host = cpal::default_host(); - let output_device = host - .default_output_device() - .context("failed to read default output device")?; - let output_config = output_device.default_output_config()?; - Ok((output_device, output_config)) -} - -#[cfg(not(all(target_os = "windows", target_env = "gnu")))] -fn start_output_stream( - output_config: cpal::SupportedStreamConfig, - output_device: cpal::Device, - track: &track::RemoteAudioTrack, - background_executor: &BackgroundExecutor, -) -> (Task<()>, std::sync::mpsc::Sender<()>) { - let buffer = Arc::new(Mutex::new(VecDeque::::new())); - let sample_rate = output_config.sample_rate(); - - let mut stream = NativeAudioStream::new( - track.rtc_track(), - sample_rate.0 as i32, - output_config.channels() as i32, - ); - - let receive_task = background_executor.spawn({ - let buffer = buffer.clone(); - async move { - const MS_OF_BUFFER: u32 = 100; - const MS_IN_SEC: u32 = 1000; - while let Some(frame) = stream.next().await { - let frame_size = frame.samples_per_channel * frame.num_channels; - debug_assert!(frame.data.len() == frame_size as usize); - - let buffer_size = - ((frame.sample_rate * frame.num_channels) / MS_IN_SEC * MS_OF_BUFFER) as usize; - - let mut buffer = buffer.lock(); - let new_size = buffer.len() + frame.data.len(); - if new_size > buffer_size { - let overflow = new_size - buffer_size; - buffer.drain(0..overflow); - } - - buffer.extend(frame.data.iter()); - } - } - }); - - // The _output_stream needs to be on it's own thread because it's !Send - // and we experienced a deadlock when it's created on the main thread. - let (thread, end_on_drop_rx) = std::sync::mpsc::channel::<()>(); - thread::spawn(move || { - if cfg!(any(test, feature = "test-support")) { - // Can't play audio in tests - return; - } - - let output_stream = output_device.build_output_stream( - &output_config.config(), - { - let buffer = buffer.clone(); - move |data, _info| { - let mut buffer = buffer.lock(); - if buffer.len() < data.len() { - // Instead of partially filling a buffer, output silence. If a partial - // buffer was outputted then this could lead to a perpetual state of - // outputting partial buffers as it never gets filled enough for a full - // frame. - data.fill(0); - } else { - // SAFETY: We know that buffer has at least data.len() values in it. - // because we just checked - let mut drain = buffer.drain(..data.len()); - data.fill_with(|| unsafe { drain.next().unwrap_unchecked() }); - } - } + Ok(( + Self { + room, + _task: task, + playback: playback::AudioStack::new(cx.background_executor().clone()), }, - |error| log::error!("error playing audio track: {:?}", error), - None, - ); - - let Some(output_stream) = output_stream.log_err() else { - return; - }; - - output_stream.play().log_err(); - // Block forever to keep the output stream alive - end_on_drop_rx.recv().ok(); - }); - - (receive_task, thread) -} - -#[cfg(all(target_os = "windows", target_env = "gnu"))] -pub fn play_remote_video_track( - track: &track::RemoteVideoTrack, -) -> impl Stream { - futures::stream::empty() -} - -#[cfg(not(all(target_os = "windows", target_env = "gnu")))] -pub fn play_remote_video_track( - track: &track::RemoteVideoTrack, -) -> impl Stream { - NativeVideoStream::new(track.rtc_track()) - .filter_map(|frame| async move { video_frame_buffer_from_webrtc(frame.buffer) }) -} - -#[cfg(target_os = "macos")] -pub type RemoteVideoFrame = media::core_video::CVImageBuffer; - -#[cfg(target_os = "macos")] -fn video_frame_buffer_from_webrtc(buffer: Box) -> Option { - use core_foundation::base::TCFType as _; - use media::core_video::CVImageBuffer; - - let buffer = buffer.as_native()?; - let pixel_buffer = buffer.get_cv_pixel_buffer(); - if pixel_buffer.is_null() { - return None; + rx, + )) } - unsafe { Some(CVImageBuffer::wrap_under_get_rule(pixel_buffer as _)) } + pub fn local_participant(&self) -> LocalParticipant { + LocalParticipant(self.room.local_participant()) + } + + pub fn remote_participants(&self) -> HashMap { + self.room + .remote_participants() + .into_iter() + .map(|(k, v)| (ParticipantIdentity(k.0), RemoteParticipant(v))) + .collect() + } + + pub fn connection_state(&self) -> ConnectionState { + self.room.connection_state() + } + + pub async fn publish_local_microphone_track( + &self, + cx: &mut AsyncApp, + ) -> Result<(LocalTrackPublication, playback::AudioStream)> { + let (track, stream) = self.playback.capture_local_microphone_track()?; + let publication = self + .local_participant() + .publish_track( + livekit::track::LocalTrack::Audio(track.0), + livekit::options::TrackPublishOptions { + source: livekit::track::TrackSource::Microphone, + ..Default::default() + }, + cx, + ) + .await?; + + Ok((publication, stream)) + } + + // pub async fn publish_local_wav_track( + // &self, + // cx: &mut AsyncApp, + // ) -> Result<(LocalTrackPublication, playback::AudioStream)> { + // let apm = self.apm.clone(); + // let executor = cx.background_executor().clone(); + // let (track, stream) = + // Tokio::spawn( + // cx, + // async move { capture_local_wav_track(apm, &executor).await }, + // )? + // .await??; + // let publication = self + // .local_participant() + // .publish_track( + // livekit::track::LocalTrack::Audio(track.0), + // livekit::options::TrackPublishOptions { + // source: livekit::track::TrackSource::Microphone, + // ..Default::default() + // }, + // cx, + // ) + // .await?; + + // Ok((publication, stream)) + // } + + pub async fn unpublish_local_track( + &self, + sid: TrackSid, + cx: &mut AsyncApp, + ) -> Result { + self.local_participant().unpublish_track(sid, cx).await + } + + pub fn play_remote_audio_track( + &self, + track: &RemoteAudioTrack, + _cx: &App, + ) -> Result { + Ok(self.playback.play_remote_audio_track(&track.0)) + } } -#[cfg(not(target_os = "macos"))] -pub type RemoteVideoFrame = Arc; +impl LocalParticipant { + pub async fn publish_screenshare_track( + &self, + source: &dyn ScreenCaptureSource, + cx: &mut AsyncApp, + ) -> Result<(LocalTrackPublication, Box)> { + let (track, stream) = capture_local_video_track(&*source, cx).await?; + let options = livekit::options::TrackPublishOptions { + source: livekit::track::TrackSource::Screenshare, + video_codec: livekit::options::VideoCodec::VP8, + ..Default::default() + }; + let publication = self + .publish_track(livekit::track::LocalTrack::Video(track.0), options, cx) + .await?; -#[cfg(not(any(target_os = "macos", all(target_os = "windows", target_env = "gnu"))))] -fn video_frame_buffer_from_webrtc(buffer: Box) -> Option { - use gpui::RenderImage; - use image::{Frame, RgbaImage}; - use livekit::webrtc::prelude::VideoFormatType; - use smallvec::SmallVec; - use std::alloc::{alloc, Layout}; + Ok((publication, stream)) + } - let width = buffer.width(); - let height = buffer.height(); - let stride = width * 4; - let byte_len = (stride * height) as usize; - let argb_image = unsafe { - // Motivation for this unsafe code is to avoid initializing the frame data, since to_argb - // will write all bytes anyway. - let start_ptr = alloc(Layout::array::(byte_len).log_err()?); - if start_ptr.is_null() { + async fn publish_track( + &self, + track: livekit::track::LocalTrack, + options: livekit::options::TrackPublishOptions, + cx: &mut AsyncApp, + ) -> Result { + let participant = self.0.clone(); + Tokio::spawn(cx, async move { + participant.publish_track(track, options).await + })? + .await? + .map(|p| LocalTrackPublication(p)) + .map_err(|error| anyhow::anyhow!("failed to publish track: {error}")) + } + + pub async fn unpublish_track( + &self, + sid: TrackSid, + cx: &mut AsyncApp, + ) -> Result { + let participant = self.0.clone(); + Tokio::spawn(cx, async move { participant.unpublish_track(&sid).await })? + .await? + .map(|p| LocalTrackPublication(p)) + .map_err(|error| anyhow::anyhow!("failed to unpublish track: {error}")) + } +} + +impl LocalTrackPublication { + pub fn mute(&self, cx: &App) { + let track = self.0.clone(); + Tokio::spawn(cx, async move { + track.mute(); + }) + .detach(); + } + + pub fn unmute(&self, cx: &App) { + let track = self.0.clone(); + Tokio::spawn(cx, async move { + track.unmute(); + }) + .detach(); + } + + pub fn sid(&self) -> TrackSid { + self.0.sid() + } + + pub fn is_muted(&self) -> bool { + self.0.is_muted() + } +} + +impl RemoteParticipant { + pub fn identity(&self) -> ParticipantIdentity { + ParticipantIdentity(self.0.identity().0) + } + + pub fn track_publications(&self) -> HashMap { + self.0 + .track_publications() + .into_iter() + .map(|(sid, publication)| (sid, RemoteTrackPublication(publication))) + .collect() + } +} + +impl RemoteAudioTrack { + pub fn sid(&self) -> TrackSid { + self.0.sid() + } +} + +impl RemoteVideoTrack { + pub fn sid(&self) -> TrackSid { + self.0.sid() + } +} + +impl RemoteTrackPublication { + pub fn is_muted(&self) -> bool { + self.0.is_muted() + } + + pub fn is_enabled(&self) -> bool { + self.0.is_enabled() + } + + pub fn track(&self) -> Option { + self.0.track().map(remote_track_from_livekit) + } + + pub fn is_audio(&self) -> bool { + self.0.kind() == livekit::track::TrackKind::Audio + } + + pub fn set_enabled(&self, enabled: bool, cx: &App) { + let track = self.0.clone(); + Tokio::spawn(cx, async move { track.set_enabled(enabled) }).detach(); + } + + pub fn sid(&self) -> TrackSid { + self.0.sid() + } +} + +impl RemoteTrack { + pub fn set_enabled(&self, enabled: bool, cx: &App) { + let this = self.clone(); + Tokio::spawn(cx, async move { + match this { + RemoteTrack::Audio(remote_audio_track) => { + remote_audio_track.0.rtc_track().set_enabled(enabled) + } + RemoteTrack::Video(remote_video_track) => { + remote_video_track.0.rtc_track().set_enabled(enabled) + } + } + }) + .detach(); + } +} + +impl Participant { + pub fn identity(&self) -> ParticipantIdentity { + match self { + Participant::Local(local_participant) => { + ParticipantIdentity(local_participant.0.identity().0) + } + Participant::Remote(remote_participant) => { + ParticipantIdentity(remote_participant.0.identity().0) + } + } + } +} + +fn participant_from_livekit(participant: livekit::participant::Participant) -> Participant { + match participant { + livekit::participant::Participant::Local(local) => { + Participant::Local(LocalParticipant(local)) + } + livekit::participant::Participant::Remote(remote) => { + Participant::Remote(RemoteParticipant(remote)) + } + } +} + +fn publication_from_livekit( + publication: livekit::publication::TrackPublication, +) -> TrackPublication { + match publication { + livekit::publication::TrackPublication::Local(local) => { + TrackPublication::Local(LocalTrackPublication(local)) + } + livekit::publication::TrackPublication::Remote(remote) => { + TrackPublication::Remote(RemoteTrackPublication(remote)) + } + } +} + +fn remote_track_from_livekit(track: livekit::track::RemoteTrack) -> RemoteTrack { + match track { + livekit::track::RemoteTrack::Audio(audio) => RemoteTrack::Audio(RemoteAudioTrack(audio)), + livekit::track::RemoteTrack::Video(video) => RemoteTrack::Video(RemoteVideoTrack(video)), + } +} + +fn local_track_from_livekit(track: livekit::track::LocalTrack) -> LocalTrack { + match track { + livekit::track::LocalTrack::Audio(audio) => LocalTrack::Audio(LocalAudioTrack(audio)), + livekit::track::LocalTrack::Video(video) => LocalTrack::Video(LocalVideoTrack(video)), + } +} +fn room_event_from_livekit(event: livekit::RoomEvent) -> Option { + let event = match event { + livekit::RoomEvent::ParticipantConnected(remote_participant) => { + RoomEvent::ParticipantConnected(RemoteParticipant(remote_participant)) + } + livekit::RoomEvent::ParticipantDisconnected(remote_participant) => { + RoomEvent::ParticipantDisconnected(RemoteParticipant(remote_participant)) + } + livekit::RoomEvent::LocalTrackPublished { + publication, + track, + participant, + } => RoomEvent::LocalTrackPublished { + publication: LocalTrackPublication(publication), + track: local_track_from_livekit(track), + participant: LocalParticipant(participant), + }, + livekit::RoomEvent::LocalTrackUnpublished { + publication, + participant, + } => RoomEvent::LocalTrackUnpublished { + publication: LocalTrackPublication(publication), + participant: LocalParticipant(participant), + }, + livekit::RoomEvent::LocalTrackSubscribed { track } => RoomEvent::LocalTrackSubscribed { + track: local_track_from_livekit(track), + }, + livekit::RoomEvent::TrackSubscribed { + track, + publication, + participant, + } => RoomEvent::TrackSubscribed { + track: remote_track_from_livekit(track), + publication: RemoteTrackPublication(publication), + participant: RemoteParticipant(participant), + }, + livekit::RoomEvent::TrackUnsubscribed { + track, + publication, + participant, + } => RoomEvent::TrackUnsubscribed { + track: remote_track_from_livekit(track), + publication: RemoteTrackPublication(publication), + participant: RemoteParticipant(participant), + }, + livekit::RoomEvent::TrackSubscriptionFailed { + participant, + error: _, + track_sid, + } => RoomEvent::TrackSubscriptionFailed { + participant: RemoteParticipant(participant), + track_sid, + }, + livekit::RoomEvent::TrackPublished { + publication, + participant, + } => RoomEvent::TrackPublished { + publication: RemoteTrackPublication(publication), + participant: RemoteParticipant(participant), + }, + livekit::RoomEvent::TrackUnpublished { + publication, + participant, + } => RoomEvent::TrackUnpublished { + publication: RemoteTrackPublication(publication), + participant: RemoteParticipant(participant), + }, + livekit::RoomEvent::TrackMuted { + participant, + publication, + } => RoomEvent::TrackMuted { + publication: publication_from_livekit(publication), + participant: participant_from_livekit(participant), + }, + livekit::RoomEvent::TrackUnmuted { + participant, + publication, + } => RoomEvent::TrackUnmuted { + publication: publication_from_livekit(publication), + participant: participant_from_livekit(participant), + }, + livekit::RoomEvent::RoomMetadataChanged { + old_metadata, + metadata, + } => RoomEvent::RoomMetadataChanged { + old_metadata, + metadata, + }, + livekit::RoomEvent::ParticipantMetadataChanged { + participant, + old_metadata, + metadata, + } => RoomEvent::ParticipantMetadataChanged { + participant: participant_from_livekit(participant), + old_metadata, + metadata, + }, + livekit::RoomEvent::ParticipantNameChanged { + participant, + old_name, + name, + } => RoomEvent::ParticipantNameChanged { + participant: participant_from_livekit(participant), + old_name, + name, + }, + livekit::RoomEvent::ParticipantAttributesChanged { + participant, + changed_attributes, + } => RoomEvent::ParticipantAttributesChanged { + participant: participant_from_livekit(participant), + changed_attributes: changed_attributes.into_iter().collect(), + }, + livekit::RoomEvent::ActiveSpeakersChanged { speakers } => { + RoomEvent::ActiveSpeakersChanged { + speakers: speakers.into_iter().map(participant_from_livekit).collect(), + } + } + livekit::RoomEvent::Connected { + participants_with_tracks, + } => RoomEvent::Connected { + participants_with_tracks: participants_with_tracks + .into_iter() + .map({ + |(p, t)| { + ( + RemoteParticipant(p), + t.into_iter().map(|t| RemoteTrackPublication(t)).collect(), + ) + } + }) + .collect(), + }, + livekit::RoomEvent::Disconnected { reason } => RoomEvent::Disconnected { + reason: reason.as_str_name(), + }, + livekit::RoomEvent::Reconnecting => RoomEvent::Reconnecting, + livekit::RoomEvent::Reconnected => RoomEvent::Reconnected, + _ => { + log::trace!("dropping livekit event: {:?}", event); return None; } - let bgra_frame_slice = std::slice::from_raw_parts_mut(start_ptr, byte_len); - buffer.to_argb( - VideoFormatType::ARGB, // For some reason, this displays correctly while RGBA (the correct format) does not - bgra_frame_slice, - stride, - width as i32, - height as i32, - ); - Vec::from_raw_parts(start_ptr, byte_len, byte_len) }; - Some(Arc::new(RenderImage::new(SmallVec::from_elem( - Frame::new( - RgbaImage::from_raw(width, height, argb_image) - .with_context(|| "Bug: not enough bytes allocated for image.") - .log_err()?, - ), - 1, - )))) + Some(event) } - -#[cfg(target_os = "macos")] -fn video_frame_buffer_to_webrtc(frame: ScreenCaptureFrame) -> Option> { - use core_foundation::base::TCFType as _; - - let pixel_buffer = frame.0.as_concrete_TypeRef(); - std::mem::forget(frame.0); - unsafe { - Some(webrtc::video_frame::native::NativeBuffer::from_cv_pixel_buffer(pixel_buffer as _)) - } -} - -#[cfg(not(any(target_os = "macos", all(target_os = "windows", target_env = "gnu"))))] -fn video_frame_buffer_to_webrtc(_frame: ScreenCaptureFrame) -> Option> { - None as Option> -} - -trait DeviceChangeListenerApi: Stream + Sized { - fn new(input: bool) -> Result; -} - -#[cfg(target_os = "macos")] -mod macos { - - use coreaudio::sys::{ - kAudioHardwarePropertyDefaultInputDevice, kAudioHardwarePropertyDefaultOutputDevice, - kAudioObjectPropertyElementMaster, kAudioObjectPropertyScopeGlobal, - kAudioObjectSystemObject, AudioObjectAddPropertyListener, AudioObjectID, - AudioObjectPropertyAddress, AudioObjectRemovePropertyListener, OSStatus, - }; - use futures::{channel::mpsc::UnboundedReceiver, StreamExt}; - - use crate::DeviceChangeListenerApi; - - /// Implementation from: https://github.com/zed-industries/cpal/blob/fd8bc2fd39f1f5fdee5a0690656caff9a26d9d50/src/host/coreaudio/macos/property_listener.rs#L15 - pub struct CoreAudioDefaultDeviceChangeListener { - rx: UnboundedReceiver<()>, - callback: Box, - input: bool, - } - - trait _AssertSend: Send {} - impl _AssertSend for CoreAudioDefaultDeviceChangeListener {} - - struct PropertyListenerCallbackWrapper(Box); - - unsafe extern "C" fn property_listener_handler_shim( - _: AudioObjectID, - _: u32, - _: *const AudioObjectPropertyAddress, - callback: *mut ::std::os::raw::c_void, - ) -> OSStatus { - let wrapper = callback as *mut PropertyListenerCallbackWrapper; - (*wrapper).0(); - 0 - } - - impl DeviceChangeListenerApi for CoreAudioDefaultDeviceChangeListener { - fn new(input: bool) -> gpui::Result { - let (tx, rx) = futures::channel::mpsc::unbounded(); - - let callback = Box::new(PropertyListenerCallbackWrapper(Box::new(move || { - tx.unbounded_send(()).ok(); - }))); - - unsafe { - coreaudio::Error::from_os_status(AudioObjectAddPropertyListener( - kAudioObjectSystemObject, - &AudioObjectPropertyAddress { - mSelector: if input { - kAudioHardwarePropertyDefaultInputDevice - } else { - kAudioHardwarePropertyDefaultOutputDevice - }, - mScope: kAudioObjectPropertyScopeGlobal, - mElement: kAudioObjectPropertyElementMaster, - }, - Some(property_listener_handler_shim), - &*callback as *const _ as *mut _, - ))?; - } - - Ok(Self { - rx, - callback, - input, - }) - } - } - - impl Drop for CoreAudioDefaultDeviceChangeListener { - fn drop(&mut self) { - unsafe { - AudioObjectRemovePropertyListener( - kAudioObjectSystemObject, - &AudioObjectPropertyAddress { - mSelector: if self.input { - kAudioHardwarePropertyDefaultInputDevice - } else { - kAudioHardwarePropertyDefaultOutputDevice - }, - mScope: kAudioObjectPropertyScopeGlobal, - mElement: kAudioObjectPropertyElementMaster, - }, - Some(property_listener_handler_shim), - &*self.callback as *const _ as *mut _, - ); - } - } - } - - impl futures::Stream for CoreAudioDefaultDeviceChangeListener { - type Item = (); - - fn poll_next( - mut self: std::pin::Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - ) -> std::task::Poll> { - self.rx.poll_next_unpin(cx) - } - } -} - -#[cfg(target_os = "macos")] -type DeviceChangeListener = macos::CoreAudioDefaultDeviceChangeListener; - -#[cfg(not(target_os = "macos"))] -mod noop_change_listener { - use std::task::Poll; - - use crate::DeviceChangeListenerApi; - - pub struct NoopOutputDeviceChangelistener {} - - impl DeviceChangeListenerApi for NoopOutputDeviceChangelistener { - fn new(_input: bool) -> anyhow::Result { - Ok(NoopOutputDeviceChangelistener {}) - } - } - - impl futures::Stream for NoopOutputDeviceChangelistener { - type Item = (); - - fn poll_next( - self: std::pin::Pin<&mut Self>, - _cx: &mut std::task::Context<'_>, - ) -> Poll> { - Poll::Pending - } - } -} - -#[cfg(not(target_os = "macos"))] -type DeviceChangeListener = noop_change_listener::NoopOutputDeviceChangelistener; diff --git a/crates/livekit_client/src/livekit_client/playback.rs b/crates/livekit_client/src/livekit_client/playback.rs new file mode 100644 index 0000000000..480b499757 --- /dev/null +++ b/crates/livekit_client/src/livekit_client/playback.rs @@ -0,0 +1,763 @@ +use anyhow::{anyhow, Context as _, Result}; + +use cpal::traits::{DeviceTrait, HostTrait, StreamTrait as _}; +use futures::channel::mpsc::UnboundedSender; +use futures::{Stream, StreamExt as _}; +use gpui::{ + BackgroundExecutor, ScreenCaptureFrame, ScreenCaptureSource, ScreenCaptureStream, Task, +}; +use libwebrtc::native::{apm, audio_mixer, audio_resampler}; +use livekit::track; + +use livekit::webrtc::{ + audio_frame::AudioFrame, + audio_source::{native::NativeAudioSource, AudioSourceOptions, RtcAudioSource}, + audio_stream::native::NativeAudioStream, + video_frame::{VideoBuffer, VideoFrame, VideoRotation}, + video_source::{native::NativeVideoSource, RtcVideoSource, VideoResolution}, + video_stream::native::NativeVideoStream, +}; +use parking_lot::Mutex; +use std::cell::RefCell; +use std::sync::atomic::{self, AtomicI32}; +use std::sync::Weak; +use std::time::Duration; +use std::{borrow::Cow, collections::VecDeque, sync::Arc, thread}; +use util::{maybe, ResultExt as _}; + +pub(crate) struct AudioStack { + executor: BackgroundExecutor, + apm: Arc>, + mixer: Arc>, + _output_task: RefCell>>, + next_ssrc: AtomicI32, +} + +// NOTE: We use WebRTC's mixer which only supports +// 16kHz, 32kHz and 48kHz. As 48 is the most common "next step up" +// for audio output devices like speakers/bluetooth, we just hard-code +// this; and downsample when we need to. +const SAMPLE_RATE: u32 = 48000; +const NUM_CHANNELS: u32 = 2; + +impl AudioStack { + pub(crate) fn new(executor: BackgroundExecutor) -> Self { + let apm = Arc::new(Mutex::new(apm::AudioProcessingModule::new( + true, true, true, true, + ))); + let mixer = Arc::new(Mutex::new(audio_mixer::AudioMixer::new())); + Self { + executor, + apm, + mixer, + _output_task: RefCell::new(Weak::new()), + next_ssrc: AtomicI32::new(1), + } + } + + pub(crate) fn play_remote_audio_track( + &self, + track: &livekit::track::RemoteAudioTrack, + ) -> AudioStream { + let output_task = self.start_output(); + + let next_ssrc = self.next_ssrc.fetch_add(1, atomic::Ordering::Relaxed); + let source = AudioMixerSource { + ssrc: next_ssrc, + sample_rate: SAMPLE_RATE, + num_channels: NUM_CHANNELS, + buffer: Arc::default(), + }; + self.mixer.lock().add_source(source.clone()); + + let mut stream = NativeAudioStream::new( + track.rtc_track(), + source.sample_rate as i32, + source.num_channels as i32, + ); + + let receive_task = self.executor.spawn({ + let source = source.clone(); + async move { + while let Some(frame) = stream.next().await { + source.receive(frame); + } + } + }); + + let mixer = self.mixer.clone(); + let on_drop = util::defer(move || { + mixer.lock().remove_source(source.ssrc); + drop(receive_task); + drop(output_task); + }); + + AudioStream::Output { + _drop: Box::new(on_drop), + } + } + + pub(crate) fn capture_local_microphone_track( + &self, + ) -> Result<(crate::LocalAudioTrack, AudioStream)> { + let source = NativeAudioSource::new( + // n.b. this struct's options are always ignored, noise cancellation is provided by apm. + AudioSourceOptions::default(), + SAMPLE_RATE, + NUM_CHANNELS, + 10, + ); + + let track = track::LocalAudioTrack::create_audio_track( + "microphone", + RtcAudioSource::Native(source.clone()), + ); + + let apm = self.apm.clone(); + + let (frame_tx, mut frame_rx) = futures::channel::mpsc::unbounded(); + let transmit_task = self.executor.spawn({ + let source = source.clone(); + async move { + while let Some(frame) = frame_rx.next().await { + source.capture_frame(&frame).await.log_err(); + } + } + }); + let capture_task = self.executor.spawn(async move { + Self::capture_input(apm, frame_tx, SAMPLE_RATE, NUM_CHANNELS).await + }); + + let on_drop = util::defer(|| { + drop(transmit_task); + drop(capture_task); + }); + return Ok(( + super::LocalAudioTrack(track), + AudioStream::Output { + _drop: Box::new(on_drop), + }, + )); + } + + fn start_output(&self) -> Arc> { + if let Some(task) = self._output_task.borrow().upgrade() { + return task; + } + let task = Arc::new(self.executor.spawn({ + let apm = self.apm.clone(); + let mixer = self.mixer.clone(); + async move { + Self::play_output(apm, mixer, SAMPLE_RATE, NUM_CHANNELS) + .await + .log_err(); + } + })); + *self._output_task.borrow_mut() = Arc::downgrade(&task); + task + } + + async fn play_output( + apm: Arc>, + mixer: Arc>, + sample_rate: u32, + num_channels: u32, + ) -> Result<()> { + let mut default_change_listener = DeviceChangeListener::new(false)?; + + loop { + let (output_device, output_config) = default_device(false)?; + let (end_on_drop_tx, end_on_drop_rx) = std::sync::mpsc::channel::<()>(); + let mixer = mixer.clone(); + let apm = apm.clone(); + let mut resampler = audio_resampler::AudioResampler::default(); + let mut buf = Vec::new(); + + thread::spawn(move || { + let output_stream = output_device.build_output_stream( + &output_config.config(), + { + move |mut data, _info| { + while data.len() > 0 { + if data.len() <= buf.len() { + let rest = buf.split_off(data.len()); + data.copy_from_slice(&buf); + buf = rest; + return; + } + if buf.len() > 0 { + let (prefix, suffix) = data.split_at_mut(buf.len()); + prefix.copy_from_slice(&buf); + data = suffix; + } + + let mut mixer = mixer.lock(); + let mixed = mixer.mix(output_config.channels() as usize); + let sampled = resampler.remix_and_resample( + mixed, + sample_rate / 100, + num_channels, + sample_rate, + output_config.channels() as u32, + output_config.sample_rate().0, + ); + buf = sampled.to_vec(); + apm.lock() + .process_reverse_stream( + &mut buf, + output_config.sample_rate().0 as i32, + output_config.channels() as i32, + ) + .ok(); + } + } + }, + |error| log::error!("error playing audio track: {:?}", error), + Some(Duration::from_millis(100)), + ); + + let Some(output_stream) = output_stream.log_err() else { + return; + }; + + output_stream.play().log_err(); + // Block forever to keep the output stream alive + end_on_drop_rx.recv().ok(); + }); + + default_change_listener.next().await; + drop(end_on_drop_tx) + } + } + + async fn capture_input( + apm: Arc>, + frame_tx: UnboundedSender>, + sample_rate: u32, + num_channels: u32, + ) -> Result<()> { + let mut default_change_listener = DeviceChangeListener::new(true)?; + loop { + let (device, config) = default_device(true)?; + let (end_on_drop_tx, end_on_drop_rx) = std::sync::mpsc::channel::<()>(); + let apm = apm.clone(); + let frame_tx = frame_tx.clone(); + let mut resampler = audio_resampler::AudioResampler::default(); + + thread::spawn(move || { + maybe!({ + if let Some(name) = device.name().ok() { + log::info!("Using microphone: {}", name) + } else { + log::info!("Using microphone: "); + } + + let ten_ms_buffer_size = + (config.channels() as u32 * config.sample_rate().0 / 100) as usize; + let mut buf: Vec = Vec::with_capacity(ten_ms_buffer_size); + + let stream = device + .build_input_stream_raw( + &config.config(), + cpal::SampleFormat::I16, + move |data, _: &_| { + let mut data = data.as_slice::().unwrap(); + while data.len() > 0 { + let remainder = (buf.capacity() - buf.len()).min(data.len()); + buf.extend_from_slice(&data[..remainder]); + data = &data[remainder..]; + + if buf.capacity() == buf.len() { + let mut sampled = resampler + .remix_and_resample( + buf.as_slice(), + config.sample_rate().0 as u32 / 100, + config.channels() as u32, + config.sample_rate().0 as u32, + num_channels, + sample_rate, + ) + .to_owned(); + apm.lock() + .process_stream( + &mut sampled, + sample_rate as i32, + num_channels as i32, + ) + .log_err(); + buf.clear(); + frame_tx + .unbounded_send(AudioFrame { + data: Cow::Owned(sampled), + sample_rate, + num_channels, + samples_per_channel: sample_rate / 100, + }) + .ok(); + } + } + }, + |err| log::error!("error capturing audio track: {:?}", err), + Some(Duration::from_millis(100)), + ) + .context("failed to build input stream")?; + + stream.play()?; + // Keep the thread alive and holding onto the `stream` + end_on_drop_rx.recv().ok(); + anyhow::Ok(Some(())) + }) + .log_err(); + }); + + default_change_listener.next().await; + drop(end_on_drop_tx) + } + } +} + +use super::LocalVideoTrack; + +pub enum AudioStream { + Input { _task: Task<()> }, + Output { _drop: Box }, +} + +pub(crate) async fn capture_local_video_track( + capture_source: &dyn ScreenCaptureSource, + cx: &mut gpui::AsyncApp, +) -> Result<(crate::LocalVideoTrack, Box)> { + let resolution = capture_source.resolution()?; + let track_source = gpui_tokio::Tokio::spawn(cx, async move { + NativeVideoSource::new(VideoResolution { + width: resolution.width.0 as u32, + height: resolution.height.0 as u32, + }) + })? + .await?; + + let capture_stream = capture_source + .stream({ + let track_source = track_source.clone(); + Box::new(move |frame| { + if let Some(buffer) = video_frame_buffer_to_webrtc(frame) { + track_source.capture_frame(&VideoFrame { + rotation: VideoRotation::VideoRotation0, + timestamp_us: 0, + buffer, + }); + } + }) + }) + .await??; + + Ok(( + LocalVideoTrack(track::LocalVideoTrack::create_video_track( + "screen share", + RtcVideoSource::Native(track_source), + )), + capture_stream, + )) +} + +fn default_device(input: bool) -> Result<(cpal::Device, cpal::SupportedStreamConfig)> { + let device; + let config; + if input { + device = cpal::default_host() + .default_input_device() + .ok_or_else(|| anyhow!("no audio input device available"))?; + config = device + .default_input_config() + .context("failed to get default input config")?; + } else { + device = cpal::default_host() + .default_output_device() + .ok_or_else(|| anyhow!("no audio output device available"))?; + config = device + .default_output_config() + .context("failed to get default output config")?; + } + Ok((device, config)) +} + +#[derive(Clone)] +struct AudioMixerSource { + ssrc: i32, + sample_rate: u32, + num_channels: u32, + buffer: Arc>>>, +} + +impl AudioMixerSource { + fn receive(&self, frame: AudioFrame) { + assert_eq!( + frame.data.len() as u32, + self.sample_rate * self.num_channels / 100 + ); + + let mut buffer = self.buffer.lock(); + buffer.push_back(frame.data.to_vec()); + while buffer.len() > 10 { + buffer.pop_front(); + } + } +} + +impl libwebrtc::native::audio_mixer::AudioMixerSource for AudioMixerSource { + fn ssrc(&self) -> i32 { + self.ssrc + } + + fn preferred_sample_rate(&self) -> u32 { + self.sample_rate + } + + fn get_audio_frame_with_info<'a>(&self, target_sample_rate: u32) -> Option { + assert_eq!(self.sample_rate, target_sample_rate); + let buf = self.buffer.lock().pop_front()?; + Some(AudioFrame { + data: Cow::Owned(buf), + sample_rate: self.sample_rate, + num_channels: self.num_channels, + samples_per_channel: self.sample_rate / 100, + }) + } +} + +pub fn play_remote_video_track( + track: &crate::RemoteVideoTrack, +) -> impl Stream { + #[cfg(target_os = "macos")] + { + let mut pool = None; + let most_recent_frame_size = (0, 0); + NativeVideoStream::new(track.0.rtc_track()).filter_map(move |frame| { + if pool == None + || most_recent_frame_size != (frame.buffer.width(), frame.buffer.height()) + { + pool = create_buffer_pool(frame.buffer.width(), frame.buffer.height()).log_err(); + } + let pool = pool.clone(); + async move { + if frame.buffer.width() < 10 && frame.buffer.height() < 10 { + // when the remote stops sharing, we get an 8x8 black image. + // In a lil bit, the unpublish will come through and close the view, + // but until then, don't flash black. + return None; + } + + video_frame_buffer_from_webrtc(pool?, frame.buffer) + } + }) + } + #[cfg(not(target_os = "macos"))] + { + NativeVideoStream::new(track.0.rtc_track()) + .filter_map(|frame| async move { video_frame_buffer_from_webrtc(frame.buffer) }) + } +} + +#[cfg(target_os = "macos")] +fn create_buffer_pool( + width: u32, + height: u32, +) -> Result { + use core_foundation::{base::TCFType, number::CFNumber, string::CFString}; + use core_video::pixel_buffer; + use core_video::{ + pixel_buffer::kCVPixelFormatType_420YpCbCr8BiPlanarFullRange, + pixel_buffer_io_surface::kCVPixelBufferIOSurfaceCoreAnimationCompatibilityKey, + pixel_buffer_pool::{self}, + }; + + let width_key: CFString = + unsafe { CFString::wrap_under_get_rule(pixel_buffer::kCVPixelBufferWidthKey) }; + let height_key: CFString = + unsafe { CFString::wrap_under_get_rule(pixel_buffer::kCVPixelBufferHeightKey) }; + let animation_key: CFString = unsafe { + CFString::wrap_under_get_rule(kCVPixelBufferIOSurfaceCoreAnimationCompatibilityKey) + }; + let format_key: CFString = + unsafe { CFString::wrap_under_get_rule(pixel_buffer::kCVPixelBufferPixelFormatTypeKey) }; + + let yes: CFNumber = 1.into(); + let width: CFNumber = (width as i32).into(); + let height: CFNumber = (height as i32).into(); + let format: CFNumber = (kCVPixelFormatType_420YpCbCr8BiPlanarFullRange as i64).into(); + + let buffer_attributes = core_foundation::dictionary::CFDictionary::from_CFType_pairs(&[ + (width_key, width.into_CFType()), + (height_key, height.into_CFType()), + (animation_key, yes.into_CFType()), + (format_key, format.into_CFType()), + ]); + + pixel_buffer_pool::CVPixelBufferPool::new(None, Some(&buffer_attributes)).map_err(|cv_return| { + anyhow!( + "failed to create pixel buffer pool: CVReturn({})", + cv_return + ) + }) +} + +#[cfg(target_os = "macos")] +pub type RemoteVideoFrame = core_video::pixel_buffer::CVPixelBuffer; + +#[cfg(target_os = "macos")] +fn video_frame_buffer_from_webrtc( + pool: core_video::pixel_buffer_pool::CVPixelBufferPool, + buffer: Box, +) -> Option { + use core_foundation::base::TCFType; + use core_video::{pixel_buffer::CVPixelBuffer, r#return::kCVReturnSuccess}; + use livekit::webrtc::native::yuv_helper::i420_to_nv12; + + if let Some(native) = buffer.as_native() { + let pixel_buffer = native.get_cv_pixel_buffer(); + if pixel_buffer.is_null() { + return None; + } + return unsafe { Some(CVPixelBuffer::wrap_under_get_rule(pixel_buffer as _)) }; + } + + let i420_buffer = buffer.as_i420()?; + let pixel_buffer = pool.create_pixel_buffer().log_err()?; + + let image_buffer = unsafe { + if pixel_buffer.lock_base_address(0) != kCVReturnSuccess { + return None; + } + + let dst_y = pixel_buffer.get_base_address_of_plane(0); + let dst_y_stride = pixel_buffer.get_bytes_per_row_of_plane(0); + let dst_y_len = pixel_buffer.get_height_of_plane(0) * dst_y_stride; + let dst_uv = pixel_buffer.get_base_address_of_plane(1); + let dst_uv_stride = pixel_buffer.get_bytes_per_row_of_plane(1); + let dst_uv_len = pixel_buffer.get_height_of_plane(1) * dst_uv_stride; + let width = pixel_buffer.get_width(); + let height = pixel_buffer.get_height(); + let dst_y_buffer = std::slice::from_raw_parts_mut(dst_y as *mut u8, dst_y_len); + let dst_uv_buffer = std::slice::from_raw_parts_mut(dst_uv as *mut u8, dst_uv_len); + + let (stride_y, stride_u, stride_v) = i420_buffer.strides(); + let (src_y, src_u, src_v) = i420_buffer.data(); + i420_to_nv12( + src_y, + stride_y, + src_u, + stride_u, + src_v, + stride_v, + dst_y_buffer, + dst_y_stride as u32, + dst_uv_buffer, + dst_uv_stride as u32, + width as i32, + height as i32, + ); + + if pixel_buffer.unlock_base_address(0) != kCVReturnSuccess { + return None; + } + + pixel_buffer + }; + + Some(image_buffer) +} + +#[cfg(not(target_os = "macos"))] +pub type RemoteVideoFrame = Arc; + +#[cfg(not(target_os = "macos"))] +fn video_frame_buffer_from_webrtc(buffer: Box) -> Option { + use gpui::RenderImage; + use image::{Frame, RgbaImage}; + use livekit::webrtc::prelude::VideoFormatType; + use smallvec::SmallVec; + use std::alloc::{alloc, Layout}; + + let width = buffer.width(); + let height = buffer.height(); + let stride = width * 4; + let byte_len = (stride * height) as usize; + let argb_image = unsafe { + // Motivation for this unsafe code is to avoid initializing the frame data, since to_argb + // will write all bytes anyway. + let start_ptr = alloc(Layout::array::(byte_len).log_err()?); + if start_ptr.is_null() { + return None; + } + let bgra_frame_slice = std::slice::from_raw_parts_mut(start_ptr, byte_len); + buffer.to_argb( + VideoFormatType::ARGB, // For some reason, this displays correctly while RGBA (the correct format) does not + bgra_frame_slice, + stride, + width as i32, + height as i32, + ); + Vec::from_raw_parts(start_ptr, byte_len, byte_len) + }; + + Some(Arc::new(RenderImage::new(SmallVec::from_elem( + Frame::new( + RgbaImage::from_raw(width, height, argb_image) + .with_context(|| "Bug: not enough bytes allocated for image.") + .log_err()?, + ), + 1, + )))) +} + +#[cfg(target_os = "macos")] +fn video_frame_buffer_to_webrtc(frame: ScreenCaptureFrame) -> Option> { + use livekit::webrtc; + + let pixel_buffer = frame.0.as_concrete_TypeRef(); + std::mem::forget(frame.0); + unsafe { + Some(webrtc::video_frame::native::NativeBuffer::from_cv_pixel_buffer(pixel_buffer as _)) + } +} + +#[cfg(not(target_os = "macos"))] +fn video_frame_buffer_to_webrtc(_frame: ScreenCaptureFrame) -> Option> { + None as Option> +} + +trait DeviceChangeListenerApi: Stream + Sized { + fn new(input: bool) -> Result; +} + +#[cfg(target_os = "macos")] +mod macos { + + use coreaudio::sys::{ + kAudioHardwarePropertyDefaultInputDevice, kAudioHardwarePropertyDefaultOutputDevice, + kAudioObjectPropertyElementMaster, kAudioObjectPropertyScopeGlobal, + kAudioObjectSystemObject, AudioObjectAddPropertyListener, AudioObjectID, + AudioObjectPropertyAddress, AudioObjectRemovePropertyListener, OSStatus, + }; + use futures::{channel::mpsc::UnboundedReceiver, StreamExt}; + + /// Implementation from: https://github.com/zed-industries/cpal/blob/fd8bc2fd39f1f5fdee5a0690656caff9a26d9d50/src/host/coreaudio/macos/property_listener.rs#L15 + pub struct CoreAudioDefaultDeviceChangeListener { + rx: UnboundedReceiver<()>, + callback: Box, + input: bool, + } + + trait _AssertSend: Send {} + impl _AssertSend for CoreAudioDefaultDeviceChangeListener {} + + struct PropertyListenerCallbackWrapper(Box); + + unsafe extern "C" fn property_listener_handler_shim( + _: AudioObjectID, + _: u32, + _: *const AudioObjectPropertyAddress, + callback: *mut ::std::os::raw::c_void, + ) -> OSStatus { + let wrapper = callback as *mut PropertyListenerCallbackWrapper; + (*wrapper).0(); + 0 + } + + impl super::DeviceChangeListenerApi for CoreAudioDefaultDeviceChangeListener { + fn new(input: bool) -> gpui::Result { + let (tx, rx) = futures::channel::mpsc::unbounded(); + + let callback = Box::new(PropertyListenerCallbackWrapper(Box::new(move || { + tx.unbounded_send(()).ok(); + }))); + + unsafe { + coreaudio::Error::from_os_status(AudioObjectAddPropertyListener( + kAudioObjectSystemObject, + &AudioObjectPropertyAddress { + mSelector: if input { + kAudioHardwarePropertyDefaultInputDevice + } else { + kAudioHardwarePropertyDefaultOutputDevice + }, + mScope: kAudioObjectPropertyScopeGlobal, + mElement: kAudioObjectPropertyElementMaster, + }, + Some(property_listener_handler_shim), + &*callback as *const _ as *mut _, + ))?; + } + + Ok(Self { + rx, + callback, + input, + }) + } + } + + impl Drop for CoreAudioDefaultDeviceChangeListener { + fn drop(&mut self) { + unsafe { + AudioObjectRemovePropertyListener( + kAudioObjectSystemObject, + &AudioObjectPropertyAddress { + mSelector: if self.input { + kAudioHardwarePropertyDefaultInputDevice + } else { + kAudioHardwarePropertyDefaultOutputDevice + }, + mScope: kAudioObjectPropertyScopeGlobal, + mElement: kAudioObjectPropertyElementMaster, + }, + Some(property_listener_handler_shim), + &*self.callback as *const _ as *mut _, + ); + } + } + } + + impl futures::Stream for CoreAudioDefaultDeviceChangeListener { + type Item = (); + + fn poll_next( + mut self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + self.rx.poll_next_unpin(cx) + } + } +} + +#[cfg(target_os = "macos")] +type DeviceChangeListener = macos::CoreAudioDefaultDeviceChangeListener; + +#[cfg(not(target_os = "macos"))] +mod noop_change_listener { + use std::task::Poll; + + use super::DeviceChangeListenerApi; + + pub struct NoopOutputDeviceChangelistener {} + + impl DeviceChangeListenerApi for NoopOutputDeviceChangelistener { + fn new(_input: bool) -> anyhow::Result { + Ok(NoopOutputDeviceChangelistener {}) + } + } + + impl futures::Stream for NoopOutputDeviceChangelistener { + type Item = (); + + fn poll_next( + self: std::pin::Pin<&mut Self>, + _cx: &mut std::task::Context<'_>, + ) -> Poll> { + Poll::Pending + } + } +} + +#[cfg(not(target_os = "macos"))] +type DeviceChangeListener = noop_change_listener::NoopOutputDeviceChangelistener; diff --git a/crates/livekit_client/src/mock_client.rs b/crates/livekit_client/src/mock_client.rs new file mode 100644 index 0000000000..d1594edff8 --- /dev/null +++ b/crates/livekit_client/src/mock_client.rs @@ -0,0 +1,38 @@ +use crate::test; + +pub(crate) mod participant; +pub(crate) mod publication; +pub(crate) mod track; + +pub type RemoteVideoTrack = track::RemoteVideoTrack; +pub type RemoteAudioTrack = track::RemoteAudioTrack; +pub type RemoteTrackPublication = publication::RemoteTrackPublication; +pub type RemoteParticipant = participant::RemoteParticipant; + +pub type LocalVideoTrack = track::LocalVideoTrack; +pub type LocalAudioTrack = track::LocalAudioTrack; +pub type LocalTrackPublication = publication::LocalTrackPublication; +pub type LocalParticipant = participant::LocalParticipant; + +pub type Room = test::Room; +pub use test::{ConnectionState, ParticipantIdentity, TrackSid}; + +pub struct AudioStream {} + +#[cfg(not(target_os = "macos"))] +pub type RemoteVideoFrame = std::sync::Arc; + +#[cfg(target_os = "macos")] +#[derive(Clone)] +pub(crate) struct RemoteVideoFrame {} +#[cfg(target_os = "macos")] +impl Into for RemoteVideoFrame { + fn into(self) -> gpui::SurfaceSource { + unimplemented!() + } +} +pub(crate) fn play_remote_video_track( + _track: &crate::RemoteVideoTrack, +) -> impl futures::Stream { + futures::stream::pending() +} diff --git a/crates/livekit_client/src/test/participant.rs b/crates/livekit_client/src/mock_client/participant.rs similarity index 55% rename from crates/livekit_client/src/test/participant.rs rename to crates/livekit_client/src/mock_client/participant.rs index f8df22ff5d..3eb6c56a99 100644 --- a/crates/livekit_client/src/test/participant.rs +++ b/crates/livekit_client/src/mock_client/participant.rs @@ -1,26 +1,24 @@ -use super::*; - -#[derive(Clone, Debug)] -pub enum Participant { - Local(LocalParticipant), - Remote(RemoteParticipant), -} +use crate::{ + test::{Room, WeakRoom}, + AudioStream, LocalAudioTrack, LocalTrackPublication, LocalVideoTrack, Participant, + ParticipantIdentity, RemoteTrack, RemoteTrackPublication, TrackSid, +}; +use anyhow::Result; +use collections::HashMap; +use gpui::{AsyncApp, ScreenCaptureSource, ScreenCaptureStream}; #[derive(Clone, Debug)] pub struct LocalParticipant { - #[cfg(not(all(target_os = "windows", target_env = "gnu")))] - pub(super) identity: ParticipantIdentity, - pub(super) room: Room, + pub(crate) identity: ParticipantIdentity, + pub(crate) room: Room, } #[derive(Clone, Debug)] pub struct RemoteParticipant { - #[cfg(not(all(target_os = "windows", target_env = "gnu")))] - pub(super) identity: ParticipantIdentity, - pub(super) room: WeakRoom, + pub(crate) identity: ParticipantIdentity, + pub(crate) room: WeakRoom, } -#[cfg(not(all(target_os = "windows", target_env = "gnu")))] impl Participant { pub fn identity(&self) -> ParticipantIdentity { match self { @@ -30,41 +28,53 @@ impl Participant { } } -#[cfg(not(all(target_os = "windows", target_env = "gnu")))] impl LocalParticipant { - pub async fn unpublish_track(&self, track: &TrackSid) -> Result<()> { + pub async fn unpublish_track(&self, track: TrackSid, _cx: &AsyncApp) -> Result<()> { self.room .test_server() - .unpublish_track(self.room.token(), track) + .unpublish_track(self.room.token(), &track) .await } - pub async fn publish_track( + pub(crate) async fn publish_microphone_track( &self, - track: LocalTrack, - _options: TrackPublishOptions, - ) -> Result { + _cx: &AsyncApp, + ) -> Result<(LocalTrackPublication, AudioStream)> { let this = self.clone(); - let track = track.clone(); let server = this.room.test_server(); - let sid = match track { - LocalTrack::Video(track) => { - server.publish_video_track(this.room.token(), track).await? - } - LocalTrack::Audio(track) => { - server - .publish_audio_track(this.room.token(), &track) - .await? - } - }; - Ok(LocalTrackPublication { - room: self.room.downgrade(), - sid, - }) + let sid = server + .publish_audio_track(this.room.token(), &LocalAudioTrack {}) + .await?; + + Ok(( + LocalTrackPublication { + room: self.room.downgrade(), + sid, + }, + AudioStream {}, + )) + } + + pub async fn publish_screenshare_track( + &self, + _source: &dyn ScreenCaptureSource, + _cx: &mut AsyncApp, + ) -> Result<(LocalTrackPublication, Box)> { + let this = self.clone(); + let server = this.room.test_server(); + let sid = server + .publish_video_track(this.room.token(), LocalVideoTrack {}) + .await?; + Ok(( + LocalTrackPublication { + room: self.room.downgrade(), + sid, + }, + Box::new(TestScreenCaptureStream {}), + )) } } -#[cfg(not(all(target_os = "windows", target_env = "gnu")))] impl RemoteParticipant { pub fn track_publications(&self) -> HashMap { if let Some(room) = self.room.upgrade() { @@ -109,3 +119,7 @@ impl RemoteParticipant { self.identity.clone() } } + +struct TestScreenCaptureStream; + +impl gpui::ScreenCaptureStream for TestScreenCaptureStream {} diff --git a/crates/livekit_client/src/test/publication.rs b/crates/livekit_client/src/mock_client/publication.rs similarity index 66% rename from crates/livekit_client/src/test/publication.rs rename to crates/livekit_client/src/mock_client/publication.rs index b6fd6c0431..0176e63a9e 100644 --- a/crates/livekit_client/src/test/publication.rs +++ b/crates/livekit_client/src/mock_client/publication.rs @@ -1,54 +1,30 @@ -use super::*; +use gpui::App; -#[derive(Clone, Debug)] -pub enum TrackPublication { - Local(LocalTrackPublication), - Remote(RemoteTrackPublication), -} +use crate::{test::WeakRoom, RemoteTrack, TrackSid}; #[derive(Clone, Debug)] pub struct LocalTrackPublication { - #[cfg(not(all(target_os = "windows", target_env = "gnu")))] pub(crate) sid: TrackSid, pub(crate) room: WeakRoom, } #[derive(Clone, Debug)] pub struct RemoteTrackPublication { - #[cfg(not(all(target_os = "windows", target_env = "gnu")))] pub(crate) sid: TrackSid, pub(crate) room: WeakRoom, pub(crate) track: RemoteTrack, } -#[cfg(not(all(target_os = "windows", target_env = "gnu")))] -impl TrackPublication { - pub fn sid(&self) -> TrackSid { - match self { - TrackPublication::Local(track) => track.sid(), - TrackPublication::Remote(track) => track.sid(), - } - } - - pub fn is_muted(&self) -> bool { - match self { - TrackPublication::Local(track) => track.is_muted(), - TrackPublication::Remote(track) => track.is_muted(), - } - } -} - -#[cfg(not(all(target_os = "windows", target_env = "gnu")))] impl LocalTrackPublication { pub fn sid(&self) -> TrackSid { self.sid.clone() } - pub fn mute(&self) { + pub fn mute(&self, _cx: &App) { self.set_mute(true) } - pub fn unmute(&self) { + pub fn unmute(&self, _cx: &App) { self.set_mute(false) } @@ -71,7 +47,6 @@ impl LocalTrackPublication { } } -#[cfg(not(all(target_os = "windows", target_env = "gnu")))] impl RemoteTrackPublication { pub fn sid(&self) -> TrackSid { self.sid.clone() @@ -81,8 +56,8 @@ impl RemoteTrackPublication { Some(self.track.clone()) } - pub fn kind(&self) -> TrackKind { - self.track.kind() + pub fn is_audio(&self) -> bool { + matches!(self.track, RemoteTrack::Audio(_)) } pub fn is_muted(&self) -> bool { @@ -103,7 +78,7 @@ impl RemoteTrackPublication { } } - pub fn set_enabled(&self, enabled: bool) { + pub fn set_enabled(&self, enabled: bool, _cx: &App) { if let Some(room) = self.room.upgrade() { let paused_audio_tracks = &mut room.0.lock().paused_audio_tracks; if enabled { @@ -114,3 +89,12 @@ impl RemoteTrackPublication { } } } + +impl RemoteTrack { + pub fn set_enabled(&self, enabled: bool, _cx: &App) { + match self { + RemoteTrack::Audio(remote_audio_track) => remote_audio_track.set_enabled(enabled), + RemoteTrack::Video(remote_video_track) => remote_video_track.set_enabled(enabled), + } + } +} diff --git a/crates/livekit_client/src/mock_client/track.rs b/crates/livekit_client/src/mock_client/track.rs new file mode 100644 index 0000000000..aaa1e52783 --- /dev/null +++ b/crates/livekit_client/src/mock_client/track.rs @@ -0,0 +1,75 @@ +use std::sync::Arc; + +use crate::{ + test::{TestServerAudioTrack, TestServerVideoTrack, WeakRoom}, + ParticipantIdentity, TrackSid, +}; + +#[derive(Clone, Debug)] +pub struct LocalVideoTrack {} + +#[derive(Clone, Debug)] +pub struct LocalAudioTrack {} + +#[derive(Clone, Debug)] +pub struct RemoteVideoTrack { + pub(crate) server_track: Arc, + pub(crate) _room: WeakRoom, +} + +#[derive(Clone, Debug)] +pub struct RemoteAudioTrack { + pub(crate) server_track: Arc, + pub(crate) room: WeakRoom, +} + +impl RemoteAudioTrack { + pub fn sid(&self) -> TrackSid { + self.server_track.sid.clone() + } + + pub fn publisher_id(&self) -> ParticipantIdentity { + self.server_track.publisher_id.clone() + } + + pub fn enabled(&self) -> bool { + if let Some(room) = self.room.upgrade() { + !room + .0 + .lock() + .paused_audio_tracks + .contains(&self.server_track.sid) + } else { + false + } + } + + pub fn set_enabled(&self, enabled: bool) { + let Some(room) = self.room.upgrade() else { + return; + }; + if enabled { + room.0 + .lock() + .paused_audio_tracks + .remove(&self.server_track.sid); + } else { + room.0 + .lock() + .paused_audio_tracks + .insert(self.server_track.sid.clone()); + } + } +} + +impl RemoteVideoTrack { + pub fn sid(&self) -> TrackSid { + self.server_track.sid.clone() + } + + pub fn publisher_id(&self) -> ParticipantIdentity { + self.server_track.publisher_id.clone() + } + + pub(crate) fn set_enabled(&self, _enabled: bool) {} +} diff --git a/crates/livekit_client/src/remote_video_track_view.rs b/crates/livekit_client/src/remote_video_track_view.rs index 5602c829c6..9073b8729a 100644 --- a/crates/livekit_client/src/remote_video_track_view.rs +++ b/crates/livekit_client/src/remote_video_track_view.rs @@ -1,5 +1,4 @@ -use crate::track::RemoteVideoTrack; -use anyhow::Result; +use super::RemoteVideoTrack; use futures::StreamExt as _; use gpui::{ AppContext as _, Context, Empty, Entity, EventEmitter, IntoElement, Render, Task, Window, @@ -12,7 +11,7 @@ pub struct RemoteVideoTrackView { current_rendered_frame: Option, #[cfg(not(target_os = "macos"))] previous_rendered_frame: Option, - _maintain_frame: Task>, + _maintain_frame: Task<()>, } #[derive(Debug)] @@ -23,8 +22,27 @@ pub enum RemoteVideoTrackViewEvent { impl RemoteVideoTrackView { pub fn new(track: RemoteVideoTrack, window: &mut Window, cx: &mut Context) -> Self { cx.focus_handle(); - let frames = super::play_remote_video_track(&track); - let _window_handle = window.window_handle(); + let frames = crate::play_remote_video_track(&track); + + #[cfg(not(target_os = "macos"))] + { + use util::ResultExt; + + let window_handle = window.window_handle(); + cx.on_release(move |this, cx| { + if let Some(frame) = this.previous_rendered_frame.take() { + window_handle + .update(cx, |_, window, _cx| window.drop_image(frame).log_err()) + .ok(); + } + if let Some(frame) = this.current_rendered_frame.take() { + window_handle + .update(cx, |_, window, _cx| window.drop_image(frame).log_err()) + .ok(); + } + }) + .detach(); + } Self { track, @@ -35,28 +53,11 @@ impl RemoteVideoTrackView { this.update(cx, |this, cx| { this.latest_frame = Some(frame); cx.notify(); - })?; + }) + .ok(); } - this.update(cx, |_this, cx| { - #[cfg(not(target_os = "macos"))] - { - use util::ResultExt as _; - if let Some(frame) = _this.previous_rendered_frame.take() { - _window_handle - .update(cx, |_, window, _cx| window.drop_image(frame).log_err()) - .ok(); - } - // TODO(mgsloan): This might leak the last image of the screenshare if - // render is called after the screenshare ends. - if let Some(frame) = _this.current_rendered_frame.take() { - _window_handle - .update(cx, |_, window, _cx| window.drop_image(frame).log_err()) - .ok(); - } - } - cx.emit(RemoteVideoTrackViewEvent::Close) - })?; - Ok(()) + this.update(cx, |_this, cx| cx.emit(RemoteVideoTrackViewEvent::Close)) + .ok(); }), #[cfg(not(target_os = "macos"))] current_rendered_frame: None, diff --git a/crates/livekit_client/src/test.rs b/crates/livekit_client/src/test.rs index 363b51c84b..b27479d7b1 100644 --- a/crates/livekit_client/src/test.rs +++ b/crates/livekit_client/src/test.rs @@ -1,19 +1,10 @@ -pub mod participant; -pub mod publication; -pub mod track; +use crate::{AudioStream, Participant, RemoteTrack, RoomEvent, TrackPublication}; -#[cfg(not(all(target_os = "windows", target_env = "gnu")))] -pub mod webrtc; - -#[cfg(not(all(target_os = "windows", target_env = "gnu")))] -use self::id::*; -use self::{participant::*, publication::*, track::*}; +use crate::mock_client::{participant::*, publication::*, track::*}; use anyhow::{anyhow, Context as _, Result}; use async_trait::async_trait; use collections::{btree_map::Entry as BTreeEntry, hash_map::Entry, BTreeMap, HashMap, HashSet}; -use gpui::BackgroundExecutor; -#[cfg(not(all(target_os = "windows", target_env = "gnu")))] -use livekit::options::TrackPublishOptions; +use gpui::{App, AsyncApp, BackgroundExecutor}; use livekit_api::{proto, token}; use parking_lot::Mutex; use postage::{mpsc, sink::Sink}; @@ -22,8 +13,32 @@ use std::sync::{ Arc, Weak, }; -#[cfg(not(all(target_os = "windows", target_env = "gnu")))] -pub use livekit::{id, options, ConnectionState, DisconnectReason, RoomOptions}; +#[derive(Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)] +pub struct ParticipantIdentity(pub String); + +#[derive(Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)] +pub struct TrackSid(pub(crate) String); + +impl std::fmt::Display for TrackSid { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.0.fmt(f) + } +} + +impl TryFrom for TrackSid { + type Error = anyhow::Error; + + fn try_from(value: String) -> Result { + Ok(TrackSid(value)) + } +} + +#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)] +#[non_exhaustive] +pub enum ConnectionState { + Connected, + Disconnected, +} static SERVERS: Mutex>> = Mutex::new(BTreeMap::new()); @@ -31,12 +46,10 @@ pub struct TestServer { pub url: String, pub api_key: String, pub secret_key: String, - #[cfg(not(all(target_os = "windows", target_env = "gnu")))] rooms: Mutex>, executor: BackgroundExecutor, } -#[cfg(not(all(target_os = "windows", target_env = "gnu")))] impl TestServer { pub fn create( url: String, @@ -83,7 +96,7 @@ impl TestServer { } pub async fn create_room(&self, room: String) -> Result<()> { - self.executor.simulate_random_delay().await; + self.simulate_random_delay().await; let mut server_rooms = self.rooms.lock(); if let Entry::Vacant(e) = server_rooms.entry(room.clone()) { @@ -95,7 +108,7 @@ impl TestServer { } async fn delete_room(&self, room: String) -> Result<()> { - self.executor.simulate_random_delay().await; + self.simulate_random_delay().await; let mut server_rooms = self.rooms.lock(); server_rooms @@ -105,7 +118,7 @@ impl TestServer { } async fn join_room(&self, token: String, client_room: Room) -> Result { - self.executor.simulate_random_delay().await; + self.simulate_random_delay().await; let claims = livekit_api::token::validate(&token, &self.secret_key)?; let identity = ParticipantIdentity(claims.sub.unwrap().to_string()); @@ -172,7 +185,7 @@ impl TestServer { } async fn leave_room(&self, token: String) -> Result<()> { - self.executor.simulate_random_delay().await; + self.simulate_random_delay().await; let claims = livekit_api::token::validate(&token, &self.secret_key)?; let identity = ParticipantIdentity(claims.sub.unwrap().to_string()); @@ -229,7 +242,7 @@ impl TestServer { room_name: String, identity: ParticipantIdentity, ) -> Result<()> { - self.executor.simulate_random_delay().await; + self.simulate_random_delay().await; let mut server_rooms = self.rooms.lock(); let room = server_rooms @@ -251,7 +264,7 @@ impl TestServer { identity: String, permission: proto::ParticipantPermission, ) -> Result<()> { - self.executor.simulate_random_delay().await; + self.simulate_random_delay().await; let mut server_rooms = self.rooms.lock(); let room = server_rooms @@ -265,7 +278,7 @@ impl TestServer { pub async fn disconnect_client(&self, client_identity: String) { let client_identity = ParticipantIdentity(client_identity); - self.executor.simulate_random_delay().await; + self.simulate_random_delay().await; let mut server_rooms = self.rooms.lock(); for room in server_rooms.values_mut() { @@ -274,19 +287,19 @@ impl TestServer { room.connection_state = ConnectionState::Disconnected; room.updates_tx .blocking_send(RoomEvent::Disconnected { - reason: DisconnectReason::SignalClose, + reason: "SIGNAL_CLOSED", }) .ok(); } } } - async fn publish_video_track( + pub(crate) async fn publish_video_track( &self, token: String, _local_track: LocalVideoTrack, ) -> Result { - self.executor.simulate_random_delay().await; + self.simulate_random_delay().await; let claims = livekit_api::token::validate(&token, &self.secret_key)?; let identity = ParticipantIdentity(claims.sub.unwrap().to_string()); @@ -347,12 +360,12 @@ impl TestServer { Ok(sid) } - async fn publish_audio_track( + pub(crate) async fn publish_audio_track( &self, token: String, _local_track: &LocalAudioTrack, ) -> Result { - self.executor.simulate_random_delay().await; + self.simulate_random_delay().await; let claims = livekit_api::token::validate(&token, &self.secret_key)?; let identity = ParticipantIdentity(claims.sub.unwrap().to_string()); @@ -414,11 +427,16 @@ impl TestServer { Ok(sid) } - async fn unpublish_track(&self, _token: String, _track: &TrackSid) -> Result<()> { + pub(crate) async fn unpublish_track(&self, _token: String, _track: &TrackSid) -> Result<()> { Ok(()) } - fn set_track_muted(&self, token: &str, track_sid: &TrackSid, muted: bool) -> Result<()> { + pub(crate) fn set_track_muted( + &self, + token: &str, + track_sid: &TrackSid, + muted: bool, + ) -> Result<()> { let claims = livekit_api::token::validate(&token, &self.secret_key)?; let room_name = claims.video.room.unwrap(); let identity = ParticipantIdentity(claims.sub.unwrap().to_string()); @@ -472,7 +490,7 @@ impl TestServer { Ok(()) } - fn is_track_muted(&self, token: &str, track_sid: &TrackSid) -> Option { + pub(crate) fn is_track_muted(&self, token: &str, track_sid: &TrackSid) -> Option { let claims = livekit_api::token::validate(&token, &self.secret_key).ok()?; let room_name = claims.video.room.unwrap(); @@ -487,7 +505,7 @@ impl TestServer { }) } - fn video_tracks(&self, token: String) -> Result> { + pub(crate) fn video_tracks(&self, token: String) -> Result> { let claims = livekit_api::token::validate(&token, &self.secret_key)?; let room_name = claims.video.room.unwrap(); let identity = ParticipantIdentity(claims.sub.unwrap().to_string()); @@ -510,7 +528,7 @@ impl TestServer { .collect()) } - fn audio_tracks(&self, token: String) -> Result> { + pub(crate) fn audio_tracks(&self, token: String) -> Result> { let claims = livekit_api::token::validate(&token, &self.secret_key)?; let room_name = claims.video.room.unwrap(); let identity = ParticipantIdentity(claims.sub.unwrap().to_string()); @@ -532,9 +550,13 @@ impl TestServer { }) .collect()) } + + async fn simulate_random_delay(&self) { + #[cfg(any(test, feature = "test-support"))] + self.executor.simulate_random_delay().await; + } } -#[cfg(not(all(target_os = "windows", target_env = "gnu")))] #[derive(Default, Debug)] struct TestServerRoom { client_rooms: HashMap, @@ -543,103 +565,24 @@ struct TestServerRoom { participant_permissions: HashMap, } -#[cfg(not(all(target_os = "windows", target_env = "gnu")))] #[derive(Debug)] -struct TestServerVideoTrack { - sid: TrackSid, - publisher_id: ParticipantIdentity, +pub(crate) struct TestServerVideoTrack { + pub(crate) sid: TrackSid, + pub(crate) publisher_id: ParticipantIdentity, // frames_rx: async_broadcast::Receiver, } -#[cfg(not(all(target_os = "windows", target_env = "gnu")))] #[derive(Debug)] -struct TestServerAudioTrack { - sid: TrackSid, - publisher_id: ParticipantIdentity, - muted: AtomicBool, +pub(crate) struct TestServerAudioTrack { + pub(crate) sid: TrackSid, + pub(crate) publisher_id: ParticipantIdentity, + pub(crate) muted: AtomicBool, } pub struct TestApiClient { url: String, } -#[derive(Clone, Debug)] -#[non_exhaustive] -pub enum RoomEvent { - ParticipantConnected(RemoteParticipant), - ParticipantDisconnected(RemoteParticipant), - LocalTrackPublished { - publication: LocalTrackPublication, - track: LocalTrack, - participant: LocalParticipant, - }, - LocalTrackUnpublished { - publication: LocalTrackPublication, - participant: LocalParticipant, - }, - TrackSubscribed { - track: RemoteTrack, - publication: RemoteTrackPublication, - participant: RemoteParticipant, - }, - TrackUnsubscribed { - track: RemoteTrack, - publication: RemoteTrackPublication, - participant: RemoteParticipant, - }, - TrackSubscriptionFailed { - participant: RemoteParticipant, - error: String, - #[cfg(not(all(target_os = "windows", target_env = "gnu")))] - track_sid: TrackSid, - }, - TrackPublished { - publication: RemoteTrackPublication, - participant: RemoteParticipant, - }, - TrackUnpublished { - publication: RemoteTrackPublication, - participant: RemoteParticipant, - }, - TrackMuted { - participant: Participant, - publication: TrackPublication, - }, - TrackUnmuted { - participant: Participant, - publication: TrackPublication, - }, - RoomMetadataChanged { - old_metadata: String, - metadata: String, - }, - ParticipantMetadataChanged { - participant: Participant, - old_metadata: String, - metadata: String, - }, - ParticipantNameChanged { - participant: Participant, - old_name: String, - name: String, - }, - ActiveSpeakersChanged { - speakers: Vec, - }, - #[cfg(not(all(target_os = "windows", target_env = "gnu")))] - ConnectionStateChanged(ConnectionState), - Connected { - participants_with_tracks: Vec<(RemoteParticipant, Vec)>, - }, - #[cfg(not(all(target_os = "windows", target_env = "gnu")))] - Disconnected { - reason: DisconnectReason, - }, - Reconnecting, - Reconnected, -} - -#[cfg(not(all(target_os = "windows", target_env = "gnu")))] #[async_trait] impl livekit_api::Client for TestApiClient { fn url(&self) -> &str { @@ -700,25 +643,21 @@ impl livekit_api::Client for TestApiClient { } } -struct RoomState { - url: String, - token: String, - #[cfg(not(all(target_os = "windows", target_env = "gnu")))] - local_identity: ParticipantIdentity, - #[cfg(not(all(target_os = "windows", target_env = "gnu")))] - connection_state: ConnectionState, - #[cfg(not(all(target_os = "windows", target_env = "gnu")))] - paused_audio_tracks: HashSet, - updates_tx: mpsc::Sender, +pub(crate) struct RoomState { + pub(crate) url: String, + pub(crate) token: String, + pub(crate) local_identity: ParticipantIdentity, + pub(crate) connection_state: ConnectionState, + pub(crate) paused_audio_tracks: HashSet, + pub(crate) updates_tx: mpsc::Sender, } #[derive(Clone, Debug)] -pub struct Room(Arc>); +pub struct Room(pub(crate) Arc>); #[derive(Clone, Debug)] pub(crate) struct WeakRoom(Weak>); -#[cfg(not(all(target_os = "windows", target_env = "gnu")))] impl std::fmt::Debug for RoomState { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("Room") @@ -731,19 +670,8 @@ impl std::fmt::Debug for RoomState { } } -#[cfg(all(target_os = "windows", target_env = "gnu"))] -impl std::fmt::Debug for RoomState { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("Room") - .field("url", &self.url) - .field("token", &self.token) - .finish() - } -} - -#[cfg(not(all(target_os = "windows", target_env = "gnu")))] impl Room { - fn downgrade(&self) -> WeakRoom { + pub(crate) fn downgrade(&self) -> WeakRoom { WeakRoom(Arc::downgrade(&self.0)) } @@ -760,9 +688,9 @@ impl Room { } pub async fn connect( - url: &str, - token: &str, - _options: RoomOptions, + url: String, + token: String, + _cx: &mut AsyncApp, ) -> Result<(Self, mpsc::Receiver)> { let server = TestServer::get(&url)?; let (updates_tx, updates_rx) = mpsc::channel(1024); @@ -794,16 +722,34 @@ impl Room { .unwrap() } - fn test_server(&self) -> Arc { + pub(crate) fn test_server(&self) -> Arc { TestServer::get(&self.0.lock().url).unwrap() } - fn token(&self) -> String { + pub(crate) fn token(&self) -> String { self.0.lock().token.clone() } + + pub fn play_remote_audio_track( + &self, + _track: &RemoteAudioTrack, + _cx: &App, + ) -> anyhow::Result { + Ok(AudioStream {}) + } + + pub async fn unpublish_local_track(&self, sid: TrackSid, cx: &mut AsyncApp) -> Result<()> { + self.local_participant().unpublish_track(sid, cx).await + } + + pub async fn publish_local_microphone_track( + &self, + cx: &mut AsyncApp, + ) -> Result<(LocalTrackPublication, AudioStream)> { + self.local_participant().publish_microphone_track(cx).await + } } -#[cfg(not(all(target_os = "windows", target_env = "gnu")))] impl Drop for RoomState { fn drop(&mut self) { if self.connection_state == ConnectionState::Connected { @@ -819,7 +765,7 @@ impl Drop for RoomState { } impl WeakRoom { - fn upgrade(&self) -> Option { + pub(crate) fn upgrade(&self) -> Option { self.0.upgrade().map(Room) } } diff --git a/crates/livekit_client/src/test/track.rs b/crates/livekit_client/src/test/track.rs deleted file mode 100644 index 93984dea4a..0000000000 --- a/crates/livekit_client/src/test/track.rs +++ /dev/null @@ -1,201 +0,0 @@ -use super::*; -#[cfg(not(all(target_os = "windows", target_env = "gnu")))] -use webrtc::{audio_source::RtcAudioSource, video_source::RtcVideoSource}; - -#[cfg(not(all(target_os = "windows", target_env = "gnu")))] -pub use livekit::track::{TrackKind, TrackSource}; - -#[derive(Clone, Debug)] -pub enum LocalTrack { - Audio(LocalAudioTrack), - Video(LocalVideoTrack), -} - -#[derive(Clone, Debug)] -pub enum RemoteTrack { - Audio(RemoteAudioTrack), - Video(RemoteVideoTrack), -} - -#[derive(Clone, Debug)] -pub struct LocalVideoTrack {} - -#[derive(Clone, Debug)] -pub struct LocalAudioTrack {} - -#[derive(Clone, Debug)] -pub struct RemoteVideoTrack { - #[cfg(not(all(target_os = "windows", target_env = "gnu")))] - pub(super) server_track: Arc, - pub(super) _room: WeakRoom, -} - -#[derive(Clone, Debug)] -pub struct RemoteAudioTrack { - #[cfg(not(all(target_os = "windows", target_env = "gnu")))] - pub(super) server_track: Arc, - pub(super) room: WeakRoom, -} - -pub enum RtcTrack { - Audio(RtcAudioTrack), - Video(RtcVideoTrack), -} - -pub struct RtcAudioTrack { - #[cfg(not(all(target_os = "windows", target_env = "gnu")))] - pub(super) server_track: Arc, - pub(super) room: WeakRoom, -} - -pub struct RtcVideoTrack { - #[cfg(not(all(target_os = "windows", target_env = "gnu")))] - pub(super) _server_track: Arc, -} - -#[cfg(not(all(target_os = "windows", target_env = "gnu")))] -impl RemoteTrack { - pub fn sid(&self) -> TrackSid { - match self { - RemoteTrack::Audio(track) => track.sid(), - RemoteTrack::Video(track) => track.sid(), - } - } - - pub fn kind(&self) -> TrackKind { - match self { - RemoteTrack::Audio(_) => TrackKind::Audio, - RemoteTrack::Video(_) => TrackKind::Video, - } - } - - pub fn publisher_id(&self) -> ParticipantIdentity { - match self { - RemoteTrack::Audio(track) => track.publisher_id(), - RemoteTrack::Video(track) => track.publisher_id(), - } - } - - pub fn rtc_track(&self) -> RtcTrack { - match self { - RemoteTrack::Audio(track) => RtcTrack::Audio(track.rtc_track()), - RemoteTrack::Video(track) => RtcTrack::Video(track.rtc_track()), - } - } -} - -#[cfg(not(all(target_os = "windows", target_env = "gnu")))] -impl LocalVideoTrack { - pub fn create_video_track(_name: &str, _source: RtcVideoSource) -> Self { - Self {} - } -} - -#[cfg(not(all(target_os = "windows", target_env = "gnu")))] -impl LocalAudioTrack { - pub fn create_audio_track(_name: &str, _source: RtcAudioSource) -> Self { - Self {} - } -} - -#[cfg(not(all(target_os = "windows", target_env = "gnu")))] -impl RemoteAudioTrack { - pub fn sid(&self) -> TrackSid { - self.server_track.sid.clone() - } - - pub fn publisher_id(&self) -> ParticipantIdentity { - self.server_track.publisher_id.clone() - } - - pub fn start(&self) { - if let Some(room) = self.room.upgrade() { - room.0 - .lock() - .paused_audio_tracks - .remove(&self.server_track.sid); - } - } - - pub fn stop(&self) { - if let Some(room) = self.room.upgrade() { - room.0 - .lock() - .paused_audio_tracks - .insert(self.server_track.sid.clone()); - } - } - - pub fn rtc_track(&self) -> RtcAudioTrack { - RtcAudioTrack { - server_track: self.server_track.clone(), - room: self.room.clone(), - } - } -} - -#[cfg(not(all(target_os = "windows", target_env = "gnu")))] -impl RemoteVideoTrack { - pub fn sid(&self) -> TrackSid { - self.server_track.sid.clone() - } - - pub fn publisher_id(&self) -> ParticipantIdentity { - self.server_track.publisher_id.clone() - } - - pub fn rtc_track(&self) -> RtcVideoTrack { - RtcVideoTrack { - _server_track: self.server_track.clone(), - } - } -} - -#[cfg(not(all(target_os = "windows", target_env = "gnu")))] -impl RtcTrack { - pub fn enabled(&self) -> bool { - match self { - RtcTrack::Audio(track) => track.enabled(), - RtcTrack::Video(track) => track.enabled(), - } - } - - pub fn set_enabled(&self, enabled: bool) { - match self { - RtcTrack::Audio(track) => track.set_enabled(enabled), - RtcTrack::Video(_) => {} - } - } -} - -#[cfg(not(all(target_os = "windows", target_env = "gnu")))] -impl RtcAudioTrack { - pub fn set_enabled(&self, enabled: bool) { - if let Some(room) = self.room.upgrade() { - let paused_audio_tracks = &mut room.0.lock().paused_audio_tracks; - if enabled { - paused_audio_tracks.remove(&self.server_track.sid); - } else { - paused_audio_tracks.insert(self.server_track.sid.clone()); - } - } - } - - pub fn enabled(&self) -> bool { - if let Some(room) = self.room.upgrade() { - !room - .0 - .lock() - .paused_audio_tracks - .contains(&self.server_track.sid) - } else { - false - } - } -} - -impl RtcVideoTrack { - pub fn enabled(&self) -> bool { - true - } -} diff --git a/crates/livekit_client/src/test/webrtc.rs b/crates/livekit_client/src/test/webrtc.rs deleted file mode 100644 index 6ac06e0484..0000000000 --- a/crates/livekit_client/src/test/webrtc.rs +++ /dev/null @@ -1,136 +0,0 @@ -use super::track::{RtcAudioTrack, RtcVideoTrack}; -use futures::Stream; -use livekit::webrtc as real; -use std::{ - pin::Pin, - task::{Context, Poll}, -}; - -pub mod video_stream { - use super::*; - - pub mod native { - use super::*; - use real::video_frame::BoxVideoFrame; - - pub struct NativeVideoStream { - pub track: RtcVideoTrack, - } - - impl NativeVideoStream { - pub fn new(track: RtcVideoTrack) -> Self { - Self { track } - } - } - - impl Stream for NativeVideoStream { - type Item = BoxVideoFrame; - - fn poll_next(self: Pin<&mut Self>, _cx: &mut Context) -> Poll> { - Poll::Pending - } - } - } -} - -pub mod audio_stream { - use super::*; - - pub mod native { - use super::*; - use real::audio_frame::AudioFrame; - - pub struct NativeAudioStream { - pub track: RtcAudioTrack, - } - - impl NativeAudioStream { - pub fn new(track: RtcAudioTrack, _sample_rate: i32, _num_channels: i32) -> Self { - Self { track } - } - } - - impl Stream for NativeAudioStream { - type Item = AudioFrame<'static>; - - fn poll_next(self: Pin<&mut Self>, _cx: &mut Context) -> Poll> { - Poll::Pending - } - } - } -} - -pub mod audio_source { - use super::*; - - pub use real::audio_source::AudioSourceOptions; - - pub mod native { - use std::sync::Arc; - - use super::*; - use real::{audio_frame::AudioFrame, RtcError}; - - #[derive(Clone)] - pub struct NativeAudioSource { - pub options: Arc, - pub sample_rate: u32, - pub num_channels: u32, - } - - impl NativeAudioSource { - pub fn new( - options: AudioSourceOptions, - sample_rate: u32, - num_channels: u32, - _queue_size_ms: u32, - ) -> Self { - Self { - options: Arc::new(options), - sample_rate, - num_channels, - } - } - - pub async fn capture_frame(&self, _frame: &AudioFrame<'_>) -> Result<(), RtcError> { - Ok(()) - } - } - } - - pub enum RtcAudioSource { - Native(native::NativeAudioSource), - } -} - -pub use livekit::webrtc::audio_frame; -pub use livekit::webrtc::video_frame; - -pub mod video_source { - use super::*; - pub use real::video_source::VideoResolution; - - pub struct RTCVideoSource; - - pub mod native { - use super::*; - use real::video_frame::{VideoBuffer, VideoFrame}; - - #[derive(Clone)] - pub struct NativeVideoSource { - pub resolution: VideoResolution, - } - - impl NativeVideoSource { - pub fn new(resolution: super::VideoResolution) -> Self { - Self { resolution } - } - - pub fn capture_frame>(&self, _frame: &VideoFrame) {} - } - } - - pub enum RtcVideoSource { - Native(native::NativeVideoSource), - } -} diff --git a/crates/livekit_client_macos/.cargo/config.toml b/crates/livekit_client_macos/.cargo/config.toml deleted file mode 100644 index 77f7c9dd6c..0000000000 --- a/crates/livekit_client_macos/.cargo/config.toml +++ /dev/null @@ -1,2 +0,0 @@ -[livekit_client_test] -rustflags = ["-C", "link-args=-ObjC"] diff --git a/crates/livekit_client_macos/Cargo.toml b/crates/livekit_client_macos/Cargo.toml deleted file mode 100644 index 78d3ad1068..0000000000 --- a/crates/livekit_client_macos/Cargo.toml +++ /dev/null @@ -1,67 +0,0 @@ -[package] -name = "livekit_client_macos" -version = "0.1.0" -edition.workspace = true -description = "Bindings to LiveKit Swift client SDK" -publish.workspace = true -license = "GPL-3.0-or-later" - -[lints] -workspace = true - -[lib] -path = "src/livekit_client.rs" -doctest = false - -[[example]] -name = "test_app_macos" - -[features] -no-webrtc = [] -test-support = [ - "async-trait", - "collections/test-support", - "gpui/test-support", - "livekit_api", - "nanoid", -] - -[dependencies] -anyhow.workspace = true -async-broadcast = "0.7" -async-trait = { workspace = true, optional = true } -collections = { workspace = true, optional = true } -futures.workspace = true -gpui = { workspace = true, optional = true } -livekit_api = { workspace = true, optional = true } -log.workspace = true -media.workspace = true -nanoid = { workspace = true, optional = true} -parking_lot.workspace = true -postage.workspace = true - -[target.'cfg(target_os = "macos")'.dependencies] -core-foundation.workspace = true - -[target.'cfg(all(not(target_os = "macos")))'.dependencies] -async-trait.workspace = true -collections.workspace = true -gpui.workspace = true -livekit_api.workspace = true -nanoid.workspace = true - -[dev-dependencies] -async-trait.workspace = true -collections = { workspace = true, features = ["test-support"] } -gpui = { workspace = true, features = ["test-support"] } -livekit_api.workspace = true -nanoid.workspace = true -sha2.workspace = true -simplelog.workspace = true - -[build-dependencies] -serde.workspace = true -serde_json.workspace = true - -[package.metadata.cargo-machete] -ignored = ["serde_json"] diff --git a/crates/livekit_client_macos/LICENSE-GPL b/crates/livekit_client_macos/LICENSE-GPL deleted file mode 120000 index 89e542f750..0000000000 --- a/crates/livekit_client_macos/LICENSE-GPL +++ /dev/null @@ -1 +0,0 @@ -../../LICENSE-GPL \ No newline at end of file diff --git a/crates/livekit_client_macos/LiveKitBridge/Package.resolved b/crates/livekit_client_macos/LiveKitBridge/Package.resolved deleted file mode 100644 index c84933e5c1..0000000000 --- a/crates/livekit_client_macos/LiveKitBridge/Package.resolved +++ /dev/null @@ -1,52 +0,0 @@ -{ - "object": { - "pins": [ - { - "package": "LiveKit", - "repositoryURL": "https://github.com/livekit/client-sdk-swift.git", - "state": { - "branch": null, - "revision": "8cde9e66ce9b470c3a743f5c72784f57c5a6d0c3", - "version": "1.1.6" - } - }, - { - "package": "Promises", - "repositoryURL": "https://github.com/google/promises.git", - "state": { - "branch": null, - "revision": "ec957ccddbcc710ccc64c9dcbd4c7006fcf8b73a", - "version": "2.2.0" - } - }, - { - "package": "WebRTC", - "repositoryURL": "https://github.com/webrtc-sdk/Specs.git", - "state": { - "branch": null, - "revision": "4fa8d6d647fc759cdd0265fd413d2f28ea2e0e08", - "version": "114.5735.8" - } - }, - { - "package": "swift-log", - "repositoryURL": "https://github.com/apple/swift-log.git", - "state": { - "branch": null, - "revision": "32e8d724467f8fe623624570367e3d50c5638e46", - "version": "1.5.2" - } - }, - { - "package": "SwiftProtobuf", - "repositoryURL": "https://github.com/apple/swift-protobuf.git", - "state": { - "branch": null, - "revision": "ce20dc083ee485524b802669890291c0d8090170", - "version": "1.22.1" - } - } - ] - }, - "version": 1 -} diff --git a/crates/livekit_client_macos/LiveKitBridge/Package.swift b/crates/livekit_client_macos/LiveKitBridge/Package.swift deleted file mode 100644 index a2a5b3eb75..0000000000 --- a/crates/livekit_client_macos/LiveKitBridge/Package.swift +++ /dev/null @@ -1,27 +0,0 @@ -// swift-tools-version: 5.5 - -import PackageDescription - -let package = Package( - name: "LiveKitBridge", - platforms: [ - .macOS(.v10_15) - ], - products: [ - // Products define the executables and libraries a package produces, and make them visible to other packages. - .library( - name: "LiveKitBridge", - type: .static, - targets: ["LiveKitBridge"]) - ], - dependencies: [ - .package(url: "https://github.com/livekit/client-sdk-swift.git", .exact("1.1.6")) - ], - targets: [ - // Targets are the basic building blocks of a package. A target can define a module or a test suite. - // Targets can depend on other targets in this package, and on products in packages this package depends on. - .target( - name: "LiveKitBridge", - dependencies: [.product(name: "LiveKit", package: "client-sdk-swift")]) - ] -) diff --git a/crates/livekit_client_macos/LiveKitBridge/README.md b/crates/livekit_client_macos/LiveKitBridge/README.md deleted file mode 100644 index b982c67286..0000000000 --- a/crates/livekit_client_macos/LiveKitBridge/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# LiveKitBridge - -A description of this package. diff --git a/crates/livekit_client_macos/LiveKitBridge/Sources/LiveKitBridge/LiveKitBridge.swift b/crates/livekit_client_macos/LiveKitBridge/Sources/LiveKitBridge/LiveKitBridge.swift deleted file mode 100644 index 7468c08791..0000000000 --- a/crates/livekit_client_macos/LiveKitBridge/Sources/LiveKitBridge/LiveKitBridge.swift +++ /dev/null @@ -1,383 +0,0 @@ -import Foundation -import LiveKit -import WebRTC -import ScreenCaptureKit - -class LKRoomDelegate: RoomDelegate { - var data: UnsafeRawPointer - var onDidDisconnect: @convention(c) (UnsafeRawPointer) -> Void - var onDidSubscribeToRemoteAudioTrack: @convention(c) (UnsafeRawPointer, CFString, CFString, UnsafeRawPointer, UnsafeRawPointer) -> Void - var onDidUnsubscribeFromRemoteAudioTrack: @convention(c) (UnsafeRawPointer, CFString, CFString) -> Void - var onMuteChangedFromRemoteAudioTrack: @convention(c) (UnsafeRawPointer, CFString, Bool) -> Void - var onActiveSpeakersChanged: @convention(c) (UnsafeRawPointer, CFArray) -> Void - var onDidSubscribeToRemoteVideoTrack: @convention(c) (UnsafeRawPointer, CFString, CFString, UnsafeRawPointer) -> Void - var onDidUnsubscribeFromRemoteVideoTrack: @convention(c) (UnsafeRawPointer, CFString, CFString) -> Void - var onDidPublishOrUnpublishLocalAudioTrack: @convention(c) (UnsafeRawPointer, UnsafeRawPointer, Bool) -> Void - var onDidPublishOrUnpublishLocalVideoTrack: @convention(c) (UnsafeRawPointer, UnsafeRawPointer, Bool) -> Void - - init( - data: UnsafeRawPointer, - onDidDisconnect: @escaping @convention(c) (UnsafeRawPointer) -> Void, - onDidSubscribeToRemoteAudioTrack: @escaping @convention(c) (UnsafeRawPointer, CFString, CFString, UnsafeRawPointer, UnsafeRawPointer) -> Void, - onDidUnsubscribeFromRemoteAudioTrack: @escaping @convention(c) (UnsafeRawPointer, CFString, CFString) -> Void, - onMuteChangedFromRemoteAudioTrack: @escaping @convention(c) (UnsafeRawPointer, CFString, Bool) -> Void, - onActiveSpeakersChanged: @convention(c) (UnsafeRawPointer, CFArray) -> Void, - onDidSubscribeToRemoteVideoTrack: @escaping @convention(c) (UnsafeRawPointer, CFString, CFString, UnsafeRawPointer) -> Void, - onDidUnsubscribeFromRemoteVideoTrack: @escaping @convention(c) (UnsafeRawPointer, CFString, CFString) -> Void, - onDidPublishOrUnpublishLocalAudioTrack: @escaping @convention(c) (UnsafeRawPointer, UnsafeRawPointer, Bool) -> Void, - onDidPublishOrUnpublishLocalVideoTrack: @escaping @convention(c) (UnsafeRawPointer, UnsafeRawPointer, Bool) -> Void - ) - { - self.data = data - self.onDidDisconnect = onDidDisconnect - self.onDidSubscribeToRemoteAudioTrack = onDidSubscribeToRemoteAudioTrack - self.onDidUnsubscribeFromRemoteAudioTrack = onDidUnsubscribeFromRemoteAudioTrack - self.onDidSubscribeToRemoteVideoTrack = onDidSubscribeToRemoteVideoTrack - self.onDidUnsubscribeFromRemoteVideoTrack = onDidUnsubscribeFromRemoteVideoTrack - self.onMuteChangedFromRemoteAudioTrack = onMuteChangedFromRemoteAudioTrack - self.onActiveSpeakersChanged = onActiveSpeakersChanged - self.onDidPublishOrUnpublishLocalAudioTrack = onDidPublishOrUnpublishLocalAudioTrack - self.onDidPublishOrUnpublishLocalVideoTrack = onDidPublishOrUnpublishLocalVideoTrack - } - - func room(_ room: Room, didUpdate connectionState: ConnectionState, oldValue: ConnectionState) { - if connectionState.isDisconnected { - self.onDidDisconnect(self.data) - } - } - - func room(_ room: Room, participant: RemoteParticipant, didSubscribe publication: RemoteTrackPublication, track: Track) { - if track.kind == .video { - self.onDidSubscribeToRemoteVideoTrack(self.data, participant.identity as CFString, track.sid! as CFString, Unmanaged.passUnretained(track).toOpaque()) - } else if track.kind == .audio { - self.onDidSubscribeToRemoteAudioTrack(self.data, participant.identity as CFString, track.sid! as CFString, Unmanaged.passUnretained(track).toOpaque(), Unmanaged.passUnretained(publication).toOpaque()) - } - } - - func room(_ room: Room, participant: Participant, didUpdate publication: TrackPublication, muted: Bool) { - if publication.kind == .audio { - self.onMuteChangedFromRemoteAudioTrack(self.data, publication.sid as CFString, muted) - } - } - - func room(_ room: Room, didUpdate speakers: [Participant]) { - guard let speaker_ids = speakers.compactMap({ $0.identity as CFString }) as CFArray? else { return } - self.onActiveSpeakersChanged(self.data, speaker_ids) - } - - func room(_ room: Room, participant: RemoteParticipant, didUnsubscribe publication: RemoteTrackPublication, track: Track) { - if track.kind == .video { - self.onDidUnsubscribeFromRemoteVideoTrack(self.data, participant.identity as CFString, track.sid! as CFString) - } else if track.kind == .audio { - self.onDidUnsubscribeFromRemoteAudioTrack(self.data, participant.identity as CFString, track.sid! as CFString) - } - } - - func room(_ room: Room, localParticipant: LocalParticipant, didPublish publication: LocalTrackPublication) { - if publication.kind == .video { - self.onDidPublishOrUnpublishLocalVideoTrack(self.data, Unmanaged.passUnretained(publication).toOpaque(), true) - } else if publication.kind == .audio { - self.onDidPublishOrUnpublishLocalAudioTrack(self.data, Unmanaged.passUnretained(publication).toOpaque(), true) - } - } - - func room(_ room: Room, localParticipant: LocalParticipant, didUnpublish publication: LocalTrackPublication) { - if publication.kind == .video { - self.onDidPublishOrUnpublishLocalVideoTrack(self.data, Unmanaged.passUnretained(publication).toOpaque(), false) - } else if publication.kind == .audio { - self.onDidPublishOrUnpublishLocalAudioTrack(self.data, Unmanaged.passUnretained(publication).toOpaque(), false) - } - } -} - -class LKVideoRenderer: NSObject, VideoRenderer { - var data: UnsafeRawPointer - var onFrame: @convention(c) (UnsafeRawPointer, CVPixelBuffer) -> Bool - var onDrop: @convention(c) (UnsafeRawPointer) -> Void - var adaptiveStreamIsEnabled: Bool = false - var adaptiveStreamSize: CGSize = .zero - weak var track: VideoTrack? - - init(data: UnsafeRawPointer, onFrame: @escaping @convention(c) (UnsafeRawPointer, CVPixelBuffer) -> Bool, onDrop: @escaping @convention(c) (UnsafeRawPointer) -> Void) { - self.data = data - self.onFrame = onFrame - self.onDrop = onDrop - } - - deinit { - self.onDrop(self.data) - } - - func setSize(_ size: CGSize) { - } - - func renderFrame(_ frame: RTCVideoFrame?) { - let buffer = frame?.buffer as? RTCCVPixelBuffer - if let pixelBuffer = buffer?.pixelBuffer { - if !self.onFrame(self.data, pixelBuffer) { - DispatchQueue.main.async { - self.track?.remove(videoRenderer: self) - } - } - } - } -} - -@_cdecl("LKRoomDelegateCreate") -public func LKRoomDelegateCreate( - data: UnsafeRawPointer, - onDidDisconnect: @escaping @convention(c) (UnsafeRawPointer) -> Void, - onDidSubscribeToRemoteAudioTrack: @escaping @convention(c) (UnsafeRawPointer, CFString, CFString, UnsafeRawPointer, UnsafeRawPointer) -> Void, - onDidUnsubscribeFromRemoteAudioTrack: @escaping @convention(c) (UnsafeRawPointer, CFString, CFString) -> Void, - onMuteChangedFromRemoteAudioTrack: @escaping @convention(c) (UnsafeRawPointer, CFString, Bool) -> Void, - onActiveSpeakerChanged: @escaping @convention(c) (UnsafeRawPointer, CFArray) -> Void, - onDidSubscribeToRemoteVideoTrack: @escaping @convention(c) (UnsafeRawPointer, CFString, CFString, UnsafeRawPointer) -> Void, - onDidUnsubscribeFromRemoteVideoTrack: @escaping @convention(c) (UnsafeRawPointer, CFString, CFString) -> Void, - onDidPublishOrUnpublishLocalAudioTrack: @escaping @convention(c) (UnsafeRawPointer, UnsafeRawPointer, Bool) -> Void, - onDidPublishOrUnpublishLocalVideoTrack: @escaping @convention(c) (UnsafeRawPointer, UnsafeRawPointer, Bool) -> Void -) -> UnsafeMutableRawPointer { - let delegate = LKRoomDelegate( - data: data, - onDidDisconnect: onDidDisconnect, - onDidSubscribeToRemoteAudioTrack: onDidSubscribeToRemoteAudioTrack, - onDidUnsubscribeFromRemoteAudioTrack: onDidUnsubscribeFromRemoteAudioTrack, - onMuteChangedFromRemoteAudioTrack: onMuteChangedFromRemoteAudioTrack, - onActiveSpeakersChanged: onActiveSpeakerChanged, - onDidSubscribeToRemoteVideoTrack: onDidSubscribeToRemoteVideoTrack, - onDidUnsubscribeFromRemoteVideoTrack: onDidUnsubscribeFromRemoteVideoTrack, - onDidPublishOrUnpublishLocalAudioTrack: onDidPublishOrUnpublishLocalAudioTrack, - onDidPublishOrUnpublishLocalVideoTrack: onDidPublishOrUnpublishLocalVideoTrack - ) - return Unmanaged.passRetained(delegate).toOpaque() -} - -@_cdecl("LKRoomCreate") -public func LKRoomCreate(delegate: UnsafeRawPointer) -> UnsafeMutableRawPointer { - let delegate = Unmanaged.fromOpaque(delegate).takeUnretainedValue() - return Unmanaged.passRetained(Room(delegate: delegate)).toOpaque() -} - -@_cdecl("LKRoomConnect") -public func LKRoomConnect(room: UnsafeRawPointer, url: CFString, token: CFString, callback: @escaping @convention(c) (UnsafeRawPointer, CFString?) -> Void, callback_data: UnsafeRawPointer) { - let room = Unmanaged.fromOpaque(room).takeUnretainedValue() - - room.connect(url as String, token as String).then { _ in - callback(callback_data, UnsafeRawPointer(nil) as! CFString?) - }.catch { error in - callback(callback_data, error.localizedDescription as CFString) - } -} - -@_cdecl("LKRoomDisconnect") -public func LKRoomDisconnect(room: UnsafeRawPointer) { - let room = Unmanaged.fromOpaque(room).takeUnretainedValue() - room.disconnect() -} - -@_cdecl("LKRoomPublishVideoTrack") -public func LKRoomPublishVideoTrack(room: UnsafeRawPointer, track: UnsafeRawPointer, callback: @escaping @convention(c) (UnsafeRawPointer, UnsafeMutableRawPointer?, CFString?) -> Void, callback_data: UnsafeRawPointer) { - let room = Unmanaged.fromOpaque(room).takeUnretainedValue() - let track = Unmanaged.fromOpaque(track).takeUnretainedValue() - room.localParticipant?.publishVideoTrack(track: track).then { publication in - callback(callback_data, Unmanaged.passRetained(publication).toOpaque(), nil) - }.catch { error in - callback(callback_data, nil, error.localizedDescription as CFString) - } -} - -@_cdecl("LKRoomPublishAudioTrack") -public func LKRoomPublishAudioTrack(room: UnsafeRawPointer, track: UnsafeRawPointer, callback: @escaping @convention(c) (UnsafeRawPointer, UnsafeMutableRawPointer?, CFString?) -> Void, callback_data: UnsafeRawPointer) { - let room = Unmanaged.fromOpaque(room).takeUnretainedValue() - let track = Unmanaged.fromOpaque(track).takeUnretainedValue() - room.localParticipant?.publishAudioTrack(track: track).then { publication in - callback(callback_data, Unmanaged.passRetained(publication).toOpaque(), nil) - }.catch { error in - callback(callback_data, nil, error.localizedDescription as CFString) - } -} - - -@_cdecl("LKRoomUnpublishTrack") -public func LKRoomUnpublishTrack(room: UnsafeRawPointer, publication: UnsafeRawPointer) { - let room = Unmanaged.fromOpaque(room).takeUnretainedValue() - let publication = Unmanaged.fromOpaque(publication).takeUnretainedValue() - let _ = room.localParticipant?.unpublish(publication: publication) -} - -@_cdecl("LKRoomAudioTracksForRemoteParticipant") -public func LKRoomAudioTracksForRemoteParticipant(room: UnsafeRawPointer, participantId: CFString) -> CFArray? { - let room = Unmanaged.fromOpaque(room).takeUnretainedValue() - - for (_, participant) in room.remoteParticipants { - if participant.identity == participantId as String { - return participant.audioTracks.compactMap { $0.track as? RemoteAudioTrack } as CFArray? - } - } - - return nil; -} - -@_cdecl("LKRoomAudioTrackPublicationsForRemoteParticipant") -public func LKRoomAudioTrackPublicationsForRemoteParticipant(room: UnsafeRawPointer, participantId: CFString) -> CFArray? { - let room = Unmanaged.fromOpaque(room).takeUnretainedValue() - - for (_, participant) in room.remoteParticipants { - if participant.identity == participantId as String { - return participant.audioTracks.compactMap { $0 as? RemoteTrackPublication } as CFArray? - } - } - - return nil; -} - -@_cdecl("LKRoomVideoTracksForRemoteParticipant") -public func LKRoomVideoTracksForRemoteParticipant(room: UnsafeRawPointer, participantId: CFString) -> CFArray? { - let room = Unmanaged.fromOpaque(room).takeUnretainedValue() - - for (_, participant) in room.remoteParticipants { - if participant.identity == participantId as String { - return participant.videoTracks.compactMap { $0.track as? RemoteVideoTrack } as CFArray? - } - } - - return nil; -} - -@_cdecl("LKLocalAudioTrackCreateTrack") -public func LKLocalAudioTrackCreateTrack() -> UnsafeMutableRawPointer { - let track = LocalAudioTrack.createTrack(options: AudioCaptureOptions( - echoCancellation: true, - noiseSuppression: true - )) - - return Unmanaged.passRetained(track).toOpaque() -} - - -@_cdecl("LKCreateScreenShareTrackForDisplay") -public func LKCreateScreenShareTrackForDisplay(display: UnsafeMutableRawPointer) -> UnsafeMutableRawPointer { - let display = Unmanaged.fromOpaque(display).takeUnretainedValue() - let track = LocalVideoTrack.createMacOSScreenShareTrack(source: display, preferredMethod: .legacy) - return Unmanaged.passRetained(track).toOpaque() -} - -@_cdecl("LKVideoRendererCreate") -public func LKVideoRendererCreate(data: UnsafeRawPointer, onFrame: @escaping @convention(c) (UnsafeRawPointer, CVPixelBuffer) -> Bool, onDrop: @escaping @convention(c) (UnsafeRawPointer) -> Void) -> UnsafeMutableRawPointer { - Unmanaged.passRetained(LKVideoRenderer(data: data, onFrame: onFrame, onDrop: onDrop)).toOpaque() -} - -@_cdecl("LKVideoTrackAddRenderer") -public func LKVideoTrackAddRenderer(track: UnsafeRawPointer, renderer: UnsafeRawPointer) { - let track = Unmanaged.fromOpaque(track).takeUnretainedValue() as! VideoTrack - let renderer = Unmanaged.fromOpaque(renderer).takeRetainedValue() - renderer.track = track - track.add(videoRenderer: renderer) -} - -@_cdecl("LKRemoteVideoTrackGetSid") -public func LKRemoteVideoTrackGetSid(track: UnsafeRawPointer) -> CFString { - let track = Unmanaged.fromOpaque(track).takeUnretainedValue() - return track.sid! as CFString -} - -@_cdecl("LKRemoteAudioTrackGetSid") -public func LKRemoteAudioTrackGetSid(track: UnsafeRawPointer) -> CFString { - let track = Unmanaged.fromOpaque(track).takeUnretainedValue() - return track.sid! as CFString -} - -@_cdecl("LKRemoteAudioTrackStart") -public func LKRemoteAudioTrackStart(track: UnsafeRawPointer) { - let track = Unmanaged.fromOpaque(track).takeUnretainedValue() - track.start() -} - -@_cdecl("LKRemoteAudioTrackStop") -public func LKRemoteAudioTrackStop(track: UnsafeRawPointer) { - let track = Unmanaged.fromOpaque(track).takeUnretainedValue() - track.stop() -} - -@_cdecl("LKDisplaySources") -public func LKDisplaySources(data: UnsafeRawPointer, callback: @escaping @convention(c) (UnsafeRawPointer, CFArray?, CFString?) -> Void) { - MacOSScreenCapturer.sources(for: .display, includeCurrentApplication: false, preferredMethod: .legacy).then { displaySources in - callback(data, displaySources as CFArray, nil) - }.catch { error in - callback(data, nil, error.localizedDescription as CFString) - } -} - -@_cdecl("LKLocalTrackPublicationSetMute") -public func LKLocalTrackPublicationSetMute( - publication: UnsafeRawPointer, - muted: Bool, - on_complete: @escaping @convention(c) (UnsafeRawPointer, CFString?) -> Void, - callback_data: UnsafeRawPointer -) { - let publication = Unmanaged.fromOpaque(publication).takeUnretainedValue() - - if muted { - publication.mute().then { - on_complete(callback_data, nil) - }.catch { error in - on_complete(callback_data, error.localizedDescription as CFString) - } - } else { - publication.unmute().then { - on_complete(callback_data, nil) - }.catch { error in - on_complete(callback_data, error.localizedDescription as CFString) - } - } -} - -@_cdecl("LKLocalTrackPublicationIsMuted") -public func LKLocalTrackPublicationIsMuted( - publication: UnsafeRawPointer -) -> Bool { - let publication = Unmanaged.fromOpaque(publication).takeUnretainedValue() - return publication.muted -} - -@_cdecl("LKRemoteTrackPublicationSetEnabled") -public func LKRemoteTrackPublicationSetEnabled( - publication: UnsafeRawPointer, - enabled: Bool, - on_complete: @escaping @convention(c) (UnsafeRawPointer, CFString?) -> Void, - callback_data: UnsafeRawPointer -) { - let publication = Unmanaged.fromOpaque(publication).takeUnretainedValue() - - publication.set(enabled: enabled).then { - on_complete(callback_data, nil) - }.catch { error in - on_complete(callback_data, error.localizedDescription as CFString) - } -} - -@_cdecl("LKRemoteTrackPublicationIsMuted") -public func LKRemoteTrackPublicationIsMuted( - publication: UnsafeRawPointer -) -> Bool { - let publication = Unmanaged.fromOpaque(publication).takeUnretainedValue() - - return publication.muted -} - -@_cdecl("LKRemoteTrackPublicationGetSid") -public func LKRemoteTrackPublicationGetSid( - publication: UnsafeRawPointer -) -> CFString { - let publication = Unmanaged.fromOpaque(publication).takeUnretainedValue() - - return publication.sid as CFString -} - -@_cdecl("LKLocalTrackPublicationGetSid") -public func LKLocalTrackPublicationGetSid( - publication: UnsafeRawPointer -) -> CFString { - let publication = Unmanaged.fromOpaque(publication).takeUnretainedValue() - - return publication.sid as CFString -} diff --git a/crates/livekit_client_macos/build.rs b/crates/livekit_client_macos/build.rs deleted file mode 100644 index 2fdfd982bf..0000000000 --- a/crates/livekit_client_macos/build.rs +++ /dev/null @@ -1,185 +0,0 @@ -use serde::Deserialize; -use std::{ - env, - path::{Path, PathBuf}, - process::Command, -}; - -const SWIFT_PACKAGE_NAME: &str = "LiveKitBridge"; - -#[derive(Debug, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct SwiftTargetInfo { - pub triple: String, - pub unversioned_triple: String, - pub module_triple: String, - pub swift_runtime_compatibility_version: String, - #[serde(rename = "librariesRequireRPath")] - pub libraries_require_rpath: bool, -} - -#[derive(Debug, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct SwiftPaths { - pub runtime_library_paths: Vec, - pub runtime_library_import_paths: Vec, - pub runtime_resource_path: String, -} - -#[derive(Debug, Deserialize)] -pub struct SwiftTarget { - pub target: SwiftTargetInfo, - pub paths: SwiftPaths, -} - -const MACOS_TARGET_VERSION: &str = "10.15.7"; - -fn main() { - if cfg!(all( - target_os = "macos", - not(any(test, feature = "test-support", feature = "no-webrtc")), - )) { - let swift_target = get_swift_target(); - - build_bridge(&swift_target); - link_swift_stdlib(&swift_target); - link_webrtc_framework(&swift_target); - - // Register exported Objective-C selectors, protocols, etc when building example binaries. - println!("cargo:rustc-link-arg=-Wl,-ObjC"); - } -} - -fn build_bridge(swift_target: &SwiftTarget) { - println!("cargo:rerun-if-env-changed=MACOSX_DEPLOYMENT_TARGET"); - println!("cargo:rerun-if-changed={}/Sources", SWIFT_PACKAGE_NAME); - println!( - "cargo:rerun-if-changed={}/Package.swift", - SWIFT_PACKAGE_NAME - ); - println!( - "cargo:rerun-if-changed={}/Package.resolved", - SWIFT_PACKAGE_NAME - ); - - let swift_package_root = swift_package_root(); - let swift_target_folder = swift_target_folder(); - let swift_cache_folder = swift_cache_folder(); - if !Command::new("swift") - .arg("build") - .arg("--disable-automatic-resolution") - .args(["--configuration", &env::var("PROFILE").unwrap()]) - .args(["--triple", &swift_target.target.triple]) - .args(["--build-path".into(), swift_target_folder]) - .args(["--cache-path".into(), swift_cache_folder]) - .current_dir(&swift_package_root) - .status() - .unwrap() - .success() - { - panic!( - "Failed to compile swift package in {}", - swift_package_root.display() - ); - } - - println!( - "cargo:rustc-link-search=native={}", - swift_target.out_dir_path().display() - ); - println!("cargo:rustc-link-lib=static={}", SWIFT_PACKAGE_NAME); -} - -fn link_swift_stdlib(swift_target: &SwiftTarget) { - for path in &swift_target.paths.runtime_library_paths { - println!("cargo:rustc-link-search=native={}", path); - } -} - -fn link_webrtc_framework(swift_target: &SwiftTarget) { - let swift_out_dir_path = swift_target.out_dir_path(); - println!("cargo:rustc-link-lib=framework=WebRTC"); - println!( - "cargo:rustc-link-search=framework={}", - swift_out_dir_path.display() - ); - // Find WebRTC.framework as a sibling of the executable when running tests. - println!("cargo:rustc-link-arg=-Wl,-rpath,@executable_path"); - // Find WebRTC.framework in parent directory of the executable when running examples. - println!("cargo:rustc-link-arg=-Wl,-rpath,@executable_path/.."); - - let source_path = swift_out_dir_path.join("WebRTC.framework"); - let deps_dir_path = - PathBuf::from(env::var("OUT_DIR").unwrap()).join("../../../deps/WebRTC.framework"); - let target_dir_path = - PathBuf::from(env::var("OUT_DIR").unwrap()).join("../../../WebRTC.framework"); - copy_dir(&source_path, &deps_dir_path); - copy_dir(&source_path, &target_dir_path); -} - -fn get_swift_target() -> SwiftTarget { - let mut arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap(); - if arch == "aarch64" { - arch = "arm64".into(); - } - let target = format!("{}-apple-macosx{}", arch, MACOS_TARGET_VERSION); - - let swift_target_info_str = Command::new("swift") - .args(["-target", &target, "-print-target-info"]) - .output() - .unwrap() - .stdout; - - serde_json::from_slice(&swift_target_info_str).unwrap() -} - -fn swift_package_root() -> PathBuf { - env::current_dir().unwrap().join(SWIFT_PACKAGE_NAME) -} - -fn swift_target_folder() -> PathBuf { - let target = env::var("TARGET").unwrap(); - env::current_dir() - .unwrap() - .join(format!("../../target/{target}/{SWIFT_PACKAGE_NAME}_target")) -} - -fn swift_cache_folder() -> PathBuf { - let target = env::var("TARGET").unwrap(); - env::current_dir() - .unwrap() - .join(format!("../../target/{target}/{SWIFT_PACKAGE_NAME}_cache")) -} - -fn copy_dir(source: &Path, destination: &Path) { - assert!( - Command::new("rm") - .arg("-rf") - .arg(destination) - .status() - .unwrap() - .success(), - "could not remove {:?} before copying", - destination - ); - - assert!( - Command::new("cp") - .arg("-R") - .args([source, destination]) - .status() - .unwrap() - .success(), - "could not copy {:?} to {:?}", - source, - destination - ); -} - -impl SwiftTarget { - fn out_dir_path(&self) -> PathBuf { - swift_target_folder() - .join(&self.target.unversioned_triple) - .join(env::var("PROFILE").unwrap()) - } -} diff --git a/crates/livekit_client_macos/examples/test_app_macos.rs b/crates/livekit_client_macos/examples/test_app_macos.rs deleted file mode 100644 index df0e0a3769..0000000000 --- a/crates/livekit_client_macos/examples/test_app_macos.rs +++ /dev/null @@ -1,172 +0,0 @@ -use std::time::Duration; - -use futures::StreamExt; -use gpui::{actions, KeyBinding, Menu, MenuItem}; -use livekit_api::token::{self, VideoGrant}; -use livekit_client_macos::{LocalAudioTrack, LocalVideoTrack, Room, RoomUpdate}; -use log::LevelFilter; -use simplelog::SimpleLogger; - -actions!(livekit_client_macos, [Quit]); - -fn main() { - SimpleLogger::init(LevelFilter::Info, Default::default()).expect("could not initialize logger"); - - gpui::Application::new().run(|cx| { - #[cfg(any(test, feature = "test-support"))] - println!("USING TEST LIVEKIT"); - - #[cfg(not(any(test, feature = "test-support")))] - println!("USING REAL LIVEKIT"); - - cx.activate(true); - - cx.on_action(quit); - cx.bind_keys([KeyBinding::new("cmd-q", Quit, None)]); - - cx.set_menus(vec![Menu { - name: "Zed".into(), - items: vec![MenuItem::Action { - name: "Quit".into(), - action: Box::new(Quit), - os_action: None, - }], - }]); - - let live_kit_url = std::env::var("LIVE_KIT_URL").unwrap_or("http://localhost:7880".into()); - let live_kit_key = std::env::var("LIVE_KIT_KEY").unwrap_or("devkey".into()); - let live_kit_secret = std::env::var("LIVE_KIT_SECRET").unwrap_or("secret".into()); - - cx.spawn(async move |cx| { - let user_a_token = token::create( - &live_kit_key, - &live_kit_secret, - Some("test-participant-1"), - VideoGrant::to_join("test-room"), - ) - .unwrap(); - let room_a = Room::new(); - room_a.connect(&live_kit_url, &user_a_token).await.unwrap(); - - let user2_token = token::create( - &live_kit_key, - &live_kit_secret, - Some("test-participant-2"), - VideoGrant::to_join("test-room"), - ) - .unwrap(); - let room_b = Room::new(); - room_b.connect(&live_kit_url, &user2_token).await.unwrap(); - - let mut room_updates = room_b.updates(); - let audio_track = LocalAudioTrack::create(); - let audio_track_publication = room_a.publish_audio_track(audio_track).await.unwrap(); - - if let RoomUpdate::SubscribedToRemoteAudioTrack(track, _) = - room_updates.next().await.unwrap() - { - let remote_tracks = room_b.remote_audio_tracks("test-participant-1"); - assert_eq!(remote_tracks.len(), 1); - assert_eq!(remote_tracks[0].publisher_id(), "test-participant-1"); - assert_eq!(track.publisher_id(), "test-participant-1"); - } else { - panic!("unexpected message"); - } - - audio_track_publication.set_mute(true).await.unwrap(); - - println!("waiting for mute changed!"); - if let RoomUpdate::RemoteAudioTrackMuteChanged { track_id, muted } = - room_updates.next().await.unwrap() - { - let remote_tracks = room_b.remote_audio_tracks("test-participant-1"); - assert_eq!(remote_tracks[0].sid(), track_id); - assert!(muted); - } else { - panic!("unexpected message"); - } - - audio_track_publication.set_mute(false).await.unwrap(); - - if let RoomUpdate::RemoteAudioTrackMuteChanged { track_id, muted } = - room_updates.next().await.unwrap() - { - let remote_tracks = room_b.remote_audio_tracks("test-participant-1"); - assert_eq!(remote_tracks[0].sid(), track_id); - assert!(!muted); - } else { - panic!("unexpected message"); - } - - println!("Pausing for 5 seconds to test audio, make some noise!"); - let timer = cx.background_executor().timer(Duration::from_secs(5)); - timer.await; - let remote_audio_track = room_b - .remote_audio_tracks("test-participant-1") - .pop() - .unwrap(); - room_a.unpublish_track(audio_track_publication); - - // Clear out any active speakers changed messages - let mut next = room_updates.next().await.unwrap(); - while let RoomUpdate::ActiveSpeakersChanged { speakers } = next { - println!("Speakers changed: {:?}", speakers); - next = room_updates.next().await.unwrap(); - } - - if let RoomUpdate::UnsubscribedFromRemoteAudioTrack { - publisher_id, - track_id, - } = next - { - assert_eq!(publisher_id, "test-participant-1"); - assert_eq!(remote_audio_track.sid(), track_id); - assert_eq!(room_b.remote_audio_tracks("test-participant-1").len(), 0); - } else { - panic!("unexpected message"); - } - - let displays = room_a.display_sources().await.unwrap(); - let display = displays.into_iter().next().unwrap(); - - let local_video_track = LocalVideoTrack::screen_share_for_display(&display); - let local_video_track_publication = - room_a.publish_video_track(local_video_track).await.unwrap(); - - if let RoomUpdate::SubscribedToRemoteVideoTrack(track) = - room_updates.next().await.unwrap() - { - let remote_video_tracks = room_b.remote_video_tracks("test-participant-1"); - assert_eq!(remote_video_tracks.len(), 1); - assert_eq!(remote_video_tracks[0].publisher_id(), "test-participant-1"); - assert_eq!(track.publisher_id(), "test-participant-1"); - } else { - panic!("unexpected message"); - } - - let remote_video_track = room_b - .remote_video_tracks("test-participant-1") - .pop() - .unwrap(); - room_a.unpublish_track(local_video_track_publication); - if let RoomUpdate::UnsubscribedFromRemoteVideoTrack { - publisher_id, - track_id, - } = room_updates.next().await.unwrap() - { - assert_eq!(publisher_id, "test-participant-1"); - assert_eq!(remote_video_track.sid(), track_id); - assert_eq!(room_b.remote_video_tracks("test-participant-1").len(), 0); - } else { - panic!("unexpected message"); - } - - cx.update(|cx| cx.shutdown()).ok(); - }) - .detach(); - }); -} - -fn quit(_: &Quit, cx: &mut gpui::App) { - cx.quit(); -} diff --git a/crates/livekit_client_macos/src/livekit_client.rs b/crates/livekit_client_macos/src/livekit_client.rs deleted file mode 100644 index 4820a4eedb..0000000000 --- a/crates/livekit_client_macos/src/livekit_client.rs +++ /dev/null @@ -1,37 +0,0 @@ -#![allow(clippy::arc_with_non_send_sync)] - -use std::sync::Arc; - -#[cfg(all(target_os = "macos", not(any(test, feature = "test-support"))))] -pub mod prod; - -#[cfg(all(target_os = "macos", not(any(test, feature = "test-support"))))] -pub use prod::*; - -#[cfg(any(test, feature = "test-support", not(target_os = "macos")))] -pub mod test; - -#[cfg(any(test, feature = "test-support", not(target_os = "macos")))] -pub use test::*; - -pub type Sid = String; - -#[derive(Clone, Eq, PartialEq)] -pub enum ConnectionState { - Disconnected, - Connected { url: String, token: String }, -} - -#[derive(Clone)] -pub enum RoomUpdate { - ActiveSpeakersChanged { speakers: Vec }, - RemoteAudioTrackMuteChanged { track_id: Sid, muted: bool }, - SubscribedToRemoteVideoTrack(Arc), - SubscribedToRemoteAudioTrack(Arc, Arc), - UnsubscribedFromRemoteVideoTrack { publisher_id: Sid, track_id: Sid }, - UnsubscribedFromRemoteAudioTrack { publisher_id: Sid, track_id: Sid }, - LocalAudioTrackPublished { publication: LocalTrackPublication }, - LocalAudioTrackUnpublished { publication: LocalTrackPublication }, - LocalVideoTrackPublished { publication: LocalTrackPublication }, - LocalVideoTrackUnpublished { publication: LocalTrackPublication }, -} diff --git a/crates/livekit_client_macos/src/prod.rs b/crates/livekit_client_macos/src/prod.rs deleted file mode 100644 index cc577b9574..0000000000 --- a/crates/livekit_client_macos/src/prod.rs +++ /dev/null @@ -1,981 +0,0 @@ -use crate::{ConnectionState, RoomUpdate, Sid}; -use anyhow::{anyhow, Context as _, Result}; -use core_foundation::{ - array::{CFArray, CFArrayRef}, - base::{CFRelease, CFRetain, TCFType}, - string::{CFString, CFStringRef}, -}; -use futures::{ - channel::{mpsc, oneshot}, - Future, -}; -pub use media::core_video::CVImageBuffer; -use media::core_video::CVImageBufferRef; -use parking_lot::Mutex; -use postage::watch; -use std::{ - ffi::c_void, - sync::{Arc, Weak}, -}; - -macro_rules! pointer_type { - ($pointer_name:ident) => { - #[repr(transparent)] - #[derive(Copy, Clone, Debug)] - pub struct $pointer_name(pub *const std::ffi::c_void); - unsafe impl Send for $pointer_name {} - }; -} - -mod swift { - pointer_type!(Room); - pointer_type!(LocalAudioTrack); - pointer_type!(RemoteAudioTrack); - pointer_type!(LocalVideoTrack); - pointer_type!(RemoteVideoTrack); - pointer_type!(LocalTrackPublication); - pointer_type!(RemoteTrackPublication); - pointer_type!(MacOSDisplay); - pointer_type!(RoomDelegate); -} - -extern "C" { - fn LKRoomDelegateCreate( - callback_data: *mut c_void, - on_did_disconnect: extern "C" fn(callback_data: *mut c_void), - on_did_subscribe_to_remote_audio_track: extern "C" fn( - callback_data: *mut c_void, - publisher_id: CFStringRef, - track_id: CFStringRef, - remote_track: swift::RemoteAudioTrack, - remote_publication: swift::RemoteTrackPublication, - ), - on_did_unsubscribe_from_remote_audio_track: extern "C" fn( - callback_data: *mut c_void, - publisher_id: CFStringRef, - track_id: CFStringRef, - ), - on_mute_changed_from_remote_audio_track: extern "C" fn( - callback_data: *mut c_void, - track_id: CFStringRef, - muted: bool, - ), - on_active_speakers_changed: extern "C" fn( - callback_data: *mut c_void, - participants: CFArrayRef, - ), - on_did_subscribe_to_remote_video_track: extern "C" fn( - callback_data: *mut c_void, - publisher_id: CFStringRef, - track_id: CFStringRef, - remote_track: swift::RemoteVideoTrack, - ), - on_did_unsubscribe_from_remote_video_track: extern "C" fn( - callback_data: *mut c_void, - publisher_id: CFStringRef, - track_id: CFStringRef, - ), - on_did_publish_or_unpublish_local_audio_track: extern "C" fn( - callback_data: *mut c_void, - publication: swift::LocalTrackPublication, - is_published: bool, - ), - on_did_publish_or_unpublish_local_video_track: extern "C" fn( - callback_data: *mut c_void, - publication: swift::LocalTrackPublication, - is_published: bool, - ), - ) -> swift::RoomDelegate; - - fn LKRoomCreate(delegate: swift::RoomDelegate) -> swift::Room; - fn LKRoomConnect( - room: swift::Room, - url: CFStringRef, - token: CFStringRef, - callback: extern "C" fn(*mut c_void, CFStringRef), - callback_data: *mut c_void, - ); - fn LKRoomDisconnect(room: swift::Room); - fn LKRoomPublishVideoTrack( - room: swift::Room, - track: swift::LocalVideoTrack, - callback: extern "C" fn(*mut c_void, swift::LocalTrackPublication, CFStringRef), - callback_data: *mut c_void, - ); - fn LKRoomPublishAudioTrack( - room: swift::Room, - track: swift::LocalAudioTrack, - callback: extern "C" fn(*mut c_void, swift::LocalTrackPublication, CFStringRef), - callback_data: *mut c_void, - ); - fn LKRoomUnpublishTrack(room: swift::Room, publication: swift::LocalTrackPublication); - - fn LKRoomAudioTracksForRemoteParticipant( - room: swift::Room, - participant_id: CFStringRef, - ) -> CFArrayRef; - - fn LKRoomAudioTrackPublicationsForRemoteParticipant( - room: swift::Room, - participant_id: CFStringRef, - ) -> CFArrayRef; - - fn LKRoomVideoTracksForRemoteParticipant( - room: swift::Room, - participant_id: CFStringRef, - ) -> CFArrayRef; - - fn LKVideoRendererCreate( - callback_data: *mut c_void, - on_frame: extern "C" fn(callback_data: *mut c_void, frame: CVImageBufferRef) -> bool, - on_drop: extern "C" fn(callback_data: *mut c_void), - ) -> *const c_void; - - fn LKRemoteAudioTrackGetSid(track: swift::RemoteAudioTrack) -> CFStringRef; - fn LKRemoteVideoTrackGetSid(track: swift::RemoteVideoTrack) -> CFStringRef; - fn LKRemoteAudioTrackStart(track: swift::RemoteAudioTrack); - fn LKRemoteAudioTrackStop(track: swift::RemoteAudioTrack); - fn LKVideoTrackAddRenderer(track: swift::RemoteVideoTrack, renderer: *const c_void); - - fn LKDisplaySources( - callback_data: *mut c_void, - callback: extern "C" fn( - callback_data: *mut c_void, - sources: CFArrayRef, - error: CFStringRef, - ), - ); - fn LKCreateScreenShareTrackForDisplay(display: swift::MacOSDisplay) -> swift::LocalVideoTrack; - fn LKLocalAudioTrackCreateTrack() -> swift::LocalAudioTrack; - - fn LKLocalTrackPublicationSetMute( - publication: swift::LocalTrackPublication, - muted: bool, - on_complete: extern "C" fn(callback_data: *mut c_void, error: CFStringRef), - callback_data: *mut c_void, - ); - - fn LKRemoteTrackPublicationSetEnabled( - publication: swift::RemoteTrackPublication, - enabled: bool, - on_complete: extern "C" fn(callback_data: *mut c_void, error: CFStringRef), - callback_data: *mut c_void, - ); - - fn LKLocalTrackPublicationIsMuted(publication: swift::LocalTrackPublication) -> bool; - fn LKRemoteTrackPublicationIsMuted(publication: swift::RemoteTrackPublication) -> bool; - fn LKLocalTrackPublicationGetSid(publication: swift::LocalTrackPublication) -> CFStringRef; - fn LKRemoteTrackPublicationGetSid(publication: swift::RemoteTrackPublication) -> CFStringRef; -} - -pub struct Room { - native_room: swift::Room, - connection: Mutex<( - watch::Sender, - watch::Receiver, - )>, - update_subscribers: Mutex>>, - _delegate: RoomDelegate, -} - -impl Room { - pub fn new() -> Arc { - Arc::new_cyclic(|weak_room| { - let delegate = RoomDelegate::new(weak_room.clone()); - Self { - native_room: unsafe { LKRoomCreate(delegate.native_delegate) }, - connection: Mutex::new(watch::channel_with(ConnectionState::Disconnected)), - update_subscribers: Default::default(), - _delegate: delegate, - } - }) - } - - pub fn status(&self) -> watch::Receiver { - self.connection.lock().1.clone() - } - - pub fn connect(self: &Arc, url: &str, token: &str) -> impl Future> { - let url = CFString::new(url); - let token = CFString::new(token); - let (did_connect, tx, rx) = Self::build_done_callback(); - unsafe { - LKRoomConnect( - self.native_room, - url.as_concrete_TypeRef(), - token.as_concrete_TypeRef(), - did_connect, - tx, - ) - } - - let this = self.clone(); - let url = url.to_string(); - let token = token.to_string(); - async move { - rx.await.unwrap().context("error connecting to room")?; - *this.connection.lock().0.borrow_mut() = ConnectionState::Connected { url, token }; - Ok(()) - } - } - - fn did_disconnect(&self) { - *self.connection.lock().0.borrow_mut() = ConnectionState::Disconnected; - } - - pub fn display_sources(self: &Arc) -> impl Future>> { - extern "C" fn callback(tx: *mut c_void, sources: CFArrayRef, error: CFStringRef) { - unsafe { - let tx = Box::from_raw(tx as *mut oneshot::Sender>>); - - if sources.is_null() { - let _ = tx.send(Err(anyhow!("{}", CFString::wrap_under_get_rule(error)))); - } else { - let sources = CFArray::wrap_under_get_rule(sources) - .into_iter() - .map(|source| MacOSDisplay::new(swift::MacOSDisplay(*source))) - .collect(); - - let _ = tx.send(Ok(sources)); - } - } - } - - let (tx, rx) = oneshot::channel(); - - unsafe { - LKDisplaySources(Box::into_raw(Box::new(tx)) as *mut _, callback); - } - - async move { rx.await.unwrap() } - } - - pub fn publish_video_track( - self: &Arc, - track: LocalVideoTrack, - ) -> impl Future> { - let (tx, rx) = oneshot::channel::>(); - extern "C" fn callback( - tx: *mut c_void, - publication: swift::LocalTrackPublication, - error: CFStringRef, - ) { - let tx = - unsafe { Box::from_raw(tx as *mut oneshot::Sender>) }; - if error.is_null() { - let _ = tx.send(Ok(LocalTrackPublication::new(publication))); - } else { - let error = unsafe { CFString::wrap_under_get_rule(error).to_string() }; - let _ = tx.send(Err(anyhow!(error))); - } - } - unsafe { - LKRoomPublishVideoTrack( - self.native_room, - track.0, - callback, - Box::into_raw(Box::new(tx)) as *mut c_void, - ); - } - async { rx.await.unwrap().context("error publishing video track") } - } - - pub fn publish_audio_track( - self: &Arc, - track: LocalAudioTrack, - ) -> impl Future> { - let (tx, rx) = oneshot::channel::>(); - extern "C" fn callback( - tx: *mut c_void, - publication: swift::LocalTrackPublication, - error: CFStringRef, - ) { - let tx = - unsafe { Box::from_raw(tx as *mut oneshot::Sender>) }; - if error.is_null() { - let _ = tx.send(Ok(LocalTrackPublication::new(publication))); - } else { - let error = unsafe { CFString::wrap_under_get_rule(error).to_string() }; - let _ = tx.send(Err(anyhow!(error))); - } - } - unsafe { - LKRoomPublishAudioTrack( - self.native_room, - track.0, - callback, - Box::into_raw(Box::new(tx)) as *mut c_void, - ); - } - async { rx.await.unwrap().context("error publishing audio track") } - } - - pub fn unpublish_track(&self, publication: LocalTrackPublication) { - unsafe { - LKRoomUnpublishTrack(self.native_room, publication.0); - } - } - - pub fn remote_video_tracks(&self, participant_id: &str) -> Vec> { - unsafe { - let tracks = LKRoomVideoTracksForRemoteParticipant( - self.native_room, - CFString::new(participant_id).as_concrete_TypeRef(), - ); - - if tracks.is_null() { - Vec::new() - } else { - let tracks = CFArray::wrap_under_get_rule(tracks); - tracks - .into_iter() - .map(|native_track| { - let native_track = swift::RemoteVideoTrack(*native_track); - let id = - CFString::wrap_under_get_rule(LKRemoteVideoTrackGetSid(native_track)) - .to_string(); - Arc::new(RemoteVideoTrack::new( - native_track, - id, - participant_id.into(), - )) - }) - .collect() - } - } - } - - pub fn remote_audio_tracks(&self, participant_id: &str) -> Vec> { - unsafe { - let tracks = LKRoomAudioTracksForRemoteParticipant( - self.native_room, - CFString::new(participant_id).as_concrete_TypeRef(), - ); - - if tracks.is_null() { - Vec::new() - } else { - let tracks = CFArray::wrap_under_get_rule(tracks); - tracks - .into_iter() - .map(|native_track| { - let native_track = swift::RemoteAudioTrack(*native_track); - let id = - CFString::wrap_under_get_rule(LKRemoteAudioTrackGetSid(native_track)) - .to_string(); - Arc::new(RemoteAudioTrack::new( - native_track, - id, - participant_id.into(), - )) - }) - .collect() - } - } - } - - pub fn remote_audio_track_publications( - &self, - participant_id: &str, - ) -> Vec> { - unsafe { - let tracks = LKRoomAudioTrackPublicationsForRemoteParticipant( - self.native_room, - CFString::new(participant_id).as_concrete_TypeRef(), - ); - - if tracks.is_null() { - Vec::new() - } else { - let tracks = CFArray::wrap_under_get_rule(tracks); - tracks - .into_iter() - .map(|native_track_publication| { - let native_track_publication = - swift::RemoteTrackPublication(*native_track_publication); - Arc::new(RemoteTrackPublication::new(native_track_publication)) - }) - .collect() - } - } - } - - pub fn updates(&self) -> mpsc::UnboundedReceiver { - let (tx, rx) = mpsc::unbounded(); - self.update_subscribers.lock().push(tx); - rx - } - - fn did_subscribe_to_remote_audio_track( - &self, - track: RemoteAudioTrack, - publication: RemoteTrackPublication, - ) { - let track = Arc::new(track); - let publication = Arc::new(publication); - self.update_subscribers.lock().retain(|tx| { - tx.unbounded_send(RoomUpdate::SubscribedToRemoteAudioTrack( - track.clone(), - publication.clone(), - )) - .is_ok() - }); - } - - fn did_unsubscribe_from_remote_audio_track(&self, publisher_id: String, track_id: String) { - self.update_subscribers.lock().retain(|tx| { - tx.unbounded_send(RoomUpdate::UnsubscribedFromRemoteAudioTrack { - publisher_id: publisher_id.clone(), - track_id: track_id.clone(), - }) - .is_ok() - }); - } - - fn mute_changed_from_remote_audio_track(&self, track_id: String, muted: bool) { - self.update_subscribers.lock().retain(|tx| { - tx.unbounded_send(RoomUpdate::RemoteAudioTrackMuteChanged { - track_id: track_id.clone(), - muted, - }) - .is_ok() - }); - } - - fn active_speakers_changed(&self, speakers: Vec) { - self.update_subscribers.lock().retain(move |tx| { - tx.unbounded_send(RoomUpdate::ActiveSpeakersChanged { - speakers: speakers.clone(), - }) - .is_ok() - }); - } - - fn did_subscribe_to_remote_video_track(&self, track: RemoteVideoTrack) { - let track = Arc::new(track); - self.update_subscribers.lock().retain(|tx| { - tx.unbounded_send(RoomUpdate::SubscribedToRemoteVideoTrack(track.clone())) - .is_ok() - }); - } - - fn did_unsubscribe_from_remote_video_track(&self, publisher_id: String, track_id: String) { - self.update_subscribers.lock().retain(|tx| { - tx.unbounded_send(RoomUpdate::UnsubscribedFromRemoteVideoTrack { - publisher_id: publisher_id.clone(), - track_id: track_id.clone(), - }) - .is_ok() - }); - } - - fn build_done_callback() -> ( - extern "C" fn(*mut c_void, CFStringRef), - *mut c_void, - oneshot::Receiver>, - ) { - let (tx, rx) = oneshot::channel(); - extern "C" fn done_callback(tx: *mut c_void, error: CFStringRef) { - let tx = unsafe { Box::from_raw(tx as *mut oneshot::Sender>) }; - if error.is_null() { - let _ = tx.send(Ok(())); - } else { - let error = unsafe { CFString::wrap_under_get_rule(error).to_string() }; - let _ = tx.send(Err(anyhow!(error))); - } - } - ( - done_callback, - Box::into_raw(Box::new(tx)) as *mut c_void, - rx, - ) - } - - pub fn set_display_sources(&self, _: Vec) { - unreachable!("This is a test-only function") - } -} - -impl Drop for Room { - fn drop(&mut self) { - unsafe { - LKRoomDisconnect(self.native_room); - CFRelease(self.native_room.0); - } - } -} - -struct RoomDelegate { - native_delegate: swift::RoomDelegate, - weak_room: *mut c_void, -} - -impl RoomDelegate { - fn new(weak_room: Weak) -> Self { - let weak_room = weak_room.into_raw() as *mut c_void; - let native_delegate = unsafe { - LKRoomDelegateCreate( - weak_room, - Self::on_did_disconnect, - Self::on_did_subscribe_to_remote_audio_track, - Self::on_did_unsubscribe_from_remote_audio_track, - Self::on_mute_change_from_remote_audio_track, - Self::on_active_speakers_changed, - Self::on_did_subscribe_to_remote_video_track, - Self::on_did_unsubscribe_from_remote_video_track, - Self::on_did_publish_or_unpublish_local_audio_track, - Self::on_did_publish_or_unpublish_local_video_track, - ) - }; - Self { - native_delegate, - weak_room, - } - } - - extern "C" fn on_did_disconnect(room: *mut c_void) { - let room = unsafe { Weak::from_raw(room as *mut Room) }; - if let Some(room) = room.upgrade() { - room.did_disconnect(); - } - let _ = Weak::into_raw(room); - } - - extern "C" fn on_did_subscribe_to_remote_audio_track( - room: *mut c_void, - publisher_id: CFStringRef, - track_id: CFStringRef, - track: swift::RemoteAudioTrack, - publication: swift::RemoteTrackPublication, - ) { - let room = unsafe { Weak::from_raw(room as *mut Room) }; - let publisher_id = unsafe { CFString::wrap_under_get_rule(publisher_id).to_string() }; - let track_id = unsafe { CFString::wrap_under_get_rule(track_id).to_string() }; - let track = RemoteAudioTrack::new(track, track_id, publisher_id); - let publication = RemoteTrackPublication::new(publication); - if let Some(room) = room.upgrade() { - room.did_subscribe_to_remote_audio_track(track, publication); - } - let _ = Weak::into_raw(room); - } - - extern "C" fn on_did_unsubscribe_from_remote_audio_track( - room: *mut c_void, - publisher_id: CFStringRef, - track_id: CFStringRef, - ) { - let room = unsafe { Weak::from_raw(room as *mut Room) }; - let publisher_id = unsafe { CFString::wrap_under_get_rule(publisher_id).to_string() }; - let track_id = unsafe { CFString::wrap_under_get_rule(track_id).to_string() }; - if let Some(room) = room.upgrade() { - room.did_unsubscribe_from_remote_audio_track(publisher_id, track_id); - } - let _ = Weak::into_raw(room); - } - - extern "C" fn on_mute_change_from_remote_audio_track( - room: *mut c_void, - track_id: CFStringRef, - muted: bool, - ) { - let room = unsafe { Weak::from_raw(room as *mut Room) }; - let track_id = unsafe { CFString::wrap_under_get_rule(track_id).to_string() }; - if let Some(room) = room.upgrade() { - room.mute_changed_from_remote_audio_track(track_id, muted); - } - let _ = Weak::into_raw(room); - } - - extern "C" fn on_active_speakers_changed(room: *mut c_void, participants: CFArrayRef) { - if participants.is_null() { - return; - } - - let room = unsafe { Weak::from_raw(room as *mut Room) }; - let speakers = unsafe { - CFArray::wrap_under_get_rule(participants) - .into_iter() - .map( - |speaker: core_foundation::base::ItemRef<'_, *const c_void>| { - CFString::wrap_under_get_rule(*speaker as CFStringRef).to_string() - }, - ) - .collect() - }; - - if let Some(room) = room.upgrade() { - room.active_speakers_changed(speakers); - } - let _ = Weak::into_raw(room); - } - - extern "C" fn on_did_subscribe_to_remote_video_track( - room: *mut c_void, - publisher_id: CFStringRef, - track_id: CFStringRef, - track: swift::RemoteVideoTrack, - ) { - let room = unsafe { Weak::from_raw(room as *mut Room) }; - let publisher_id = unsafe { CFString::wrap_under_get_rule(publisher_id).to_string() }; - let track_id = unsafe { CFString::wrap_under_get_rule(track_id).to_string() }; - let track = RemoteVideoTrack::new(track, track_id, publisher_id); - if let Some(room) = room.upgrade() { - room.did_subscribe_to_remote_video_track(track); - } - let _ = Weak::into_raw(room); - } - - extern "C" fn on_did_unsubscribe_from_remote_video_track( - room: *mut c_void, - publisher_id: CFStringRef, - track_id: CFStringRef, - ) { - let room = unsafe { Weak::from_raw(room as *mut Room) }; - let publisher_id = unsafe { CFString::wrap_under_get_rule(publisher_id).to_string() }; - let track_id = unsafe { CFString::wrap_under_get_rule(track_id).to_string() }; - if let Some(room) = room.upgrade() { - room.did_unsubscribe_from_remote_video_track(publisher_id, track_id); - } - let _ = Weak::into_raw(room); - } - - extern "C" fn on_did_publish_or_unpublish_local_audio_track( - room: *mut c_void, - publication: swift::LocalTrackPublication, - is_published: bool, - ) { - let room = unsafe { Weak::from_raw(room as *mut Room) }; - if let Some(room) = room.upgrade() { - let publication = LocalTrackPublication::new(publication); - let update = if is_published { - RoomUpdate::LocalAudioTrackPublished { publication } - } else { - RoomUpdate::LocalAudioTrackUnpublished { publication } - }; - room.update_subscribers - .lock() - .retain(|tx| tx.unbounded_send(update.clone()).is_ok()); - } - let _ = Weak::into_raw(room); - } - - extern "C" fn on_did_publish_or_unpublish_local_video_track( - room: *mut c_void, - publication: swift::LocalTrackPublication, - is_published: bool, - ) { - let room = unsafe { Weak::from_raw(room as *mut Room) }; - if let Some(room) = room.upgrade() { - let publication = LocalTrackPublication::new(publication); - let update = if is_published { - RoomUpdate::LocalVideoTrackPublished { publication } - } else { - RoomUpdate::LocalVideoTrackUnpublished { publication } - }; - room.update_subscribers - .lock() - .retain(|tx| tx.unbounded_send(update.clone()).is_ok()); - } - let _ = Weak::into_raw(room); - } -} - -impl Drop for RoomDelegate { - fn drop(&mut self) { - unsafe { - CFRelease(self.native_delegate.0); - let _ = Weak::from_raw(self.weak_room as *mut Room); - } - } -} - -pub struct LocalAudioTrack(swift::LocalAudioTrack); - -impl LocalAudioTrack { - pub fn create() -> Self { - Self(unsafe { LKLocalAudioTrackCreateTrack() }) - } -} - -impl Drop for LocalAudioTrack { - fn drop(&mut self) { - unsafe { CFRelease(self.0 .0) } - } -} - -pub struct LocalVideoTrack(swift::LocalVideoTrack); - -impl LocalVideoTrack { - pub fn screen_share_for_display(display: &MacOSDisplay) -> Self { - Self(unsafe { LKCreateScreenShareTrackForDisplay(display.0) }) - } -} - -impl Drop for LocalVideoTrack { - fn drop(&mut self) { - unsafe { CFRelease(self.0 .0) } - } -} - -pub struct LocalTrackPublication(swift::LocalTrackPublication); - -impl LocalTrackPublication { - pub fn new(native_track_publication: swift::LocalTrackPublication) -> Self { - unsafe { - CFRetain(native_track_publication.0); - } - Self(native_track_publication) - } - - pub fn sid(&self) -> String { - unsafe { CFString::wrap_under_get_rule(LKLocalTrackPublicationGetSid(self.0)).to_string() } - } - - pub fn set_mute(&self, muted: bool) -> impl Future> { - let (tx, rx) = futures::channel::oneshot::channel(); - - extern "C" fn complete_callback(callback_data: *mut c_void, error: CFStringRef) { - let tx = unsafe { Box::from_raw(callback_data as *mut oneshot::Sender>) }; - if error.is_null() { - tx.send(Ok(())).ok(); - } else { - let error = unsafe { CFString::wrap_under_get_rule(error).to_string() }; - tx.send(Err(anyhow!(error))).ok(); - } - } - - unsafe { - LKLocalTrackPublicationSetMute( - self.0, - muted, - complete_callback, - Box::into_raw(Box::new(tx)) as *mut c_void, - ) - } - - async move { rx.await.unwrap() } - } - - pub fn is_muted(&self) -> bool { - unsafe { LKLocalTrackPublicationIsMuted(self.0) } - } -} - -impl Clone for LocalTrackPublication { - fn clone(&self) -> Self { - unsafe { - CFRetain(self.0 .0); - } - Self(self.0) - } -} - -impl Drop for LocalTrackPublication { - fn drop(&mut self) { - unsafe { CFRelease(self.0 .0) } - } -} - -pub struct RemoteTrackPublication(swift::RemoteTrackPublication); - -impl RemoteTrackPublication { - pub fn new(native_track_publication: swift::RemoteTrackPublication) -> Self { - unsafe { - CFRetain(native_track_publication.0); - } - Self(native_track_publication) - } - - pub fn sid(&self) -> String { - unsafe { CFString::wrap_under_get_rule(LKRemoteTrackPublicationGetSid(self.0)).to_string() } - } - - pub fn is_muted(&self) -> bool { - unsafe { LKRemoteTrackPublicationIsMuted(self.0) } - } - - pub fn set_enabled(&self, enabled: bool) -> impl Future> { - let (tx, rx) = futures::channel::oneshot::channel(); - - extern "C" fn complete_callback(callback_data: *mut c_void, error: CFStringRef) { - let tx = unsafe { Box::from_raw(callback_data as *mut oneshot::Sender>) }; - if error.is_null() { - tx.send(Ok(())).ok(); - } else { - let error = unsafe { CFString::wrap_under_get_rule(error).to_string() }; - tx.send(Err(anyhow!(error))).ok(); - } - } - - unsafe { - LKRemoteTrackPublicationSetEnabled( - self.0, - enabled, - complete_callback, - Box::into_raw(Box::new(tx)) as *mut c_void, - ) - } - - async move { rx.await.unwrap() } - } -} - -impl Drop for RemoteTrackPublication { - fn drop(&mut self) { - unsafe { CFRelease(self.0 .0) } - } -} - -#[derive(Debug)] -pub struct RemoteAudioTrack { - native_track: swift::RemoteAudioTrack, - sid: Sid, - publisher_id: String, -} - -impl RemoteAudioTrack { - fn new(native_track: swift::RemoteAudioTrack, sid: Sid, publisher_id: String) -> Self { - unsafe { - CFRetain(native_track.0); - } - Self { - native_track, - sid, - publisher_id, - } - } - - pub fn sid(&self) -> &str { - &self.sid - } - - pub fn publisher_id(&self) -> &str { - &self.publisher_id - } - - pub fn start(&self) { - unsafe { LKRemoteAudioTrackStart(self.native_track) } - } - - pub fn stop(&self) { - unsafe { LKRemoteAudioTrackStop(self.native_track) } - } -} - -impl Drop for RemoteAudioTrack { - fn drop(&mut self) { - // todo: uncomment this `CFRelease`, unless we find that it was causing - // the crash in the `livekit.multicast` thread. - // - // unsafe { CFRelease(self.native_track.0) } - let _ = self.native_track; - } -} - -#[derive(Debug)] -pub struct RemoteVideoTrack { - native_track: swift::RemoteVideoTrack, - sid: Sid, - publisher_id: String, -} - -impl RemoteVideoTrack { - fn new(native_track: swift::RemoteVideoTrack, sid: Sid, publisher_id: String) -> Self { - unsafe { - CFRetain(native_track.0); - } - Self { - native_track, - sid, - publisher_id, - } - } - - pub fn sid(&self) -> &str { - &self.sid - } - - pub fn publisher_id(&self) -> &str { - &self.publisher_id - } - - pub fn frames(&self) -> async_broadcast::Receiver { - extern "C" fn on_frame(callback_data: *mut c_void, frame: CVImageBufferRef) -> bool { - unsafe { - let tx = Box::from_raw(callback_data as *mut async_broadcast::Sender); - let buffer = CVImageBuffer::wrap_under_get_rule(frame); - let result = tx.try_broadcast(Frame(buffer)); - let _ = Box::into_raw(tx); - match result { - Ok(_) => true, - Err(async_broadcast::TrySendError::Closed(_)) - | Err(async_broadcast::TrySendError::Inactive(_)) => { - log::warn!("no active receiver for frame"); - false - } - Err(async_broadcast::TrySendError::Full(_)) => { - log::warn!("skipping frame as receiver is not keeping up"); - true - } - } - } - } - - extern "C" fn on_drop(callback_data: *mut c_void) { - unsafe { - let _ = Box::from_raw(callback_data as *mut async_broadcast::Sender); - } - } - - let (tx, rx) = async_broadcast::broadcast(64); - unsafe { - let renderer = LKVideoRendererCreate( - Box::into_raw(Box::new(tx)) as *mut c_void, - on_frame, - on_drop, - ); - LKVideoTrackAddRenderer(self.native_track, renderer); - rx - } - } -} - -impl Drop for RemoteVideoTrack { - fn drop(&mut self) { - unsafe { CFRelease(self.native_track.0) } - } -} - -pub struct MacOSDisplay(swift::MacOSDisplay); - -impl MacOSDisplay { - fn new(ptr: swift::MacOSDisplay) -> Self { - unsafe { - CFRetain(ptr.0); - } - Self(ptr) - } -} - -impl Drop for MacOSDisplay { - fn drop(&mut self) { - unsafe { CFRelease(self.0 .0) } - } -} - -#[derive(Clone)] -pub struct Frame(CVImageBuffer); - -impl Frame { - pub fn width(&self) -> usize { - self.0.width() - } - - pub fn height(&self) -> usize { - self.0.height() - } - - pub fn image(&self) -> CVImageBuffer { - self.0.clone() - } -} diff --git a/crates/livekit_client_macos/src/test.rs b/crates/livekit_client_macos/src/test.rs deleted file mode 100644 index bccc713c87..0000000000 --- a/crates/livekit_client_macos/src/test.rs +++ /dev/null @@ -1,882 +0,0 @@ -use crate::{ConnectionState, RoomUpdate, Sid}; -use anyhow::{anyhow, Context as _, Result}; -use async_trait::async_trait; -use collections::{btree_map::Entry as BTreeEntry, hash_map::Entry, BTreeMap, HashMap, HashSet}; -use futures::Stream; -use gpui::{BackgroundExecutor, SurfaceSource}; -use livekit_api::{proto, token}; - -use parking_lot::Mutex; -use postage::watch; -use std::{ - future::Future, - mem, - sync::{ - atomic::{AtomicBool, Ordering::SeqCst}, - Arc, Weak, - }, -}; - -static SERVERS: Mutex>> = Mutex::new(BTreeMap::new()); - -pub struct TestServer { - pub url: String, - pub api_key: String, - pub secret_key: String, - rooms: Mutex>, - executor: BackgroundExecutor, -} - -impl TestServer { - pub fn create( - url: String, - api_key: String, - secret_key: String, - executor: BackgroundExecutor, - ) -> Result> { - let mut servers = SERVERS.lock(); - if let BTreeEntry::Vacant(e) = servers.entry(url.clone()) { - let server = Arc::new(TestServer { - url, - api_key, - secret_key, - rooms: Default::default(), - executor, - }); - e.insert(server.clone()); - Ok(server) - } else { - Err(anyhow!("a server with url {:?} already exists", url)) - } - } - - fn get(url: &str) -> Result> { - Ok(SERVERS - .lock() - .get(url) - .ok_or_else(|| anyhow!("no server found for url"))? - .clone()) - } - - pub fn teardown(&self) -> Result<()> { - SERVERS - .lock() - .remove(&self.url) - .ok_or_else(|| anyhow!("server with url {:?} does not exist", self.url))?; - Ok(()) - } - - pub fn create_api_client(&self) -> TestApiClient { - TestApiClient { - url: self.url.clone(), - } - } - - pub async fn create_room(&self, room: String) -> Result<()> { - // todo(linux): Remove this once the cross-platform LiveKit implementation is merged - #[cfg(any(test, feature = "test-support"))] - self.executor.simulate_random_delay().await; - let mut server_rooms = self.rooms.lock(); - if let Entry::Vacant(e) = server_rooms.entry(room.clone()) { - e.insert(Default::default()); - Ok(()) - } else { - Err(anyhow!("room {:?} already exists", room)) - } - } - - async fn delete_room(&self, room: String) -> Result<()> { - // TODO: clear state associated with all `Room`s. - // todo(linux): Remove this once the cross-platform LiveKit implementation is merged - #[cfg(any(test, feature = "test-support"))] - self.executor.simulate_random_delay().await; - let mut server_rooms = self.rooms.lock(); - server_rooms - .remove(&room) - .ok_or_else(|| anyhow!("room {:?} does not exist", room))?; - Ok(()) - } - - async fn join_room(&self, token: String, client_room: Arc) -> Result<()> { - // todo(linux): Remove this once the cross-platform LiveKit implementation is merged - #[cfg(any(test, feature = "test-support"))] - self.executor.simulate_random_delay().await; - - let claims = livekit_api::token::validate(&token, &self.secret_key)?; - let identity = claims.sub.unwrap().to_string(); - let room_name = claims.video.room.unwrap(); - let mut server_rooms = self.rooms.lock(); - let room = (*server_rooms).entry(room_name.to_string()).or_default(); - - if let Entry::Vacant(e) = room.client_rooms.entry(identity.clone()) { - for track in &room.video_tracks { - client_room - .0 - .lock() - .updates_tx - .try_broadcast(RoomUpdate::SubscribedToRemoteVideoTrack(Arc::new( - RemoteVideoTrack { - server_track: track.clone(), - }, - ))) - .unwrap(); - } - for track in &room.audio_tracks { - client_room - .0 - .lock() - .updates_tx - .try_broadcast(RoomUpdate::SubscribedToRemoteAudioTrack( - Arc::new(RemoteAudioTrack { - server_track: track.clone(), - room: Arc::downgrade(&client_room), - }), - Arc::new(RemoteTrackPublication), - )) - .unwrap(); - } - e.insert(client_room); - Ok(()) - } else { - Err(anyhow!( - "{:?} attempted to join room {:?} twice", - identity, - room_name - )) - } - } - - async fn leave_room(&self, token: String) -> Result<()> { - // todo(linux): Remove this once the cross-platform LiveKit implementation is merged - #[cfg(any(test, feature = "test-support"))] - self.executor.simulate_random_delay().await; - let claims = livekit_api::token::validate(&token, &self.secret_key)?; - let identity = claims.sub.unwrap().to_string(); - let room_name = claims.video.room.unwrap(); - let mut server_rooms = self.rooms.lock(); - let room = server_rooms - .get_mut(&*room_name) - .ok_or_else(|| anyhow!("room {} does not exist", room_name))?; - room.client_rooms.remove(&identity).ok_or_else(|| { - anyhow!( - "{:?} attempted to leave room {:?} before joining it", - identity, - room_name - ) - })?; - Ok(()) - } - - async fn remove_participant(&self, room_name: String, identity: String) -> Result<()> { - // TODO: clear state associated with the `Room`. - // todo(linux): Remove this once the cross-platform LiveKit implementation is merged - #[cfg(any(test, feature = "test-support"))] - self.executor.simulate_random_delay().await; - - let mut server_rooms = self.rooms.lock(); - let room = server_rooms - .get_mut(&room_name) - .ok_or_else(|| anyhow!("room {} does not exist", room_name))?; - room.client_rooms.remove(&identity).ok_or_else(|| { - anyhow!( - "participant {:?} did not join room {:?}", - identity, - room_name - ) - })?; - Ok(()) - } - - async fn update_participant( - &self, - room_name: String, - identity: String, - permission: proto::ParticipantPermission, - ) -> Result<()> { - // todo(linux): Remove this once the cross-platform LiveKit implementation is merged - #[cfg(any(test, feature = "test-support"))] - self.executor.simulate_random_delay().await; - let mut server_rooms = self.rooms.lock(); - let room = server_rooms - .get_mut(&room_name) - .ok_or_else(|| anyhow!("room {} does not exist", room_name))?; - room.participant_permissions.insert(identity, permission); - Ok(()) - } - - pub async fn disconnect_client(&self, client_identity: String) { - // todo(linux): Remove this once the cross-platform LiveKit implementation is merged - #[cfg(any(test, feature = "test-support"))] - self.executor.simulate_random_delay().await; - let mut server_rooms = self.rooms.lock(); - for room in server_rooms.values_mut() { - if let Some(room) = room.client_rooms.remove(&client_identity) { - *room.0.lock().connection.0.borrow_mut() = ConnectionState::Disconnected; - } - } - } - - async fn publish_video_track( - &self, - token: String, - local_track: LocalVideoTrack, - ) -> Result { - // todo(linux): Remove this once the cross-platform LiveKit implementation is merged - #[cfg(any(test, feature = "test-support"))] - self.executor.simulate_random_delay().await; - let claims = livekit_api::token::validate(&token, &self.secret_key)?; - let identity = claims.sub.unwrap().to_string(); - let room_name = claims.video.room.unwrap(); - - let mut server_rooms = self.rooms.lock(); - let room = server_rooms - .get_mut(&*room_name) - .ok_or_else(|| anyhow!("room {} does not exist", room_name))?; - - let can_publish = room - .participant_permissions - .get(&identity) - .map(|permission| permission.can_publish) - .or(claims.video.can_publish) - .unwrap_or(true); - - if !can_publish { - return Err(anyhow!("user is not allowed to publish")); - } - - let sid = nanoid::nanoid!(17); - let track = Arc::new(TestServerVideoTrack { - sid: sid.clone(), - publisher_id: identity.clone(), - frames_rx: local_track.frames_rx.clone(), - }); - - room.video_tracks.push(track.clone()); - - for (id, client_room) in &room.client_rooms { - if *id != identity { - let _ = client_room - .0 - .lock() - .updates_tx - .try_broadcast(RoomUpdate::SubscribedToRemoteVideoTrack(Arc::new( - RemoteVideoTrack { - server_track: track.clone(), - }, - ))) - .unwrap(); - } - } - - Ok(sid) - } - - async fn publish_audio_track( - &self, - token: String, - _local_track: &LocalAudioTrack, - ) -> Result { - // todo(linux): Remove this once the cross-platform LiveKit implementation is merged - #[cfg(any(test, feature = "test-support"))] - self.executor.simulate_random_delay().await; - - let claims = livekit_api::token::validate(&token, &self.secret_key)?; - let identity = claims.sub.unwrap().to_string(); - let room_name = claims.video.room.unwrap(); - - let mut server_rooms = self.rooms.lock(); - let room = server_rooms - .get_mut(&*room_name) - .ok_or_else(|| anyhow!("room {} does not exist", room_name))?; - - let can_publish = room - .participant_permissions - .get(&identity) - .map(|permission| permission.can_publish) - .or(claims.video.can_publish) - .unwrap_or(true); - - if !can_publish { - return Err(anyhow!("user is not allowed to publish")); - } - - let sid = nanoid::nanoid!(17); - let track = Arc::new(TestServerAudioTrack { - sid: sid.clone(), - publisher_id: identity.clone(), - muted: AtomicBool::new(false), - }); - - let publication = Arc::new(RemoteTrackPublication); - - room.audio_tracks.push(track.clone()); - - for (id, client_room) in &room.client_rooms { - if *id != identity { - let _ = client_room - .0 - .lock() - .updates_tx - .try_broadcast(RoomUpdate::SubscribedToRemoteAudioTrack( - Arc::new(RemoteAudioTrack { - server_track: track.clone(), - room: Arc::downgrade(client_room), - }), - publication.clone(), - )) - .unwrap(); - } - } - - Ok(sid) - } - - fn set_track_muted(&self, token: &str, track_sid: &str, muted: bool) -> Result<()> { - let claims = livekit_api::token::validate(token, &self.secret_key)?; - let room_name = claims.video.room.unwrap(); - let identity = claims.sub.unwrap(); - let mut server_rooms = self.rooms.lock(); - let room = server_rooms - .get_mut(&*room_name) - .ok_or_else(|| anyhow!("room {} does not exist", room_name))?; - if let Some(track) = room - .audio_tracks - .iter_mut() - .find(|track| track.sid == track_sid) - { - track.muted.store(muted, SeqCst); - for (id, client_room) in room.client_rooms.iter() { - if *id != identity { - client_room - .0 - .lock() - .updates_tx - .try_broadcast(RoomUpdate::RemoteAudioTrackMuteChanged { - track_id: track_sid.to_string(), - muted, - }) - .unwrap(); - } - } - } - Ok(()) - } - - fn is_track_muted(&self, token: &str, track_sid: &str) -> Option { - let claims = livekit_api::token::validate(token, &self.secret_key).ok()?; - let room_name = claims.video.room.unwrap(); - - let mut server_rooms = self.rooms.lock(); - let room = server_rooms.get_mut(&*room_name)?; - room.audio_tracks.iter().find_map(|track| { - if track.sid == track_sid { - Some(track.muted.load(SeqCst)) - } else { - None - } - }) - } - - fn video_tracks(&self, token: String) -> Result>> { - let claims = livekit_api::token::validate(&token, &self.secret_key)?; - let room_name = claims.video.room.unwrap(); - let identity = claims.sub.unwrap(); - - let mut server_rooms = self.rooms.lock(); - let room = server_rooms - .get_mut(&*room_name) - .ok_or_else(|| anyhow!("room {} does not exist", room_name))?; - room.client_rooms - .get(identity.as_ref()) - .ok_or_else(|| anyhow!("not a participant in room"))?; - Ok(room - .video_tracks - .iter() - .map(|track| { - Arc::new(RemoteVideoTrack { - server_track: track.clone(), - }) - }) - .collect()) - } - - fn audio_tracks(&self, token: String) -> Result>> { - let claims = livekit_api::token::validate(&token, &self.secret_key)?; - let room_name = claims.video.room.unwrap(); - let identity = claims.sub.unwrap(); - - let mut server_rooms = self.rooms.lock(); - let room = server_rooms - .get_mut(&*room_name) - .ok_or_else(|| anyhow!("room {} does not exist", room_name))?; - let client_room = room - .client_rooms - .get(identity.as_ref()) - .ok_or_else(|| anyhow!("not a participant in room"))?; - Ok(room - .audio_tracks - .iter() - .map(|track| { - Arc::new(RemoteAudioTrack { - server_track: track.clone(), - room: Arc::downgrade(client_room), - }) - }) - .collect()) - } -} - -#[derive(Default)] -struct TestServerRoom { - client_rooms: HashMap>, - video_tracks: Vec>, - audio_tracks: Vec>, - participant_permissions: HashMap, -} - -#[derive(Debug)] -struct TestServerVideoTrack { - sid: Sid, - publisher_id: Sid, - frames_rx: async_broadcast::Receiver, -} - -#[derive(Debug)] -struct TestServerAudioTrack { - sid: Sid, - publisher_id: Sid, - muted: AtomicBool, -} - -impl TestServerRoom {} - -pub struct TestApiClient { - url: String, -} - -#[async_trait] -impl livekit_api::Client for TestApiClient { - fn url(&self) -> &str { - &self.url - } - - async fn create_room(&self, name: String) -> Result<()> { - let server = TestServer::get(&self.url)?; - server.create_room(name).await?; - Ok(()) - } - - async fn delete_room(&self, name: String) -> Result<()> { - let server = TestServer::get(&self.url)?; - server.delete_room(name).await?; - Ok(()) - } - - async fn remove_participant(&self, room: String, identity: String) -> Result<()> { - let server = TestServer::get(&self.url)?; - server.remove_participant(room, identity).await?; - Ok(()) - } - - async fn update_participant( - &self, - room: String, - identity: String, - permission: livekit_api::proto::ParticipantPermission, - ) -> Result<()> { - let server = TestServer::get(&self.url)?; - server - .update_participant(room, identity, permission) - .await?; - Ok(()) - } - - fn room_token(&self, room: &str, identity: &str) -> Result { - let server = TestServer::get(&self.url)?; - token::create( - &server.api_key, - &server.secret_key, - Some(identity), - token::VideoGrant::to_join(room), - ) - } - - fn guest_token(&self, room: &str, identity: &str) -> Result { - let server = TestServer::get(&self.url)?; - token::create( - &server.api_key, - &server.secret_key, - Some(identity), - token::VideoGrant::for_guest(room), - ) - } -} - -struct RoomState { - connection: ( - watch::Sender, - watch::Receiver, - ), - display_sources: Vec, - paused_audio_tracks: HashSet, - updates_tx: async_broadcast::Sender, - updates_rx: async_broadcast::Receiver, -} - -pub struct Room(Mutex); - -impl Room { - pub fn new() -> Arc { - let (updates_tx, updates_rx) = async_broadcast::broadcast(128); - Arc::new(Self(Mutex::new(RoomState { - connection: watch::channel_with(ConnectionState::Disconnected), - display_sources: Default::default(), - paused_audio_tracks: Default::default(), - updates_tx, - updates_rx, - }))) - } - - pub fn status(&self) -> watch::Receiver { - self.0.lock().connection.1.clone() - } - - pub fn connect(self: &Arc, url: &str, token: &str) -> impl Future> { - let this = self.clone(); - let url = url.to_string(); - let token = token.to_string(); - async move { - let server = TestServer::get(&url)?; - server - .join_room(token.clone(), this.clone()) - .await - .context("room join")?; - *this.0.lock().connection.0.borrow_mut() = ConnectionState::Connected { url, token }; - Ok(()) - } - } - - pub fn display_sources(self: &Arc) -> impl Future>> { - let this = self.clone(); - async move { - // todo(linux): Remove this once the cross-platform LiveKit implementation is merged - #[cfg(any(test, feature = "test-support"))] - { - let server = this.test_server(); - server.executor.simulate_random_delay().await; - } - - Ok(this.0.lock().display_sources.clone()) - } - } - - pub fn publish_video_track( - self: &Arc, - track: LocalVideoTrack, - ) -> impl Future> { - let this = self.clone(); - let track = track.clone(); - async move { - let sid = this - .test_server() - .publish_video_track(this.token(), track) - .await?; - Ok(LocalTrackPublication { - room: Arc::downgrade(&this), - sid, - }) - } - } - - pub fn publish_audio_track( - self: &Arc, - track: LocalAudioTrack, - ) -> impl Future> { - let this = self.clone(); - let track = track.clone(); - async move { - let sid = this - .test_server() - .publish_audio_track(this.token(), &track) - .await?; - Ok(LocalTrackPublication { - room: Arc::downgrade(&this), - sid, - }) - } - } - - pub fn unpublish_track(&self, _publication: LocalTrackPublication) {} - - pub fn remote_audio_tracks(&self, publisher_id: &str) -> Vec> { - if !self.is_connected() { - return Vec::new(); - } - - self.test_server() - .audio_tracks(self.token()) - .unwrap() - .into_iter() - .filter(|track| track.publisher_id() == publisher_id) - .collect() - } - - pub fn remote_audio_track_publications( - &self, - publisher_id: &str, - ) -> Vec> { - if !self.is_connected() { - return Vec::new(); - } - - self.test_server() - .audio_tracks(self.token()) - .unwrap() - .into_iter() - .filter(|track| track.publisher_id() == publisher_id) - .map(|_track| Arc::new(RemoteTrackPublication {})) - .collect() - } - - pub fn remote_video_tracks(&self, publisher_id: &str) -> Vec> { - if !self.is_connected() { - return Vec::new(); - } - - self.test_server() - .video_tracks(self.token()) - .unwrap() - .into_iter() - .filter(|track| track.publisher_id() == publisher_id) - .collect() - } - - pub fn updates(&self) -> impl Stream { - self.0.lock().updates_rx.clone() - } - - pub fn set_display_sources(&self, sources: Vec) { - self.0.lock().display_sources = sources; - } - - fn test_server(&self) -> Arc { - match self.0.lock().connection.1.borrow().clone() { - ConnectionState::Disconnected => panic!("must be connected to call this method"), - ConnectionState::Connected { url, .. } => TestServer::get(&url).unwrap(), - } - } - - fn token(&self) -> String { - match self.0.lock().connection.1.borrow().clone() { - ConnectionState::Disconnected => panic!("must be connected to call this method"), - ConnectionState::Connected { token, .. } => token, - } - } - - fn is_connected(&self) -> bool { - match *self.0.lock().connection.1.borrow() { - ConnectionState::Disconnected => false, - ConnectionState::Connected { .. } => true, - } - } -} - -impl Drop for Room { - fn drop(&mut self) { - if let ConnectionState::Connected { token, .. } = mem::replace( - &mut *self.0.lock().connection.0.borrow_mut(), - ConnectionState::Disconnected, - ) { - if let Ok(server) = TestServer::get(&token) { - let executor = server.executor.clone(); - executor - .spawn(async move { server.leave_room(token).await.unwrap() }) - .detach(); - } - } - } -} - -#[derive(Clone)] -pub struct LocalTrackPublication { - sid: String, - room: Weak, -} - -impl LocalTrackPublication { - pub fn set_mute(&self, mute: bool) -> impl Future> { - let sid = self.sid.clone(); - let room = self.room.clone(); - async move { - if let Some(room) = room.upgrade() { - room.test_server() - .set_track_muted(&room.token(), &sid, mute) - } else { - Err(anyhow!("no such room")) - } - } - } - - pub fn is_muted(&self) -> bool { - if let Some(room) = self.room.upgrade() { - room.test_server() - .is_track_muted(&room.token(), &self.sid) - .unwrap_or(false) - } else { - false - } - } - - pub fn sid(&self) -> String { - self.sid.clone() - } -} - -pub struct RemoteTrackPublication; - -impl RemoteTrackPublication { - pub fn set_enabled(&self, _enabled: bool) -> impl Future> { - async { Ok(()) } - } - - pub fn is_muted(&self) -> bool { - false - } - - pub fn sid(&self) -> String { - "".to_string() - } -} - -#[derive(Clone)] -pub struct LocalVideoTrack { - frames_rx: async_broadcast::Receiver, -} - -impl LocalVideoTrack { - pub fn screen_share_for_display(display: &MacOSDisplay) -> Self { - Self { - frames_rx: display.frames.1.clone(), - } - } -} - -#[derive(Clone)] -pub struct LocalAudioTrack; - -impl LocalAudioTrack { - pub fn create() -> Self { - Self - } -} - -#[derive(Debug)] -pub struct RemoteVideoTrack { - server_track: Arc, -} - -impl RemoteVideoTrack { - pub fn sid(&self) -> &str { - &self.server_track.sid - } - - pub fn publisher_id(&self) -> &str { - &self.server_track.publisher_id - } - - pub fn frames(&self) -> async_broadcast::Receiver { - self.server_track.frames_rx.clone() - } -} - -#[derive(Debug)] -pub struct RemoteAudioTrack { - server_track: Arc, - room: Weak, -} - -impl RemoteAudioTrack { - pub fn sid(&self) -> &str { - &self.server_track.sid - } - - pub fn publisher_id(&self) -> &str { - &self.server_track.publisher_id - } - - pub fn start(&self) { - if let Some(room) = self.room.upgrade() { - room.0 - .lock() - .paused_audio_tracks - .remove(&self.server_track.sid); - } - } - - pub fn stop(&self) { - if let Some(room) = self.room.upgrade() { - room.0 - .lock() - .paused_audio_tracks - .insert(self.server_track.sid.clone()); - } - } - - pub fn is_playing(&self) -> bool { - !self - .room - .upgrade() - .unwrap() - .0 - .lock() - .paused_audio_tracks - .contains(&self.server_track.sid) - } -} - -#[derive(Clone)] -pub struct MacOSDisplay { - frames: ( - async_broadcast::Sender, - async_broadcast::Receiver, - ), -} - -impl Default for MacOSDisplay { - fn default() -> Self { - Self::new() - } -} - -impl MacOSDisplay { - pub fn new() -> Self { - Self { - frames: async_broadcast::broadcast(128), - } - } - - pub fn send_frame(&self, frame: Frame) { - self.frames.0.try_broadcast(frame).unwrap(); - } -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct Frame { - pub label: String, - pub width: usize, - pub height: usize, -} - -impl Frame { - pub fn width(&self) -> usize { - self.width - } - - pub fn height(&self) -> usize { - self.height - } - - pub fn image(&self) -> SurfaceSource { - unimplemented!("you can't call this in test mode") - } -} diff --git a/crates/media/Cargo.toml b/crates/media/Cargo.toml index d078e9afd6..934f6f9d0d 100644 --- a/crates/media/Cargo.toml +++ b/crates/media/Cargo.toml @@ -20,6 +20,7 @@ core-foundation.workspace = true ctor.workspace = true foreign-types = "0.5" metal.workspace = true +core-video.workspace = true objc = "0.2" [build-dependencies] diff --git a/crates/media/src/media.rs b/crates/media/src/media.rs index 3f55475589..29563de85f 100644 --- a/crates/media/src/media.rs +++ b/crates/media/src/media.rs @@ -3,213 +3,6 @@ mod bindings; -#[cfg(target_os = "macos")] -use core_foundation::{ - base::{CFTypeID, TCFType}, - declare_TCFType, impl_CFTypeDescription, impl_TCFType, -}; -#[cfg(target_os = "macos")] -use std::ffi::c_void; - -#[cfg(target_os = "macos")] -pub mod io_surface { - use super::*; - - #[repr(C)] - pub struct __IOSurface(c_void); - // The ref type must be a pointer to the underlying struct. - pub type IOSurfaceRef = *const __IOSurface; - - declare_TCFType!(IOSurface, IOSurfaceRef); - impl_TCFType!(IOSurface, IOSurfaceRef, IOSurfaceGetTypeID); - impl_CFTypeDescription!(IOSurface); - - #[link(name = "IOSurface", kind = "framework")] - extern "C" { - fn IOSurfaceGetTypeID() -> CFTypeID; - } -} - -#[cfg(target_os = "macos")] -pub mod core_video { - #![allow(non_snake_case)] - - use super::*; - pub use crate::bindings::{ - kCVPixelFormatType_32BGRA, kCVPixelFormatType_420YpCbCr8BiPlanarFullRange, - kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, kCVPixelFormatType_420YpCbCr8Planar, - }; - use crate::bindings::{kCVReturnSuccess, CVReturn, OSType}; - use anyhow::{anyhow, Result}; - use core_foundation::{ - base::kCFAllocatorDefault, dictionary::CFDictionaryRef, mach_port::CFAllocatorRef, - }; - use foreign_types::ForeignTypeRef; - use io_surface::{IOSurface, IOSurfaceRef}; - use metal::{MTLDevice, MTLPixelFormat}; - use std::ptr; - - #[repr(C)] - pub struct __CVImageBuffer(c_void); - // The ref type must be a pointer to the underlying struct. - pub type CVImageBufferRef = *const __CVImageBuffer; - - declare_TCFType!(CVImageBuffer, CVImageBufferRef); - impl_TCFType!(CVImageBuffer, CVImageBufferRef, CVImageBufferGetTypeID); - impl_CFTypeDescription!(CVImageBuffer); - - impl CVImageBuffer { - pub fn io_surface(&self) -> IOSurface { - unsafe { - IOSurface::wrap_under_get_rule(CVPixelBufferGetIOSurface( - self.as_concrete_TypeRef(), - )) - } - } - - pub fn width(&self) -> usize { - unsafe { CVPixelBufferGetWidth(self.as_concrete_TypeRef()) } - } - - pub fn height(&self) -> usize { - unsafe { CVPixelBufferGetHeight(self.as_concrete_TypeRef()) } - } - - pub fn plane_width(&self, plane: usize) -> usize { - unsafe { CVPixelBufferGetWidthOfPlane(self.as_concrete_TypeRef(), plane) } - } - - pub fn plane_height(&self, plane: usize) -> usize { - unsafe { CVPixelBufferGetHeightOfPlane(self.as_concrete_TypeRef(), plane) } - } - - pub fn pixel_format_type(&self) -> OSType { - unsafe { CVPixelBufferGetPixelFormatType(self.as_concrete_TypeRef()) } - } - } - - #[link(name = "CoreVideo", kind = "framework")] - extern "C" { - fn CVImageBufferGetTypeID() -> CFTypeID; - fn CVPixelBufferGetIOSurface(buffer: CVImageBufferRef) -> IOSurfaceRef; - fn CVPixelBufferGetWidth(buffer: CVImageBufferRef) -> usize; - fn CVPixelBufferGetHeight(buffer: CVImageBufferRef) -> usize; - fn CVPixelBufferGetWidthOfPlane(buffer: CVImageBufferRef, plane: usize) -> usize; - fn CVPixelBufferGetHeightOfPlane(buffer: CVImageBufferRef, plane: usize) -> usize; - fn CVPixelBufferGetPixelFormatType(buffer: CVImageBufferRef) -> OSType; - } - - #[repr(C)] - pub struct __CVMetalTextureCache(c_void); - pub type CVMetalTextureCacheRef = *const __CVMetalTextureCache; - - declare_TCFType!(CVMetalTextureCache, CVMetalTextureCacheRef); - impl_TCFType!( - CVMetalTextureCache, - CVMetalTextureCacheRef, - CVMetalTextureCacheGetTypeID - ); - impl_CFTypeDescription!(CVMetalTextureCache); - - impl CVMetalTextureCache { - /// # Safety - /// - /// metal_device must be valid according to CVMetalTextureCacheCreate - pub unsafe fn new(metal_device: *mut MTLDevice) -> Result { - let mut this = ptr::null(); - let result = CVMetalTextureCacheCreate( - kCFAllocatorDefault, - ptr::null(), - metal_device, - ptr::null(), - &mut this, - ); - if result == kCVReturnSuccess { - Ok(CVMetalTextureCache::wrap_under_create_rule(this)) - } else { - Err(anyhow!("could not create texture cache, code: {}", result)) - } - } - - /// # Safety - /// - /// The arguments to this function must be valid according to CVMetalTextureCacheCreateTextureFromImage - pub unsafe fn create_texture_from_image( - &self, - source: CVImageBufferRef, - texture_attributes: CFDictionaryRef, - pixel_format: MTLPixelFormat, - width: usize, - height: usize, - plane_index: usize, - ) -> Result { - let mut this = ptr::null(); - let result = CVMetalTextureCacheCreateTextureFromImage( - kCFAllocatorDefault, - self.as_concrete_TypeRef(), - source, - texture_attributes, - pixel_format, - width, - height, - plane_index, - &mut this, - ); - if result == kCVReturnSuccess { - Ok(CVMetalTexture::wrap_under_create_rule(this)) - } else { - Err(anyhow!("could not create texture, code: {}", result)) - } - } - } - - #[link(name = "CoreVideo", kind = "framework")] - extern "C" { - fn CVMetalTextureCacheGetTypeID() -> CFTypeID; - fn CVMetalTextureCacheCreate( - allocator: CFAllocatorRef, - cache_attributes: CFDictionaryRef, - metal_device: *const MTLDevice, - texture_attributes: CFDictionaryRef, - cache_out: *mut CVMetalTextureCacheRef, - ) -> CVReturn; - fn CVMetalTextureCacheCreateTextureFromImage( - allocator: CFAllocatorRef, - texture_cache: CVMetalTextureCacheRef, - source_image: CVImageBufferRef, - texture_attributes: CFDictionaryRef, - pixel_format: MTLPixelFormat, - width: usize, - height: usize, - plane_index: usize, - texture_out: *mut CVMetalTextureRef, - ) -> CVReturn; - } - - #[repr(C)] - pub struct __CVMetalTexture(c_void); - pub type CVMetalTextureRef = *const __CVMetalTexture; - - declare_TCFType!(CVMetalTexture, CVMetalTextureRef); - impl_TCFType!(CVMetalTexture, CVMetalTextureRef, CVMetalTextureGetTypeID); - impl_CFTypeDescription!(CVMetalTexture); - - impl CVMetalTexture { - pub fn as_texture_ref(&self) -> &metal::TextureRef { - unsafe { - let texture = CVMetalTextureGetTexture(self.as_concrete_TypeRef()); - metal::TextureRef::from_ptr(texture as *mut _) - } - } - } - - #[link(name = "CoreVideo", kind = "framework")] - extern "C" { - fn CVMetalTextureGetTypeID() -> CFTypeID; - fn CVMetalTextureGetTexture(texture: CVMetalTextureRef) -> *mut c_void; - } -} - #[cfg(target_os = "macos")] pub mod core_media { #![allow(non_snake_case)] @@ -218,7 +11,6 @@ pub mod core_media { kCMSampleAttachmentKey_NotSync, kCMTimeInvalid, kCMVideoCodecType_H264, CMItemIndex, CMSampleTimingInfo, CMTime, CMTimeMake, CMVideoCodecType, }; - use crate::core_video::{CVImageBuffer, CVImageBufferRef}; use anyhow::{anyhow, Result}; use core_foundation::{ array::{CFArray, CFArrayRef}, @@ -228,6 +20,7 @@ pub mod core_media { impl_CFTypeDescription, impl_TCFType, string::CFString, }; + use core_video::image_buffer::{CVImageBuffer, CVImageBufferRef}; use std::{ffi::c_void, ptr}; #[repr(C)] @@ -422,129 +215,138 @@ pub mod core_media { } #[cfg(target_os = "macos")] -pub mod video_toolbox { +pub mod core_video { #![allow(non_snake_case)] - use super::*; - use crate::{ - core_media::{CMSampleBufferRef, CMTime, CMVideoCodecType}, - core_video::CVImageBufferRef, + #[cfg(target_os = "macos")] + use core_foundation::{ + base::{CFTypeID, TCFType}, + declare_TCFType, impl_CFTypeDescription, impl_TCFType, }; + #[cfg(target_os = "macos")] + use std::ffi::c_void; + + pub use crate::bindings::{ + kCVPixelFormatType_32BGRA, kCVPixelFormatType_420YpCbCr8BiPlanarFullRange, + kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, kCVPixelFormatType_420YpCbCr8Planar, + }; + use crate::bindings::{kCVReturnSuccess, CVReturn}; use anyhow::{anyhow, Result}; - pub use bindings::VTEncodeInfoFlags; - use core_foundation::{base::OSStatus, dictionary::CFDictionaryRef, mach_port::CFAllocatorRef}; + use core_foundation::{ + base::kCFAllocatorDefault, dictionary::CFDictionaryRef, mach_port::CFAllocatorRef, + }; + use foreign_types::ForeignTypeRef; + + use metal::{MTLDevice, MTLPixelFormat}; use std::ptr; #[repr(C)] - pub struct __VTCompressionSession(c_void); - // The ref type must be a pointer to the underlying struct. - pub type VTCompressionSessionRef = *const __VTCompressionSession; + pub struct __CVMetalTextureCache(c_void); + pub type CVMetalTextureCacheRef = *const __CVMetalTextureCache; - declare_TCFType!(VTCompressionSession, VTCompressionSessionRef); + declare_TCFType!(CVMetalTextureCache, CVMetalTextureCacheRef); impl_TCFType!( - VTCompressionSession, - VTCompressionSessionRef, - VTCompressionSessionGetTypeID + CVMetalTextureCache, + CVMetalTextureCacheRef, + CVMetalTextureCacheGetTypeID ); - impl_CFTypeDescription!(VTCompressionSession); + impl_CFTypeDescription!(CVMetalTextureCache); - impl VTCompressionSession { - /// Create a new compression session. - /// + impl CVMetalTextureCache { /// # Safety /// - /// The callback must be a valid function pointer. and the callback_data must be valid - /// in whatever terms that callback expects. - pub unsafe fn new( - width: usize, - height: usize, - codec: CMVideoCodecType, - callback: VTCompressionOutputCallback, - callback_data: *const c_void, - ) -> Result { + /// metal_device must be valid according to CVMetalTextureCacheCreate + pub unsafe fn new(metal_device: *mut MTLDevice) -> Result { let mut this = ptr::null(); - let result = VTCompressionSessionCreate( + let result = CVMetalTextureCacheCreate( + kCFAllocatorDefault, ptr::null(), - width as i32, - height as i32, - codec, + metal_device, ptr::null(), - ptr::null(), - ptr::null(), - callback, - callback_data, &mut this, ); - - if result == 0 { - Ok(Self::wrap_under_create_rule(this)) + if result == kCVReturnSuccess { + Ok(CVMetalTextureCache::wrap_under_create_rule(this)) } else { - Err(anyhow!( - "error creating compression session, code {}", - result - )) + Err(anyhow!("could not create texture cache, code: {}", result)) } } /// # Safety /// - /// The arguments to this function must be valid according to VTCompressionSessionEncodeFrame - pub unsafe fn encode_frame( + /// The arguments to this function must be valid according to CVMetalTextureCacheCreateTextureFromImage + pub unsafe fn create_texture_from_image( &self, - buffer: CVImageBufferRef, - presentation_timestamp: CMTime, - duration: CMTime, - ) -> Result<()> { - let result = VTCompressionSessionEncodeFrame( + source: ::core_video::image_buffer::CVImageBufferRef, + texture_attributes: CFDictionaryRef, + pixel_format: MTLPixelFormat, + width: usize, + height: usize, + plane_index: usize, + ) -> Result { + let mut this = ptr::null(); + let result = CVMetalTextureCacheCreateTextureFromImage( + kCFAllocatorDefault, self.as_concrete_TypeRef(), - buffer, - presentation_timestamp, - duration, - ptr::null(), - ptr::null(), - ptr::null_mut(), + source, + texture_attributes, + pixel_format, + width, + height, + plane_index, + &mut this, ); - if result == 0 { - Ok(()) + if result == kCVReturnSuccess { + Ok(CVMetalTexture::wrap_under_create_rule(this)) } else { - Err(anyhow!("error encoding frame, code {}", result)) + Err(anyhow!("could not create texture, code: {}", result)) } } } - type VTCompressionOutputCallback = Option< - unsafe extern "C" fn( - outputCallbackRefCon: *mut c_void, - sourceFrameRefCon: *mut c_void, - status: OSStatus, - infoFlags: VTEncodeInfoFlags, - sampleBuffer: CMSampleBufferRef, - ), - >; - - #[link(name = "VideoToolbox", kind = "framework")] + #[link(name = "CoreVideo", kind = "framework")] extern "C" { - fn VTCompressionSessionGetTypeID() -> CFTypeID; - fn VTCompressionSessionCreate( + fn CVMetalTextureCacheGetTypeID() -> CFTypeID; + fn CVMetalTextureCacheCreate( allocator: CFAllocatorRef, - width: i32, - height: i32, - codec_type: CMVideoCodecType, - encoder_specification: CFDictionaryRef, - source_image_buffer_attributes: CFDictionaryRef, - compressed_data_allocator: CFAllocatorRef, - output_callback: VTCompressionOutputCallback, - output_callback_ref_con: *const c_void, - compression_session_out: *mut VTCompressionSessionRef, - ) -> OSStatus; - fn VTCompressionSessionEncodeFrame( - session: VTCompressionSessionRef, - image_buffer: CVImageBufferRef, - presentation_timestamp: CMTime, - duration: CMTime, - frame_properties: CFDictionaryRef, - source_frame_ref_con: *const c_void, - output_flags: *mut VTEncodeInfoFlags, - ) -> OSStatus; + cache_attributes: CFDictionaryRef, + metal_device: *const MTLDevice, + texture_attributes: CFDictionaryRef, + cache_out: *mut CVMetalTextureCacheRef, + ) -> CVReturn; + fn CVMetalTextureCacheCreateTextureFromImage( + allocator: CFAllocatorRef, + texture_cache: CVMetalTextureCacheRef, + source_image: ::core_video::image_buffer::CVImageBufferRef, + texture_attributes: CFDictionaryRef, + pixel_format: MTLPixelFormat, + width: usize, + height: usize, + plane_index: usize, + texture_out: *mut CVMetalTextureRef, + ) -> CVReturn; + } + + #[repr(C)] + pub struct __CVMetalTexture(c_void); + pub type CVMetalTextureRef = *const __CVMetalTexture; + + declare_TCFType!(CVMetalTexture, CVMetalTextureRef); + impl_TCFType!(CVMetalTexture, CVMetalTextureRef, CVMetalTextureGetTypeID); + impl_CFTypeDescription!(CVMetalTexture); + + impl CVMetalTexture { + pub fn as_texture_ref(&self) -> &metal::TextureRef { + unsafe { + let texture = CVMetalTextureGetTexture(self.as_concrete_TypeRef()); + metal::TextureRef::from_ptr(texture as *mut _) + } + } + } + + #[link(name = "CoreVideo", kind = "framework")] + extern "C" { + fn CVMetalTextureGetTypeID() -> CFTypeID; + fn CVMetalTextureGetTexture(texture: CVMetalTextureRef) -> *mut c_void; } } diff --git a/crates/storybook/build.rs b/crates/storybook/build.rs index ec0e8944f5..66791cae42 100644 --- a/crates/storybook/build.rs +++ b/crates/storybook/build.rs @@ -1,11 +1,4 @@ fn main() { - // Find WebRTC.framework as a sibling of the executable when running outside of an application bundle. - // TODO: We shouldn't depend on WebRTC in editor - #[cfg(target_os = "macos")] - { - println!("cargo:rustc-link-arg=-Wl,-rpath,@executable_path"); - } - #[cfg(target_os = "windows")] { #[cfg(target_env = "msvc")] diff --git a/crates/workspace/src/shared_screen.rs b/crates/workspace/src/shared_screen.rs index 1d17cfa145..7d109074b7 100644 --- a/crates/workspace/src/shared_screen.rs +++ b/crates/workspace/src/shared_screen.rs @@ -1,11 +1,133 @@ -#[cfg(target_os = "macos")] -mod macos; +use crate::{ + item::{Item, ItemEvent}, + ItemNavHistory, WorkspaceId, +}; +use call::{RemoteVideoTrack, RemoteVideoTrackView, Room}; +use client::{proto::PeerId, User}; +use gpui::{ + div, AppContext as _, Entity, EventEmitter, FocusHandle, Focusable, InteractiveElement, + ParentElement, Render, SharedString, Styled, +}; +use std::sync::Arc; +use ui::{prelude::*, Icon, IconName}; -#[cfg(target_os = "macos")] -pub use macos::*; +pub enum Event { + Close, +} -#[cfg(not(target_os = "macos"))] -mod cross_platform; +pub struct SharedScreen { + pub peer_id: PeerId, + user: Arc, + nav_history: Option, + view: Entity, + focus: FocusHandle, +} -#[cfg(not(target_os = "macos"))] -pub use cross_platform::*; +impl SharedScreen { + pub fn new( + track: RemoteVideoTrack, + peer_id: PeerId, + user: Arc, + room: Entity, + window: &mut Window, + cx: &mut Context, + ) -> Self { + let my_sid = track.sid(); + cx.subscribe(&room, move |_, _, ev, cx| match ev { + call::room::Event::RemoteVideoTrackUnsubscribed { sid } => { + if sid == &my_sid { + cx.emit(Event::Close) + } + } + _ => {} + }) + .detach(); + + let view = cx.new(|cx| RemoteVideoTrackView::new(track.clone(), window, cx)); + cx.subscribe(&view, |_, _, ev, cx| match ev { + call::RemoteVideoTrackViewEvent::Close => cx.emit(Event::Close), + }) + .detach(); + Self { + view, + peer_id, + user, + nav_history: Default::default(), + focus: cx.focus_handle(), + } + } +} + +impl EventEmitter for SharedScreen {} + +impl Focusable for SharedScreen { + fn focus_handle(&self, _: &App) -> FocusHandle { + self.focus.clone() + } +} +impl Render for SharedScreen { + fn render(&mut self, _window: &mut Window, cx: &mut Context) -> impl IntoElement { + div() + .bg(cx.theme().colors().editor_background) + .track_focus(&self.focus) + .key_context("SharedScreen") + .size_full() + .child(self.view.clone()) + } +} + +impl Item for SharedScreen { + type Event = Event; + + fn tab_tooltip_text(&self, _: &App) -> Option { + Some(format!("{}'s screen", self.user.github_login).into()) + } + + fn deactivated(&mut self, _window: &mut Window, cx: &mut Context) { + if let Some(nav_history) = self.nav_history.as_mut() { + nav_history.push::<()>(None, cx); + } + } + + fn tab_icon(&self, _window: &Window, _cx: &App) -> Option { + Some(Icon::new(IconName::Screen)) + } + + fn tab_content_text(&self, _window: &Window, _cx: &App) -> Option { + Some(format!("{}'s screen", self.user.github_login).into()) + } + + fn telemetry_event_text(&self) -> Option<&'static str> { + None + } + + fn set_nav_history( + &mut self, + history: ItemNavHistory, + _window: &mut Window, + _cx: &mut Context, + ) { + self.nav_history = Some(history); + } + + fn clone_on_split( + &self, + _workspace_id: Option, + window: &mut Window, + cx: &mut Context, + ) -> Option> { + Some(cx.new(|cx| Self { + view: self.view.update(cx, |view, cx| view.clone(window, cx)), + peer_id: self.peer_id, + user: self.user.clone(), + nav_history: Default::default(), + focus: cx.focus_handle(), + })) + } + + fn to_item_events(event: &Self::Event, mut f: impl FnMut(ItemEvent)) { + match event { + Event::Close => f(ItemEvent::CloseItem), + } + } +} diff --git a/crates/workspace/src/shared_screen/cross_platform.rs b/crates/workspace/src/shared_screen/cross_platform.rs deleted file mode 100644 index 376140622a..0000000000 --- a/crates/workspace/src/shared_screen/cross_platform.rs +++ /dev/null @@ -1,121 +0,0 @@ -use crate::{ - item::{Item, ItemEvent}, - ItemNavHistory, WorkspaceId, -}; -use call::{RemoteVideoTrack, RemoteVideoTrackView}; -use client::{proto::PeerId, User}; -use gpui::{ - div, AppContext as _, Entity, EventEmitter, FocusHandle, Focusable, InteractiveElement, - ParentElement, Render, SharedString, Styled, -}; -use std::sync::Arc; -use ui::{prelude::*, Icon, IconName}; - -pub enum Event { - Close, -} - -pub struct SharedScreen { - pub peer_id: PeerId, - user: Arc, - nav_history: Option, - view: Entity, - focus: FocusHandle, -} - -impl SharedScreen { - pub fn new( - track: RemoteVideoTrack, - peer_id: PeerId, - user: Arc, - window: &mut Window, - cx: &mut Context, - ) -> Self { - let view = cx.new(|cx| RemoteVideoTrackView::new(track.clone(), window, cx)); - cx.subscribe(&view, |_, _, ev, cx| match ev { - call::RemoteVideoTrackViewEvent::Close => cx.emit(Event::Close), - }) - .detach(); - Self { - view, - peer_id, - user, - nav_history: Default::default(), - focus: cx.focus_handle(), - } - } -} - -impl EventEmitter for SharedScreen {} - -impl Focusable for SharedScreen { - fn focus_handle(&self, _: &App) -> FocusHandle { - self.focus.clone() - } -} -impl Render for SharedScreen { - fn render(&mut self, _window: &mut Window, cx: &mut Context) -> impl IntoElement { - div() - .bg(cx.theme().colors().editor_background) - .track_focus(&self.focus) - .key_context("SharedScreen") - .size_full() - .child(self.view.clone()) - } -} - -impl Item for SharedScreen { - type Event = Event; - - fn tab_tooltip_text(&self, _: &App) -> Option { - Some(format!("{}'s screen", self.user.github_login).into()) - } - - fn deactivated(&mut self, _window: &mut Window, cx: &mut Context) { - if let Some(nav_history) = self.nav_history.as_mut() { - nav_history.push::<()>(None, cx); - } - } - - fn tab_icon(&self, _window: &Window, _cx: &App) -> Option { - Some(Icon::new(IconName::Screen)) - } - - fn tab_content_text(&self, _window: &Window, _cx: &App) -> Option { - Some(format!("{}'s screen", self.user.github_login).into()) - } - - fn telemetry_event_text(&self) -> Option<&'static str> { - None - } - - fn set_nav_history( - &mut self, - history: ItemNavHistory, - _window: &mut Window, - _cx: &mut Context, - ) { - self.nav_history = Some(history); - } - - fn clone_on_split( - &self, - _workspace_id: Option, - window: &mut Window, - cx: &mut Context, - ) -> Option> { - Some(cx.new(|cx| Self { - view: self.view.update(cx, |view, cx| view.clone(window, cx)), - peer_id: self.peer_id, - user: self.user.clone(), - nav_history: Default::default(), - focus: cx.focus_handle(), - })) - } - - fn to_item_events(event: &Self::Event, mut f: impl FnMut(ItemEvent)) { - match event { - Event::Close => f(ItemEvent::CloseItem), - } - } -} diff --git a/crates/workspace/src/shared_screen/macos.rs b/crates/workspace/src/shared_screen/macos.rs deleted file mode 100644 index e943b18692..0000000000 --- a/crates/workspace/src/shared_screen/macos.rs +++ /dev/null @@ -1,132 +0,0 @@ -use crate::{ - item::{Item, ItemEvent}, - ItemNavHistory, WorkspaceId, -}; -use anyhow::Result; -use call::participant::{Frame, RemoteVideoTrack}; -use client::{proto::PeerId, User}; -use futures::StreamExt; -use gpui::{ - div, surface, App, Context, Entity, EventEmitter, FocusHandle, Focusable, InteractiveElement, - ParentElement, Render, SharedString, Styled, Task, Window, -}; -use std::sync::{Arc, Weak}; -use ui::{prelude::*, Icon, IconName}; - -pub enum Event { - Close, -} - -pub struct SharedScreen { - track: Weak, - frame: Option, - pub peer_id: PeerId, - user: Arc, - nav_history: Option, - _maintain_frame: Task>, - focus: FocusHandle, -} - -impl SharedScreen { - pub fn new( - track: Arc, - peer_id: PeerId, - user: Arc, - window: &mut Window, - cx: &mut Context, - ) -> Self { - cx.focus_handle(); - let mut frames = track.frames(); - Self { - track: Arc::downgrade(&track), - frame: None, - peer_id, - user, - nav_history: Default::default(), - _maintain_frame: cx.spawn_in(window, async move |this, cx| { - while let Some(frame) = frames.next().await { - this.update(cx, |this, cx| { - this.frame = Some(frame); - cx.notify(); - })?; - } - this.update(cx, |_, cx| cx.emit(Event::Close))?; - Ok(()) - }), - focus: cx.focus_handle(), - } - } -} - -impl EventEmitter for SharedScreen {} - -impl Focusable for SharedScreen { - fn focus_handle(&self, _: &App) -> FocusHandle { - self.focus.clone() - } -} -impl Render for SharedScreen { - fn render(&mut self, _window: &mut Window, cx: &mut Context) -> impl IntoElement { - div() - .bg(cx.theme().colors().editor_background) - .track_focus(&self.focus) - .key_context("SharedScreen") - .size_full() - .children( - self.frame - .as_ref() - .map(|frame| surface(frame.image()).size_full()), - ) - } -} - -impl Item for SharedScreen { - type Event = Event; - - fn tab_tooltip_text(&self, _: &App) -> Option { - Some(format!("{}'s screen", self.user.github_login).into()) - } - - fn deactivated(&mut self, _: &mut Window, cx: &mut Context) { - if let Some(nav_history) = self.nav_history.as_mut() { - nav_history.push::<()>(None, cx); - } - } - - fn tab_icon(&self, _window: &Window, _cx: &App) -> Option { - Some(Icon::new(IconName::Screen)) - } - - fn tab_content_text(&self, _window: &Window, _cx: &App) -> Option { - Some(format!("{}'s screen", self.user.github_login).into()) - } - - fn telemetry_event_text(&self) -> Option<&'static str> { - None - } - - fn set_nav_history( - &mut self, - history: ItemNavHistory, - _window: &mut Window, - _: &mut Context, - ) { - self.nav_history = Some(history); - } - - fn clone_on_split( - &self, - _workspace_id: Option, - window: &mut Window, - cx: &mut Context, - ) -> Option> { - let track = self.track.upgrade()?; - Some(cx.new(|cx| Self::new(track, self.peer_id, self.user.clone(), window, cx))) - } - - fn to_item_events(event: &Self::Event, mut f: impl FnMut(ItemEvent)) { - match event { - Event::Close => f(ItemEvent::CloseItem), - } - } -} diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 99160b1701..d289d52e83 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -4435,8 +4435,8 @@ impl Workspace { cx: &mut App, ) -> Option> { let call = self.active_call()?; - let room = call.read(cx).room()?.read(cx); - let participant = room.remote_participant_for_peer_id(peer_id)?; + let room = call.read(cx).room()?.clone(); + let participant = room.read(cx).remote_participant_for_peer_id(peer_id)?; let track = participant.video_tracks.values().next()?.clone(); let user = participant.user.clone(); @@ -4446,7 +4446,7 @@ impl Workspace { } } - Some(cx.new(|cx| SharedScreen::new(track, peer_id, user.clone(), window, cx))) + Some(cx.new(|cx| SharedScreen::new(track, peer_id, user.clone(), room.clone(), window, cx))) } pub fn on_window_activation_changed(&mut self, window: &mut Window, cx: &mut Context) { diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index ae9759c167..059e5dbc7f 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -162,7 +162,7 @@ tree-sitter-md.workspace = true tree-sitter-rust.workspace = true workspace = { workspace = true, features = ["test-support"] } -[package.metadata.bundle-dev] +[package.metadata.bundle] icon = ["resources/app-icon-dev@2x.png", "resources/app-icon-dev.png"] identifier = "dev.zed.Zed-Dev" name = "Zed Dev" diff --git a/crates/zed/build.rs b/crates/zed/build.rs index b97bda1681..7c097ba068 100644 --- a/crates/zed/build.rs +++ b/crates/zed/build.rs @@ -4,15 +4,6 @@ fn main() { if cfg!(target_os = "macos") { println!("cargo:rustc-env=MACOSX_DEPLOYMENT_TARGET=10.15.7"); - println!("cargo:rerun-if-env-changed=ZED_BUNDLE"); - if std::env::var("ZED_BUNDLE").ok().as_deref() == Some("true") { - // Find WebRTC.framework in the Frameworks folder when running as part of an application bundle. - println!("cargo:rustc-link-arg=-Wl,-rpath,@executable_path/../Frameworks"); - } else { - // Find WebRTC.framework as a sibling of the executable when running outside of an application bundle. - println!("cargo:rustc-link-arg=-Wl,-rpath,@executable_path"); - } - // Weakly link ReplayKit to ensure Zed can be used on macOS 10.15+. println!("cargo:rustc-link-arg=-Wl,-weak_framework,ReplayKit"); diff --git a/nix/build.nix b/nix/build.nix index 6590c9b445..77e7804538 100644 --- a/nix/build.nix +++ b/nix/build.nix @@ -197,12 +197,6 @@ craneLib.buildPackage ( lib.recursiveUpdate commonArgs { inherit cargoArtifacts; - patches = lib.optionals stdenv.hostPlatform.isDarwin [ - # Livekit requires Swift 6 - # We need this until livekit-rust sdk is used - ../script/patches/use-cross-platform-livekit.patch - ]; - dontUseCmakeConfigure = true; # without the env var generate-licenses fails due to crane's fetchCargoVendor, see: diff --git a/script/bundle-mac b/script/bundle-mac index dcdf0f765d..9aa9763e9c 100755 --- a/script/bundle-mac +++ b/script/bundle-mac @@ -221,13 +221,9 @@ function sign_app_binaries() { local app_path=$1 local architecture=$2 local architecture_dir=$3 - echo "Copying WebRTC.framework into the frameworks folder" rm -rf "${app_path}/Contents/Frameworks" mkdir -p "${app_path}/Contents/Frameworks" - if [ "$local_arch" = false ]; then - cp -R target/${local_target_triple}/${target_dir}/WebRTC.framework "${app_path}/Contents/Frameworks/" - else - cp -R target/${target_dir}/WebRTC.framework "${app_path}/Contents/Frameworks/" + if [ "$local_arch" = true ]; then cp -R target/${target_dir}/cli "${app_path}/Contents/MacOS/" fi @@ -240,7 +236,6 @@ function sign_app_binaries() { if [[ $can_code_sign = true ]]; then echo "Code signing binaries" # sequence of codesign commands modeled after this example: https://developer.apple.com/forums/thread/701514 - /usr/bin/codesign --deep --force --timestamp --sign "$IDENTITY" "${app_path}/Contents/Frameworks/WebRTC.framework" -v /usr/bin/codesign --deep --force --timestamp --options runtime --sign "$IDENTITY" "${app_path}/Contents/MacOS/cli" -v /usr/bin/codesign --deep --force --timestamp --options runtime --sign "$IDENTITY" "${app_path}/Contents/MacOS/git" -v /usr/bin/codesign --deep --force --timestamp --options runtime --entitlements crates/zed/resources/zed.entitlements --sign "$IDENTITY" "${app_path}/Contents/MacOS/zed" -v diff --git a/script/check-rust-livekit-macos b/script/check-rust-livekit-macos deleted file mode 100755 index 1afdc81b22..0000000000 --- a/script/check-rust-livekit-macos +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env bash - - -set -exuo pipefail - -git apply script/patches/use-cross-platform-livekit.patch - -# Re-enable error skipping for this check, so that we can unapply the patch -set +e - -cargo check -p workspace -exit_code=$? - -# Disable error skipping again -set -e - -git apply -R script/patches/use-cross-platform-livekit.patch - -exit "$exit_code" diff --git a/script/patches/use-cross-platform-livekit.patch b/script/patches/use-cross-platform-livekit.patch deleted file mode 100644 index 93010a0eec..0000000000 --- a/script/patches/use-cross-platform-livekit.patch +++ /dev/null @@ -1,59 +0,0 @@ -diff --git a/crates/call/Cargo.toml b/crates/call/Cargo.toml -index 9ba10e56ba..bb69440691 100644 ---- a/crates/call/Cargo.toml -+++ b/crates/call/Cargo.toml -@@ -41,10 +41,10 @@ serde_derive.workspace = true - telemetry.workspace = true - util.workspace = true - --[target.'cfg(target_os = "macos")'.dependencies] -+[target.'cfg(any())'.dependencies] - livekit_client_macos.workspace = true - --[target.'cfg(not(target_os = "macos"))'.dependencies] -+[target.'cfg(all())'.dependencies] - livekit_client.workspace = true - - [dev-dependencies] -diff --git a/crates/call/src/call.rs b/crates/call/src/call.rs -index 5e212d35b7..a8f9e8f43e 100644 ---- a/crates/call/src/call.rs -+++ b/crates/call/src/call.rs -@@ -1,13 +1,13 @@ - pub mod call_settings; - --#[cfg(target_os = "macos")] -+#[cfg(any())] - mod macos; - --#[cfg(target_os = "macos")] -+#[cfg(any())] - pub use macos::*; - --#[cfg(not(target_os = "macos"))] -+#[cfg(all())] - mod cross_platform; - --#[cfg(not(target_os = "macos"))] -+#[cfg(all())] - pub use cross_platform::*; -diff --git a/crates/workspace/src/shared_screen.rs b/crates/workspace/src/shared_screen.rs -index 1d17cfa145..f845234987 100644 ---- a/crates/workspace/src/shared_screen.rs -+++ b/crates/workspace/src/shared_screen.rs -@@ -1,11 +1,11 @@ --#[cfg(target_os = "macos")] -+#[cfg(any())] - mod macos; - --#[cfg(target_os = "macos")] -+#[cfg(any())] - pub use macos::*; - --#[cfg(not(target_os = "macos"))] -+#[cfg(all())] - mod cross_platform; - --#[cfg(not(target_os = "macos"))] -+#[cfg(all())] - pub use cross_platform::*; diff --git a/typos.toml b/typos.toml index 7c8bd02c74..9500d8f895 100644 --- a/typos.toml +++ b/typos.toml @@ -41,8 +41,6 @@ extend-exclude = [ "docs/theme/css/", # Spellcheck triggers on `|Fixe[sd]|` regex part. "script/danger/dangerfile.ts", - # Hashes are not typos - "script/patches/use-cross-platform-livekit.patch" ] [default]