Add native self-hosted instance connection to fluxer_desktop
Trimmed monorepo checkout (fluxer_desktop + packages/voice_engine_v2 + tools/ci) with a "Connect to a Different Server" menu item and popout that lets the desktop app switch to any self-hosted Fluxer instance, plus fixes for well-known discovery on single-domain self-hosted deployments and a false-positive ERR_ABORTED on same-origin client redirects during the switch. Defaults to chat.fluxr.chat and uses an isolated userData directory from the official build.
This commit is contained in:
@@ -0,0 +1 @@
|
||||
{"v":1}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"git": {
|
||||
"sha1": "ba0c31290ad6f7836ef96efd62167c3501ed07fa"
|
||||
},
|
||||
"path_in_vcs": "webrtc-sys"
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
# Using the same .clang-format as libwebrtc
|
||||
# https://github.com/webrtc-sdk/webrtc/blob/m104_release/.clang-format
|
||||
BasedOnStyle: Chromium
|
||||
---
|
||||
Language: Java
|
||||
BasedOnStyle: Google
|
||||
---
|
||||
Language: ObjC
|
||||
BasedOnStyle: Google
|
||||
BinPackParameters: false
|
||||
BinPackArguments: false
|
||||
ColumnLimit: 100
|
||||
ObjCBlockIndentWidth: 2
|
||||
AllowAllParametersOfDeclarationOnNextLine: true
|
||||
AlignOperands: false
|
||||
AlwaysBreakBeforeMultilineStrings: false
|
||||
AllowShortFunctionsOnASingleLine: Inline
|
||||
BreakBeforeTernaryOperators: false
|
||||
IndentWrappedFunctionNames: true
|
||||
ContinuationIndentWidth: 4
|
||||
ObjCSpaceBeforeProtocolList: true
|
||||
---
|
||||
Language: Cpp
|
||||
IncludeBlocks: Regroup
|
||||
@@ -0,0 +1,359 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [0.3.23](https://github.com/livekit/rust-sdks/compare/rust-sdks/webrtc-sys@0.3.22...rust-sdks/webrtc-sys@0.3.23) - 2026-02-16
|
||||
|
||||
### Other
|
||||
|
||||
- add is_screencast to VideoSource ([#896](https://github.com/livekit/rust-sdks/pull/896))
|
||||
|
||||
## [0.3.22](https://github.com/livekit/rust-sdks/compare/rust-sdks/webrtc-sys@0.3.21...rust-sdks/webrtc-sys@0.3.22) - 2026-02-09
|
||||
|
||||
### Fixed
|
||||
|
||||
- fix the 440->441 samples issue and pass a noop callback for release ([#848](https://github.com/livekit/rust-sdks/pull/848))
|
||||
|
||||
### Other
|
||||
|
||||
- Use workspace dependencies & settings ([#856](https://github.com/livekit/rust-sdks/pull/856))
|
||||
|
||||
## [0.3.21](https://github.com/livekit/rust-sdks/compare/rust-sdks/webrtc-sys@0.3.20...rust-sdks/webrtc-sys@0.3.21) - 2026-01-15
|
||||
|
||||
### Other
|
||||
|
||||
- Rename namespace for ffi bridge code. ([#836](https://github.com/livekit/rust-sdks/pull/836))
|
||||
|
||||
## [0.3.20](https://github.com/livekit/rust-sdks/compare/rust-sdks/webrtc-sys@0.3.19...rust-sdks/webrtc-sys@0.3.20) - 2025-12-19
|
||||
|
||||
### Fixed
|
||||
|
||||
- Exclude the desktop-capturer module link for mobile. ([#817](https://github.com/livekit/rust-sdks/pull/817))
|
||||
|
||||
## [0.3.19](https://github.com/livekit/rust-sdks/compare/rust-sdks/webrtc-sys@0.3.18...rust-sdks/webrtc-sys@0.3.19) - 2025-12-17
|
||||
|
||||
### Fixed
|
||||
|
||||
- lazy loading for additional dependencies. ([#814](https://github.com/livekit/rust-sdks/pull/814))
|
||||
|
||||
### Other
|
||||
|
||||
- Expose WebRTC's audio_mixer ([#806](https://github.com/livekit/rust-sdks/pull/806))
|
||||
|
||||
## [0.3.18](https://github.com/livekit/rust-sdks/compare/rust-sdks/webrtc-sys@0.3.17...rust-sdks/webrtc-sys@0.3.18) - 2025-12-04
|
||||
|
||||
### Other
|
||||
|
||||
- initialize frame_.samples_per_channel_ ([#779](https://github.com/livekit/rust-sdks/pull/779))
|
||||
- Expose desktop capturer ([#725](https://github.com/livekit/rust-sdks/pull/725))
|
||||
|
||||
## [0.3.17](https://github.com/livekit/rust-sdks/compare/rust-sdks/webrtc-sys@0.3.16...rust-sdks/webrtc-sys@0.3.17) - 2025-11-20
|
||||
|
||||
### Fixed
|
||||
|
||||
- fix Android libwebrtc builds ([#791](https://github.com/livekit/rust-sdks/pull/791))
|
||||
|
||||
### Other
|
||||
|
||||
- Enable H265 encoding & decoding on Nvidia GPU ([#776](https://github.com/livekit/rust-sdks/pull/776))
|
||||
- copy desktop_capture.ninja into C++ library archive ([#787](https://github.com/livekit/rust-sdks/pull/787))
|
||||
- enable Pipewire (Wayland) and X11 desktop capture ([#784](https://github.com/livekit/rust-sdks/pull/784))
|
||||
|
||||
## [0.3.16](https://github.com/livekit/rust-sdks/compare/rust-sdks/webrtc-sys@0.3.15...rust-sdks/webrtc-sys@0.3.16) - 2025-10-27
|
||||
|
||||
### Fixed
|
||||
|
||||
- fix unable to locate __arm_tpidr2_save for android ffi. ([#765](https://github.com/livekit/rust-sdks/pull/765))
|
||||
|
||||
### Other
|
||||
|
||||
- Linux hardware acceleration build fixes ([#753](https://github.com/livekit/rust-sdks/pull/753))
|
||||
|
||||
## [0.3.15](https://github.com/livekit/rust-sdks/compare/rust-sdks/webrtc-sys@0.3.14...rust-sdks/webrtc-sys@0.3.15) - 2025-10-22
|
||||
|
||||
### Other
|
||||
|
||||
- License check ([#746](https://github.com/livekit/rust-sdks/pull/746))
|
||||
- put examples in root Cargo workspace ([#731](https://github.com/livekit/rust-sdks/pull/731))
|
||||
|
||||
## [0.3.14](https://github.com/livekit/rust-sdks/compare/rust-sdks/webrtc-sys@0.3.13...rust-sdks/webrtc-sys@0.3.14) - 2025-10-13
|
||||
|
||||
### Added
|
||||
|
||||
- *(e2ee)* add data channel encryption ([#708](https://github.com/livekit/rust-sdks/pull/708))
|
||||
|
||||
### Fixed
|
||||
|
||||
- fix some potential audio issues, clean up the code a bit, and suppress some warnings ([#737](https://github.com/livekit/rust-sdks/pull/737))
|
||||
- fix linux so link issue. ([#733](https://github.com/livekit/rust-sdks/pull/733))
|
||||
- change search_dirs to use cc --print-search-dirs instead of clang --print-search-dirs ([#697](https://github.com/livekit/rust-sdks/pull/697))
|
||||
|
||||
### Other
|
||||
|
||||
- bump libwebrtc libs version for webrtc-sys. ([#741](https://github.com/livekit/rust-sdks/pull/741))
|
||||
- Enable buffer scaling ([#473](https://github.com/livekit/rust-sdks/pull/473))
|
||||
|
||||
## [0.3.13](https://github.com/livekit/rust-sdks/compare/rust-sdks/webrtc-sys@0.3.12...rust-sdks/webrtc-sys@0.3.13) - 2025-10-03
|
||||
|
||||
### Other
|
||||
|
||||
- Fix empty audio frames after resample ([#722](https://github.com/livekit/rust-sdks/pull/722))
|
||||
|
||||
## [0.3.12](https://github.com/livekit/rust-sdks/compare/rust-sdks/webrtc-sys@0.3.11...rust-sdks/webrtc-sys@0.3.12) - 2025-09-29
|
||||
|
||||
### Fixed
|
||||
|
||||
- fix Builds/E2E Tests CI. ([#715](https://github.com/livekit/rust-sdks/pull/715))
|
||||
|
||||
### Other
|
||||
|
||||
- nvidia codec improve ([#721](https://github.com/livekit/rust-sdks/pull/721))
|
||||
- Upgrade libwebrtc to m137. ([#696](https://github.com/livekit/rust-sdks/pull/696))
|
||||
|
||||
## [0.3.11](https://github.com/livekit/rust-sdks/compare/rust-sdks/webrtc-sys@0.3.10...rust-sdks/webrtc-sys@0.3.11) - 2025-09-09
|
||||
|
||||
### Other
|
||||
|
||||
- Optional flags for video hw codec. ([#701](https://github.com/livekit/rust-sdks/pull/701))
|
||||
|
||||
## [0.3.10](https://github.com/livekit/rust-sdks/compare/rust-sdks/webrtc-sys@0.3.9...rust-sdks/webrtc-sys@0.3.10) - 2025-09-03
|
||||
|
||||
### Added
|
||||
|
||||
- VA-API support for linux. ([#638](https://github.com/livekit/rust-sdks/pull/638))
|
||||
|
||||
### Fixed
|
||||
|
||||
- hardware rendering ([#695](https://github.com/livekit/rust-sdks/pull/695))
|
||||
# Changelog
|
||||
|
||||
## [0.3.9](https://github.com/livekit/rust-sdks/compare/rust-sdks/webrtc-sys@0.3.8...rust-sdks/webrtc-sys@0.3.9) - 2025-06-17
|
||||
|
||||
### Other
|
||||
|
||||
- updated the following local packages: webrtc-sys-build
|
||||
|
||||
## [0.3.8](https://github.com/livekit/rust-sdks/compare/rust-sdks/webrtc-sys@0.3.7...rust-sdks/webrtc-sys@0.3.8) - 2025-06-11
|
||||
|
||||
### Fixed
|
||||
|
||||
- fix libwebrtc.jar build issue ([#586](https://github.com/livekit/rust-sdks/pull/586))
|
||||
|
||||
### Other
|
||||
|
||||
- bump version for webrtc (fix win CI) ([#650](https://github.com/livekit/rust-sdks/pull/650))
|
||||
- try to fix webrtc build for iOS/macOS. ([#646](https://github.com/livekit/rust-sdks/pull/646))
|
||||
- remove ([#633](https://github.com/livekit/rust-sdks/pull/633))
|
||||
- expose apm stream_delay ([#616](https://github.com/livekit/rust-sdks/pull/616))
|
||||
- Add i420_to_nv12 ([#605](https://github.com/livekit/rust-sdks/pull/605))
|
||||
- ffi-v0.13.0 ([#590](https://github.com/livekit/rust-sdks/pull/590))
|
||||
- add AudioProcessingModule ([#580](https://github.com/livekit/rust-sdks/pull/580))
|
||||
|
||||
## [0.3.7] - 2025-02-05
|
||||
|
||||
### Added
|
||||
|
||||
- Expose DataChannel.bufferedAmount property
|
||||
|
||||
## [0.3.6] - 2024-12-14
|
||||
|
||||
### Added
|
||||
|
||||
- bump libwebrtc to m125
|
||||
## 0.3.33 (2026-05-29)
|
||||
|
||||
### Fixes
|
||||
|
||||
- Add native video pipeline timing instrumentation for local video measurements, exposing local publish and subscribe timing through async streams and subscriber overlay GPU upload and receive-to-GPU latency metrics through explicit timing observers.
|
||||
|
||||
## 0.3.32 (2026-05-21)
|
||||
|
||||
### Fixes
|
||||
|
||||
#### feat: add Android application context initialization for PlatformAudio support.
|
||||
|
||||
Android requires `ContextUtils.initialize(applicationContext)` before WebRTC audio components can be created. This change:
|
||||
|
||||
- Adds `livekit_ffi_initialize_android_context()` C FFI function for Unity and other FFI consumers
|
||||
- Uses `CreateAndroidAudioDeviceModule()` instead of generic `CreateAudioDeviceModule()` on Android
|
||||
- Handles empty device GUIDs on Android (falls back to index 0)
|
||||
- Documents Android-specific limitations: single default device, no app-level device selection
|
||||
|
||||
Platform notes:
|
||||
- Android device enumeration returns only one "default" device with empty name/GUID
|
||||
- Audio routing (speaker/earpiece/Bluetooth) is controlled by Android's AudioManager, not WebRTC
|
||||
|
||||
## 0.3.31 (2026-05-14)
|
||||
|
||||
### Fixes
|
||||
|
||||
- chore: bump libwebrtc version to webrtc-51ef663
|
||||
- Add `LIVEKIT_PREFERRED_HW_ENCODER` to prefer `nvenc` or `vaapi` hardware video encoding when both are available.
|
||||
|
||||
#### fix: fix LICENSE.md generation in webrtc build scripts
|
||||
|
||||
- Add fix_license_json_parsing.patch to handle GN warnings in JSON output
|
||||
- Enable add_licenses.patch for iOS and Android builds (was commented out)
|
||||
- Restore LICENSE.md copy in iOS build script (regression from #1053)
|
||||
|
||||
The license generation script was failing because `gn desc --format=json`
|
||||
outputs warnings before the JSON when certain build args trigger deprecation
|
||||
notices. The new patch strips non-JSON content before parsing.
|
||||
|
||||
#### Get WebRTC ADM into Rust - #1037 (@xianshijing-lk)
|
||||
|
||||
This PR introduces platform audio device management via WebRTC's Audio Device Module (ADM).
|
||||
|
||||
#### Features
|
||||
- **ADM Proxy**: New `AdmProxy` class that switches between Dummy ADM (synthetic mode) and Platform ADM (real audio I/O)
|
||||
- **PlatformAudio API**: High-level Rust API for microphone capture and speaker playout with AEC/AGC/NS
|
||||
- **Device enumeration**: List and select recording/playout devices by index or GUID
|
||||
- **Mode switching**: Seamlessly switch between synthetic mode (FFI callbacks) and platform mode (native speakers) while audio is active
|
||||
- **FFI platform audio support**: Expose platform audio device enumeration and selection through `livekit-ffi`
|
||||
- **Audio processing**: Configure echo cancellation, noise suppression, and auto gain control with platform-specific defaults (hardware on iOS, software elsewhere)
|
||||
|
||||
#### Audio Modes
|
||||
| Mode | Recording | Playout | Use Case |
|
||||
|------|-----------|---------|----------|
|
||||
| Synthetic | NativeAudioSource | Dummy ADM + FFI | Unity audio, agents |
|
||||
| Platform | Platform ADM mic | Platform ADM speakers | VoIP with AEC |
|
||||
|
||||
#### API
|
||||
```rust
|
||||
// Create PlatformAudio for microphone/speaker access
|
||||
let audio = PlatformAudio::new()?;
|
||||
|
||||
// Enumerate and select devices
|
||||
for i in 0..audio.recording_devices() as u16 {
|
||||
println!("Mic {}: {}", i, audio.recording_device_name(i));
|
||||
}
|
||||
audio.set_recording_device(0)?;
|
||||
|
||||
// Create audio track for publishing
|
||||
let track = LocalAudioTrack::create_audio_track("mic", audio.rtc_source());
|
||||
```
|
||||
|
||||
## 0.3.30 (2026-05-11)
|
||||
|
||||
### Fixes
|
||||
|
||||
#### Fix missing RTC_OBJC_TYPE macros in webrtc-sys .mm files
|
||||
|
||||
Wrap bare ObjC class references in `RTC_OBJC_TYPE()` in `objc_video_factory.mm` and `objc_video_frame_buffer.mm` to support builds with `rtc_objc_prefix` set.
|
||||
|
||||
#### Fix WebRTC build scripts to properly report failures and fix C++ module compilation issues
|
||||
|
||||
- Add `set -e` to all build scripts so CI properly reports build failures instead of silently creating empty/broken artifacts
|
||||
- Re-add `use_clang_modules=false` to macOS, iOS, and Linux build scripts to fix C++ module compilation errors
|
||||
|
||||
Without `use_clang_modules=false`, builds fail due to libc++ header incompatibilities (on macOS/iOS with Xcode 26.0) or other C++ module issues, resulting in:
|
||||
- macOS/iOS: Empty `libwebrtc.a` (~13KB instead of ~700MB)
|
||||
- Android: Missing `libwebrtc.jar`
|
||||
- Linux: Incomplete artifacts
|
||||
|
||||
The builds appeared successful because the scripts continued after ninja failures, but now with `set -e`, failures will be properly reported.
|
||||
|
||||
## 0.3.29 (2026-05-10)
|
||||
|
||||
### Fixes
|
||||
|
||||
- Fix missing `libwebrtc.jar` for Android builds, harden build scripts
|
||||
- fix race in download_webrtc to reduce flaky build - #1047 (@hechen-eng)
|
||||
- Improve WebRTC build scripts and add external_audio_source patch - #1053 (@xianshijing-lk)
|
||||
|
||||
## 0.3.28 (2026-04-23)
|
||||
|
||||
### Features
|
||||
|
||||
#### Add support for frame level packet trailer
|
||||
|
||||
##890 by @chenosaurus
|
||||
|
||||
- Add support to attach/parse frame level timestamps & frame ID to VideoTracks as a custom payload trailer.
|
||||
- Breaking change in VideoFrame API, must include `frame_metadata` or use VideoFrame::new().
|
||||
|
||||
### Fixes
|
||||
|
||||
- Add NVENC check when Nvidia GPU detected - #1015 (@chenosaurus)
|
||||
|
||||
## 0.3.27 (2026-04-02)
|
||||
|
||||
### Features
|
||||
|
||||
#### chore: upgrade libwebrtc to m144.
|
||||
|
||||
##965 by @cloudwebrtc
|
||||
|
||||
### Fixes
|
||||
|
||||
#### use the bounded buffer for video stream
|
||||
|
||||
##956 by @xianshijing-lk
|
||||
|
||||
Before this PR, it uses an unbounded buffer for video stream, that will cause multiple problems:
|
||||
1, video will be lagged behind if rendering is slow or just wake up from background
|
||||
2, it will be out of sync with audio
|
||||
|
||||
This PRs provides options to set a bounded buffer for video stream, and use 1 buffer as the default option.
|
||||
|
||||
## 0.3.26 (2026-03-31)
|
||||
|
||||
### Fixes
|
||||
|
||||
- fix unity android build with "livekit" prefixed jni - #983 (@xianshijing-lk)
|
||||
|
||||
#### fix: fix unavailable sem symbol for Linux aarch64.
|
||||
|
||||
##975 by @cloudwebrtc
|
||||
|
||||
## 0.3.25 (2026-03-22)
|
||||
|
||||
### Fixes
|
||||
|
||||
- fix: enable AGC2 adaptive digital controller
|
||||
- Fix H.264 codec matching
|
||||
|
||||
#### E2EE: allow setting key_ring_size and key_derivation_algorithm, update webrtc to m144
|
||||
|
||||
##921 by @onestacked
|
||||
|
||||
This PR uses [this webrtc-sdk PR](https://github.com/webrtc-sdk/webrtc/pull/224) to configure the KDF.
|
||||
|
||||
I've tested this with https://codeberg.org/esoteric_programmer/matrix-jukebox and it is compatible with Element Call.
|
||||
|
||||
Fixed: https://github.com/livekit/rust-sdks/issues/796
|
||||
|
||||
#### fix clang build issue from zed patches (#949)
|
||||
|
||||
##950 by @cloudwebrtc
|
||||
|
||||
* webrtc-sys: Use clang instead of gcc
|
||||
|
||||
* Debug CI output for aarch64-linux
|
||||
|
||||
* ci: Install lld for aarch64-linux FFI builders
|
||||
|
||||
* webrtc-sys: Disable CREL
|
||||
|
||||
## 0.3.24 (2026-03-13)
|
||||
|
||||
### Fixes
|
||||
|
||||
#### avoid getting webrtc into underrun
|
||||
|
||||
Before this change, the Rust implementation would only start sending silence frames after missing 10 consecutive audio frames. This could cause WebRTC's audio pipeline to enter an underrun state when audio stopped temporarily.
|
||||
|
||||
Once WebRTC enters underrun, resuming audio can significantly increase latency until the pipeline stabilizes again. In testing, this could add hundreds of milliseconds of additional latency when audio resumed shortly after the underrun.
|
||||
|
||||
This change ensures silence frames are sent earlier to prevent the audio pipeline from entering underrun. By maintaining a continuous stream of audio (including silence), WebRTC can avoid unnecessary buffering and latency spikes when audio resumes.
|
||||
|
||||
In testing, this reduces the additional latency observed after underrun recovery and results in more stable end-to-end audio latency.
|
||||
|
||||
#### webrtc-sys: Handle gracefully lack of libva on linux
|
||||
|
||||
##924 by @kubkon
|
||||
+1932
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,60 @@
|
||||
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
|
||||
#
|
||||
# When uploading crates to the registry Cargo will automatically
|
||||
# "normalize" Cargo.toml files for maximal compatibility
|
||||
# with all versions of Cargo and also rewrite `path` dependencies
|
||||
# to registry (e.g., crates.io) dependencies.
|
||||
#
|
||||
# If you are reading this file be aware that the original Cargo.toml
|
||||
# will likely look very different (and much more reasonable).
|
||||
# See Cargo.toml.orig for the original contents.
|
||||
|
||||
[package]
|
||||
edition = "2021"
|
||||
name = "webrtc-sys"
|
||||
version = "0.3.33"
|
||||
build = "build.rs"
|
||||
autolib = false
|
||||
autobins = false
|
||||
autoexamples = false
|
||||
autotests = false
|
||||
autobenches = false
|
||||
description = "Unsafe bindings to libwebrtc"
|
||||
homepage = "https://livekit.io"
|
||||
readme = "README.md"
|
||||
license = "Apache-2.0"
|
||||
repository = "https://github.com/livekit/rust-sdks"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
|
||||
[lib]
|
||||
name = "webrtc_sys"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[dependencies.cxx]
|
||||
version = "1.0"
|
||||
|
||||
[dependencies.log]
|
||||
version = "0.4"
|
||||
|
||||
[dev-dependencies.env_logger]
|
||||
version = "0.11"
|
||||
|
||||
[build-dependencies.cc]
|
||||
version = "1.0"
|
||||
|
||||
[build-dependencies.cxx-build]
|
||||
version = "1.0"
|
||||
|
||||
[build-dependencies.glob]
|
||||
version = "0.3"
|
||||
|
||||
[build-dependencies.pkg-config]
|
||||
version = "0.3.22"
|
||||
|
||||
[build-dependencies.webrtc-sys-build]
|
||||
version = "0.3.18"
|
||||
|
||||
[target.'cfg(target_os = "linux")'.build-dependencies.pkg-config]
|
||||
version = "0.3.22"
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
[package]
|
||||
name = "webrtc-sys"
|
||||
version = "0.3.33"
|
||||
edition.workspace = true
|
||||
homepage = "https://livekit.io"
|
||||
license.workspace = true
|
||||
description = "Unsafe bindings to libwebrtc"
|
||||
repository.workspace = true
|
||||
|
||||
[features]
|
||||
default = []
|
||||
|
||||
[dependencies]
|
||||
cxx = "1.0"
|
||||
log = { workspace = true }
|
||||
|
||||
[build-dependencies]
|
||||
webrtc-sys-build = { workspace = true }
|
||||
cxx-build = "1.0"
|
||||
glob = "0.3"
|
||||
cc = "1.0"
|
||||
pkg-config = "0.3.22"
|
||||
|
||||
[target.'cfg(target_os = "linux")'.build-dependencies]
|
||||
pkg-config = "0.3.22"
|
||||
|
||||
[dev-dependencies]
|
||||
env_logger = { workspace = true }
|
||||
@@ -0,0 +1,426 @@
|
||||
## webrtc patches APACHE 2.0 License
|
||||
License of some patches we use to build webrtc
|
||||
```
|
||||
Copyright 2019-2022, Wandbox LLC (Original Author)
|
||||
Copyright 2019-2022, tnoho (Original Author)
|
||||
Copyright 2019-2022, Shiguredo Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
```
|
||||
## littlebearlabs/arcas-io MIT License
|
||||
The build system and the bindings are inspired by arcas-io (https://github.com/arcas-io)
|
||||
```
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 arcas-io
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
Footer
|
||||
```
|
||||
## Shiguredo Apache 2.0 License
|
||||
The Android build system is inspired by Shiguredo sora-cpp-sdk.
|
||||
```
|
||||
Sora C++ SDK
|
||||
|
||||
Copyright 2021-2022, Wandbox LLC (Original Author)
|
||||
|
||||
Copyright 2021-2022, Shiguredo Inc.
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation, and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
```
|
||||
|
||||
## webrtc build scripts APACHE 2.0 License
|
||||
The webrtc builds scripts are inspired by Unity-Technologies com.unity.webrtc
|
||||
```
|
||||
com.unity.webrtc copyright © 2022 Unity Technologies ApS
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
---
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
```
|
||||
@@ -0,0 +1,16 @@
|
||||
# webrtc-sys
|
||||
|
||||
This crate provides wrapper over the WebRTC API for use from Rust.
|
||||
We use the crate [cxx.rs](https://cxx.rs/) to simplify our bindings.
|
||||
|
||||
## Wrappers
|
||||
|
||||
Most of our wrappers use the cxx.rs types compatible with Rust.
|
||||
As most of our wrappers are stateless, we allow multiple instances of a specific wrapper to point to the same underlying webrtc pointer. (e.g: multiple livekit::MediaStreamTrack pointing to the same webrtc::MediaStreamTrackInterface).
|
||||
|
||||
Threadsafe methods use the const keyword so we can easily call them from the Rust side without worrying about the mutability of the object. (This is similar on how Cell/UnsafeCell works but implemented on the C++ side: interior mutability).
|
||||
|
||||
## Code
|
||||
|
||||
We also use this C++ code to provide other needed utilities/features on the Rust side (e.g: tiny bindings to libyuv).
|
||||
|
||||
@@ -0,0 +1,503 @@
|
||||
// Copyright 2025 LiveKit, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::{env, path, process::Command};
|
||||
|
||||
fn main() {
|
||||
if env::var("DOCS_RS").is_ok() {
|
||||
return;
|
||||
}
|
||||
|
||||
let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap();
|
||||
let target_arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap();
|
||||
let is_desktop = target_os == "linux" || target_os == "windows" || target_os == "macos";
|
||||
|
||||
println!("cargo:rerun-if-env-changed=LK_DEBUG_WEBRTC");
|
||||
println!("cargo:rerun-if-env-changed=LK_CUSTOM_WEBRTC");
|
||||
|
||||
let mut rust_files = vec![
|
||||
"src/peer_connection.rs",
|
||||
"src/peer_connection_factory.rs",
|
||||
"src/audio_device_controller.rs",
|
||||
"src/media_stream.rs",
|
||||
"src/media_stream_track.rs",
|
||||
"src/audio_track.rs",
|
||||
"src/video_track.rs",
|
||||
"src/data_channel.rs",
|
||||
"src/frame_cryptor.rs",
|
||||
"src/jsep.rs",
|
||||
"src/candidate.rs",
|
||||
"src/rtp_parameters.rs",
|
||||
"src/rtp_sender.rs",
|
||||
"src/rtp_receiver.rs",
|
||||
"src/rtp_transceiver.rs",
|
||||
"src/rtc_error.rs",
|
||||
"src/webrtc.rs",
|
||||
"src/video_frame.rs",
|
||||
"src/video_frame_buffer.rs",
|
||||
"src/helper.rs",
|
||||
"src/yuv_helper.rs",
|
||||
"src/audio_resampler.rs",
|
||||
"src/android.rs",
|
||||
"src/prohibit_libsrtp_initialization.rs",
|
||||
"src/recorded_audio_tap.rs",
|
||||
"src/apm.rs",
|
||||
"src/audio_mixer.rs",
|
||||
"src/packet_trailer.rs",
|
||||
];
|
||||
|
||||
if is_desktop {
|
||||
rust_files.push("src/desktop_capturer.rs");
|
||||
}
|
||||
|
||||
let mut builder = cxx_build::bridges(rust_files);
|
||||
|
||||
builder.files(&[
|
||||
"src/peer_connection.cpp",
|
||||
"src/peer_connection_factory.cpp",
|
||||
"src/audio_device_controller.cpp",
|
||||
"src/media_stream.cpp",
|
||||
"src/media_stream_track.cpp",
|
||||
"src/audio_track.cpp",
|
||||
"src/video_track.cpp",
|
||||
"src/data_channel.cpp",
|
||||
"src/jsep.cpp",
|
||||
"src/candidate.cpp",
|
||||
"src/rtp_receiver.cpp",
|
||||
"src/rtp_sender.cpp",
|
||||
"src/rtp_transceiver.cpp",
|
||||
"src/rtp_parameters.cpp",
|
||||
"src/rtc_error.cpp",
|
||||
"src/webrtc.cpp",
|
||||
"src/video_frame.cpp",
|
||||
"src/video_frame_buffer.cpp",
|
||||
"src/video_encoder_factory.cpp",
|
||||
"src/video_decoder_factory.cpp",
|
||||
"src/synthetic_audio_device.cpp",
|
||||
"src/adm_proxy.cpp",
|
||||
"src/audio_resampler.cpp",
|
||||
"src/frame_cryptor.cpp",
|
||||
"src/global_task_queue.cpp",
|
||||
"src/prohibit_libsrtp_initialization.cpp",
|
||||
"src/recorded_audio_tap.cpp",
|
||||
"src/apm.cpp",
|
||||
"src/audio_mixer.cpp",
|
||||
"src/packet_trailer.cpp",
|
||||
]);
|
||||
|
||||
if is_desktop {
|
||||
builder.file("src/desktop_capturer.cpp");
|
||||
}
|
||||
|
||||
let webrtc_dir = webrtc_sys_build::webrtc_dir();
|
||||
let webrtc_include = webrtc_dir.join("include");
|
||||
let webrtc_lib = webrtc_dir.join("lib");
|
||||
|
||||
if !webrtc_dir.exists() {
|
||||
webrtc_sys_build::download_webrtc().unwrap();
|
||||
}
|
||||
|
||||
builder.includes(&[
|
||||
path::PathBuf::from("./include"),
|
||||
path::PathBuf::from("src/nvidia/NvCodec/include"),
|
||||
webrtc_include.clone(),
|
||||
webrtc_include.join("third_party/abseil-cpp/"),
|
||||
webrtc_include.join("third_party/libyuv/include/"),
|
||||
webrtc_include.join("third_party/libc++/"),
|
||||
// For mac & ios
|
||||
webrtc_include.join("sdk/objc"),
|
||||
webrtc_include.join("sdk/objc/base"),
|
||||
]);
|
||||
builder.define("WEBRTC_APM_DEBUG_DUMP", "0");
|
||||
|
||||
println!("cargo:rustc-link-search=native={}", webrtc_lib.to_str().unwrap());
|
||||
|
||||
for (key, value) in webrtc_sys_build::webrtc_defines() {
|
||||
let value = value.as_deref();
|
||||
builder.define(key.as_str(), value);
|
||||
}
|
||||
|
||||
// Link webrtc library
|
||||
println!("cargo:rustc-link-lib=static=webrtc");
|
||||
match target_os.as_str() {
|
||||
"windows" => {
|
||||
println!("cargo:rustc-link-lib=dylib=msdmo");
|
||||
println!("cargo:rustc-link-lib=dylib=wmcodecdspuuid");
|
||||
println!("cargo:rustc-link-lib=dylib=dmoguids");
|
||||
println!("cargo:rustc-link-lib=dylib=crypt32");
|
||||
println!("cargo:rustc-link-lib=dylib=iphlpapi");
|
||||
println!("cargo:rustc-link-lib=dylib=ole32");
|
||||
println!("cargo:rustc-link-lib=dylib=secur32");
|
||||
println!("cargo:rustc-link-lib=dylib=winmm");
|
||||
println!("cargo:rustc-link-lib=dylib=ws2_32");
|
||||
println!("cargo:rustc-link-lib=dylib=strmiids");
|
||||
println!("cargo:rustc-link-lib=dylib=d3d11");
|
||||
println!("cargo:rustc-link-lib=dylib=gdi32");
|
||||
println!("cargo:rustc-link-lib=dylib=dxgi");
|
||||
println!("cargo:rustc-link-lib=dylib=dwmapi");
|
||||
println!("cargo:rustc-link-lib=dylib=shcore");
|
||||
|
||||
//let path = env::current_dir().unwrap();
|
||||
//println!("cargo:rustc-link-search=native={}/vaapi-windows/x64/lib", path.display());
|
||||
//println!("cargo:rustc-link-lib=dylib=va");
|
||||
//println!("cargo:rustc-link-lib=dylib=va_win32");
|
||||
|
||||
builder
|
||||
//.include("./vaapi-windows/DirectX-Headers-1.0/include")
|
||||
//.include(path::PathBuf::from("./vaapi-windows/x64/include"))
|
||||
//.file("vaapi-windows/DirectX-Headers-1.0/src/dxguids.cpp")
|
||||
//.file("src/vaapi/vaapi_display_win32.cpp")
|
||||
//.file("src/vaapi/vaapi_h264_encoder_wrapper.cpp")
|
||||
//.file("src/vaapi/vaapi_encoder_factory.cpp")
|
||||
//.file("src/vaapi/h264_encoder_impl.cpp")
|
||||
.include("src/nvidia/NvCodec/include")
|
||||
.flag("-Isrc/nvidia/NvCodec/include")
|
||||
.flag("-Isrc/nvidia/NvCodec/NvCodec")
|
||||
.file("src/nvidia/NvCodec/NvCodec/NvEncoder/NvEncoder.cpp")
|
||||
.file("src/nvidia/NvCodec/NvCodec/NvEncoder/NvEncoderCuda.cpp")
|
||||
.file("src/nvidia/h264_encoder_impl.cpp")
|
||||
.file("src/nvidia/h265_encoder_impl.cpp")
|
||||
.file("src/nvidia/nvidia_encoder_factory.cpp")
|
||||
.file("src/nvidia/native_gpu_encode_bridge.cpp")
|
||||
.file("src/nvidia/cuda_context.cpp")
|
||||
.file("src/nvidia/cuda_driver_dynamic.cpp")
|
||||
.flag("-DUSE_NVIDIA_VIDEO_ENCODER=1")
|
||||
.flag("/std:c++20")
|
||||
//.flag("/wd4819")
|
||||
//.flag("/wd4068")
|
||||
.flag("/EHsc");
|
||||
}
|
||||
"linux" => {
|
||||
println!("cargo:rustc-link-lib=dylib=rt");
|
||||
println!("cargo:rustc-link-lib=dylib=dl");
|
||||
println!("cargo:rustc-link-lib=dylib=pthread");
|
||||
println!("cargo:rustc-link-lib=dylib=m");
|
||||
|
||||
// In order to avoid any ABI mismatches we use the sysroot's headers.
|
||||
add_gio_headers(&mut builder);
|
||||
|
||||
for lib_name in ["glib-2.0", "gobject-2.0", "gio-2.0"] {
|
||||
pkg_config::probe_library(lib_name).unwrap();
|
||||
}
|
||||
|
||||
add_lazy_load_so(
|
||||
&mut builder,
|
||||
"desktop_capturer",
|
||||
["drm", "gbm", "X11", "Xfixes", "Xdamage", "Xrandr", "Xcomposite", "Xext"]
|
||||
.map(String::from)
|
||||
.to_vec(),
|
||||
);
|
||||
|
||||
let x86 = target_arch == "x86_64" || target_arch == "i686";
|
||||
let arm = target_arch == "aarch64" || target_arch.contains("arm");
|
||||
|
||||
if x86 {
|
||||
if let Some(libva_include) = pkg_config::get_variable("libva", "includedir").ok() {
|
||||
// Do not use pkg_config::probe_library because libva is dlopened
|
||||
// and pkg_config::probe_library would link it.
|
||||
builder
|
||||
.include(libva_include)
|
||||
.file("src/vaapi/vaapi_display_drm.cpp")
|
||||
.file("src/vaapi/vaapi_h264_encoder_wrapper.cpp")
|
||||
.file("src/vaapi/vaapi_encoder_factory.cpp")
|
||||
.file("src/vaapi/h264_encoder_impl.cpp")
|
||||
.flag("-DUSE_VAAPI_VIDEO_CODEC=1");
|
||||
|
||||
add_lazy_load_so(
|
||||
&mut builder,
|
||||
"vaapi",
|
||||
["va", "va-drm"].map(String::from).to_vec(),
|
||||
);
|
||||
} else {
|
||||
println!("cargo:warning=libva not found; building without hardware accelerated video codecs");
|
||||
}
|
||||
}
|
||||
|
||||
if x86 || arm {
|
||||
let cuda_include_dir = PathBuf::from("src/nvidia/NvCodec/include");
|
||||
|
||||
// libcuda and libnvcuvid are dlopened, so do not link them.
|
||||
if cuda_include_dir.join("cuda.h").exists() {
|
||||
match probe_egl() {
|
||||
Some(egl) => {
|
||||
for include_path in egl.include_paths {
|
||||
builder.include(include_path);
|
||||
}
|
||||
builder
|
||||
.include(cuda_include_dir)
|
||||
.flag("-Isrc/nvidia/NvCodec/include")
|
||||
.flag("-Isrc/nvidia/NvCodec/NvCodec")
|
||||
.file("src/nvidia/NvCodec/NvCodec/NvDecoder/NvDecoder.cpp")
|
||||
.file("src/nvidia/NvCodec/NvCodec/NvEncoder/NvEncoder.cpp")
|
||||
.file("src/nvidia/NvCodec/NvCodec/NvEncoder/NvEncoderCuda.cpp")
|
||||
.file("src/nvidia/h264_encoder_impl.cpp")
|
||||
.file("src/nvidia/h265_encoder_impl.cpp")
|
||||
.file("src/nvidia/h264_decoder_impl.cpp")
|
||||
.file("src/nvidia/h265_decoder_impl.cpp")
|
||||
.file("src/nvidia/nvidia_decoder_factory.cpp")
|
||||
.file("src/nvidia/nvidia_encoder_factory.cpp")
|
||||
.file("src/nvidia/native_gpu_encode_bridge.cpp")
|
||||
.file("src/nvidia/cuda_context.cpp")
|
||||
.flag("-Wno-deprecated-declarations")
|
||||
.flag("-DUSE_NVIDIA_VIDEO_CODEC=1");
|
||||
println!("cargo:rustc-link-lib=dylib=EGL");
|
||||
|
||||
add_lazy_load_so(
|
||||
&mut builder,
|
||||
"nvidia",
|
||||
["cuda", "nvcuvid"].map(String::from).to_vec(),
|
||||
);
|
||||
}
|
||||
None => {
|
||||
println!("cargo:warning=EGL headers not found; building without NVIDIA hardware accelerated video codec support");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
println!("cargo:warning=vendored cuda.h not found; building without hardware accelerated video codec support for NVidia GPUs");
|
||||
}
|
||||
}
|
||||
|
||||
builder
|
||||
.flag("-Wno-changes-meaning")
|
||||
.flag("-Wno-deprecated-declarations")
|
||||
.flag("-std=c++20");
|
||||
}
|
||||
"macos" => {
|
||||
println!("cargo:rustc-link-lib=framework=Foundation");
|
||||
println!("cargo:rustc-link-lib=framework=AVFoundation");
|
||||
println!("cargo:rustc-link-lib=framework=CoreAudio");
|
||||
println!("cargo:rustc-link-lib=framework=AudioToolbox");
|
||||
println!("cargo:rustc-link-lib=framework=Appkit");
|
||||
println!("cargo:rustc-link-lib=framework=CoreMedia");
|
||||
println!("cargo:rustc-link-lib=framework=CoreGraphics");
|
||||
println!("cargo:rustc-link-lib=framework=VideoToolbox");
|
||||
println!("cargo:rustc-link-lib=framework=CoreVideo");
|
||||
println!("cargo:rustc-link-lib=framework=OpenGL");
|
||||
println!("cargo:rustc-link-lib=framework=Metal");
|
||||
println!("cargo:rustc-link-lib=framework=MetalKit");
|
||||
println!("cargo:rustc-link-lib=framework=QuartzCore");
|
||||
println!("cargo:rustc-link-lib=framework=IOKit");
|
||||
println!("cargo:rustc-link-lib=framework=IOSurface");
|
||||
println!("cargo:rustc-link-lib=framework=ScreenCaptureKit");
|
||||
|
||||
configure_darwin_sysroot(&mut builder);
|
||||
|
||||
builder
|
||||
.file("src/objc_video_factory.mm")
|
||||
.file("src/objc_video_frame_buffer.mm")
|
||||
.flag("-stdlib=libc++")
|
||||
.flag("-std=c++20")
|
||||
.flag("-Wno-nullability-completeness");
|
||||
}
|
||||
"ios" => {
|
||||
println!("cargo:rustc-link-lib=framework=Foundation");
|
||||
println!("cargo:rustc-link-lib=framework=CoreFoundation");
|
||||
println!("cargo:rustc-link-lib=framework=AVFoundation");
|
||||
println!("cargo:rustc-link-lib=framework=CoreAudio");
|
||||
println!("cargo:rustc-link-lib=framework=UIKit");
|
||||
println!("cargo:rustc-link-lib=framework=CoreVideo");
|
||||
println!("cargo:rustc-link-lib=framework=CoreGraphics");
|
||||
println!("cargo:rustc-link-lib=framework=CoreMedia");
|
||||
println!("cargo:rustc-link-lib=framework=VideoToolbox");
|
||||
println!("cargo:rustc-link-lib=framework=AudioToolbox");
|
||||
println!("cargo:rustc-link-lib=framework=OpenGLES");
|
||||
println!("cargo:rustc-link-lib=framework=GLKit");
|
||||
println!("cargo:rustc-link-lib=framework=Metal");
|
||||
println!("cargo:rustc-link-lib=framework=MetalKit");
|
||||
println!("cargo:rustc-link-lib=framework=Network");
|
||||
println!("cargo:rustc-link-lib=framework=QuartzCore");
|
||||
|
||||
configure_darwin_sysroot(&mut builder);
|
||||
|
||||
builder
|
||||
.file("src/objc_video_factory.mm")
|
||||
.file("src/objc_video_frame_buffer.mm")
|
||||
.flag("-std=c++20");
|
||||
}
|
||||
"android" => {
|
||||
webrtc_sys_build::configure_jni_symbols().unwrap();
|
||||
|
||||
println!("cargo:rustc-link-lib=EGL");
|
||||
println!("cargo:rustc-link-lib=OpenSLES");
|
||||
println!("cargo:rustc-link-lib=c++_static");
|
||||
println!("cargo:rustc-link-lib=c++abi");
|
||||
|
||||
configure_android_sysroot(&mut builder);
|
||||
builder.file("src/android.cpp").flag("-std=c++20");
|
||||
}
|
||||
_ => {
|
||||
panic!("Unsupported target, {}", target_os);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(theomonnom) Only add this define when building tests
|
||||
builder.define("LIVEKIT_TEST", None);
|
||||
builder.warnings(false).compile("webrtcsys-cxx");
|
||||
|
||||
for entry in glob::glob("./src/**/*.cpp").unwrap() {
|
||||
println!("cargo:rerun-if-changed={}", entry.unwrap().display());
|
||||
}
|
||||
|
||||
for entry in glob::glob("./src/**/*.mm").unwrap() {
|
||||
println!("cargo:rerun-if-changed={}", entry.unwrap().display());
|
||||
}
|
||||
|
||||
for entry in glob::glob("./include/**/*.h").unwrap() {
|
||||
println!("cargo:rerun-if-changed={}", entry.unwrap().display());
|
||||
}
|
||||
|
||||
for entry in glob::glob("./src/nvidia/**/*.h").unwrap() {
|
||||
println!("cargo:rerun-if-changed={}", entry.unwrap().display());
|
||||
}
|
||||
|
||||
if target_os.as_str() == "android" {
|
||||
copy_libwebrtc_jar(&PathBuf::from(Path::new(&webrtc_dir)));
|
||||
}
|
||||
}
|
||||
|
||||
fn copy_libwebrtc_jar(webrtc_dir: &PathBuf) {
|
||||
let jar_path = webrtc_dir.join("libwebrtc.jar");
|
||||
let output_path = get_output_path();
|
||||
let output_jar_path = output_path.join("libwebrtc.jar");
|
||||
let res = std::fs::copy(jar_path, output_jar_path);
|
||||
if let Err(e) = res {
|
||||
println!("Failed to copy libwebrtc.jar: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
fn get_output_path() -> PathBuf {
|
||||
let manifest_dir_string = env::var("CARGO_MANIFEST_DIR").unwrap();
|
||||
let build_type = env::var("PROFILE").unwrap();
|
||||
let build_target = env::var("TARGET").unwrap();
|
||||
let path =
|
||||
Path::new(&manifest_dir_string).join("../target").join(build_target).join(build_type);
|
||||
return PathBuf::from(path);
|
||||
}
|
||||
|
||||
fn configure_darwin_sysroot(builder: &mut cc::Build) {
|
||||
let target_os = webrtc_sys_build::target_os();
|
||||
|
||||
let sdk = match target_os.as_str() {
|
||||
"mac" => "macosx",
|
||||
"ios-device" => "iphoneos",
|
||||
"ios-simulator" => "iphonesimulator",
|
||||
_ => panic!("Unsupported target_os: {}", target_os),
|
||||
};
|
||||
|
||||
let clang_rt = match target_os.as_str() {
|
||||
"mac" => "clang_rt.osx",
|
||||
"ios-device" => "clang_rt.ios",
|
||||
"ios-simulator" => "clang_rt.iossim",
|
||||
_ => panic!("Unsupported target_os: {}", target_os),
|
||||
};
|
||||
|
||||
println!("cargo:rustc-link-lib={}", clang_rt);
|
||||
println!("cargo:rustc-link-arg=-ObjC");
|
||||
|
||||
let sysroot = Command::new("xcrun").args(["--sdk", sdk, "--show-sdk-path"]).output().unwrap();
|
||||
|
||||
let sysroot = String::from_utf8_lossy(&sysroot.stdout);
|
||||
let sysroot = sysroot.trim();
|
||||
|
||||
let search_dirs = Command::new("cc").arg("--print-search-dirs").output().unwrap();
|
||||
|
||||
let search_dirs = String::from_utf8_lossy(&search_dirs.stdout);
|
||||
for line in search_dirs.lines() {
|
||||
if line.contains("libraries: =") {
|
||||
let path = line.split('=').nth(1).unwrap();
|
||||
let path = format!("{}/lib/darwin", path);
|
||||
println!("cargo:rustc-link-search={}", path);
|
||||
}
|
||||
}
|
||||
|
||||
builder.flag(format!("-isysroot{}", sysroot).as_str());
|
||||
}
|
||||
|
||||
fn configure_android_sysroot(builder: &mut cc::Build) {
|
||||
let toolchain = webrtc_sys_build::android_ndk_toolchain().unwrap();
|
||||
let sysroot = toolchain.join("sysroot").canonicalize().unwrap();
|
||||
builder.flag(format!("-isysroot{}", sysroot.display()).as_str());
|
||||
}
|
||||
|
||||
fn add_lazy_load_so(builder: &mut cc::Build, name: &str, libraries: Vec<String>) {
|
||||
let target_arch = webrtc_sys_build::target_arch();
|
||||
for lib_name in libraries {
|
||||
let mut arch_dir = "x86_64-linux-gnu";
|
||||
if target_arch.contains("arm64") {
|
||||
arch_dir = "aarch64-linux-gnu";
|
||||
}
|
||||
let implib_file_c_name = "src/lazy_load_deps_for/".to_owned()
|
||||
+ name
|
||||
+ "/"
|
||||
+ arch_dir
|
||||
+ "/lib"
|
||||
+ &lib_name
|
||||
+ ".so.init.c";
|
||||
let implib_file_asm_name = "src/lazy_load_deps_for/".to_owned()
|
||||
+ name
|
||||
+ "/"
|
||||
+ arch_dir
|
||||
+ "/lib"
|
||||
+ &lib_name
|
||||
+ ".so.tramp.S";
|
||||
builder.file(implib_file_c_name).file(implib_file_asm_name);
|
||||
}
|
||||
}
|
||||
|
||||
fn add_gio_headers(builder: &mut cc::Build) {
|
||||
let webrtc_dir = webrtc_sys_build::webrtc_dir();
|
||||
let target_arch = webrtc_sys_build::target_arch();
|
||||
let target_arch_sysroot = match target_arch.as_str() {
|
||||
"arm64" => "arm64",
|
||||
"x64" => "amd64",
|
||||
_ => panic!("unsupported arch"),
|
||||
};
|
||||
let sysroot_path = format!("include/build/linux/debian_bullseye_{target_arch_sysroot}-sysroot");
|
||||
let sysroot = webrtc_dir.join(sysroot_path);
|
||||
let glib_path = sysroot.join("usr/include/glib-2.0");
|
||||
println!("cargo:info=add_gio_headers {}", glib_path.display());
|
||||
|
||||
builder.include(&glib_path);
|
||||
let arch_specific_path = match target_arch.as_str() {
|
||||
"x64" => "x86_64-linux-gnu",
|
||||
"arm64" => "aarch64-linux-gnu",
|
||||
_ => panic!("unsupported target"),
|
||||
};
|
||||
|
||||
let glib_path_config = sysroot.join("usr/lib");
|
||||
let glib_path_config = glib_path_config.join(arch_specific_path);
|
||||
let glib_path_config = glib_path_config.join("glib-2.0/include");
|
||||
builder.include(&glib_path_config);
|
||||
}
|
||||
|
||||
fn probe_egl() -> Option<pkg_config::Library> {
|
||||
println!("cargo:rerun-if-env-changed=EGL_NO_PKG_CONFIG");
|
||||
let egl = pkg_config::Config::new()
|
||||
.cargo_metadata(false)
|
||||
.probe("egl")
|
||||
.ok()?;
|
||||
if !egl.include_paths.iter().any(|path| path.join("EGL/egl.h").exists()) {
|
||||
return None;
|
||||
}
|
||||
if !egl
|
||||
.include_paths
|
||||
.iter()
|
||||
.any(|path| path.join("EGL/eglext.h").exists())
|
||||
{
|
||||
return None;
|
||||
}
|
||||
Some(egl)
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
-xc++
|
||||
-xobjective-c
|
||||
-xobjective-c++
|
||||
-std=c++17
|
||||
-Iinclude
|
||||
-Ilibwebrtc/src
|
||||
-Ilibwebrtc/src/third_party/abseil-cpp
|
||||
-Ilibwebrtc/src/third_party/libc++
|
||||
-Ilibwebrtc/src/third_party/libyuv/include
|
||||
-Ilibwebrtc/src/sdk/objc/base
|
||||
-Ilibwebrtc/src/sdk/objc
|
||||
-I../target/cxxbridge
|
||||
-DWEBRTC_POSIX
|
||||
-DWEBRTC_ANDROID
|
||||
-DNDEBUG
|
||||
-DWEBRTC_ENABLE_SYMBOL_EXPORT
|
||||
-DWEBRTC_APM_DEBUG_DUMP=0
|
||||
-D__ANDROID_API__=29
|
||||
-DWEBRTC_LIBRARY_IMPL
|
||||
--sysroot=/Users/theomonnom/Library/Android/sdk/ndk-bundle/toolchains/llvm/prebuilt/darwin-x86_64/sysroot
|
||||
--target=aarch64-none-linux-android
|
||||
--gcc-toolchain=/Users/theomonnom/Library/Android/sdk/ndk-bundle/toolchains/llvm/prebuilt/darwin-x86_64
|
||||
+305
@@ -0,0 +1,305 @@
|
||||
/*
|
||||
* Copyright 2026 LiveKit, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "api/environment/environment.h"
|
||||
#include "api/scoped_refptr.h"
|
||||
#include "livekit/recorded_audio_tap.h"
|
||||
#include "livekit/synthetic_audio_device.h"
|
||||
#include "modules/audio_device/include/audio_device.h"
|
||||
#include "modules/audio_device/include/audio_device_defines.h"
|
||||
#include "rtc_base/synchronization/mutex.h"
|
||||
|
||||
namespace webrtc {
|
||||
class Thread;
|
||||
} // namespace webrtc
|
||||
|
||||
namespace livekit_ffi {
|
||||
|
||||
/// ADM Proxy that manages synthetic and platform audio modes.
|
||||
///
|
||||
/// This proxy implements the AudioDeviceModule interface and switches between:
|
||||
/// 1. **Synthetic mode**: Uses `SyntheticAudioDevice`, which pumps the WebRTC
|
||||
/// audio pipeline without platform audio. Remote audio is delivered via FFI
|
||||
/// callbacks to external audio systems (e.g., Unity AudioSource).
|
||||
/// 2. **Platform mode**: Real audio I/O through the Platform ADM with microphone
|
||||
/// capture and speaker playout. Used when PlatformAudio is active for VoIP
|
||||
/// with AEC.
|
||||
///
|
||||
/// ## Mode Selection
|
||||
///
|
||||
/// - **Playout**: Uses Platform ADM when `ref_count > 0 && playout_enabled`,
|
||||
/// otherwise uses synthetic mode (internal audio pumping task).
|
||||
/// - **Recording**: Uses Platform ADM when `ref_count > 0 && recording_enabled`,
|
||||
/// otherwise recording is unavailable (synthetic mode has no microphone).
|
||||
///
|
||||
/// ## Lifecycle Management
|
||||
///
|
||||
/// Platform ADM creation is attempted eagerly at construction (for iOS
|
||||
/// compatibility). If that attempt fails (e.g. the OS audio stack is still
|
||||
/// starting up), creation is retried on demand via EnsurePlatformAdm() and
|
||||
/// AcquirePlatformAdm() instead of failing for the process lifetime.
|
||||
/// Reference counting controls which mode is active:
|
||||
/// - `AcquirePlatformAdm()`: Increments ref count
|
||||
/// - `ReleasePlatformAdm()`: Decrements ref count
|
||||
/// - When ref_count is 0, playout uses synthetic mode
|
||||
///
|
||||
/// ## Audio Modes
|
||||
///
|
||||
/// | Mode | Recording | Playout | Use Case |
|
||||
/// |------|-----------|---------|----------|
|
||||
/// | Synthetic | NativeAudioSource | Internal task + FFI | Unity audio, agents |
|
||||
/// | Platform | Platform ADM mic | Platform ADM speakers | VoIP with AEC |
|
||||
///
|
||||
class AdmProxy : public webrtc::AudioDeviceModule {
|
||||
public:
|
||||
explicit AdmProxy(const webrtc::Environment& env,
|
||||
webrtc::Thread* worker_thread);
|
||||
~AdmProxy() override;
|
||||
|
||||
// ===========================================================================
|
||||
// Platform ADM Lifecycle Management
|
||||
// ===========================================================================
|
||||
|
||||
/// Acquires a reference to the Platform ADM.
|
||||
///
|
||||
/// On first call, creates and initializes the Platform ADM. On subsequent
|
||||
/// calls, just increments the reference count.
|
||||
///
|
||||
/// @return true if Platform ADM is ready for use, false if initialization failed.
|
||||
bool AcquirePlatformAdm();
|
||||
|
||||
/// Releases a reference to the Platform ADM.
|
||||
///
|
||||
/// When the reference count reaches zero, the Platform ADM is terminated
|
||||
/// and the proxy returns to synthetic mode.
|
||||
void ReleasePlatformAdm();
|
||||
|
||||
/// Returns the current reference count for the Platform ADM.
|
||||
int platform_adm_ref_count() const;
|
||||
|
||||
/// Returns true if Platform ADM is currently active (ref_count > 0).
|
||||
bool is_platform_adm_active() const;
|
||||
|
||||
/// Ensures the Platform ADM exists, creating and initializing it if needed.
|
||||
///
|
||||
/// Platform ADM creation can fail transiently when the OS audio stack is
|
||||
/// not ready yet (e.g. an app launched at login racing coreaudiod or the
|
||||
/// Windows audio services). A failed attempt leaves platform_adm_ null so
|
||||
/// the next call retries instead of staying broken for the process
|
||||
/// lifetime.
|
||||
///
|
||||
/// @return true if the Platform ADM is available after the call.
|
||||
bool EnsurePlatformAdm();
|
||||
|
||||
/// Returns true if the Platform ADM has been created and initialized.
|
||||
/// Distinguishes "audio stack unavailable" from "zero audio devices".
|
||||
bool platform_adm_available() const;
|
||||
|
||||
// ===========================================================================
|
||||
// Recording/Playout Control
|
||||
// ===========================================================================
|
||||
|
||||
/// Control whether recording (microphone) is enabled.
|
||||
///
|
||||
/// When disabled (default), InitRecording/StartRecording return success but
|
||||
/// do nothing. This allows NativeAudioSource to work without interference.
|
||||
///
|
||||
/// @note Only effective when Platform ADM is active.
|
||||
void set_recording_enabled(bool enabled);
|
||||
bool recording_enabled() const;
|
||||
|
||||
/// Control whether playout goes through Platform ADM speakers.
|
||||
///
|
||||
/// When disabled (default), playout uses synthetic mode - remote audio is
|
||||
/// delivered via FFI callbacks to the application (e.g., Unity AudioSource).
|
||||
///
|
||||
/// When enabled, remote audio plays through the platform speakers with AEC.
|
||||
///
|
||||
/// @note Only effective when Platform ADM is active.
|
||||
void set_playout_enabled(bool enabled);
|
||||
bool playout_enabled() const;
|
||||
|
||||
// ===========================================================================
|
||||
// AudioDeviceModule Interface
|
||||
// ===========================================================================
|
||||
|
||||
int32_t ActiveAudioLayer(AudioLayer* audioLayer) const override;
|
||||
int32_t RegisterAudioCallback(webrtc::AudioTransport* transport) override;
|
||||
|
||||
int32_t Init() override;
|
||||
int32_t Terminate() override;
|
||||
bool Initialized() const override;
|
||||
|
||||
int16_t PlayoutDevices() override;
|
||||
int16_t RecordingDevices() override;
|
||||
int32_t PlayoutDeviceName(uint16_t index,
|
||||
char name[webrtc::kAdmMaxDeviceNameSize],
|
||||
char guid[webrtc::kAdmMaxGuidSize]) override;
|
||||
int32_t RecordingDeviceName(uint16_t index,
|
||||
char name[webrtc::kAdmMaxDeviceNameSize],
|
||||
char guid[webrtc::kAdmMaxGuidSize]) override;
|
||||
|
||||
int32_t SetPlayoutDevice(uint16_t index) override;
|
||||
int32_t SetPlayoutDevice(WindowsDeviceType device) override;
|
||||
int32_t SetRecordingDevice(uint16_t index) override;
|
||||
int32_t SetRecordingDevice(WindowsDeviceType device) override;
|
||||
|
||||
int32_t PlayoutIsAvailable(bool* available) override;
|
||||
int32_t InitPlayout() override;
|
||||
bool PlayoutIsInitialized() const override;
|
||||
int32_t RecordingIsAvailable(bool* available) override;
|
||||
int32_t InitRecording() override;
|
||||
bool RecordingIsInitialized() const override;
|
||||
|
||||
int32_t StartPlayout() override;
|
||||
int32_t StopPlayout() override;
|
||||
bool Playing() const override;
|
||||
int32_t StartRecording() override;
|
||||
int32_t StopRecording() override;
|
||||
bool Recording() const override;
|
||||
|
||||
int32_t InitSpeaker() override;
|
||||
bool SpeakerIsInitialized() const override;
|
||||
int32_t InitMicrophone() override;
|
||||
bool MicrophoneIsInitialized() const override;
|
||||
|
||||
int32_t SpeakerVolumeIsAvailable(bool* available) override;
|
||||
int32_t SetSpeakerVolume(uint32_t volume) override;
|
||||
int32_t SpeakerVolume(uint32_t* volume) const override;
|
||||
int32_t MaxSpeakerVolume(uint32_t* maxVolume) const override;
|
||||
int32_t MinSpeakerVolume(uint32_t* minVolume) const override;
|
||||
|
||||
int32_t MicrophoneVolumeIsAvailable(bool* available) override;
|
||||
int32_t SetMicrophoneVolume(uint32_t volume) override;
|
||||
int32_t MicrophoneVolume(uint32_t* volume) const override;
|
||||
int32_t MaxMicrophoneVolume(uint32_t* maxVolume) const override;
|
||||
int32_t MinMicrophoneVolume(uint32_t* minVolume) const override;
|
||||
|
||||
int32_t SpeakerMuteIsAvailable(bool* available) override;
|
||||
int32_t SetSpeakerMute(bool enable) override;
|
||||
int32_t SpeakerMute(bool* enabled) const override;
|
||||
|
||||
int32_t MicrophoneMuteIsAvailable(bool* available) override;
|
||||
int32_t SetMicrophoneMute(bool enable) override;
|
||||
int32_t MicrophoneMute(bool* enabled) const override;
|
||||
|
||||
int32_t StereoPlayoutIsAvailable(bool* available) const override;
|
||||
int32_t SetStereoPlayout(bool enable) override;
|
||||
int32_t StereoPlayout(bool* enabled) const override;
|
||||
int32_t StereoRecordingIsAvailable(bool* available) const override;
|
||||
int32_t SetStereoRecording(bool enable) override;
|
||||
int32_t StereoRecording(bool* enabled) const override;
|
||||
|
||||
int32_t PlayoutDelay(uint16_t* delayMS) const override;
|
||||
|
||||
bool BuiltInAECIsAvailable() const override;
|
||||
bool BuiltInAGCIsAvailable() const override;
|
||||
bool BuiltInNSIsAvailable() const override;
|
||||
|
||||
int32_t EnableBuiltInAEC(bool enable) override;
|
||||
int32_t EnableBuiltInAGC(bool enable) override;
|
||||
int32_t EnableBuiltInNS(bool enable) override;
|
||||
|
||||
#if defined(WEBRTC_IOS)
|
||||
int GetPlayoutAudioParameters(webrtc::AudioParameters* params) const override;
|
||||
int GetRecordAudioParameters(webrtc::AudioParameters* params) const override;
|
||||
#endif
|
||||
|
||||
int32_t SetObserver(webrtc::AudioDeviceObserver* observer) override;
|
||||
|
||||
private:
|
||||
// Returns true if platform mode is active for playout
|
||||
// (ref_count > 0 && playout_enabled)
|
||||
bool is_platform_playout_active() const;
|
||||
|
||||
// Returns the ADM to use for recording operations
|
||||
// - Platform ADM when recording is enabled (ref_count > 0 && recording_enabled)
|
||||
// - nullptr otherwise (recording not available in synthetic mode)
|
||||
webrtc::AudioDeviceModule* recording_adm() const;
|
||||
|
||||
// Switches playout mode based on current state.
|
||||
// Called when ref_count or playout_enabled changes.
|
||||
// If playout is active, stops the old mode and starts the new one.
|
||||
// Must be called with mutex_ held.
|
||||
void SwitchPlayoutModeIfNeeded();
|
||||
|
||||
// Switches recording to the correct ADM based on current mode.
|
||||
// Called when ref_count or recording_enabled changes.
|
||||
// If recording is active, stops the old ADM and starts the new one.
|
||||
// Must be called with mutex_ held.
|
||||
void SwitchRecordingAdmIfNeeded();
|
||||
|
||||
// Lazily creates and initializes the Platform ADM. On failure the ADM
|
||||
// stays null so a later call can retry once the OS audio stack is ready.
|
||||
// Must be called with mutex_ held.
|
||||
// Returns true if ADM is available after the call.
|
||||
bool EnsurePlatformAdmCreated();
|
||||
|
||||
// Re-applies state that predates a late Platform ADM creation: the
|
||||
// registered audio transport and any previously selected devices.
|
||||
// Must be called with mutex_ held and platform_adm_ non-null.
|
||||
void RestorePlatformAdmStateLocked();
|
||||
|
||||
const webrtc::Environment env_;
|
||||
webrtc::Thread* worker_thread_;
|
||||
|
||||
// Mutex for thread-safe access to mutable state
|
||||
mutable webrtc::Mutex mutex_;
|
||||
|
||||
// Synthetic ADM for synthetic mode - pumps the WebRTC audio pipeline without
|
||||
// platform audio via SyntheticAudioDevice's internal task.
|
||||
webrtc::scoped_refptr<SyntheticAudioDevice> synthetic_adm_;
|
||||
|
||||
// Platform ADM for real audio I/O (microphone capture, speaker playout with AEC)
|
||||
webrtc::scoped_refptr<webrtc::AudioDeviceModule> platform_adm_;
|
||||
|
||||
// Reference count for Platform ADM users (PlatformAudio instances)
|
||||
int platform_adm_ref_count_ = 0;
|
||||
|
||||
// Audio transport callback (registered by WebRTC)
|
||||
webrtc::AudioTransport* audio_transport_ = nullptr;
|
||||
|
||||
// Interposed between the platform ADM and audio_transport_ so recorded
|
||||
// microphone frames can be teed to the global recorded-audio sink. Forwards
|
||||
// every call to audio_transport_, leaving the send and playout paths intact.
|
||||
RecordingTransportProxy recording_transport_proxy_;
|
||||
|
||||
// State tracking
|
||||
bool playout_initialized_ = false;
|
||||
bool recording_initialized_ = false;
|
||||
bool playing_ = false;
|
||||
bool recording_ = false;
|
||||
|
||||
// Control flags
|
||||
// When false (default), recording operations are no-ops (NativeAudioSource mode)
|
||||
bool recording_enabled_ = false;
|
||||
// When false (default), playout uses synthetic mode (internal task pumps audio)
|
||||
bool playout_enabled_ = false;
|
||||
|
||||
// Selected device information (for re-initialization after ADM restart)
|
||||
// We store both index and GUID. GUID is preferred for restoration as it's
|
||||
// stable across device hot-plug events.
|
||||
uint16_t selected_playout_device_ = 0;
|
||||
uint16_t selected_recording_device_ = 0;
|
||||
std::string selected_playout_guid_;
|
||||
std::string selected_recording_guid_;
|
||||
};
|
||||
|
||||
} // namespace livekit_ffi
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright 2025 LiveKit, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
#include "api/video_codecs/video_decoder_factory.h"
|
||||
#include "api/video_codecs/video_encoder_factory.h"
|
||||
|
||||
namespace livekit_ffi {
|
||||
typedef JavaVM JavaVM;
|
||||
} // namespace livekit_ffi
|
||||
#include "webrtc-sys/src/android.rs.h"
|
||||
|
||||
namespace livekit_ffi {
|
||||
|
||||
/// Initialize Android WebRTC with the JVM.
|
||||
/// This is called automatically by init_android_context(), so you only need to
|
||||
/// call this directly in JNI_OnLoad or if you don't have an Android Context.
|
||||
/// This function is idempotent - safe to call multiple times.
|
||||
///
|
||||
/// @param jvm The JavaVM pointer
|
||||
void init_android(JavaVM* jvm);
|
||||
|
||||
/// Initialize Android WebRTC with the application context.
|
||||
/// This is the main initialization function - it calls init_android() internally
|
||||
/// and then initializes ContextUtils for PlatformAudio support.
|
||||
/// This function is idempotent - safe to call multiple times.
|
||||
///
|
||||
/// @param jvm The JavaVM pointer
|
||||
/// @param context The Android application context (jobject cast to uintptr_t)
|
||||
/// @return true if context initialization was successful, false otherwise.
|
||||
/// Note: JVM init (init_android) always happens regardless of return value.
|
||||
bool init_android_context(JavaVM* jvm, uintptr_t context);
|
||||
|
||||
std::unique_ptr<webrtc::VideoEncoderFactory> CreateAndroidVideoEncoderFactory();
|
||||
std::unique_ptr<webrtc::VideoDecoderFactory> CreateAndroidVideoDecoderFactory();
|
||||
|
||||
} // namespace livekit_ffi
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright 2025 LiveKit, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "api/scoped_refptr.h"
|
||||
#include "api/video_codecs/video_decoder_factory.h"
|
||||
#include "api/video_codecs/video_encoder_factory.h"
|
||||
#include "modules/audio_processing/aec3/echo_canceller3.h"
|
||||
#include "modules/audio_processing/audio_buffer.h"
|
||||
|
||||
namespace livekit_ffi {
|
||||
|
||||
struct AudioProcessingConfig {
|
||||
bool echo_canceller_enabled;
|
||||
bool gain_controller_enabled;
|
||||
bool high_pass_filter_enabled;
|
||||
bool noise_suppression_enabled;
|
||||
|
||||
webrtc::AudioProcessing::Config ToWebrtcConfig() const {
|
||||
webrtc::AudioProcessing::Config config;
|
||||
config.echo_canceller.enabled = echo_canceller_enabled;
|
||||
config.gain_controller2.enabled = gain_controller_enabled;
|
||||
config.gain_controller2.adaptive_digital.enabled = gain_controller_enabled;
|
||||
config.high_pass_filter.enabled = high_pass_filter_enabled;
|
||||
config.noise_suppression.enabled = noise_suppression_enabled;
|
||||
return config;
|
||||
}
|
||||
};
|
||||
|
||||
class AudioProcessingModule {
|
||||
public:
|
||||
AudioProcessingModule(const AudioProcessingConfig& config);
|
||||
|
||||
int process_stream(const int16_t* src,
|
||||
size_t src_len,
|
||||
int16_t* dst,
|
||||
size_t dst_len,
|
||||
int sample_rate,
|
||||
int num_channels);
|
||||
|
||||
int process_reverse_stream(const int16_t* src,
|
||||
size_t src_len,
|
||||
int16_t* dst,
|
||||
size_t dst_len,
|
||||
int sample_rate,
|
||||
int num_channels);
|
||||
|
||||
int set_stream_delay_ms(int delay_ms);
|
||||
|
||||
private:
|
||||
webrtc::scoped_refptr<webrtc::AudioProcessing> apm_;
|
||||
};
|
||||
|
||||
std::unique_ptr<AudioProcessingModule> create_apm(
|
||||
bool echo_canceller_enabled,
|
||||
bool gain_controller_enabled,
|
||||
bool high_pass_filter_enabled,
|
||||
bool noise_suppression_enabled);
|
||||
|
||||
} // namespace livekit_ffi
|
||||
Vendored
+85
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright 2026 LiveKit, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "api/scoped_refptr.h"
|
||||
#include "livekit/adm_proxy.h"
|
||||
#include "rust/cxx.h"
|
||||
|
||||
namespace livekit_ffi {
|
||||
|
||||
class AudioDeviceController {
|
||||
public:
|
||||
explicit AudioDeviceController(webrtc::scoped_refptr<AdmProxy> adm_proxy);
|
||||
|
||||
// Device enumeration
|
||||
int16_t playout_devices() const;
|
||||
int16_t recording_devices() const;
|
||||
rust::String playout_device_name(uint16_t index) const;
|
||||
rust::String recording_device_name(uint16_t index) const;
|
||||
rust::String playout_device_guid(uint16_t index) const;
|
||||
rust::String recording_device_guid(uint16_t index) const;
|
||||
|
||||
// Device selection
|
||||
bool set_playout_device(uint16_t index) const;
|
||||
bool set_recording_device(uint16_t index) const;
|
||||
bool set_playout_device_by_guid(rust::String guid) const;
|
||||
bool set_recording_device_by_guid(rust::String guid) const;
|
||||
|
||||
// Recording control
|
||||
bool stop_recording() const;
|
||||
bool init_recording() const;
|
||||
bool start_recording() const;
|
||||
bool recording_is_initialized() const;
|
||||
|
||||
// Playout control
|
||||
bool stop_playout() const;
|
||||
bool init_playout() const;
|
||||
bool start_playout() const;
|
||||
bool playout_is_initialized() const;
|
||||
|
||||
// Built-in audio processing
|
||||
bool builtin_aec_is_available() const;
|
||||
bool builtin_agc_is_available() const;
|
||||
bool builtin_ns_is_available() const;
|
||||
bool enable_builtin_aec(bool enable) const;
|
||||
bool enable_builtin_agc(bool enable) const;
|
||||
bool enable_builtin_ns(bool enable) const;
|
||||
|
||||
// ADM recording control
|
||||
void set_adm_recording_enabled(bool enabled) const;
|
||||
bool adm_recording_enabled() const;
|
||||
|
||||
// ADM playout control
|
||||
void set_adm_playout_enabled(bool enabled) const;
|
||||
bool adm_playout_enabled() const;
|
||||
|
||||
// Platform ADM lifecycle management
|
||||
bool acquire_platform_adm() const;
|
||||
void release_platform_adm() const;
|
||||
int platform_adm_ref_count() const;
|
||||
bool is_platform_adm_active() const;
|
||||
bool ensure_platform_adm() const;
|
||||
bool platform_adm_available() const;
|
||||
|
||||
private:
|
||||
webrtc::scoped_refptr<AdmProxy> adm_proxy_;
|
||||
};
|
||||
|
||||
} // namespace livekit_ffi
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright 2025 LiveKit, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "api/audio/audio_mixer.h"
|
||||
#include "api/scoped_refptr.h"
|
||||
#include "modules/audio_mixer/audio_mixer_impl.h"
|
||||
#include "modules/audio_processing/audio_buffer.h"
|
||||
#include "rtc_base/synchronization/mutex.h"
|
||||
#include "rust/cxx.h"
|
||||
|
||||
namespace livekit_ffi {
|
||||
class AudioMixer;
|
||||
class NativeAudioFrame;
|
||||
} // namespace livekit_ffi
|
||||
|
||||
#include "webrtc-sys/src/audio_mixer.rs.h"
|
||||
|
||||
namespace livekit_ffi {
|
||||
|
||||
class NativeAudioFrame {
|
||||
public:
|
||||
NativeAudioFrame(webrtc::AudioFrame* frame) : frame_(frame) {}
|
||||
void update_frame(uint32_t timestamp,
|
||||
const int16_t* data,
|
||||
size_t samples_per_channel,
|
||||
int sample_rate_hz,
|
||||
size_t num_channels);
|
||||
|
||||
private:
|
||||
webrtc::AudioFrame* frame_;
|
||||
};
|
||||
|
||||
class AudioMixerSource : public webrtc::AudioMixer::Source {
|
||||
public:
|
||||
AudioMixerSource(rust::Box<AudioMixerSourceWrapper> source);
|
||||
|
||||
AudioFrameInfo GetAudioFrameWithInfo(
|
||||
int sample_rate_hz,
|
||||
webrtc::AudioFrame* audio_frame) override;
|
||||
|
||||
int Ssrc() const override;
|
||||
|
||||
int PreferredSampleRate() const override;
|
||||
|
||||
~AudioMixerSource() {}
|
||||
|
||||
private:
|
||||
rust::Box<AudioMixerSourceWrapper> source_;
|
||||
};
|
||||
|
||||
class AudioMixer {
|
||||
public:
|
||||
AudioMixer();
|
||||
|
||||
void add_source(rust::Box<AudioMixerSourceWrapper> source);
|
||||
|
||||
void remove_source(int ssrc);
|
||||
|
||||
size_t mix(size_t num_channels);
|
||||
const int16_t* data() const;
|
||||
|
||||
private:
|
||||
mutable webrtc::Mutex sources_mutex_;
|
||||
webrtc::AudioFrame frame_;
|
||||
std::vector<std::shared_ptr<AudioMixerSource>> sources_;
|
||||
webrtc::scoped_refptr<webrtc::AudioMixer> audio_mixer_;
|
||||
};
|
||||
|
||||
std::unique_ptr<AudioMixer> create_audio_mixer();
|
||||
|
||||
} // namespace livekit_ffi
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2025 LiveKit, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "api/audio/audio_frame.h"
|
||||
#include "api/data_channel_interface.h"
|
||||
#include "common_audio/resampler/include/push_resampler.h"
|
||||
#include "livekit/webrtc.h"
|
||||
#include "rust/cxx.h"
|
||||
|
||||
namespace livekit_ffi {
|
||||
|
||||
class AudioResampler {
|
||||
public:
|
||||
size_t remix_and_resample(const int16_t* src,
|
||||
size_t samples_per_channel,
|
||||
size_t num_channels,
|
||||
int sample_rate_hz,
|
||||
size_t dest_num_channels,
|
||||
int dest_sample_rate_hz);
|
||||
|
||||
const int16_t* data() const;
|
||||
|
||||
private:
|
||||
webrtc::AudioFrame frame_;
|
||||
webrtc::PushResampler<int16_t> resampler_;
|
||||
};
|
||||
|
||||
std::unique_ptr<AudioResampler> create_audio_resampler();
|
||||
|
||||
} // namespace livekit_ffi
|
||||
+206
@@ -0,0 +1,206 @@
|
||||
/*
|
||||
* Copyright 2025 LiveKit, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "api/audio/audio_frame.h"
|
||||
#include "api/audio_options.h"
|
||||
#include "api/task_queue/task_queue_factory.h"
|
||||
#include "common_audio/resampler/include/push_resampler.h"
|
||||
#include "livekit/helper.h"
|
||||
#include "livekit/media_stream_track.h"
|
||||
#include "livekit/webrtc.h"
|
||||
#include "pc/local_audio_source.h"
|
||||
#include "rtc_base/synchronization/mutex.h"
|
||||
#include "api/task_queue/task_queue_base.h"
|
||||
#include "rtc_base/task_utils/repeating_task.h"
|
||||
#include "rtc_base/thread_annotations.h"
|
||||
#include "rust/cxx.h"
|
||||
|
||||
namespace livekit_ffi {
|
||||
class AudioTrack;
|
||||
class NativeAudioSink;
|
||||
class AudioTrackSource;
|
||||
class SourceContext;
|
||||
|
||||
using CompleteCallback = void (*)(const livekit_ffi::SourceContext*);
|
||||
} // namespace livekit_ffi
|
||||
#include "webrtc-sys/src/audio_track.rs.h"
|
||||
|
||||
namespace livekit_ffi {
|
||||
|
||||
class AudioTrack : public MediaStreamTrack {
|
||||
private:
|
||||
friend RtcRuntime;
|
||||
AudioTrack(std::shared_ptr<RtcRuntime> rtc_runtime,
|
||||
webrtc::scoped_refptr<webrtc::AudioTrackInterface> track);
|
||||
|
||||
public:
|
||||
~AudioTrack();
|
||||
|
||||
void add_sink(const std::shared_ptr<NativeAudioSink>& sink) const;
|
||||
void remove_sink(const std::shared_ptr<NativeAudioSink>& sink) const;
|
||||
|
||||
private:
|
||||
webrtc::AudioTrackInterface* track() const {
|
||||
return static_cast<webrtc::AudioTrackInterface*>(track_.get());
|
||||
}
|
||||
|
||||
mutable webrtc::Mutex mutex_;
|
||||
|
||||
// Same for VideoTrack:
|
||||
// Keep a strong reference to the added sinks, so we don't need to
|
||||
// manage the lifetime safety on the Rust side
|
||||
mutable std::vector<std::shared_ptr<NativeAudioSink>> sinks_;
|
||||
};
|
||||
|
||||
class NativeAudioSink : public webrtc::AudioTrackSinkInterface {
|
||||
public:
|
||||
explicit NativeAudioSink(rust::Box<AudioSinkWrapper> observer,
|
||||
int sample_rate,
|
||||
int num_channels);
|
||||
void OnData(const void* audio_data,
|
||||
int bits_per_sample,
|
||||
int sample_rate,
|
||||
size_t number_of_channels,
|
||||
size_t number_of_frames) override;
|
||||
|
||||
private:
|
||||
rust::Box<AudioSinkWrapper> observer_;
|
||||
|
||||
int sample_rate_;
|
||||
int num_channels_;
|
||||
|
||||
webrtc::AudioFrame frame_;
|
||||
webrtc::PushResampler<int16_t> resampler_;
|
||||
};
|
||||
|
||||
std::shared_ptr<NativeAudioSink> new_native_audio_sink(
|
||||
rust::Box<AudioSinkWrapper> observer,
|
||||
int sample_rate,
|
||||
int num_channels);
|
||||
|
||||
class AudioTrackSource {
|
||||
class InternalSource : public webrtc::LocalAudioSource {
|
||||
public:
|
||||
InternalSource(const webrtc::AudioOptions& options,
|
||||
int sample_rate,
|
||||
int num_channels,
|
||||
int buffer_size_ms,
|
||||
webrtc::TaskQueueFactory* task_queue_factory);
|
||||
|
||||
~InternalSource() override;
|
||||
|
||||
SourceState state() const override;
|
||||
bool remote() const override;
|
||||
|
||||
const webrtc::AudioOptions options() const override;
|
||||
|
||||
void AddSink(webrtc::AudioTrackSinkInterface* sink) override;
|
||||
void RemoveSink(webrtc::AudioTrackSinkInterface* sink) override;
|
||||
|
||||
void set_options(const webrtc::AudioOptions& options);
|
||||
|
||||
bool capture_frame(rust::Slice<const int16_t> audio_data,
|
||||
uint32_t sample_rate,
|
||||
uint32_t number_of_channels,
|
||||
size_t number_of_frames,
|
||||
const SourceContext* ctx,
|
||||
void (*on_complete)(const SourceContext*));
|
||||
|
||||
void clear_buffer();
|
||||
|
||||
// Indicate this is an external audio source (when external_audio_source.patch is applied).
|
||||
// This prevents AudioState from sending device audio to streams using this source.
|
||||
// Note: Omit 'override' to allow builds without the patch applied.
|
||||
// When the patch is applied, this will correctly override the base class virtual method.
|
||||
bool is_external_source() const { return true; }
|
||||
|
||||
private:
|
||||
mutable webrtc::Mutex mutex_;
|
||||
std::unique_ptr<webrtc::TaskQueueBase, webrtc::TaskQueueDeleter> audio_queue_;
|
||||
webrtc::RepeatingTaskHandle audio_task_;
|
||||
|
||||
std::vector<webrtc::AudioTrackSinkInterface*> sinks_ RTC_GUARDED_BY(mutex_);
|
||||
std::vector<int16_t> buffer_ RTC_GUARDED_BY(mutex_);
|
||||
|
||||
const SourceContext* capture_userdata_ RTC_GUARDED_BY(mutex_);
|
||||
void (*on_complete_)(const SourceContext*) RTC_GUARDED_BY(mutex_);
|
||||
|
||||
std::vector<int16_t> silence_buffer_;
|
||||
|
||||
int sample_rate_ = 0;
|
||||
int num_channels_ = 0;
|
||||
int queue_size_samples_ = 0;
|
||||
int notify_threshold_samples_ = 0;
|
||||
|
||||
webrtc::AudioOptions options_{};
|
||||
};
|
||||
|
||||
public:
|
||||
AudioTrackSource(AudioSourceOptions options,
|
||||
int sample_rate,
|
||||
int num_channels,
|
||||
int queue_size_ms,
|
||||
webrtc::TaskQueueFactory* task_queue_factory);
|
||||
|
||||
AudioSourceOptions audio_options() const;
|
||||
|
||||
void set_audio_options(const AudioSourceOptions& options) const;
|
||||
|
||||
bool capture_frame(rust::Slice<const int16_t> audio_data,
|
||||
uint32_t sample_rate,
|
||||
uint32_t number_of_channels,
|
||||
size_t number_of_frames,
|
||||
const SourceContext* ctx,
|
||||
CompleteCallback on_complete) const;
|
||||
|
||||
void clear_buffer() const;
|
||||
|
||||
webrtc::scoped_refptr<InternalSource> get() const;
|
||||
|
||||
private:
|
||||
webrtc::scoped_refptr<InternalSource> source_;
|
||||
};
|
||||
|
||||
std::shared_ptr<AudioTrackSource> new_audio_track_source(
|
||||
AudioSourceOptions options,
|
||||
int sample_rate,
|
||||
int num_channels,
|
||||
int queue_size_ms);
|
||||
|
||||
static std::shared_ptr<MediaStreamTrack> audio_to_media(
|
||||
std::shared_ptr<AudioTrack> track) {
|
||||
return track;
|
||||
}
|
||||
|
||||
static std::shared_ptr<AudioTrack> media_to_audio(
|
||||
std::shared_ptr<MediaStreamTrack> track) {
|
||||
return std::static_pointer_cast<AudioTrack>(track);
|
||||
}
|
||||
|
||||
static std::shared_ptr<AudioTrack> _shared_audio_track() {
|
||||
return nullptr; // Ignore
|
||||
}
|
||||
|
||||
static std::shared_ptr<AudioTrackSource> _shared_audio_track_source() {
|
||||
return nullptr; // Ignore
|
||||
}
|
||||
|
||||
} // namespace livekit_ffi
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2025 LiveKit, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "api/candidate.h"
|
||||
|
||||
namespace livekit_ffi {
|
||||
class Candidate;
|
||||
}
|
||||
#include "webrtc-sys/src/candidate.rs.h"
|
||||
|
||||
// webrtc::Candidate
|
||||
namespace livekit_ffi {
|
||||
|
||||
class Candidate {
|
||||
public:
|
||||
explicit Candidate(const webrtc::Candidate& candidate);
|
||||
|
||||
private:
|
||||
webrtc::Candidate candidate_;
|
||||
};
|
||||
|
||||
static std::shared_ptr<Candidate> _shared_candidate() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace livekit_ffi
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright 2025 LiveKit, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
#include "api/data_channel_interface.h"
|
||||
#include "livekit/webrtc.h"
|
||||
#include "rtc_base/synchronization/mutex.h"
|
||||
#include "rust/cxx.h"
|
||||
|
||||
namespace livekit_ffi {
|
||||
class DataChannel;
|
||||
} // namespace livekit_ffi
|
||||
#include "webrtc-sys/src/data_channel.rs.h"
|
||||
|
||||
namespace livekit_ffi {
|
||||
|
||||
class NativeDataChannelObserver;
|
||||
|
||||
webrtc::DataChannelInit to_native_data_channel_init(DataChannelInit init);
|
||||
|
||||
class DataChannel {
|
||||
public:
|
||||
explicit DataChannel(
|
||||
std::shared_ptr<RtcRuntime> rtc_runtime,
|
||||
webrtc::scoped_refptr<webrtc::DataChannelInterface> data_channel);
|
||||
~DataChannel();
|
||||
|
||||
void register_observer(rust::Box<DataChannelObserverWrapper> observer) const;
|
||||
void unregister_observer() const;
|
||||
bool send(const DataBuffer& buffer) const;
|
||||
int id() const;
|
||||
rust::String label() const;
|
||||
DataState state() const;
|
||||
void close() const;
|
||||
uint64_t buffered_amount() const;
|
||||
|
||||
private:
|
||||
mutable webrtc::Mutex mutex_;
|
||||
std::shared_ptr<RtcRuntime> rtc_runtime_;
|
||||
webrtc::scoped_refptr<webrtc::DataChannelInterface> data_channel_;
|
||||
mutable std::unique_ptr<NativeDataChannelObserver> observer_;
|
||||
};
|
||||
|
||||
static std::shared_ptr<DataChannel> _shared_data_channel() {
|
||||
return nullptr; // Ignore
|
||||
}
|
||||
|
||||
class NativeDataChannelObserver : public webrtc::DataChannelObserver {
|
||||
public:
|
||||
NativeDataChannelObserver(rust::Box<DataChannelObserverWrapper> observer,
|
||||
const DataChannel* dc);
|
||||
|
||||
void OnStateChange() override;
|
||||
void OnMessage(const webrtc::DataBuffer& buffer) override;
|
||||
void OnBufferedAmountChange(uint64_t sent_data_size) override;
|
||||
|
||||
private:
|
||||
rust::Box<DataChannelObserverWrapper> observer_;
|
||||
const DataChannel* dc_;
|
||||
};
|
||||
|
||||
} // namespace livekit_ffi
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2025 LiveKit, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <memory>
|
||||
|
||||
#include "modules/desktop_capture/desktop_capturer.h"
|
||||
#include "rust/cxx.h"
|
||||
|
||||
namespace livekit_ffi {
|
||||
class DesktopFrame;
|
||||
class DesktopCapturer;
|
||||
class DesktopCapturerOptions;
|
||||
class Source;
|
||||
} // namespace livekit_ffi
|
||||
|
||||
#include "webrtc-sys/src/desktop_capturer.rs.h"
|
||||
|
||||
namespace livekit_ffi {
|
||||
|
||||
class DesktopCapturer : public webrtc::DesktopCapturer::Callback {
|
||||
public:
|
||||
explicit DesktopCapturer(std::unique_ptr<webrtc::DesktopCapturer> capturer)
|
||||
: capturer(std::move(capturer)), callback(std::nullopt) {}
|
||||
|
||||
void OnCaptureResult(webrtc::DesktopCapturer::Result result,
|
||||
std::unique_ptr<webrtc::DesktopFrame> frame) final;
|
||||
|
||||
rust::Vec<Source> get_source_list() const;
|
||||
bool select_source(uint64_t id) const { return capturer->SelectSource(id); }
|
||||
void start(rust::Box<DesktopCapturerCallbackWrapper> callback);
|
||||
void capture_frame() const { capturer->CaptureFrame(); }
|
||||
|
||||
private:
|
||||
std::unique_ptr<webrtc::DesktopCapturer> capturer;
|
||||
std::optional<rust::Box<DesktopCapturerCallbackWrapper>> callback;
|
||||
};
|
||||
|
||||
class DesktopFrame {
|
||||
public:
|
||||
DesktopFrame(std::unique_ptr<webrtc::DesktopFrame> frame) : frame(std::move(frame)) {}
|
||||
int32_t width() const { return frame->size().width(); }
|
||||
|
||||
int32_t height() const { return frame->size().height(); }
|
||||
|
||||
int32_t left() const { return frame->rect().left(); }
|
||||
|
||||
int32_t top() const { return frame->rect().top(); }
|
||||
|
||||
int32_t stride() const { return frame->stride(); }
|
||||
|
||||
const uint8_t* data() const { return frame->data(); }
|
||||
|
||||
private:
|
||||
std::unique_ptr<webrtc::DesktopFrame> frame;
|
||||
};
|
||||
|
||||
std::unique_ptr<DesktopCapturer> new_desktop_capturer(DesktopCapturerOptions options);
|
||||
} // namespace livekit_ffi
|
||||
+232
@@ -0,0 +1,232 @@
|
||||
/*
|
||||
* Copyright 2025 LiveKit, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "api/crypto/frame_crypto_transformer.h"
|
||||
#include "api/scoped_refptr.h"
|
||||
#include "livekit/peer_connection.h"
|
||||
#include "livekit/peer_connection_factory.h"
|
||||
#include "livekit/rtp_receiver.h"
|
||||
#include "livekit/rtp_sender.h"
|
||||
#include "livekit/webrtc.h"
|
||||
#include "rtc_base/synchronization/mutex.h"
|
||||
#include "rust/cxx.h"
|
||||
|
||||
namespace livekit_ffi {
|
||||
|
||||
struct KeyProviderOptions;
|
||||
struct EncryptedPacket;
|
||||
enum class Algorithm : ::std::int32_t;
|
||||
class RtcFrameCryptorObserverWrapper;
|
||||
class NativeFrameCryptorObserver;
|
||||
class PacketTrailerHandler;
|
||||
|
||||
/// Shared secret key for frame encryption.
|
||||
class KeyProvider {
|
||||
public:
|
||||
KeyProvider(KeyProviderOptions options);
|
||||
~KeyProvider() {}
|
||||
|
||||
bool set_shared_key(int32_t index, rust::Vec<::std::uint8_t> key) const {
|
||||
std::vector<uint8_t> key_vec;
|
||||
std::copy(key.begin(), key.end(), std::back_inserter(key_vec));
|
||||
return impl_->SetSharedKey(index, key_vec);
|
||||
}
|
||||
|
||||
rust::Vec<::std::uint8_t> ratchet_shared_key(int32_t key_index) const {
|
||||
rust::Vec<uint8_t> vec;
|
||||
auto data = impl_->RatchetSharedKey(key_index);
|
||||
if (data.empty()) {
|
||||
throw std::runtime_error("ratchet_shared_key failed");
|
||||
}
|
||||
|
||||
std::move(data.begin(), data.end(), std::back_inserter(vec));
|
||||
return vec;
|
||||
}
|
||||
|
||||
rust::Vec<::std::uint8_t> get_shared_key(int32_t key_index) const {
|
||||
rust::Vec<uint8_t> vec;
|
||||
auto data = impl_->ExportSharedKey(key_index);
|
||||
if (data.empty()) {
|
||||
throw std::runtime_error("get_shared_key failed");
|
||||
}
|
||||
|
||||
std::move(data.begin(), data.end(), std::back_inserter(vec));
|
||||
return vec;
|
||||
}
|
||||
|
||||
/// Set the key at the given index.
|
||||
bool set_key(const ::rust::String participant_id,
|
||||
int32_t index,
|
||||
rust::Vec<::std::uint8_t> key) const {
|
||||
std::vector<uint8_t> key_vec;
|
||||
std::copy(key.begin(), key.end(), std::back_inserter(key_vec));
|
||||
return impl_->SetKey(
|
||||
std::string(participant_id.data(), participant_id.size()), index,
|
||||
key_vec);
|
||||
}
|
||||
|
||||
rust::Vec<::std::uint8_t> ratchet_key(const ::rust::String participant_id,
|
||||
int32_t key_index) const {
|
||||
rust::Vec<uint8_t> vec;
|
||||
auto data = impl_->RatchetKey(
|
||||
std::string(participant_id.data(), participant_id.size()), key_index);
|
||||
if (data.empty()) {
|
||||
throw std::runtime_error("ratchet_key failed");
|
||||
}
|
||||
|
||||
std::move(data.begin(), data.end(), std::back_inserter(vec));
|
||||
return vec;
|
||||
}
|
||||
|
||||
rust::Vec<::std::uint8_t> get_key(const ::rust::String participant_id,
|
||||
int32_t key_index) const {
|
||||
rust::Vec<uint8_t> vec;
|
||||
auto data = impl_->ExportKey(
|
||||
std::string(participant_id.data(), participant_id.size()), key_index);
|
||||
if (data.empty()) {
|
||||
throw std::runtime_error("get_key failed");
|
||||
}
|
||||
|
||||
std::move(data.begin(), data.end(), std::back_inserter(vec));
|
||||
return vec;
|
||||
}
|
||||
|
||||
void set_sif_trailer(rust::Vec<::std::uint8_t> trailer) const {
|
||||
std::vector<uint8_t> trailer_vec;
|
||||
std::copy(trailer.begin(), trailer.end(), std::back_inserter(trailer_vec));
|
||||
impl_->SetSifTrailer(trailer_vec);
|
||||
}
|
||||
|
||||
webrtc::scoped_refptr<webrtc::KeyProvider> rtc_key_provider() { return impl_; }
|
||||
|
||||
private:
|
||||
webrtc::scoped_refptr<webrtc::DefaultKeyProviderImpl> impl_;
|
||||
};
|
||||
|
||||
class FrameCryptor {
|
||||
public:
|
||||
FrameCryptor(std::shared_ptr<RtcRuntime> rtc_runtime,
|
||||
const std::string participant_id,
|
||||
webrtc::FrameCryptorTransformer::Algorithm algorithm,
|
||||
webrtc::scoped_refptr<webrtc::KeyProvider> key_provider,
|
||||
webrtc::scoped_refptr<webrtc::RtpSenderInterface> sender);
|
||||
|
||||
FrameCryptor(std::shared_ptr<RtcRuntime> rtc_runtime,
|
||||
const std::string participant_id,
|
||||
webrtc::FrameCryptorTransformer::Algorithm algorithm,
|
||||
webrtc::scoped_refptr<webrtc::KeyProvider> key_provider,
|
||||
webrtc::scoped_refptr<webrtc::RtpReceiverInterface> receiver);
|
||||
~FrameCryptor();
|
||||
|
||||
/// Enable/Disable frame crypto for the sender or receiver.
|
||||
void set_enabled(bool enabled) const;
|
||||
|
||||
/// Get the enabled state for the sender or receiver.
|
||||
bool enabled() const;
|
||||
|
||||
/// Set the key index for the sender or receiver.
|
||||
/// If the key index is not set, the key index will be set to 0.
|
||||
void set_key_index(int32_t index) const;
|
||||
|
||||
/// Get the key index for the sender or receiver.
|
||||
int32_t key_index() const;
|
||||
|
||||
rust::String participant_id() const { return participant_id_; }
|
||||
|
||||
void register_observer(
|
||||
rust::Box<RtcFrameCryptorObserverWrapper> observer) const;
|
||||
|
||||
void unregister_observer() const;
|
||||
|
||||
/// Attach a packet trailer transformer for chained processing.
|
||||
void set_packet_trailer_handler(
|
||||
std::shared_ptr<PacketTrailerHandler> handler) const;
|
||||
|
||||
private:
|
||||
std::shared_ptr<RtcRuntime> rtc_runtime_;
|
||||
const rust::String participant_id_;
|
||||
mutable webrtc::Mutex mutex_;
|
||||
webrtc::scoped_refptr<webrtc::FrameCryptorTransformer> e2ee_transformer_;
|
||||
webrtc::scoped_refptr<webrtc::KeyProvider> key_provider_;
|
||||
webrtc::scoped_refptr<webrtc::RtpSenderInterface> sender_;
|
||||
webrtc::scoped_refptr<webrtc::RtpReceiverInterface> receiver_;
|
||||
mutable webrtc::scoped_refptr<NativeFrameCryptorObserver> observer_;
|
||||
mutable webrtc::scoped_refptr<webrtc::FrameTransformerInterface>
|
||||
chained_transformer_;
|
||||
};
|
||||
|
||||
class NativeFrameCryptorObserver
|
||||
: public webrtc::FrameCryptorTransformerObserver {
|
||||
public:
|
||||
NativeFrameCryptorObserver(rust::Box<RtcFrameCryptorObserverWrapper> observer,
|
||||
const FrameCryptor* fc);
|
||||
~NativeFrameCryptorObserver();
|
||||
|
||||
void OnFrameCryptionStateChanged(const std::string participant_id,
|
||||
webrtc::FrameCryptionState error) override;
|
||||
|
||||
private:
|
||||
rust::Box<RtcFrameCryptorObserverWrapper> observer_;
|
||||
const FrameCryptor* fc_;
|
||||
};
|
||||
|
||||
class DataPacketCryptor {
|
||||
public:
|
||||
DataPacketCryptor(webrtc::FrameCryptorTransformer::Algorithm algorithm,
|
||||
webrtc::scoped_refptr<webrtc::KeyProvider> key_provider);
|
||||
|
||||
EncryptedPacket encrypt_data_packet(
|
||||
const ::rust::String participant_id,
|
||||
uint32_t key_index,
|
||||
rust::Vec<::std::uint8_t> data) const;
|
||||
|
||||
rust::Vec<::std::uint8_t> decrypt_data_packet(
|
||||
const ::rust::String participant_id,
|
||||
const EncryptedPacket& encrypted_packet) const;
|
||||
|
||||
private:
|
||||
webrtc::scoped_refptr<webrtc::DataPacketCryptor> data_packet_cryptor_;
|
||||
};
|
||||
|
||||
std::shared_ptr<FrameCryptor> new_frame_cryptor_for_rtp_sender(
|
||||
std::shared_ptr<PeerConnectionFactory> peer_factory,
|
||||
const ::rust::String participant_id,
|
||||
Algorithm algorithm,
|
||||
std::shared_ptr<KeyProvider> key_provider,
|
||||
std::shared_ptr<RtpSender> sender);
|
||||
|
||||
std::shared_ptr<FrameCryptor> new_frame_cryptor_for_rtp_receiver(
|
||||
std::shared_ptr<PeerConnectionFactory> peer_factory,
|
||||
const ::rust::String participant_id,
|
||||
Algorithm algorithm,
|
||||
std::shared_ptr<KeyProvider> key_provider,
|
||||
std::shared_ptr<RtpReceiver> receiver);
|
||||
|
||||
std::shared_ptr<KeyProvider> new_key_provider(KeyProviderOptions options);
|
||||
|
||||
std::shared_ptr<DataPacketCryptor> new_data_packet_cryptor(
|
||||
Algorithm algorithm,
|
||||
std::shared_ptr<KeyProvider> key_provider);
|
||||
|
||||
} // namespace livekit_ffi
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright 2025 LiveKit, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "api/task_queue/task_queue_factory.h"
|
||||
|
||||
namespace livekit_ffi {
|
||||
|
||||
webrtc::TaskQueueFactory* GetGlobalTaskQueueFactory();
|
||||
|
||||
} // namespace livekit_ffi
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2025 LiveKit, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "rust/cxx.h"
|
||||
|
||||
namespace livekit_ffi {
|
||||
class MediaStream;
|
||||
class AudioTrack;
|
||||
class VideoTrack;
|
||||
class Candidate;
|
||||
class RtpSender;
|
||||
class RtpReceiver;
|
||||
class RtpTransceiver;
|
||||
} // namespace livekit_ffi
|
||||
#include "webrtc-sys/src/helper.rs.h"
|
||||
|
||||
namespace livekit_ffi {
|
||||
|
||||
// Impl not needed
|
||||
static rust::Vec<MediaStreamPtr> _vec_media_stream_ptr() {
|
||||
throw;
|
||||
}
|
||||
static rust::Vec<CandidatePtr> _vec_candidate_ptr() {
|
||||
throw;
|
||||
}
|
||||
static rust::Vec<AudioTrackPtr> _vec_audio_track_ptr() {
|
||||
throw;
|
||||
}
|
||||
static rust::Vec<VideoTrackPtr> _vec_video_track_ptr() {
|
||||
throw;
|
||||
}
|
||||
static rust::Vec<RtpSenderPtr> _vec_rtp_sender_ptr() {
|
||||
throw;
|
||||
}
|
||||
static rust::Vec<RtpReceiverPtr> _vec_rtp_receiver_ptr() {
|
||||
throw;
|
||||
}
|
||||
static rust::Vec<RtpTransceiverPtr> _vec_rtp_transceiver_ptr() {
|
||||
throw;
|
||||
}
|
||||
|
||||
} // namespace livekit_ffi
|
||||
+155
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* Copyright 2025 LiveKit, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "api/jsep.h"
|
||||
#include "api/ref_counted_base.h"
|
||||
#include "api/set_local_description_observer_interface.h"
|
||||
#include "api/set_remote_description_observer_interface.h"
|
||||
#include "api/stats/rtc_stats_collector_callback.h"
|
||||
#include "livekit/rtc_error.h"
|
||||
#include "rtc_base/ref_count.h"
|
||||
#include "rust/cxx.h"
|
||||
|
||||
namespace livekit_ffi {
|
||||
class IceCandidate;
|
||||
class SessionDescription;
|
||||
}; // namespace livekit_ffi
|
||||
#include "webrtc-sys/src/jsep.rs.h"
|
||||
|
||||
namespace livekit_ffi {
|
||||
|
||||
class PeerContext;
|
||||
|
||||
class IceCandidate {
|
||||
public:
|
||||
explicit IceCandidate(
|
||||
std::unique_ptr<webrtc::IceCandidateInterface> ice_candidate);
|
||||
|
||||
rust::String sdp_mid() const;
|
||||
int sdp_mline_index() const;
|
||||
rust::String candidate() const; // TODO(theomonnom) Return livekit_ffi::Candidate
|
||||
// instead of rust::String
|
||||
|
||||
rust::String stringify() const;
|
||||
std::unique_ptr<webrtc::IceCandidateInterface> release();
|
||||
|
||||
private:
|
||||
std::unique_ptr<webrtc::IceCandidateInterface> ice_candidate_;
|
||||
};
|
||||
|
||||
std::shared_ptr<IceCandidate> create_ice_candidate(rust::String sdp_mid,
|
||||
int sdp_mline_index,
|
||||
rust::String sdp);
|
||||
|
||||
static std::shared_ptr<IceCandidate> _shared_ice_candidate() {
|
||||
return nullptr; // Ignore
|
||||
}
|
||||
|
||||
class SessionDescription {
|
||||
public:
|
||||
explicit SessionDescription(
|
||||
std::unique_ptr<webrtc::SessionDescriptionInterface> session_description);
|
||||
|
||||
SdpType sdp_type() const;
|
||||
rust::String stringify() const;
|
||||
std::unique_ptr<SessionDescription> clone() const;
|
||||
std::unique_ptr<webrtc::SessionDescriptionInterface> release();
|
||||
|
||||
private:
|
||||
std::unique_ptr<webrtc::SessionDescriptionInterface> session_description_;
|
||||
};
|
||||
|
||||
std::unique_ptr<SessionDescription> create_session_description(
|
||||
SdpType type,
|
||||
rust::String sdp);
|
||||
|
||||
static std::unique_ptr<SessionDescription> _unique_session_description() {
|
||||
return nullptr; // Ignore
|
||||
}
|
||||
|
||||
#ifdef LIVEKIT_TEST
|
||||
rust::String serialize_sdp_parse_error_for_test();
|
||||
#endif
|
||||
|
||||
class NativeCreateSdpObserver
|
||||
: public webrtc::CreateSessionDescriptionObserver {
|
||||
public:
|
||||
NativeCreateSdpObserver(
|
||||
rust::Box<PeerContext> ctx,
|
||||
rust::Fn<void(rust::Box<PeerContext> ctx,
|
||||
std::unique_ptr<SessionDescription>)> on_success,
|
||||
rust::Fn<void(rust::Box<PeerContext> ctx, RtcError)> on_error);
|
||||
|
||||
void OnSuccess(webrtc::SessionDescriptionInterface* desc) override;
|
||||
void OnFailure(webrtc::RTCError error) override;
|
||||
|
||||
private:
|
||||
rust::Box<PeerContext> ctx_;
|
||||
rust::Fn<void(rust::Box<PeerContext>, std::unique_ptr<SessionDescription>)>
|
||||
on_success_;
|
||||
rust::Fn<void(rust::Box<PeerContext>, RtcError)> on_error_;
|
||||
};
|
||||
|
||||
class NativeSetLocalSdpObserver
|
||||
: public webrtc::SetLocalDescriptionObserverInterface {
|
||||
public:
|
||||
NativeSetLocalSdpObserver(
|
||||
rust::Box<PeerContext> ctx,
|
||||
rust::Fn<void(rust::Box<PeerContext>, RtcError)> on_complete);
|
||||
|
||||
void OnSetLocalDescriptionComplete(webrtc::RTCError error) override;
|
||||
|
||||
private:
|
||||
rust::Box<PeerContext> ctx_;
|
||||
rust::Fn<void(rust::Box<PeerContext>, RtcError)> on_complete_;
|
||||
};
|
||||
|
||||
class NativeSetRemoteSdpObserver
|
||||
: public webrtc::SetRemoteDescriptionObserverInterface {
|
||||
public:
|
||||
NativeSetRemoteSdpObserver(
|
||||
rust::Box<PeerContext> ctx,
|
||||
rust::Fn<void(rust::Box<PeerContext>, RtcError)> on_complete);
|
||||
|
||||
void OnSetRemoteDescriptionComplete(webrtc::RTCError error) override;
|
||||
|
||||
private:
|
||||
rust::Box<PeerContext> ctx_;
|
||||
rust::Fn<void(rust::Box<PeerContext>, RtcError)> on_complete_;
|
||||
};
|
||||
|
||||
template <class T> // Context type
|
||||
class NativeRtcStatsCollector : public webrtc::RTCStatsCollectorCallback {
|
||||
public:
|
||||
NativeRtcStatsCollector(rust::Box<T> ctx,
|
||||
rust::Fn<void(rust::Box<T>, rust::String)> on_stats)
|
||||
: ctx_(std::move(ctx)), on_stats_(on_stats) {}
|
||||
|
||||
void OnStatsDelivered(
|
||||
const webrtc::scoped_refptr<const webrtc::RTCStatsReport>& report) override {
|
||||
on_stats_(std::move(ctx_), report->ToJson());
|
||||
}
|
||||
|
||||
private:
|
||||
rust::Box<T> ctx_;
|
||||
rust::Fn<void(rust::Box<T>, rust::String)> on_stats_;
|
||||
};
|
||||
|
||||
} // namespace livekit_ffi
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2025 LiveKit, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "api/media_stream_interface.h"
|
||||
#include "livekit/helper.h"
|
||||
#include "livekit/webrtc.h"
|
||||
#include "rust/cxx.h"
|
||||
|
||||
namespace livekit_ffi {
|
||||
class MediaStream;
|
||||
} // namespace livekit_ffi
|
||||
#include "webrtc-sys/src/media_stream.rs.h"
|
||||
|
||||
namespace livekit_ffi {
|
||||
|
||||
class MediaStream {
|
||||
public:
|
||||
MediaStream(std::shared_ptr<RtcRuntime> rtc_runtime,
|
||||
webrtc::scoped_refptr<webrtc::MediaStreamInterface> stream);
|
||||
|
||||
rust::String id() const;
|
||||
rust::Vec<VideoTrackPtr> get_video_tracks() const;
|
||||
rust::Vec<AudioTrackPtr> get_audio_tracks() const;
|
||||
|
||||
std::shared_ptr<AudioTrack> find_audio_track(rust::String track_id) const;
|
||||
std::shared_ptr<VideoTrack> find_video_track(rust::String track_id) const;
|
||||
|
||||
bool add_track(std::shared_ptr<MediaStreamTrack> track) const;
|
||||
bool remove_track(std::shared_ptr<MediaStreamTrack> track) const;
|
||||
|
||||
private:
|
||||
std::shared_ptr<RtcRuntime> rtc_runtime_;
|
||||
webrtc::scoped_refptr<webrtc::MediaStreamInterface> media_stream_;
|
||||
};
|
||||
|
||||
static std::shared_ptr<MediaStream> _shared_media_stream() {
|
||||
return nullptr; // Ignore
|
||||
}
|
||||
|
||||
} // namespace livekit_ffi
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2025 LiveKit, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "api/media_stream_interface.h"
|
||||
#include "livekit/helper.h"
|
||||
#include "livekit/webrtc.h"
|
||||
#include "rust/cxx.h"
|
||||
|
||||
namespace livekit_ffi {
|
||||
class MediaStreamTrack;
|
||||
} // namespace livekit_ffi
|
||||
#include "webrtc-sys/src/media_stream_track.rs.h"
|
||||
|
||||
namespace livekit_ffi {
|
||||
|
||||
class MediaStreamTrack {
|
||||
protected:
|
||||
MediaStreamTrack(std::shared_ptr<RtcRuntime>,
|
||||
webrtc::scoped_refptr<webrtc::MediaStreamTrackInterface> track);
|
||||
|
||||
public:
|
||||
rust::String kind() const;
|
||||
rust::String id() const;
|
||||
|
||||
bool enabled() const;
|
||||
bool set_enabled(bool enable) const;
|
||||
|
||||
TrackState state() const;
|
||||
|
||||
webrtc::scoped_refptr<webrtc::MediaStreamTrackInterface> rtc_track() const {
|
||||
return track_;
|
||||
}
|
||||
|
||||
protected:
|
||||
std::shared_ptr<RtcRuntime> rtc_runtime_;
|
||||
webrtc::scoped_refptr<webrtc::MediaStreamTrackInterface> track_;
|
||||
};
|
||||
|
||||
static std::shared_ptr<MediaStreamTrack> _shared_media_stream_track() {
|
||||
return nullptr; // Ignore
|
||||
}
|
||||
|
||||
} // namespace livekit_ffi
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2025 LiveKit, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "api/video_codecs/video_decoder_factory.h"
|
||||
#include "api/video_codecs/video_encoder_factory.h"
|
||||
|
||||
namespace livekit_ffi {
|
||||
|
||||
std::unique_ptr<webrtc::VideoEncoderFactory> CreateObjCVideoEncoderFactory();
|
||||
std::unique_ptr<webrtc::VideoDecoderFactory> CreateObjCVideoDecoderFactory();
|
||||
|
||||
} // namespace livekit_ffi
|
||||
+290
@@ -0,0 +1,290 @@
|
||||
/*
|
||||
* Copyright 2025 LiveKit, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <deque>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/types/optional.h"
|
||||
#include "api/frame_transformer_interface.h"
|
||||
#include "api/rtp_sender_interface.h"
|
||||
#include "api/rtp_receiver_interface.h"
|
||||
#include "api/scoped_refptr.h"
|
||||
#include "livekit/webrtc.h"
|
||||
#include "rtc_base/synchronization/mutex.h"
|
||||
#include "rust/cxx.h"
|
||||
|
||||
// Forward declarations to avoid circular includes
|
||||
// (video_track.h -> packet_trailer.h -> peer_connection.h -> media_stream.h -> video_track.h)
|
||||
namespace livekit_ffi {
|
||||
class PeerConnectionFactory;
|
||||
class RtpSender;
|
||||
class RtpReceiver;
|
||||
enum class VideoPublishTimingStage : int32_t;
|
||||
enum class VideoSubscribeTimingStage : int32_t;
|
||||
struct VideoPublishTimingObserverWrapper;
|
||||
struct VideoSubscribeTimingObserverWrapper;
|
||||
} // namespace livekit_ffi
|
||||
|
||||
namespace livekit_ffi {
|
||||
|
||||
// Magic bytes to identify packet trailers: "LKTS" (LiveKit TimeStamp)
|
||||
constexpr uint8_t kPacketTrailerMagic[4] = {'L', 'K', 'T', 'S'};
|
||||
|
||||
// Trailer envelope: [trailer_len: 1B] [magic: 4B] = 5 bytes.
|
||||
// Always present at the end of every trailer.
|
||||
constexpr size_t kTrailerEnvelopeSize = 5;
|
||||
|
||||
// TLV element overhead: [tag: 1B] [len: 1B] = 2 bytes before value.
|
||||
// All TLV bytes (tag, len, value) are XORed with 0xFF.
|
||||
|
||||
// TLV tag IDs
|
||||
constexpr uint8_t kTagTimestampUs = 0x01; // value: 8 bytes big-endian uint64
|
||||
constexpr uint8_t kTagFrameId = 0x02; // value: 4 bytes big-endian uint32
|
||||
|
||||
constexpr size_t kTimestampTlvSize = 10; // tag + len + 8-byte value
|
||||
constexpr size_t kFrameIdTlvSize = 6; // tag + len + 4-byte value
|
||||
|
||||
// Trailer size varies because frame_id is omitted when it is unset (0).
|
||||
constexpr size_t kPacketTrailerMinSize =
|
||||
kTimestampTlvSize + kTrailerEnvelopeSize;
|
||||
constexpr size_t kPacketTrailerMaxSize =
|
||||
kTimestampTlvSize + kFrameIdTlvSize + kTrailerEnvelopeSize;
|
||||
|
||||
struct PacketTrailerMetadata {
|
||||
uint64_t user_timestamp;
|
||||
uint32_t frame_id;
|
||||
uint32_t ssrc; // SSRC that produced this entry (for simulcast tracking)
|
||||
};
|
||||
|
||||
/// Frame transformer that appends/extracts packet trailers.
|
||||
/// This transformer can be used standalone or in conjunction with e2ee.
|
||||
///
|
||||
/// On the send side, user timestamps are stored in an internal map keyed
|
||||
/// by capture timestamp (microseconds). When TransformSend fires it
|
||||
/// looks up the user timestamp via the frame's CaptureTime().
|
||||
///
|
||||
/// On the receive side, extracted frame metadata is stored in an
|
||||
/// internal map keyed by RTP timestamp (uint32_t). Decoded frames can
|
||||
/// look up their metadata via lookup_frame_metadata(rtp_ts).
|
||||
class PacketTrailerTransformer : public webrtc::FrameTransformerInterface {
|
||||
public:
|
||||
enum class Direction { kSend, kReceive };
|
||||
|
||||
explicit PacketTrailerTransformer(Direction direction);
|
||||
~PacketTrailerTransformer() override = default;
|
||||
|
||||
// FrameTransformerInterface implementation
|
||||
void Transform(
|
||||
std::unique_ptr<webrtc::TransformableFrameInterface> frame) override;
|
||||
void RegisterTransformedFrameCallback(
|
||||
webrtc::scoped_refptr<webrtc::TransformedFrameCallback> callback) override;
|
||||
void RegisterTransformedFrameSinkCallback(
|
||||
webrtc::scoped_refptr<webrtc::TransformedFrameCallback> callback,
|
||||
uint32_t ssrc) override;
|
||||
void UnregisterTransformedFrameCallback() override;
|
||||
void UnregisterTransformedFrameSinkCallback(uint32_t ssrc) override;
|
||||
|
||||
/// Enable/disable timestamp embedding
|
||||
void set_enabled(bool enabled);
|
||||
bool enabled() const;
|
||||
|
||||
/// Lookup the frame metadata associated with a given RTP timestamp.
|
||||
/// Returns the metadata if found, nullopt otherwise.
|
||||
/// The entry is removed from the map after lookup.
|
||||
std::optional<PacketTrailerMetadata> lookup_frame_metadata(uint32_t rtp_timestamp);
|
||||
|
||||
/// Store frame metadata for a given capture timestamp (sender side).
|
||||
/// Called from VideoTrackSource::on_captured_frame with the
|
||||
/// TimestampAligner-adjusted timestamp, which matches CaptureTime()
|
||||
/// in the encoder pipeline.
|
||||
void store_frame_metadata(int64_t capture_timestamp_us,
|
||||
uint64_t user_timestamp,
|
||||
uint32_t frame_id);
|
||||
|
||||
/// Set the observer receiving sender-side publish timing events.
|
||||
void set_publish_timing_observer(
|
||||
rust::Box<VideoPublishTimingObserverWrapper> observer);
|
||||
|
||||
/// Clear the observer receiving sender-side publish timing events.
|
||||
void clear_publish_timing_observer();
|
||||
|
||||
/// Emit a sender-side publish timing event.
|
||||
void emit_publish_timing(VideoPublishTimingStage stage,
|
||||
uint64_t user_timestamp,
|
||||
uint32_t frame_id) const;
|
||||
|
||||
/// Set the observer receiving receiver-side subscribe timing events.
|
||||
void set_subscribe_timing_observer(
|
||||
rust::Box<VideoSubscribeTimingObserverWrapper> observer);
|
||||
|
||||
/// Clear the observer receiving receiver-side subscribe timing events.
|
||||
void clear_subscribe_timing_observer();
|
||||
|
||||
/// Emit a receiver-side subscribe timing event.
|
||||
void emit_subscribe_timing(VideoSubscribeTimingStage stage,
|
||||
uint64_t user_timestamp,
|
||||
uint32_t frame_id) const;
|
||||
|
||||
private:
|
||||
void TransformSend(
|
||||
std::unique_ptr<webrtc::TransformableFrameInterface> frame);
|
||||
void TransformReceive(
|
||||
std::unique_ptr<webrtc::TransformableFrameInterface> frame);
|
||||
void emit_subscribe_timing(VideoSubscribeTimingStage stage,
|
||||
uint64_t user_timestamp,
|
||||
uint32_t frame_id,
|
||||
uint64_t timestamp_us) const;
|
||||
bool publish_timing_enabled() const;
|
||||
bool subscribe_timing_enabled() const;
|
||||
|
||||
PacketTrailerMetadata LookupSendMetadata(
|
||||
const webrtc::TransformableFrameInterface& frame,
|
||||
uint32_t ssrc,
|
||||
uint32_t rtp_timestamp) const;
|
||||
|
||||
/// Append frame metadata trailer to frame data
|
||||
std::vector<uint8_t> AppendTrailer(
|
||||
webrtc::ArrayView<const uint8_t> data,
|
||||
uint64_t user_timestamp,
|
||||
uint32_t frame_id);
|
||||
|
||||
/// Extract and remove frame metadata trailer from frame data
|
||||
std::optional<PacketTrailerMetadata> ExtractTrailer(
|
||||
webrtc::ArrayView<const uint8_t> data,
|
||||
std::vector<uint8_t>& out_data);
|
||||
|
||||
const Direction direction_;
|
||||
std::atomic<bool> enabled_{true};
|
||||
mutable webrtc::Mutex mutex_;
|
||||
webrtc::scoped_refptr<webrtc::TransformedFrameCallback> callback_;
|
||||
std::unordered_map<uint32_t,
|
||||
webrtc::scoped_refptr<webrtc::TransformedFrameCallback>>
|
||||
sink_callbacks_;
|
||||
// Send-side map: capture timestamp (us) -> frame metadata.
|
||||
// Populated by store_frame_metadata(), consumed by TransformSend()
|
||||
// via CaptureTime() lookup.
|
||||
mutable webrtc::Mutex send_map_mutex_;
|
||||
mutable std::unordered_map<int64_t, PacketTrailerMetadata> send_map_;
|
||||
mutable std::deque<int64_t> send_map_order_;
|
||||
static constexpr size_t kMaxSendMapEntries = 300;
|
||||
|
||||
// Receive-side map: RTP timestamp -> frame metadata.
|
||||
// Keyed by RTP timestamp so decoded frames can look up their
|
||||
// metadata regardless of frame drops or reordering.
|
||||
mutable webrtc::Mutex recv_map_mutex_;
|
||||
mutable std::unordered_map<uint32_t, PacketTrailerMetadata> recv_map_;
|
||||
mutable std::deque<uint32_t> recv_map_order_;
|
||||
static constexpr size_t kMaxRecvMapEntries = 300;
|
||||
|
||||
// Simulcast tracking: detect layer switches and flush stale entries.
|
||||
mutable uint32_t recv_active_ssrc_{0};
|
||||
|
||||
mutable webrtc::Mutex publish_timing_observer_mutex_;
|
||||
std::atomic<bool> publish_timing_enabled_{false};
|
||||
mutable std::shared_ptr<rust::Box<VideoPublishTimingObserverWrapper>>
|
||||
publish_timing_observer_;
|
||||
mutable webrtc::Mutex subscribe_timing_observer_mutex_;
|
||||
std::atomic<bool> subscribe_timing_enabled_{false};
|
||||
mutable std::shared_ptr<rust::Box<VideoSubscribeTimingObserverWrapper>>
|
||||
subscribe_timing_observer_;
|
||||
};
|
||||
|
||||
/// Wrapper class for Rust FFI that manages packet trailer transformers.
|
||||
class PacketTrailerHandler {
|
||||
public:
|
||||
PacketTrailerHandler(
|
||||
std::shared_ptr<RtcRuntime> rtc_runtime,
|
||||
webrtc::scoped_refptr<webrtc::RtpSenderInterface> sender);
|
||||
|
||||
PacketTrailerHandler(
|
||||
std::shared_ptr<RtcRuntime> rtc_runtime,
|
||||
webrtc::scoped_refptr<webrtc::RtpReceiverInterface> receiver);
|
||||
|
||||
~PacketTrailerHandler() = default;
|
||||
|
||||
/// Enable/disable timestamp embedding
|
||||
void set_enabled(bool enabled) const;
|
||||
bool enabled() const;
|
||||
|
||||
/// Lookup the user timestamp for a given RTP timestamp (receiver side).
|
||||
/// Returns UINT64_MAX if not found. The entry is removed after lookup.
|
||||
/// Also caches the frame_id for retrieval via last_lookup_frame_id().
|
||||
uint64_t lookup_timestamp(uint32_t rtp_timestamp) const;
|
||||
|
||||
/// Returns the frame_id from the most recent successful
|
||||
/// lookup_timestamp() call. Returns 0 if no lookup succeeded.
|
||||
uint32_t last_lookup_frame_id() const;
|
||||
|
||||
/// Store frame metadata for a given capture timestamp (sender side).
|
||||
void store_frame_metadata(int64_t capture_timestamp_us,
|
||||
uint64_t user_timestamp,
|
||||
uint32_t frame_id) const;
|
||||
|
||||
/// Set the observer receiving sender-side publish timing events.
|
||||
void set_publish_timing_observer(
|
||||
rust::Box<VideoPublishTimingObserverWrapper> observer) const;
|
||||
|
||||
/// Clear the observer receiving sender-side publish timing events.
|
||||
void clear_publish_timing_observer() const;
|
||||
|
||||
/// Emit a sender-side publish timing event.
|
||||
void emit_publish_timing(VideoPublishTimingStage stage,
|
||||
uint64_t user_timestamp,
|
||||
uint32_t frame_id) const;
|
||||
|
||||
/// Set the observer receiving receiver-side subscribe timing events.
|
||||
void set_subscribe_timing_observer(
|
||||
rust::Box<VideoSubscribeTimingObserverWrapper> observer) const;
|
||||
|
||||
/// Clear the observer receiving receiver-side subscribe timing events.
|
||||
void clear_subscribe_timing_observer() const;
|
||||
|
||||
/// Emit a receiver-side subscribe timing event.
|
||||
void emit_subscribe_timing(VideoSubscribeTimingStage stage,
|
||||
uint64_t user_timestamp,
|
||||
uint32_t frame_id) const;
|
||||
|
||||
/// Access the underlying transformer for chaining.
|
||||
webrtc::scoped_refptr<PacketTrailerTransformer> transformer() const;
|
||||
|
||||
private:
|
||||
std::shared_ptr<RtcRuntime> rtc_runtime_;
|
||||
webrtc::scoped_refptr<PacketTrailerTransformer> transformer_;
|
||||
webrtc::scoped_refptr<webrtc::RtpSenderInterface> sender_;
|
||||
webrtc::scoped_refptr<webrtc::RtpReceiverInterface> receiver_;
|
||||
mutable uint32_t last_frame_id_{0};
|
||||
};
|
||||
|
||||
// Factory functions for Rust FFI
|
||||
|
||||
std::shared_ptr<PacketTrailerHandler> new_packet_trailer_sender(
|
||||
std::shared_ptr<PeerConnectionFactory> peer_factory,
|
||||
std::shared_ptr<RtpSender> sender);
|
||||
|
||||
std::shared_ptr<PacketTrailerHandler> new_packet_trailer_receiver(
|
||||
std::shared_ptr<PeerConnectionFactory> peer_factory,
|
||||
std::shared_ptr<RtpReceiver> receiver);
|
||||
|
||||
} // namespace livekit_ffi
|
||||
+207
@@ -0,0 +1,207 @@
|
||||
/*
|
||||
* Copyright 2025 LiveKit, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "api/peer_connection_interface.h"
|
||||
#include "api/scoped_refptr.h"
|
||||
#include "livekit/data_channel.h"
|
||||
#include "livekit/helper.h"
|
||||
#include "livekit/jsep.h"
|
||||
#include "livekit/media_stream.h"
|
||||
#include "livekit/rtc_error.h"
|
||||
#include "livekit/rtp_receiver.h"
|
||||
#include "livekit/rtp_sender.h"
|
||||
#include "livekit/rtp_transceiver.h"
|
||||
#include "livekit/webrtc.h"
|
||||
#include "rust/cxx.h"
|
||||
#include "webrtc-sys/src/data_channel.rs.h"
|
||||
|
||||
namespace livekit_ffi {
|
||||
class PeerConnection;
|
||||
} // namespace livekit_ffi
|
||||
#include "webrtc-sys/src/peer_connection.rs.h"
|
||||
|
||||
namespace livekit_ffi {
|
||||
|
||||
webrtc::PeerConnectionInterface::RTCConfiguration to_native_rtc_configuration(
|
||||
RtcConfiguration config);
|
||||
|
||||
class PeerConnectionObserverWrapper;
|
||||
|
||||
class PeerConnection : webrtc::PeerConnectionObserver {
|
||||
public:
|
||||
PeerConnection(
|
||||
std::shared_ptr<RtcRuntime> rtc_runtime,
|
||||
webrtc::scoped_refptr<webrtc::PeerConnectionFactoryInterface> pc_factory,
|
||||
rust::Box<PeerConnectionObserverWrapper> observer);
|
||||
|
||||
~PeerConnection();
|
||||
|
||||
bool Initialize(webrtc::PeerConnectionInterface::RTCConfiguration config);
|
||||
|
||||
void set_configuration(RtcConfiguration config) const;
|
||||
|
||||
void create_offer(
|
||||
RtcOfferAnswerOptions options,
|
||||
rust::Box<PeerContext> ctx,
|
||||
rust::Fn<void(rust::Box<PeerContext>,
|
||||
std::unique_ptr<SessionDescription>)> on_success,
|
||||
rust::Fn<void(rust::Box<PeerContext>, RtcError)> on_error) const;
|
||||
|
||||
void create_answer(
|
||||
RtcOfferAnswerOptions options,
|
||||
rust::Box<PeerContext> ctx,
|
||||
rust::Fn<void(rust::Box<PeerContext>,
|
||||
std::unique_ptr<SessionDescription>)> on_success,
|
||||
rust::Fn<void(rust::Box<PeerContext>, RtcError)> on_error) const;
|
||||
|
||||
void set_local_description(
|
||||
std::unique_ptr<SessionDescription> desc,
|
||||
rust::Box<PeerContext> ctx,
|
||||
rust::Fn<void(rust::Box<PeerContext>, RtcError)> on_complete) const;
|
||||
|
||||
void set_remote_description(
|
||||
std::unique_ptr<SessionDescription> desc,
|
||||
rust::Box<PeerContext> ctx,
|
||||
rust::Fn<void(rust::Box<PeerContext>, RtcError)> on_complete) const;
|
||||
|
||||
std::shared_ptr<DataChannel> create_data_channel(rust::String label,
|
||||
DataChannelInit init) const;
|
||||
|
||||
void add_ice_candidate(
|
||||
std::shared_ptr<IceCandidate> candidate,
|
||||
rust::Box<PeerContext> ctx,
|
||||
rust::Fn<void(rust::Box<PeerContext>, RtcError)> on_complete) const;
|
||||
|
||||
std::shared_ptr<RtpSender> add_track(
|
||||
std::shared_ptr<MediaStreamTrack> track,
|
||||
const rust::Vec<rust::String>& stream_ids) const;
|
||||
|
||||
void remove_track(std::shared_ptr<RtpSender> sender) const;
|
||||
|
||||
void get_stats(
|
||||
rust::Box<PeerContext> ctx,
|
||||
rust::Fn<void(rust::Box<PeerContext>, rust::String)> on_stats) const;
|
||||
|
||||
void restart_ice() const;
|
||||
|
||||
std::shared_ptr<RtpTransceiver> add_transceiver(
|
||||
std::shared_ptr<MediaStreamTrack> track,
|
||||
RtpTransceiverInit init) const;
|
||||
|
||||
std::shared_ptr<RtpTransceiver> add_transceiver_for_media(
|
||||
MediaType media_type,
|
||||
RtpTransceiverInit init) const;
|
||||
|
||||
rust::Vec<RtpSenderPtr> get_senders() const;
|
||||
|
||||
rust::Vec<RtpReceiverPtr> get_receivers() const;
|
||||
|
||||
rust::Vec<RtpTransceiverPtr> get_transceivers() const;
|
||||
|
||||
std::unique_ptr<SessionDescription> current_local_description() const;
|
||||
|
||||
std::unique_ptr<SessionDescription> current_remote_description() const;
|
||||
|
||||
std::unique_ptr<SessionDescription> pending_local_description() const;
|
||||
|
||||
std::unique_ptr<SessionDescription> pending_remote_description() const;
|
||||
|
||||
std::unique_ptr<SessionDescription> local_description() const;
|
||||
|
||||
std::unique_ptr<SessionDescription> remote_description() const;
|
||||
|
||||
PeerConnectionState connection_state() const;
|
||||
|
||||
SignalingState signaling_state() const;
|
||||
|
||||
IceGatheringState ice_gathering_state() const;
|
||||
|
||||
IceConnectionState ice_connection_state() const;
|
||||
|
||||
void close() const;
|
||||
|
||||
void OnSignalingChange(
|
||||
webrtc::PeerConnectionInterface::SignalingState new_state) override;
|
||||
|
||||
void OnAddStream(
|
||||
webrtc::scoped_refptr<webrtc::MediaStreamInterface> stream) override;
|
||||
|
||||
void OnRemoveStream(
|
||||
webrtc::scoped_refptr<webrtc::MediaStreamInterface> stream) override;
|
||||
|
||||
void OnDataChannel(
|
||||
webrtc::scoped_refptr<webrtc::DataChannelInterface> data_channel) override;
|
||||
|
||||
void OnRenegotiationNeeded() override;
|
||||
|
||||
void OnNegotiationNeededEvent(uint32_t event_id) override;
|
||||
|
||||
void OnIceConnectionChange(
|
||||
webrtc::PeerConnectionInterface::IceConnectionState new_state) override;
|
||||
|
||||
void OnStandardizedIceConnectionChange(
|
||||
webrtc::PeerConnectionInterface::IceConnectionState new_state) override;
|
||||
|
||||
void OnConnectionChange(
|
||||
webrtc::PeerConnectionInterface::PeerConnectionState new_state) override;
|
||||
|
||||
void OnIceGatheringChange(
|
||||
webrtc::PeerConnectionInterface::IceGatheringState new_state) override;
|
||||
|
||||
void OnIceCandidate(const webrtc::IceCandidate* candidate) override;
|
||||
|
||||
void OnIceCandidateError(const std::string& address,
|
||||
int port,
|
||||
const std::string& url,
|
||||
int error_code,
|
||||
const std::string& error_text) override;
|
||||
|
||||
void OnIceCandidateRemoved(const webrtc::IceCandidate* candidate) override;
|
||||
|
||||
void OnIceConnectionReceivingChange(bool receiving) override;
|
||||
|
||||
void OnIceSelectedCandidatePairChanged(
|
||||
const webrtc::CandidatePairChangeEvent& event) override;
|
||||
|
||||
void OnAddTrack(
|
||||
webrtc::scoped_refptr<webrtc::RtpReceiverInterface> receiver,
|
||||
const std::vector<webrtc::scoped_refptr<webrtc::MediaStreamInterface>>&
|
||||
streams) override;
|
||||
|
||||
void OnTrack(
|
||||
webrtc::scoped_refptr<webrtc::RtpTransceiverInterface> transceiver) override;
|
||||
|
||||
void OnRemoveTrack(
|
||||
webrtc::scoped_refptr<webrtc::RtpReceiverInterface> receiver) override;
|
||||
|
||||
void OnInterestingUsage(int usage_pattern) override;
|
||||
|
||||
private:
|
||||
std::shared_ptr<RtcRuntime> rtc_runtime_;
|
||||
webrtc::scoped_refptr<webrtc::PeerConnectionFactoryInterface> pc_factory_;
|
||||
rust::Box<PeerConnectionObserverWrapper> observer_;
|
||||
webrtc::scoped_refptr<webrtc::PeerConnectionInterface> peer_connection_;
|
||||
};
|
||||
|
||||
static std::shared_ptr<PeerConnection> _shared_peer_connection() {
|
||||
return nullptr; // Ignore
|
||||
}
|
||||
|
||||
} // namespace livekit_ffi
|
||||
Vendored
+83
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright 2025 LiveKit, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "api/environment/environment_factory.h"
|
||||
#include "api/peer_connection_interface.h"
|
||||
#include "api/scoped_refptr.h"
|
||||
#include "api/task_queue/task_queue_factory.h"
|
||||
#include "livekit/adm_proxy.h"
|
||||
#include "livekit/audio_device_controller.h"
|
||||
#include "media_stream.h"
|
||||
#include "rtp_parameters.h"
|
||||
#include "rust/cxx.h"
|
||||
#include "webrtc.h"
|
||||
|
||||
namespace livekit_ffi {
|
||||
class PeerConnectionFactory;
|
||||
class AudioDeviceController;
|
||||
class PeerConnectionObserverWrapper;
|
||||
} // namespace livekit_ffi
|
||||
#include "webrtc-sys/src/peer_connection_factory.rs.h"
|
||||
|
||||
namespace livekit_ffi {
|
||||
|
||||
class PeerConnection;
|
||||
struct RtcConfiguration;
|
||||
|
||||
webrtc::PeerConnectionInterface::RTCConfiguration to_native_rtc_configuration(
|
||||
RtcConfiguration config);
|
||||
|
||||
class PeerConnectionFactory {
|
||||
public:
|
||||
explicit PeerConnectionFactory(std::shared_ptr<RtcRuntime> rtc_runtime);
|
||||
~PeerConnectionFactory();
|
||||
|
||||
std::shared_ptr<PeerConnection> create_peer_connection(
|
||||
RtcConfiguration config,
|
||||
rust::Box<PeerConnectionObserverWrapper> observer) const;
|
||||
|
||||
std::shared_ptr<VideoTrack> create_video_track(
|
||||
rust::String label,
|
||||
std::shared_ptr<VideoTrackSource> source) const;
|
||||
|
||||
std::shared_ptr<AudioTrack> create_audio_track(
|
||||
rust::String label,
|
||||
std::shared_ptr<AudioTrackSource> source) const;
|
||||
|
||||
// Create an audio track that uses the ADM for capture (microphone)
|
||||
// This creates a track that captures from the selected recording device
|
||||
std::shared_ptr<AudioTrack> create_device_audio_track(
|
||||
rust::String label) const;
|
||||
|
||||
RtpCapabilities rtp_sender_capabilities(MediaType type) const;
|
||||
|
||||
RtpCapabilities rtp_receiver_capabilities(MediaType type) const;
|
||||
|
||||
std::shared_ptr<RtcRuntime> rtc_runtime() const { return rtc_runtime_; }
|
||||
std::shared_ptr<AudioDeviceController> audio_device() const;
|
||||
|
||||
private:
|
||||
std::shared_ptr<RtcRuntime> rtc_runtime_;
|
||||
webrtc::scoped_refptr<AdmProxy> adm_proxy_;
|
||||
std::shared_ptr<AudioDeviceController> audio_device_;
|
||||
webrtc::scoped_refptr<webrtc::PeerConnectionFactoryInterface> peer_factory_;
|
||||
webrtc::Environment env_;
|
||||
};
|
||||
|
||||
std::shared_ptr<PeerConnectionFactory> create_peer_connection_factory();
|
||||
} // namespace livekit_ffi
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright 2025 LiveKit, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace livekit_ffi {
|
||||
void ProhibitLibsrtpInitialization();
|
||||
}
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright 2026 LiveKit, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
|
||||
#include "api/audio/audio_device_defines.h"
|
||||
#include "api/audio/audio_frame.h"
|
||||
#include "common_audio/resampler/include/push_resampler.h"
|
||||
#include "rust/cxx.h"
|
||||
|
||||
namespace livekit_ffi {
|
||||
|
||||
struct RecordedAudioSinkWrapper;
|
||||
|
||||
// Installs the process-global recorded-audio sink. The platform ADM delivers
|
||||
// 10ms PCM frames on its capture thread; while a sink is installed each frame
|
||||
// is resampled to 48kHz mono and handed to Rust. Returns a generation token so
|
||||
// a later clear can target exactly this installation and never clobber a sink
|
||||
// that a subsequent caller installed in the meantime.
|
||||
uint64_t set_recorded_audio_sink(rust::Box<RecordedAudioSinkWrapper> sink);
|
||||
|
||||
// Removes the global recorded-audio sink only if `generation` matches the
|
||||
// currently-installed sink. A stale token is a no-op.
|
||||
void clear_recorded_audio_sink(uint64_t generation);
|
||||
|
||||
// AudioTransport interposer registered with the platform ADM in place of the
|
||||
// real transport. It tees recorded microphone frames to the global sink and
|
||||
// forwards every call unchanged to the real transport so the normal send and
|
||||
// playout pipelines are unaffected.
|
||||
class RecordingTransportProxy : public webrtc::AudioTransport {
|
||||
public:
|
||||
RecordingTransportProxy();
|
||||
~RecordingTransportProxy();
|
||||
|
||||
void set_real_transport(webrtc::AudioTransport* transport);
|
||||
|
||||
int32_t RecordedDataIsAvailable(const void* audioSamples,
|
||||
size_t nSamples,
|
||||
size_t nBytesPerSample,
|
||||
size_t nChannels,
|
||||
uint32_t samplesPerSec,
|
||||
uint32_t totalDelayMS,
|
||||
int32_t clockDrift,
|
||||
uint32_t currentMicLevel,
|
||||
bool keyPressed,
|
||||
uint32_t& newMicLevel) override;
|
||||
|
||||
int32_t RecordedDataIsAvailable(
|
||||
const void* audioSamples,
|
||||
size_t nSamples,
|
||||
size_t nBytesPerSample,
|
||||
size_t nChannels,
|
||||
uint32_t samplesPerSec,
|
||||
uint32_t totalDelayMS,
|
||||
int32_t clockDrift,
|
||||
uint32_t currentMicLevel,
|
||||
bool keyPressed,
|
||||
uint32_t& newMicLevel,
|
||||
std::optional<int64_t> estimatedCaptureTimeNS) override;
|
||||
|
||||
int32_t NeedMorePlayData(size_t nSamples,
|
||||
size_t nBytesPerSample,
|
||||
size_t nChannels,
|
||||
uint32_t samplesPerSec,
|
||||
void* audioSamples,
|
||||
size_t& nSamplesOut,
|
||||
int64_t* elapsed_time_ms,
|
||||
int64_t* ntp_time_ms) override;
|
||||
|
||||
void PullRenderData(int bits_per_sample,
|
||||
int sample_rate,
|
||||
size_t number_of_channels,
|
||||
size_t number_of_frames,
|
||||
void* audio_data,
|
||||
int64_t* elapsed_time_ms,
|
||||
int64_t* ntp_time_ms) override;
|
||||
|
||||
private:
|
||||
void TeeRecordedData(const void* audioSamples,
|
||||
size_t nSamples,
|
||||
size_t nBytesPerSample,
|
||||
size_t nChannels,
|
||||
uint32_t samplesPerSec);
|
||||
|
||||
std::atomic<webrtc::AudioTransport*> real_transport_{nullptr};
|
||||
|
||||
// Touched only on the ADM capture thread inside RecordedDataIsAvailable,
|
||||
// which WebRTC serializes, so neither member needs a lock.
|
||||
webrtc::AudioFrame capture_frame_;
|
||||
webrtc::PushResampler<int16_t> capture_resampler_;
|
||||
};
|
||||
|
||||
} // namespace livekit_ffi
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2025 LiveKit, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "api/rtc_error.h"
|
||||
#include "rust/cxx.h"
|
||||
#include "webrtc-sys/src/rtc_error.rs.h"
|
||||
|
||||
namespace livekit_ffi {
|
||||
|
||||
RtcError to_error(const webrtc::RTCError& error);
|
||||
std::string serialize_error(
|
||||
const RtcError& error); // to be used inside cxx::Exception msg
|
||||
|
||||
#ifdef LIVEKIT_TEST
|
||||
rust::String serialize_deserialize();
|
||||
#endif
|
||||
|
||||
} // namespace livekit_ffi
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2025 LiveKit, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "api/media_types.h"
|
||||
#include "api/priority.h"
|
||||
#include "api/rtp_parameters.h"
|
||||
#include "api/rtp_transceiver_direction.h"
|
||||
#include "webrtc-sys/src/rtp_parameters.rs.h"
|
||||
|
||||
namespace livekit_ffi {
|
||||
|
||||
webrtc::RtcpFeedback to_native_rtcp_feedback(RtcpFeedback feedback);
|
||||
webrtc::RtpCodecCapability to_native_rtp_codec_capability(
|
||||
RtpCodecCapability capability);
|
||||
webrtc::RtpHeaderExtensionCapability to_native_rtp_header_extension_capability(
|
||||
RtpHeaderExtensionCapability header);
|
||||
webrtc::RtpExtension to_native_rtp_extension(RtpExtension ext);
|
||||
webrtc::RtpFecParameters to_rtp_fec_parameters(RtpFecParameters fec);
|
||||
webrtc::RtpRtxParameters to_rtp_rtx_parameters(RtpRtxParameters rtx);
|
||||
webrtc::RtpEncodingParameters to_native_rtp_encoding_paramters(
|
||||
RtpEncodingParameters parameters);
|
||||
webrtc::RtpCodecParameters to_native_rtp_codec_parameters(
|
||||
RtpCodecParameters params);
|
||||
webrtc::RtpCapabilities to_rtp_capabilities(RtpCapabilities capabilities);
|
||||
webrtc::RtcpParameters to_native_rtcp_paramaters(RtcpParameters params);
|
||||
webrtc::RtpParameters to_native_rtp_parameters(RtpParameters params);
|
||||
|
||||
RtcpFeedback to_rust_rtcp_feedback(webrtc::RtcpFeedback feedback);
|
||||
RtpCodecCapability to_rust_rtp_codec_capability(
|
||||
webrtc::RtpCodecCapability capability);
|
||||
RtpHeaderExtensionCapability to_rust_rtp_header_extension_capability(
|
||||
webrtc::RtpHeaderExtensionCapability header);
|
||||
RtpExtension to_rust_rtp_extension(webrtc::RtpExtension ext);
|
||||
RtpFecParameters to_rust_rtp_fec_parameters(webrtc::RtpFecParameters fec);
|
||||
RtpRtxParameters to_rust_rtp_rtx_parameters(webrtc::RtpRtxParameters param);
|
||||
RtpEncodingParameters to_rust_rtp_encoding_parameters(
|
||||
webrtc::RtpEncodingParameters params);
|
||||
RtpCodecParameters to_rust_rtp_codec_parameters(
|
||||
webrtc::RtpCodecParameters params);
|
||||
RtpCapabilities to_rust_rtp_capabilities(webrtc::RtpCapabilities capabilities);
|
||||
RtcpParameters to_rust_rtcp_parameters(webrtc::RtcpParameters params);
|
||||
RtpParameters to_rust_rtp_parameters(webrtc::RtpParameters params);
|
||||
|
||||
} // namespace livekit_ffi
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright 2025 LiveKit, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "api/peer_connection_interface.h"
|
||||
#include "api/rtp_receiver_interface.h"
|
||||
#include "api/scoped_refptr.h"
|
||||
#include "livekit/helper.h"
|
||||
#include "livekit/media_stream.h"
|
||||
#include "livekit/rtp_parameters.h"
|
||||
#include "livekit/webrtc.h"
|
||||
#include "rust/cxx.h"
|
||||
|
||||
namespace livekit_ffi {
|
||||
class RtpReceiver;
|
||||
}
|
||||
#include "webrtc-sys/src/rtp_receiver.rs.h"
|
||||
namespace livekit_ffi {
|
||||
|
||||
// TODO(theomonnom): Implement RtpReceiverObserverInterface?
|
||||
// TODO(theomonnom): RtpSource
|
||||
// TODO(theomonnom): FrameTransformer & FrameDecryptor interface
|
||||
class RtpReceiver {
|
||||
public:
|
||||
RtpReceiver(
|
||||
std::shared_ptr<RtcRuntime> rtc_runtime,
|
||||
webrtc::scoped_refptr<webrtc::RtpReceiverInterface> receiver,
|
||||
webrtc::scoped_refptr<webrtc::PeerConnectionInterface> peer_connection);
|
||||
|
||||
std::shared_ptr<MediaStreamTrack> track() const;
|
||||
|
||||
void get_stats(
|
||||
rust::Box<ReceiverContext> ctx,
|
||||
rust::Fn<void(rust::Box<ReceiverContext>, rust::String)> on_stats) const;
|
||||
|
||||
rust::Vec<rust::String> stream_ids() const;
|
||||
rust::Vec<MediaStreamPtr> streams() const;
|
||||
|
||||
MediaType media_type() const;
|
||||
rust::String id() const;
|
||||
|
||||
RtpParameters get_parameters() const;
|
||||
|
||||
// bool set_parameters(RtpParameters parameters) const; // Seems unsupported
|
||||
|
||||
void set_jitter_buffer_minimum_delay(bool is_some,
|
||||
double delay_seconds) const;
|
||||
|
||||
webrtc::scoped_refptr<webrtc::RtpReceiverInterface> rtc_receiver() const {
|
||||
return receiver_;
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<RtcRuntime> rtc_runtime_;
|
||||
webrtc::scoped_refptr<webrtc::RtpReceiverInterface> receiver_;
|
||||
webrtc::scoped_refptr<webrtc::PeerConnectionInterface> peer_connection_;
|
||||
};
|
||||
|
||||
static std::shared_ptr<RtpReceiver> _shared_rtp_receiver() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace livekit_ffi
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright 2025 LiveKit, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "api/peer_connection_interface.h"
|
||||
#include "api/rtp_sender_interface.h"
|
||||
#include "api/scoped_refptr.h"
|
||||
#include "livekit/media_stream.h"
|
||||
#include "livekit/rtc_error.h"
|
||||
#include "livekit/rtp_parameters.h"
|
||||
#include "rust/cxx.h"
|
||||
|
||||
namespace livekit_ffi {
|
||||
class RtpSender;
|
||||
}
|
||||
#include "webrtc-sys/src/rtp_sender.rs.h"
|
||||
|
||||
namespace livekit_ffi {
|
||||
|
||||
// TODO(theomonnom): FrameTransformer & FrameEncryptor interface
|
||||
class RtpSender {
|
||||
public:
|
||||
RtpSender(
|
||||
std::shared_ptr<RtcRuntime> rtc_runtime,
|
||||
webrtc::scoped_refptr<webrtc::RtpSenderInterface> sender,
|
||||
webrtc::scoped_refptr<webrtc::PeerConnectionInterface> peer_connection);
|
||||
|
||||
bool set_track(std::shared_ptr<MediaStreamTrack> track) const;
|
||||
|
||||
std::shared_ptr<MediaStreamTrack> track() const;
|
||||
|
||||
uint32_t ssrc() const;
|
||||
|
||||
void get_stats(
|
||||
rust::Box<SenderContext> ctx,
|
||||
rust::Fn<void(rust::Box<SenderContext>, rust::String)> on_stats) const;
|
||||
|
||||
MediaType media_type() const;
|
||||
|
||||
rust::String id() const;
|
||||
|
||||
rust::Vec<rust::String> stream_ids() const;
|
||||
|
||||
void set_streams(const rust::Vec<rust::String>& stream_ids) const;
|
||||
|
||||
rust::Vec<RtpEncodingParameters> init_send_encodings() const;
|
||||
|
||||
RtpParameters get_parameters() const;
|
||||
|
||||
void set_parameters(RtpParameters params) const;
|
||||
|
||||
webrtc::scoped_refptr<webrtc::RtpSenderInterface> rtc_sender() const {
|
||||
return sender_;
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<RtcRuntime> rtc_runtime_;
|
||||
webrtc::scoped_refptr<webrtc::RtpSenderInterface> sender_;
|
||||
webrtc::scoped_refptr<webrtc::PeerConnectionInterface> peer_connection_;
|
||||
};
|
||||
|
||||
static std::shared_ptr<RtpSender> _shared_rtp_sender() {
|
||||
return nullptr; // Ignore
|
||||
}
|
||||
} // namespace livekit_ffi
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright 2025 LiveKit, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "api/peer_connection_interface.h"
|
||||
#include "api/rtp_parameters.h"
|
||||
#include "api/rtp_transceiver_direction.h"
|
||||
#include "api/rtp_transceiver_interface.h"
|
||||
#include "api/scoped_refptr.h"
|
||||
#include "livekit/rtc_error.h"
|
||||
#include "livekit/rtp_parameters.h"
|
||||
#include "livekit/rtp_receiver.h"
|
||||
#include "livekit/rtp_sender.h"
|
||||
#include "rust/cxx.h"
|
||||
|
||||
namespace livekit_ffi {
|
||||
class RtpTransceiver;
|
||||
}
|
||||
#include "webrtc-sys/src/rtp_transceiver.rs.h"
|
||||
|
||||
namespace livekit_ffi {
|
||||
|
||||
webrtc::RtpTransceiverInit to_native_rtp_transceiver_init(
|
||||
RtpTransceiverInit init);
|
||||
|
||||
class RtpTransceiver {
|
||||
public:
|
||||
RtpTransceiver(
|
||||
std::shared_ptr<RtcRuntime> rtc_runtime,
|
||||
webrtc::scoped_refptr<webrtc::RtpTransceiverInterface> transceiver,
|
||||
webrtc::scoped_refptr<webrtc::PeerConnectionInterface> peer_connection);
|
||||
|
||||
MediaType media_type() const;
|
||||
|
||||
rust::String mid() const;
|
||||
|
||||
std::shared_ptr<RtpSender> sender() const;
|
||||
|
||||
std::shared_ptr<RtpReceiver> receiver() const;
|
||||
|
||||
bool stopped() const;
|
||||
|
||||
bool stopping() const;
|
||||
|
||||
RtpTransceiverDirection direction() const;
|
||||
|
||||
void set_direction(RtpTransceiverDirection direction) const;
|
||||
|
||||
RtpTransceiverDirection current_direction() const;
|
||||
|
||||
RtpTransceiverDirection fired_direction() const;
|
||||
|
||||
void stop_standard() const;
|
||||
|
||||
void set_codec_preferences(rust::Vec<RtpCodecCapability> codecs) const;
|
||||
|
||||
rust::Vec<RtpCodecCapability> codec_preferences() const;
|
||||
|
||||
rust::Vec<RtpHeaderExtensionCapability> header_extensions_to_negotiate()
|
||||
const;
|
||||
|
||||
rust::Vec<RtpHeaderExtensionCapability> negotiated_header_extensions() const;
|
||||
|
||||
void set_header_extensions_to_negotiate(
|
||||
rust::Vec<RtpHeaderExtensionCapability> header_extensions_to_offer) const;
|
||||
|
||||
private:
|
||||
std::shared_ptr<RtcRuntime> rtc_runtime_;
|
||||
webrtc::scoped_refptr<webrtc::RtpTransceiverInterface> transceiver_;
|
||||
webrtc::scoped_refptr<webrtc::PeerConnectionInterface> peer_connection_;
|
||||
};
|
||||
|
||||
static std::shared_ptr<RtpTransceiver> _shared_rtp_transceiver() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace livekit_ffi
|
||||
Vendored
+129
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* Copyright 2025 LiveKit, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#include "api/environment/environment.h"
|
||||
#include "api/task_queue/task_queue_base.h"
|
||||
#include "modules/audio_device/include/audio_device.h"
|
||||
#include "rtc_base/synchronization/mutex.h"
|
||||
#include "rtc_base/task_utils/repeating_task.h"
|
||||
|
||||
namespace livekit_ffi {
|
||||
|
||||
class SyntheticAudioDevice : public webrtc::AudioDeviceModule {
|
||||
public:
|
||||
SyntheticAudioDevice(const webrtc::Environment& env);
|
||||
~SyntheticAudioDevice() override;
|
||||
|
||||
int32_t ActiveAudioLayer(AudioLayer* audioLayer) const override;
|
||||
int32_t RegisterAudioCallback(webrtc::AudioTransport* transport) override;
|
||||
|
||||
int32_t Init() override;
|
||||
int32_t Terminate() override;
|
||||
bool Initialized() const override;
|
||||
|
||||
int16_t PlayoutDevices() override;
|
||||
int16_t RecordingDevices() override;
|
||||
int32_t PlayoutDeviceName(uint16_t index,
|
||||
char name[webrtc::kAdmMaxDeviceNameSize],
|
||||
char guid[webrtc::kAdmMaxGuidSize]) override;
|
||||
|
||||
int32_t RecordingDeviceName(uint16_t index,
|
||||
char name[webrtc::kAdmMaxDeviceNameSize],
|
||||
char guid[webrtc::kAdmMaxGuidSize]) override;
|
||||
|
||||
int32_t SetPlayoutDevice(uint16_t index) override;
|
||||
int32_t SetPlayoutDevice(WindowsDeviceType device) override;
|
||||
int32_t SetRecordingDevice(uint16_t index) override;
|
||||
int32_t SetRecordingDevice(WindowsDeviceType device) override;
|
||||
|
||||
int32_t PlayoutIsAvailable(bool* available) override;
|
||||
int32_t InitPlayout() override;
|
||||
bool PlayoutIsInitialized() const override;
|
||||
int32_t RecordingIsAvailable(bool* available) override;
|
||||
int32_t InitRecording() override;
|
||||
bool RecordingIsInitialized() const override;
|
||||
|
||||
int32_t StartPlayout() override;
|
||||
int32_t StopPlayout() override;
|
||||
bool Playing() const override;
|
||||
int32_t StartRecording() override;
|
||||
int32_t StopRecording() override;
|
||||
bool Recording() const override;
|
||||
|
||||
int32_t InitSpeaker() override;
|
||||
bool SpeakerIsInitialized() const override;
|
||||
int32_t InitMicrophone() override;
|
||||
bool MicrophoneIsInitialized() const override;
|
||||
|
||||
int32_t SpeakerVolumeIsAvailable(bool* available) override;
|
||||
int32_t SetSpeakerVolume(uint32_t volume) override;
|
||||
int32_t SpeakerVolume(uint32_t* volume) const override;
|
||||
int32_t MaxSpeakerVolume(uint32_t* maxVolume) const override;
|
||||
int32_t MinSpeakerVolume(uint32_t* minVolume) const override;
|
||||
|
||||
int32_t MicrophoneVolumeIsAvailable(bool* available) override;
|
||||
int32_t SetMicrophoneVolume(uint32_t volume) override;
|
||||
int32_t MicrophoneVolume(uint32_t* volume) const override;
|
||||
int32_t MaxMicrophoneVolume(uint32_t* maxVolume) const override;
|
||||
int32_t MinMicrophoneVolume(uint32_t* minVolume) const override;
|
||||
|
||||
int32_t SpeakerMuteIsAvailable(bool* available) override;
|
||||
int32_t SetSpeakerMute(bool enable) override;
|
||||
int32_t SpeakerMute(bool* enabled) const override;
|
||||
|
||||
int32_t MicrophoneMuteIsAvailable(bool* available) override;
|
||||
int32_t SetMicrophoneMute(bool enable) override;
|
||||
int32_t MicrophoneMute(bool* enabled) const override;
|
||||
|
||||
int32_t StereoPlayoutIsAvailable(bool* available) const override;
|
||||
int32_t SetStereoPlayout(bool enable) override;
|
||||
int32_t StereoPlayout(bool* enabled) const override;
|
||||
int32_t StereoRecordingIsAvailable(bool* available) const override;
|
||||
int32_t SetStereoRecording(bool enable) override;
|
||||
int32_t StereoRecording(bool* enabled) const override;
|
||||
|
||||
int32_t PlayoutDelay(uint16_t* delayMS) const override;
|
||||
|
||||
bool BuiltInAECIsAvailable() const override;
|
||||
bool BuiltInAGCIsAvailable() const override;
|
||||
bool BuiltInNSIsAvailable() const override;
|
||||
|
||||
int32_t EnableBuiltInAEC(bool enable) override;
|
||||
int32_t EnableBuiltInAGC(bool enable) override;
|
||||
int32_t EnableBuiltInNS(bool enable) override;
|
||||
|
||||
#if defined(WEBRTC_IOS)
|
||||
int GetPlayoutAudioParameters(webrtc::AudioParameters* params) const override;
|
||||
int GetRecordAudioParameters(webrtc::AudioParameters* params) const override;
|
||||
#endif // WEBRTC_IOS
|
||||
|
||||
int32_t SetObserver(webrtc::AudioDeviceObserver* sink) override;
|
||||
|
||||
private:
|
||||
mutable webrtc::Mutex mutex_;
|
||||
std::vector<int16_t> data_;
|
||||
std::unique_ptr<webrtc::TaskQueueBase, webrtc::TaskQueueDeleter> audio_queue_;
|
||||
webrtc::RepeatingTaskHandle audio_task_;
|
||||
webrtc::AudioTransport* audio_transport_;
|
||||
const webrtc::Environment& env_;
|
||||
bool playing_{false};
|
||||
bool initialized_{false};
|
||||
};
|
||||
} // namespace livekit_ffi
|
||||
Vendored
+39
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2025 LiveKit, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "api/video_codecs/video_decoder.h"
|
||||
#include "api/video_codecs/video_decoder_factory.h"
|
||||
#include "absl/strings/match.h"
|
||||
|
||||
namespace livekit_ffi {
|
||||
class VideoDecoderFactory : public webrtc::VideoDecoderFactory {
|
||||
public:
|
||||
VideoDecoderFactory();
|
||||
|
||||
std::vector<webrtc::SdpVideoFormat> GetSupportedFormats() const override;
|
||||
|
||||
CodecSupport QueryCodecSupport(const webrtc::SdpVideoFormat& format,
|
||||
bool reference_scaling) const override;
|
||||
|
||||
std::unique_ptr<webrtc::VideoDecoder> Create(
|
||||
const webrtc::Environment& env, const webrtc::SdpVideoFormat& format) override;
|
||||
|
||||
private:
|
||||
std::vector<std::unique_ptr<webrtc::VideoDecoderFactory>> factories_;
|
||||
};
|
||||
} // namespace livekit_ffi
|
||||
Vendored
+56
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright 2025 LiveKit, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "api/video_codecs/video_encoder.h"
|
||||
#include "api/video_codecs/video_encoder_factory.h"
|
||||
|
||||
namespace livekit_ffi {
|
||||
class VideoEncoderFactory : public webrtc::VideoEncoderFactory {
|
||||
class InternalFactory : public webrtc::VideoEncoderFactory {
|
||||
public:
|
||||
InternalFactory();
|
||||
|
||||
std::vector<webrtc::SdpVideoFormat> GetSupportedFormats() const override;
|
||||
|
||||
CodecSupport QueryCodecSupport(
|
||||
const webrtc::SdpVideoFormat& format,
|
||||
std::optional<std::string> scalability_mode) const override;
|
||||
|
||||
std::unique_ptr<webrtc::VideoEncoder> Create(
|
||||
const webrtc::Environment& env, const webrtc::SdpVideoFormat& format) override;
|
||||
|
||||
private:
|
||||
std::vector<std::unique_ptr<webrtc::VideoEncoderFactory>> factories_;
|
||||
};
|
||||
|
||||
public:
|
||||
VideoEncoderFactory();
|
||||
|
||||
std::vector<webrtc::SdpVideoFormat> GetSupportedFormats() const override;
|
||||
|
||||
CodecSupport QueryCodecSupport(
|
||||
const webrtc::SdpVideoFormat& format,
|
||||
std::optional<std::string> scalability_mode) const override;
|
||||
|
||||
std::unique_ptr<webrtc::VideoEncoder> Create(
|
||||
const webrtc::Environment& env, const webrtc::SdpVideoFormat& format) override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<InternalFactory> internal_factory_;
|
||||
};
|
||||
} // namespace livekit_ffi
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright 2025 LiveKit, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "api/video/video_frame.h"
|
||||
#include "livekit/video_frame_buffer.h"
|
||||
#include "rtc_base/checks.h"
|
||||
|
||||
namespace livekit_ffi {
|
||||
class VideoFrame;
|
||||
class VideoFrameBuilder;
|
||||
} // namespace livekit_ffi
|
||||
#include "webrtc-sys/src/video_frame.rs.h"
|
||||
|
||||
namespace livekit_ffi {
|
||||
|
||||
class VideoFrame {
|
||||
public:
|
||||
explicit VideoFrame(const webrtc::VideoFrame& frame);
|
||||
|
||||
unsigned int width() const;
|
||||
unsigned int height() const;
|
||||
uint32_t size() const;
|
||||
uint16_t id() const;
|
||||
int64_t timestamp_us() const;
|
||||
int64_t ntp_time_ms() const;
|
||||
uint32_t timestamp() const;
|
||||
|
||||
VideoRotation rotation() const;
|
||||
std::unique_ptr<VideoFrameBuffer> video_frame_buffer() const;
|
||||
|
||||
webrtc::VideoFrame get() const;
|
||||
|
||||
private:
|
||||
webrtc::VideoFrame frame_;
|
||||
};
|
||||
|
||||
// Allow to create VideoFrames from Rust,
|
||||
// the builder pattern will be redone in Rust
|
||||
class VideoFrameBuilder {
|
||||
public:
|
||||
VideoFrameBuilder() = default;
|
||||
|
||||
// TODO(theomonnom): other setters?
|
||||
void set_video_frame_buffer(const VideoFrameBuffer& buffer);
|
||||
void set_timestamp_us(int64_t timestamp_us);
|
||||
void set_rotation(VideoRotation rotation);
|
||||
void set_id(uint16_t id);
|
||||
std::unique_ptr<VideoFrame> build();
|
||||
|
||||
private:
|
||||
webrtc::VideoFrame::Builder builder_;
|
||||
};
|
||||
|
||||
std::unique_ptr<VideoFrameBuilder> new_video_frame_builder();
|
||||
|
||||
} // namespace livekit_ffi
|
||||
+369
@@ -0,0 +1,369 @@
|
||||
/*
|
||||
* Copyright 2025 LiveKit, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "api/video/i420_buffer.h"
|
||||
#include "api/video/i422_buffer.h"
|
||||
#include "api/video/i444_buffer.h"
|
||||
#include "api/video/i010_buffer.h"
|
||||
#include "api/video/nv12_buffer.h"
|
||||
#include "api/video/video_frame_buffer.h"
|
||||
|
||||
namespace livekit_ffi {
|
||||
class VideoFrameBuffer;
|
||||
class FluxerGpuFrameBuffer;
|
||||
class PlanarYuvBuffer;
|
||||
class PlanarYuv8Buffer;
|
||||
class PlanarYuv16BBuffer;
|
||||
class BiplanarYuvBuffer;
|
||||
class BiplanarYuv8Buffer;
|
||||
class I420Buffer;
|
||||
class I420ABuffer;
|
||||
class I422Buffer;
|
||||
class I444Buffer;
|
||||
class I010Buffer;
|
||||
class NV12Buffer;
|
||||
} // namespace livekit_ffi
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <CoreVideo/CoreVideo.h>
|
||||
namespace livekit_ffi {
|
||||
typedef __CVBuffer PlatformImageBuffer;
|
||||
} // namespace livekit_ffi
|
||||
#else
|
||||
namespace livekit_ffi {
|
||||
typedef void PlatformImageBuffer;
|
||||
} // namespace livekit_ffi
|
||||
#endif
|
||||
|
||||
#include "webrtc-sys/src/video_frame_buffer.rs.h"
|
||||
|
||||
namespace livekit_ffi {
|
||||
|
||||
class VideoFrameBuffer {
|
||||
public:
|
||||
explicit VideoFrameBuffer(
|
||||
webrtc::scoped_refptr<webrtc::VideoFrameBuffer> buffer);
|
||||
|
||||
VideoFrameBufferType buffer_type() const;
|
||||
|
||||
unsigned int width() const;
|
||||
unsigned int height() const;
|
||||
|
||||
std::unique_ptr<I420Buffer> to_i420() const;
|
||||
|
||||
// Requires ownership
|
||||
std::unique_ptr<I420Buffer> get_i420();
|
||||
std::unique_ptr<I420ABuffer> get_i420a();
|
||||
std::unique_ptr<I422Buffer> get_i422();
|
||||
std::unique_ptr<I444Buffer> get_i444();
|
||||
std::unique_ptr<I010Buffer> get_i010();
|
||||
std::unique_ptr<NV12Buffer> get_nv12();
|
||||
webrtc::scoped_refptr<webrtc::VideoFrameBuffer> get() const;
|
||||
|
||||
protected:
|
||||
webrtc::scoped_refptr<webrtc::VideoFrameBuffer> buffer_;
|
||||
};
|
||||
|
||||
class FluxerGpuFrameBuffer : public webrtc::VideoFrameBuffer {
|
||||
public:
|
||||
enum class Kind {
|
||||
kD3D11Texture,
|
||||
kDmaBuf,
|
||||
};
|
||||
|
||||
FluxerGpuFrameBuffer(uint64_t handle,
|
||||
uint32_t width,
|
||||
uint32_t height,
|
||||
uint32_t dxgi_format);
|
||||
FluxerGpuFrameBuffer(int fd0,
|
||||
int fd1,
|
||||
int fd2,
|
||||
int fd3,
|
||||
uint32_t plane_count,
|
||||
uint32_t width,
|
||||
uint32_t height,
|
||||
uint32_t drm_format,
|
||||
uint64_t modifier,
|
||||
uint32_t stride0,
|
||||
uint32_t stride1,
|
||||
uint32_t stride2,
|
||||
uint32_t stride3,
|
||||
uint32_t offset0,
|
||||
uint32_t offset1,
|
||||
uint32_t offset2,
|
||||
uint32_t offset3,
|
||||
uint64_t device_uuid_hi,
|
||||
uint64_t device_uuid_lo);
|
||||
~FluxerGpuFrameBuffer() override;
|
||||
|
||||
Type type() const override;
|
||||
int width() const override;
|
||||
int height() const override;
|
||||
webrtc::scoped_refptr<webrtc::I420BufferInterface> ToI420() override;
|
||||
webrtc::scoped_refptr<webrtc::VideoFrameBuffer> CropAndScale(
|
||||
int offset_x,
|
||||
int offset_y,
|
||||
int crop_width,
|
||||
int crop_height,
|
||||
int scaled_width,
|
||||
int scaled_height) override;
|
||||
webrtc::scoped_refptr<webrtc::VideoFrameBuffer> GetMappedFrameBuffer(
|
||||
webrtc::ArrayView<Type> types) override;
|
||||
std::string storage_representation() const override;
|
||||
|
||||
Kind kind() const { return kind_; }
|
||||
uint64_t d3d11_handle() const { return d3d11_handle_; }
|
||||
uint32_t dxgi_format() const { return dxgi_format_; }
|
||||
uint32_t drm_format() const { return drm_format_; }
|
||||
uint64_t modifier() const { return modifier_; }
|
||||
uint32_t plane_count() const { return plane_count_; }
|
||||
int fd(uint32_t plane) const;
|
||||
uint32_t stride(uint32_t plane) const;
|
||||
uint32_t offset(uint32_t plane) const;
|
||||
uint64_t device_uuid_hi() const { return device_uuid_hi_; }
|
||||
uint64_t device_uuid_lo() const { return device_uuid_lo_; }
|
||||
|
||||
private:
|
||||
Kind kind_;
|
||||
uint32_t width_;
|
||||
uint32_t height_;
|
||||
uint64_t d3d11_handle_ = 0;
|
||||
uint32_t dxgi_format_ = 0;
|
||||
int fds_[4] = {-1, -1, -1, -1};
|
||||
uint32_t plane_count_ = 0;
|
||||
uint32_t drm_format_ = 0;
|
||||
uint64_t modifier_ = 0;
|
||||
uint32_t strides_[4] = {0, 0, 0, 0};
|
||||
uint32_t offsets_[4] = {0, 0, 0, 0};
|
||||
uint64_t device_uuid_hi_ = 0;
|
||||
uint64_t device_uuid_lo_ = 0;
|
||||
};
|
||||
|
||||
const FluxerGpuFrameBuffer* AsFluxerGpuFrameBuffer(
|
||||
const webrtc::VideoFrameBuffer* buffer);
|
||||
|
||||
class PlanarYuvBuffer : public VideoFrameBuffer {
|
||||
public:
|
||||
explicit PlanarYuvBuffer(webrtc::scoped_refptr<webrtc::PlanarYuvBuffer> buffer);
|
||||
|
||||
unsigned int chroma_width() const;
|
||||
unsigned int chroma_height() const;
|
||||
|
||||
unsigned int stride_y() const;
|
||||
unsigned int stride_u() const;
|
||||
unsigned int stride_v() const;
|
||||
|
||||
private:
|
||||
webrtc::PlanarYuvBuffer* buffer() const;
|
||||
};
|
||||
|
||||
class PlanarYuv8Buffer : public PlanarYuvBuffer {
|
||||
public:
|
||||
explicit PlanarYuv8Buffer(
|
||||
webrtc::scoped_refptr<webrtc::PlanarYuv8Buffer> buffer);
|
||||
|
||||
const uint8_t* data_y() const;
|
||||
const uint8_t* data_u() const;
|
||||
const uint8_t* data_v() const;
|
||||
|
||||
private:
|
||||
webrtc::PlanarYuv8Buffer* buffer() const;
|
||||
};
|
||||
|
||||
class PlanarYuv16BBuffer : public PlanarYuvBuffer {
|
||||
public:
|
||||
explicit PlanarYuv16BBuffer(
|
||||
webrtc::scoped_refptr<webrtc::PlanarYuv16BBuffer> buffer);
|
||||
|
||||
const uint16_t* data_y() const;
|
||||
const uint16_t* data_u() const;
|
||||
const uint16_t* data_v() const;
|
||||
|
||||
private:
|
||||
webrtc::PlanarYuv16BBuffer* buffer() const;
|
||||
};
|
||||
|
||||
class BiplanarYuvBuffer : public VideoFrameBuffer {
|
||||
public:
|
||||
explicit BiplanarYuvBuffer(
|
||||
webrtc::scoped_refptr<webrtc::BiplanarYuvBuffer> buffer);
|
||||
|
||||
unsigned int chroma_width() const;
|
||||
unsigned int chroma_height() const;
|
||||
|
||||
unsigned int stride_y() const;
|
||||
unsigned int stride_uv() const;
|
||||
|
||||
private:
|
||||
webrtc::BiplanarYuvBuffer* buffer() const;
|
||||
};
|
||||
|
||||
class BiplanarYuv8Buffer : public BiplanarYuvBuffer {
|
||||
public:
|
||||
explicit BiplanarYuv8Buffer(
|
||||
webrtc::scoped_refptr<webrtc::BiplanarYuv8Buffer> buffer);
|
||||
|
||||
const uint8_t* data_y() const;
|
||||
const uint8_t* data_uv() const;
|
||||
|
||||
private:
|
||||
webrtc::BiplanarYuv8Buffer* buffer() const;
|
||||
};
|
||||
|
||||
class I420Buffer : public PlanarYuv8Buffer {
|
||||
public:
|
||||
explicit I420Buffer(webrtc::scoped_refptr<webrtc::I420BufferInterface> buffer);
|
||||
|
||||
std::unique_ptr<I420Buffer> scale(int scaled_width, int scaled_height) const;
|
||||
|
||||
private:
|
||||
webrtc::I420BufferInterface* buffer() const;
|
||||
};
|
||||
|
||||
class I420ABuffer : public I420Buffer {
|
||||
public:
|
||||
explicit I420ABuffer(webrtc::scoped_refptr<webrtc::I420ABufferInterface> buffer);
|
||||
|
||||
unsigned int stride_a() const;
|
||||
const uint8_t* data_a() const;
|
||||
|
||||
std::unique_ptr<I420ABuffer> scale(int scaled_width, int scaled_height) const;
|
||||
|
||||
private:
|
||||
webrtc::I420ABufferInterface* buffer() const;
|
||||
};
|
||||
|
||||
class I422Buffer : public PlanarYuv8Buffer {
|
||||
public:
|
||||
explicit I422Buffer(webrtc::scoped_refptr<webrtc::I422BufferInterface> buffer);
|
||||
|
||||
std::unique_ptr<I422Buffer> scale(int scaled_width, int scaled_height) const;
|
||||
|
||||
private:
|
||||
webrtc::I422BufferInterface* buffer() const;
|
||||
};
|
||||
|
||||
class I444Buffer : public PlanarYuv8Buffer {
|
||||
public:
|
||||
explicit I444Buffer(webrtc::scoped_refptr<webrtc::I444BufferInterface> buffer);
|
||||
|
||||
std::unique_ptr<I444Buffer> scale(int scaled_width, int scaled_height) const;
|
||||
|
||||
private:
|
||||
webrtc::I444BufferInterface* buffer() const;
|
||||
};
|
||||
|
||||
class I010Buffer : public PlanarYuv16BBuffer {
|
||||
public:
|
||||
explicit I010Buffer(webrtc::scoped_refptr<webrtc::I010BufferInterface> buffer);
|
||||
|
||||
std::unique_ptr<I010Buffer> scale(int scaled_width, int scaled_height) const;
|
||||
|
||||
private:
|
||||
webrtc::I010BufferInterface* buffer() const;
|
||||
};
|
||||
|
||||
class NV12Buffer : public BiplanarYuv8Buffer {
|
||||
public:
|
||||
explicit NV12Buffer(webrtc::scoped_refptr<webrtc::NV12BufferInterface> buffer);
|
||||
|
||||
std::unique_ptr<NV12Buffer> scale(int scaled_width, int scaled_height) const;
|
||||
|
||||
private:
|
||||
webrtc::NV12BufferInterface* buffer() const;
|
||||
};
|
||||
|
||||
std::unique_ptr<I420Buffer> copy_i420_buffer(
|
||||
const std::unique_ptr<I420Buffer>& i420);
|
||||
std::unique_ptr<I420Buffer> new_i420_buffer(int width, int height, int stride_y, int stride_u, int stride_v);
|
||||
std::unique_ptr<I422Buffer> new_i422_buffer(int width, int height, int stride_y, int stride_u, int stride_v);
|
||||
std::unique_ptr<I444Buffer> new_i444_buffer(int width, int height, int stride_y, int stride_u, int stride_v);
|
||||
std::unique_ptr<I010Buffer> new_i010_buffer(int width, int height, int stride_y, int stride_u, int stride_v);
|
||||
std::unique_ptr<NV12Buffer> new_nv12_buffer(int width, int height, int stride_y, int stride_uv);
|
||||
|
||||
std::unique_ptr<VideoFrameBuffer> new_fluxer_d3d11_texture_buffer(
|
||||
uint64_t handle, uint32_t width, uint32_t height, uint32_t dxgi_format);
|
||||
std::unique_ptr<VideoFrameBuffer> new_fluxer_dmabuf_texture_buffer(
|
||||
int fd0, int fd1, int fd2, int fd3, uint32_t plane_count,
|
||||
uint32_t width, uint32_t height, uint32_t drm_format, uint64_t modifier,
|
||||
uint32_t stride0, uint32_t stride1, uint32_t stride2, uint32_t stride3,
|
||||
uint32_t offset0, uint32_t offset1, uint32_t offset2, uint32_t offset3,
|
||||
uint64_t device_uuid_hi, uint64_t device_uuid_lo);
|
||||
bool is_fluxer_gpu_buffer(const std::unique_ptr<VideoFrameBuffer>& buffer);
|
||||
uint64_t fluxer_d3d11_texture_handle(const std::unique_ptr<VideoFrameBuffer>& buffer);
|
||||
uint32_t fluxer_gpu_buffer_width(const std::unique_ptr<VideoFrameBuffer>& buffer);
|
||||
uint32_t fluxer_gpu_buffer_height(const std::unique_ptr<VideoFrameBuffer>& buffer);
|
||||
uint32_t fluxer_gpu_buffer_format(const std::unique_ptr<VideoFrameBuffer>& buffer);
|
||||
|
||||
std::unique_ptr<VideoFrameBuffer> new_native_buffer_from_platform_image_buffer(PlatformImageBuffer *buffer);
|
||||
PlatformImageBuffer* native_buffer_to_platform_image_buffer(const std::unique_ptr<VideoFrameBuffer> &);
|
||||
|
||||
static const VideoFrameBuffer* yuv_to_vfb(const PlanarYuvBuffer* yuv) {
|
||||
return yuv;
|
||||
}
|
||||
|
||||
static const VideoFrameBuffer* biyuv_to_vfb(const BiplanarYuvBuffer* biyuv) {
|
||||
return biyuv;
|
||||
}
|
||||
|
||||
static const PlanarYuvBuffer* yuv8_to_yuv(const PlanarYuv8Buffer* yuv8) {
|
||||
return yuv8;
|
||||
}
|
||||
|
||||
static const PlanarYuvBuffer* yuv16b_to_yuv(const PlanarYuv16BBuffer* yuv16) {
|
||||
return yuv16;
|
||||
}
|
||||
|
||||
static const BiplanarYuvBuffer* biyuv8_to_biyuv(
|
||||
const BiplanarYuv8Buffer* biyuv8) {
|
||||
return biyuv8;
|
||||
}
|
||||
|
||||
static const PlanarYuv8Buffer* i420_to_yuv8(const I420Buffer* i420) {
|
||||
return i420;
|
||||
}
|
||||
|
||||
static const PlanarYuv8Buffer* i420a_to_yuv8(const I420ABuffer* i420a) {
|
||||
return i420a;
|
||||
}
|
||||
|
||||
static const PlanarYuv8Buffer* i422_to_yuv8(const I422Buffer* i422) {
|
||||
return i422;
|
||||
}
|
||||
|
||||
static const PlanarYuv8Buffer* i444_to_yuv8(const I444Buffer* i444) {
|
||||
return i444;
|
||||
}
|
||||
|
||||
static const PlanarYuv16BBuffer* i010_to_yuv16b(const I010Buffer* i010) {
|
||||
return i010;
|
||||
}
|
||||
|
||||
static const BiplanarYuv8Buffer* nv12_to_biyuv8(const NV12Buffer* nv12) {
|
||||
return nv12;
|
||||
}
|
||||
|
||||
static std::unique_ptr<VideoFrameBuffer> _unique_video_frame_buffer() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace livekit_ffi
|
||||
+151
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Copyright 2025 LiveKit, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "api/media_stream_interface.h"
|
||||
#include "api/video/video_frame.h"
|
||||
#include "livekit/helper.h"
|
||||
#include "livekit/media_stream_track.h"
|
||||
#include "livekit/video_frame.h"
|
||||
#include "livekit/webrtc.h"
|
||||
#include "media/base/adapted_video_track_source.h"
|
||||
#include "rtc_base/synchronization/mutex.h"
|
||||
#include "rtc_base/timestamp_aligner.h"
|
||||
#include "rust/cxx.h"
|
||||
|
||||
namespace livekit_ffi {
|
||||
class VideoTrack;
|
||||
class NativeVideoSink;
|
||||
class VideoTrackSource;
|
||||
class PacketTrailerHandler; // forward declaration to avoid circular include
|
||||
} // namespace livekit_ffi
|
||||
#include "webrtc-sys/src/video_track.rs.h"
|
||||
|
||||
namespace livekit_ffi {
|
||||
|
||||
class VideoTrack : public MediaStreamTrack {
|
||||
private:
|
||||
friend RtcRuntime;
|
||||
VideoTrack(std::shared_ptr<RtcRuntime> rtc_runtime,
|
||||
webrtc::scoped_refptr<webrtc::VideoTrackInterface> track);
|
||||
|
||||
public:
|
||||
~VideoTrack();
|
||||
|
||||
void add_sink(const std::shared_ptr<NativeVideoSink>& sink) const;
|
||||
void remove_sink(const std::shared_ptr<NativeVideoSink>& sink) const;
|
||||
|
||||
void set_should_receive(bool should_receive) const;
|
||||
bool should_receive() const;
|
||||
ContentHint content_hint() const;
|
||||
void set_content_hint(ContentHint hint) const;
|
||||
|
||||
private:
|
||||
webrtc::VideoTrackInterface* track() const {
|
||||
return static_cast<webrtc::VideoTrackInterface*>(track_.get());
|
||||
}
|
||||
|
||||
mutable webrtc::Mutex mutex_;
|
||||
|
||||
// Same for AudioTrack:
|
||||
// Keep a strong reference to the added sinks, so we don't need to
|
||||
// manage the lifetime safety on the Rust side
|
||||
mutable std::vector<std::shared_ptr<NativeVideoSink>> sinks_;
|
||||
};
|
||||
|
||||
class NativeVideoSink : public webrtc::VideoSinkInterface<webrtc::VideoFrame> {
|
||||
public:
|
||||
explicit NativeVideoSink(rust::Box<VideoSinkWrapper> observer);
|
||||
|
||||
void OnFrame(const webrtc::VideoFrame& frame) override;
|
||||
void OnDiscardedFrame() override;
|
||||
void OnConstraintsChanged(
|
||||
const webrtc::VideoTrackSourceConstraints& constraints) override;
|
||||
|
||||
private:
|
||||
rust::Box<VideoSinkWrapper> observer_;
|
||||
};
|
||||
|
||||
std::shared_ptr<NativeVideoSink> new_native_video_sink(
|
||||
rust::Box<VideoSinkWrapper> observer);
|
||||
|
||||
class VideoTrackSource {
|
||||
class InternalSource : public webrtc::AdaptedVideoTrackSource {
|
||||
public:
|
||||
InternalSource(const VideoResolution& resolution,
|
||||
bool is_screencast); // (0, 0) means no resolution/optional, the
|
||||
// source will guess the resolution at the
|
||||
// first captured frame
|
||||
~InternalSource() override;
|
||||
|
||||
bool is_screencast() const override;
|
||||
std::optional<bool> needs_denoising() const override;
|
||||
SourceState state() const override;
|
||||
bool remote() const override;
|
||||
VideoResolution video_resolution() const;
|
||||
bool on_captured_frame(const webrtc::VideoFrame& frame,
|
||||
const FrameMetadata& frame_metadata);
|
||||
|
||||
void set_packet_trailer_handler(
|
||||
std::shared_ptr<PacketTrailerHandler> handler);
|
||||
|
||||
private:
|
||||
mutable webrtc::Mutex mutex_;
|
||||
webrtc::TimestampAligner timestamp_aligner_;
|
||||
VideoResolution resolution_;
|
||||
std::shared_ptr<PacketTrailerHandler> packet_trailer_handler_;
|
||||
bool is_screencast_;
|
||||
};
|
||||
|
||||
public:
|
||||
VideoTrackSource(const VideoResolution& resolution, bool is_screencast);
|
||||
|
||||
VideoResolution video_resolution() const;
|
||||
|
||||
bool on_captured_frame(const std::unique_ptr<VideoFrame>& frame,
|
||||
const FrameMetadata& frame_metadata)
|
||||
const; // frames pushed from Rust (+interior mutability)
|
||||
|
||||
void set_packet_trailer_handler(
|
||||
std::shared_ptr<PacketTrailerHandler> handler) const;
|
||||
|
||||
webrtc::scoped_refptr<InternalSource> get() const;
|
||||
|
||||
private:
|
||||
webrtc::scoped_refptr<InternalSource> source_;
|
||||
};
|
||||
|
||||
std::shared_ptr<VideoTrackSource> new_video_track_source(
|
||||
const VideoResolution& resolution, bool is_screencast);
|
||||
|
||||
static std::shared_ptr<MediaStreamTrack> video_to_media(
|
||||
std::shared_ptr<VideoTrack> track) {
|
||||
return track;
|
||||
}
|
||||
|
||||
static std::shared_ptr<VideoTrack> media_to_video(
|
||||
std::shared_ptr<MediaStreamTrack> track) {
|
||||
return std::static_pointer_cast<VideoTrack>(track);
|
||||
}
|
||||
|
||||
static std::shared_ptr<VideoTrack> _shared_video_track() {
|
||||
return nullptr; // Ignore
|
||||
}
|
||||
|
||||
} // namespace livekit_ffi
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Copyright 2025 LiveKit, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "api/media_stream_interface.h"
|
||||
#include "api/rtp_receiver_interface.h"
|
||||
#include "api/rtp_sender_interface.h"
|
||||
#include "livekit/helper.h"
|
||||
#include "rtc_base/logging.h"
|
||||
#include "rtc_base/physical_socket_server.h"
|
||||
#include "rtc_base/ssl_adapter.h"
|
||||
#include "rtc_base/thread.h"
|
||||
#include "rust/cxx.h"
|
||||
|
||||
#ifdef WEBRTC_WIN
|
||||
#include "rtc_base/win32_socket_init.h"
|
||||
#endif
|
||||
|
||||
namespace livekit_ffi {
|
||||
class RtcRuntime;
|
||||
class LogSink;
|
||||
} // namespace livekit_ffi
|
||||
#include "webrtc-sys/src/webrtc.rs.h"
|
||||
|
||||
namespace livekit_ffi {
|
||||
|
||||
class MediaStreamTrack;
|
||||
class RtpReceiver;
|
||||
class RtpSender;
|
||||
|
||||
// Using a shared_ptr in RtcRuntime allows us to keep a strong reference to it
|
||||
// on resources that depend on it. (e.g: AudioTrack, VideoTrack).
|
||||
class RtcRuntime : public std::enable_shared_from_this<RtcRuntime> {
|
||||
public:
|
||||
[[nodiscard]] static std::shared_ptr<RtcRuntime> create() {
|
||||
return std::shared_ptr<RtcRuntime>(new RtcRuntime());
|
||||
}
|
||||
|
||||
RtcRuntime(const RtcRuntime&) = delete;
|
||||
RtcRuntime& operator=(const RtcRuntime&) = delete;
|
||||
~RtcRuntime();
|
||||
|
||||
webrtc::Thread* network_thread() const;
|
||||
webrtc::Thread* worker_thread() const;
|
||||
webrtc::Thread* signaling_thread() const;
|
||||
|
||||
std::shared_ptr<MediaStreamTrack> get_or_create_media_stream_track(
|
||||
webrtc::scoped_refptr<webrtc::MediaStreamTrackInterface> track);
|
||||
|
||||
std::shared_ptr<AudioTrack> get_or_create_audio_track(
|
||||
webrtc::scoped_refptr<webrtc::AudioTrackInterface> track);
|
||||
|
||||
std::shared_ptr<VideoTrack> get_or_create_video_track(
|
||||
webrtc::scoped_refptr<webrtc::VideoTrackInterface> track);
|
||||
|
||||
private:
|
||||
RtcRuntime();
|
||||
|
||||
std::unique_ptr<webrtc::Thread> network_thread_;
|
||||
std::unique_ptr<webrtc::Thread> worker_thread_;
|
||||
std::unique_ptr<webrtc::Thread> signaling_thread_;
|
||||
|
||||
// Lists used to make sure we don't create multiple wrappers for one
|
||||
// underlying webrtc object. (e.g: webrtc::VideoTrackInterface should only
|
||||
// have one livekit_ffi::VideoTrack associated with it).
|
||||
// The only reason we to do that is to allow to add states inside our
|
||||
// wrappers (e.g: the sinks_ member inside AudioTrack)
|
||||
// DataChannel and the PeerConnectionFactory don't need to do this (There's no
|
||||
// way to retrieve them after creation)
|
||||
webrtc::Mutex mutex_;
|
||||
std::vector<std::weak_ptr<MediaStreamTrack>> media_stream_tracks_;
|
||||
// We don't have additonal state in RtpReceiver and RtpSender atm..
|
||||
// std::vector<std::weak_ptr<RtpReceiver>> rtp_receivers_;
|
||||
// std::vector<std::weak_ptr<RtpSender>> rtp_senders_;
|
||||
|
||||
#ifdef WEBRTC_WIN
|
||||
// webrtc::WinsockInitializer winsock_;
|
||||
// webrtc::PhysicalSocketServer ss_;
|
||||
// webrtc::AutoSocketServerThread main_thread_{&ss_};
|
||||
#endif
|
||||
};
|
||||
|
||||
class LogSink : public webrtc::LogSink {
|
||||
public:
|
||||
LogSink(rust::Fn<void(rust::String message, LoggingSeverity severity)> fnc);
|
||||
~LogSink();
|
||||
|
||||
void OnLogMessage(const std::string& message,
|
||||
webrtc::LoggingSeverity severity) override;
|
||||
|
||||
void OnLogMessage(const std::string& message) override {}
|
||||
|
||||
private:
|
||||
rust::Fn<void(rust::String message, LoggingSeverity severity)> fnc_;
|
||||
};
|
||||
|
||||
std::unique_ptr<LogSink> new_log_sink(
|
||||
rust::Fn<void(rust::String, LoggingSeverity)> fnc);
|
||||
|
||||
rust::String create_random_uuid();
|
||||
|
||||
} // namespace livekit_ffi
|
||||
+381
@@ -0,0 +1,381 @@
|
||||
/*
|
||||
* Copyright 2025 LiveKit, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
#include "api/video/yuv_helper.h"
|
||||
#include "webrtc-sys/src/yuv_helper.rs.h"
|
||||
|
||||
namespace livekit_ffi {
|
||||
|
||||
#define THROW_ON_ERROR(ret) \
|
||||
if (ret != 0) { \
|
||||
throw std::runtime_error("libyuv error: " + std::to_string(ret)); \
|
||||
}
|
||||
|
||||
static void i420_to_argb(const uint8_t* src_y,
|
||||
int src_stride_y,
|
||||
const uint8_t* src_u,
|
||||
int src_stride_u,
|
||||
const uint8_t* src_v,
|
||||
int src_stride_v,
|
||||
uint8_t* dst_argb,
|
||||
int dst_stride_argb,
|
||||
int width,
|
||||
int height) {
|
||||
THROW_ON_ERROR(webrtc::I420ToARGB(src_y, src_stride_y, src_u, src_stride_u,
|
||||
src_v, src_stride_v, dst_argb,
|
||||
dst_stride_argb, width, height));
|
||||
}
|
||||
|
||||
static void i420_to_bgra(const uint8_t* src_y,
|
||||
int src_stride_y,
|
||||
const uint8_t* src_u,
|
||||
int src_stride_u,
|
||||
const uint8_t* src_v,
|
||||
int src_stride_v,
|
||||
uint8_t* dst_bgra,
|
||||
int dst_stride_bgra,
|
||||
int width,
|
||||
int height) {
|
||||
THROW_ON_ERROR(webrtc::I420ToBGRA(src_y, src_stride_y, src_u, src_stride_u,
|
||||
src_v, src_stride_v, dst_bgra,
|
||||
dst_stride_bgra, width, height));
|
||||
}
|
||||
|
||||
static void i420_to_abgr(const uint8_t* src_y,
|
||||
int src_stride_y,
|
||||
const uint8_t* src_u,
|
||||
int src_stride_u,
|
||||
const uint8_t* src_v,
|
||||
int src_stride_v,
|
||||
uint8_t* dst_abgr,
|
||||
int dst_stride_abgr,
|
||||
int width,
|
||||
int height) {
|
||||
THROW_ON_ERROR(webrtc::I420ToABGR(src_y, src_stride_y, src_u, src_stride_u,
|
||||
src_v, src_stride_v, dst_abgr,
|
||||
dst_stride_abgr, width, height));
|
||||
}
|
||||
|
||||
static void i420_to_rgba(const uint8_t* src_y,
|
||||
int src_stride_y,
|
||||
const uint8_t* src_u,
|
||||
int src_stride_u,
|
||||
const uint8_t* src_v,
|
||||
int src_stride_v,
|
||||
uint8_t* dst_rgba,
|
||||
int dst_stride_rgba,
|
||||
int width,
|
||||
int height) {
|
||||
THROW_ON_ERROR(webrtc::I420ToRGBA(src_y, src_stride_y, src_u, src_stride_u,
|
||||
src_v, src_stride_v, dst_rgba,
|
||||
dst_stride_rgba, width, height));
|
||||
}
|
||||
|
||||
static void argb_to_i420(const uint8_t* src_argb,
|
||||
int src_stride_argb,
|
||||
uint8_t* dst_y,
|
||||
int dst_stride_y,
|
||||
uint8_t* dst_u,
|
||||
int dst_stride_u,
|
||||
uint8_t* dst_v,
|
||||
int dst_stride_v,
|
||||
int width,
|
||||
int height) {
|
||||
THROW_ON_ERROR(webrtc::ARGBToI420(src_argb, src_stride_argb, dst_y,
|
||||
dst_stride_y, dst_u, dst_stride_u, dst_v,
|
||||
dst_stride_v, width, height));
|
||||
}
|
||||
|
||||
static void abgr_to_i420(const uint8_t* src_abgr,
|
||||
int src_stride_abgr,
|
||||
uint8_t* dst_y,
|
||||
int dst_stride_y,
|
||||
uint8_t* dst_u,
|
||||
int dst_stride_u,
|
||||
uint8_t* dst_v,
|
||||
int dst_stride_v,
|
||||
int width,
|
||||
int height) {
|
||||
THROW_ON_ERROR(webrtc::ABGRToI420(src_abgr, src_stride_abgr, dst_y,
|
||||
dst_stride_y, dst_u, dst_stride_u, dst_v,
|
||||
dst_stride_v, width, height));
|
||||
}
|
||||
|
||||
static void argb_to_rgb24(const uint8_t* src_argb,
|
||||
int src_stride_argb,
|
||||
uint8_t* dst_rgb24,
|
||||
int dst_stride_rgb24,
|
||||
int width,
|
||||
int height) {
|
||||
THROW_ON_ERROR(webrtc::ARGBToRGB24(src_argb, src_stride_argb, dst_rgb24,
|
||||
dst_stride_rgb24, width, height));
|
||||
}
|
||||
|
||||
static void i420_to_nv12(const uint8_t* src_y,
|
||||
int src_stride_y,
|
||||
const uint8_t* src_u,
|
||||
int src_stride_u,
|
||||
const uint8_t* src_v,
|
||||
int src_stride_v,
|
||||
uint8_t* dst_y,
|
||||
int dst_stride_y,
|
||||
uint8_t* dst_uv,
|
||||
int dst_stride_uv,
|
||||
int width,
|
||||
int height) {
|
||||
THROW_ON_ERROR(webrtc::I420ToNV12(src_y, src_stride_y, src_u, src_stride_u,
|
||||
src_v, src_stride_v, dst_y, dst_stride_y,
|
||||
dst_uv, dst_stride_uv, width, height));
|
||||
}
|
||||
|
||||
static void nv12_to_i420(const uint8_t* src_y,
|
||||
int src_stride_y,
|
||||
const uint8_t* src_uv,
|
||||
int src_stride_uv,
|
||||
uint8_t* dst_y,
|
||||
int dst_stride_y,
|
||||
uint8_t* dst_u,
|
||||
int dst_stride_u,
|
||||
uint8_t* dst_v,
|
||||
int dst_stride_v,
|
||||
int width,
|
||||
int height) {
|
||||
THROW_ON_ERROR(webrtc::NV12ToI420(src_y, src_stride_y, src_uv, src_stride_uv,
|
||||
dst_y, dst_stride_y, dst_u, dst_stride_u,
|
||||
dst_v, dst_stride_v, width, height));
|
||||
}
|
||||
|
||||
static void i420_to_nv12(const uint8_t* src_y,
|
||||
int src_stride_y,
|
||||
const uint8_t* src_u,
|
||||
int src_stride_u,
|
||||
uint8_t* src_v,
|
||||
int src_stride_v,
|
||||
uint8_t* dst_y,
|
||||
int dst_stride_y,
|
||||
uint8_t* dst_uv,
|
||||
int dst_stride_uv,
|
||||
int width,
|
||||
int height) {
|
||||
THROW_ON_ERROR(webrtc::NV12ToI420(src_y, src_stride_y, src_u, src_stride_u,
|
||||
src_v, src_stride_v, dst_y, dst_stride_y,
|
||||
dst_uv, dst_stride_uv, width, height));
|
||||
}
|
||||
|
||||
static void i444_to_i420(const uint8_t* src_y,
|
||||
int src_stride_y,
|
||||
const uint8_t* src_u,
|
||||
int src_stride_u,
|
||||
const uint8_t* src_v,
|
||||
int src_stride_v,
|
||||
uint8_t* dst_y,
|
||||
int dst_stride_y,
|
||||
uint8_t* dst_u,
|
||||
int dst_stride_u,
|
||||
uint8_t* dst_v,
|
||||
int dst_stride_v,
|
||||
int width,
|
||||
int height) {
|
||||
THROW_ON_ERROR(webrtc::I444ToI420(
|
||||
src_y, src_stride_y, src_u, src_stride_u, src_v, src_stride_v, dst_y,
|
||||
dst_stride_y, dst_u, dst_stride_u, dst_v, dst_stride_v, width, height));
|
||||
}
|
||||
|
||||
static void i422_to_i420(const uint8_t* src_y,
|
||||
int src_stride_y,
|
||||
const uint8_t* src_u,
|
||||
int src_stride_u,
|
||||
const uint8_t* src_v,
|
||||
int src_stride_v,
|
||||
uint8_t* dst_y,
|
||||
int dst_stride_y,
|
||||
uint8_t* dst_u,
|
||||
int dst_stride_u,
|
||||
uint8_t* dst_v,
|
||||
int dst_stride_v,
|
||||
int width,
|
||||
int height) {
|
||||
THROW_ON_ERROR(webrtc::I422ToI420(
|
||||
src_y, src_stride_y, src_u, src_stride_u, src_v, src_stride_v, dst_y,
|
||||
dst_stride_y, dst_u, dst_stride_u, dst_v, dst_stride_v, width, height));
|
||||
}
|
||||
|
||||
static void i010_to_i420(const uint16_t* src_y,
|
||||
int src_stride_y,
|
||||
const uint16_t* src_u,
|
||||
int src_stride_u,
|
||||
const uint16_t* src_v,
|
||||
int src_stride_v,
|
||||
uint8_t* dst_y,
|
||||
int dst_stride_y,
|
||||
uint8_t* dst_u,
|
||||
int dst_stride_u,
|
||||
uint8_t* dst_v,
|
||||
int dst_stride_v,
|
||||
int width,
|
||||
int height) {
|
||||
THROW_ON_ERROR(webrtc::I010ToI420(
|
||||
src_y, src_stride_y, src_u, src_stride_u, src_v, src_stride_v, dst_y,
|
||||
dst_stride_y, dst_u, dst_stride_u, dst_v, dst_stride_v, width, height));
|
||||
}
|
||||
|
||||
static void nv12_to_argb(const uint8_t* src_y,
|
||||
int src_stride_y,
|
||||
const uint8_t* src_uv,
|
||||
int src_stride_uv,
|
||||
uint8_t* dst_argb,
|
||||
int dst_stride_argb,
|
||||
int width,
|
||||
int height) {
|
||||
THROW_ON_ERROR(webrtc::NV12ToARGB(src_y, src_stride_y, src_uv, src_stride_uv,
|
||||
dst_argb, dst_stride_argb, width, height));
|
||||
}
|
||||
|
||||
static void nv12_to_abgr(const uint8_t* src_y,
|
||||
int src_stride_y,
|
||||
const uint8_t* src_uv,
|
||||
int src_stride_uv,
|
||||
uint8_t* dst_abgr,
|
||||
int dst_stride_abgr,
|
||||
int width,
|
||||
int height) {
|
||||
THROW_ON_ERROR(webrtc::NV12ToABGR(src_y, src_stride_y, src_uv, src_stride_uv,
|
||||
dst_abgr, dst_stride_abgr, width, height));
|
||||
}
|
||||
|
||||
static void i444_to_argb(const uint8_t* src_y,
|
||||
int src_stride_y,
|
||||
const uint8_t* src_u,
|
||||
int src_stride_u,
|
||||
const uint8_t* src_v,
|
||||
int src_stride_v,
|
||||
uint8_t* dst_abgr,
|
||||
int dst_stride_abgr,
|
||||
int width,
|
||||
int height) {
|
||||
THROW_ON_ERROR(webrtc::I444ToARGB(src_y, src_stride_y, src_u, src_stride_u,
|
||||
src_v, src_stride_v, dst_abgr,
|
||||
dst_stride_abgr, width, height));
|
||||
}
|
||||
|
||||
static void i444_to_abgr(const uint8_t* src_y,
|
||||
int src_stride_y,
|
||||
const uint8_t* src_u,
|
||||
int src_stride_u,
|
||||
const uint8_t* src_v,
|
||||
int src_stride_v,
|
||||
uint8_t* dst_abgr,
|
||||
int dst_stride_abgr,
|
||||
int width,
|
||||
int height) {
|
||||
THROW_ON_ERROR(webrtc::I444ToABGR(src_y, src_stride_y, src_u, src_stride_u,
|
||||
src_v, src_stride_v, dst_abgr,
|
||||
dst_stride_abgr, width, height));
|
||||
}
|
||||
|
||||
static void i422_to_argb(const uint8_t* src_y,
|
||||
int src_stride_y,
|
||||
const uint8_t* src_u,
|
||||
int src_stride_u,
|
||||
const uint8_t* src_v,
|
||||
int src_stride_v,
|
||||
uint8_t* dst_argb,
|
||||
int dst_stride_argb,
|
||||
int width,
|
||||
int height) {
|
||||
THROW_ON_ERROR(webrtc::I422ToARGB(src_y, src_stride_y, src_u, src_stride_u,
|
||||
src_v, src_stride_v, dst_argb,
|
||||
dst_stride_argb, width, height));
|
||||
}
|
||||
|
||||
static void i422_to_abgr(const uint8_t* src_y,
|
||||
int src_stride_y,
|
||||
const uint8_t* src_u,
|
||||
int src_stride_u,
|
||||
const uint8_t* src_v,
|
||||
int src_stride_v,
|
||||
uint8_t* dst_abgr,
|
||||
int dst_stride_abgr,
|
||||
int width,
|
||||
int height) {
|
||||
THROW_ON_ERROR(webrtc::I422ToABGR(src_y, src_stride_y, src_u, src_stride_u,
|
||||
src_v, src_stride_v, dst_abgr,
|
||||
dst_stride_abgr, width, height));
|
||||
}
|
||||
|
||||
static void i010_to_argb(const uint16_t* src_y,
|
||||
int src_stride_y,
|
||||
const uint16_t* src_u,
|
||||
int src_stride_u,
|
||||
const uint16_t* src_v,
|
||||
int src_stride_v,
|
||||
uint8_t* dst_argb,
|
||||
int dst_stride_argb,
|
||||
int width,
|
||||
int height) {
|
||||
THROW_ON_ERROR(webrtc::I010ToARGB(src_y, src_stride_y, src_u, src_stride_u,
|
||||
src_v, src_stride_v, dst_argb,
|
||||
dst_stride_argb, width, height));
|
||||
}
|
||||
|
||||
static void i010_to_abgr(const uint16_t* src_y,
|
||||
int src_stride_y,
|
||||
const uint16_t* src_u,
|
||||
int src_stride_u,
|
||||
const uint16_t* src_v,
|
||||
int src_stride_v,
|
||||
uint8_t* dst_abgr,
|
||||
int dst_stride_abgr,
|
||||
int width,
|
||||
int height) {
|
||||
THROW_ON_ERROR(webrtc::I010ToABGR(src_y, src_stride_y, src_u, src_stride_u,
|
||||
src_v, src_stride_v, dst_abgr,
|
||||
dst_stride_abgr, width, height));
|
||||
}
|
||||
|
||||
static void abgr_to_nv12(const uint8_t* src_abgr,
|
||||
int src_stride_abgr,
|
||||
uint8_t* dst_y,
|
||||
int dst_stride_y,
|
||||
uint8_t* dst_uv,
|
||||
int dst_stride_uv,
|
||||
int width,
|
||||
int height) {
|
||||
THROW_ON_ERROR(webrtc::ABGRToNV12(src_abgr, src_stride_abgr, dst_y,
|
||||
dst_stride_y, dst_uv, dst_stride_uv, width,
|
||||
height));
|
||||
}
|
||||
|
||||
static void argb_to_nv12(const uint8_t* src_argb,
|
||||
int src_stride_argb,
|
||||
uint8_t* dst_y,
|
||||
int dst_stride_y,
|
||||
uint8_t* dst_uv,
|
||||
int dst_stride_uv,
|
||||
int width,
|
||||
int height) {
|
||||
THROW_ON_ERROR(webrtc::ARGBToNV12(src_argb, src_stride_argb, dst_y,
|
||||
dst_stride_y, dst_uv, dst_stride_uv, width,
|
||||
height));
|
||||
}
|
||||
|
||||
} // namespace livekit_ffi
|
||||
@@ -0,0 +1,9 @@
|
||||
solutions = [
|
||||
{
|
||||
"name": 'src',
|
||||
"url": 'https://github.com/webrtc-sdk/webrtc.git@m144_release',
|
||||
"custom_deps": {},
|
||||
"deps_file": "DEPS",
|
||||
"managed": False,
|
||||
},
|
||||
]
|
||||
@@ -0,0 +1,12 @@
|
||||
.cipd
|
||||
src
|
||||
.gclient_*
|
||||
depot_tools
|
||||
ninja
|
||||
|
||||
# builds
|
||||
win-*
|
||||
mac-*
|
||||
linux-*
|
||||
android-*
|
||||
ios-*
|
||||
@@ -0,0 +1,28 @@
|
||||
This directory can contain a checkout of WebRTC. The build scripts
|
||||
here will install dependencies, checkout the version that LiveKit
|
||||
currently uses, apply some patches to it, and build it. For example to
|
||||
do a Linux debug build on x64:
|
||||
|
||||
```sh
|
||||
$ ./build-linux.sh --arch x64 --profile release
|
||||
```
|
||||
|
||||
After running this, `linux-x64-debug/lib/libwebrtc.a` should
|
||||
exist. This can be rerun to rebuild it, but will complain about
|
||||
patches not applying as they have already been applied.
|
||||
|
||||
If something goes wrong it may be helpful to consult the [WebRTC native
|
||||
development documentation](https://webrtc.googlesource.com/src/+/main/docs/native-code/development/).
|
||||
|
||||
# Building LiveKit Rust SDK with custom WebRTC checkout
|
||||
|
||||
Add the following environment variable to `/.config/config.toml`, to
|
||||
specify use of a custom WebRTC build:
|
||||
|
||||
```toml
|
||||
[env]
|
||||
LK_CUSTOM_WEBRTC = { value = "webrtc-sys/libwebrtc/linux-x64-release", relative = true }
|
||||
```
|
||||
|
||||
Note that `linux-x64-debug` should be replaced with the artifact
|
||||
directory appropriate for your configuration.
|
||||
Vendored
+2767
File diff suppressed because it is too large
Load Diff
Vendored
Executable
+159
@@ -0,0 +1,159 @@
|
||||
#!/bin/bash
|
||||
# Exit immediately if any command fails. This ensures CI properly reports build
|
||||
# failures instead of continuing to create empty/broken artifacts.
|
||||
set -e
|
||||
|
||||
# Copyright 2023 LiveKit, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
|
||||
arch=""
|
||||
profile="release"
|
||||
|
||||
while [ "$#" -gt 0 ]; do
|
||||
case "$1" in
|
||||
--arch)
|
||||
arch="$2"
|
||||
if [ "$arch" != "arm" ] && [ "$arch" != "x64" ] && [ "$arch" != "arm64" ]; then
|
||||
echo "Error: Invalid value for --arch. Must be 'arm', 'x64' or 'arm64'."
|
||||
exit 1
|
||||
fi
|
||||
shift 2
|
||||
;;
|
||||
--profile)
|
||||
profile="$2"
|
||||
if [ "$profile" != "debug" ] && [ "$profile" != "release" ]; then
|
||||
echo "Error: Invalid value for --profile. Must be 'debug' or 'release'."
|
||||
exit 1
|
||||
fi
|
||||
shift 2
|
||||
;;
|
||||
*)
|
||||
echo "Error: Unknown argument '$1'"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ -z "$arch" ]; then
|
||||
echo "Error: --arch must be set."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Building LiveKit WebRTC - Android"
|
||||
echo "Arch: $arch"
|
||||
echo "Profile: $profile"
|
||||
|
||||
if [ ! -e "$(pwd)/depot_tools" ]
|
||||
then
|
||||
git clone --depth 1 https://chromium.googlesource.com/chromium/tools/depot_tools.git
|
||||
fi
|
||||
|
||||
export COMMAND_DIR=$(cd $(dirname $0); pwd)
|
||||
export PATH="$(pwd)/depot_tools:$PATH"
|
||||
export OUTPUT_DIR="$(pwd)/src/out-$arch-$profile"
|
||||
export ARTIFACTS_DIR="$(pwd)/android-$arch-$profile"
|
||||
|
||||
if [ ! -e "$(pwd)/src" ]
|
||||
then
|
||||
gclient sync -D --no-history
|
||||
fi
|
||||
|
||||
cd src
|
||||
git apply "$COMMAND_DIR/patches/add_licenses.patch" -v --ignore-space-change --ignore-whitespace --whitespace=nowarn
|
||||
git apply "$COMMAND_DIR/patches/fix_license_json_parsing.patch" -v --ignore-space-change --ignore-whitespace --whitespace=nowarn
|
||||
git apply "$COMMAND_DIR/patches/ssl_verify_callback_with_native_handle.patch" -v --ignore-space-change --ignore-whitespace --whitespace=nowarn
|
||||
git apply "$COMMAND_DIR/patches/add_deps.patch" -v --ignore-space-change --ignore-whitespace --whitespace=nowarn
|
||||
git apply "$COMMAND_DIR/patches/android_use_libunwind.patch" -v --ignore-space-change --ignore-whitespace --whitespace=nowarn
|
||||
git apply "$COMMAND_DIR/patches/external_audio_source.patch" -v --ignore-space-change --ignore-whitespace --whitespace=nowarn
|
||||
# livekit prefixed jni
|
||||
git apply "$COMMAND_DIR/patches/jni_prefix.patch" -v --ignore-space-change --ignore-whitespace --whitespace=nowarn
|
||||
|
||||
cd third_party/libyuv
|
||||
git apply "$COMMAND_DIR/patches/disable_sme_for_libyuv.patch" -v --ignore-space-change --ignore-whitespace --whitespace=nowarn
|
||||
|
||||
cd ../../..
|
||||
|
||||
mkdir -p "$ARTIFACTS_DIR/lib"
|
||||
|
||||
debug="false"
|
||||
if [ "$profile" = "debug" ]; then
|
||||
debug="true"
|
||||
fi
|
||||
|
||||
# Note: use_clang_modules=false is required to avoid C++ module compilation issues.
|
||||
# Without this flag, the build may fail partway through, resulting in missing
|
||||
# artifacts like libwebrtc.jar.
|
||||
args="is_debug=$debug \
|
||||
is_java_debug=$debug \
|
||||
target_os=\"android\" \
|
||||
target_cpu=\"$arch\" \
|
||||
rtc_enable_protobuf=false \
|
||||
treat_warnings_as_errors=false \
|
||||
rtc_include_tests=false \
|
||||
rtc_build_tools=false \
|
||||
rtc_build_examples=false \
|
||||
rtc_libvpx_build_vp9=false \
|
||||
is_component_build=false \
|
||||
enable_stripping=true \
|
||||
rtc_use_h264=false \
|
||||
rtc_use_h265=true \
|
||||
rtc_use_pipewire=false \
|
||||
symbol_level=0 \
|
||||
enable_iterator_debugging=false \
|
||||
android_package_prefix=\"livekit\" \
|
||||
use_custom_libcxx=false \
|
||||
use_clang_modules=false \
|
||||
use_rtti=true"
|
||||
|
||||
if [ "$debug" = "true" ]; then
|
||||
args="${args} is_asan=true is_lsan=true";
|
||||
fi
|
||||
|
||||
# generate ninja files
|
||||
gn gen "$OUTPUT_DIR" --root="src" --args="${args}"
|
||||
|
||||
# build shared library
|
||||
autoninja -C "$OUTPUT_DIR" :default \
|
||||
sdk/android:native_api \
|
||||
sdk/android:libwebrtc \
|
||||
sdk/android:libjingle_peerconnection_so
|
||||
|
||||
# make libwebrtc.a
|
||||
# don't include nasm
|
||||
ar -rc "$ARTIFACTS_DIR/lib/libwebrtc.a" `find "$OUTPUT_DIR/obj" -name '*.o' -not -path "*/third_party/nasm/*"`
|
||||
|
||||
# License generation is optional - may fail with some Python versions
|
||||
# Use vpython3 from depot_tools for consistent Python version
|
||||
vpython3 "./src/tools_webrtc/libs/generate_licenses.py" \
|
||||
--target :default "$OUTPUT_DIR" "$OUTPUT_DIR" || echo "Warning: License generation failed (non-critical)"
|
||||
|
||||
cp "$OUTPUT_DIR/obj/webrtc.ninja" "$ARTIFACTS_DIR"
|
||||
cp "$OUTPUT_DIR/libjingle_peerconnection_so.so" "$ARTIFACTS_DIR/lib"
|
||||
cp "$OUTPUT_DIR/args.gn" "$ARTIFACTS_DIR"
|
||||
|
||||
cp "$OUTPUT_DIR/LICENSE.md" "$ARTIFACTS_DIR"
|
||||
|
||||
mkdir -p "$COMMAND_DIR/prefixed-jni/libs"
|
||||
cp "$OUTPUT_DIR/lib.java/sdk/android/libwebrtc.jar" "$COMMAND_DIR/prefixed-jni/libs/classes.jar"
|
||||
cd "$COMMAND_DIR/prefixed-jni" && ./gradlew shadowJar
|
||||
cp "$COMMAND_DIR/prefixed-jni/build/libs/prefixed-jni-all.jar" "$ARTIFACTS_DIR/libwebrtc.jar"
|
||||
|
||||
cd ..
|
||||
|
||||
cp "src/sdk/android/AndroidManifest.xml" "$ARTIFACTS_DIR"
|
||||
|
||||
cd src
|
||||
find . -name "*.h" -print | cpio -pd "$ARTIFACTS_DIR/include"
|
||||
find . -name "*.inc" -print | cpio -pd "$ARTIFACTS_DIR/include"
|
||||
+168
@@ -0,0 +1,168 @@
|
||||
#!/bin/bash
|
||||
# Exit immediately if any command fails. This ensures CI properly reports build
|
||||
# failures instead of continuing to create empty/broken artifacts.
|
||||
set -e
|
||||
|
||||
# Copyright 2023 LiveKit, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
|
||||
arch=""
|
||||
profile="release"
|
||||
environment="device"
|
||||
|
||||
while [ "$#" -gt 0 ]; do
|
||||
case "$1" in
|
||||
--arch)
|
||||
arch="$2"
|
||||
if [ "$arch" != "arm64" ]; then
|
||||
echo "Error: Invalid value for --arch. Must 'arm64'."
|
||||
exit 1
|
||||
fi
|
||||
shift 2
|
||||
;;
|
||||
--environment)
|
||||
environment="$2"
|
||||
if [ "$environment" != "device" ] && [ "$environment" != "simulator" ]; then
|
||||
echo "Error: Invalid value for --environment. Must be 'device' or 'simulator'."
|
||||
exit 1
|
||||
fi
|
||||
shift 2
|
||||
;;
|
||||
--profile)
|
||||
profile="$2"
|
||||
if [ "$profile" != "debug" ] && [ "$profile" != "release" ]; then
|
||||
echo "Error: Invalid value for --profile. Must be 'debug' or 'release'."
|
||||
exit 1
|
||||
fi
|
||||
shift 2
|
||||
;;
|
||||
*)
|
||||
echo "Error: Unknown argument '$1'"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ -z "$arch" ]; then
|
||||
echo "Error: --arch must be set."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Building LiveKit WebRTC - iOS"
|
||||
echo "Arch: $arch"
|
||||
echo "Profile: $profile"
|
||||
echo "Environment: $environment"
|
||||
|
||||
if [ ! -e "$(pwd)/depot_tools" ]
|
||||
then
|
||||
git clone --depth 1 https://chromium.googlesource.com/chromium/tools/depot_tools.git
|
||||
fi
|
||||
|
||||
export COMMAND_DIR=$(cd $(dirname $0); pwd)
|
||||
export PATH="$(pwd)/depot_tools:$PATH"
|
||||
|
||||
export OUTPUT_DIR="$(pwd)/src/out-$arch-$profile"
|
||||
export ARTIFACTS_DIR="$(pwd)/ios-$environment-$arch-$profile"
|
||||
|
||||
if [ ! -e "$(pwd)/src" ]
|
||||
then
|
||||
gclient sync -D --no-history
|
||||
fi
|
||||
|
||||
cd src
|
||||
|
||||
# Apply patches only if not already applied (check with --reverse --check)
|
||||
apply_patch_if_needed() {
|
||||
local patch="$1"
|
||||
if git apply --reverse --check "$patch" 2>/dev/null; then
|
||||
echo "Patch already applied: $(basename "$patch")"
|
||||
else
|
||||
echo "Applying patch: $(basename "$patch")"
|
||||
git apply "$patch" -v --ignore-space-change --ignore-whitespace --whitespace=nowarn || true
|
||||
fi
|
||||
}
|
||||
|
||||
apply_patch_if_needed "$COMMAND_DIR/patches/add_licenses.patch"
|
||||
apply_patch_if_needed "$COMMAND_DIR/patches/fix_license_json_parsing.patch"
|
||||
apply_patch_if_needed "$COMMAND_DIR/patches/ssl_verify_callback_with_native_handle.patch"
|
||||
apply_patch_if_needed "$COMMAND_DIR/patches/add_deps.patch"
|
||||
apply_patch_if_needed "$COMMAND_DIR/patches/external_audio_source.patch"
|
||||
|
||||
cd ..
|
||||
|
||||
mkdir -p "$ARTIFACTS_DIR/lib"
|
||||
|
||||
debug="false"
|
||||
if [ "$profile" = "debug" ]; then
|
||||
debug="true"
|
||||
fi
|
||||
|
||||
# generate ninja files
|
||||
# Note: use_clang_modules=false is required to avoid libc++ header incompatibility
|
||||
# with Xcode 26.0. When enabled, C++ module compilation fails with errors like
|
||||
# "unknown type name 'size_t'" due to conflicts between WebRTC's bundled libc++
|
||||
# headers and the iOS SDK headers.
|
||||
gn gen "$OUTPUT_DIR" --root="src" \
|
||||
--args="is_debug=$debug \
|
||||
enable_dsyms=$debug \
|
||||
target_os=\"ios\" \
|
||||
target_cpu=\"$arch\" \
|
||||
target_environment=\"$environment\" \
|
||||
treat_warnings_as_errors=false \
|
||||
ios_enable_code_signing=false \
|
||||
rtc_enable_protobuf=false \
|
||||
rtc_include_tests=false \
|
||||
rtc_build_examples=false \
|
||||
rtc_build_tools=false \
|
||||
rtc_libvpx_build_vp9=false \
|
||||
is_component_build=false \
|
||||
enable_stripping=true \
|
||||
rtc_enable_symbol_export=true \
|
||||
rtc_enable_objc_symbol_export=false \
|
||||
rtc_use_h264=false \
|
||||
use_custom_libcxx=false \
|
||||
use_clang_modules=false \
|
||||
clang_use_chrome_plugins=false \
|
||||
use_rtti=true \
|
||||
use_lld=false"
|
||||
|
||||
# build static library
|
||||
ninja -C "$OUTPUT_DIR" :default \
|
||||
api/audio_codecs:builtin_audio_decoder_factory \
|
||||
api/task_queue:default_task_queue_factory \
|
||||
sdk:native_api \
|
||||
sdk:default_codec_factory_objc \
|
||||
pc:peer_connection \
|
||||
sdk:videocapture_objc \
|
||||
sdk:framework_objc
|
||||
|
||||
# make libwebrtc.a
|
||||
# don't include nasm
|
||||
ar -rc "$ARTIFACTS_DIR/lib/libwebrtc.a" `find "$OUTPUT_DIR/obj" -name '*.o' -not -path "*/third_party/nasm/*"`
|
||||
|
||||
# License generation - may fail locally due to GN warnings breaking JSON parsing
|
||||
# Use vpython3 from depot_tools for consistent Python version
|
||||
vpython3 "./src/tools_webrtc/libs/generate_licenses.py" \
|
||||
--target :webrtc "$OUTPUT_DIR" "$OUTPUT_DIR" || echo "Warning: License generation failed"
|
||||
|
||||
cp "$OUTPUT_DIR/obj/webrtc.ninja" "$ARTIFACTS_DIR"
|
||||
cp "$OUTPUT_DIR/obj/modules/desktop_capture/desktop_capture.ninja" "$ARTIFACTS_DIR" 2>/dev/null || true
|
||||
cp "$OUTPUT_DIR/args.gn" "$ARTIFACTS_DIR"
|
||||
|
||||
cp "$OUTPUT_DIR/LICENSE.md" "$ARTIFACTS_DIR"
|
||||
|
||||
cd src
|
||||
find . -name "*.h" -print | cpio -pd "$ARTIFACTS_DIR/include"
|
||||
find . -name "*.inc" -print | cpio -pd "$ARTIFACTS_DIR/include"
|
||||
+159
@@ -0,0 +1,159 @@
|
||||
#!/bin/bash
|
||||
# Exit immediately if any command fails. This ensures CI properly reports build
|
||||
# failures instead of continuing to create empty/broken artifacts.
|
||||
set -e
|
||||
|
||||
# Copyright 2023 LiveKit, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
|
||||
arch=""
|
||||
profile="release"
|
||||
|
||||
while [ "$#" -gt 0 ]; do
|
||||
case "$1" in
|
||||
--arch)
|
||||
arch="$2"
|
||||
if [ "$arch" != "x64" ] && [ "$arch" != "arm64" ]; then
|
||||
echo "Error: Invalid value for --arch. Must be 'x64' or 'arm64'."
|
||||
exit 1
|
||||
fi
|
||||
shift 2
|
||||
;;
|
||||
--profile)
|
||||
profile="$2"
|
||||
if [ "$profile" != "debug" ] && [ "$profile" != "release" ]; then
|
||||
echo "Error: Invalid value for --profile. Must be 'debug' or 'release'."
|
||||
exit 1
|
||||
fi
|
||||
shift 2
|
||||
;;
|
||||
*)
|
||||
echo "Error: Unknown argument '$1'"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ -z "$arch" ]; then
|
||||
echo "Error: --arch must be set."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Building LiveKit WebRTC - Linux"
|
||||
echo "Arch: $arch"
|
||||
echo "Profile: $profile"
|
||||
|
||||
if [ ! -e "$(pwd)/depot_tools" ]
|
||||
then
|
||||
git clone --depth 1 https://chromium.googlesource.com/chromium/tools/depot_tools.git
|
||||
fi
|
||||
|
||||
export COMMAND_DIR=$(cd $(dirname $0); pwd)
|
||||
export PATH="$(pwd)/depot_tools:$PATH"
|
||||
export OUTPUT_DIR="$(pwd)/src/out-$arch-$profile"
|
||||
export ARTIFACTS_DIR="$(pwd)/linux-$arch-$profile"
|
||||
|
||||
if [ ! -e "$(pwd)/src" ]
|
||||
then
|
||||
gclient sync -D --no-history
|
||||
fi
|
||||
|
||||
cd src
|
||||
git apply "$COMMAND_DIR/patches/add_licenses.patch" -v --ignore-space-change --ignore-whitespace --whitespace=nowarn
|
||||
git apply "$COMMAND_DIR/patches/fix_license_json_parsing.patch" -v --ignore-space-change --ignore-whitespace --whitespace=nowarn
|
||||
git apply "$COMMAND_DIR/patches/ssl_verify_callback_with_native_handle.patch" -v --ignore-space-change --ignore-whitespace --whitespace=nowarn
|
||||
git apply "$COMMAND_DIR/patches/add_deps.patch" -v --ignore-space-change --ignore-whitespace --whitespace=nowarn
|
||||
git apply "$COMMAND_DIR/patches/fix_desktop_capture_compile.patch" -v --ignore-space-change --ignore-whitespace --whitespace=nowarn
|
||||
git apply "$COMMAND_DIR/patches/external_audio_source.patch" -v --ignore-space-change --ignore-whitespace --whitespace=nowarn
|
||||
|
||||
# Disable CREL (compact relocations). Chromium's build enables experimental
|
||||
# CREL via -Wa,--crel which causes segfaults on aarch64-linux (and is known
|
||||
# broken on arm32 and s390x too).
|
||||
# See: https://crbug.com/376278218
|
||||
# See: https://github.com/zed-industries/zed/pull/51433#discussion_r2944567608
|
||||
git -C build apply "$COMMAND_DIR/patches/disable_crel.patch" -v --ignore-space-change --ignore-whitespace --whitespace=nowarn
|
||||
|
||||
cd third_party
|
||||
|
||||
git apply "$COMMAND_DIR/patches/david_disable_gun_source_macro.patch" -v --ignore-space-change --ignore-whitespace --whitespace=nowarn
|
||||
|
||||
cd libyuv
|
||||
|
||||
git apply "$COMMAND_DIR/patches/disable_sme_for_libyuv.patch" -v --ignore-space-change --ignore-whitespace --whitespace=nowarn
|
||||
|
||||
cd ../../..
|
||||
|
||||
mkdir -p "$ARTIFACTS_DIR/lib"
|
||||
|
||||
python3 "./src/build/linux/sysroot_scripts/install-sysroot.py" --arch="$arch"
|
||||
|
||||
debug="false"
|
||||
if [ "$profile" = "debug" ]; then
|
||||
debug="true"
|
||||
fi
|
||||
|
||||
# Note: use_clang_modules=false is required to avoid C++ module compilation issues.
|
||||
# Without this flag, the build may fail partway through, resulting in missing
|
||||
# or incomplete artifacts.
|
||||
args="is_debug=$debug \
|
||||
target_os=\"linux\" \
|
||||
target_cpu=\"$arch\" \
|
||||
rtc_enable_protobuf=false \
|
||||
treat_warnings_as_errors=false \
|
||||
use_llvm_libatomic=false \
|
||||
use_custom_libcxx=false \
|
||||
use_custom_libcxx_for_host=false \
|
||||
use_clang_modules=false \
|
||||
rtc_include_tests=false \
|
||||
rtc_build_tools=false \
|
||||
rtc_build_examples=false \
|
||||
rtc_libvpx_build_vp9=true \
|
||||
enable_libaom=true \
|
||||
is_component_build=false \
|
||||
enable_stripping=true \
|
||||
ffmpeg_branding=\"Chrome\" \
|
||||
rtc_use_h264=true \
|
||||
rtc_use_h265=true \
|
||||
rtc_use_pipewire=true \
|
||||
symbol_level=0 \
|
||||
enable_iterator_debugging=false \
|
||||
use_rtti=true \
|
||||
rtc_use_x11=true"
|
||||
|
||||
# generate ninja files
|
||||
gn gen "$OUTPUT_DIR" --root="src" --args="${args}"
|
||||
|
||||
# build static library
|
||||
ninja -C "$OUTPUT_DIR" :default
|
||||
|
||||
# make libwebrtc.a
|
||||
# don't include nasm
|
||||
ar -rc "$ARTIFACTS_DIR/lib/libwebrtc.a" `find "$OUTPUT_DIR/obj" -name '*.o' -not -path "*/third_party/nasm/*"`
|
||||
src/third_party/llvm-build/Release+Asserts/bin/llvm-objcopy --redefine-syms="$COMMAND_DIR/boringssl_prefix_symbols.txt" "$ARTIFACTS_DIR/lib/libwebrtc.a"
|
||||
|
||||
# License generation is optional - may fail with some Python versions
|
||||
# Use vpython3 from depot_tools for consistent Python version
|
||||
vpython3 "./src/tools_webrtc/libs/generate_licenses.py" \
|
||||
--target :default "$OUTPUT_DIR" "$OUTPUT_DIR" || echo "Warning: License generation failed (non-critical)"
|
||||
|
||||
cp "$OUTPUT_DIR/obj/webrtc.ninja" "$ARTIFACTS_DIR"
|
||||
cp "$OUTPUT_DIR/obj/modules/desktop_capture/desktop_capture.ninja" "$ARTIFACTS_DIR"
|
||||
cp "$OUTPUT_DIR/args.gn" "$ARTIFACTS_DIR"
|
||||
|
||||
cp "$OUTPUT_DIR/LICENSE.md" "$ARTIFACTS_DIR"
|
||||
|
||||
cd src
|
||||
find . -name "*.h" -print | cpio -pd "$ARTIFACTS_DIR/include"
|
||||
find . -name "*.inc" -print | cpio -pd "$ARTIFACTS_DIR/include"
|
||||
+163
@@ -0,0 +1,163 @@
|
||||
#!/bin/bash
|
||||
# Exit immediately if any command fails. This ensures CI properly reports build
|
||||
# failures instead of continuing to create empty/broken artifacts.
|
||||
set -e
|
||||
|
||||
# Copyright 2023 LiveKit, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
|
||||
arch=""
|
||||
profile="release"
|
||||
|
||||
while [ "$#" -gt 0 ]; do
|
||||
case "$1" in
|
||||
--arch)
|
||||
arch="$2"
|
||||
if [ "$arch" != "x64" ] && [ "$arch" != "arm64" ]; then
|
||||
echo "Error: Invalid value for --arch. Must be 'x64' or 'arm64'."
|
||||
exit 1
|
||||
fi
|
||||
shift 2
|
||||
;;
|
||||
--profile)
|
||||
profile="$2"
|
||||
if [ "$profile" != "debug" ] && [ "$profile" != "release" ]; then
|
||||
echo "Error: Invalid value for --profile. Must be 'debug' or 'release'."
|
||||
exit 1
|
||||
fi
|
||||
shift 2
|
||||
;;
|
||||
*)
|
||||
echo "Error: Unknown argument '$1'"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ -z "$arch" ]; then
|
||||
echo "Error: --arch must be set."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Building LiveKit WebRTC - MacOS"
|
||||
echo "Arch: $arch"
|
||||
echo "Profile: $profile"
|
||||
|
||||
if [ ! -e "$(pwd)/depot_tools" ]
|
||||
then
|
||||
git clone --depth 1 https://chromium.googlesource.com/chromium/tools/depot_tools.git
|
||||
fi
|
||||
|
||||
export COMMAND_DIR=$(cd $(dirname $0); pwd)
|
||||
export PATH="$(pwd)/depot_tools:$PATH"
|
||||
export OUTPUT_DIR="$(pwd)/src/out-$arch-$profile"
|
||||
export ARTIFACTS_DIR="$(pwd)/mac-$arch-$profile"
|
||||
|
||||
if [ ! -e "$(pwd)/src" ]
|
||||
then
|
||||
gclient sync -D --no-history
|
||||
fi
|
||||
|
||||
cd src
|
||||
|
||||
# Apply patches only if not already applied (check with --reverse --check)
|
||||
apply_patch_if_needed() {
|
||||
local patch="$1"
|
||||
if git apply --reverse --check "$patch" 2>/dev/null; then
|
||||
echo "Patch already applied: $(basename "$patch")"
|
||||
else
|
||||
echo "Applying patch: $(basename "$patch")"
|
||||
git apply "$patch" -v --ignore-space-change --ignore-whitespace --whitespace=nowarn || true
|
||||
fi
|
||||
}
|
||||
|
||||
apply_patch_if_needed "$COMMAND_DIR/patches/add_licenses.patch"
|
||||
apply_patch_if_needed "$COMMAND_DIR/patches/fix_license_json_parsing.patch"
|
||||
apply_patch_if_needed "$COMMAND_DIR/patches/ssl_verify_callback_with_native_handle.patch"
|
||||
apply_patch_if_needed "$COMMAND_DIR/patches/add_deps.patch"
|
||||
apply_patch_if_needed "$COMMAND_DIR/patches/external_audio_source.patch"
|
||||
|
||||
cd ..
|
||||
|
||||
mkdir -p "$ARTIFACTS_DIR/lib"
|
||||
|
||||
debug="false"
|
||||
if [ "$profile" = "debug" ]; then
|
||||
debug="true"
|
||||
fi
|
||||
|
||||
# generate ninja files
|
||||
# Note: use_clang_modules=false is required to avoid libc++ header incompatibility
|
||||
# with Xcode 26.0. When enabled, C++ module compilation fails with errors like
|
||||
# "unknown type name 'size_t'" due to conflicts between WebRTC's bundled libc++
|
||||
# headers and the macOS SDK headers.
|
||||
gn gen "$OUTPUT_DIR" --root="src" \
|
||||
--args="is_debug=$debug \
|
||||
enable_dsyms=$debug \
|
||||
target_os=\"mac\" \
|
||||
target_cpu=\"$arch\" \
|
||||
mac_deployment_target=\"10.15\" \
|
||||
mac_min_system_version=\"10.15\" \
|
||||
treat_warnings_as_errors=false \
|
||||
rtc_enable_protobuf=false \
|
||||
rtc_include_tests=false \
|
||||
rtc_build_examples=false \
|
||||
rtc_build_tools=false \
|
||||
rtc_libvpx_build_vp9=true \
|
||||
enable_libaom=true \
|
||||
is_component_build=false \
|
||||
enable_stripping=true \
|
||||
rtc_enable_symbol_export=true \
|
||||
rtc_enable_objc_symbol_export=false \
|
||||
rtc_include_dav1d_in_internal_decoder_factory = true \
|
||||
rtc_use_h264=true \
|
||||
rtc_use_h265=true \
|
||||
use_custom_libcxx=false \
|
||||
use_clang_modules=false \
|
||||
clang_use_chrome_plugins=false \
|
||||
use_rtti=true \
|
||||
use_lld=false \
|
||||
rtc_include_internal_audio_device=true"
|
||||
|
||||
# build static library
|
||||
ninja -C "$OUTPUT_DIR" :default \
|
||||
api/audio_codecs:builtin_audio_decoder_factory \
|
||||
api/task_queue:default_task_queue_factory \
|
||||
sdk:native_api \
|
||||
sdk:default_codec_factory_objc \
|
||||
pc:peer_connection \
|
||||
sdk:videocapture_objc \
|
||||
sdk:mac_framework_objc \
|
||||
desktop_capture_objc \
|
||||
modules/audio_device:audio_device
|
||||
|
||||
# make libwebrtc.a
|
||||
# don't include nasm
|
||||
ar -rc "$ARTIFACTS_DIR/lib/libwebrtc.a" `find "$OUTPUT_DIR/obj" -name '*.o' -not -path "*/third_party/nasm/*"`
|
||||
|
||||
# License generation - may fail locally due to GN warnings breaking JSON parsing
|
||||
# Use vpython3 from depot_tools for consistent Python version
|
||||
vpython3 "./src/tools_webrtc/libs/generate_licenses.py" \
|
||||
--target :webrtc "$OUTPUT_DIR" "$OUTPUT_DIR" || echo "Warning: License generation failed"
|
||||
|
||||
cp "$OUTPUT_DIR/obj/webrtc.ninja" "$ARTIFACTS_DIR"
|
||||
cp "$OUTPUT_DIR/obj/modules/desktop_capture/desktop_capture.ninja" "$ARTIFACTS_DIR" 2>/dev/null || true
|
||||
cp "$OUTPUT_DIR/args.gn" "$ARTIFACTS_DIR"
|
||||
|
||||
cp "$OUTPUT_DIR/LICENSE.md" "$ARTIFACTS_DIR"
|
||||
|
||||
cd src
|
||||
find . -name "*.h" -print | cpio -pd "$ARTIFACTS_DIR/include"
|
||||
find . -name "*.inc" -print | cpio -pd "$ARTIFACTS_DIR/include"
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
@echo off
|
||||
|
||||
setlocal enabledelayedexpansion
|
||||
|
||||
set arch=
|
||||
set profile=release
|
||||
|
||||
:arg_loop
|
||||
if "%1" == "" goto end_arg_loop
|
||||
if "%1" == "--arch" (
|
||||
set "arch=%2"
|
||||
shift & shift & goto arg_loop
|
||||
)
|
||||
if "%1" == "--profile" (
|
||||
set "profile=%2"
|
||||
shift & shift & goto arg_loop
|
||||
)
|
||||
echo Error: Unknown argument '%1'
|
||||
exit /b 1
|
||||
:end_arg_loop
|
||||
|
||||
if not "!arch!" == "x64" if not "!arch!" == "arm64" (
|
||||
echo Error: Invalid value for --arch. Must be 'x64' or 'arm64'.
|
||||
exit /b 1
|
||||
)
|
||||
if not "!profile!" == "debug" if not "!profile!" == "release" (
|
||||
echo Error: Invalid value for --profile. Must be 'debug' or 'release'.
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
echo "Building LiveKit WebRTC - Windows"
|
||||
echo "Arch: !arch!"
|
||||
echo "Profile: !profile!"
|
||||
|
||||
if not exist depot_tools (
|
||||
git clone --depth 1 https://chromium.googlesource.com/chromium/tools/depot_tools.git
|
||||
)
|
||||
|
||||
set COMMAND_DIR=%~dp0
|
||||
set PATH=%cd%\depot_tools;%PATH%
|
||||
set DEPOT_TOOLS_WIN_TOOLCHAIN=0
|
||||
set GYP_GENERATORS=ninja,msvs-ninja
|
||||
set GYP_MSVS_VERSION=2022
|
||||
set OUTPUT_DIR=src\out-!arch!-!profile!
|
||||
set ARTIFACTS_DIR=%cd%\win-!arch!-!profile!
|
||||
set vs2019_install=C:\Program Files\Microsoft Visual Studio\2022\Enterprise
|
||||
|
||||
if not exist src (
|
||||
call gclient.bat sync -D --with_branch_heads --with_tags
|
||||
)
|
||||
|
||||
cd src
|
||||
call git apply "%COMMAND_DIR%/patches/add_licenses.patch" -v --ignore-space-change --ignore-whitespace --whitespace=nowarn
|
||||
call git apply "%COMMAND_DIR%/patches/add_deps.patch" -v --ignore-space-change --ignore-whitespace --whitespace=nowarn
|
||||
call git apply "%COMMAND_DIR%/patches/windows_silence_warnings.patch" -v --ignore-space-change --ignore-whitespace --whitespace=nowarn
|
||||
call git apply "%COMMAND_DIR%/patches/ssl_verify_callback_with_native_handle.patch" -v --ignore-space-change --ignore-whitespace --whitespace=nowarn
|
||||
call git apply "%COMMAND_DIR%/patches/external_audio_source.patch" -v --ignore-space-change --ignore-whitespace --whitespace=nowarn
|
||||
|
||||
copy ".vpython3" "..\"
|
||||
|
||||
cd ..
|
||||
|
||||
mkdir "%ARTIFACTS_DIR%\lib"
|
||||
|
||||
set "debug=false"
|
||||
if "!profile!" == "debug" (
|
||||
set "debug=true"
|
||||
)
|
||||
|
||||
rem generate ninja for release
|
||||
call gn.bat gen %OUTPUT_DIR% --root="src" ^
|
||||
--args="is_debug=!debug! is_clang=true target_cpu=\"!arch!\" use_custom_libcxx=false rtc_libvpx_build_vp9=true enable_libaom=true rtc_include_tests=false rtc_build_examples=false rtc_build_tools=false is_component_build=false rtc_enable_protobuf=false rtc_use_h264=true ffmpeg_branding=\"Chrome\" symbol_level=0 enable_iterator_debugging=false"
|
||||
|
||||
rem build
|
||||
ninja.exe -C %OUTPUT_DIR% :default
|
||||
|
||||
rem copy static library for release build
|
||||
copy "%OUTPUT_DIR%\obj\webrtc.lib" "%ARTIFACTS_DIR%\lib"
|
||||
|
||||
rem generate license
|
||||
call python3 "%cd%\src\tools_webrtc\libs\generate_licenses.py" ^
|
||||
--target :default %OUTPUT_DIR% %OUTPUT_DIR%
|
||||
|
||||
copy "%OUTPUT_DIR%\obj\webrtc.ninja" "%ARTIFACTS_DIR%"
|
||||
copy "%OUTPUT_DIR%\obj\modules\desktop_capture\desktop_capture.ninja" "%ARTIFACTS_DIR%"
|
||||
copy "%OUTPUT_DIR%\args.gn" "%ARTIFACTS_DIR%"
|
||||
copy "%OUTPUT_DIR%\LICENSE.md" "%ARTIFACTS_DIR%"
|
||||
|
||||
rem copy header
|
||||
xcopy src\*.h "%ARTIFACTS_DIR%\include" /C /S /I /F /H
|
||||
xcopy src\*.inc "%ARTIFACTS_DIR%\include" /C /S /I /F /H
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
diff --git a/BUILD.gn b/BUILD.gn
|
||||
index ca8d8faa61..13e07a2f28 100644
|
||||
--- a/BUILD.gn
|
||||
+++ b/BUILD.gn
|
||||
@@ -24,6 +24,9 @@
|
||||
import("//build/config/linux/pkg_config.gni")
|
||||
import("//build/config/sanitizers/sanitizers.gni")
|
||||
import("webrtc.gni")
|
||||
+import("//third_party/libaom/options.gni")
|
||||
+
|
||||
+
|
||||
if (rtc_enable_protobuf) {
|
||||
import("//third_party/protobuf/proto_library.gni")
|
||||
}
|
||||
@@ -331,6 +334,10 @@ config("common_config") {
|
||||
defines += [ "WEBRTC_INCLUDE_INTERNAL_AUDIO_DEVICE" ]
|
||||
}
|
||||
|
||||
+ if (enable_libaom) {
|
||||
+ defines += [ "RTC_USE_LIBAOM_AV1_ENCODER" ]
|
||||
+ }
|
||||
+
|
||||
if (rtc_libvpx_build_vp9) {
|
||||
defines += [ "RTC_ENABLE_VP9" ]
|
||||
}
|
||||
@@ -565,6 +572,10 @@ if (!build_with_chromium) {
|
||||
"pc:rtc_pc",
|
||||
"sdk",
|
||||
"video",
|
||||
+ "//third_party/zlib",
|
||||
+ "rtc_base:log_sinks",
|
||||
+ "media:rtc_simulcast_encoder_adapter",
|
||||
+ "api/crypto:frame_crypto_transformer",
|
||||
]
|
||||
|
||||
if (rtc_include_builtin_audio_codecs) {
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
diff --git a/tools_webrtc/libs/generate_licenses.py b/tools_webrtc/libs/generate_licenses.py
|
||||
index d945b43..5f3e457 100755
|
||||
--- a/tools_webrtc/libs/generate_licenses.py
|
||||
+++ b/tools_webrtc/libs/generate_licenses.py
|
||||
@@ -86,6 +86,18 @@ LIB_TO_LICENSES_DICT = {
|
||||
# Compile time dependencies, no license needed:
|
||||
'ow2_asm': [],
|
||||
'jdk': [],
|
||||
+
|
||||
+ 'ffmpeg':[
|
||||
+ 'third_party/ffmpeg/COPYING.GPLv2',
|
||||
+ 'third_party/ffmpeg/COPYING.GPLv3',
|
||||
+ 'third_party/ffmpeg/COPYING.LGPLv2.1',
|
||||
+ 'third_party/ffmpeg/COPYING.LGPLv3'
|
||||
+ ],
|
||||
+ 'openh264': ['third_party/openh264/src/LICENSE'],
|
||||
+ 'catapult': [],
|
||||
+ 'google_benchmark': [],
|
||||
+ 'googletest': [],
|
||||
+ 'vinn': [],
|
||||
}
|
||||
|
||||
# Third_party library _regex_ to licences mapping. Keys are regular expression
|
||||
Vendored
+22
@@ -0,0 +1,22 @@
|
||||
--- src/buildtools/third_party/libunwind/BUILD.gn 2023-07-10 10:19:16
|
||||
+++ src/buildtools/third_party/libunwind/BUILD.gn 2023-07-10 10:19:23
|
||||
@@ -21,7 +21,7 @@ config("libunwind_config") {
|
||||
|
||||
# TODO(crbug.com/40273848): Move this build file to third_party/libc++/BUILD.gn once submodule migration is done
|
||||
source_set("libunwind") {
|
||||
- visibility = [ "//buildtools/third_party/libc++abi" ]
|
||||
+ visibility = [ "//buildtools/third_party/libc++abi", "//build/config:common_deps" ]
|
||||
if (is_android) {
|
||||
visibility += [ "//services/tracing/public/cpp" ]
|
||||
}
|
||||
--- src/build/config/BUILD.gn 2023-07-10 10:23:49
|
||||
+++ src/build/config/BUILD.gn 2023-07-10 10:23:54
|
||||
@@ -296,6 +296,8 @@ group("common_deps") {
|
||||
|
||||
if (use_custom_libcxx) {
|
||||
public_deps += [ "//buildtools/third_party/libc++" ]
|
||||
+ } else {
|
||||
+ public_deps += [ "//buildtools/third_party/libunwind" ]
|
||||
}
|
||||
|
||||
if (use_llvm_libatomic) {
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
diff --git a/dav1d/BUILD.gn b/dav1d/BUILD.gn
|
||||
index 9348e15c3c..f34db7ff98 100644
|
||||
--- a/dav1d/BUILD.gn
|
||||
+++ b/dav1d/BUILD.gn
|
||||
@@ -101,7 +101,7 @@ if (is_win) {
|
||||
dav1d_copts += [ "-D_DARWIN_C_SOURCE" ]
|
||||
}
|
||||
if (is_linux || is_chromeos || is_android || current_os == "aix") {
|
||||
- if (!is_clang) {
|
||||
+ if (!is_clang && (current_cpu == "x86" || current_cpu == "x64")) {
|
||||
dav1d_copts += [ "-D_GNU_SOURCE" ]
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
diff --git a/config/compiler/BUILD.gn b/config/compiler/BUILD.gn
|
||||
index c09c9677b..3cfce80a1 100644
|
||||
--- a/config/compiler/BUILD.gn
|
||||
+++ b/config/compiler/BUILD.gn
|
||||
@@ -721,14 +721,6 @@ config("compiler") {
|
||||
} else {
|
||||
cflags += [ "-ffp-contract=off" ]
|
||||
}
|
||||
-
|
||||
- # Enable ELF CREL (see crbug.com/357878242) for all platforms that use ELF.
|
||||
- # TODO(crbug.com/376278218): This causes segfault on Linux ARM builds.
|
||||
- # It also causes segfault on Linux s390x:
|
||||
- # https://github.com/llvm/llvm-project/issues/149511
|
||||
- if (is_linux && use_lld && current_cpu != "arm" && current_cpu != "s390x") {
|
||||
- cflags += [ "-Wa,--crel,--allow-experimental-crel" ]
|
||||
- }
|
||||
}
|
||||
|
||||
# C11/C++11 compiler flags setup.
|
||||
fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/libwebrtc/patches/disable_sme_for_libyuv.patch
Vendored
+13
@@ -0,0 +1,13 @@
|
||||
diff --git a/libyuv.gni b/libyuv.gni
|
||||
index 3334df70..9859a121 100644
|
||||
--- a/libyuv.gni
|
||||
+++ b/libyuv.gni
|
||||
@@ -24,7 +24,7 @@ declare_args() {
|
||||
# errors on Fuchsia, macOS, and compilation errors on Windows.
|
||||
# TODO: bug 359006069 - Remove the restriction after the linker and
|
||||
# compilation errors are fixed.
|
||||
- libyuv_use_sme = current_cpu == "arm64" && (is_linux || is_android)
|
||||
+ libyuv_use_sme = false
|
||||
libyuv_use_msa =
|
||||
(current_cpu == "mips64el" || current_cpu == "mipsel") && mips_use_msa
|
||||
libyuv_use_mmi =
|
||||
Vendored
+142
@@ -0,0 +1,142 @@
|
||||
diff --git a/api/media_stream_interface.h b/api/media_stream_interface.h
|
||||
index fb1cc4e58e..85062ba60e 100644
|
||||
--- a/api/media_stream_interface.h
|
||||
+++ b/api/media_stream_interface.h
|
||||
@@ -267,6 +267,11 @@ class RTC_EXPORT AudioSourceInterface : public MediaSourceInterface {
|
||||
// (for some of the settings this approach is broken, e.g. setting
|
||||
// audio network adaptation on the source is the wrong layer of abstraction).
|
||||
virtual const AudioOptions options() const;
|
||||
+
|
||||
+ // Returns true if this source delivers audio externally (via AddSink),
|
||||
+ // bypassing the ADM/AudioState audio distribution path.
|
||||
+ // When true, AudioSendStream should not register with AudioState.
|
||||
+ virtual bool is_external_source() const { return false; }
|
||||
};
|
||||
|
||||
// Interface of the audio processor used by the audio track to collect
|
||||
diff --git a/audio/audio_send_stream.cc b/audio/audio_send_stream.cc
|
||||
index 76156ce830..10b59d3ff6 100644
|
||||
--- a/audio/audio_send_stream.cc
|
||||
+++ b/audio/audio_send_stream.cc
|
||||
@@ -373,8 +373,13 @@ void AudioSendStream::Start() {
|
||||
}
|
||||
channel_send_->StartSend();
|
||||
sending_ = true;
|
||||
- audio_state()->AddSendingStream(this, encoder_sample_rate_hz_,
|
||||
- encoder_num_channels_);
|
||||
+ // Only register with AudioState if not using external source.
|
||||
+ // External sources (like NativeAudioSource) deliver audio directly via AddSink,
|
||||
+ // so we don't want AudioState to also send device audio to this stream.
|
||||
+ if (!config_.external_source) {
|
||||
+ audio_state()->AddSendingStream(this, encoder_sample_rate_hz_,
|
||||
+ encoder_num_channels_);
|
||||
+ }
|
||||
}
|
||||
|
||||
void AudioSendStream::Stop() {
|
||||
@@ -386,7 +391,10 @@ void AudioSendStream::Stop() {
|
||||
RemoveBitrateObserver();
|
||||
channel_send_->StopSend();
|
||||
sending_ = false;
|
||||
- audio_state()->RemoveSendingStream(this);
|
||||
+ // Only unregister if we registered (when not using external source).
|
||||
+ if (!config_.external_source) {
|
||||
+ audio_state()->RemoveSendingStream(this);
|
||||
+ }
|
||||
}
|
||||
|
||||
void AudioSendStream::SendAudioData(std::unique_ptr<AudioFrame> audio_frame) {
|
||||
diff --git a/call/audio_send_stream.h b/call/audio_send_stream.h
|
||||
index 84341b5cb1..9359777bc9 100644
|
||||
--- a/call/audio_send_stream.h
|
||||
+++ b/call/audio_send_stream.h
|
||||
@@ -178,6 +178,12 @@ class AudioSendStream : public AudioSender {
|
||||
// An optional frame transformer used by insertable streams to transform
|
||||
// encoded frames.
|
||||
scoped_refptr<webrtc::FrameTransformerInterface> frame_transformer;
|
||||
+
|
||||
+ // When true, this stream uses an external audio source (not ADM).
|
||||
+ // AudioState will NOT send device-captured audio to this stream.
|
||||
+ // Audio is delivered directly via the source's AddSink mechanism.
|
||||
+ // This prevents mixing of device audio with externally-sourced audio.
|
||||
+ bool external_source = false;
|
||||
};
|
||||
|
||||
virtual ~AudioSendStream() = default;
|
||||
diff --git a/media/base/audio_source.h b/media/base/audio_source.h
|
||||
index 04a7d19dfa..9f513f3c75 100644
|
||||
--- a/media/base/audio_source.h
|
||||
+++ b/media/base/audio_source.h
|
||||
@@ -49,6 +49,10 @@ class AudioSource {
|
||||
// to the source at a time.
|
||||
virtual void SetSink(Sink* sink) = 0;
|
||||
|
||||
+ // Returns true if this source delivers audio externally (bypassing ADM).
|
||||
+ // When true, AudioSendStream should not register with AudioState.
|
||||
+ virtual bool is_external_source() const { return false; }
|
||||
+
|
||||
protected:
|
||||
virtual ~AudioSource() {}
|
||||
};
|
||||
diff --git a/media/engine/webrtc_voice_engine.cc b/media/engine/webrtc_voice_engine.cc
|
||||
index 762f9d584c..4ce07ddc9d 100644
|
||||
--- a/media/engine/webrtc_voice_engine.cc
|
||||
+++ b/media/engine/webrtc_voice_engine.cc
|
||||
@@ -1017,6 +1017,14 @@ class WebRtcVoiceSendChannel::WebRtcAudioSendStream : public AudioSource::Sink {
|
||||
RTC_DCHECK(source_ == source);
|
||||
return;
|
||||
}
|
||||
+
|
||||
+ // Check if this is an external audio source (delivers audio via AddSink).
|
||||
+ // If so, mark the config so AudioState doesn't send device audio to this
|
||||
+ // stream. This must be done before UpdateSendState() calls Start().
|
||||
+ if (source->is_external_source() && !config_.external_source) {
|
||||
+ config_.external_source = true;
|
||||
+ stream_->Reconfigure(config_, nullptr);
|
||||
+ }
|
||||
source->SetSink(this);
|
||||
source_ = source;
|
||||
UpdateSendState();
|
||||
diff --git a/pc/rtp_sender.cc b/pc/rtp_sender.cc
|
||||
index d5edbbf0ed..c14ddfe868 100644
|
||||
--- a/pc/rtp_sender.cc
|
||||
+++ b/pc/rtp_sender.cc
|
||||
@@ -786,6 +786,13 @@ void AudioRtpSender::SetSend() {
|
||||
RTC_DCHECK_RUN_ON(signaling_thread_);
|
||||
RTC_DCHECK(!stopped_);
|
||||
RTC_DCHECK(can_send_track());
|
||||
+
|
||||
+ // Propagate is_external_source from AudioSourceInterface to the sink adapter.
|
||||
+ // This ensures the voice engine knows not to mix ADM audio with external sources.
|
||||
+ if (audio_track()->GetSource() && audio_track()->GetSource()->is_external_source()) {
|
||||
+ sink_adapter_->set_is_external_source(true);
|
||||
+ }
|
||||
+
|
||||
if (!media_channel_) {
|
||||
RTC_LOG(LS_ERROR) << "SetAudioSend: No audio channel exists.";
|
||||
return;
|
||||
diff --git a/pc/rtp_sender.h b/pc/rtp_sender.h
|
||||
index eaffd4ef0f..d0489df9e7 100644
|
||||
--- a/pc/rtp_sender.h
|
||||
+++ b/pc/rtp_sender.h
|
||||
@@ -307,6 +307,12 @@ class LocalAudioSinkAdapter : public AudioTrackSinkInterface,
|
||||
LocalAudioSinkAdapter();
|
||||
virtual ~LocalAudioSinkAdapter();
|
||||
|
||||
+ // Set whether the original AudioSourceInterface is an external source.
|
||||
+ // This propagates the is_external_source state from AudioSourceInterface
|
||||
+ // to this AudioSource adapter.
|
||||
+ void set_is_external_source(bool value) { is_external_source_ = value; }
|
||||
+ bool is_external_source() const override { return is_external_source_; }
|
||||
+
|
||||
private:
|
||||
// AudioSinkInterface implementation.
|
||||
void OnData(const void* audio_data,
|
||||
@@ -337,6 +343,7 @@ class LocalAudioSinkAdapter : public AudioTrackSinkInterface,
|
||||
// Critical section protecting `sink_`.
|
||||
Mutex lock_;
|
||||
int num_preferred_channels_ = -1;
|
||||
+ bool is_external_source_ = false;
|
||||
};
|
||||
|
||||
class AudioRtpSender : public DtmfProviderInterface, public RtpSenderBase {
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
diff --git a/modules/desktop_capture/linux/wayland/shared_screencast_stream.cc b/modules/desktop_capture/linux/wayland/shared_screencast_stream.cc
|
||||
index 070257f072..61fd0c8f3b 100644
|
||||
--- a/modules/desktop_capture/linux/wayland/shared_screencast_stream.cc
|
||||
+++ b/modules/desktop_capture/linux/wayland/shared_screencast_stream.cc
|
||||
@@ -71,10 +71,10 @@ constexpr int CursorMetaSize(int w, int h) {
|
||||
w * h * kCursorBpp);
|
||||
}
|
||||
|
||||
-constexpr PipeWireVersion kDmaBufModifierMinVersion = {.major = 0,
|
||||
+const PipeWireVersion kDmaBufModifierMinVersion = {.major = 0,
|
||||
.minor = 3,
|
||||
.micro = 33};
|
||||
-constexpr PipeWireVersion kDropSingleModifierMinVersion = {.major = 0,
|
||||
+const PipeWireVersion kDropSingleModifierMinVersion = {.major = 0,
|
||||
.minor = 3,
|
||||
.micro = 40};
|
||||
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
diff --git a/tools_webrtc/libs/generate_licenses.py b/tools_webrtc/libs/generate_licenses.py
|
||||
--- a/tools_webrtc/libs/generate_licenses.py
|
||||
+++ b/tools_webrtc/libs/generate_licenses.py
|
||||
@@ -211,6 +211,12 @@ class LicenseBuilder:
|
||||
|
||||
def _get_third_party_libraries(self, buildfile_dir, target):
|
||||
license_json = LicenseBuilder._run_gn(buildfile_dir, target)
|
||||
+ # Strip any non-JSON content (e.g., GN warnings) before the actual JSON.
|
||||
+ # GN may output warnings before the JSON when certain build args trigger
|
||||
+ # deprecation notices.
|
||||
+ json_start = license_json.find('{')
|
||||
+ if json_start > 0:
|
||||
+ license_json = license_json[json_start:]
|
||||
try:
|
||||
output = json.loads(license_json)
|
||||
except:
|
||||
+317
@@ -0,0 +1,317 @@
|
||||
diff --git a/modules/video_coding/codecs/test/android_codec_factory_helper.cc b/modules/video_coding/codecs/test/android_codec_factory_helper.cc
|
||||
index f6a5046210..4ce5a79d25 100644
|
||||
--- a/modules/video_coding/codecs/test/android_codec_factory_helper.cc
|
||||
+++ b/modules/video_coding/codecs/test/android_codec_factory_helper.cc
|
||||
@@ -54,9 +54,9 @@ void InitializeAndroidObjects() {
|
||||
std::unique_ptr<VideoEncoderFactory> CreateAndroidEncoderFactory() {
|
||||
JNIEnv* env = AttachCurrentThreadIfNeeded();
|
||||
ScopedJavaLocalRef<jclass> factory_class =
|
||||
- GetClass(env, "org/webrtc/HardwareVideoEncoderFactory");
|
||||
+ GetClass(env, "livekit/org/webrtc/HardwareVideoEncoderFactory");
|
||||
jmethodID factory_constructor = env->GetMethodID(
|
||||
- factory_class.obj(), "<init>", "(Lorg/webrtc/EglBase$Context;ZZ)V");
|
||||
+ factory_class.obj(), "<init>", "(Llivekit/org/webrtc/EglBase$Context;ZZ)V");
|
||||
ScopedJavaLocalRef<jobject> factory_object =
|
||||
ScopedJavaLocalRef<jobject>::Adopt(
|
||||
env, env->NewObject(factory_class.obj(), factory_constructor,
|
||||
@@ -69,9 +69,9 @@ std::unique_ptr<VideoEncoderFactory> CreateAndroidEncoderFactory() {
|
||||
std::unique_ptr<VideoDecoderFactory> CreateAndroidDecoderFactory() {
|
||||
JNIEnv* env = AttachCurrentThreadIfNeeded();
|
||||
ScopedJavaLocalRef<jclass> factory_class =
|
||||
- GetClass(env, "org/webrtc/HardwareVideoDecoderFactory");
|
||||
+ GetClass(env, "livekit/org/webrtc/HardwareVideoDecoderFactory");
|
||||
jmethodID factory_constructor = env->GetMethodID(
|
||||
- factory_class.obj(), "<init>", "(Lorg/webrtc/EglBase$Context;)V");
|
||||
+ factory_class.obj(), "<init>", "(Llivekit/org/webrtc/EglBase$Context;)V");
|
||||
ScopedJavaLocalRef<jobject> factory_object =
|
||||
ScopedJavaLocalRef<jobject>::Adopt(
|
||||
env, env->NewObject(factory_class.obj(), factory_constructor,
|
||||
diff --git a/sdk/android/BUILD.gn b/sdk/android/BUILD.gn
|
||||
index 8ce74e2c8c..2e88760d0a 100644
|
||||
--- a/sdk/android/BUILD.gn
|
||||
+++ b/sdk/android/BUILD.gn
|
||||
@@ -1446,11 +1446,13 @@ if (current_os == "linux" || is_android) {
|
||||
generate_jni("generated_environment_jni") {
|
||||
sources = [ "api/org/webrtc/Environment.java" ]
|
||||
namespace = "webrtc::jni"
|
||||
+ package_prefix = android_package_prefix
|
||||
}
|
||||
|
||||
generate_jni("generated_metrics_jni") {
|
||||
sources = [ "api/org/webrtc/Metrics.java" ]
|
||||
namespace = "webrtc::jni"
|
||||
+ package_prefix = android_package_prefix
|
||||
}
|
||||
|
||||
# Generated JNI for public JNI targets, matching order of targets
|
||||
@@ -1464,6 +1466,7 @@ if (current_os == "linux" || is_android) {
|
||||
"src/java/org/webrtc/JniCommon.java",
|
||||
]
|
||||
namespace = "webrtc::jni"
|
||||
+ package_prefix = android_package_prefix
|
||||
}
|
||||
|
||||
generate_jni("generated_video_jni") {
|
||||
@@ -1493,11 +1496,13 @@ if (current_os == "linux" || is_android) {
|
||||
"src/java/org/webrtc/WrappedNativeI420Buffer.java",
|
||||
]
|
||||
namespace = "webrtc::jni"
|
||||
+ package_prefix = android_package_prefix
|
||||
}
|
||||
|
||||
generate_jni("generated_video_egl_jni") {
|
||||
sources = [ "src/java/org/webrtc/EglBase10Impl.java" ]
|
||||
namespace = "webrtc::jni"
|
||||
+ package_prefix = android_package_prefix
|
||||
}
|
||||
|
||||
generate_jni("generated_libvpx_vp8_jni") {
|
||||
@@ -1507,6 +1512,7 @@ if (current_os == "linux" || is_android) {
|
||||
]
|
||||
|
||||
namespace = "webrtc::jni"
|
||||
+ package_prefix = android_package_prefix
|
||||
}
|
||||
|
||||
generate_jni("generated_libvpx_vp9_jni") {
|
||||
@@ -1516,18 +1522,21 @@ if (current_os == "linux" || is_android) {
|
||||
]
|
||||
|
||||
namespace = "webrtc::jni"
|
||||
+ package_prefix = android_package_prefix
|
||||
}
|
||||
|
||||
generate_jni("generated_libaom_av1_encoder_jni") {
|
||||
sources = [ "api/org/webrtc/LibaomAv1Encoder.java" ]
|
||||
|
||||
namespace = "webrtc::jni"
|
||||
+ package_prefix = android_package_prefix
|
||||
}
|
||||
|
||||
generate_jni("generated_dav1d_jni") {
|
||||
sources = [ "api/org/webrtc/Dav1dDecoder.java" ]
|
||||
|
||||
namespace = "webrtc::jni"
|
||||
+ package_prefix = android_package_prefix
|
||||
}
|
||||
|
||||
generate_jni("generated_swcodecs_jni") {
|
||||
@@ -1537,11 +1546,13 @@ if (current_os == "linux" || is_android) {
|
||||
]
|
||||
|
||||
namespace = "webrtc::jni"
|
||||
+ package_prefix = android_package_prefix
|
||||
}
|
||||
|
||||
generate_jni("generated_rtcerror_jni") {
|
||||
sources = [ "src/java/org/webrtc/RtcError.java" ]
|
||||
namespace = "webrtc::jni"
|
||||
+ package_prefix = android_package_prefix
|
||||
}
|
||||
|
||||
generate_jni("generated_peerconnection_jni") {
|
||||
@@ -1585,11 +1596,13 @@ if (current_os == "linux" || is_android) {
|
||||
"api/org/webrtc/TurnCustomizer.java",
|
||||
]
|
||||
namespace = "webrtc::jni"
|
||||
+ package_prefix = android_package_prefix
|
||||
}
|
||||
|
||||
generate_jni("generated_java_audio_jni") {
|
||||
sources = [ "api/org/webrtc/audio/JavaAudioDeviceModule.java" ]
|
||||
namespace = "webrtc::jni"
|
||||
+ package_prefix = android_package_prefix
|
||||
}
|
||||
|
||||
generate_jni("generated_builtin_audio_codecs_jni") {
|
||||
@@ -1598,6 +1611,7 @@ if (current_os == "linux" || is_android) {
|
||||
"api/org/webrtc/BuiltinAudioEncoderFactoryFactory.java",
|
||||
]
|
||||
namespace = "webrtc::jni"
|
||||
+ package_prefix = android_package_prefix
|
||||
}
|
||||
|
||||
# Generated JNI for native API targets
|
||||
@@ -1609,17 +1623,20 @@ if (current_os == "linux" || is_android) {
|
||||
"src/java/org/webrtc/WebRtcClassLoader.java",
|
||||
]
|
||||
namespace = "webrtc::jni"
|
||||
+ package_prefix = android_package_prefix
|
||||
}
|
||||
|
||||
# Generated JNI for internal targets.
|
||||
|
||||
generate_jni("generated_logging_jni") {
|
||||
sources = [ "src/java/org/webrtc/JNILogging.java" ]
|
||||
+ package_prefix = android_package_prefix
|
||||
}
|
||||
|
||||
generate_jni("generated_audio_device_module_base_jni") {
|
||||
sources = [ "src/java/org/webrtc/audio/WebRtcAudioManager.java" ]
|
||||
namespace = "webrtc::jni"
|
||||
+ package_prefix = android_package_prefix
|
||||
}
|
||||
|
||||
generate_jni("generated_java_audio_device_module_native_jni") {
|
||||
@@ -1628,6 +1645,7 @@ if (current_os == "linux" || is_android) {
|
||||
"src/java/org/webrtc/audio/WebRtcAudioTrack.java",
|
||||
]
|
||||
namespace = "webrtc::jni"
|
||||
+ package_prefix = android_package_prefix
|
||||
}
|
||||
}
|
||||
|
||||
diff --git a/sdk/android/api/org/webrtc/PeerConnectionFactory.java b/sdk/android/api/org/webrtc/PeerConnectionFactory.java
|
||||
index 01679ad12b..7b8545902d 100644
|
||||
--- a/sdk/android/api/org/webrtc/PeerConnectionFactory.java
|
||||
+++ b/sdk/android/api/org/webrtc/PeerConnectionFactory.java
|
||||
@@ -88,7 +88,7 @@ public class PeerConnectionFactory {
|
||||
private String fieldTrials = "";
|
||||
private boolean enableInternalTracer;
|
||||
private NativeLibraryLoader nativeLibraryLoader = new NativeLibrary.DefaultLoader();
|
||||
- private String nativeLibraryName = "jingle_peerconnection_so";
|
||||
+ private String nativeLibraryName = "lkjingle_peerconnection_so";
|
||||
@Nullable private Loggable loggable;
|
||||
@Nullable private Severity loggableSeverity;
|
||||
|
||||
diff --git a/sdk/android/src/jni/jni_helpers.h b/sdk/android/src/jni/jni_helpers.h
|
||||
index d86c8fa4ad..30f307a2ca 100644
|
||||
--- a/sdk/android/src/jni/jni_helpers.h
|
||||
+++ b/sdk/android/src/jni/jni_helpers.h
|
||||
@@ -29,10 +29,10 @@
|
||||
// boundary. crbug.com/655248
|
||||
#define JNI_FUNCTION_DECLARATION(rettype, name, ...) \
|
||||
__attribute__((force_align_arg_pointer)) extern "C" JNIEXPORT rettype \
|
||||
- JNICALL Java_org_webrtc_##name(__VA_ARGS__)
|
||||
+ JNICALL Java_livekit_org_webrtc_##name(__VA_ARGS__)
|
||||
#else
|
||||
#define JNI_FUNCTION_DECLARATION(rettype, name, ...) \
|
||||
- extern "C" JNIEXPORT rettype JNICALL Java_org_webrtc_##name(__VA_ARGS__)
|
||||
+ extern "C" JNIEXPORT rettype JNICALL Java_livekit_org_webrtc_##name(__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
namespace webrtc {
|
||||
diff --git a/sdk/android/src/jni/simulcast_video_encoder.cc b/sdk/android/src/jni/simulcast_video_encoder.cc
|
||||
index 6d4ba5b3b6..48d3cb3541 100644
|
||||
--- a/sdk/android/src/jni/simulcast_video_encoder.cc
|
||||
+++ b/sdk/android/src/jni/simulcast_video_encoder.cc
|
||||
@@ -15,7 +15,14 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
// (VideoEncoderFactory primary, VideoEncoderFactory fallback, VideoCodecInfo info)
|
||||
-JNIEXPORT jlong JNICALL Java_org_webrtc_SimulcastVideoEncoder_nativeCreateEncoder(JNIEnv *env, jclass klass, jlong webrtcEnvRef, jobject primary, jobject fallback, jobject info) {
|
||||
+JNI_FUNCTION_DECLARATION(jlong,
|
||||
+ SimulcastVideoEncoder_nativeCreateEncoder,
|
||||
+ JNIEnv *env,
|
||||
+ jclass klass,
|
||||
+ jlong webrtcEnvRef,
|
||||
+ jobject primary,
|
||||
+ jobject fallback,
|
||||
+ jobject info) {
|
||||
RTC_LOG(LS_INFO) << "Create simulcast video encoder";
|
||||
auto info_ref = JavaParamRef<jobject>::CreateLeaky(env, info);
|
||||
SdpVideoFormat format = VideoCodecInfoToSdpVideoFormat(env, info_ref);
|
||||
diff --git a/tools_webrtc/android/build_aar.py b/tools_webrtc/android/build_aar.py
|
||||
index 2fbce97c60..130b3d5fa7 100755
|
||||
--- a/tools_webrtc/android/build_aar.py
|
||||
+++ b/tools_webrtc/android/build_aar.py
|
||||
@@ -254,7 +254,7 @@ def BuildAar(build_dir,
|
||||
Collect(aar_file, build_dir, arch, unstripped)
|
||||
|
||||
license_dir = os.path.dirname(os.path.realpath(output_file))
|
||||
- GenerateLicenses(license_dir, build_dir, archs)
|
||||
+ #GenerateLicenses(license_dir, build_dir, archs)
|
||||
|
||||
|
||||
def main():
|
||||
diff --git a/webrtc.gni b/webrtc.gni
|
||||
index 468ba33b01..ca81ce298c 100644
|
||||
--- a/webrtc.gni
|
||||
+++ b/webrtc.gni
|
||||
@@ -241,6 +241,10 @@ declare_args() {
|
||||
# hasn't been registered.
|
||||
rtc_strict_field_trials = ""
|
||||
|
||||
+ if (is_android) {
|
||||
+ android_package_prefix = "livekit"
|
||||
+ }
|
||||
+
|
||||
# If different from "", symbols exported with RTC_OBJC_EXPORT will be prefixed
|
||||
# with this string.
|
||||
# See the definition of RTC_OBJC_TYPE_PREFIX in the code.
|
||||
diff --git a/third_party/jni_zero/BUILD.gn b/third_party/jni_zero/BUILD.gn
|
||||
index 7056bdd033c1..2b97de476de7 100644
|
||||
--- a/third_party/jni_zero/BUILD.gn
|
||||
+++ b/third_party/jni_zero/BUILD.gn
|
||||
@@ -17,6 +17,7 @@ generate_jni("generate_jni") {
|
||||
":*",
|
||||
"//components/cronet/android/*",
|
||||
]
|
||||
+ package_prefix = "livekit"
|
||||
}
|
||||
|
||||
generate_jar_jni("system_jni") {
|
||||
diff --git a/third_party/jni_zero/codegen/header_common.py b/third_party/jni_zero/codegen/header_common.py
|
||||
index 338cd748079f..be2577281a91 100644
|
||||
--- a/third_party/jni_zero/codegen/header_common.py
|
||||
+++ b/third_party/jni_zero/codegen/header_common.py
|
||||
@@ -12,7 +12,8 @@ def class_accessors(sb, java_classes, module_name):
|
||||
for java_class in java_classes:
|
||||
if java_class in (java_types.OBJECT_CLASS, java_types.STRING_CLASS):
|
||||
continue
|
||||
- escaped_name = java_class.to_cpp()
|
||||
+ escaped_name = java_class.class_without_prefix.to_cpp()
|
||||
+ print("header accessor: ", escaped_name)
|
||||
# #ifdef needed when multple .h files are #included that common classes.
|
||||
sb(f"""\
|
||||
#ifndef {escaped_name}_clazz_defined
|
||||
@@ -44,7 +45,7 @@ def class_accessor_expression(java_class):
|
||||
if java_class == java_types.STRING_CLASS:
|
||||
return 'jni_zero::g_string_class'
|
||||
|
||||
- return f'{java_class.to_cpp()}_clazz(env)'
|
||||
+ return f'{java_class.class_without_prefix.to_cpp()}_clazz(env)'
|
||||
|
||||
|
||||
def header_preamble(script_name,
|
||||
@@ -53,7 +54,7 @@ def header_preamble(script_name,
|
||||
user_includes=None,
|
||||
header_guard=None):
|
||||
if header_guard is None:
|
||||
- header_guard = f'{java_class.to_cpp()}_JNI'
|
||||
+ header_guard = f'{java_class.class_without_prefix.to_cpp()}_JNI'
|
||||
sb = []
|
||||
sb.append(f"""\
|
||||
// This file was generated by
|
||||
diff --git a/third_party/jni_zero/jni_zero.gni b/third_party/jni_zero/jni_zero.gni
|
||||
index 71826b906d10..22d8eaf6e232 100644
|
||||
--- a/third_party/jni_zero/jni_zero.gni
|
||||
+++ b/third_party/jni_zero/jni_zero.gni
|
||||
@@ -123,7 +123,7 @@ template("_invoke_jni_zero") {
|
||||
# namespace: Registration functions will be wrapped into this. (optional)
|
||||
# priority_java_targets: List of java targets that, if using multiplexing,
|
||||
# will always be placed first in the sequential switch numbers. (optional)
|
||||
-#
|
||||
+# package_prefix: package prefix to add to jni
|
||||
# Example
|
||||
# generate_jni_registration("chrome_jni_registration") {
|
||||
# java_targets = [ ":chrome_public_apk" ]
|
||||
@@ -327,6 +327,7 @@ template("generate_jni_impl") {
|
||||
"deps",
|
||||
"metadata",
|
||||
"public_deps",
|
||||
+ "package_prefix",
|
||||
])
|
||||
if (!defined(public_deps)) {
|
||||
public_deps = []
|
||||
@@ -421,7 +422,9 @@ template("generate_jni_impl") {
|
||||
if (defined(invoker.split_name)) {
|
||||
args += [ "--split-name=${invoker.split_name}" ]
|
||||
}
|
||||
-
|
||||
+ if (defined(package_prefix)) {
|
||||
+ args += [ "--package-prefix=${package_prefix}" ]
|
||||
+ }
|
||||
foreach(_name, _input_names) {
|
||||
_name =
|
||||
string_replace(get_path_info(_name, "name"), "\$", "__") + "_jni.h"
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
diff --git a/rtc_base/boringssl_certificate.cc b/rtc_base/boringssl_certificate.cc
|
||||
index a4f919fbbe..fe3beeaed9 100644
|
||||
--- a/rtc_base/boringssl_certificate.cc
|
||||
+++ b/rtc_base/boringssl_certificate.cc
|
||||
@@ -252,6 +252,12 @@ BoringSSLCertificate::BoringSSLCertificate(
|
||||
RTC_DCHECK(cert_buffer_ != nullptr);
|
||||
}
|
||||
|
||||
+BoringSSLCertificate::BoringSSLCertificate(
|
||||
+ bssl::UniquePtr<CRYPTO_BUFFER> cert_buffer, SSL* ssl)
|
||||
+ : cert_buffer_(std::move(cert_buffer)), ssl_(ssl) {
|
||||
+ RTC_DCHECK(cert_buffer_ != nullptr);
|
||||
+}
|
||||
+
|
||||
std::unique_ptr<BoringSSLCertificate> BoringSSLCertificate::Generate(
|
||||
OpenSSLKeyPair* key_pair,
|
||||
const SSLIdentityParams& params) {
|
||||
diff --git a/rtc_base/boringssl_certificate.h b/rtc_base/boringssl_certificate.h
|
||||
index b5a18d0843..53759fd064 100644
|
||||
--- a/rtc_base/boringssl_certificate.h
|
||||
+++ b/rtc_base/boringssl_certificate.h
|
||||
@@ -34,6 +34,7 @@ namespace webrtc {
|
||||
class BoringSSLCertificate final : public SSLCertificate {
|
||||
public:
|
||||
explicit BoringSSLCertificate(bssl::UniquePtr<CRYPTO_BUFFER> cert_buffer);
|
||||
+ BoringSSLCertificate(bssl::UniquePtr<CRYPTO_BUFFER> cert_buffer, SSL* ssl);
|
||||
|
||||
static std::unique_ptr<BoringSSLCertificate> Generate(
|
||||
OpenSSLKeyPair* key_pair,
|
||||
@@ -66,6 +67,10 @@ class BoringSSLCertificate final : public SSLCertificate {
|
||||
private:
|
||||
// A handle to the DER encoded certificate data.
|
||||
bssl::UniquePtr<CRYPTO_BUFFER> cert_buffer_;
|
||||
+ SSL* ssl_ = nullptr;
|
||||
+
|
||||
+ public:
|
||||
+ SSL* ssl() const { return ssl_; }
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
diff --git a/rtc_base/openssl_adapter.cc b/rtc_base/openssl_adapter.cc
|
||||
index b80e665d78..1f3dd28795 100644
|
||||
--- a/rtc_base/openssl_adapter.cc
|
||||
+++ b/rtc_base/openssl_adapter.cc
|
||||
@@ -846,7 +846,7 @@ enum ssl_verify_result_t OpenSSLAdapter::SSLVerifyInternal(SSL* ssl,
|
||||
std::vector<std::unique_ptr<SSLCertificate>> certs;
|
||||
for (size_t i = 0; i < sk_CRYPTO_BUFFER_num(chain); ++i) {
|
||||
certs.emplace_back(new BoringSSLCertificate(
|
||||
- bssl::UpRef(sk_CRYPTO_BUFFER_value(chain, i))));
|
||||
+ bssl::UpRef(sk_CRYPTO_BUFFER_value(chain, i)), ssl));
|
||||
}
|
||||
|
||||
SSLCertChain cert_chain(std::move(certs));
|
||||
@@ -932,7 +932,7 @@ int OpenSSLAdapter::SSLVerifyInternal(int previous_status,
|
||||
RTC_LOG(LS_ERROR) << "Failed to allocate CRYPTO_BUFFER.";
|
||||
return previous_status;
|
||||
}
|
||||
- certs.emplace_back(new BoringSSLCertificate(std::move(crypto_buffer)));
|
||||
+ certs.emplace_back(new BoringSSLCertificate(std::move(crypto_buffer), ssl));
|
||||
#else
|
||||
certs.emplace_back(new OpenSSLCertificate(x509_cert));
|
||||
#endif
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn
|
||||
index 8048ec5e8..4e00f7e53 100644
|
||||
--- a/build/config/compiler/BUILD.gn
|
||||
+++ b/build/config/compiler/BUILD.gn
|
||||
@@ -1424,6 +1424,9 @@ config("default_warnings") {
|
||||
# gethostbyname. Fires mostly in non-Chromium code. We probably
|
||||
# want to remove this define eventually.
|
||||
"_WINSOCK_DEPRECATED_NO_WARNINGS",
|
||||
+
|
||||
+ "_SILENCE_ALL_CXX17_DEPRECATION_WARNINGS",
|
||||
+ "_SILENCE_ALL_CXX20_DEPRECATION_WARNINGS",
|
||||
]
|
||||
if (!is_clang) {
|
||||
# TODO(thakis): Remove this once
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
plugins {
|
||||
id 'java-library'
|
||||
id 'com.github.johnrengelman.shadow' version '7.1.2'
|
||||
}
|
||||
|
||||
java {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_7
|
||||
targetCompatibility = JavaVersion.VERSION_1_7
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api files("libs/classes.jar")
|
||||
}
|
||||
|
||||
shadowJar {
|
||||
|
||||
}
|
||||
import com.github.jengelman.gradle.plugins.shadow.tasks.ConfigureShadowRelocation
|
||||
|
||||
task relocateShadowJar(type: ConfigureShadowRelocation) {
|
||||
target = tasks.shadowJar
|
||||
prefix = "livekit" // Default value is "shadow"
|
||||
}
|
||||
tasks.shadowJar.dependsOn tasks.relocateShadowJar
|
||||
BIN
Binary file not shown.
+6
@@ -0,0 +1,6 @@
|
||||
#Thu Apr 29 14:50:17 JST 2021
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
|
||||
distributionPath=wrapper/dists
|
||||
zipStorePath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
Vendored
Executable
+172
@@ -0,0 +1,172 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS=""
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin, switch paths to Windows format before running java
|
||||
if $cygwin ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=$((i+1))
|
||||
done
|
||||
case $i in
|
||||
(0) set -- ;;
|
||||
(1) set -- "$args0" ;;
|
||||
(2) set -- "$args0" "$args1" ;;
|
||||
(3) set -- "$args0" "$args1" "$args2" ;;
|
||||
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=$(save "$@")
|
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
|
||||
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
|
||||
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
|
||||
cd "$(dirname "$0")"
|
||||
fi
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS=
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:init
|
||||
@rem Get command-line arguments, handling Windows variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||
|
||||
:win9xME_args
|
||||
@rem Slurp the command line arguments.
|
||||
set CMD_LINE_ARGS=
|
||||
set _SKIP=2
|
||||
|
||||
:win9xME_args_slurp
|
||||
if "x%~1" == "x" goto execute
|
||||
|
||||
set CMD_LINE_ARGS=%*
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
Vendored
+14
@@ -0,0 +1,14 @@
|
||||
pluginManagement {
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
gradlePluginPortal()
|
||||
}
|
||||
}
|
||||
dependencyResolutionManagement {
|
||||
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,926 @@
|
||||
/*
|
||||
* Copyright 2026 LiveKit, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "livekit/adm_proxy.h"
|
||||
|
||||
#include "api/audio/audio_device.h"
|
||||
#include "api/audio/create_audio_device_module.h"
|
||||
#include "api/make_ref_counted.h"
|
||||
#include "rtc_base/logging.h"
|
||||
#include "rtc_base/thread.h"
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
#include <jni.h>
|
||||
#include "sdk/android/native_api/audio_device_module/audio_device_android.h"
|
||||
#include "sdk/android/native_api/base/init.h"
|
||||
#endif
|
||||
|
||||
namespace livekit_ffi {
|
||||
|
||||
AdmProxy::AdmProxy(const webrtc::Environment& env, webrtc::Thread* worker_thread)
|
||||
: env_(env),
|
||||
worker_thread_(worker_thread) {
|
||||
// Create the synthetic ADM for synthetic mode. SyntheticAudioDevice pumps
|
||||
// the WebRTC audio pipeline without platform audio, allowing FFI callbacks
|
||||
// to receive decoded remote audio.
|
||||
synthetic_adm_ = webrtc::make_ref_counted<SyntheticAudioDevice>(env_);
|
||||
if (synthetic_adm_->Init() != 0) {
|
||||
RTC_LOG(LS_ERROR) << "AdmProxy: Failed to initialize synthetic ADM";
|
||||
}
|
||||
|
||||
// Attempt to create the Platform ADM for real audio I/O.
|
||||
// The eager attempt exists for iOS compatibility: the iOS audio session
|
||||
// requires early setup to avoid KVO race conditions.
|
||||
// On Android, we defer Platform ADM creation to AcquirePlatformAdm().
|
||||
// This is because:
|
||||
// 1. CreateAudioDeviceModule requires JNI to be fully initialized
|
||||
// 2. The JNI initialization (via JNI_OnLoad or manual init) may not have
|
||||
// completed by the time the AdmProxy constructor runs
|
||||
// 3. Deferring creation ensures JNI is ready when we actually need the ADM
|
||||
// On all platforms a failed attempt is retried later by
|
||||
// EnsurePlatformAdmCreated(): desktop apps launched at login can race the
|
||||
// OS audio stack (coreaudiod, Windows audio services), and a one-shot
|
||||
// creation would leave audio permanently broken for the process lifetime.
|
||||
#if defined(__ANDROID__)
|
||||
// platform_adm_ stays nullptr, will be created in EnsurePlatformAdmCreated()
|
||||
#else
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
if (!EnsurePlatformAdmCreated()) {
|
||||
RTC_LOG(LS_WARNING)
|
||||
<< "AdmProxy: Platform ADM unavailable at construction; will retry on demand";
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
AdmProxy::~AdmProxy() {
|
||||
RTC_LOG(LS_VERBOSE) << "AdmProxy::~AdmProxy()";
|
||||
|
||||
if (synthetic_adm_) {
|
||||
synthetic_adm_->Terminate();
|
||||
synthetic_adm_ = nullptr;
|
||||
}
|
||||
|
||||
if (platform_adm_) {
|
||||
platform_adm_->Terminate();
|
||||
platform_adm_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Helper Methods
|
||||
// =============================================================================
|
||||
|
||||
bool AdmProxy::is_platform_playout_active() const {
|
||||
// Platform playout is active when: ref_count > 0 AND playout explicitly enabled.
|
||||
// Otherwise, synthetic mode handles playout via the internal pumping task.
|
||||
return platform_adm_ && platform_adm_ref_count_ > 0 && playout_enabled_;
|
||||
}
|
||||
|
||||
webrtc::AudioDeviceModule* AdmProxy::recording_adm() const {
|
||||
// Recording only available through platform ADM when enabled.
|
||||
// Synthetic mode doesn't support recording (no microphone).
|
||||
if (platform_adm_ && platform_adm_ref_count_ > 0 && recording_enabled_) {
|
||||
return platform_adm_.get();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Platform ADM Lifecycle Management
|
||||
// =============================================================================
|
||||
|
||||
// Lazily creates the Platform ADM. Must be called with mutex held.
|
||||
// Returns true if ADM is available (either already existed or successfully created).
|
||||
// A failed attempt leaves platform_adm_ null so the next call retries; the OS
|
||||
// audio stack may simply not be ready yet (cold boot, login launch).
|
||||
bool AdmProxy::EnsurePlatformAdmCreated() {
|
||||
if (platform_adm_) {
|
||||
return true; // Already created
|
||||
}
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
// Use CreateAndroidAudioDeviceModule which properly uses GetAppContext()
|
||||
// to get the application context set via ContextUtils.initialize().
|
||||
platform_adm_ = webrtc::CreateAndroidAudioDeviceModule(
|
||||
env_, webrtc::AudioDeviceModule::kPlatformDefaultAudio);
|
||||
|
||||
if (!platform_adm_) {
|
||||
RTC_LOG(LS_ERROR) << "AdmProxy: CreateAndroidAudioDeviceModule returned nullptr. "
|
||||
<< "Ensure ContextUtils.initialize() was called.";
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
platform_adm_ = webrtc::CreateAudioDeviceModule(
|
||||
env_, webrtc::AudioDeviceModule::kPlatformDefaultAudio);
|
||||
|
||||
if (!platform_adm_) {
|
||||
RTC_LOG(LS_ERROR) << "AdmProxy: CreateAudioDeviceModule returned nullptr";
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
int32_t init_result = platform_adm_->Init();
|
||||
if (init_result != 0) {
|
||||
RTC_LOG(LS_ERROR) << "AdmProxy: Platform ADM Init() failed with error=" << init_result;
|
||||
platform_adm_ = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
RestorePlatformAdmStateLocked();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Re-applies state that may have been recorded before the Platform ADM
|
||||
// existed: the audio transport registered by WebRTC and any device selection.
|
||||
// Without this, an ADM created after RegisterAudioCallback() would never
|
||||
// receive audio data.
|
||||
void AdmProxy::RestorePlatformAdmStateLocked() {
|
||||
RTC_DCHECK(platform_adm_);
|
||||
if (audio_transport_) {
|
||||
recording_transport_proxy_.set_real_transport(audio_transport_);
|
||||
platform_adm_->RegisterAudioCallback(&recording_transport_proxy_);
|
||||
}
|
||||
if (!selected_playout_guid_.empty()) {
|
||||
int16_t count = platform_adm_->PlayoutDevices();
|
||||
for (int16_t i = 0; i < count; i++) {
|
||||
char name[webrtc::kAdmMaxDeviceNameSize] = {0};
|
||||
char guid[webrtc::kAdmMaxGuidSize] = {0};
|
||||
if (platform_adm_->PlayoutDeviceName(static_cast<uint16_t>(i), name, guid) == 0 &&
|
||||
selected_playout_guid_ == guid) {
|
||||
platform_adm_->SetPlayoutDevice(static_cast<uint16_t>(i));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!selected_recording_guid_.empty()) {
|
||||
int16_t count = platform_adm_->RecordingDevices();
|
||||
for (int16_t i = 0; i < count; i++) {
|
||||
char name[webrtc::kAdmMaxDeviceNameSize] = {0};
|
||||
char guid[webrtc::kAdmMaxGuidSize] = {0};
|
||||
if (platform_adm_->RecordingDeviceName(static_cast<uint16_t>(i), name, guid) == 0 &&
|
||||
selected_recording_guid_ == guid) {
|
||||
platform_adm_->SetRecordingDevice(static_cast<uint16_t>(i));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool AdmProxy::EnsurePlatformAdm() {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
return EnsurePlatformAdmCreated();
|
||||
}
|
||||
|
||||
bool AdmProxy::platform_adm_available() const {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
return platform_adm_ != nullptr;
|
||||
}
|
||||
|
||||
bool AdmProxy::AcquirePlatformAdm() {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
|
||||
// Lazily create the Platform ADM on first acquire (and retry after any
|
||||
// earlier failed attempt, e.g. when the OS audio stack was still starting).
|
||||
if (!EnsurePlatformAdmCreated()) {
|
||||
RTC_LOG(LS_ERROR) << "AdmProxy::AcquirePlatformAdm() - Failed to create Platform ADM";
|
||||
return false;
|
||||
}
|
||||
|
||||
int old_ref_count = platform_adm_ref_count_;
|
||||
platform_adm_ref_count_++;
|
||||
|
||||
// If this is the first acquisition and playout/recording is enabled,
|
||||
// we may need to switch from synthetic mode to platform ADM
|
||||
if (old_ref_count == 0) {
|
||||
SwitchPlayoutModeIfNeeded();
|
||||
SwitchRecordingAdmIfNeeded();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void AdmProxy::ReleasePlatformAdm() {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
|
||||
if (platform_adm_ref_count_ <= 0) {
|
||||
RTC_LOG(LS_WARNING) << "AdmProxy::ReleasePlatformAdm() called with ref_count="
|
||||
<< platform_adm_ref_count_;
|
||||
return;
|
||||
}
|
||||
|
||||
platform_adm_ref_count_--;
|
||||
|
||||
// If ref_count reaches 0, switch back from platform ADM to synthetic mode
|
||||
// Note: We do NOT terminate the Platform ADM - it stays alive until destructor.
|
||||
// This avoids iOS KVO race conditions from re-creating the ADM.
|
||||
if (platform_adm_ref_count_ == 0) {
|
||||
SwitchPlayoutModeIfNeeded();
|
||||
SwitchRecordingAdmIfNeeded();
|
||||
}
|
||||
}
|
||||
|
||||
int AdmProxy::platform_adm_ref_count() const {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
return platform_adm_ref_count_;
|
||||
}
|
||||
|
||||
bool AdmProxy::is_platform_adm_active() const {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
// Platform ADM is considered active when there are users and playout/recording is enabled
|
||||
return platform_adm_ != nullptr && platform_adm_ref_count_ > 0;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Recording/Playout Control
|
||||
// =============================================================================
|
||||
|
||||
void AdmProxy::set_recording_enabled(bool enabled) {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
if (recording_enabled_ == enabled) {
|
||||
return;
|
||||
}
|
||||
recording_enabled_ = enabled;
|
||||
SwitchRecordingAdmIfNeeded();
|
||||
}
|
||||
|
||||
bool AdmProxy::recording_enabled() const {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
return recording_enabled_;
|
||||
}
|
||||
|
||||
void AdmProxy::set_playout_enabled(bool enabled) {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
if (playout_enabled_ == enabled) {
|
||||
return;
|
||||
}
|
||||
playout_enabled_ = enabled;
|
||||
SwitchPlayoutModeIfNeeded();
|
||||
}
|
||||
|
||||
bool AdmProxy::playout_enabled() const {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
return playout_enabled_;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Mode Switching Helpers (called with mutex held)
|
||||
// =============================================================================
|
||||
|
||||
void AdmProxy::SwitchPlayoutModeIfNeeded() {
|
||||
if (!playing_) return;
|
||||
|
||||
bool use_platform = is_platform_playout_active();
|
||||
|
||||
if (use_platform) {
|
||||
// Switch to platform mode - stop synthetic, start platform ADM
|
||||
if (synthetic_adm_) {
|
||||
synthetic_adm_->StopPlayout();
|
||||
}
|
||||
if (platform_adm_) {
|
||||
platform_adm_->InitPlayout();
|
||||
platform_adm_->StartPlayout();
|
||||
}
|
||||
} else {
|
||||
// Switch to synthetic mode - stop platform ADM, start synthetic ADM
|
||||
if (platform_adm_) {
|
||||
platform_adm_->StopPlayout();
|
||||
}
|
||||
if (synthetic_adm_) {
|
||||
synthetic_adm_->StartPlayout();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AdmProxy::SwitchRecordingAdmIfNeeded() {
|
||||
if (!recording_) return;
|
||||
|
||||
// Stop platform ADM recording (only one that supports recording)
|
||||
if (platform_adm_) platform_adm_->StopRecording();
|
||||
|
||||
// Start if new ADM supports recording
|
||||
auto* adm = recording_adm();
|
||||
if (adm) {
|
||||
adm->InitRecording();
|
||||
adm->StartRecording();
|
||||
} else {
|
||||
recording_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// AudioDeviceModule Interface Implementation
|
||||
// =============================================================================
|
||||
|
||||
int32_t AdmProxy::ActiveAudioLayer(AudioLayer* audioLayer) const {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
if (platform_adm_) {
|
||||
return platform_adm_->ActiveAudioLayer(audioLayer);
|
||||
}
|
||||
*audioLayer = AudioLayer::kDummyAudio;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t AdmProxy::RegisterAudioCallback(webrtc::AudioTransport* transport) {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
audio_transport_ = transport;
|
||||
recording_transport_proxy_.set_real_transport(transport);
|
||||
|
||||
// Register the interposing proxy with both ADMs so they're ready when we
|
||||
// switch modes. The proxy tees recorded frames and forwards to `transport`.
|
||||
webrtc::AudioTransport* proxy = &recording_transport_proxy_;
|
||||
if (synthetic_adm_) {
|
||||
synthetic_adm_->RegisterAudioCallback(proxy);
|
||||
}
|
||||
if (platform_adm_) {
|
||||
platform_adm_->RegisterAudioCallback(proxy);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t AdmProxy::Init() {
|
||||
// Init is a no-op - Platform ADM is created lazily via AcquirePlatformAdm()
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t AdmProxy::Terminate() {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
|
||||
int32_t result = 0;
|
||||
if (synthetic_adm_) {
|
||||
result = synthetic_adm_->Terminate();
|
||||
}
|
||||
if (platform_adm_) {
|
||||
int32_t platform_result = platform_adm_->Terminate();
|
||||
if (result == 0) result = platform_result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool AdmProxy::Initialized() const {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
// We're initialized if at least one ADM is initialized
|
||||
bool synthetic_init = synthetic_adm_ && synthetic_adm_->Initialized();
|
||||
bool platform_init = platform_adm_ && platform_adm_->Initialized();
|
||||
return synthetic_init || platform_init;
|
||||
}
|
||||
|
||||
int16_t AdmProxy::PlayoutDevices() {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
if (platform_adm_) {
|
||||
return platform_adm_->PlayoutDevices();
|
||||
}
|
||||
// In synthetic mode, return 0 devices (no platform audio)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int16_t AdmProxy::RecordingDevices() {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
if (platform_adm_) {
|
||||
return platform_adm_->RecordingDevices();
|
||||
}
|
||||
// In synthetic mode, return 0 devices (no platform audio)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t AdmProxy::PlayoutDeviceName(uint16_t index,
|
||||
char name[webrtc::kAdmMaxDeviceNameSize],
|
||||
char guid[webrtc::kAdmMaxGuidSize]) {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
if (platform_adm_) {
|
||||
return platform_adm_->PlayoutDeviceName(index, name, guid);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int32_t AdmProxy::RecordingDeviceName(uint16_t index,
|
||||
char name[webrtc::kAdmMaxDeviceNameSize],
|
||||
char guid[webrtc::kAdmMaxGuidSize]) {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
if (platform_adm_) {
|
||||
return platform_adm_->RecordingDeviceName(index, name, guid);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int32_t AdmProxy::SetPlayoutDevice(uint16_t index) {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
selected_playout_device_ = index;
|
||||
|
||||
// Also store the GUID for this device for robust restoration
|
||||
if (platform_adm_) {
|
||||
char name[webrtc::kAdmMaxDeviceNameSize] = {0};
|
||||
char guid[webrtc::kAdmMaxGuidSize] = {0};
|
||||
if (platform_adm_->PlayoutDeviceName(index, name, guid) == 0) {
|
||||
selected_playout_guid_ = guid;
|
||||
}
|
||||
return platform_adm_->SetPlayoutDevice(index);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t AdmProxy::SetPlayoutDevice(WindowsDeviceType device) {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
// Note: When using WindowsDeviceType, we can't easily get the GUID
|
||||
// The GUID will be populated on next CreatePlatformAdm if needed
|
||||
selected_playout_guid_.clear();
|
||||
if (platform_adm_) {
|
||||
return platform_adm_->SetPlayoutDevice(device);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t AdmProxy::SetRecordingDevice(uint16_t index) {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
selected_recording_device_ = index;
|
||||
|
||||
// Also store the GUID for this device for robust restoration
|
||||
if (platform_adm_) {
|
||||
char name[webrtc::kAdmMaxDeviceNameSize] = {0};
|
||||
char guid[webrtc::kAdmMaxGuidSize] = {0};
|
||||
if (platform_adm_->RecordingDeviceName(index, name, guid) == 0) {
|
||||
selected_recording_guid_ = guid;
|
||||
}
|
||||
return platform_adm_->SetRecordingDevice(index);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t AdmProxy::SetRecordingDevice(WindowsDeviceType device) {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
// Note: When using WindowsDeviceType, we can't easily get the GUID
|
||||
// The GUID will be populated on next CreatePlatformAdm if needed
|
||||
selected_recording_guid_.clear();
|
||||
if (platform_adm_) {
|
||||
return platform_adm_->SetRecordingDevice(device);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t AdmProxy::PlayoutIsAvailable(bool* available) {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
if (platform_adm_) {
|
||||
return platform_adm_->PlayoutIsAvailable(available);
|
||||
}
|
||||
*available = true; // Synthetic playout is always available
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t AdmProxy::InitPlayout() {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
|
||||
if (is_platform_playout_active()) {
|
||||
if (platform_adm_) {
|
||||
int32_t result = platform_adm_->InitPlayout();
|
||||
if (result == 0) {
|
||||
playout_initialized_ = true;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Synthetic mode
|
||||
if (synthetic_adm_) {
|
||||
int32_t result = synthetic_adm_->InitPlayout();
|
||||
if (result == 0) {
|
||||
playout_initialized_ = true;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool AdmProxy::PlayoutIsInitialized() const {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
if (is_platform_playout_active()) {
|
||||
return platform_adm_ && platform_adm_->PlayoutIsInitialized();
|
||||
}
|
||||
// Synthetic mode
|
||||
return synthetic_adm_ && synthetic_adm_->PlayoutIsInitialized();
|
||||
}
|
||||
|
||||
int32_t AdmProxy::RecordingIsAvailable(bool* available) {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
if (platform_adm_) {
|
||||
return platform_adm_->RecordingIsAvailable(available);
|
||||
}
|
||||
*available = false; // Recording not available in synthetic mode
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t AdmProxy::InitRecording() {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
|
||||
auto* adm = recording_adm();
|
||||
if (!adm) {
|
||||
// Recording not available (no platform ADM or recording disabled)
|
||||
// Return success to avoid breaking WebRTC's initialization flow
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t result = adm->InitRecording();
|
||||
if (result == 0) {
|
||||
recording_initialized_ = true;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool AdmProxy::RecordingIsInitialized() const {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
auto* adm = recording_adm();
|
||||
if (adm) {
|
||||
return adm->RecordingIsInitialized();
|
||||
}
|
||||
return false; // Recording not available
|
||||
}
|
||||
|
||||
int32_t AdmProxy::StartPlayout() {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
playing_ = true;
|
||||
|
||||
if (is_platform_playout_active()) {
|
||||
if (platform_adm_) {
|
||||
return platform_adm_->StartPlayout();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Synthetic mode
|
||||
if (synthetic_adm_) {
|
||||
return synthetic_adm_->StartPlayout();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int32_t AdmProxy::StopPlayout() {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
playing_ = false;
|
||||
|
||||
// Stop both ADMs
|
||||
if (synthetic_adm_) {
|
||||
synthetic_adm_->StopPlayout();
|
||||
}
|
||||
if (platform_adm_) {
|
||||
platform_adm_->StopPlayout();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool AdmProxy::Playing() const {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
if (is_platform_playout_active()) {
|
||||
return platform_adm_ && platform_adm_->Playing();
|
||||
}
|
||||
return synthetic_adm_ && synthetic_adm_->Playing();
|
||||
}
|
||||
|
||||
int32_t AdmProxy::StartRecording() {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
|
||||
auto* adm = recording_adm();
|
||||
if (!adm) {
|
||||
// Recording not available - return success to avoid breaking WebRTC
|
||||
return 0;
|
||||
}
|
||||
|
||||
recording_ = true;
|
||||
return adm->StartRecording();
|
||||
}
|
||||
|
||||
int32_t AdmProxy::StopRecording() {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
recording_ = false;
|
||||
|
||||
auto* adm = recording_adm();
|
||||
if (adm) {
|
||||
return adm->StopRecording();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool AdmProxy::Recording() const {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
auto* adm = recording_adm();
|
||||
if (adm) {
|
||||
return adm->Recording();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int32_t AdmProxy::InitSpeaker() {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
if (platform_adm_) {
|
||||
return platform_adm_->InitSpeaker();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool AdmProxy::SpeakerIsInitialized() const {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
if (platform_adm_) {
|
||||
return platform_adm_->SpeakerIsInitialized();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int32_t AdmProxy::InitMicrophone() {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
if (platform_adm_) {
|
||||
return platform_adm_->InitMicrophone();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool AdmProxy::MicrophoneIsInitialized() const {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
if (platform_adm_) {
|
||||
return platform_adm_->MicrophoneIsInitialized();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int32_t AdmProxy::SpeakerVolumeIsAvailable(bool* available) {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
if (platform_adm_) {
|
||||
return platform_adm_->SpeakerVolumeIsAvailable(available);
|
||||
}
|
||||
*available = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t AdmProxy::SetSpeakerVolume(uint32_t volume) {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
if (platform_adm_) {
|
||||
return platform_adm_->SetSpeakerVolume(volume);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int32_t AdmProxy::SpeakerVolume(uint32_t* volume) const {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
if (platform_adm_) {
|
||||
return platform_adm_->SpeakerVolume(volume);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int32_t AdmProxy::MaxSpeakerVolume(uint32_t* maxVolume) const {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
if (platform_adm_) {
|
||||
return platform_adm_->MaxSpeakerVolume(maxVolume);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int32_t AdmProxy::MinSpeakerVolume(uint32_t* minVolume) const {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
if (platform_adm_) {
|
||||
return platform_adm_->MinSpeakerVolume(minVolume);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int32_t AdmProxy::MicrophoneVolumeIsAvailable(bool* available) {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
if (platform_adm_) {
|
||||
return platform_adm_->MicrophoneVolumeIsAvailable(available);
|
||||
}
|
||||
*available = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t AdmProxy::SetMicrophoneVolume(uint32_t volume) {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
if (platform_adm_) {
|
||||
return platform_adm_->SetMicrophoneVolume(volume);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int32_t AdmProxy::MicrophoneVolume(uint32_t* volume) const {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
if (platform_adm_) {
|
||||
return platform_adm_->MicrophoneVolume(volume);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int32_t AdmProxy::MaxMicrophoneVolume(uint32_t* maxVolume) const {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
if (platform_adm_) {
|
||||
return platform_adm_->MaxMicrophoneVolume(maxVolume);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int32_t AdmProxy::MinMicrophoneVolume(uint32_t* minVolume) const {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
if (platform_adm_) {
|
||||
return platform_adm_->MinMicrophoneVolume(minVolume);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int32_t AdmProxy::SpeakerMuteIsAvailable(bool* available) {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
if (platform_adm_) {
|
||||
return platform_adm_->SpeakerMuteIsAvailable(available);
|
||||
}
|
||||
*available = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t AdmProxy::SetSpeakerMute(bool enable) {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
if (platform_adm_) {
|
||||
return platform_adm_->SetSpeakerMute(enable);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int32_t AdmProxy::SpeakerMute(bool* enabled) const {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
if (platform_adm_) {
|
||||
return platform_adm_->SpeakerMute(enabled);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int32_t AdmProxy::MicrophoneMuteIsAvailable(bool* available) {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
if (platform_adm_) {
|
||||
return platform_adm_->MicrophoneMuteIsAvailable(available);
|
||||
}
|
||||
*available = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t AdmProxy::SetMicrophoneMute(bool enable) {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
if (platform_adm_) {
|
||||
return platform_adm_->SetMicrophoneMute(enable);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int32_t AdmProxy::MicrophoneMute(bool* enabled) const {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
if (platform_adm_) {
|
||||
return platform_adm_->MicrophoneMute(enabled);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int32_t AdmProxy::StereoPlayoutIsAvailable(bool* available) const {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
if (platform_adm_) {
|
||||
return platform_adm_->StereoPlayoutIsAvailable(available);
|
||||
}
|
||||
*available = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t AdmProxy::SetStereoPlayout(bool enable) {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
if (platform_adm_) {
|
||||
return platform_adm_->SetStereoPlayout(enable);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t AdmProxy::StereoPlayout(bool* enabled) const {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
if (platform_adm_) {
|
||||
return platform_adm_->StereoPlayout(enabled);
|
||||
}
|
||||
*enabled = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t AdmProxy::StereoRecordingIsAvailable(bool* available) const {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
if (platform_adm_) {
|
||||
return platform_adm_->StereoRecordingIsAvailable(available);
|
||||
}
|
||||
*available = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t AdmProxy::SetStereoRecording(bool enable) {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
if (platform_adm_) {
|
||||
return platform_adm_->SetStereoRecording(enable);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t AdmProxy::StereoRecording(bool* enabled) const {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
if (platform_adm_) {
|
||||
return platform_adm_->StereoRecording(enabled);
|
||||
}
|
||||
*enabled = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t AdmProxy::PlayoutDelay(uint16_t* delayMS) const {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
if (is_platform_playout_active()) {
|
||||
if (platform_adm_) {
|
||||
return platform_adm_->PlayoutDelay(delayMS);
|
||||
}
|
||||
} else if (synthetic_adm_) {
|
||||
return synthetic_adm_->PlayoutDelay(delayMS);
|
||||
}
|
||||
*delayMS = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool AdmProxy::BuiltInAECIsAvailable() const {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
if (platform_adm_) {
|
||||
return platform_adm_->BuiltInAECIsAvailable();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AdmProxy::BuiltInAGCIsAvailable() const {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
if (platform_adm_) {
|
||||
return platform_adm_->BuiltInAGCIsAvailable();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AdmProxy::BuiltInNSIsAvailable() const {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
if (platform_adm_) {
|
||||
return platform_adm_->BuiltInNSIsAvailable();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int32_t AdmProxy::EnableBuiltInAEC(bool enable) {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
if (platform_adm_) {
|
||||
return platform_adm_->EnableBuiltInAEC(enable);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int32_t AdmProxy::EnableBuiltInAGC(bool enable) {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
if (platform_adm_) {
|
||||
return platform_adm_->EnableBuiltInAGC(enable);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int32_t AdmProxy::EnableBuiltInNS(bool enable) {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
if (platform_adm_) {
|
||||
return platform_adm_->EnableBuiltInNS(enable);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
#if defined(WEBRTC_IOS)
|
||||
int AdmProxy::GetPlayoutAudioParameters(webrtc::AudioParameters* params) const {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
if (platform_adm_) {
|
||||
return platform_adm_->GetPlayoutAudioParameters(params);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int AdmProxy::GetRecordAudioParameters(webrtc::AudioParameters* params) const {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
if (platform_adm_) {
|
||||
return platform_adm_->GetRecordAudioParameters(params);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
int32_t AdmProxy::SetObserver(webrtc::AudioDeviceObserver* observer) {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
if (platform_adm_) {
|
||||
return platform_adm_->SetObserver(observer);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace livekit_ffi
|
||||
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* Copyright 2025 LiveKit, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "livekit/android.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <jni.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <memory>
|
||||
|
||||
#include "api/video_codecs/video_decoder_factory.h"
|
||||
#include "rtc_base/logging.h"
|
||||
#include "sdk/android/native_api/base/init.h"
|
||||
#include "sdk/android/native_api/codecs/wrapper.h"
|
||||
#include "sdk/android/native_api/jni/class_loader.h"
|
||||
#include "sdk/android/native_api/jni/scoped_java_ref.h"
|
||||
#include "sdk/android/src/jni/jni_helpers.h"
|
||||
|
||||
// When compiling the examples app on Android, the linker complains that
|
||||
// `stdout` and `stderr` symbols cannot be found. The previous workaround
|
||||
// referenced `__sF`, but that symbol was removed in NDK 28+.
|
||||
// Use POSIX `fdopen()` with the standard file descriptors instead — works
|
||||
// across all NDK versions.
|
||||
#undef stdout
|
||||
FILE *stdout = fdopen(STDOUT_FILENO, "w");
|
||||
|
||||
#undef stderr
|
||||
FILE *stderr = fdopen(STDERR_FILENO, "w");
|
||||
|
||||
namespace livekit_ffi {
|
||||
|
||||
// Track whether Android WebRTC has been initialized to prevent crashes on double-init.
|
||||
static std::atomic<bool> g_android_initialized{false};
|
||||
|
||||
void init_android(JavaVM* jvm) {
|
||||
// Idempotent - safe to call multiple times
|
||||
if (g_android_initialized.exchange(true)) {
|
||||
RTC_LOG(LS_INFO) << "livekit_ffi::init_android() - already initialized, skipping";
|
||||
return;
|
||||
}
|
||||
|
||||
RTC_LOG(LS_INFO) << "livekit_ffi::init_android() called with jvm=" << (jvm ? "valid" : "null");
|
||||
if (!jvm) {
|
||||
RTC_LOG(LS_ERROR) << "livekit_ffi::init_android() - JavaVM is null! Cannot initialize Android WebRTC.";
|
||||
g_android_initialized.store(false);
|
||||
return;
|
||||
}
|
||||
webrtc::InitAndroid(jvm);
|
||||
RTC_LOG(LS_INFO) << "livekit_ffi::init_android() - webrtc::InitAndroid() completed";
|
||||
}
|
||||
|
||||
bool init_android_context(JavaVM* jvm, uintptr_t context_ptr) {
|
||||
RTC_LOG(LS_INFO) << "livekit_ffi::init_android_context() called";
|
||||
|
||||
if (!jvm || !context_ptr) {
|
||||
RTC_LOG(LS_ERROR) << "livekit_ffi::init_android_context() - jvm or context is null";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Initialize JVM first (idempotent - WebRTC handles double-init internally)
|
||||
init_android(jvm);
|
||||
|
||||
// Cast uintptr_t back to jobject
|
||||
jobject context = reinterpret_cast<jobject>(context_ptr);
|
||||
|
||||
JNIEnv* env = webrtc::AttachCurrentThreadIfNeeded();
|
||||
if (!env) {
|
||||
RTC_LOG(LS_ERROR) << "livekit_ffi::init_android_context() - Failed to attach to JNI";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Find livekit.org.webrtc.ContextUtils class
|
||||
jclass context_utils_class = env->FindClass("livekit/org/webrtc/ContextUtils");
|
||||
if (!context_utils_class) {
|
||||
RTC_LOG(LS_ERROR) << "livekit_ffi::init_android_context() - Failed to find ContextUtils class";
|
||||
env->ExceptionClear();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the initialize method
|
||||
jmethodID initialize_method = env->GetStaticMethodID(
|
||||
context_utils_class, "initialize", "(Landroid/content/Context;)V");
|
||||
if (!initialize_method) {
|
||||
RTC_LOG(LS_ERROR) << "livekit_ffi::init_android_context() - Failed to find initialize method";
|
||||
env->ExceptionClear();
|
||||
env->DeleteLocalRef(context_utils_class);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Call ContextUtils.initialize(context)
|
||||
env->CallStaticVoidMethod(context_utils_class, initialize_method, context);
|
||||
|
||||
// Check for exceptions
|
||||
if (env->ExceptionCheck()) {
|
||||
RTC_LOG(LS_ERROR) << "livekit_ffi::init_android_context() - Exception during initialize";
|
||||
env->ExceptionDescribe();
|
||||
env->ExceptionClear();
|
||||
env->DeleteLocalRef(context_utils_class);
|
||||
return false;
|
||||
}
|
||||
|
||||
env->DeleteLocalRef(context_utils_class);
|
||||
RTC_LOG(LS_INFO) << "livekit_ffi::init_android_context() - ContextUtils initialized successfully";
|
||||
return true;
|
||||
}
|
||||
|
||||
std::unique_ptr<webrtc::VideoEncoderFactory>
|
||||
CreateAndroidVideoEncoderFactory() {
|
||||
JNIEnv* env = webrtc::AttachCurrentThreadIfNeeded();
|
||||
webrtc::ScopedJavaLocalRef<jclass> factory_class =
|
||||
webrtc::GetClass(env, "livekit/org/webrtc/DefaultVideoEncoderFactory");
|
||||
|
||||
jmethodID ctor = env->GetMethodID(factory_class.obj(), "<init>",
|
||||
"(Llivekit/org/webrtc/EglBase$Context;ZZ)V");
|
||||
|
||||
jobject encoder_factory =
|
||||
env->NewObject(factory_class.obj(), ctor, nullptr, true, false);
|
||||
|
||||
return webrtc::JavaToNativeVideoEncoderFactory(env, encoder_factory);
|
||||
}
|
||||
|
||||
std::unique_ptr<webrtc::VideoDecoderFactory>
|
||||
CreateAndroidVideoDecoderFactory() {
|
||||
JNIEnv* env = webrtc::AttachCurrentThreadIfNeeded();
|
||||
|
||||
webrtc::ScopedJavaLocalRef<jclass> factory_class =
|
||||
webrtc::GetClass(env, "livekit/org/webrtc/WrappedVideoDecoderFactory");
|
||||
|
||||
jmethodID ctor = env->GetMethodID(factory_class.obj(), "<init>",
|
||||
"(Llivekit/org/webrtc/EglBase$Context;)V");
|
||||
|
||||
jobject decoder_factory = env->NewObject(factory_class.obj(), ctor, nullptr);
|
||||
return webrtc::JavaToNativeVideoDecoderFactory(env, decoder_factory);
|
||||
}
|
||||
|
||||
} // namespace livekit_ffi
|
||||
@@ -0,0 +1,43 @@
|
||||
// Copyright 2025 LiveKit, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
#[cxx::bridge(namespace = "livekit_ffi")]
|
||||
pub mod ffi {
|
||||
unsafe extern "C++" {
|
||||
include!("livekit/android.h");
|
||||
|
||||
type JavaVM;
|
||||
|
||||
/// Initialize Android WebRTC with the JVM.
|
||||
/// Called automatically by init_android_context(), so only call directly
|
||||
/// in JNI_OnLoad or when you don't have an Android Context.
|
||||
/// Idempotent - safe to call multiple times.
|
||||
unsafe fn init_android(vm: *mut JavaVM);
|
||||
|
||||
/// Initialize Android WebRTC with the application context.
|
||||
/// This is the main init function - calls init_android() internally,
|
||||
/// then initializes ContextUtils for PlatformAudio.
|
||||
/// Idempotent - safe to call multiple times.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `jvm` - The JavaVM pointer
|
||||
/// * `context` - The Android application context (jobject as usize)
|
||||
///
|
||||
/// # Returns
|
||||
/// true if context init succeeded, false otherwise.
|
||||
/// Note: JVM init always happens regardless of return value.
|
||||
unsafe fn init_android_context(jvm: *mut JavaVM, context: usize) -> bool;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright 2025 LiveKit, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "livekit/apm.h"
|
||||
|
||||
#include "api/audio/builtin_audio_processing_builder.h"
|
||||
#include "api/environment/environment_factory.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
|
||||
namespace livekit_ffi {
|
||||
|
||||
AudioProcessingModule::AudioProcessingModule(
|
||||
const AudioProcessingConfig& config) {
|
||||
apm_ = webrtc::BuiltinAudioProcessingBuilder()
|
||||
.Build(webrtc::CreateEnvironment());
|
||||
|
||||
apm_->ApplyConfig(config.ToWebrtcConfig());
|
||||
apm_->Initialize();
|
||||
}
|
||||
|
||||
int AudioProcessingModule::process_stream(const int16_t* src,
|
||||
size_t src_len,
|
||||
int16_t* dst,
|
||||
size_t dst_len,
|
||||
int sample_rate,
|
||||
int num_channels) {
|
||||
webrtc::StreamConfig stream_cfg(sample_rate, num_channels);
|
||||
return apm_->ProcessStream(src, stream_cfg, stream_cfg, dst);
|
||||
}
|
||||
|
||||
int AudioProcessingModule::process_reverse_stream(const int16_t* src,
|
||||
size_t src_len,
|
||||
int16_t* dst,
|
||||
size_t dst_len,
|
||||
int sample_rate,
|
||||
int num_channels) {
|
||||
webrtc::StreamConfig stream_cfg(sample_rate, num_channels);
|
||||
return apm_->ProcessReverseStream(src, stream_cfg, stream_cfg, dst);
|
||||
}
|
||||
|
||||
int AudioProcessingModule::set_stream_delay_ms(int delay_ms) {
|
||||
return apm_->set_stream_delay_ms(delay_ms);
|
||||
}
|
||||
|
||||
std::unique_ptr<AudioProcessingModule> create_apm(
|
||||
bool echo_canceller_enabled,
|
||||
bool gain_controller_enabled,
|
||||
bool high_pass_filter_enabled,
|
||||
bool noise_suppression_enabled) {
|
||||
AudioProcessingConfig config;
|
||||
config.echo_canceller_enabled = echo_canceller_enabled;
|
||||
config.gain_controller_enabled = gain_controller_enabled;
|
||||
config.high_pass_filter_enabled = high_pass_filter_enabled;
|
||||
config.noise_suppression_enabled = noise_suppression_enabled;
|
||||
return std::make_unique<AudioProcessingModule>(config);
|
||||
}
|
||||
|
||||
} // namespace livekit_ffi
|
||||
@@ -0,0 +1,55 @@
|
||||
// Copyright 2025 LiveKit, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::impl_thread_safety;
|
||||
|
||||
#[cxx::bridge(namespace = "livekit_ffi")]
|
||||
pub mod ffi {
|
||||
unsafe extern "C++" {
|
||||
include!("livekit/apm.h");
|
||||
|
||||
type AudioProcessingModule;
|
||||
|
||||
unsafe fn process_stream(
|
||||
self: Pin<&mut AudioProcessingModule>,
|
||||
src: *const i16,
|
||||
src_len: usize,
|
||||
dst: *mut i16,
|
||||
dst_len: usize,
|
||||
sample_rate: i32,
|
||||
num_channels: i32,
|
||||
) -> i32;
|
||||
|
||||
unsafe fn process_reverse_stream(
|
||||
self: Pin<&mut AudioProcessingModule>,
|
||||
src: *const i16,
|
||||
src_len: usize,
|
||||
dst: *mut i16,
|
||||
dst_len: usize,
|
||||
sample_rate: i32,
|
||||
num_channels: i32,
|
||||
) -> i32;
|
||||
|
||||
fn set_stream_delay_ms(self: Pin<&mut AudioProcessingModule>, delay: i32) -> i32;
|
||||
|
||||
fn create_apm(
|
||||
echo_canceller_enabled: bool,
|
||||
gain_controller_enabled: bool,
|
||||
high_pass_filter_enabled: bool,
|
||||
noise_suppression_enabled: bool,
|
||||
) -> UniquePtr<AudioProcessingModule>;
|
||||
}
|
||||
}
|
||||
|
||||
impl_thread_safety!(ffi::AudioProcessingModule, Send + Sync);
|
||||
+216
@@ -0,0 +1,216 @@
|
||||
/*
|
||||
* Copyright 2026 LiveKit, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "livekit/audio_device_controller.h"
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
namespace livekit_ffi {
|
||||
|
||||
AudioDeviceController::AudioDeviceController(
|
||||
webrtc::scoped_refptr<AdmProxy> adm_proxy)
|
||||
: adm_proxy_(std::move(adm_proxy)) {}
|
||||
|
||||
int16_t AudioDeviceController::playout_devices() const {
|
||||
return adm_proxy_->PlayoutDevices();
|
||||
}
|
||||
|
||||
int16_t AudioDeviceController::recording_devices() const {
|
||||
return adm_proxy_->RecordingDevices();
|
||||
}
|
||||
|
||||
rust::String AudioDeviceController::playout_device_name(uint16_t index) const {
|
||||
char name[webrtc::kAdmMaxDeviceNameSize] = {0};
|
||||
char guid[webrtc::kAdmMaxGuidSize] = {0};
|
||||
adm_proxy_->PlayoutDeviceName(index, name, guid);
|
||||
return rust::String(name);
|
||||
}
|
||||
|
||||
rust::String AudioDeviceController::recording_device_name(uint16_t index) const {
|
||||
char name[webrtc::kAdmMaxDeviceNameSize] = {0};
|
||||
char guid[webrtc::kAdmMaxGuidSize] = {0};
|
||||
adm_proxy_->RecordingDeviceName(index, name, guid);
|
||||
return rust::String(name);
|
||||
}
|
||||
|
||||
rust::String AudioDeviceController::playout_device_guid(uint16_t index) const {
|
||||
char name[webrtc::kAdmMaxDeviceNameSize] = {0};
|
||||
char guid[webrtc::kAdmMaxGuidSize] = {0};
|
||||
adm_proxy_->PlayoutDeviceName(index, name, guid);
|
||||
return rust::String(guid);
|
||||
}
|
||||
|
||||
rust::String AudioDeviceController::recording_device_guid(uint16_t index) const {
|
||||
char name[webrtc::kAdmMaxDeviceNameSize] = {0};
|
||||
char guid[webrtc::kAdmMaxGuidSize] = {0};
|
||||
adm_proxy_->RecordingDeviceName(index, name, guid);
|
||||
return rust::String(guid);
|
||||
}
|
||||
|
||||
bool AudioDeviceController::set_playout_device(uint16_t index) const {
|
||||
return adm_proxy_->SetPlayoutDevice(index) == 0;
|
||||
}
|
||||
|
||||
bool AudioDeviceController::set_recording_device(uint16_t index) const {
|
||||
return adm_proxy_->SetRecordingDevice(index) == 0;
|
||||
}
|
||||
|
||||
bool AudioDeviceController::set_playout_device_by_guid(rust::String guid) const {
|
||||
int16_t count = adm_proxy_->PlayoutDevices();
|
||||
|
||||
// Try to find a device matching the GUID
|
||||
for (int16_t i = 0; i < count; i++) {
|
||||
char name[webrtc::kAdmMaxDeviceNameSize] = {0};
|
||||
char device_guid[webrtc::kAdmMaxGuidSize] = {0};
|
||||
if (adm_proxy_->PlayoutDeviceName(i, name, device_guid) == 0) {
|
||||
if (std::string(guid.c_str()) == std::string(device_guid)) {
|
||||
return adm_proxy_->SetPlayoutDevice(i) == 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// No match found - fall back to default device (index 0).
|
||||
// This handles mobile platforms (iOS/Android) where:
|
||||
// - GUIDs may be empty or not meaningful
|
||||
// - Device selection is a no-op (system handles routing)
|
||||
if (count > 0) {
|
||||
return adm_proxy_->SetPlayoutDevice(0) == 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AudioDeviceController::set_recording_device_by_guid(rust::String guid) const {
|
||||
int16_t count = adm_proxy_->RecordingDevices();
|
||||
|
||||
// Try to find a device matching the GUID
|
||||
for (int16_t i = 0; i < count; i++) {
|
||||
char name[webrtc::kAdmMaxDeviceNameSize] = {0};
|
||||
char device_guid[webrtc::kAdmMaxGuidSize] = {0};
|
||||
if (adm_proxy_->RecordingDeviceName(i, name, device_guid) == 0) {
|
||||
if (std::string(guid.c_str()) == std::string(device_guid)) {
|
||||
return adm_proxy_->SetRecordingDevice(i) == 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// No match found - fall back to default device (index 0).
|
||||
// This handles mobile platforms (iOS/Android) where:
|
||||
// - GUIDs may be empty or not meaningful
|
||||
// - Device selection is a no-op (system handles routing)
|
||||
if (count > 0) {
|
||||
return adm_proxy_->SetRecordingDevice(0) == 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AudioDeviceController::stop_recording() const {
|
||||
return adm_proxy_->StopRecording() == 0;
|
||||
}
|
||||
|
||||
bool AudioDeviceController::init_recording() const {
|
||||
return adm_proxy_->InitRecording() == 0;
|
||||
}
|
||||
|
||||
bool AudioDeviceController::start_recording() const {
|
||||
return adm_proxy_->StartRecording() == 0;
|
||||
}
|
||||
|
||||
bool AudioDeviceController::recording_is_initialized() const {
|
||||
return adm_proxy_->RecordingIsInitialized();
|
||||
}
|
||||
|
||||
bool AudioDeviceController::stop_playout() const {
|
||||
return adm_proxy_->StopPlayout() == 0;
|
||||
}
|
||||
|
||||
bool AudioDeviceController::init_playout() const {
|
||||
return adm_proxy_->InitPlayout() == 0;
|
||||
}
|
||||
|
||||
bool AudioDeviceController::start_playout() const {
|
||||
return adm_proxy_->StartPlayout() == 0;
|
||||
}
|
||||
|
||||
bool AudioDeviceController::playout_is_initialized() const {
|
||||
return adm_proxy_->PlayoutIsInitialized();
|
||||
}
|
||||
|
||||
bool AudioDeviceController::builtin_aec_is_available() const {
|
||||
return adm_proxy_->BuiltInAECIsAvailable();
|
||||
}
|
||||
|
||||
bool AudioDeviceController::builtin_agc_is_available() const {
|
||||
return adm_proxy_->BuiltInAGCIsAvailable();
|
||||
}
|
||||
|
||||
bool AudioDeviceController::builtin_ns_is_available() const {
|
||||
return adm_proxy_->BuiltInNSIsAvailable();
|
||||
}
|
||||
|
||||
bool AudioDeviceController::enable_builtin_aec(bool enable) const {
|
||||
return adm_proxy_->EnableBuiltInAEC(enable) == 0;
|
||||
}
|
||||
|
||||
bool AudioDeviceController::enable_builtin_agc(bool enable) const {
|
||||
return adm_proxy_->EnableBuiltInAGC(enable) == 0;
|
||||
}
|
||||
|
||||
bool AudioDeviceController::enable_builtin_ns(bool enable) const {
|
||||
return adm_proxy_->EnableBuiltInNS(enable) == 0;
|
||||
}
|
||||
|
||||
void AudioDeviceController::set_adm_recording_enabled(bool enabled) const {
|
||||
adm_proxy_->set_recording_enabled(enabled);
|
||||
}
|
||||
|
||||
bool AudioDeviceController::adm_recording_enabled() const {
|
||||
return adm_proxy_->recording_enabled();
|
||||
}
|
||||
|
||||
void AudioDeviceController::set_adm_playout_enabled(bool enabled) const {
|
||||
adm_proxy_->set_playout_enabled(enabled);
|
||||
}
|
||||
|
||||
bool AudioDeviceController::adm_playout_enabled() const {
|
||||
return adm_proxy_->playout_enabled();
|
||||
}
|
||||
|
||||
bool AudioDeviceController::acquire_platform_adm() const {
|
||||
return adm_proxy_->AcquirePlatformAdm();
|
||||
}
|
||||
|
||||
void AudioDeviceController::release_platform_adm() const {
|
||||
adm_proxy_->ReleasePlatformAdm();
|
||||
}
|
||||
|
||||
int AudioDeviceController::platform_adm_ref_count() const {
|
||||
return adm_proxy_->platform_adm_ref_count();
|
||||
}
|
||||
|
||||
bool AudioDeviceController::is_platform_adm_active() const {
|
||||
return adm_proxy_->is_platform_adm_active();
|
||||
}
|
||||
|
||||
bool AudioDeviceController::ensure_platform_adm() const {
|
||||
return adm_proxy_->EnsurePlatformAdm();
|
||||
}
|
||||
|
||||
bool AudioDeviceController::platform_adm_available() const {
|
||||
return adm_proxy_->platform_adm_available();
|
||||
}
|
||||
|
||||
} // namespace livekit_ffi
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
// Copyright 2026 LiveKit, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
pub use cxx::SharedPtr;
|
||||
|
||||
use crate::impl_thread_safety;
|
||||
|
||||
#[cxx::bridge(namespace = "livekit_ffi")]
|
||||
pub mod ffi {
|
||||
unsafe extern "C++" {
|
||||
include!("livekit/audio_device_controller.h");
|
||||
include!("livekit/peer_connection_factory.h");
|
||||
|
||||
type AudioDeviceController;
|
||||
type PeerConnectionFactory = crate::peer_connection_factory::ffi::PeerConnectionFactory;
|
||||
|
||||
fn audio_device(self: &PeerConnectionFactory) -> SharedPtr<AudioDeviceController>;
|
||||
|
||||
fn playout_devices(self: &AudioDeviceController) -> i16;
|
||||
fn recording_devices(self: &AudioDeviceController) -> i16;
|
||||
fn playout_device_name(self: &AudioDeviceController, index: u16) -> String;
|
||||
fn recording_device_name(self: &AudioDeviceController, index: u16) -> String;
|
||||
fn playout_device_guid(self: &AudioDeviceController, index: u16) -> String;
|
||||
fn recording_device_guid(self: &AudioDeviceController, index: u16) -> String;
|
||||
|
||||
fn set_playout_device(self: &AudioDeviceController, index: u16) -> bool;
|
||||
fn set_recording_device(self: &AudioDeviceController, index: u16) -> bool;
|
||||
fn set_playout_device_by_guid(self: &AudioDeviceController, guid: String) -> bool;
|
||||
fn set_recording_device_by_guid(self: &AudioDeviceController, guid: String) -> bool;
|
||||
|
||||
fn stop_recording(self: &AudioDeviceController) -> bool;
|
||||
fn init_recording(self: &AudioDeviceController) -> bool;
|
||||
fn start_recording(self: &AudioDeviceController) -> bool;
|
||||
fn recording_is_initialized(self: &AudioDeviceController) -> bool;
|
||||
|
||||
fn stop_playout(self: &AudioDeviceController) -> bool;
|
||||
fn init_playout(self: &AudioDeviceController) -> bool;
|
||||
fn start_playout(self: &AudioDeviceController) -> bool;
|
||||
fn playout_is_initialized(self: &AudioDeviceController) -> bool;
|
||||
|
||||
fn builtin_aec_is_available(self: &AudioDeviceController) -> bool;
|
||||
fn builtin_agc_is_available(self: &AudioDeviceController) -> bool;
|
||||
fn builtin_ns_is_available(self: &AudioDeviceController) -> bool;
|
||||
fn enable_builtin_aec(self: &AudioDeviceController, enable: bool) -> bool;
|
||||
fn enable_builtin_agc(self: &AudioDeviceController, enable: bool) -> bool;
|
||||
fn enable_builtin_ns(self: &AudioDeviceController, enable: bool) -> bool;
|
||||
|
||||
fn set_adm_recording_enabled(self: &AudioDeviceController, enabled: bool);
|
||||
fn adm_recording_enabled(self: &AudioDeviceController) -> bool;
|
||||
|
||||
fn set_adm_playout_enabled(self: &AudioDeviceController, enabled: bool);
|
||||
fn adm_playout_enabled(self: &AudioDeviceController) -> bool;
|
||||
|
||||
fn acquire_platform_adm(self: &AudioDeviceController) -> bool;
|
||||
fn release_platform_adm(self: &AudioDeviceController);
|
||||
fn platform_adm_ref_count(self: &AudioDeviceController) -> i32;
|
||||
fn is_platform_adm_active(self: &AudioDeviceController) -> bool;
|
||||
fn ensure_platform_adm(self: &AudioDeviceController) -> bool;
|
||||
fn platform_adm_available(self: &AudioDeviceController) -> bool;
|
||||
}
|
||||
}
|
||||
|
||||
impl_thread_safety!(ffi::AudioDeviceController, Send + Sync);
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::peer_connection_factory::ffi::create_peer_connection_factory;
|
||||
use std::sync::Mutex;
|
||||
|
||||
static TEST_MUTEX: Mutex<()> = Mutex::new(());
|
||||
|
||||
#[test]
|
||||
fn test_audio_device_controller_bridge() {
|
||||
let _guard = TEST_MUTEX.lock().expect("test mutex poisoned");
|
||||
let factory = create_peer_connection_factory();
|
||||
let audio = factory.audio_device();
|
||||
|
||||
let recording_count = audio.recording_devices();
|
||||
let playout_count = audio.playout_devices();
|
||||
assert!(recording_count >= 0);
|
||||
assert!(playout_count >= 0);
|
||||
|
||||
if recording_count > 0 {
|
||||
let _ = audio.recording_device_name(0);
|
||||
let guid = audio.recording_device_guid(0);
|
||||
let _ = audio.set_recording_device_by_guid(guid);
|
||||
}
|
||||
|
||||
if playout_count > 0 {
|
||||
let _ = audio.playout_device_name(0);
|
||||
let guid = audio.playout_device_guid(0);
|
||||
let _ = audio.set_playout_device_by_guid(guid);
|
||||
}
|
||||
|
||||
let initial_recording = audio.adm_recording_enabled();
|
||||
audio.set_adm_recording_enabled(!initial_recording);
|
||||
assert_eq!(audio.adm_recording_enabled(), !initial_recording);
|
||||
audio.set_adm_recording_enabled(initial_recording);
|
||||
assert_eq!(audio.adm_recording_enabled(), initial_recording);
|
||||
|
||||
let initial_playout = audio.adm_playout_enabled();
|
||||
audio.set_adm_playout_enabled(!initial_playout);
|
||||
assert_eq!(audio.adm_playout_enabled(), !initial_playout);
|
||||
audio.set_adm_playout_enabled(initial_playout);
|
||||
assert_eq!(audio.adm_playout_enabled(), initial_playout);
|
||||
|
||||
assert!(audio.platform_adm_ref_count() >= 0);
|
||||
let _ = audio.is_platform_adm_active();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright 2025 LiveKit, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "livekit/audio_mixer.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
|
||||
#include "api/audio/audio_frame.h"
|
||||
#include "api/audio/audio_mixer.h"
|
||||
#include "modules/audio_mixer/audio_mixer_impl.h"
|
||||
#include "webrtc-sys/src/audio_mixer.rs.h"
|
||||
|
||||
namespace livekit_ffi {
|
||||
|
||||
AudioMixer::AudioMixer() {
|
||||
audio_mixer_ = webrtc::AudioMixerImpl::Create();
|
||||
}
|
||||
|
||||
void AudioMixer::add_source(rust::Box<AudioMixerSourceWrapper> source) {
|
||||
auto native_source = std::make_shared<AudioMixerSource>(std::move(source));
|
||||
|
||||
webrtc::MutexLock lock(&sources_mutex_);
|
||||
audio_mixer_->AddSource(native_source.get());
|
||||
sources_.push_back(native_source);
|
||||
}
|
||||
|
||||
void AudioMixer::remove_source(int source_ssrc) {
|
||||
webrtc::MutexLock lock(&sources_mutex_);
|
||||
auto it = std::find_if(
|
||||
sources_.begin(), sources_.end(),
|
||||
[source_ssrc](const auto& s) { return s->Ssrc() == source_ssrc; });
|
||||
|
||||
if (it != sources_.end()) {
|
||||
audio_mixer_->RemoveSource(it->get());
|
||||
sources_.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
size_t AudioMixer::mix(size_t number_of_channels) {
|
||||
audio_mixer_->Mix(number_of_channels, &frame_);
|
||||
return frame_.num_channels() * frame_.samples_per_channel();
|
||||
}
|
||||
|
||||
const int16_t* AudioMixer::data() const {
|
||||
return frame_.data();
|
||||
}
|
||||
|
||||
std::unique_ptr<AudioMixer> create_audio_mixer() {
|
||||
return std::make_unique<AudioMixer>();
|
||||
}
|
||||
|
||||
AudioMixerSource::AudioMixerSource(rust::Box<AudioMixerSourceWrapper> source)
|
||||
: source_(std::move(source)) {}
|
||||
|
||||
int AudioMixerSource::Ssrc() const {
|
||||
return source_->ssrc();
|
||||
}
|
||||
|
||||
int AudioMixerSource::PreferredSampleRate() const {
|
||||
return source_->preferred_sample_rate();
|
||||
}
|
||||
|
||||
webrtc::AudioMixer::Source::AudioFrameInfo
|
||||
AudioMixerSource::GetAudioFrameWithInfo(int sample_rate,
|
||||
webrtc::AudioFrame* audio_frame) {
|
||||
NativeAudioFrame frame(audio_frame);
|
||||
|
||||
livekit_ffi::AudioFrameInfo result =
|
||||
source_->get_audio_frame_with_info(sample_rate, frame);
|
||||
|
||||
if (result == livekit_ffi::AudioFrameInfo::Normal) {
|
||||
return webrtc::AudioMixer::Source::AudioFrameInfo::kNormal;
|
||||
} else if (result == livekit_ffi::AudioFrameInfo::Muted) {
|
||||
return webrtc::AudioMixer::Source::AudioFrameInfo::kMuted;
|
||||
} else {
|
||||
return webrtc::AudioMixer::Source::AudioFrameInfo::kError;
|
||||
}
|
||||
}
|
||||
|
||||
void NativeAudioFrame::update_frame(uint32_t timestamp,
|
||||
const int16_t* data,
|
||||
size_t samples_per_channel,
|
||||
int sample_rate_hz,
|
||||
size_t num_channels) {
|
||||
frame_->UpdateFrame(timestamp, data, samples_per_channel, sample_rate_hz,
|
||||
webrtc::AudioFrame::SpeechType::kNormalSpeech,
|
||||
webrtc::AudioFrame::VADActivity::kVadUnknown,
|
||||
num_channels);
|
||||
}
|
||||
|
||||
} // namespace livekit_ffi
|
||||
@@ -0,0 +1,106 @@
|
||||
// Copyright 2025 LiveKit, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use std::{pin::Pin, sync::Arc};
|
||||
|
||||
use ffi::AudioFrameInfo;
|
||||
|
||||
use crate::impl_thread_safety;
|
||||
|
||||
#[cxx::bridge(namespace = "livekit_ffi")]
|
||||
pub mod ffi {
|
||||
unsafe extern "C++" {
|
||||
include!("livekit/audio_mixer.h");
|
||||
|
||||
type AudioMixer;
|
||||
|
||||
unsafe fn add_source(self: Pin<&mut AudioMixer>, src: Box<AudioMixerSourceWrapper>);
|
||||
|
||||
unsafe fn remove_source(self: Pin<&mut AudioMixer>, ssrc: i32);
|
||||
|
||||
unsafe fn mix(self: Pin<&mut AudioMixer>, num_channels: usize) -> usize;
|
||||
|
||||
unsafe fn data(self: &AudioMixer) -> *const i16;
|
||||
|
||||
fn create_audio_mixer() -> UniquePtr<AudioMixer>;
|
||||
|
||||
type NativeAudioFrame;
|
||||
|
||||
unsafe fn update_frame(
|
||||
self: Pin<&mut NativeAudioFrame>,
|
||||
timestamp: u32,
|
||||
data: *const i16,
|
||||
samples_per_channel: usize,
|
||||
sample_rate_hz: i32,
|
||||
num_channels: usize,
|
||||
);
|
||||
}
|
||||
|
||||
pub enum AudioFrameInfo {
|
||||
Normal,
|
||||
Muted,
|
||||
Error,
|
||||
}
|
||||
|
||||
extern "Rust" {
|
||||
type AudioMixerSourceWrapper;
|
||||
|
||||
fn ssrc(self: &AudioMixerSourceWrapper) -> i32;
|
||||
fn preferred_sample_rate(self: &AudioMixerSourceWrapper) -> i32;
|
||||
fn get_audio_frame_with_info(
|
||||
self: &AudioMixerSourceWrapper,
|
||||
target_sample_rate: i32,
|
||||
frame: Pin<&mut NativeAudioFrame>,
|
||||
) -> AudioFrameInfo;
|
||||
}
|
||||
}
|
||||
|
||||
pub trait AudioMixerSource {
|
||||
fn ssrc(&self) -> i32;
|
||||
fn preferred_sample_rate(&self) -> i32;
|
||||
fn get_audio_frame_with_info(
|
||||
&self,
|
||||
target_sample_rate: i32,
|
||||
frame: NativeAudioFrame,
|
||||
) -> AudioFrameInfo;
|
||||
}
|
||||
|
||||
pub struct AudioMixerSourceWrapper {
|
||||
source: Arc<dyn AudioMixerSource>,
|
||||
}
|
||||
|
||||
pub type NativeAudioFrame<'a> = Pin<&'a mut ffi::NativeAudioFrame>;
|
||||
|
||||
impl AudioMixerSourceWrapper {
|
||||
pub fn new(source: Arc<dyn AudioMixerSource>) -> Self {
|
||||
Self { source }
|
||||
}
|
||||
|
||||
pub fn ssrc(&self) -> i32 {
|
||||
self.source.ssrc()
|
||||
}
|
||||
pub fn preferred_sample_rate(&self) -> i32 {
|
||||
self.source.preferred_sample_rate()
|
||||
}
|
||||
|
||||
pub fn get_audio_frame_with_info(
|
||||
&self,
|
||||
target_sample_rate: i32,
|
||||
frame: Pin<&mut ffi::NativeAudioFrame>,
|
||||
) -> AudioFrameInfo {
|
||||
self.source.get_audio_frame_with_info(target_sample_rate, frame)
|
||||
}
|
||||
}
|
||||
|
||||
impl_thread_safety!(ffi::AudioMixer, Send + Sync);
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2025 LiveKit, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "livekit/audio_resampler.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "audio/remix_resample.h"
|
||||
#include "api/audio/audio_view.h"
|
||||
#include "api/audio/audio_frame.h"
|
||||
|
||||
namespace livekit_ffi {
|
||||
|
||||
size_t AudioResampler::remix_and_resample(const int16_t* src,
|
||||
size_t samples_per_channel,
|
||||
size_t num_channels,
|
||||
int sample_rate,
|
||||
size_t dest_num_channels,
|
||||
int dest_sample_rate) {
|
||||
frame_.num_channels_ = dest_num_channels;
|
||||
frame_.sample_rate_hz_ = dest_sample_rate;
|
||||
frame_.samples_per_channel_ = webrtc::SampleRateToDefaultChannelSize(dest_sample_rate);
|
||||
webrtc::InterleavedView<const int16_t> source(static_cast<const int16_t*>(src),
|
||||
samples_per_channel,
|
||||
num_channels);
|
||||
webrtc::voe::RemixAndResample(source, sample_rate, &resampler_, &frame_);
|
||||
|
||||
return frame_.num_channels() * frame_.samples_per_channel() * sizeof(int16_t);
|
||||
}
|
||||
|
||||
const int16_t* AudioResampler::data() const {
|
||||
return frame_.data();
|
||||
}
|
||||
|
||||
std::unique_ptr<AudioResampler> create_audio_resampler() {
|
||||
return std::make_unique<AudioResampler>();
|
||||
}
|
||||
|
||||
} // namespace livekit_ffi
|
||||
@@ -0,0 +1,40 @@
|
||||
// Copyright 2025 LiveKit, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::impl_thread_safety;
|
||||
|
||||
#[cxx::bridge(namespace = "livekit_ffi")]
|
||||
pub mod ffi {
|
||||
unsafe extern "C++" {
|
||||
include!("livekit/audio_resampler.h");
|
||||
|
||||
type AudioResampler;
|
||||
|
||||
unsafe fn remix_and_resample(
|
||||
self: Pin<&mut AudioResampler>,
|
||||
src: *const i16,
|
||||
samples_per_channel: usize,
|
||||
num_channels: usize,
|
||||
sample_rate: i32,
|
||||
dst_num_channels: usize,
|
||||
dst_sample_rate: i32,
|
||||
) -> usize;
|
||||
|
||||
unsafe fn data(self: &AudioResampler) -> *const i16;
|
||||
|
||||
fn create_audio_resampler() -> UniquePtr<AudioResampler>;
|
||||
}
|
||||
}
|
||||
|
||||
impl_thread_safety!(ffi::AudioResampler, Send + Sync);
|
||||
@@ -0,0 +1,319 @@
|
||||
/*
|
||||
* Copyright 2025 LiveKit, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "livekit/audio_track.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
|
||||
#include "api/audio_options.h"
|
||||
#include "api/audio/audio_frame.h"
|
||||
#include "api/media_stream_interface.h"
|
||||
#include "api/task_queue/task_queue_base.h"
|
||||
#include "audio/remix_resample.h"
|
||||
#include "common_audio/include/audio_util.h"
|
||||
#include "livekit/global_task_queue.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/logging.h"
|
||||
#include "rtc_base/ref_counted_object.h"
|
||||
#include "rtc_base/synchronization/mutex.h"
|
||||
#include "rust/cxx.h"
|
||||
#include "webrtc-sys/src/audio_track.rs.h"
|
||||
|
||||
namespace livekit_ffi {
|
||||
|
||||
inline webrtc::AudioOptions to_native_audio_options(
|
||||
const AudioSourceOptions& options) {
|
||||
webrtc::AudioOptions rtc_options{};
|
||||
rtc_options.echo_cancellation = options.echo_cancellation;
|
||||
rtc_options.noise_suppression = options.noise_suppression;
|
||||
rtc_options.auto_gain_control = options.auto_gain_control;
|
||||
return rtc_options;
|
||||
}
|
||||
|
||||
inline AudioSourceOptions to_rust_audio_options(
|
||||
const webrtc::AudioOptions& rtc_options) {
|
||||
AudioSourceOptions options{};
|
||||
options.echo_cancellation = rtc_options.echo_cancellation.value_or(false);
|
||||
options.noise_suppression = rtc_options.noise_suppression.value_or(false);
|
||||
options.auto_gain_control = rtc_options.auto_gain_control.value_or(false);
|
||||
return options;
|
||||
}
|
||||
|
||||
AudioTrack::AudioTrack(std::shared_ptr<RtcRuntime> rtc_runtime,
|
||||
webrtc::scoped_refptr<webrtc::AudioTrackInterface> track)
|
||||
: MediaStreamTrack(rtc_runtime, std::move(track)) {}
|
||||
|
||||
AudioTrack::~AudioTrack() {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
for (auto& sink : sinks_) {
|
||||
track()->RemoveSink(sink.get());
|
||||
}
|
||||
}
|
||||
|
||||
void AudioTrack::add_sink(const std::shared_ptr<NativeAudioSink>& sink) const {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
track()->AddSink(sink.get());
|
||||
sinks_.push_back(sink);
|
||||
}
|
||||
|
||||
void AudioTrack::remove_sink(
|
||||
const std::shared_ptr<NativeAudioSink>& sink) const {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
track()->RemoveSink(sink.get());
|
||||
sinks_.erase(std::remove(sinks_.begin(), sinks_.end(), sink), sinks_.end());
|
||||
}
|
||||
|
||||
NativeAudioSink::NativeAudioSink(rust::Box<AudioSinkWrapper> observer,
|
||||
int sample_rate,
|
||||
int num_channels)
|
||||
: observer_(std::move(observer)),
|
||||
sample_rate_(sample_rate),
|
||||
num_channels_(num_channels) {
|
||||
frame_.sample_rate_hz_ = sample_rate;
|
||||
frame_.num_channels_ = num_channels;
|
||||
frame_.samples_per_channel_ = webrtc::SampleRateToDefaultChannelSize(sample_rate);
|
||||
}
|
||||
|
||||
void NativeAudioSink::OnData(const void* audio_data,
|
||||
int bits_per_sample,
|
||||
int sample_rate,
|
||||
size_t number_of_channels,
|
||||
size_t number_of_frames) {
|
||||
RTC_CHECK_EQ(16, bits_per_sample);
|
||||
|
||||
const int16_t* data = static_cast<const int16_t*>(audio_data);
|
||||
|
||||
if (sample_rate_ != sample_rate || num_channels_ != number_of_channels) {
|
||||
webrtc::InterleavedView<const int16_t> source(data,
|
||||
number_of_frames,
|
||||
number_of_channels);
|
||||
// resample/remix before capturing
|
||||
webrtc::voe::RemixAndResample(source, sample_rate, &resampler_, &frame_);
|
||||
|
||||
rust::Slice<const int16_t> rust_slice(
|
||||
frame_.data(), frame_.num_channels() * frame_.samples_per_channel());
|
||||
|
||||
observer_->on_data(rust_slice, frame_.sample_rate_hz(),
|
||||
frame_.num_channels(), frame_.samples_per_channel());
|
||||
|
||||
} else {
|
||||
rust::Slice<const int16_t> rust_slice(
|
||||
data, number_of_channels * number_of_frames);
|
||||
|
||||
observer_->on_data(rust_slice, sample_rate, number_of_channels,
|
||||
number_of_frames);
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<NativeAudioSink> new_native_audio_sink(
|
||||
rust::Box<AudioSinkWrapper> observer,
|
||||
int sample_rate,
|
||||
int num_channels) {
|
||||
return std::make_shared<NativeAudioSink>(std::move(observer), sample_rate,
|
||||
num_channels);
|
||||
}
|
||||
|
||||
AudioTrackSource::InternalSource::InternalSource(
|
||||
const webrtc::AudioOptions& options,
|
||||
int sample_rate,
|
||||
int num_channels,
|
||||
int queue_size_ms, // must be a multiple of 10ms
|
||||
webrtc::TaskQueueFactory* task_queue_factory)
|
||||
: options_(options),
|
||||
sample_rate_(sample_rate),
|
||||
num_channels_(num_channels),
|
||||
capture_userdata_(nullptr),
|
||||
on_complete_(nullptr) {
|
||||
if (!queue_size_ms) {
|
||||
// Set queue_size_samples_ to 0 so that capture_frame() will get to the fast path.
|
||||
queue_size_samples_ = 0;
|
||||
return; // no audio queue
|
||||
}
|
||||
|
||||
int samples10ms = sample_rate / 100 * num_channels;
|
||||
|
||||
silence_buffer_.assign(samples10ms, 0);
|
||||
queue_size_samples_ = queue_size_ms / 10 * samples10ms;
|
||||
notify_threshold_samples_ = queue_size_samples_; // TODO: this is currently
|
||||
// using x2 the queue size
|
||||
buffer_.reserve(queue_size_samples_ + notify_threshold_samples_);
|
||||
|
||||
audio_queue_ =
|
||||
task_queue_factory->CreateTaskQueue(
|
||||
"AudioSourceCapture", webrtc::TaskQueueFactory::Priority::NORMAL);
|
||||
|
||||
audio_task_ = webrtc::RepeatingTaskHandle::Start(
|
||||
audio_queue_.get(),
|
||||
[this, samples10ms]() {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
constexpr int kBitsPerSample = sizeof(int16_t) * 8;
|
||||
|
||||
if (buffer_.size() >= samples10ms) {
|
||||
for (auto sink : sinks_)
|
||||
sink->OnData(buffer_.data(), kBitsPerSample, sample_rate_,
|
||||
num_channels_, samples10ms / num_channels_);
|
||||
|
||||
buffer_.erase(buffer_.begin(), buffer_.begin() + samples10ms);
|
||||
} else {
|
||||
// Always provide a 10ms frame to avoid playout underruns.
|
||||
for (auto sink : sinks_)
|
||||
sink->OnData(silence_buffer_.data(), kBitsPerSample, sample_rate_,
|
||||
num_channels_, samples10ms / num_channels_);
|
||||
}
|
||||
|
||||
if (on_complete_ && buffer_.size() <= notify_threshold_samples_) {
|
||||
on_complete_(capture_userdata_);
|
||||
on_complete_ = nullptr;
|
||||
capture_userdata_ = nullptr;
|
||||
}
|
||||
|
||||
return webrtc::TimeDelta::Millis(10);
|
||||
},
|
||||
webrtc::TaskQueueBase::DelayPrecision::kHigh);
|
||||
}
|
||||
|
||||
AudioTrackSource::InternalSource::~InternalSource() {
|
||||
}
|
||||
|
||||
bool AudioTrackSource::InternalSource::capture_frame(
|
||||
rust::Slice<const int16_t> data,
|
||||
uint32_t sample_rate,
|
||||
uint32_t number_of_channels,
|
||||
size_t number_of_frames,
|
||||
const SourceContext* ctx,
|
||||
void (*on_complete)(const SourceContext*)) {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
|
||||
if (queue_size_samples_) {
|
||||
int available =
|
||||
(queue_size_samples_ + notify_threshold_samples_) - buffer_.size();
|
||||
if (available < data.size())
|
||||
return false;
|
||||
|
||||
if (on_complete_ || capture_userdata_)
|
||||
return false;
|
||||
|
||||
buffer_.insert(buffer_.end(), data.begin(), data.end());
|
||||
|
||||
if (buffer_.size() <= notify_threshold_samples_) {
|
||||
on_complete(ctx); // complete directly
|
||||
} else {
|
||||
on_complete_ = on_complete;
|
||||
capture_userdata_ = ctx;
|
||||
}
|
||||
|
||||
} else {
|
||||
// Fast path: capture directly when the queue buffer is 0 (frame size must be 10ms)
|
||||
for (auto sink : sinks_)
|
||||
sink->OnData(data.data(), sizeof(int16_t) * 8, sample_rate,
|
||||
number_of_channels, number_of_frames);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void AudioTrackSource::InternalSource::clear_buffer() {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
buffer_.clear();
|
||||
}
|
||||
|
||||
webrtc::MediaSourceInterface::SourceState
|
||||
AudioTrackSource::InternalSource::state() const {
|
||||
return webrtc::MediaSourceInterface::SourceState::kLive;
|
||||
}
|
||||
|
||||
bool AudioTrackSource::InternalSource::remote() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
const webrtc::AudioOptions AudioTrackSource::InternalSource::options() const {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
return options_;
|
||||
}
|
||||
|
||||
void AudioTrackSource::InternalSource::set_options(
|
||||
const webrtc::AudioOptions& options) {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
options_ = options;
|
||||
}
|
||||
|
||||
void AudioTrackSource::InternalSource::AddSink(
|
||||
webrtc::AudioTrackSinkInterface* sink) {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
sinks_.push_back(sink);
|
||||
}
|
||||
|
||||
void AudioTrackSource::InternalSource::RemoveSink(
|
||||
webrtc::AudioTrackSinkInterface* sink) {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
sinks_.erase(std::remove(sinks_.begin(), sinks_.end(), sink), sinks_.end());
|
||||
}
|
||||
|
||||
AudioTrackSource::AudioTrackSource(AudioSourceOptions options,
|
||||
int sample_rate,
|
||||
int num_channels,
|
||||
int queue_size_ms,
|
||||
webrtc::TaskQueueFactory* task_queue_factory)
|
||||
: source_(webrtc::make_ref_counted<InternalSource>(
|
||||
to_native_audio_options(options),
|
||||
sample_rate,
|
||||
num_channels,
|
||||
queue_size_ms,
|
||||
task_queue_factory)) {}
|
||||
|
||||
AudioSourceOptions AudioTrackSource::audio_options() const {
|
||||
return to_rust_audio_options(source_->options());
|
||||
}
|
||||
|
||||
void AudioTrackSource::set_audio_options(
|
||||
const AudioSourceOptions& options) const {
|
||||
source_->set_options(to_native_audio_options(options));
|
||||
}
|
||||
|
||||
bool AudioTrackSource::capture_frame(
|
||||
rust::Slice<const int16_t> audio_data,
|
||||
uint32_t sample_rate,
|
||||
uint32_t number_of_channels,
|
||||
size_t number_of_frames,
|
||||
const SourceContext* ctx,
|
||||
void (*on_complete)(const SourceContext*)) const {
|
||||
return source_->capture_frame(audio_data, sample_rate, number_of_channels,
|
||||
number_of_frames, ctx, on_complete);
|
||||
}
|
||||
|
||||
void AudioTrackSource::clear_buffer() const {
|
||||
source_->clear_buffer();
|
||||
}
|
||||
|
||||
std::shared_ptr<AudioTrackSource> new_audio_track_source(
|
||||
AudioSourceOptions options,
|
||||
int sample_rate,
|
||||
int num_channels,
|
||||
int queue_size_ms) {
|
||||
return std::make_shared<AudioTrackSource>(options, sample_rate, num_channels,
|
||||
queue_size_ms,
|
||||
GetGlobalTaskQueueFactory());
|
||||
}
|
||||
|
||||
webrtc::scoped_refptr<AudioTrackSource::InternalSource> AudioTrackSource::get()
|
||||
const {
|
||||
return source_;
|
||||
}
|
||||
|
||||
} // namespace livekit_ffi
|
||||
@@ -0,0 +1,124 @@
|
||||
// Copyright 2025 LiveKit, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use cxx::type_id;
|
||||
use cxx::ExternType;
|
||||
use std::any::Any;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::impl_thread_safety;
|
||||
|
||||
#[cxx::bridge(namespace = "livekit_ffi")]
|
||||
pub mod ffi {
|
||||
|
||||
pub struct AudioSourceOptions {
|
||||
pub echo_cancellation: bool,
|
||||
pub noise_suppression: bool,
|
||||
pub auto_gain_control: bool,
|
||||
}
|
||||
|
||||
extern "C++" {
|
||||
include!("livekit/media_stream_track.h");
|
||||
|
||||
type MediaStreamTrack = crate::media_stream_track::ffi::MediaStreamTrack;
|
||||
type CompleteCallback = crate::audio_track::CompleteCallback;
|
||||
}
|
||||
|
||||
unsafe extern "C++" {
|
||||
include!("livekit/audio_track.h");
|
||||
|
||||
type AudioTrack;
|
||||
type NativeAudioSink;
|
||||
type AudioTrackSource;
|
||||
|
||||
fn add_sink(self: &AudioTrack, sink: &SharedPtr<NativeAudioSink>);
|
||||
fn remove_sink(self: &AudioTrack, sink: &SharedPtr<NativeAudioSink>);
|
||||
fn new_native_audio_sink(
|
||||
observer: Box<AudioSinkWrapper>,
|
||||
sample_rate: i32,
|
||||
num_channels: i32,
|
||||
) -> SharedPtr<NativeAudioSink>;
|
||||
|
||||
unsafe fn capture_frame(
|
||||
self: &AudioTrackSource,
|
||||
data: &[i16],
|
||||
sample_rate: u32,
|
||||
nb_channels: u32,
|
||||
nb_frames: usize,
|
||||
userdata: *const SourceContext,
|
||||
on_complete: CompleteCallback,
|
||||
) -> bool;
|
||||
fn clear_buffer(self: &AudioTrackSource);
|
||||
fn audio_options(self: &AudioTrackSource) -> AudioSourceOptions;
|
||||
fn set_audio_options(self: &AudioTrackSource, options: &AudioSourceOptions);
|
||||
|
||||
fn new_audio_track_source(
|
||||
options: AudioSourceOptions,
|
||||
sample_rate: i32,
|
||||
num_channels: i32,
|
||||
queue_size_ms: i32,
|
||||
) -> SharedPtr<AudioTrackSource>;
|
||||
|
||||
fn audio_to_media(track: SharedPtr<AudioTrack>) -> SharedPtr<MediaStreamTrack>;
|
||||
unsafe fn media_to_audio(track: SharedPtr<MediaStreamTrack>) -> SharedPtr<AudioTrack>;
|
||||
fn _shared_audio_track() -> SharedPtr<AudioTrack>;
|
||||
fn _shared_audio_track_source() -> SharedPtr<AudioTrackSource>;
|
||||
}
|
||||
|
||||
extern "Rust" {
|
||||
type AudioSinkWrapper;
|
||||
type SourceContext;
|
||||
|
||||
fn on_data(
|
||||
self: &AudioSinkWrapper,
|
||||
data: &[i16],
|
||||
sample_rate: i32,
|
||||
nb_channels: usize,
|
||||
nb_frames: usize,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl_thread_safety!(ffi::AudioTrack, Send + Sync);
|
||||
impl_thread_safety!(ffi::NativeAudioSink, Send + Sync);
|
||||
impl_thread_safety!(ffi::AudioTrackSource, Send + Sync);
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct SourceContext(pub Box<dyn Any + Send>);
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct CompleteCallback(pub extern "C" fn(ctx: *const SourceContext));
|
||||
|
||||
unsafe impl ExternType for CompleteCallback {
|
||||
type Id = type_id!("livekit_ffi::CompleteCallback");
|
||||
type Kind = cxx::kind::Trivial;
|
||||
}
|
||||
|
||||
pub trait AudioSink: Send {
|
||||
fn on_data(&self, data: &[i16], sample_rate: i32, nb_channels: usize, nb_frames: usize);
|
||||
}
|
||||
|
||||
pub struct AudioSinkWrapper {
|
||||
observer: Arc<dyn AudioSink>,
|
||||
}
|
||||
|
||||
impl AudioSinkWrapper {
|
||||
pub fn new(observer: Arc<dyn AudioSink>) -> Self {
|
||||
Self { observer }
|
||||
}
|
||||
|
||||
fn on_data(&self, data: &[i16], sample_rate: i32, nb_channels: usize, nb_frames: usize) {
|
||||
self.observer.on_data(data, sample_rate, nb_channels, nb_frames);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright 2025 LiveKit, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "livekit/candidate.h"
|
||||
|
||||
namespace livekit_ffi {
|
||||
Candidate::Candidate(const webrtc::Candidate& candidate)
|
||||
: candidate_(candidate) {}
|
||||
} // namespace livekit_ffi
|
||||
@@ -0,0 +1,24 @@
|
||||
// Copyright 2025 LiveKit, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#[cxx::bridge(namespace = "livekit_ffi")]
|
||||
pub mod ffi {
|
||||
unsafe extern "C++" {
|
||||
include!("livekit/candidate.h");
|
||||
|
||||
type Candidate; // webrtc::Candidate
|
||||
|
||||
fn _shared_candidate() -> SharedPtr<Candidate>;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Copyright 2025 LiveKit, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "livekit/data_channel.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "rtc_base/synchronization/mutex.h"
|
||||
#include "webrtc-sys/src/data_channel.rs.h"
|
||||
|
||||
namespace livekit_ffi {
|
||||
|
||||
webrtc::DataChannelInit to_native_data_channel_init(DataChannelInit init) {
|
||||
webrtc::DataChannelInit rtc_init{};
|
||||
rtc_init.id = init.id;
|
||||
rtc_init.negotiated = init.negotiated;
|
||||
rtc_init.ordered = init.ordered;
|
||||
rtc_init.protocol = init.protocol.c_str();
|
||||
|
||||
if (init.has_max_retransmit_time)
|
||||
rtc_init.maxRetransmitTime = init.max_retransmit_time;
|
||||
|
||||
if (init.has_max_retransmits)
|
||||
rtc_init.maxRetransmits = init.max_retransmits;
|
||||
|
||||
if (init.has_priority)
|
||||
rtc_init.priority = webrtc::PriorityValue(static_cast<webrtc::Priority>(init.priority));
|
||||
|
||||
return rtc_init;
|
||||
}
|
||||
|
||||
DataChannel::DataChannel(
|
||||
std::shared_ptr<RtcRuntime> rtc_runtime,
|
||||
webrtc::scoped_refptr<webrtc::DataChannelInterface> data_channel)
|
||||
: rtc_runtime_(rtc_runtime), data_channel_(std::move(data_channel)) {
|
||||
RTC_LOG(LS_VERBOSE) << "DataChannel::DataChannel()";
|
||||
}
|
||||
|
||||
DataChannel::~DataChannel() {
|
||||
RTC_LOG(LS_VERBOSE) << "DataChannel::~DataChannel()";
|
||||
unregister_observer();
|
||||
}
|
||||
|
||||
void DataChannel::register_observer(
|
||||
rust::Box<DataChannelObserverWrapper> observer) const {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
|
||||
data_channel_->UnregisterObserver();
|
||||
|
||||
observer_ =
|
||||
std::make_unique<NativeDataChannelObserver>(std::move(observer), this);
|
||||
data_channel_->RegisterObserver(observer_.get());
|
||||
}
|
||||
|
||||
void DataChannel::unregister_observer() const {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
data_channel_->UnregisterObserver();
|
||||
observer_ = nullptr;
|
||||
}
|
||||
|
||||
bool DataChannel::send(const DataBuffer& buffer) const {
|
||||
return data_channel_->Send(webrtc::DataBuffer{
|
||||
webrtc::CopyOnWriteBuffer(buffer.ptr, buffer.len), buffer.binary});
|
||||
}
|
||||
|
||||
int DataChannel::id() const {
|
||||
return data_channel_->id();
|
||||
}
|
||||
|
||||
rust::String DataChannel::label() const {
|
||||
return data_channel_->label();
|
||||
}
|
||||
|
||||
DataState DataChannel::state() const {
|
||||
return static_cast<DataState>(data_channel_->state());
|
||||
}
|
||||
|
||||
void DataChannel::close() const {
|
||||
return data_channel_->Close();
|
||||
}
|
||||
|
||||
uint64_t DataChannel::buffered_amount() const {
|
||||
return data_channel_->buffered_amount();
|
||||
}
|
||||
|
||||
NativeDataChannelObserver::NativeDataChannelObserver(
|
||||
rust::Box<DataChannelObserverWrapper> observer,
|
||||
const DataChannel* dc)
|
||||
: observer_(std::move(observer)), dc_(dc) {}
|
||||
|
||||
void NativeDataChannelObserver::OnStateChange() {
|
||||
observer_->on_state_change(dc_->state());
|
||||
}
|
||||
|
||||
void NativeDataChannelObserver::OnMessage(const webrtc::DataBuffer& buffer) {
|
||||
DataBuffer data{};
|
||||
data.ptr = buffer.data.data();
|
||||
data.len = buffer.data.size();
|
||||
data.binary = buffer.binary;
|
||||
observer_->on_message(data);
|
||||
}
|
||||
|
||||
void NativeDataChannelObserver::OnBufferedAmountChange(
|
||||
uint64_t sent_data_size) {
|
||||
observer_->on_buffered_amount_change(sent_data_size);
|
||||
}
|
||||
|
||||
} // namespace livekit_ffi
|
||||
@@ -0,0 +1,118 @@
|
||||
// Copyright 2025 LiveKit, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::impl_thread_safety;
|
||||
|
||||
#[cxx::bridge(namespace = "livekit_ffi")]
|
||||
pub mod ffi {
|
||||
#[derive(Debug)]
|
||||
#[repr(i32)]
|
||||
pub enum Priority {
|
||||
VeryLow,
|
||||
Low,
|
||||
Medium,
|
||||
High,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DataChannelInit {
|
||||
pub ordered: bool,
|
||||
pub has_max_retransmit_time: bool,
|
||||
pub max_retransmit_time: i32,
|
||||
pub has_max_retransmits: bool,
|
||||
pub max_retransmits: i32,
|
||||
pub protocol: String,
|
||||
pub negotiated: bool,
|
||||
pub id: i32,
|
||||
pub has_priority: bool,
|
||||
pub priority: Priority,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DataBuffer {
|
||||
pub ptr: *const u8,
|
||||
pub len: usize,
|
||||
pub binary: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[repr(i32)]
|
||||
pub enum DataState {
|
||||
Connecting,
|
||||
Open,
|
||||
Closing,
|
||||
Closed,
|
||||
}
|
||||
|
||||
unsafe extern "C++" {
|
||||
include!("livekit/data_channel.h");
|
||||
|
||||
type DataChannel;
|
||||
|
||||
fn register_observer(self: &DataChannel, observer: Box<DataChannelObserverWrapper>);
|
||||
fn unregister_observer(self: &DataChannel);
|
||||
|
||||
fn send(self: &DataChannel, data: &DataBuffer) -> bool;
|
||||
fn id(self: &DataChannel) -> i32;
|
||||
fn label(self: &DataChannel) -> String;
|
||||
fn state(self: &DataChannel) -> DataState;
|
||||
fn close(self: &DataChannel);
|
||||
fn buffered_amount(self: &DataChannel) -> u64;
|
||||
|
||||
fn _shared_data_channel() -> SharedPtr<DataChannel>; // Ignore
|
||||
}
|
||||
|
||||
extern "Rust" {
|
||||
type DataChannelObserverWrapper;
|
||||
|
||||
fn on_state_change(self: &DataChannelObserverWrapper, state: DataState);
|
||||
fn on_message(self: &DataChannelObserverWrapper, buffer: DataBuffer);
|
||||
fn on_buffered_amount_change(self: &DataChannelObserverWrapper, sent_data_size: u64);
|
||||
}
|
||||
}
|
||||
|
||||
impl_thread_safety!(ffi::DataChannel, Send + Sync);
|
||||
|
||||
pub trait DataChannelObserver: Send + Sync {
|
||||
fn on_state_change(&self, state: ffi::DataState);
|
||||
fn on_message(&self, data: &[u8], is_binary: bool);
|
||||
fn on_buffered_amount_change(&self, sent_data_size: u64);
|
||||
}
|
||||
|
||||
pub struct DataChannelObserverWrapper {
|
||||
observer: Arc<dyn DataChannelObserver>,
|
||||
}
|
||||
|
||||
impl DataChannelObserverWrapper {
|
||||
pub fn new(observer: Arc<dyn DataChannelObserver>) -> Self {
|
||||
Self { observer }
|
||||
}
|
||||
|
||||
fn on_state_change(&self, state: ffi::DataState) {
|
||||
self.observer.on_state_change(state);
|
||||
}
|
||||
|
||||
fn on_message(&self, buffer: ffi::DataBuffer) {
|
||||
unsafe {
|
||||
let data = std::slice::from_raw_parts(buffer.ptr, buffer.len);
|
||||
self.observer.on_message(data, buffer.binary);
|
||||
}
|
||||
}
|
||||
|
||||
fn on_buffered_amount_change(&self, sent_data_size: u64) {
|
||||
self.observer.on_buffered_amount_change(sent_data_size);
|
||||
}
|
||||
}
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Copyright 2025 LiveKit, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "livekit/desktop_capturer.h"
|
||||
|
||||
#include "modules/desktop_capture/desktop_capture_options.h"
|
||||
|
||||
using SourceList = webrtc::DesktopCapturer::SourceList;
|
||||
|
||||
namespace livekit_ffi {
|
||||
|
||||
std::unique_ptr<DesktopCapturer> new_desktop_capturer(
|
||||
DesktopCapturerOptions options) {
|
||||
webrtc::DesktopCaptureOptions webrtc_options =
|
||||
webrtc::DesktopCaptureOptions::CreateDefault();
|
||||
#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS)
|
||||
webrtc_options.set_allow_sck_capturer(true);
|
||||
webrtc_options.set_allow_sck_system_picker(options.allow_sck_system_picker);
|
||||
#endif /* defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) */
|
||||
#ifdef _WIN64
|
||||
switch (options.source_type) {
|
||||
case SourceType::Screen:
|
||||
webrtc_options.set_allow_wgc_screen_capturer(true);
|
||||
break;
|
||||
case SourceType::Window:
|
||||
webrtc_options.set_allow_wgc_window_capturer(true);
|
||||
// https://github.com/webrtc-sdk/webrtc/blob/m137_release/modules/desktop_capture/desktop_capture_options.h#L133-L142
|
||||
webrtc_options.set_enumerate_current_process_windows(false);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
webrtc_options.set_allow_directx_capturer(true);
|
||||
#endif /* _WIN64 */
|
||||
#ifdef WEBRTC_USE_PIPEWIRE
|
||||
webrtc_options.set_allow_pipewire(true);
|
||||
#endif /* WEBRTC_USE_PIPEWIRE */
|
||||
|
||||
// prefer_cursor_embedded indicate that the capturer should try to include the
|
||||
// cursor in the frame
|
||||
webrtc_options.set_prefer_cursor_embedded(options.include_cursor);
|
||||
|
||||
std::unique_ptr<webrtc::DesktopCapturer> capturer = nullptr;
|
||||
switch (options.source_type) {
|
||||
case SourceType::Window:
|
||||
capturer = webrtc::DesktopCapturer::CreateWindowCapturer(webrtc_options);
|
||||
break;
|
||||
case SourceType::Screen:
|
||||
capturer = webrtc::DesktopCapturer::CreateScreenCapturer(webrtc_options);
|
||||
break;
|
||||
case SourceType::Generic:
|
||||
capturer = webrtc::DesktopCapturer::CreateGenericCapturer(webrtc_options);
|
||||
break;
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!capturer) {
|
||||
return nullptr;
|
||||
}
|
||||
return std::make_unique<DesktopCapturer>(std::move(capturer));
|
||||
}
|
||||
|
||||
void DesktopCapturer::start(
|
||||
rust::Box<DesktopCapturerCallbackWrapper> callback) {
|
||||
this->callback = std::move(callback);
|
||||
capturer->Start(this);
|
||||
}
|
||||
|
||||
void DesktopCapturer::OnCaptureResult(
|
||||
webrtc::DesktopCapturer::Result result,
|
||||
std::unique_ptr<webrtc::DesktopFrame> frame) {
|
||||
CaptureResult ret_result = CaptureResult::ErrorPermanent;
|
||||
switch (result) {
|
||||
case webrtc::DesktopCapturer::Result::SUCCESS:
|
||||
ret_result = CaptureResult::Success;
|
||||
break;
|
||||
case webrtc::DesktopCapturer::Result::ERROR_PERMANENT:
|
||||
ret_result = CaptureResult::ErrorPermanent;
|
||||
break;
|
||||
case webrtc::DesktopCapturer::Result::ERROR_TEMPORARY:
|
||||
ret_result = CaptureResult::ErrorTemporary;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (callback) {
|
||||
(*callback)->on_capture_result(
|
||||
ret_result, std::make_unique<DesktopFrame>(std::move(frame)));
|
||||
}
|
||||
}
|
||||
|
||||
rust::Vec<Source> DesktopCapturer::get_source_list() const {
|
||||
SourceList list{};
|
||||
bool res = capturer->GetSourceList(&list);
|
||||
rust::Vec<Source> source_list{};
|
||||
if (res) {
|
||||
for (auto& source : list) {
|
||||
source_list.push_back(Source{static_cast<uint64_t>(source.id),
|
||||
source.title, source.display_id});
|
||||
}
|
||||
}
|
||||
return source_list;
|
||||
}
|
||||
} // namespace livekit_ffi
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
// Copyright 2025 LiveKit, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use cxx::UniquePtr;
|
||||
use ffi::CaptureResult;
|
||||
|
||||
use crate::{desktop_capturer::ffi::DesktopFrame, impl_thread_safety};
|
||||
|
||||
#[cxx::bridge(namespace = "livekit_ffi")]
|
||||
pub mod ffi {
|
||||
#[derive(Clone)]
|
||||
struct Source {
|
||||
id: u64,
|
||||
title: String,
|
||||
display_id: i64,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
enum SourceType {
|
||||
Screen,
|
||||
Window,
|
||||
Generic,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct DesktopCapturerOptions {
|
||||
source_type: SourceType,
|
||||
include_cursor: bool,
|
||||
allow_sck_system_picker: bool,
|
||||
}
|
||||
|
||||
enum CaptureResult {
|
||||
Success,
|
||||
ErrorTemporary,
|
||||
ErrorPermanent,
|
||||
}
|
||||
|
||||
unsafe extern "C++" {
|
||||
include!("livekit/desktop_capturer.h");
|
||||
|
||||
type DesktopCapturer;
|
||||
type DesktopFrame;
|
||||
|
||||
fn new_desktop_capturer(options: DesktopCapturerOptions) -> UniquePtr<DesktopCapturer>;
|
||||
fn capture_frame(self: &DesktopCapturer);
|
||||
fn get_source_list(self: &DesktopCapturer) -> Vec<Source>;
|
||||
fn select_source(self: &DesktopCapturer, id: u64) -> bool;
|
||||
fn start(self: Pin<&mut DesktopCapturer>, callback: Box<DesktopCapturerCallbackWrapper>);
|
||||
|
||||
fn width(self: &DesktopFrame) -> i32;
|
||||
fn height(self: &DesktopFrame) -> i32;
|
||||
fn stride(self: &DesktopFrame) -> i32;
|
||||
fn left(self: &DesktopFrame) -> i32;
|
||||
fn top(self: &DesktopFrame) -> i32;
|
||||
fn data(self: &DesktopFrame) -> *const u8;
|
||||
}
|
||||
|
||||
extern "Rust" {
|
||||
type DesktopCapturerCallbackWrapper;
|
||||
|
||||
fn on_capture_result(
|
||||
self: &mut DesktopCapturerCallbackWrapper,
|
||||
result: CaptureResult,
|
||||
frame: UniquePtr<DesktopFrame>,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl_thread_safety!(ffi::DesktopCapturer, Send + Sync);
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum CaptureError {
|
||||
Temporary,
|
||||
Permanent,
|
||||
}
|
||||
|
||||
pub trait DesktopCapturerCallback: Send {
|
||||
fn on_capture_result(&mut self, result: Result<UniquePtr<DesktopFrame>, CaptureError>);
|
||||
}
|
||||
|
||||
pub struct DesktopCapturerCallbackWrapper {
|
||||
callback: Box<dyn DesktopCapturerCallback>,
|
||||
}
|
||||
|
||||
impl DesktopCapturerCallbackWrapper {
|
||||
pub fn new(callback: Box<dyn DesktopCapturerCallback>) -> Self {
|
||||
Self { callback }
|
||||
}
|
||||
|
||||
fn on_capture_result(&mut self, result: CaptureResult, frame: UniquePtr<DesktopFrame>) {
|
||||
match result {
|
||||
CaptureResult::Success => self.callback.on_capture_result(Ok(frame)),
|
||||
CaptureResult::ErrorTemporary => {
|
||||
self.callback.on_capture_result(Err(CaptureError::Temporary))
|
||||
}
|
||||
CaptureResult::ErrorPermanent => {
|
||||
self.callback.on_capture_result(Err(CaptureError::Permanent))
|
||||
}
|
||||
_ => self.callback.on_capture_result(Err(CaptureError::Permanent)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,364 @@
|
||||
/*
|
||||
* Copyright 2025 LiveKit, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "livekit/frame_cryptor.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "absl/types/optional.h"
|
||||
#include "api/make_ref_counted.h"
|
||||
#include "livekit/peer_connection.h"
|
||||
#include "livekit/peer_connection_factory.h"
|
||||
#include "livekit/packet_trailer.h"
|
||||
#include "livekit/webrtc.h"
|
||||
#include "rtc_base/thread.h"
|
||||
#include "webrtc-sys/src/frame_cryptor.rs.h"
|
||||
|
||||
namespace livekit_ffi {
|
||||
|
||||
class ChainedFrameTransformer : public webrtc::FrameTransformerInterface,
|
||||
public webrtc::TransformedFrameCallback {
|
||||
public:
|
||||
ChainedFrameTransformer(
|
||||
webrtc::scoped_refptr<webrtc::FrameTransformerInterface> first,
|
||||
webrtc::scoped_refptr<webrtc::FrameTransformerInterface> second)
|
||||
: first_(std::move(first)), second_(std::move(second)) {}
|
||||
|
||||
void Transform(
|
||||
std::unique_ptr<webrtc::TransformableFrameInterface> frame) override {
|
||||
first_->Transform(std::move(frame));
|
||||
}
|
||||
|
||||
void RegisterTransformedFrameCallback(
|
||||
webrtc::scoped_refptr<webrtc::TransformedFrameCallback> callback) override {
|
||||
second_->RegisterTransformedFrameCallback(callback);
|
||||
first_->RegisterTransformedFrameCallback(
|
||||
webrtc::scoped_refptr<webrtc::TransformedFrameCallback>(this));
|
||||
}
|
||||
|
||||
void RegisterTransformedFrameSinkCallback(
|
||||
webrtc::scoped_refptr<webrtc::TransformedFrameCallback> callback,
|
||||
uint32_t ssrc) override {
|
||||
second_->RegisterTransformedFrameSinkCallback(callback, ssrc);
|
||||
first_->RegisterTransformedFrameSinkCallback(
|
||||
webrtc::scoped_refptr<webrtc::TransformedFrameCallback>(this), ssrc);
|
||||
}
|
||||
|
||||
void UnregisterTransformedFrameCallback() override {
|
||||
first_->UnregisterTransformedFrameCallback();
|
||||
second_->UnregisterTransformedFrameCallback();
|
||||
}
|
||||
|
||||
void UnregisterTransformedFrameSinkCallback(uint32_t ssrc) override {
|
||||
first_->UnregisterTransformedFrameSinkCallback(ssrc);
|
||||
second_->UnregisterTransformedFrameSinkCallback(ssrc);
|
||||
}
|
||||
|
||||
void OnTransformedFrame(
|
||||
std::unique_ptr<webrtc::TransformableFrameInterface> frame) override {
|
||||
second_->Transform(std::move(frame));
|
||||
}
|
||||
|
||||
private:
|
||||
webrtc::scoped_refptr<webrtc::FrameTransformerInterface> first_;
|
||||
webrtc::scoped_refptr<webrtc::FrameTransformerInterface> second_;
|
||||
};
|
||||
|
||||
webrtc::FrameCryptorTransformer::Algorithm AlgorithmToFrameCryptorAlgorithm(
|
||||
Algorithm algorithm) {
|
||||
switch (algorithm) {
|
||||
case Algorithm::AesGcm:
|
||||
return webrtc::FrameCryptorTransformer::Algorithm::kAesGcm;
|
||||
case Algorithm::AesCbc:
|
||||
return webrtc::FrameCryptorTransformer::Algorithm::kAesCbc;
|
||||
default:
|
||||
return webrtc::FrameCryptorTransformer::Algorithm::kAesGcm;
|
||||
}
|
||||
}
|
||||
|
||||
webrtc::KeyDerivationAlgorithm
|
||||
KeyDerivationAlgorithmToFrameCryptorKeyDerivationAlgorithm(
|
||||
KeyDerivationAlgorithm algorithm) {
|
||||
switch (algorithm) {
|
||||
case KeyDerivationAlgorithm::PBKDF2:
|
||||
return webrtc::KeyDerivationAlgorithm::kPBKDF2;
|
||||
case KeyDerivationAlgorithm::HKDF:
|
||||
return webrtc::KeyDerivationAlgorithm::kHKDF;
|
||||
default:
|
||||
return webrtc::KeyDerivationAlgorithm::kPBKDF2;
|
||||
}
|
||||
}
|
||||
|
||||
KeyProvider::KeyProvider(KeyProviderOptions options) {
|
||||
webrtc::KeyProviderOptions rtc_options;
|
||||
rtc_options.shared_key = options.shared_key;
|
||||
|
||||
std::vector<uint8_t> ratchet_salt;
|
||||
std::copy(options.ratchet_salt.begin(), options.ratchet_salt.end(),
|
||||
std::back_inserter(ratchet_salt));
|
||||
|
||||
rtc_options.ratchet_salt = ratchet_salt;
|
||||
rtc_options.ratchet_window_size = options.ratchet_window_size;
|
||||
rtc_options.failure_tolerance = options.failure_tolerance;
|
||||
rtc_options.key_ring_size = options.key_ring_size;
|
||||
rtc_options.key_derivation_algorithm =
|
||||
KeyDerivationAlgorithmToFrameCryptorKeyDerivationAlgorithm(
|
||||
options.key_derivation_algorithm);
|
||||
impl_ =
|
||||
new webrtc::RefCountedObject<webrtc::DefaultKeyProviderImpl>(rtc_options);
|
||||
}
|
||||
|
||||
FrameCryptor::FrameCryptor(
|
||||
std::shared_ptr<RtcRuntime> rtc_runtime,
|
||||
const std::string participant_id,
|
||||
webrtc::FrameCryptorTransformer::Algorithm algorithm,
|
||||
webrtc::scoped_refptr<webrtc::KeyProvider> key_provider,
|
||||
webrtc::scoped_refptr<webrtc::RtpSenderInterface> sender)
|
||||
: rtc_runtime_(rtc_runtime),
|
||||
participant_id_(participant_id),
|
||||
key_provider_(key_provider),
|
||||
sender_(sender) {
|
||||
auto mediaType =
|
||||
sender->track()->kind() == "audio"
|
||||
? webrtc::FrameCryptorTransformer::MediaType::kAudioFrame
|
||||
: webrtc::FrameCryptorTransformer::MediaType::kVideoFrame;
|
||||
e2ee_transformer_ = webrtc::scoped_refptr<webrtc::FrameCryptorTransformer>(
|
||||
new webrtc::FrameCryptorTransformer(rtc_runtime->signaling_thread(),
|
||||
participant_id, mediaType, algorithm,
|
||||
key_provider_));
|
||||
sender->SetEncoderToPacketizerFrameTransformer(e2ee_transformer_);
|
||||
e2ee_transformer_->SetEnabled(false);
|
||||
}
|
||||
|
||||
FrameCryptor::FrameCryptor(
|
||||
std::shared_ptr<RtcRuntime> rtc_runtime,
|
||||
const std::string participant_id,
|
||||
webrtc::FrameCryptorTransformer::Algorithm algorithm,
|
||||
webrtc::scoped_refptr<webrtc::KeyProvider> key_provider,
|
||||
webrtc::scoped_refptr<webrtc::RtpReceiverInterface> receiver)
|
||||
: rtc_runtime_(rtc_runtime),
|
||||
participant_id_(participant_id),
|
||||
key_provider_(key_provider),
|
||||
receiver_(receiver) {
|
||||
auto mediaType =
|
||||
receiver->track()->kind() == "audio"
|
||||
? webrtc::FrameCryptorTransformer::MediaType::kAudioFrame
|
||||
: webrtc::FrameCryptorTransformer::MediaType::kVideoFrame;
|
||||
e2ee_transformer_ = webrtc::scoped_refptr<webrtc::FrameCryptorTransformer>(
|
||||
new webrtc::FrameCryptorTransformer(rtc_runtime->signaling_thread(),
|
||||
participant_id, mediaType, algorithm,
|
||||
key_provider_));
|
||||
receiver->SetDepacketizerToDecoderFrameTransformer(e2ee_transformer_);
|
||||
e2ee_transformer_->SetEnabled(false);
|
||||
}
|
||||
|
||||
FrameCryptor::~FrameCryptor() {
|
||||
if (observer_) {
|
||||
unregister_observer();
|
||||
}
|
||||
}
|
||||
|
||||
void FrameCryptor::register_observer(
|
||||
rust::Box<RtcFrameCryptorObserverWrapper> observer) const {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
observer_ = webrtc::make_ref_counted<NativeFrameCryptorObserver>(
|
||||
std::move(observer), this);
|
||||
e2ee_transformer_->RegisterFrameCryptorTransformerObserver(observer_);
|
||||
}
|
||||
|
||||
void FrameCryptor::unregister_observer() const {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
observer_ = nullptr;
|
||||
e2ee_transformer_->UnRegisterFrameCryptorTransformerObserver();
|
||||
}
|
||||
|
||||
void FrameCryptor::set_packet_trailer_handler(
|
||||
std::shared_ptr<PacketTrailerHandler> handler) const {
|
||||
if (!handler) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto timestamp_transformer = handler->transformer();
|
||||
if (!timestamp_transformer) {
|
||||
return;
|
||||
}
|
||||
|
||||
webrtc::scoped_refptr<webrtc::FrameTransformerInterface> first;
|
||||
webrtc::scoped_refptr<webrtc::FrameTransformerInterface> second;
|
||||
if (sender_) {
|
||||
first = e2ee_transformer_;
|
||||
second = timestamp_transformer;
|
||||
} else if (receiver_) {
|
||||
first = timestamp_transformer;
|
||||
second = e2ee_transformer_;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
chained_transformer_ =
|
||||
webrtc::make_ref_counted<ChainedFrameTransformer>(first, second);
|
||||
|
||||
if (sender_) {
|
||||
sender_->SetEncoderToPacketizerFrameTransformer(chained_transformer_);
|
||||
}
|
||||
if (receiver_) {
|
||||
receiver_->SetDepacketizerToDecoderFrameTransformer(chained_transformer_);
|
||||
}
|
||||
}
|
||||
|
||||
NativeFrameCryptorObserver::NativeFrameCryptorObserver(
|
||||
rust::Box<RtcFrameCryptorObserverWrapper> observer,
|
||||
const FrameCryptor* fc)
|
||||
: observer_(std::move(observer)), fc_(fc) {}
|
||||
|
||||
NativeFrameCryptorObserver::~NativeFrameCryptorObserver() {}
|
||||
|
||||
void NativeFrameCryptorObserver::OnFrameCryptionStateChanged(
|
||||
const std::string participant_id,
|
||||
webrtc::FrameCryptionState state) {
|
||||
observer_->on_frame_cryption_state_change(
|
||||
participant_id, static_cast<FrameCryptionState>(state));
|
||||
}
|
||||
|
||||
void FrameCryptor::set_enabled(bool enabled) const {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
e2ee_transformer_->SetEnabled(enabled);
|
||||
}
|
||||
|
||||
bool FrameCryptor::enabled() const {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
return e2ee_transformer_->enabled();
|
||||
}
|
||||
|
||||
void FrameCryptor::set_key_index(int32_t index) const {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
e2ee_transformer_->SetKeyIndex(index);
|
||||
}
|
||||
|
||||
int32_t FrameCryptor::key_index() const {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
return e2ee_transformer_->key_index();
|
||||
}
|
||||
|
||||
DataPacketCryptor::DataPacketCryptor(
|
||||
webrtc::FrameCryptorTransformer::Algorithm algorithm,
|
||||
webrtc::scoped_refptr<webrtc::KeyProvider> key_provider)
|
||||
: data_packet_cryptor_(
|
||||
webrtc::make_ref_counted<webrtc::DataPacketCryptor>(algorithm,
|
||||
key_provider)) {}
|
||||
|
||||
EncryptedPacket DataPacketCryptor::encrypt_data_packet(
|
||||
const ::rust::String participant_id,
|
||||
uint32_t key_index,
|
||||
rust::Vec<::std::uint8_t> data) const {
|
||||
std::vector<uint8_t> data_vec;
|
||||
std::copy(data.begin(), data.end(), std::back_inserter(data_vec));
|
||||
|
||||
auto result = data_packet_cryptor_->Encrypt(
|
||||
std::string(participant_id.data(), participant_id.size()), key_index,
|
||||
data_vec);
|
||||
|
||||
if (!result.ok()) {
|
||||
throw std::runtime_error(std::string("Failed to encrypt data packet: ") +
|
||||
result.error().message());
|
||||
}
|
||||
|
||||
auto& packet = result.value();
|
||||
|
||||
EncryptedPacket encrypted_packet;
|
||||
encrypted_packet.data = rust::Vec<uint8_t>();
|
||||
std::copy(packet->data.begin(), packet->data.end(),
|
||||
std::back_inserter(encrypted_packet.data));
|
||||
|
||||
encrypted_packet.iv = rust::Vec<uint8_t>();
|
||||
std::copy(packet->iv.begin(), packet->iv.end(),
|
||||
std::back_inserter(encrypted_packet.iv));
|
||||
|
||||
encrypted_packet.key_index = packet->key_index;
|
||||
|
||||
return encrypted_packet;
|
||||
}
|
||||
|
||||
rust::Vec<::std::uint8_t> DataPacketCryptor::decrypt_data_packet(
|
||||
const ::rust::String participant_id,
|
||||
const EncryptedPacket& encrypted_packet) const {
|
||||
std::vector<uint8_t> data_vec;
|
||||
std::copy(encrypted_packet.data.begin(), encrypted_packet.data.end(),
|
||||
std::back_inserter(data_vec));
|
||||
|
||||
std::vector<uint8_t> iv_vec;
|
||||
std::copy(encrypted_packet.iv.begin(), encrypted_packet.iv.end(),
|
||||
std::back_inserter(iv_vec));
|
||||
|
||||
auto native_encrypted_packet =
|
||||
webrtc::make_ref_counted<webrtc::EncryptedPacket>(
|
||||
std::move(data_vec), std::move(iv_vec), encrypted_packet.key_index);
|
||||
|
||||
auto result = data_packet_cryptor_->Decrypt(
|
||||
std::string(participant_id.data(), participant_id.size()),
|
||||
native_encrypted_packet);
|
||||
|
||||
if (!result.ok()) {
|
||||
throw std::runtime_error(std::string("Failed to decrypt data packet: ") +
|
||||
result.error().message());
|
||||
}
|
||||
|
||||
rust::Vec<uint8_t> decrypted_data;
|
||||
auto& decrypted = result.value();
|
||||
std::copy(decrypted.begin(), decrypted.end(),
|
||||
std::back_inserter(decrypted_data));
|
||||
return decrypted_data;
|
||||
}
|
||||
|
||||
std::shared_ptr<KeyProvider> new_key_provider(KeyProviderOptions options) {
|
||||
return std::make_shared<KeyProvider>(options);
|
||||
}
|
||||
|
||||
std::shared_ptr<FrameCryptor> new_frame_cryptor_for_rtp_sender(
|
||||
std::shared_ptr<PeerConnectionFactory> peer_factory,
|
||||
const ::rust::String participant_id,
|
||||
Algorithm algorithm,
|
||||
std::shared_ptr<KeyProvider> key_provider,
|
||||
std::shared_ptr<RtpSender> sender) {
|
||||
return std::make_shared<FrameCryptor>(
|
||||
peer_factory->rtc_runtime(),
|
||||
std::string(participant_id.data(), participant_id.size()),
|
||||
AlgorithmToFrameCryptorAlgorithm(algorithm),
|
||||
key_provider->rtc_key_provider(), sender->rtc_sender());
|
||||
}
|
||||
|
||||
std::shared_ptr<FrameCryptor> new_frame_cryptor_for_rtp_receiver(
|
||||
std::shared_ptr<PeerConnectionFactory> peer_factory,
|
||||
const ::rust::String participant_id,
|
||||
Algorithm algorithm,
|
||||
std::shared_ptr<KeyProvider> key_provider,
|
||||
std::shared_ptr<RtpReceiver> receiver) {
|
||||
return std::make_shared<FrameCryptor>(
|
||||
peer_factory->rtc_runtime(),
|
||||
std::string(participant_id.data(), participant_id.size()),
|
||||
AlgorithmToFrameCryptorAlgorithm(algorithm),
|
||||
key_provider->rtc_key_provider(), receiver->rtc_receiver());
|
||||
}
|
||||
|
||||
std::shared_ptr<DataPacketCryptor> new_data_packet_cryptor(
|
||||
Algorithm algorithm,
|
||||
std::shared_ptr<KeyProvider> key_provider) {
|
||||
return std::make_shared<DataPacketCryptor>(
|
||||
AlgorithmToFrameCryptorAlgorithm(algorithm),
|
||||
key_provider->rtc_key_provider());
|
||||
}
|
||||
|
||||
} // namespace livekit_ffi
|
||||
@@ -0,0 +1,275 @@
|
||||
// Copyright 2025 LiveKit, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::impl_thread_safety;
|
||||
|
||||
#[cxx::bridge(namespace = "livekit_ffi")]
|
||||
pub mod ffi {
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct KeyProviderOptions {
|
||||
pub shared_key: bool,
|
||||
pub ratchet_window_size: i32,
|
||||
pub ratchet_salt: Vec<u8>,
|
||||
pub failure_tolerance: i32,
|
||||
pub key_ring_size: i32,
|
||||
pub key_derivation_algorithm: KeyDerivationAlgorithm,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[repr(i32)]
|
||||
pub enum KeyDerivationAlgorithm {
|
||||
PBKDF2 = 0,
|
||||
HKDF,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[repr(i32)]
|
||||
pub enum Algorithm {
|
||||
AesGcm = 0,
|
||||
AesCbc,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[repr(i32)]
|
||||
pub enum FrameCryptionState {
|
||||
New = 0,
|
||||
Ok,
|
||||
EncryptionFailed,
|
||||
DecryptionFailed,
|
||||
MissingKey,
|
||||
KeyRatcheted,
|
||||
InternalError,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct EncryptedPacket {
|
||||
pub data: Vec<u8>,
|
||||
pub iv: Vec<u8>,
|
||||
pub key_index: u32,
|
||||
}
|
||||
|
||||
unsafe extern "C++" {
|
||||
include!("livekit/frame_cryptor.h");
|
||||
|
||||
pub type KeyProvider;
|
||||
|
||||
pub fn new_key_provider(options: KeyProviderOptions) -> SharedPtr<KeyProvider>;
|
||||
|
||||
pub fn set_shared_key(self: &KeyProvider, key_index: i32, key: Vec<u8>) -> bool;
|
||||
|
||||
pub fn ratchet_shared_key(self: &KeyProvider, key_index: i32) -> Result<Vec<u8>>;
|
||||
|
||||
pub fn get_shared_key(self: &KeyProvider, key_index: i32) -> Result<Vec<u8>>;
|
||||
|
||||
pub fn set_sif_trailer(&self, trailer: Vec<u8>);
|
||||
|
||||
pub fn set_key(
|
||||
self: &KeyProvider,
|
||||
participant_id: String,
|
||||
key_index: i32,
|
||||
key: Vec<u8>,
|
||||
) -> bool;
|
||||
|
||||
pub fn ratchet_key(
|
||||
self: &KeyProvider,
|
||||
participant_id: String,
|
||||
key_index: i32,
|
||||
) -> Result<Vec<u8>>;
|
||||
|
||||
pub fn get_key(
|
||||
self: &KeyProvider,
|
||||
participant_id: String,
|
||||
key_index: i32,
|
||||
) -> Result<Vec<u8>>;
|
||||
}
|
||||
|
||||
unsafe extern "C++" {
|
||||
include!("livekit/frame_cryptor.h");
|
||||
include!("livekit/rtp_sender.h");
|
||||
include!("livekit/rtp_receiver.h");
|
||||
include!("livekit/peer_connection_factory.h");
|
||||
include!("livekit/packet_trailer.h");
|
||||
|
||||
type RtpSender = crate::rtp_sender::ffi::RtpSender;
|
||||
type RtpReceiver = crate::rtp_receiver::ffi::RtpReceiver;
|
||||
type PeerConnectionFactory = crate::peer_connection_factory::ffi::PeerConnectionFactory;
|
||||
type PacketTrailerHandler = crate::packet_trailer::ffi::PacketTrailerHandler;
|
||||
|
||||
pub type FrameCryptor;
|
||||
|
||||
pub fn new_frame_cryptor_for_rtp_sender(
|
||||
peer_factory: SharedPtr<PeerConnectionFactory>,
|
||||
participant_id: String,
|
||||
algorithm: Algorithm,
|
||||
key_provider: SharedPtr<KeyProvider>,
|
||||
sender: SharedPtr<RtpSender>,
|
||||
) -> SharedPtr<FrameCryptor>;
|
||||
|
||||
pub fn new_frame_cryptor_for_rtp_receiver(
|
||||
peer_factory: SharedPtr<PeerConnectionFactory>,
|
||||
participant_id: String,
|
||||
algorithm: Algorithm,
|
||||
key_provider: SharedPtr<KeyProvider>,
|
||||
receiver: SharedPtr<RtpReceiver>,
|
||||
) -> SharedPtr<FrameCryptor>;
|
||||
|
||||
pub fn set_enabled(self: &FrameCryptor, enabled: bool);
|
||||
|
||||
pub fn enabled(self: &FrameCryptor) -> bool;
|
||||
|
||||
pub fn set_key_index(self: &FrameCryptor, index: i32);
|
||||
|
||||
pub fn key_index(self: &FrameCryptor) -> i32;
|
||||
|
||||
pub fn participant_id(self: &FrameCryptor) -> String;
|
||||
|
||||
pub fn register_observer(
|
||||
self: &FrameCryptor,
|
||||
observer: Box<RtcFrameCryptorObserverWrapper>,
|
||||
);
|
||||
|
||||
pub fn unregister_observer(self: &FrameCryptor);
|
||||
|
||||
pub fn set_packet_trailer_handler(
|
||||
self: &FrameCryptor,
|
||||
handler: SharedPtr<PacketTrailerHandler>,
|
||||
);
|
||||
}
|
||||
|
||||
unsafe extern "C++" {
|
||||
include!("livekit/frame_cryptor.h");
|
||||
|
||||
pub type DataPacketCryptor;
|
||||
|
||||
pub fn new_data_packet_cryptor(
|
||||
algorithm: Algorithm,
|
||||
key_provider: SharedPtr<KeyProvider>,
|
||||
) -> SharedPtr<DataPacketCryptor>;
|
||||
|
||||
pub fn encrypt_data_packet(
|
||||
self: &DataPacketCryptor,
|
||||
participant_id: String,
|
||||
key_index: u32,
|
||||
data: Vec<u8>,
|
||||
) -> Result<EncryptedPacket>;
|
||||
|
||||
pub fn decrypt_data_packet(
|
||||
self: &DataPacketCryptor,
|
||||
participant_id: String,
|
||||
encrypted_packet: &EncryptedPacket,
|
||||
) -> Result<Vec<u8>>;
|
||||
}
|
||||
|
||||
extern "Rust" {
|
||||
type RtcFrameCryptorObserverWrapper;
|
||||
|
||||
fn on_frame_cryption_state_change(
|
||||
self: &RtcFrameCryptorObserverWrapper,
|
||||
participant_id: String,
|
||||
state: FrameCryptionState,
|
||||
);
|
||||
}
|
||||
} // namespace livekit_ffi
|
||||
|
||||
impl_thread_safety!(ffi::FrameCryptor, Send + Sync);
|
||||
impl_thread_safety!(ffi::KeyProvider, Send + Sync);
|
||||
impl_thread_safety!(ffi::DataPacketCryptor, Send + Sync);
|
||||
|
||||
use ffi::FrameCryptionState;
|
||||
|
||||
// Re-export the EncryptedPacket for convenience
|
||||
pub use ffi::EncryptedPacket;
|
||||
|
||||
pub trait RtcFrameCryptorObserver: Send + Sync {
|
||||
fn on_frame_cryption_state_change(&self, participant_id: String, state: FrameCryptionState);
|
||||
}
|
||||
|
||||
pub struct RtcFrameCryptorObserverWrapper {
|
||||
observer: Arc<dyn RtcFrameCryptorObserver>,
|
||||
}
|
||||
|
||||
impl RtcFrameCryptorObserverWrapper {
|
||||
pub fn new(observer: Arc<dyn RtcFrameCryptorObserver>) -> Self {
|
||||
Self { observer }
|
||||
}
|
||||
|
||||
fn on_frame_cryption_state_change(
|
||||
self: &RtcFrameCryptorObserverWrapper,
|
||||
participant_id: String,
|
||||
state: FrameCryptionState,
|
||||
) {
|
||||
self.observer.on_frame_cryption_state_change(participant_id, state);
|
||||
}
|
||||
}
|
||||
|
||||
/// High-level Rust wrapper for data packet cryptor functionality
|
||||
pub struct DataPacketCryptor {
|
||||
inner: cxx::SharedPtr<ffi::DataPacketCryptor>,
|
||||
}
|
||||
|
||||
impl DataPacketCryptor {
|
||||
/// Create a new data packet cryptor with the specified algorithm and key provider
|
||||
pub fn new(algorithm: ffi::Algorithm, key_provider: cxx::SharedPtr<ffi::KeyProvider>) -> Self {
|
||||
Self { inner: ffi::new_data_packet_cryptor(algorithm, key_provider) }
|
||||
}
|
||||
|
||||
/// Encrypt data for a specific participant
|
||||
pub fn encrypt(
|
||||
&self,
|
||||
participant_id: &str,
|
||||
key_index: u32,
|
||||
data: &[u8],
|
||||
) -> Result<ffi::EncryptedPacket, Box<dyn std::error::Error>> {
|
||||
let data_vec: Vec<u8> = data.to_vec();
|
||||
match self.inner.encrypt_data_packet(participant_id.to_string(), key_index, data_vec) {
|
||||
Ok(packet) => Ok(packet),
|
||||
Err(e) => Err(format!("Encryption failed: {}", e).into()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Decrypt an encrypted packet for a specific participant
|
||||
pub fn decrypt(
|
||||
&self,
|
||||
participant_id: &str,
|
||||
encrypted_packet: &ffi::EncryptedPacket,
|
||||
) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
|
||||
match self.inner.decrypt_data_packet(participant_id.to_string(), encrypted_packet) {
|
||||
Ok(data) => Ok(data.into_iter().collect()),
|
||||
Err(e) => Err(format!("Decryption failed: {}", e).into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_data_packet_cryptor_creation() {
|
||||
let options = ffi::KeyProviderOptions {
|
||||
shared_key: true,
|
||||
ratchet_window_size: 16,
|
||||
ratchet_salt: vec![],
|
||||
failure_tolerance: -1,
|
||||
key_ring_size: 16,
|
||||
key_derivation_algorithm: ffi::KeyDerivationAlgorithm::HKDF,
|
||||
};
|
||||
|
||||
let key_provider = ffi::new_key_provider(options);
|
||||
let _cryptor = DataPacketCryptor::new(ffi::Algorithm::AesGcm, key_provider);
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2025 LiveKit, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "livekit/global_task_queue.h"
|
||||
|
||||
#include "api/task_queue/default_task_queue_factory.h"
|
||||
#include "api/task_queue/task_queue_factory.h"
|
||||
|
||||
namespace livekit_ffi {
|
||||
|
||||
webrtc::TaskQueueFactory* GetGlobalTaskQueueFactory() {
|
||||
static std::unique_ptr<webrtc::TaskQueueFactory> global_task_queue_factory =
|
||||
webrtc::CreateDefaultTaskQueueFactory();
|
||||
return global_task_queue_factory.get();
|
||||
}
|
||||
|
||||
} // namespace livekit_ffi
|
||||
@@ -0,0 +1,67 @@
|
||||
// Copyright 2025 LiveKit, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#[cxx::bridge(namespace = "livekit_ffi")]
|
||||
pub mod ffi {
|
||||
// Wrapper to opaque C++ objects
|
||||
// https://github.com/dtolnay/cxx/issues/741
|
||||
// Used to allow SharedPtr/UniquePtr type inside a rust::Vec
|
||||
pub struct MediaStreamPtr {
|
||||
pub ptr: SharedPtr<MediaStream>,
|
||||
}
|
||||
|
||||
pub struct CandidatePtr {
|
||||
pub ptr: SharedPtr<Candidate>,
|
||||
}
|
||||
|
||||
pub struct AudioTrackPtr {
|
||||
pub ptr: SharedPtr<AudioTrack>,
|
||||
}
|
||||
|
||||
pub struct VideoTrackPtr {
|
||||
pub ptr: SharedPtr<VideoTrack>,
|
||||
}
|
||||
|
||||
pub struct RtpSenderPtr {
|
||||
pub ptr: SharedPtr<RtpSender>,
|
||||
}
|
||||
|
||||
pub struct RtpReceiverPtr {
|
||||
pub ptr: SharedPtr<RtpReceiver>,
|
||||
}
|
||||
|
||||
pub struct RtpTransceiverPtr {
|
||||
pub ptr: SharedPtr<RtpTransceiver>,
|
||||
}
|
||||
|
||||
unsafe extern "C++" {
|
||||
include!("livekit/helper.h");
|
||||
|
||||
type MediaStream = crate::media_stream::ffi::MediaStream;
|
||||
type AudioTrack = crate::media_stream::ffi::AudioTrack;
|
||||
type VideoTrack = crate::media_stream::ffi::VideoTrack;
|
||||
type Candidate = crate::candidate::ffi::Candidate;
|
||||
type RtpSender = crate::rtp_sender::ffi::RtpSender;
|
||||
type RtpReceiver = crate::rtp_receiver::ffi::RtpReceiver;
|
||||
type RtpTransceiver = crate::rtp_transceiver::ffi::RtpTransceiver;
|
||||
|
||||
fn _vec_media_stream_ptr() -> Vec<MediaStreamPtr>;
|
||||
fn _vec_candidate_ptr() -> Vec<CandidatePtr>;
|
||||
fn _vec_audio_track_ptr() -> Vec<AudioTrackPtr>;
|
||||
fn _vec_video_track_ptr() -> Vec<VideoTrackPtr>;
|
||||
fn _vec_rtp_sender_ptr() -> Vec<RtpSenderPtr>;
|
||||
fn _vec_rtp_receiver_ptr() -> Vec<RtpReceiverPtr>;
|
||||
fn _vec_rtp_transceiver_ptr() -> Vec<RtpTransceiverPtr>;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
/*
|
||||
* Copyright 2025 LiveKit, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "livekit/jsep.h"
|
||||
|
||||
#include <iomanip>
|
||||
#include <memory>
|
||||
|
||||
#include "livekit/rtc_error.h"
|
||||
#include "rtc_base/ref_counted_object.h"
|
||||
#include "rust/cxx.h"
|
||||
|
||||
namespace livekit_ffi {
|
||||
|
||||
std::string serialize_sdp_error(webrtc::SdpParseError error) {
|
||||
std::stringstream ss;
|
||||
ss << std::hex << std::setfill('0');
|
||||
ss << std::setw(8) << (uint32_t)error.line.length();
|
||||
ss << std::dec << std::setw(1) << error.line;
|
||||
ss << std::dec << std::setw(1) << error.description;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
IceCandidate::IceCandidate(
|
||||
std::unique_ptr<webrtc::IceCandidateInterface> ice_candidate)
|
||||
: ice_candidate_(std::move(ice_candidate)) {}
|
||||
|
||||
rust::String IceCandidate::sdp_mid() const {
|
||||
return ice_candidate_->sdp_mid();
|
||||
}
|
||||
|
||||
int IceCandidate::sdp_mline_index() const {
|
||||
return ice_candidate_->sdp_mline_index();
|
||||
}
|
||||
|
||||
rust::String IceCandidate::candidate() const {
|
||||
return stringify();
|
||||
}
|
||||
|
||||
rust::String IceCandidate::stringify() const {
|
||||
std::string str;
|
||||
ice_candidate_->ToString(&str);
|
||||
return rust::String::lossy(str);
|
||||
}
|
||||
|
||||
std::unique_ptr<webrtc::IceCandidateInterface> IceCandidate::release() {
|
||||
return std::move(ice_candidate_);
|
||||
}
|
||||
|
||||
std::shared_ptr<IceCandidate> create_ice_candidate(rust::String sdp_mid,
|
||||
int sdp_mline_index,
|
||||
rust::String sdp) {
|
||||
webrtc::SdpParseError error;
|
||||
auto ice_rtc = webrtc::CreateIceCandidate(sdp_mid.c_str(), sdp_mline_index,
|
||||
sdp.c_str(), &error);
|
||||
if (!ice_rtc) {
|
||||
throw std::runtime_error(serialize_sdp_error(error));
|
||||
}
|
||||
|
||||
return std::make_shared<IceCandidate>(
|
||||
std::unique_ptr<webrtc::IceCandidateInterface>(ice_rtc));
|
||||
}
|
||||
|
||||
SessionDescription::SessionDescription(
|
||||
std::unique_ptr<webrtc::SessionDescriptionInterface> session_description)
|
||||
: session_description_(std::move(session_description)) {}
|
||||
|
||||
SdpType SessionDescription::sdp_type() const {
|
||||
return static_cast<SdpType>(session_description_->GetType());
|
||||
}
|
||||
|
||||
rust::String SessionDescription::stringify() const {
|
||||
std::string str;
|
||||
session_description_->ToString(&str);
|
||||
return rust::String::lossy(str);
|
||||
}
|
||||
|
||||
std::unique_ptr<SessionDescription> SessionDescription::clone() const {
|
||||
return std::make_unique<SessionDescription>(session_description_->Clone());
|
||||
}
|
||||
|
||||
std::unique_ptr<webrtc::SessionDescriptionInterface>
|
||||
SessionDescription::release() {
|
||||
return std::move(session_description_);
|
||||
}
|
||||
|
||||
std::unique_ptr<SessionDescription> create_session_description(
|
||||
SdpType type,
|
||||
rust::String sdp) {
|
||||
webrtc::SdpParseError error;
|
||||
auto rtc_sdp = webrtc::CreateSessionDescription(
|
||||
static_cast<webrtc::SdpType>(type), sdp.c_str(), &error);
|
||||
if (!rtc_sdp) {
|
||||
throw std::runtime_error(serialize_sdp_error(error));
|
||||
}
|
||||
|
||||
return std::make_unique<SessionDescription>(std::move(rtc_sdp));
|
||||
}
|
||||
|
||||
#ifdef LIVEKIT_TEST
|
||||
rust::String serialize_sdp_parse_error_for_test() {
|
||||
webrtc::SdpParseError error;
|
||||
auto rtc_sdp = webrtc::CreateSessionDescription(
|
||||
webrtc::SdpType::kOffer,
|
||||
"v=0\n"
|
||||
"o=- 6549709950142776241 2 IN IP4 127.0.0.1\n"
|
||||
"s=-\n"
|
||||
"t=0 0\n"
|
||||
"======================== ERROR HERE\n"
|
||||
"a=group:BUNDLE 0\n"
|
||||
"a=extmap-allow-mixed\n"
|
||||
"a=msid-semantic: WMS\n"
|
||||
"m=application 9 UDP/DTLS/SCTP webrtc-datachannel\n"
|
||||
"c=IN IP4 0.0.0.0\n"
|
||||
"a=ice-ufrag:Tw7h\n"
|
||||
"a=ice-pwd:6XOVUD6HpcB4c1M8EB8jXJE9\n"
|
||||
"a=ice-options:trickle\n"
|
||||
"a=fingerprint:sha-256 "
|
||||
"4F:EC:23:59:5D:A5:E6:3E:3E:5D:8A:09:B6:FA:04:AA:19:99:49:67:BD:65:93:06:BB:EE:AC:D5:21:0F:57:D6\n"
|
||||
"a=setup:actpass\n"
|
||||
"a=mid:0\n"
|
||||
"a=sctp-port:5000\n"
|
||||
"a=max-message-size:262144\n",
|
||||
&error);
|
||||
if (rtc_sdp) {
|
||||
return rust::String("");
|
||||
}
|
||||
return rust::String::lossy(serialize_sdp_error(error));
|
||||
}
|
||||
#endif
|
||||
|
||||
NativeCreateSdpObserver::NativeCreateSdpObserver(
|
||||
rust::Box<PeerContext> ctx,
|
||||
rust::Fn<void(rust::Box<PeerContext>, std::unique_ptr<SessionDescription>)>
|
||||
on_success,
|
||||
rust::Fn<void(rust::Box<PeerContext>, RtcError)> on_error)
|
||||
: ctx_(std::move(ctx)), on_success_(on_success), on_error_(on_error) {}
|
||||
|
||||
void NativeCreateSdpObserver::OnSuccess(
|
||||
webrtc::SessionDescriptionInterface* desc) {
|
||||
// We have ownership of desc
|
||||
on_success_(std::move(ctx_),
|
||||
std::make_unique<SessionDescription>(
|
||||
std::unique_ptr<webrtc::SessionDescriptionInterface>(desc)));
|
||||
}
|
||||
|
||||
void NativeCreateSdpObserver::OnFailure(webrtc::RTCError error) {
|
||||
on_error_(std::move(ctx_), to_error(error));
|
||||
}
|
||||
|
||||
NativeSetLocalSdpObserver::NativeSetLocalSdpObserver(
|
||||
rust::Box<PeerContext> ctx,
|
||||
rust::Fn<void(rust::Box<PeerContext>, RtcError)> on_complete)
|
||||
: ctx_(std::move(ctx)), on_complete_(on_complete) {}
|
||||
|
||||
void NativeSetLocalSdpObserver::OnSetLocalDescriptionComplete(
|
||||
webrtc::RTCError error) {
|
||||
on_complete_(std::move(ctx_), to_error(error));
|
||||
}
|
||||
|
||||
NativeSetRemoteSdpObserver::NativeSetRemoteSdpObserver(
|
||||
rust::Box<PeerContext> ctx,
|
||||
rust::Fn<void(rust::Box<PeerContext>, RtcError)> on_complete)
|
||||
: ctx_(std::move(ctx)), on_complete_(on_complete) {}
|
||||
|
||||
void NativeSetRemoteSdpObserver::OnSetRemoteDescriptionComplete(
|
||||
webrtc::RTCError error) {
|
||||
on_complete_(std::move(ctx_), to_error(error));
|
||||
}
|
||||
} // namespace livekit_ffi
|
||||
@@ -0,0 +1,123 @@
|
||||
// Copyright 2025 LiveKit, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use std::{
|
||||
error::Error,
|
||||
fmt::{Display, Formatter},
|
||||
};
|
||||
|
||||
use crate::impl_thread_safety;
|
||||
|
||||
#[cxx::bridge(namespace = "livekit_ffi")]
|
||||
pub mod ffi {
|
||||
#[derive(Debug)]
|
||||
#[repr(i32)]
|
||||
pub enum SdpType {
|
||||
Offer,
|
||||
PrAnswer,
|
||||
Answer,
|
||||
Rollback,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SdpParseError {
|
||||
pub line: String,
|
||||
pub description: String,
|
||||
}
|
||||
|
||||
extern "C++" {
|
||||
include!("livekit/rtc_error.h");
|
||||
|
||||
type RtcError = crate::rtc_error::ffi::RtcError;
|
||||
}
|
||||
|
||||
unsafe extern "C++" {
|
||||
include!("livekit/jsep.h");
|
||||
|
||||
type IceCandidate;
|
||||
type SessionDescription;
|
||||
|
||||
fn sdp_mid(self: &IceCandidate) -> String;
|
||||
fn sdp_mline_index(self: &IceCandidate) -> i32;
|
||||
fn candidate(self: &IceCandidate) -> String;
|
||||
fn stringify(self: &IceCandidate) -> String;
|
||||
|
||||
fn sdp_type(self: &SessionDescription) -> SdpType;
|
||||
fn stringify(self: &SessionDescription) -> String;
|
||||
fn clone(self: &SessionDescription) -> UniquePtr<SessionDescription>;
|
||||
|
||||
fn create_ice_candidate(
|
||||
sdp_mid: String,
|
||||
sdp_mline_index: i32,
|
||||
sdp: String,
|
||||
) -> Result<SharedPtr<IceCandidate>>;
|
||||
|
||||
fn create_session_description(
|
||||
sdp_type: SdpType,
|
||||
sdp: String,
|
||||
) -> Result<UniquePtr<SessionDescription>>;
|
||||
|
||||
fn _shared_ice_candidate() -> SharedPtr<IceCandidate>; // Ignore
|
||||
fn _unique_session_description() -> UniquePtr<SessionDescription>; // Ignore
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for ffi::SdpParseError {}
|
||||
|
||||
impl Display for ffi::SdpParseError {
|
||||
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
|
||||
write!(f, "SdpParseError occurred {}: {}", self.line, self.description)
|
||||
}
|
||||
}
|
||||
|
||||
impl_thread_safety!(ffi::SessionDescription, Send + Sync);
|
||||
impl_thread_safety!(ffi::IceCandidate, Send + Sync);
|
||||
|
||||
impl ffi::SdpParseError {
|
||||
/// # Safety
|
||||
/// The value must be correctly encoded
|
||||
pub unsafe fn from(value: &str) -> Self {
|
||||
// Parse the hex encoded error from c++
|
||||
let line_length = u32::from_str_radix(&value[0..8], 16).unwrap() as usize + 8;
|
||||
let line = String::from(&value[8..line_length]);
|
||||
let description = String::from(&value[line_length..]);
|
||||
|
||||
Self { line, description }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[cxx::bridge(namespace = "livekit_ffi")]
|
||||
pub mod ffi_tests {
|
||||
unsafe extern "C++" {
|
||||
include!("livekit/jsep.h");
|
||||
|
||||
fn serialize_sdp_parse_error_for_test() -> String;
|
||||
}
|
||||
}
|
||||
|
||||
use crate::jsep::ffi;
|
||||
|
||||
/// Tests that SdpParseError can correctly deserialize the hex-encoded
|
||||
/// error format produced by C++ when SDP parsing fails.
|
||||
#[test]
|
||||
fn sdp_parse_error_deserialization() {
|
||||
let serialized = ffi_tests::serialize_sdp_parse_error_for_test();
|
||||
let err = unsafe { ffi::SdpParseError::from(&serialized) };
|
||||
|
||||
assert!(!err.line.is_empty(), "error line should not be empty");
|
||||
assert!(!err.description.is_empty(), "error description should not be empty");
|
||||
}
|
||||
}
|
||||
+1426
File diff suppressed because it is too large
Load Diff
+48543
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user