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:
2026-07-01 18:22:43 -04:00
commit 682afacd30
1763 changed files with 613720 additions and 0 deletions
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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();
}
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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