WIP: Start on live_kit crate that uses a C-based bridge

Nathan Sobo created

Change summary

Cargo.lock                                                              | 11 
crates/capture/Cargo.toml                                               |  3 
crates/capture/build.rs                                                 | 67 
crates/capture/src/main.rs                                              | 24 
crates/live_kit/Cargo.toml                                              | 15 
crates/live_kit/LiveKitBridge/.gitignore                                |  9 
crates/live_kit/LiveKitBridge/Package.resolved                          | 50 
crates/live_kit/LiveKitBridge/Package.swift                             | 27 
crates/live_kit/LiveKitBridge/README.md                                 |  3 
crates/live_kit/LiveKitBridge/Sources/LiveKitBridge/LiveKitBridge.swift | 12 
crates/live_kit/build.rs                                                | 95 
crates/live_kit/src/live_kit.rs                                         | 24 
12 files changed, 253 insertions(+), 87 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -765,13 +765,12 @@ dependencies = [
  "foreign-types",
  "futures",
  "gpui",
+ "live_kit",
  "log",
  "media",
  "objc",
  "parking_lot 0.11.2",
  "postage",
- "serde",
- "serde_json",
  "simplelog",
 ]
 
@@ -2918,6 +2917,14 @@ dependencies = [
  "rand_chacha 0.3.1",
 ]
 
+[[package]]
+name = "live_kit"
+version = "0.1.0"
+dependencies = [
+ "serde",
+ "serde_json",
+]
+
 [[package]]
 name = "lock_api"
 version = "0.4.7"

crates/capture/Cargo.toml 🔗

@@ -6,6 +6,7 @@ description = "An example of screen capture"
 
 [dependencies]
 gpui = { path = "../gpui" }
+live_kit = { path = "../live_kit" }
 media = { path = "../media" }
 
 anyhow = "1.0.38"
@@ -25,5 +26,3 @@ simplelog = "0.9"
 
 [build-dependencies]
 bindgen = "0.59.2"
-serde = { version = "1.0", features = ["derive", "rc"] }
-serde_json = { version = "1.0", features = ["preserve_order"] }

crates/capture/build.rs 🔗

@@ -1,68 +1,15 @@
-use serde::Deserialize;
 use std::{env, path::PathBuf, process::Command};
 
-#[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<String>,
-    pub runtime_library_import_paths: Vec<String>,
-    pub runtime_resource_path: String,
-}
-
-#[derive(Debug, Deserialize)]
-pub struct SwiftTarget {
-    pub target: SwiftTargetInfo,
-    pub paths: SwiftPaths,
-}
-
-const MACOS_TARGET_VERSION: &str = "12";
-
-pub fn link_swift_libs() {
-    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;
-
-    let swift_target_info: SwiftTarget = serde_json::from_slice(&swift_target_info_str).unwrap();
-    if swift_target_info.target.libraries_require_rpath {
-        panic!("Libraries require RPath! Change minimum MacOS value to fix.")
-    }
+fn main() {
+    println!(
+        "cargo:rustc-link-search=framework={}",
+        "crates/live_kit/LiveKitBridge/.build/arm64-apple-macosx/debug"
+    );
 
-    swift_target_info
-        .paths
-        .runtime_library_paths
-        .iter()
-        .for_each(|path| {
-            println!("cargo:rustc-link-search=native={}", path);
-        });
-}
+    // Find frameworks as a sibling of the executable at runtime
+    println!("cargo:rustc-link-arg=-Wl,-rpath,@executable_path");
 
-fn main() {
-    link_swift_libs();
-    println!("cargo:rerun-if-changed=/Users/as-cii/Library/Developer/Xcode/DerivedData/LiveKitObjC-ftgpxknhsgkrocbhhgjkyyvkgkbj/Build/Products/Debug/libLiveKitObjC.a");
-    println!("cargo:rustc-link-search=native=/Users/as-cii/Library/Developer/Xcode/DerivedData/LiveKitObjC-ftgpxknhsgkrocbhhgjkyyvkgkbj/Build/Products/libs");
-    println!("cargo:rustc-link-search=framework=/Users/as-cii/Library/Developer/Xcode/DerivedData/LiveKitObjC-ftgpxknhsgkrocbhhgjkyyvkgkbj/Build/Products/frameworks");
-    println!("cargo:rustc-link-lib=static=LiveKitObjC");
     println!("cargo:rustc-link-lib=framework=ScreenCaptureKit");
-    println!("cargo:rustc-link-lib=framework=WebRTC");
     println!("cargo:rustc-env=MACOSX_DEPLOYMENT_TARGET=12.3");
 
     let sdk_path = String::from_utf8(

crates/capture/src/main.rs 🔗

@@ -22,6 +22,7 @@ use gpui::{
     platform::current::Surface,
     Menu, MenuItem, ViewContext,
 };
+use live_kit::Room;
 use log::LevelFilter;
 use media::{
     core_media::{
@@ -47,29 +48,6 @@ const NSUTF8StringEncoding: NSUInteger = 4;
 
 actions!(capture, [Quit]);
 
-extern "C" {
-    fn LKRoomCreate() -> *const c_void;
-    fn LKRoomDestroy(ptr: *const c_void);
-}
-
-struct Room {
-    native_room: *const c_void,
-}
-
-impl Room {
-    pub fn new() -> Self {
-        Self {
-            native_room: unsafe { LKRoomCreate() },
-        }
-    }
-}
-
-impl Drop for Room {
-    fn drop(&mut self) {
-        unsafe { LKRoomDestroy(self.native_room) }
-    }
-}
-
 fn main() {
     println!("Creating room...");
     let room = Room::new();

crates/live_kit/Cargo.toml 🔗

@@ -0,0 +1,15 @@
+[package]
+name = "live_kit"
+version = "0.1.0"
+edition = "2021"
+description = "Bindings to LiveKit Swift client SDK"
+
+[lib]
+path = "src/live_kit.rs"
+doctest = false
+
+[dependencies]
+
+[build-dependencies]
+serde = { version = "1.0", features = ["derive", "rc"] }
+serde_json = { version = "1.0", features = ["preserve_order"] }

crates/live_kit/LiveKitBridge/.gitignore 🔗

@@ -0,0 +1,9 @@
+.DS_Store
+/.build
+/Packages
+/*.xcodeproj
+xcuserdata/
+DerivedData/
+.swiftpm/config/registries.json
+.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
+.netrc

crates/live_kit/LiveKitBridge/Package.resolved 🔗

@@ -0,0 +1,50 @@
+{
+  "pins" : [
+    {
+      "identity" : "client-sdk-swift",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/livekit/client-sdk-swift.git",
+      "state" : {
+        "revision" : "7e7decf3a09de4a169dfc0445a14d9fd2d8db58d",
+        "version" : "1.0.4"
+      }
+    },
+    {
+      "identity" : "promises",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/google/promises.git",
+      "state" : {
+        "revision" : "3e4e743631e86c8c70dbc6efdc7beaa6e90fd3bb",
+        "version" : "2.1.1"
+      }
+    },
+    {
+      "identity" : "specs",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/webrtc-sdk/Specs.git",
+      "state" : {
+        "revision" : "5225f2de4b6d0098803b3a0e55b255a41f293dad",
+        "version" : "104.5112.2"
+      }
+    },
+    {
+      "identity" : "swift-log",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/apple/swift-log.git",
+      "state" : {
+        "revision" : "6fe203dc33195667ce1759bf0182975e4653ba1c",
+        "version" : "1.4.4"
+      }
+    },
+    {
+      "identity" : "swift-protobuf",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/apple/swift-protobuf.git",
+      "state" : {
+        "revision" : "b8230909dedc640294d7324d37f4c91ad3dcf177",
+        "version" : "1.20.1"
+      }
+    }
+  ],
+  "version" : 2
+}

crates/live_kit/LiveKitBridge/Package.swift 🔗

@@ -0,0 +1,27 @@
+// swift-tools-version: 5.6
+
+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", from: "1.0.0"),
+    ],
+    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")]),
+    ]
+)

crates/live_kit/LiveKitBridge/Sources/LiveKitBridge/LiveKitBridge.swift 🔗

@@ -0,0 +1,12 @@
+import Foundation
+import LiveKit
+
+@_cdecl("LKRoomCreate")
+public func LKRoomCreate() -> UnsafeMutableRawPointer  {
+    Unmanaged.passRetained(Room()).toOpaque()
+}
+
+@_cdecl("LKRoomDestroy")
+public func LKRoomDestroy(ptr: UnsafeRawPointer)  {
+    let _ = Unmanaged<Room>.fromOpaque(ptr).takeRetainedValue();
+}

crates/live_kit/build.rs 🔗

@@ -0,0 +1,95 @@
+use serde::Deserialize;
+use std::{env, path::PathBuf, process::Command};
+
+#[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<String>,
+    pub runtime_library_import_paths: Vec<String>,
+    pub runtime_resource_path: String,
+}
+
+#[derive(Debug, Deserialize)]
+pub struct SwiftTarget {
+    pub target: SwiftTargetInfo,
+    pub paths: SwiftPaths,
+}
+
+const MACOS_TARGET_VERSION: &str = "12";
+
+fn main() {
+    build_bridge();
+    link_swift_stdlib();
+}
+
+fn build_bridge() {
+    let profile = env::var("PROFILE").unwrap();
+    let package_name = "LiveKitBridge";
+    let package_root = env::current_dir().unwrap().join(package_name);
+    if !Command::new("swift")
+        .args(&["build", "-c", &profile])
+        .current_dir(&package_root)
+        .status()
+        .unwrap()
+        .success()
+    {
+        panic!(
+            "Failed to compile swift package in {}",
+            package_root.display()
+        );
+    }
+
+    let swift_target_info = get_swift_target();
+    let swift_out_dir_path = format!(
+        "{}/.build/{}/{}",
+        package_root.display(),
+        swift_target_info.target.unversioned_triple,
+        profile
+    );
+
+    println!("cargo:rustc-link-search=native={}", swift_out_dir_path);
+    println!(
+        "cargo:rustc-link-search=framework={}",
+        "/Users/nathan/src/zed/crates/live_kit/frameworks"
+    );
+    println!("cargo:rustc-link-lib=static={}", package_name);
+    println!("cargo:rustc-link-lib=framework=WebRTC");
+}
+
+fn link_swift_stdlib() {
+    let target = get_swift_target();
+    if target.target.libraries_require_rpath {
+        panic!("Libraries require RPath! Change minimum MacOS value to fix.")
+    }
+
+    target.paths.runtime_library_paths.iter().for_each(|path| {
+        println!("cargo:rustc-link-search=native={}", 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()
+}

crates/live_kit/src/live_kit.rs 🔗

@@ -0,0 +1,24 @@
+use std::ffi::c_void;
+
+extern "C" {
+    fn LKRoomCreate() -> *const c_void;
+    fn LKRoomDestroy(ptr: *const c_void);
+}
+
+pub struct Room {
+    native_room: *const c_void,
+}
+
+impl Room {
+    pub fn new() -> Self {
+        Self {
+            native_room: unsafe { LKRoomCreate() },
+        }
+    }
+}
+
+impl Drop for Room {
+    fn drop(&mut self) {
+        unsafe { LKRoomDestroy(self.native_room) }
+    }
+}