cleanup: webrtc-sys & fix RtcRuntime disposing crashes (#81)
This commit is contained in:
Generated
+106
-4
@@ -841,7 +841,7 @@ dependencies = [
|
||||
"futures-sink",
|
||||
"nanorand",
|
||||
"pin-project",
|
||||
"spin",
|
||||
"spin 0.9.8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2352,6 +2352,21 @@ dependencies = [
|
||||
"winreg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ring"
|
||||
version = "0.16.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"spin 0.5.2",
|
||||
"untrusted",
|
||||
"web-sys",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.23"
|
||||
@@ -2378,6 +2393,49 @@ dependencies = [
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.21.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c911ba11bc8433e811ce56fde130ccf32f5127cab0e0194e9c68c5a5b671791e"
|
||||
dependencies = [
|
||||
"log",
|
||||
"ring",
|
||||
"rustls-webpki",
|
||||
"sct",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-native-certs"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0167bac7a9f490495f3c33013e7722b53cb087ecbe082fb0c6387c96f634ea50"
|
||||
dependencies = [
|
||||
"openssl-probe",
|
||||
"rustls-pemfile",
|
||||
"schannel",
|
||||
"security-framework",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-pemfile"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b"
|
||||
dependencies = [
|
||||
"base64",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-webpki"
|
||||
version = "0.100.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d6207cd5ed3d8dca7816f8f3725513a34609c0c765bf652b8c3cb4cfd87db46b"
|
||||
dependencies = [
|
||||
"ring",
|
||||
"untrusted",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.13"
|
||||
@@ -2431,6 +2489,16 @@ version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1792db035ce95be60c3f8853017b3999209281c24e2ba5bc8e59bf97a0c590c1"
|
||||
|
||||
[[package]]
|
||||
name = "sct"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4"
|
||||
dependencies = [
|
||||
"ring",
|
||||
"untrusted",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sctk-adwaita"
|
||||
version = "0.5.4"
|
||||
@@ -2619,6 +2687,12 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
version = "0.9.8"
|
||||
@@ -2854,6 +2928,16 @@ dependencies = [
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-rustls"
|
||||
version = "0.24.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e0d409377ff5b1e3ca6437aa86c1eb7d40c134bfec254e44c830defa92669db5"
|
||||
dependencies = [
|
||||
"rustls",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-stream"
|
||||
version = "0.1.14"
|
||||
@@ -2873,9 +2957,10 @@ checksum = "ec509ac96e9a0c43427c74f003127d953a265737636129424288d27cb5c4b12c"
|
||||
dependencies = [
|
||||
"futures-util",
|
||||
"log",
|
||||
"native-tls",
|
||||
"rustls",
|
||||
"rustls-native-certs",
|
||||
"tokio",
|
||||
"tokio-native-tls",
|
||||
"tokio-rustls",
|
||||
"tungstenite",
|
||||
]
|
||||
|
||||
@@ -2998,12 +3083,13 @@ dependencies = [
|
||||
"http",
|
||||
"httparse",
|
||||
"log",
|
||||
"native-tls",
|
||||
"rand",
|
||||
"rustls",
|
||||
"sha1",
|
||||
"thiserror",
|
||||
"url",
|
||||
"utf-8",
|
||||
"webpki",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3054,6 +3140,12 @@ version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
|
||||
|
||||
[[package]]
|
||||
name = "untrusted"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.3.1"
|
||||
@@ -3287,6 +3379,16 @@ dependencies = [
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webpki"
|
||||
version = "0.22.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd"
|
||||
dependencies = [
|
||||
"ring",
|
||||
"untrusted",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webrtc-sys"
|
||||
version = "0.1.1"
|
||||
|
||||
@@ -98,15 +98,20 @@ impl SineTrack {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn track_task(_close_rx: oneshot::Receiver<()>, rtc_source: NativeAudioSource) {
|
||||
async fn track_task(mut close_rx: oneshot::Receiver<()>, rtc_source: NativeAudioSource) {
|
||||
let mut data = FrameData::default();
|
||||
let mut interval = tokio::time::interval(Duration::from_millis(10));
|
||||
let mut samples_10ms = Vec::<i16>::new();
|
||||
|
||||
loop {
|
||||
const NUM_CHANNELS: usize = 2;
|
||||
tokio::select! {
|
||||
_ = &mut close_rx => {
|
||||
break;
|
||||
}
|
||||
_ = interval.tick() => {}
|
||||
}
|
||||
|
||||
interval.tick().await;
|
||||
const NUM_CHANNELS: usize = 2;
|
||||
|
||||
let samples_count_10ms = (data.sample_rate / 100) as usize * NUM_CHANNELS;
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ use livekit::prelude::*;
|
||||
use livekit::webrtc::audio_frame::AudioFrame;
|
||||
use livekit::webrtc::audio_source::native::NativeAudioSource;
|
||||
use livekit::webrtc::audio_stream::native::NativeAudioStream;
|
||||
use livekit::webrtc::media_stream::MediaStreamTrack;
|
||||
use livekit::webrtc::prelude::*;
|
||||
use log::warn;
|
||||
use server::utils;
|
||||
use tokio::sync::oneshot;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::{proto, server, FfiError, FfiHandleId, FfiResult};
|
||||
use futures_util::StreamExt;
|
||||
use livekit::prelude::*;
|
||||
use livekit::webrtc::media_stream::MediaStreamTrack;
|
||||
use livekit::webrtc::prelude::*;
|
||||
use livekit::webrtc::video_frame::{BoxVideoFrameBuffer, VideoFrame};
|
||||
use livekit::webrtc::video_source::native::NativeVideoSource;
|
||||
use livekit::webrtc::video_stream::native::NativeVideoStream;
|
||||
|
||||
@@ -4,7 +4,7 @@ use crate::imp::audio_stream as stream_imp;
|
||||
pub mod native {
|
||||
use super::stream_imp;
|
||||
use crate::audio_frame::AudioFrame;
|
||||
use crate::media_stream::RtcAudioTrack;
|
||||
use crate::audio_track::RtcAudioTrack;
|
||||
use futures::stream::Stream;
|
||||
use std::fmt::{Debug, Formatter};
|
||||
use std::pin::Pin;
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
use crate::imp::audio_track as imp_at;
|
||||
use crate::media_stream_track::media_stream_track;
|
||||
use crate::media_stream_track::RtcTrackState;
|
||||
use std::fmt::Debug;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct RtcAudioTrack {
|
||||
pub(crate) handle: imp_at::RtcAudioTrack,
|
||||
}
|
||||
|
||||
impl RtcAudioTrack {
|
||||
media_stream_track!();
|
||||
}
|
||||
|
||||
impl Debug for RtcAudioTrack {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("RtcAudioTrack")
|
||||
.field("id", &self.id())
|
||||
.field("enabled", &self.enabled())
|
||||
.field("state", &self.state())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
@@ -29,9 +29,11 @@ pub struct RtcError {
|
||||
pub mod audio_frame;
|
||||
pub mod audio_source;
|
||||
pub mod audio_stream;
|
||||
pub mod audio_track;
|
||||
pub mod data_channel;
|
||||
pub mod ice_candidate;
|
||||
pub mod media_stream;
|
||||
pub mod media_stream_track;
|
||||
pub mod peer_connection;
|
||||
pub mod peer_connection_factory;
|
||||
pub mod prelude;
|
||||
@@ -43,6 +45,7 @@ pub mod session_description;
|
||||
pub mod video_frame;
|
||||
pub mod video_source;
|
||||
pub mod video_stream;
|
||||
pub mod video_track;
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub mod native {
|
||||
|
||||
@@ -1,13 +1,8 @@
|
||||
use crate::audio_track::RtcAudioTrack;
|
||||
use crate::imp::media_stream as imp_ms;
|
||||
use livekit_protocol::enum_dispatch;
|
||||
use crate::video_track::RtcVideoTrack;
|
||||
use std::fmt::Debug;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum RtcTrackState {
|
||||
Live,
|
||||
Ended,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct MediaStream {
|
||||
pub(crate) handle: imp_ms::MediaStream,
|
||||
@@ -36,104 +31,3 @@ impl Debug for MediaStream {
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct RtcVideoTrack {
|
||||
pub(crate) handle: imp_ms::RtcVideoTrack,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct RtcAudioTrack {
|
||||
pub(crate) handle: imp_ms::RtcAudioTrack,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum MediaStreamTrack {
|
||||
Video(RtcVideoTrack),
|
||||
Audio(RtcAudioTrack),
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
impl MediaStreamTrack {
|
||||
enum_dispatch!(
|
||||
[Video, Audio];
|
||||
pub(crate) fn sys_handle(self: &Self) -> cxx::SharedPtr<webrtc_sys::media_stream::ffi::MediaStreamTrack>;
|
||||
);
|
||||
}
|
||||
|
||||
impl MediaStreamTrack {
|
||||
enum_dispatch!(
|
||||
[Video, Audio];
|
||||
pub fn id(self: &Self) -> String;
|
||||
pub fn enabled(self: &Self) -> bool;
|
||||
pub fn set_enabled(self: &Self, enabled: bool) -> bool;
|
||||
pub fn state(self: &Self) -> RtcTrackState;
|
||||
);
|
||||
}
|
||||
|
||||
macro_rules! media_stream_track {
|
||||
() => {
|
||||
pub fn id(&self) -> String {
|
||||
self.handle.id()
|
||||
}
|
||||
|
||||
pub fn enabled(&self) -> bool {
|
||||
self.handle.enabled()
|
||||
}
|
||||
|
||||
pub fn set_enabled(&self, enabled: bool) -> bool {
|
||||
self.handle.set_enabled(enabled)
|
||||
}
|
||||
|
||||
pub fn state(&self) -> RtcTrackState {
|
||||
self.handle.state().into()
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub(crate) fn sys_handle(
|
||||
&self,
|
||||
) -> cxx::SharedPtr<webrtc_sys::media_stream::ffi::MediaStreamTrack> {
|
||||
self.handle.sys_handle()
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl RtcVideoTrack {
|
||||
media_stream_track!();
|
||||
}
|
||||
|
||||
impl RtcAudioTrack {
|
||||
media_stream_track!();
|
||||
}
|
||||
|
||||
impl Debug for RtcAudioTrack {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("RtcAudioTrack")
|
||||
.field("id", &self.id())
|
||||
.field("enabled", &self.enabled())
|
||||
.field("state", &self.state())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for RtcVideoTrack {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("RtcVideoTrack")
|
||||
.field("id", &self.id())
|
||||
.field("enabled", &self.enabled())
|
||||
.field("state", &self.state())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RtcAudioTrack> for MediaStreamTrack {
|
||||
fn from(track: RtcAudioTrack) -> Self {
|
||||
Self::Audio(track)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RtcVideoTrack> for MediaStreamTrack {
|
||||
fn from(track: RtcVideoTrack) -> Self {
|
||||
Self::Video(track)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
use crate::audio_track::RtcAudioTrack;
|
||||
use crate::video_track::RtcVideoTrack;
|
||||
use livekit_protocol::enum_dispatch;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum RtcTrackState {
|
||||
Live,
|
||||
Ended,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum MediaStreamTrack {
|
||||
Video(RtcVideoTrack),
|
||||
Audio(RtcAudioTrack),
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
impl MediaStreamTrack {
|
||||
enum_dispatch!(
|
||||
[Video, Audio];
|
||||
pub(crate) fn sys_handle(self: &Self) -> cxx::SharedPtr<webrtc_sys::media_stream::ffi::MediaStreamTrack>;
|
||||
);
|
||||
}
|
||||
|
||||
impl MediaStreamTrack {
|
||||
enum_dispatch!(
|
||||
[Video, Audio];
|
||||
pub fn id(self: &Self) -> String;
|
||||
pub fn enabled(self: &Self) -> bool;
|
||||
pub fn set_enabled(self: &Self, enabled: bool) -> bool;
|
||||
pub fn state(self: &Self) -> RtcTrackState;
|
||||
);
|
||||
}
|
||||
|
||||
macro_rules! media_stream_track {
|
||||
() => {
|
||||
pub fn id(&self) -> String {
|
||||
self.handle.id()
|
||||
}
|
||||
|
||||
pub fn enabled(&self) -> bool {
|
||||
self.handle.enabled()
|
||||
}
|
||||
|
||||
pub fn set_enabled(&self, enabled: bool) -> bool {
|
||||
self.handle.set_enabled(enabled)
|
||||
}
|
||||
|
||||
pub fn state(&self) -> RtcTrackState {
|
||||
self.handle.state().into()
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub(crate) fn sys_handle(
|
||||
&self,
|
||||
) -> cxx::SharedPtr<webrtc_sys::media_stream::ffi::MediaStreamTrack> {
|
||||
self.handle.sys_handle()
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub(crate) use media_stream_track;
|
||||
|
||||
impl From<RtcAudioTrack> for MediaStreamTrack {
|
||||
fn from(track: RtcAudioTrack) -> Self {
|
||||
Self::Audio(track)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RtcVideoTrack> for MediaStreamTrack {
|
||||
fn from(track: RtcVideoTrack) -> Self {
|
||||
Self::Video(track)
|
||||
}
|
||||
}
|
||||
@@ -2,11 +2,11 @@ use crate::audio_frame::AudioFrame;
|
||||
use cxx::SharedPtr;
|
||||
use parking_lot::Mutex;
|
||||
use std::sync::Arc;
|
||||
use webrtc_sys::media_stream as sys_ms;
|
||||
use webrtc_sys::audio_track as sys_at;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct NativeAudioSource {
|
||||
sys_handle: SharedPtr<sys_ms::ffi::AudioTrackSource>,
|
||||
sys_handle: SharedPtr<sys_at::ffi::AudioTrackSource>,
|
||||
inner: Arc<Mutex<AudioSourceInner>>,
|
||||
}
|
||||
|
||||
@@ -21,14 +21,14 @@ struct AudioSourceInner {
|
||||
impl Default for NativeAudioSource {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
sys_handle: sys_ms::ffi::new_audio_track_source(),
|
||||
sys_handle: sys_at::ffi::new_audio_track_source(),
|
||||
inner: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl NativeAudioSource {
|
||||
pub fn sys_handle(&self) -> SharedPtr<sys_ms::ffi::AudioTrackSource> {
|
||||
pub fn sys_handle(&self) -> SharedPtr<sys_at::ffi::AudioTrackSource> {
|
||||
self.sys_handle.clone()
|
||||
}
|
||||
|
||||
@@ -70,14 +70,12 @@ impl NativeAudioSource {
|
||||
&frame.data[i..i + samples_10ms]
|
||||
};
|
||||
|
||||
unsafe {
|
||||
self.sys_handle.on_captured_frame(
|
||||
data.as_ptr(),
|
||||
frame.sample_rate as i32,
|
||||
frame.num_channels as usize,
|
||||
samples_10ms / frame.num_channels as usize,
|
||||
)
|
||||
}
|
||||
self.sys_handle.on_captured_frame(
|
||||
data,
|
||||
frame.sample_rate as i32,
|
||||
frame.num_channels as usize,
|
||||
samples_10ms / frame.num_channels as usize,
|
||||
);
|
||||
|
||||
i += needed_data;
|
||||
}
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
use crate::{audio_frame::AudioFrame, media_stream::RtcAudioTrack};
|
||||
use cxx::UniquePtr;
|
||||
use crate::audio_frame::AudioFrame;
|
||||
use crate::audio_track::RtcAudioTrack;
|
||||
use cxx::SharedPtr;
|
||||
use futures::stream::Stream;
|
||||
use std::pin::Pin;
|
||||
use std::sync::Arc;
|
||||
use std::task::{Context, Poll};
|
||||
use tokio::sync::mpsc;
|
||||
use webrtc_sys::media_stream as sys_ms;
|
||||
use webrtc_sys::audio_track as sys_at;
|
||||
|
||||
pub struct NativeAudioStream {
|
||||
native_observer: UniquePtr<sys_ms::ffi::NativeAudioSink>,
|
||||
_observer: Box<AudioTrackObserver>,
|
||||
native_sink: SharedPtr<sys_at::ffi::NativeAudioSink>,
|
||||
audio_track: RtcAudioTrack,
|
||||
frame_rx: mpsc::UnboundedReceiver<AudioFrame>,
|
||||
}
|
||||
@@ -16,21 +17,16 @@ pub struct NativeAudioStream {
|
||||
impl NativeAudioStream {
|
||||
pub fn new(audio_track: RtcAudioTrack) -> Self {
|
||||
let (frame_tx, frame_rx) = mpsc::unbounded_channel();
|
||||
let mut observer = Box::new(AudioTrackObserver { frame_tx });
|
||||
let mut native_observer = unsafe {
|
||||
sys_ms::ffi::new_native_audio_sink(Box::new(sys_ms::AudioSinkWrapper::new(
|
||||
&mut *observer,
|
||||
)))
|
||||
};
|
||||
let observer = Arc::new(AudioTrackObserver { frame_tx });
|
||||
let native_sink = sys_at::ffi::new_native_audio_sink(Box::new(
|
||||
sys_at::AudioSinkWrapper::new(observer.clone()),
|
||||
));
|
||||
|
||||
unsafe {
|
||||
sys_ms::ffi::media_to_audio(audio_track.sys_handle())
|
||||
.add_sink(native_observer.pin_mut());
|
||||
}
|
||||
let audio = unsafe { sys_at::ffi::media_to_audio(audio_track.sys_handle()) };
|
||||
audio.add_sink(&native_sink);
|
||||
|
||||
Self {
|
||||
native_observer,
|
||||
_observer: observer,
|
||||
native_sink,
|
||||
audio_track,
|
||||
frame_rx,
|
||||
}
|
||||
@@ -41,11 +37,10 @@ impl NativeAudioStream {
|
||||
}
|
||||
|
||||
pub fn close(&mut self) {
|
||||
let audio = unsafe { sys_at::ffi::media_to_audio(self.audio_track.sys_handle()) };
|
||||
audio.remove_sink(&self.native_sink);
|
||||
|
||||
self.frame_rx.close();
|
||||
unsafe {
|
||||
sys_ms::ffi::media_to_audio(self.audio_track.sys_handle())
|
||||
.remove_sink(self.native_observer.pin_mut());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,7 +62,7 @@ pub struct AudioTrackObserver {
|
||||
frame_tx: mpsc::UnboundedSender<AudioFrame>,
|
||||
}
|
||||
|
||||
impl sys_ms::AudioSink for AudioTrackObserver {
|
||||
impl sys_at::AudioSink for AudioTrackObserver {
|
||||
fn on_data(&self, data: &[i16], sample_rate: i32, nb_channels: usize, nb_frames: usize) {
|
||||
// TODO(theomonnom): Should we avoid copy here?
|
||||
let _ = self.frame_tx.send(AudioFrame {
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
use super::media_stream_track::impl_media_stream_track;
|
||||
use crate::media_stream_track::RtcTrackState;
|
||||
use cxx::SharedPtr;
|
||||
use sys_at::ffi::audio_to_media;
|
||||
use webrtc_sys::audio_track as sys_at;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct RtcAudioTrack {
|
||||
pub(crate) sys_handle: SharedPtr<sys_at::ffi::AudioTrack>,
|
||||
}
|
||||
|
||||
impl RtcAudioTrack {
|
||||
impl_media_stream_track!(audio_to_media);
|
||||
|
||||
pub fn sys_handle(&self) -> SharedPtr<sys_at::ffi::MediaStreamTrack> {
|
||||
audio_to_media(self.sys_handle.clone())
|
||||
}
|
||||
}
|
||||
@@ -39,30 +39,21 @@ impl From<DataChannelInit> for sys_dc::ffi::DataChannelInit {
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct DataChannel {
|
||||
#[allow(dead_code)]
|
||||
native_observer: SharedPtr<sys_dc::ffi::NativeDataChannelObserver>,
|
||||
observer: Arc<DataChannelObserver>,
|
||||
|
||||
pub(crate) sys_handle: SharedPtr<sys_dc::ffi::DataChannel>,
|
||||
}
|
||||
|
||||
impl DataChannel {
|
||||
pub fn configure(sys_handle: SharedPtr<sys_dc::ffi::DataChannel>) -> Self {
|
||||
unsafe {
|
||||
let observer = Arc::new(DataChannelObserver::default());
|
||||
let dc = Self {
|
||||
sys_handle: sys_handle.clone(),
|
||||
native_observer: sys_dc::ffi::create_native_data_channel_observer(
|
||||
Box::new(sys_dc::DataChannelObserverWrapper::new(observer.clone())),
|
||||
&*sys_handle as *const _ as *mut _,
|
||||
),
|
||||
observer,
|
||||
};
|
||||
let observer = Arc::new(DataChannelObserver::default());
|
||||
let dc = Self {
|
||||
sys_handle: sys_handle.clone(),
|
||||
observer: observer.clone(),
|
||||
};
|
||||
|
||||
dc.sys_handle
|
||||
.register_observer(&*dc.native_observer as *const _ as *mut _);
|
||||
dc
|
||||
}
|
||||
dc.sys_handle
|
||||
.register_observer(Box::new(sys_dc::DataChannelObserverWrapper::new(observer)));
|
||||
dc
|
||||
}
|
||||
|
||||
pub fn send(&self, data: &[u8], binary: bool) -> Result<(), DataChannelError> {
|
||||
|
||||
@@ -1,20 +1,9 @@
|
||||
use crate::media_stream::{self, MediaStreamTrack, RtcTrackState};
|
||||
use crate::audio_track;
|
||||
use crate::imp::audio_track::RtcAudioTrack;
|
||||
use crate::imp::video_track::RtcVideoTrack;
|
||||
use crate::video_track;
|
||||
use cxx::SharedPtr;
|
||||
use webrtc_sys::media_stream as sys_ms;
|
||||
use webrtc_sys::media_stream::ffi::{
|
||||
audio_to_media, media_to_audio, media_to_video, video_to_media,
|
||||
};
|
||||
use webrtc_sys::{MEDIA_TYPE_AUDIO, MEDIA_TYPE_VIDEO};
|
||||
|
||||
impl From<sys_ms::ffi::TrackState> for RtcTrackState {
|
||||
fn from(state: sys_ms::ffi::TrackState) -> Self {
|
||||
match state {
|
||||
sys_ms::ffi::TrackState::Live => RtcTrackState::Live,
|
||||
sys_ms::ffi::TrackState::Ended => RtcTrackState::Ended,
|
||||
_ => panic!("unknown TrackState"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct MediaStream {
|
||||
@@ -26,93 +15,23 @@ impl MediaStream {
|
||||
self.sys_handle.id()
|
||||
}
|
||||
|
||||
pub fn audio_tracks(&self) -> Vec<media_stream::RtcAudioTrack> {
|
||||
pub fn audio_tracks(&self) -> Vec<audio_track::RtcAudioTrack> {
|
||||
self.sys_handle
|
||||
.get_audio_tracks()
|
||||
.into_iter()
|
||||
.map(|t| media_stream::RtcAudioTrack {
|
||||
.map(|t| audio_track::RtcAudioTrack {
|
||||
handle: RtcAudioTrack { sys_handle: t.ptr },
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn video_tracks(&self) -> Vec<media_stream::RtcVideoTrack> {
|
||||
pub fn video_tracks(&self) -> Vec<video_track::RtcVideoTrack> {
|
||||
self.sys_handle
|
||||
.get_video_tracks()
|
||||
.into_iter()
|
||||
.map(|t| media_stream::RtcVideoTrack {
|
||||
.map(|t| video_track::RtcVideoTrack {
|
||||
handle: RtcVideoTrack { sys_handle: t.ptr },
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_media_stream_track(
|
||||
sys_handle: SharedPtr<sys_ms::ffi::MediaStreamTrack>,
|
||||
) -> MediaStreamTrack {
|
||||
if sys_handle.kind() == MEDIA_TYPE_AUDIO {
|
||||
MediaStreamTrack::Audio(media_stream::RtcAudioTrack {
|
||||
handle: RtcAudioTrack {
|
||||
sys_handle: media_to_audio(sys_handle),
|
||||
},
|
||||
})
|
||||
} else if sys_handle.kind() == MEDIA_TYPE_VIDEO {
|
||||
MediaStreamTrack::Video(media_stream::RtcVideoTrack {
|
||||
handle: RtcVideoTrack {
|
||||
sys_handle: media_to_video(sys_handle),
|
||||
},
|
||||
})
|
||||
} else {
|
||||
panic!("unknown track kind")
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_media_stream_track {
|
||||
($cast:ident) => {
|
||||
pub fn id(&self) -> String {
|
||||
let ptr = sys_ms::ffi::$cast(self.sys_handle.clone());
|
||||
ptr.id()
|
||||
}
|
||||
|
||||
pub fn enabled(&self) -> bool {
|
||||
let ptr = sys_ms::ffi::$cast(self.sys_handle.clone());
|
||||
ptr.enabled()
|
||||
}
|
||||
|
||||
pub fn set_enabled(&self, enabled: bool) -> bool {
|
||||
let ptr = sys_ms::ffi::$cast(self.sys_handle.clone());
|
||||
ptr.set_enabled(enabled)
|
||||
}
|
||||
|
||||
pub fn state(&self) -> RtcTrackState {
|
||||
let ptr = sys_ms::ffi::$cast(self.sys_handle.clone());
|
||||
ptr.state().into()
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct RtcVideoTrack {
|
||||
pub(crate) sys_handle: SharedPtr<sys_ms::ffi::VideoTrack>,
|
||||
}
|
||||
|
||||
impl RtcVideoTrack {
|
||||
impl_media_stream_track!(video_to_media);
|
||||
|
||||
pub fn sys_handle(&self) -> SharedPtr<sys_ms::ffi::MediaStreamTrack> {
|
||||
video_to_media(self.sys_handle.clone())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct RtcAudioTrack {
|
||||
pub(crate) sys_handle: SharedPtr<sys_ms::ffi::AudioTrack>,
|
||||
}
|
||||
|
||||
impl RtcAudioTrack {
|
||||
impl_media_stream_track!(audio_to_media);
|
||||
|
||||
pub fn sys_handle(&self) -> SharedPtr<sys_ms::ffi::MediaStreamTrack> {
|
||||
audio_to_media(self.sys_handle.clone())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
use crate::audio_track;
|
||||
use crate::imp::audio_track::RtcAudioTrack;
|
||||
use crate::imp::video_track::RtcVideoTrack;
|
||||
use crate::media_stream_track::MediaStreamTrack;
|
||||
use crate::media_stream_track::RtcTrackState;
|
||||
use crate::video_track;
|
||||
use cxx::SharedPtr;
|
||||
use webrtc_sys::audio_track::ffi::media_to_audio;
|
||||
use webrtc_sys::media_stream_track as sys_mst;
|
||||
use webrtc_sys::video_track::ffi::media_to_video;
|
||||
use webrtc_sys::{MEDIA_TYPE_AUDIO, MEDIA_TYPE_VIDEO};
|
||||
|
||||
impl From<sys_mst::ffi::TrackState> for RtcTrackState {
|
||||
fn from(state: sys_mst::ffi::TrackState) -> Self {
|
||||
match state {
|
||||
sys_mst::ffi::TrackState::Live => RtcTrackState::Live,
|
||||
sys_mst::ffi::TrackState::Ended => RtcTrackState::Ended,
|
||||
_ => panic!("unknown TrackState"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_media_stream_track(
|
||||
sys_handle: SharedPtr<sys_mst::ffi::MediaStreamTrack>,
|
||||
) -> MediaStreamTrack {
|
||||
if sys_handle.kind() == MEDIA_TYPE_AUDIO {
|
||||
MediaStreamTrack::Audio(audio_track::RtcAudioTrack {
|
||||
handle: RtcAudioTrack {
|
||||
sys_handle: unsafe { media_to_audio(sys_handle) },
|
||||
},
|
||||
})
|
||||
} else if sys_handle.kind() == MEDIA_TYPE_VIDEO {
|
||||
MediaStreamTrack::Video(video_track::RtcVideoTrack {
|
||||
handle: RtcVideoTrack {
|
||||
sys_handle: unsafe { media_to_video(sys_handle) },
|
||||
},
|
||||
})
|
||||
} else {
|
||||
panic!("unknown track kind")
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_media_stream_track {
|
||||
($cast:expr) => {
|
||||
pub fn id(&self) -> String {
|
||||
let ptr = $cast(self.sys_handle.clone());
|
||||
ptr.id()
|
||||
}
|
||||
|
||||
pub fn enabled(&self) -> bool {
|
||||
let ptr = $cast(self.sys_handle.clone());
|
||||
ptr.enabled()
|
||||
}
|
||||
|
||||
pub fn set_enabled(&self, enabled: bool) -> bool {
|
||||
let ptr = $cast(self.sys_handle.clone());
|
||||
ptr.set_enabled(enabled)
|
||||
}
|
||||
|
||||
pub fn state(&self) -> RtcTrackState {
|
||||
let ptr = $cast(self.sys_handle.clone());
|
||||
ptr.state().into()
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub(super) use impl_media_stream_track;
|
||||
@@ -1,9 +1,11 @@
|
||||
pub mod audio_resampler;
|
||||
pub mod audio_source;
|
||||
pub mod audio_stream;
|
||||
pub mod audio_track;
|
||||
pub mod data_channel;
|
||||
pub mod ice_candidate;
|
||||
pub mod media_stream;
|
||||
pub mod media_stream_track;
|
||||
pub mod peer_connection;
|
||||
pub mod peer_connection_factory;
|
||||
pub mod rtp_parameters;
|
||||
@@ -14,6 +16,7 @@ pub mod session_description;
|
||||
pub mod video_frame;
|
||||
pub mod video_source;
|
||||
pub mod video_stream;
|
||||
pub mod video_track;
|
||||
pub mod yuv_helper;
|
||||
|
||||
use crate::MediaType;
|
||||
@@ -21,17 +24,17 @@ use crate::{RtcError, RtcErrorType};
|
||||
use webrtc_sys::rtc_error as sys_err;
|
||||
use webrtc_sys::webrtc as sys_rtc;
|
||||
|
||||
impl From<sys_err::ffi::RTCErrorType> for RtcErrorType {
|
||||
fn from(value: sys_err::ffi::RTCErrorType) -> Self {
|
||||
impl From<sys_err::ffi::RtcErrorType> for RtcErrorType {
|
||||
fn from(value: sys_err::ffi::RtcErrorType) -> Self {
|
||||
match value {
|
||||
sys_err::ffi::RTCErrorType::InvalidState => Self::InvalidState,
|
||||
sys_err::ffi::RtcErrorType::InvalidState => Self::InvalidState,
|
||||
_ => Self::Internal,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<sys_err::ffi::RTCError> for RtcError {
|
||||
fn from(value: sys_err::ffi::RTCError) -> Self {
|
||||
impl From<sys_err::ffi::RtcError> for RtcError {
|
||||
fn from(value: sys_err::ffi::RtcError) -> Self {
|
||||
Self {
|
||||
error_type: value.error_type.into(),
|
||||
message: value.message,
|
||||
|
||||
@@ -4,11 +4,13 @@ use crate::ice_candidate::IceCandidate;
|
||||
use crate::imp::data_channel as imp_dc;
|
||||
use crate::imp::ice_candidate as imp_ic;
|
||||
use crate::imp::media_stream as imp_ms;
|
||||
use crate::imp::media_stream_track as imp_mst;
|
||||
use crate::imp::rtp_receiver as imp_rr;
|
||||
use crate::imp::rtp_sender as imp_rs;
|
||||
use crate::imp::rtp_transceiver as imp_rt;
|
||||
use crate::imp::session_description as imp_sdp;
|
||||
use crate::media_stream::{MediaStream, MediaStreamTrack};
|
||||
use crate::media_stream::MediaStream;
|
||||
use crate::media_stream_track::MediaStreamTrack;
|
||||
use crate::peer_connection::{
|
||||
AnswerOptions, IceCandidateError, IceConnectionState, IceGatheringState, OfferOptions,
|
||||
OnConnectionChange, OnDataChannel, OnIceCandidate, OnIceCandidateError, OnIceConnectionChange,
|
||||
@@ -21,17 +23,16 @@ use crate::rtp_transceiver::RtpTransceiver;
|
||||
use crate::rtp_transceiver::RtpTransceiverInit;
|
||||
use crate::MediaType;
|
||||
use crate::{session_description::SessionDescription, RtcError};
|
||||
use cxx::{SharedPtr, UniquePtr};
|
||||
use cxx::SharedPtr;
|
||||
use futures::channel::oneshot;
|
||||
use parking_lot::Mutex;
|
||||
use std::mem::ManuallyDrop;
|
||||
use std::sync::Arc;
|
||||
use webrtc_sys::data_channel as sys_dc;
|
||||
use webrtc_sys::jsep as sys_jsep;
|
||||
use webrtc_sys::peer_connection as sys_pc;
|
||||
use webrtc_sys::rtc_error as sys_err;
|
||||
|
||||
impl From<OfferOptions> for sys_pc::ffi::RTCOfferAnswerOptions {
|
||||
impl From<OfferOptions> for sys_pc::ffi::RtcOfferAnswerOptions {
|
||||
fn from(options: OfferOptions) -> Self {
|
||||
Self {
|
||||
ice_restart: options.ice_restart,
|
||||
@@ -42,7 +43,7 @@ impl From<OfferOptions> for sys_pc::ffi::RTCOfferAnswerOptions {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<AnswerOptions> for sys_pc::ffi::RTCOfferAnswerOptions {
|
||||
impl From<AnswerOptions> for sys_pc::ffi::RtcOfferAnswerOptions {
|
||||
fn from(_options: AnswerOptions) -> Self {
|
||||
Self::default()
|
||||
}
|
||||
@@ -111,10 +112,7 @@ impl From<sys_pc::ffi::SignalingState> for SignalingState {
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct PeerConnection {
|
||||
#[allow(dead_code)]
|
||||
native_observer: SharedPtr<sys_pc::ffi::NativePeerConnectionObserver>,
|
||||
observer: Arc<PeerObserver>,
|
||||
|
||||
pub(crate) sys_handle: SharedPtr<sys_pc::ffi::PeerConnection>,
|
||||
}
|
||||
|
||||
@@ -122,12 +120,10 @@ impl PeerConnection {
|
||||
pub fn configure(
|
||||
sys_handle: SharedPtr<sys_pc::ffi::PeerConnection>,
|
||||
observer: Arc<PeerObserver>,
|
||||
native_observer: SharedPtr<sys_pc::ffi::NativePeerConnectionObserver>,
|
||||
) -> Self {
|
||||
Self {
|
||||
sys_handle,
|
||||
observer,
|
||||
native_observer,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,12 +131,29 @@ impl PeerConnection {
|
||||
&self,
|
||||
options: OfferOptions,
|
||||
) -> Result<SessionDescription, RtcError> {
|
||||
let (mut native_wrapper, mut sdp_rx, mut err_rx) = create_sdp_observer();
|
||||
let (sdp_tx, mut sdp_rx) = oneshot::channel();
|
||||
let (err_tx, mut err_rx) = oneshot::channel();
|
||||
|
||||
unsafe {
|
||||
self.sys_handle
|
||||
.create_offer(native_wrapper.pin_mut(), options.into());
|
||||
}
|
||||
let ctx = Box::new(sys_pc::AsyncContext(Box::new((sdp_tx, err_tx))));
|
||||
type CtxType = (
|
||||
oneshot::Sender<SessionDescription>,
|
||||
oneshot::Sender<RtcError>,
|
||||
);
|
||||
|
||||
self.sys_handle.create_offer(
|
||||
options.into(),
|
||||
ctx,
|
||||
|ctx, sdp| {
|
||||
let (sdp_tx, _) = *ctx.0.downcast::<CtxType>().unwrap();
|
||||
let _ = sdp_tx.send(SessionDescription {
|
||||
handle: imp_sdp::SessionDescription { sys_handle: sdp },
|
||||
});
|
||||
},
|
||||
|ctx, error| {
|
||||
let (_, err_tx) = *ctx.0.downcast::<CtxType>().unwrap();
|
||||
let _ = err_tx.send(error.into());
|
||||
},
|
||||
);
|
||||
|
||||
futures::select! {
|
||||
sdp = sdp_rx => Ok(sdp.unwrap()),
|
||||
@@ -152,13 +165,29 @@ impl PeerConnection {
|
||||
&self,
|
||||
options: AnswerOptions,
|
||||
) -> Result<SessionDescription, RtcError> {
|
||||
let (mut native_wrapper, mut sdp_rx, mut err_rx) = create_sdp_observer();
|
||||
let (sdp_tx, mut sdp_rx) = oneshot::channel();
|
||||
let (err_tx, mut err_rx) = oneshot::channel();
|
||||
|
||||
unsafe {
|
||||
self.sys_handle
|
||||
.create_answer(native_wrapper.pin_mut(), options.into());
|
||||
}
|
||||
let ctx = Box::new(sys_pc::AsyncContext(Box::new((sdp_tx, err_tx))));
|
||||
type CtxType = (
|
||||
oneshot::Sender<SessionDescription>,
|
||||
oneshot::Sender<RtcError>,
|
||||
);
|
||||
|
||||
self.sys_handle.create_answer(
|
||||
options.into(),
|
||||
ctx,
|
||||
|ctx, sdp| {
|
||||
let (sdp_tx, _) = *ctx.0.downcast::<CtxType>().unwrap();
|
||||
let _ = sdp_tx.send(SessionDescription {
|
||||
handle: imp_sdp::SessionDescription { sys_handle: sdp },
|
||||
});
|
||||
},
|
||||
|ctx, error| {
|
||||
let (_, err_tx) = *ctx.0.downcast::<CtxType>().unwrap();
|
||||
let _ = err_tx.send(error.into());
|
||||
},
|
||||
);
|
||||
futures::select! {
|
||||
sdp = sdp_rx => Ok(sdp.unwrap()),
|
||||
err = err_rx => Err(err.unwrap()),
|
||||
@@ -166,54 +195,66 @@ impl PeerConnection {
|
||||
}
|
||||
|
||||
pub async fn set_local_description(&self, desc: SessionDescription) -> Result<(), RtcError> {
|
||||
let (tx, rx) = oneshot::channel();
|
||||
let wrapper =
|
||||
sys_jsep::SetLocalSdpObserverWrapper(ManuallyDrop::new(Box::new(move |error| {
|
||||
let _ = tx.send(if error.ok() { Ok(()) } else { Err(error) });
|
||||
})));
|
||||
let (tx, rx) = oneshot::channel::<Result<(), RtcError>>();
|
||||
let ctx = Box::new(sys_pc::AsyncContext(Box::new(tx)));
|
||||
|
||||
let mut native_wrapper =
|
||||
sys_jsep::ffi::create_native_set_local_sdp_observer(Box::new(wrapper));
|
||||
self.sys_handle
|
||||
.set_local_description(desc.handle.sys_handle, ctx, |ctx, err| {
|
||||
let tx = ctx
|
||||
.0
|
||||
.downcast::<oneshot::Sender<Result<(), RtcError>>>()
|
||||
.unwrap();
|
||||
|
||||
unsafe {
|
||||
self.sys_handle
|
||||
.set_local_description(desc.handle.sys_handle, native_wrapper.pin_mut());
|
||||
}
|
||||
if err.ok() {
|
||||
let _ = tx.send(Ok(()));
|
||||
} else {
|
||||
let _ = tx.send(Err(err.into()));
|
||||
}
|
||||
});
|
||||
|
||||
rx.await.unwrap().map_err(Into::into)
|
||||
rx.await.unwrap()
|
||||
}
|
||||
|
||||
pub async fn set_remote_description(&self, desc: SessionDescription) -> Result<(), RtcError> {
|
||||
let (tx, rx) = oneshot::channel();
|
||||
let wrapper =
|
||||
sys_jsep::SetRemoteSdpObserverWrapper(ManuallyDrop::new(Box::new(move |error| {
|
||||
let _ = tx.send(if error.ok() { Ok(()) } else { Err(error) });
|
||||
})));
|
||||
let (tx, rx) = oneshot::channel::<Result<(), RtcError>>();
|
||||
let ctx = Box::new(sys_pc::AsyncContext(Box::new(tx)));
|
||||
|
||||
let mut native_wrapper =
|
||||
sys_jsep::ffi::create_native_set_remote_sdp_observer(Box::new(wrapper));
|
||||
self.sys_handle
|
||||
.set_remote_description(desc.handle.sys_handle, ctx, |ctx, err| {
|
||||
let tx = ctx
|
||||
.0
|
||||
.downcast::<oneshot::Sender<Result<(), RtcError>>>()
|
||||
.unwrap();
|
||||
|
||||
unsafe {
|
||||
self.sys_handle
|
||||
.set_remote_description(desc.handle.sys_handle, native_wrapper.pin_mut());
|
||||
}
|
||||
if err.ok() {
|
||||
let _ = tx.send(Ok(()));
|
||||
} else {
|
||||
let _ = tx.send(Err(err.into()));
|
||||
}
|
||||
});
|
||||
|
||||
rx.await.unwrap().map_err(Into::into)
|
||||
rx.await.unwrap()
|
||||
}
|
||||
|
||||
pub async fn add_ice_candidate(&self, candidate: IceCandidate) -> Result<(), RtcError> {
|
||||
let (tx, rx) = oneshot::channel();
|
||||
let observer =
|
||||
sys_pc::AddIceCandidateObserverWrapper(ManuallyDrop::new(Box::new(|error| {
|
||||
let _ = tx.send(if error.ok() { Ok(()) } else { Err(error) });
|
||||
})));
|
||||
let (tx, rx) = oneshot::channel::<Result<(), RtcError>>();
|
||||
let ctx = Box::new(sys_pc::AsyncContext(Box::new(tx)));
|
||||
|
||||
let mut native_observer =
|
||||
sys_pc::ffi::create_native_add_ice_candidate_observer(Box::new(observer));
|
||||
self.sys_handle
|
||||
.add_ice_candidate(candidate.handle.sys_handle, native_observer.pin_mut());
|
||||
.add_ice_candidate(candidate.handle.sys_handle, ctx, |ctx, err| {
|
||||
let tx = ctx
|
||||
.0
|
||||
.downcast::<oneshot::Sender<Result<(), RtcError>>>()
|
||||
.unwrap();
|
||||
|
||||
rx.await.unwrap().map_err(Into::into)
|
||||
if err.ok() {
|
||||
let _ = tx.send(Ok(()));
|
||||
} else {
|
||||
let _ = tx.send(Err(err.into()));
|
||||
}
|
||||
});
|
||||
|
||||
rx.await.unwrap()
|
||||
}
|
||||
|
||||
pub fn create_data_channel(
|
||||
@@ -221,16 +262,15 @@ impl PeerConnection {
|
||||
label: &str,
|
||||
init: DataChannelInit,
|
||||
) -> Result<DataChannel, RtcError> {
|
||||
let native_init = sys_dc::ffi::create_data_channel_init(init.into());
|
||||
let res = self
|
||||
.sys_handle
|
||||
.create_data_channel(label.to_string(), native_init);
|
||||
.create_data_channel(label.to_string(), init.into());
|
||||
|
||||
match res {
|
||||
Ok(sys_handle) => Ok(DataChannel {
|
||||
handle: imp_dc::DataChannel::configure(sys_handle),
|
||||
}),
|
||||
Err(e) => Err(unsafe { sys_err::ffi::RTCError::from(e.what()).into() }),
|
||||
Err(e) => Err(unsafe { sys_err::ffi::RtcError::from(e.what()).into() }),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -246,7 +286,7 @@ impl PeerConnection {
|
||||
Ok(sys_handle) => Ok(RtpSender {
|
||||
handle: imp_rs::RtpSender { sys_handle },
|
||||
}),
|
||||
Err(e) => unsafe { Err(sys_err::ffi::RTCError::from(e.what()).into()) },
|
||||
Err(e) => unsafe { Err(sys_err::ffi::RtcError::from(e.what()).into()) },
|
||||
}
|
||||
}
|
||||
|
||||
@@ -265,7 +305,7 @@ impl PeerConnection {
|
||||
sys_handle: sys_handle,
|
||||
},
|
||||
}),
|
||||
Err(e) => unsafe { Err(sys_err::ffi::RTCError::from(e.what()).into()) },
|
||||
Err(e) => unsafe { Err(sys_err::ffi::RtcError::from(e.what()).into()) },
|
||||
}
|
||||
}
|
||||
|
||||
@@ -284,7 +324,7 @@ impl PeerConnection {
|
||||
sys_handle: cxx_handle,
|
||||
},
|
||||
}),
|
||||
Err(e) => unsafe { Err(sys_err::ffi::RTCError::from(e.what()).into()) },
|
||||
Err(e) => unsafe { Err(sys_err::ffi::RtcError::from(e.what()).into()) },
|
||||
}
|
||||
}
|
||||
|
||||
@@ -333,7 +373,7 @@ impl PeerConnection {
|
||||
pub fn remove_track(&self, sender: RtpSender) -> Result<(), RtcError> {
|
||||
self.sys_handle
|
||||
.remove_track(sender.handle.sys_handle)
|
||||
.map_err(|e| unsafe { sys_err::ffi::RTCError::from(e.what()).into() })
|
||||
.map_err(|e| unsafe { sys_err::ffi::RtcError::from(e.what()).into() })
|
||||
}
|
||||
|
||||
pub fn senders(&self) -> Vec<RtpSender> {
|
||||
@@ -409,34 +449,6 @@ impl PeerConnection {
|
||||
}
|
||||
}
|
||||
|
||||
fn create_sdp_observer() -> (
|
||||
UniquePtr<sys_pc::ffi::NativeCreateSdpObserverHandle>,
|
||||
oneshot::Receiver<SessionDescription>,
|
||||
oneshot::Receiver<RtcError>,
|
||||
) {
|
||||
let (sdp_tx, sdp_rx) = oneshot::channel();
|
||||
let (err_tx, err_rx) = oneshot::channel();
|
||||
|
||||
let wrapper = sys_jsep::CreateSdpObserverWrapper {
|
||||
on_success: ManuallyDrop::new(Box::new(move |session_description| {
|
||||
let _ = sdp_tx.send(SessionDescription {
|
||||
handle: imp_sdp::SessionDescription {
|
||||
sys_handle: session_description,
|
||||
},
|
||||
});
|
||||
})),
|
||||
on_failure: ManuallyDrop::new(Box::new(move |error| {
|
||||
let _ = err_tx.send(error.into());
|
||||
})),
|
||||
};
|
||||
|
||||
(
|
||||
sys_jsep::ffi::create_native_create_sdp_observer(Box::new(wrapper)),
|
||||
sdp_rx,
|
||||
err_rx,
|
||||
)
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct PeerObserver {
|
||||
pub connection_change_handler: Mutex<Option<OnConnectionChange>>,
|
||||
@@ -565,7 +577,7 @@ impl sys_pc::PeerConnectionObserver for PeerObserver {
|
||||
handle: imp_ms::MediaStream { sys_handle: s.ptr },
|
||||
})
|
||||
.collect(),
|
||||
track: imp_ms::new_media_stream_track(track),
|
||||
track: imp_mst::new_media_stream_track(track),
|
||||
transceiver: RtpTransceiver {
|
||||
handle: imp_rt::RtpTransceiver {
|
||||
sys_handle: transceiver,
|
||||
|
||||
@@ -1,89 +1,61 @@
|
||||
use crate::audio_source::native::NativeAudioSource;
|
||||
use crate::imp::media_stream as imp_ms;
|
||||
use crate::audio_track::RtcAudioTrack;
|
||||
use crate::imp::audio_track as imp_at;
|
||||
use crate::imp::peer_connection as imp_pc;
|
||||
use crate::media_stream::{RtcAudioTrack, RtcVideoTrack};
|
||||
use crate::imp::video_track as imp_vt;
|
||||
use crate::peer_connection::PeerConnection;
|
||||
use crate::peer_connection_factory::{
|
||||
ContinualGatheringPolicy, IceServer, IceTransportsType, RtcConfiguration,
|
||||
};
|
||||
use crate::rtp_parameters::RtpCapabilities;
|
||||
use crate::video_source::native::NativeVideoSource;
|
||||
use crate::video_track::RtcVideoTrack;
|
||||
use crate::MediaType;
|
||||
use crate::RtcError;
|
||||
use cxx::SharedPtr;
|
||||
use cxx::UniquePtr;
|
||||
use lazy_static::lazy_static;
|
||||
use parking_lot::Mutex;
|
||||
use std::sync::{Arc, Weak};
|
||||
use webrtc_sys::logsink as sys_ls;
|
||||
use std::sync::Arc;
|
||||
use webrtc_sys::peer_connection as sys_pc;
|
||||
use webrtc_sys::peer_connection_factory as sys_pcf;
|
||||
use webrtc_sys::rtc_error as sys_err;
|
||||
use webrtc_sys::webrtc as sys_webrtc;
|
||||
use webrtc_sys::webrtc as sys_rtc;
|
||||
|
||||
lazy_static! {
|
||||
static ref RTC_RUNTIME: Mutex<Weak<RtcRuntime>> = Mutex::new(Weak::new());
|
||||
}
|
||||
|
||||
pub struct RtcRuntime {
|
||||
pub(crate) sys_handle: SharedPtr<sys_webrtc::ffi::RTCRuntime>,
|
||||
_logsink: UniquePtr<sys_ls::ffi::LogSink>,
|
||||
}
|
||||
|
||||
impl RtcRuntime {
|
||||
pub fn instance() -> Arc<RtcRuntime> {
|
||||
let mut lk_runtime_ref = RTC_RUNTIME.lock();
|
||||
if let Some(lk_runtime) = lk_runtime_ref.upgrade() {
|
||||
lk_runtime
|
||||
} else {
|
||||
log::trace!("RtcRuntime::new()");
|
||||
let new_runtime = Arc::new(Self {
|
||||
sys_handle: sys_webrtc::ffi::create_rtc_runtime(),
|
||||
_logsink: sys_ls::ffi::new_log_sink(|msg, severity| {
|
||||
// Forward logs from webrtc to rust log crate
|
||||
let msg = msg
|
||||
.strip_suffix("\r\n")
|
||||
.or(msg.strip_suffix("\n"))
|
||||
.unwrap_or(&msg);
|
||||
|
||||
let lvl = match severity {
|
||||
sys_ls::ffi::LoggingSeverity::Verbose => log::Level::Trace,
|
||||
sys_ls::ffi::LoggingSeverity::Info => log::Level::Debug, // Translte webrtc
|
||||
// info to debug log level to avoid polluting the user logs
|
||||
sys_ls::ffi::LoggingSeverity::Warning => log::Level::Warn,
|
||||
sys_ls::ffi::LoggingSeverity::Error => log::Level::Error,
|
||||
_ => log::Level::Debug,
|
||||
};
|
||||
|
||||
log::log!(target: "libwebrtc", lvl, "{}", msg);
|
||||
}),
|
||||
});
|
||||
*lk_runtime_ref = Arc::downgrade(&new_runtime);
|
||||
new_runtime
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for RtcRuntime {
|
||||
fn drop(&mut self) {
|
||||
log::trace!("RtcRuntime::drop()");
|
||||
}
|
||||
static ref LOG_SINK: Mutex<Option<UniquePtr<sys_rtc::ffi::LogSink>>> = Default::default();
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct PeerConnectionFactory {
|
||||
sys_handle: SharedPtr<sys_pcf::ffi::PeerConnectionFactory>,
|
||||
|
||||
#[allow(unused)]
|
||||
runtime: Arc<RtcRuntime>,
|
||||
}
|
||||
|
||||
impl Default for PeerConnectionFactory {
|
||||
fn default() -> Self {
|
||||
let runtime = RtcRuntime::instance();
|
||||
let mut log_sink = LOG_SINK.lock();
|
||||
if log_sink.is_none() {
|
||||
*log_sink = Some(sys_rtc::ffi::new_log_sink(|msg, severity| {
|
||||
let msg = msg
|
||||
.strip_suffix("\r\n")
|
||||
.or(msg.strip_suffix("\n"))
|
||||
.unwrap_or(&msg);
|
||||
|
||||
let lvl = match severity {
|
||||
sys_rtc::ffi::LoggingSeverity::Verbose => log::Level::Trace,
|
||||
sys_rtc::ffi::LoggingSeverity::Info => log::Level::Debug, // Translte webrtc
|
||||
// info to debug log level to avoid polluting the user logs
|
||||
sys_rtc::ffi::LoggingSeverity::Warning => log::Level::Warn,
|
||||
sys_rtc::ffi::LoggingSeverity::Error => log::Level::Error,
|
||||
_ => log::Level::Debug,
|
||||
};
|
||||
|
||||
log::log!(target: "libwebrtc", lvl, "{}", msg);
|
||||
}));
|
||||
}
|
||||
|
||||
Self {
|
||||
sys_handle: sys_pcf::ffi::create_peer_connection_factory(runtime.sys_handle.clone()),
|
||||
runtime,
|
||||
sys_handle: sys_pcf::ffi::create_peer_connection_factory(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -93,35 +65,26 @@ impl PeerConnectionFactory {
|
||||
&self,
|
||||
config: RtcConfiguration,
|
||||
) -> Result<PeerConnection, RtcError> {
|
||||
let native_config = sys_pcf::ffi::create_rtc_configuration(config.into());
|
||||
let observer = Arc::new(imp_pc::PeerObserver::default());
|
||||
let native_observer = sys_pc::ffi::create_native_peer_connection_observer(Box::new(
|
||||
sys_pc::PeerConnectionObserverWrapper::new(observer.clone()),
|
||||
));
|
||||
|
||||
unsafe {
|
||||
let observer = Arc::new(imp_pc::PeerObserver::default());
|
||||
let native_observer = sys_pc::ffi::create_native_peer_connection_observer(
|
||||
self.runtime.sys_handle.clone(),
|
||||
Box::new(sys_pc::PeerConnectionObserverWrapper::new(observer.clone())),
|
||||
);
|
||||
let res = self
|
||||
.sys_handle
|
||||
.create_peer_connection(config.into(), native_observer);
|
||||
|
||||
let res = self
|
||||
.sys_handle
|
||||
.create_peer_connection(native_config, &*native_observer as *const _ as *mut _);
|
||||
|
||||
match res {
|
||||
Ok(sys_handle) => Ok(PeerConnection {
|
||||
handle: imp_pc::PeerConnection::configure(
|
||||
sys_handle,
|
||||
observer,
|
||||
native_observer,
|
||||
),
|
||||
}),
|
||||
Err(e) => Err(sys_err::ffi::RTCError::from(e.what()).into()),
|
||||
}
|
||||
match res {
|
||||
Ok(sys_handle) => Ok(PeerConnection {
|
||||
handle: imp_pc::PeerConnection::configure(sys_handle, observer),
|
||||
}),
|
||||
Err(e) => Err(unsafe { sys_err::ffi::RtcError::from(e.what()).into() }),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_video_track(&self, label: &str, source: NativeVideoSource) -> RtcVideoTrack {
|
||||
RtcVideoTrack {
|
||||
handle: imp_ms::RtcVideoTrack {
|
||||
handle: imp_vt::RtcVideoTrack {
|
||||
sys_handle: self
|
||||
.sys_handle
|
||||
.create_video_track(label.to_string(), source.handle.sys_handle()),
|
||||
@@ -131,7 +94,7 @@ impl PeerConnectionFactory {
|
||||
|
||||
pub fn create_audio_track(&self, label: &str, source: NativeAudioSource) -> RtcAudioTrack {
|
||||
RtcAudioTrack {
|
||||
handle: imp_ms::RtcAudioTrack {
|
||||
handle: imp_at::RtcAudioTrack {
|
||||
sys_handle: self
|
||||
.sys_handle
|
||||
.create_audio_track(label.to_string(), source.handle.sys_handle()),
|
||||
@@ -153,9 +116,9 @@ impl PeerConnectionFactory {
|
||||
}
|
||||
|
||||
// Conversions
|
||||
impl From<IceServer> for sys_pcf::ffi::ICEServer {
|
||||
impl From<IceServer> for sys_pcf::ffi::IceServer {
|
||||
fn from(value: IceServer) -> Self {
|
||||
sys_pcf::ffi::ICEServer {
|
||||
sys_pcf::ffi::IceServer {
|
||||
urls: value.urls,
|
||||
username: value.username,
|
||||
password: value.password,
|
||||
@@ -187,7 +150,7 @@ impl From<IceTransportsType> for sys_pcf::ffi::IceTransportsType {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RtcConfiguration> for sys_pcf::ffi::RTCConfiguration {
|
||||
impl From<RtcConfiguration> for sys_pcf::ffi::RtcConfiguration {
|
||||
fn from(value: RtcConfiguration) -> Self {
|
||||
Self {
|
||||
ice_servers: value.ice_servers.into_iter().map(Into::into).collect(),
|
||||
@@ -196,3 +159,18 @@ impl From<RtcConfiguration> for sys_pcf::ffi::RTCConfiguration {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_peer_connection_factory() {
|
||||
let _ = env_logger::builder().is_test(true).try_init();
|
||||
|
||||
let factory = PeerConnectionFactory::default();
|
||||
let source = NativeVideoSource::default();
|
||||
let _track = factory.create_video_track("test", source);
|
||||
drop(factory);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use super::media_stream::new_media_stream_track;
|
||||
use crate::{media_stream::MediaStreamTrack, rtp_parameters::RtpParameters};
|
||||
use crate::imp::media_stream_track::new_media_stream_track;
|
||||
use crate::media_stream_track::MediaStreamTrack;
|
||||
use crate::rtp_parameters::RtpParameters;
|
||||
use cxx::SharedPtr;
|
||||
use webrtc_sys::rtp_receiver as sys_rr;
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use super::media_stream::new_media_stream_track;
|
||||
use crate::{
|
||||
media_stream::MediaStreamTrack, rtp_parameters::RtpParameters, RtcError, RtcErrorType,
|
||||
};
|
||||
use super::media_stream_track::new_media_stream_track;
|
||||
use crate::media_stream_track::MediaStreamTrack;
|
||||
use crate::{rtp_parameters::RtpParameters, RtcError, RtcErrorType};
|
||||
use cxx::SharedPtr;
|
||||
use webrtc_sys::{rtc_error::ffi::RTCError, rtp_sender as sys_rs};
|
||||
use webrtc_sys::rtc_error as sys_err;
|
||||
use webrtc_sys::rtp_sender as sys_rs;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct RtpSender {
|
||||
@@ -41,6 +41,6 @@ impl RtpSender {
|
||||
pub fn set_parameters(&self, parameters: RtpParameters) -> Result<(), RtcError> {
|
||||
self.sys_handle
|
||||
.set_parameters(parameters.into())
|
||||
.map_err(|e| unsafe { RTCError::from(e.what()).into() })
|
||||
.map_err(|e| unsafe { sys_err::ffi::RtcError::from(e.what()).into() })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,12 +83,12 @@ impl RtpTransceiver {
|
||||
pub fn set_codec_preferences(&self, codecs: Vec<RtpCodecCapability>) -> Result<(), RtcError> {
|
||||
self.sys_handle
|
||||
.set_codec_preferences(codecs.into_iter().map(Into::into).collect())
|
||||
.map_err(|e| unsafe { sys_err::ffi::RTCError::from(e.what()).into() })
|
||||
.map_err(|e| unsafe { sys_err::ffi::RtcError::from(e.what()).into() })
|
||||
}
|
||||
|
||||
pub fn stop(&self) -> Result<(), RtcError> {
|
||||
self.sys_handle
|
||||
.stop_standard()
|
||||
.map_err(|e| unsafe { sys_err::ffi::RTCError::from(e.what()).into() })
|
||||
.map_err(|e| unsafe { sys_err::ffi::RtcError::from(e.what()).into() })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
use crate::video_frame::{VideoFrame, VideoFrameBuffer};
|
||||
use cxx::SharedPtr;
|
||||
use webrtc_sys::media_stream as ms_sys;
|
||||
use webrtc_sys::video_frame as vf_sys;
|
||||
use webrtc_sys::video_track as vt_sys;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct NativeVideoSource {
|
||||
sys_handle: SharedPtr<ms_sys::ffi::AdaptedVideoTrackSource>,
|
||||
sys_handle: SharedPtr<vt_sys::ffi::VideoTrackSource>,
|
||||
}
|
||||
|
||||
impl Default for NativeVideoSource {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
sys_handle: ms_sys::ffi::new_adapted_video_track_source(),
|
||||
sys_handle: vt_sys::ffi::new_video_track_source(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl NativeVideoSource {
|
||||
pub fn sys_handle(&self) -> SharedPtr<ms_sys::ffi::AdaptedVideoTrackSource> {
|
||||
pub fn sys_handle(&self) -> SharedPtr<vt_sys::ffi::VideoTrackSource> {
|
||||
self.sys_handle.clone()
|
||||
}
|
||||
|
||||
@@ -27,8 +27,7 @@ impl NativeVideoSource {
|
||||
builder
|
||||
.pin_mut()
|
||||
.set_video_frame_buffer(frame.buffer.as_ref().sys_handle());
|
||||
|
||||
let frame = builder.pin_mut().build();
|
||||
self.sys_handle.on_captured_frame(&frame);
|
||||
self.sys_handle
|
||||
.on_captured_frame(&builder.pin_mut().build());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
use super::video_frame::new_video_frame_buffer;
|
||||
use crate::media_stream::RtcVideoTrack;
|
||||
use crate::video_frame::{BoxVideoFrame, VideoFrame};
|
||||
use cxx::UniquePtr;
|
||||
use crate::video_track::RtcVideoTrack;
|
||||
use cxx::{SharedPtr, UniquePtr};
|
||||
use futures::stream::Stream;
|
||||
use std::pin::Pin;
|
||||
use std::sync::Arc;
|
||||
use std::task::{Context, Poll};
|
||||
use tokio::sync::mpsc;
|
||||
use webrtc_sys::media_stream as sys_ms;
|
||||
use webrtc_sys::video_track as sys_vt;
|
||||
|
||||
pub struct NativeVideoStream {
|
||||
native_observer: UniquePtr<sys_ms::ffi::NativeVideoFrameSink>,
|
||||
_observer: Box<VideoTrackObserver>,
|
||||
native_sink: SharedPtr<sys_vt::ffi::NativeVideoSink>,
|
||||
video_track: RtcVideoTrack,
|
||||
frame_rx: mpsc::UnboundedReceiver<BoxVideoFrame>,
|
||||
}
|
||||
@@ -18,21 +18,16 @@ pub struct NativeVideoStream {
|
||||
impl NativeVideoStream {
|
||||
pub fn new(video_track: RtcVideoTrack) -> Self {
|
||||
let (frame_tx, frame_rx) = mpsc::unbounded_channel();
|
||||
let mut observer = Box::new(VideoTrackObserver { frame_tx });
|
||||
let mut native_observer = unsafe {
|
||||
sys_ms::ffi::new_native_video_frame_sink(Box::new(sys_ms::VideoFrameSinkWrapper::new(
|
||||
&mut *observer,
|
||||
)))
|
||||
};
|
||||
let observer = Arc::new(VideoTrackObserver { frame_tx });
|
||||
let native_sink = sys_vt::ffi::new_native_video_sink(Box::new(
|
||||
sys_vt::VideoSinkWrapper::new(observer.clone()),
|
||||
));
|
||||
|
||||
unsafe {
|
||||
sys_ms::ffi::media_to_video(video_track.sys_handle())
|
||||
.add_sink(native_observer.pin_mut());
|
||||
}
|
||||
let video = unsafe { sys_vt::ffi::media_to_video(video_track.sys_handle()) };
|
||||
video.add_sink(&native_sink);
|
||||
|
||||
Self {
|
||||
native_observer,
|
||||
_observer: observer,
|
||||
native_sink,
|
||||
video_track,
|
||||
frame_rx,
|
||||
}
|
||||
@@ -43,11 +38,10 @@ impl NativeVideoStream {
|
||||
}
|
||||
|
||||
pub fn close(&mut self) {
|
||||
let video = unsafe { sys_vt::ffi::media_to_video(self.video_track.sys_handle()) };
|
||||
video.remove_sink(&self.native_sink);
|
||||
|
||||
self.frame_rx.close();
|
||||
unsafe {
|
||||
sys_ms::ffi::media_to_video(self.video_track.sys_handle())
|
||||
.remove_sink(self.native_observer.pin_mut());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,7 +63,7 @@ struct VideoTrackObserver {
|
||||
frame_tx: mpsc::UnboundedSender<BoxVideoFrame>,
|
||||
}
|
||||
|
||||
impl sys_ms::VideoFrameSink for VideoTrackObserver {
|
||||
impl sys_vt::VideoSink for VideoTrackObserver {
|
||||
fn on_frame(&self, frame: UniquePtr<webrtc_sys::video_frame::ffi::VideoFrame>) {
|
||||
let _ = self.frame_tx.send(VideoFrame {
|
||||
rotation: frame.rotation().into(),
|
||||
@@ -80,5 +74,5 @@ impl sys_ms::VideoFrameSink for VideoTrackObserver {
|
||||
|
||||
fn on_discarded_frame(&self) {}
|
||||
|
||||
fn on_constraints_changed(&self, _constraints: sys_ms::ffi::VideoTrackSourceConstraints) {}
|
||||
fn on_constraints_changed(&self, _constraints: sys_vt::ffi::VideoTrackSourceConstraints) {}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
use super::media_stream_track::impl_media_stream_track;
|
||||
use crate::media_stream_track::RtcTrackState;
|
||||
use cxx::SharedPtr;
|
||||
use sys_vt::ffi::video_to_media;
|
||||
use webrtc_sys::video_track as sys_vt;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct RtcVideoTrack {
|
||||
pub(crate) sys_handle: SharedPtr<sys_vt::ffi::VideoTrack>,
|
||||
}
|
||||
|
||||
impl RtcVideoTrack {
|
||||
impl_media_stream_track!(video_to_media);
|
||||
|
||||
pub fn sys_handle(&self) -> SharedPtr<sys_vt::ffi::MediaStreamTrack> {
|
||||
video_to_media(self.sys_handle.clone())
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,8 @@ use std::fmt::Debug;
|
||||
use crate::data_channel::{DataChannel, DataChannelInit};
|
||||
use crate::ice_candidate::IceCandidate;
|
||||
use crate::imp::peer_connection as imp_pc;
|
||||
use crate::media_stream::{MediaStream, MediaStreamTrack};
|
||||
use crate::media_stream::MediaStream;
|
||||
use crate::media_stream_track::MediaStreamTrack;
|
||||
use crate::rtp_receiver::RtpReceiver;
|
||||
use crate::rtp_sender::RtpSender;
|
||||
use crate::rtp_transceiver::{RtpTransceiver, RtpTransceiverInit};
|
||||
|
||||
@@ -33,6 +33,16 @@ pub struct RtcConfiguration {
|
||||
pub ice_transport_type: IceTransportsType,
|
||||
}
|
||||
|
||||
impl Default for RtcConfiguration {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
ice_servers: vec![],
|
||||
continual_gathering_policy: ContinualGatheringPolicy::GatherOnce,
|
||||
ice_transport_type: IceTransportsType::All,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct PeerConnectionFactory {
|
||||
pub(crate) handle: imp_pcf::PeerConnectionFactory,
|
||||
@@ -64,8 +74,9 @@ impl PeerConnectionFactory {
|
||||
pub mod native {
|
||||
use super::PeerConnectionFactory;
|
||||
use crate::audio_source::native::NativeAudioSource;
|
||||
use crate::media_stream::{RtcAudioTrack, RtcVideoTrack};
|
||||
use crate::audio_track::RtcAudioTrack;
|
||||
use crate::video_source::native::NativeVideoSource;
|
||||
use crate::video_track::RtcVideoTrack;
|
||||
|
||||
pub trait PeerConnectionFactoryExt {
|
||||
fn create_video_track(&self, label: &str, source: NativeVideoSource) -> RtcVideoTrack;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
pub use crate::audio_frame::AudioFrame;
|
||||
pub use crate::audio_track::RtcAudioTrack;
|
||||
pub use crate::data_channel::{
|
||||
DataBuffer, DataChannel, DataChannelError, DataChannelInit, DataState,
|
||||
};
|
||||
pub use crate::ice_candidate::IceCandidate;
|
||||
pub use crate::media_stream::{
|
||||
MediaStream, MediaStreamTrack, RtcAudioTrack, RtcTrackState, RtcVideoTrack,
|
||||
};
|
||||
pub use crate::media_stream::MediaStream;
|
||||
pub use crate::media_stream_track::{MediaStreamTrack, RtcTrackState};
|
||||
pub use crate::peer_connection::{
|
||||
AnswerOptions, IceConnectionState, IceGatheringState, OfferOptions, PeerConnection,
|
||||
PeerConnectionState, SignalingState,
|
||||
@@ -22,4 +22,5 @@ pub use crate::video_frame::{
|
||||
BoxVideoFrame, I010Buffer, I420ABuffer, I420Buffer, I422Buffer, I444Buffer, NV12Buffer,
|
||||
VideoFormatType, VideoFrame, VideoFrameBuffer, VideoFrameBufferType, VideoRotation,
|
||||
};
|
||||
pub use crate::{RtcError, RtcErrorType};
|
||||
pub use crate::video_track::RtcVideoTrack;
|
||||
pub use crate::{MediaType, RtcError, RtcErrorType};
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
use std::fmt::Debug;
|
||||
|
||||
use crate::{
|
||||
imp::rtp_receiver as imp_rr, media_stream::MediaStreamTrack, rtp_parameters::RtpParameters,
|
||||
imp::rtp_receiver as imp_rr, media_stream_track::MediaStreamTrack,
|
||||
rtp_parameters::RtpParameters,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::fmt::Debug;
|
||||
|
||||
use crate::{
|
||||
imp::rtp_sender as imp_rs, media_stream::MediaStreamTrack, rtp_parameters::RtpParameters,
|
||||
imp::rtp_sender as imp_rs, media_stream_track::MediaStreamTrack, rtp_parameters::RtpParameters,
|
||||
RtcError,
|
||||
};
|
||||
|
||||
|
||||
@@ -6,8 +6,8 @@ use crate::imp::video_stream as stream_imp;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub mod native {
|
||||
use super::stream_imp;
|
||||
use crate::media_stream::RtcVideoTrack;
|
||||
use crate::video_frame::BoxVideoFrame;
|
||||
use crate::video_track::RtcVideoTrack;
|
||||
use futures::stream::Stream;
|
||||
use std::fmt::Debug;
|
||||
use std::pin::Pin;
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
use crate::imp::video_track as imp_vt;
|
||||
use crate::media_stream_track::media_stream_track;
|
||||
use crate::media_stream_track::RtcTrackState;
|
||||
use std::fmt::Debug;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct RtcVideoTrack {
|
||||
pub(crate) handle: imp_vt::RtcVideoTrack,
|
||||
}
|
||||
|
||||
impl RtcVideoTrack {
|
||||
media_stream_track!();
|
||||
}
|
||||
|
||||
impl Debug for RtcVideoTrack {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("RtcVideoTrack")
|
||||
.field("id", &self.id())
|
||||
.field("enabled", &self.enabled())
|
||||
.field("state", &self.state())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
@@ -2,9 +2,8 @@ use super::{ConnectionQuality, ParticipantInner};
|
||||
use crate::prelude::*;
|
||||
use crate::track::TrackError;
|
||||
use livekit_protocol as proto;
|
||||
use livekit_webrtc as rtc;
|
||||
use livekit_webrtc::prelude::*;
|
||||
use parking_lot::RwLockReadGuard;
|
||||
use rtc::prelude::MediaStreamTrack;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::fmt::Debug;
|
||||
use std::sync::Arc;
|
||||
@@ -67,7 +66,7 @@ impl RemoteParticipant {
|
||||
pub(crate) async fn add_subscribed_media_track(
|
||||
&self,
|
||||
sid: TrackSid,
|
||||
media_track: rtc::media_stream::MediaStreamTrack,
|
||||
media_track: MediaStreamTrack,
|
||||
) {
|
||||
let wait_publication = {
|
||||
let participant = self.clone();
|
||||
|
||||
@@ -4,9 +4,8 @@ use crate::prelude::*;
|
||||
use crate::rtc_engine::lk_runtime::LkRuntime;
|
||||
use crate::webrtc::peer_connection_factory::native::PeerConnectionFactoryExt;
|
||||
use livekit_protocol as proto;
|
||||
use livekit_webrtc as rtc;
|
||||
use livekit_webrtc::prelude::*;
|
||||
use parking_lot::Mutex;
|
||||
use rtc::audio_source::native::NativeAudioSource;
|
||||
use std::fmt::Debug;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::mpsc;
|
||||
@@ -35,7 +34,7 @@ impl Debug for LocalAudioTrack {
|
||||
impl LocalAudioTrack {
|
||||
pub(crate) fn new(
|
||||
name: String,
|
||||
rtc_track: rtc::media_stream::RtcAudioTrack,
|
||||
rtc_track: RtcAudioTrack,
|
||||
capture_options: AudioCaptureOptions,
|
||||
) -> Self {
|
||||
Self {
|
||||
@@ -44,7 +43,7 @@ impl LocalAudioTrack {
|
||||
"unknown".to_string().into(), // sid
|
||||
name,
|
||||
TrackKind::Audio,
|
||||
rtc::media_stream::MediaStreamTrack::Audio(rtc_track),
|
||||
MediaStreamTrack::Audio(rtc_track),
|
||||
),
|
||||
capture_options: Mutex::new(capture_options),
|
||||
}),
|
||||
@@ -102,14 +101,11 @@ impl LocalAudioTrack {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn rtc_track(&self) -> rtc::media_stream::RtcAudioTrack {
|
||||
if let rtc::media_stream::MediaStreamTrack::Audio(audio) =
|
||||
self.inner.track_inner.rtc_track()
|
||||
{
|
||||
audio
|
||||
} else {
|
||||
unreachable!()
|
||||
pub fn rtc_track(&self) -> RtcAudioTrack {
|
||||
if let MediaStreamTrack::Audio(audio) = self.inner.track_inner.rtc_track() {
|
||||
return audio;
|
||||
}
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -123,15 +119,12 @@ impl LocalAudioTrack {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn transceiver(&self) -> Option<rtc::rtp_transceiver::RtpTransceiver> {
|
||||
pub(crate) fn transceiver(&self) -> Option<RtpTransceiver> {
|
||||
self.inner.track_inner.transceiver()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn update_transceiver(
|
||||
&self,
|
||||
transceiver: Option<rtc::rtp_transceiver::RtpTransceiver>,
|
||||
) {
|
||||
pub(crate) fn update_transceiver(&self, transceiver: Option<RtpTransceiver>) {
|
||||
self.inner.track_inner.update_transceiver(transceiver)
|
||||
}
|
||||
|
||||
@@ -145,11 +138,11 @@ impl LocalAudioTrack {
|
||||
pub fn create_audio_track(
|
||||
name: &str,
|
||||
options: AudioCaptureOptions,
|
||||
source: NativeAudioSource,
|
||||
source: livekit_webrtc::audio_source::native::NativeAudioSource,
|
||||
) -> LocalAudioTrack {
|
||||
let rtc_track = LkRuntime::instance()
|
||||
.pc_factory()
|
||||
.create_audio_track(&rtc::native::create_random_uuid(), source);
|
||||
.create_audio_track(&livekit_webrtc::native::create_random_uuid(), source);
|
||||
|
||||
Self::new(name.to_string(), rtc_track, options)
|
||||
}
|
||||
|
||||
@@ -2,10 +2,9 @@ use super::TrackInner;
|
||||
use crate::rtc_engine::lk_runtime::LkRuntime;
|
||||
use crate::{options::VideoCaptureOptions, prelude::*};
|
||||
use livekit_protocol as proto;
|
||||
use livekit_webrtc as rtc;
|
||||
use livekit_webrtc::peer_connection_factory::native::PeerConnectionFactoryExt;
|
||||
use livekit_webrtc::prelude::*;
|
||||
use parking_lot::Mutex;
|
||||
use rtc::video_source::native::NativeVideoSource;
|
||||
use std::fmt::Debug;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::mpsc;
|
||||
@@ -34,7 +33,7 @@ impl Debug for LocalVideoTrack {
|
||||
impl LocalVideoTrack {
|
||||
pub fn new(
|
||||
name: String,
|
||||
rtc_track: rtc::media_stream::RtcVideoTrack,
|
||||
rtc_track: RtcVideoTrack,
|
||||
capture_options: VideoCaptureOptions,
|
||||
) -> Self {
|
||||
Self {
|
||||
@@ -43,7 +42,7 @@ impl LocalVideoTrack {
|
||||
"unknown".to_string().into(), // sid
|
||||
name,
|
||||
TrackKind::Video,
|
||||
rtc::media_stream::MediaStreamTrack::Video(rtc_track),
|
||||
MediaStreamTrack::Video(rtc_track),
|
||||
),
|
||||
capture_options: Mutex::new(capture_options),
|
||||
}),
|
||||
@@ -101,14 +100,11 @@ impl LocalVideoTrack {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn rtc_track(&self) -> rtc::media_stream::RtcVideoTrack {
|
||||
if let rtc::media_stream::MediaStreamTrack::Video(video) =
|
||||
self.inner.track_inner.rtc_track()
|
||||
{
|
||||
video
|
||||
} else {
|
||||
unreachable!()
|
||||
pub fn rtc_track(&self) -> RtcVideoTrack {
|
||||
if let MediaStreamTrack::Video(video) = self.inner.track_inner.rtc_track() {
|
||||
return video;
|
||||
}
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -122,15 +118,12 @@ impl LocalVideoTrack {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn transceiver(&self) -> Option<rtc::rtp_transceiver::RtpTransceiver> {
|
||||
pub(crate) fn transceiver(&self) -> Option<RtpTransceiver> {
|
||||
self.inner.track_inner.transceiver()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn update_transceiver(
|
||||
&self,
|
||||
transceiver: Option<rtc::rtp_transceiver::RtpTransceiver>,
|
||||
) {
|
||||
pub(crate) fn update_transceiver(&self, transceiver: Option<RtpTransceiver>) {
|
||||
self.inner.track_inner.update_transceiver(transceiver)
|
||||
}
|
||||
|
||||
@@ -144,11 +137,11 @@ impl LocalVideoTrack {
|
||||
pub fn create_video_track(
|
||||
name: &str,
|
||||
options: VideoCaptureOptions,
|
||||
source: NativeVideoSource,
|
||||
source: livekit_webrtc::video_source::native::NativeVideoSource,
|
||||
) -> LocalVideoTrack {
|
||||
let rtc_track = LkRuntime::instance()
|
||||
.pc_factory()
|
||||
.create_video_track(&rtc::native::create_random_uuid(), source);
|
||||
.create_video_track(&livekit_webrtc::native::create_random_uuid(), source);
|
||||
|
||||
Self::new(name.to_string(), rtc_track, options)
|
||||
}
|
||||
|
||||
@@ -2,9 +2,8 @@ use crate::prelude::*;
|
||||
use livekit_protocol as proto;
|
||||
use livekit_protocol::enum_dispatch;
|
||||
use livekit_protocol::observer::Dispatcher;
|
||||
use livekit_webrtc as rtc;
|
||||
use livekit_webrtc::prelude::*;
|
||||
use parking_lot::Mutex;
|
||||
use rtc::MediaType;
|
||||
use std::sync::atomic::{AtomicBool, AtomicU8, Ordering};
|
||||
use thiserror::Error;
|
||||
use tokio::sync::mpsc;
|
||||
@@ -103,8 +102,8 @@ macro_rules! track_dispatch {
|
||||
pub fn register_observer(self: &Self) -> mpsc::UnboundedReceiver<TrackEvent>;
|
||||
pub fn is_remote(self: &Self) -> bool;
|
||||
|
||||
pub(crate) fn transceiver(self: &Self) -> Option<rtc::rtp_transceiver::RtpTransceiver>;
|
||||
pub(crate) fn update_transceiver(self: &Self, transceiver: Option<rtc::rtp_transceiver::RtpTransceiver>) -> ();
|
||||
pub(crate) fn transceiver(self: &Self) -> Option<RtpTransceiver>;
|
||||
pub(crate) fn update_transceiver(self: &Self, transceiver: Option<RtpTransceiver>) -> ();
|
||||
pub(crate) fn update_info(self: &Self, info: proto::TrackInfo) -> ();
|
||||
);
|
||||
};
|
||||
@@ -114,7 +113,7 @@ impl Track {
|
||||
track_dispatch!([LocalAudio, LocalVideo, RemoteAudio, RemoteVideo]);
|
||||
|
||||
#[inline]
|
||||
pub fn rtc_track(&self) -> rtc::media_stream::MediaStreamTrack {
|
||||
pub fn rtc_track(&self) -> MediaStreamTrack {
|
||||
match self {
|
||||
Self::LocalAudio(track) => track.rtc_track().into(),
|
||||
Self::LocalVideo(track) => track.rtc_track().into(),
|
||||
@@ -128,7 +127,7 @@ impl LocalTrack {
|
||||
track_dispatch!([Audio, Video]);
|
||||
|
||||
#[inline]
|
||||
pub fn rtc_track(&self) -> rtc::media_stream::MediaStreamTrack {
|
||||
pub fn rtc_track(&self) -> MediaStreamTrack {
|
||||
match self {
|
||||
Self::Audio(track) => track.rtc_track().into(),
|
||||
Self::Video(track) => track.rtc_track().into(),
|
||||
@@ -140,7 +139,7 @@ impl RemoteTrack {
|
||||
track_dispatch!([Audio, Video]);
|
||||
|
||||
#[inline]
|
||||
pub fn rtc_track(&self) -> rtc::media_stream::MediaStreamTrack {
|
||||
pub fn rtc_track(&self) -> MediaStreamTrack {
|
||||
match self {
|
||||
Self::Audio(track) => track.rtc_track().into(),
|
||||
Self::Video(track) => track.rtc_track().into(),
|
||||
@@ -152,7 +151,7 @@ impl VideoTrack {
|
||||
track_dispatch!([Local, Remote]);
|
||||
|
||||
#[inline]
|
||||
pub fn rtc_track(&self) -> rtc::media_stream::RtcVideoTrack {
|
||||
pub fn rtc_track(&self) -> RtcVideoTrack {
|
||||
match self {
|
||||
Self::Local(track) => track.rtc_track(),
|
||||
Self::Remote(track) => track.rtc_track(),
|
||||
@@ -164,7 +163,7 @@ impl AudioTrack {
|
||||
track_dispatch!([Local, Remote]);
|
||||
|
||||
#[inline]
|
||||
pub fn rtc_track(&self) -> rtc::media_stream::RtcAudioTrack {
|
||||
pub fn rtc_track(&self) -> RtcAudioTrack {
|
||||
match self {
|
||||
Self::Local(track) => track.rtc_track().into(),
|
||||
Self::Remote(track) => track.rtc_track().into(),
|
||||
@@ -180,18 +179,13 @@ pub(crate) struct TrackInner {
|
||||
pub source: AtomicU8, // TrackSource
|
||||
pub stream_state: AtomicU8, // StreamState
|
||||
pub muted: AtomicBool,
|
||||
pub rtc_track: rtc::media_stream::MediaStreamTrack,
|
||||
pub transceiver: Mutex<Option<rtc::rtp_transceiver::RtpTransceiver>>,
|
||||
pub rtc_track: MediaStreamTrack,
|
||||
pub transceiver: Mutex<Option<RtpTransceiver>>,
|
||||
pub dispatcher: Dispatcher<TrackEvent>,
|
||||
}
|
||||
|
||||
impl TrackInner {
|
||||
pub fn new(
|
||||
sid: TrackSid,
|
||||
name: String,
|
||||
kind: TrackKind,
|
||||
rtc_track: rtc::media_stream::MediaStreamTrack,
|
||||
) -> Self {
|
||||
pub fn new(sid: TrackSid, name: String, kind: TrackKind, rtc_track: MediaStreamTrack) -> Self {
|
||||
Self {
|
||||
sid: Mutex::new(sid),
|
||||
name: Mutex::new(name),
|
||||
@@ -261,7 +255,7 @@ impl TrackInner {
|
||||
self.dispatcher.dispatch(&event);
|
||||
}
|
||||
|
||||
pub fn rtc_track(&self) -> rtc::media_stream::MediaStreamTrack {
|
||||
pub fn rtc_track(&self) -> MediaStreamTrack {
|
||||
self.rtc_track.clone()
|
||||
}
|
||||
|
||||
@@ -269,11 +263,11 @@ impl TrackInner {
|
||||
self.dispatcher.register()
|
||||
}
|
||||
|
||||
pub fn transceiver(&self) -> Option<rtc::rtp_transceiver::RtpTransceiver> {
|
||||
pub fn transceiver(&self) -> Option<RtpTransceiver> {
|
||||
self.transceiver.lock().clone()
|
||||
}
|
||||
|
||||
pub fn update_transceiver(&self, transceiver: Option<rtc::rtp_transceiver::RtpTransceiver>) {
|
||||
pub fn update_transceiver(&self, transceiver: Option<RtpTransceiver>) {
|
||||
*self.transceiver.lock() = transceiver;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use super::TrackInner;
|
||||
use crate::prelude::*;
|
||||
use livekit_protocol as proto;
|
||||
use livekit_webrtc as rtc;
|
||||
use livekit_webrtc::prelude::*;
|
||||
use std::fmt::Debug;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::mpsc;
|
||||
@@ -22,17 +22,13 @@ impl Debug for RemoteAudioTrack {
|
||||
}
|
||||
|
||||
impl RemoteAudioTrack {
|
||||
pub(crate) fn new(
|
||||
sid: TrackSid,
|
||||
name: String,
|
||||
rtc_track: rtc::media_stream::RtcAudioTrack,
|
||||
) -> Self {
|
||||
pub(crate) fn new(sid: TrackSid, name: String, rtc_track: RtcAudioTrack) -> Self {
|
||||
Self {
|
||||
inner: Arc::new(TrackInner::new(
|
||||
sid,
|
||||
name,
|
||||
TrackKind::Audio,
|
||||
rtc::media_stream::MediaStreamTrack::Audio(rtc_track),
|
||||
MediaStreamTrack::Audio(rtc_track),
|
||||
)),
|
||||
}
|
||||
}
|
||||
@@ -83,12 +79,11 @@ impl RemoteAudioTrack {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn rtc_track(&self) -> rtc::media_stream::RtcAudioTrack {
|
||||
if let rtc::media_stream::MediaStreamTrack::Audio(audio) = self.inner.rtc_track() {
|
||||
audio
|
||||
} else {
|
||||
unreachable!()
|
||||
pub fn rtc_track(&self) -> RtcAudioTrack {
|
||||
if let MediaStreamTrack::Audio(audio) = self.inner.rtc_track() {
|
||||
return audio;
|
||||
}
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -103,16 +98,13 @@ impl RemoteAudioTrack {
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[inline]
|
||||
pub(crate) fn transceiver(&self) -> Option<rtc::rtp_transceiver::RtpTransceiver> {
|
||||
pub(crate) fn transceiver(&self) -> Option<RtpTransceiver> {
|
||||
self.inner.transceiver()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn update_transceiver(
|
||||
&self,
|
||||
transceiver: Option<rtc::rtp_transceiver::RtpTransceiver>,
|
||||
) {
|
||||
pub(crate) fn update_transceiver(&self, transceiver: Option<RtpTransceiver>) {
|
||||
self.inner.update_transceiver(transceiver)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use super::TrackInner;
|
||||
use crate::prelude::*;
|
||||
use livekit_protocol as proto;
|
||||
use livekit_webrtc as rtc;
|
||||
use livekit_webrtc::prelude::*;
|
||||
use std::fmt::Debug;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::mpsc;
|
||||
@@ -22,17 +22,13 @@ impl Debug for RemoteVideoTrack {
|
||||
}
|
||||
|
||||
impl RemoteVideoTrack {
|
||||
pub(crate) fn new(
|
||||
sid: TrackSid,
|
||||
name: String,
|
||||
rtc_track: rtc::media_stream::RtcVideoTrack,
|
||||
) -> Self {
|
||||
pub(crate) fn new(sid: TrackSid, name: String, rtc_track: RtcVideoTrack) -> Self {
|
||||
Self {
|
||||
inner: Arc::new(TrackInner::new(
|
||||
sid,
|
||||
name,
|
||||
TrackKind::Video,
|
||||
rtc::media_stream::MediaStreamTrack::Video(rtc_track),
|
||||
MediaStreamTrack::Video(rtc_track),
|
||||
)),
|
||||
}
|
||||
}
|
||||
@@ -83,12 +79,11 @@ impl RemoteVideoTrack {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn rtc_track(&self) -> rtc::media_stream::RtcVideoTrack {
|
||||
if let rtc::media_stream::MediaStreamTrack::Video(video) = self.inner.rtc_track() {
|
||||
video
|
||||
} else {
|
||||
unreachable!()
|
||||
pub fn rtc_track(&self) -> RtcVideoTrack {
|
||||
if let MediaStreamTrack::Video(video) = self.inner.rtc_track() {
|
||||
return video;
|
||||
}
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -103,16 +98,13 @@ impl RemoteVideoTrack {
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[inline]
|
||||
pub(crate) fn transceiver(&self) -> Option<rtc::rtp_transceiver::RtpTransceiver> {
|
||||
pub(crate) fn transceiver(&self) -> Option<RtpTransceiver> {
|
||||
self.inner.transceiver()
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[inline]
|
||||
pub(crate) fn update_transceiver(
|
||||
&self,
|
||||
transceiver: Option<rtc::rtp_transceiver::RtpTransceiver>,
|
||||
) {
|
||||
pub(crate) fn update_transceiver(&self, transceiver: Option<RtpTransceiver>) {
|
||||
self.inner.update_transceiver(transceiver)
|
||||
}
|
||||
|
||||
|
||||
+6
-2
@@ -136,6 +136,9 @@ fn main() {
|
||||
"src/peer_connection.rs",
|
||||
"src/peer_connection_factory.rs",
|
||||
"src/media_stream.rs",
|
||||
"src/media_stream_track.rs",
|
||||
"src/audio_track.rs",
|
||||
"src/video_track.rs",
|
||||
"src/data_channel.rs",
|
||||
"src/jsep.rs",
|
||||
"src/candidate.rs",
|
||||
@@ -150,13 +153,15 @@ fn main() {
|
||||
"src/yuv_helper.rs",
|
||||
"src/helper.rs",
|
||||
"src/audio_resampler.rs",
|
||||
"src/logsink.rs",
|
||||
]);
|
||||
|
||||
builder.files(&[
|
||||
"src/peer_connection.cpp",
|
||||
"src/peer_connection_factory.cpp",
|
||||
"src/media_stream.cpp",
|
||||
"src/media_stream_track.cpp",
|
||||
"src/audio_track.cpp",
|
||||
"src/video_track.cpp",
|
||||
"src/data_channel.cpp",
|
||||
"src/jsep.cpp",
|
||||
"src/candidate.cpp",
|
||||
@@ -172,7 +177,6 @@ fn main() {
|
||||
"src/video_decoder_factory.cpp",
|
||||
"src/audio_device.cpp",
|
||||
"src/audio_resampler.cpp",
|
||||
"src/logsink.cpp",
|
||||
]);
|
||||
|
||||
for include in includes {
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Copyright 2023 LiveKit
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the “License”);
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an “AS IS” BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#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 "rust/cxx.h"
|
||||
|
||||
namespace livekit {
|
||||
class AudioTrack;
|
||||
class NativeAudioSink;
|
||||
class AudioTrackSource;
|
||||
} // namespace livekit
|
||||
#include "webrtc-sys/src/audio_track.rs.h"
|
||||
|
||||
namespace livekit {
|
||||
|
||||
class AudioTrack : public MediaStreamTrack {
|
||||
private:
|
||||
friend RtcRuntime;
|
||||
AudioTrack(std::shared_ptr<RtcRuntime> rtc_runtime,
|
||||
rtc::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);
|
||||
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_;
|
||||
};
|
||||
|
||||
std::shared_ptr<NativeAudioSink> new_native_audio_sink(
|
||||
rust::Box<AudioSinkWrapper> observer);
|
||||
|
||||
class AudioTrackSource {
|
||||
class InternalSource : public webrtc::LocalAudioSource {
|
||||
public:
|
||||
InternalSource();
|
||||
|
||||
SourceState state() const override;
|
||||
bool remote() const override;
|
||||
|
||||
const cricket::AudioOptions options() const override;
|
||||
|
||||
void AddSink(webrtc::AudioTrackSinkInterface* sink) override;
|
||||
void RemoveSink(webrtc::AudioTrackSinkInterface* sink) override;
|
||||
|
||||
// AudioFrame should always contain 10 ms worth of data (see index.md of
|
||||
// acm)
|
||||
void on_captured_frame(rust::Slice<const int16_t> audio_data,
|
||||
int sample_rate,
|
||||
size_t number_of_channels,
|
||||
size_t number_of_frames);
|
||||
|
||||
private:
|
||||
webrtc::Mutex mutex_;
|
||||
std::vector<webrtc::AudioTrackSinkInterface*> sinks_;
|
||||
cricket::AudioOptions options_{};
|
||||
};
|
||||
|
||||
public:
|
||||
AudioTrackSource();
|
||||
|
||||
void on_captured_frame(rust::Slice<const int16_t> audio_data,
|
||||
int sample_rate,
|
||||
size_t number_of_channels,
|
||||
size_t number_of_frames) const;
|
||||
|
||||
rtc::scoped_refptr<InternalSource> get() const;
|
||||
|
||||
private:
|
||||
rtc::scoped_refptr<InternalSource> source_;
|
||||
};
|
||||
|
||||
std::shared_ptr<AudioTrackSource> new_audio_track_source();
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
} // namespace livekit
|
||||
@@ -17,27 +17,31 @@
|
||||
#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 {
|
||||
class DataChannel;
|
||||
using NativeDataChannelInit = webrtc::DataChannelInit;
|
||||
class NativeDataChannelObserver;
|
||||
} // namespace livekit
|
||||
#include "webrtc-sys/src/data_channel.rs.h"
|
||||
|
||||
namespace livekit {
|
||||
|
||||
class NativeDataChannelObserver;
|
||||
|
||||
webrtc::DataChannelInit to_native_data_channel_init(DataChannelInit init);
|
||||
|
||||
class DataChannel {
|
||||
public:
|
||||
explicit DataChannel(
|
||||
std::shared_ptr<RTCRuntime> rtc_runtime,
|
||||
std::shared_ptr<RtcRuntime> rtc_runtime,
|
||||
rtc::scoped_refptr<webrtc::DataChannelInterface> data_channel);
|
||||
|
||||
void register_observer(NativeDataChannelObserver* observer) const;
|
||||
void register_observer(rust::Box<DataChannelObserverWrapper> observer) const;
|
||||
void unregister_observer() const;
|
||||
bool send(const DataBuffer& buffer) const;
|
||||
rust::String label() const;
|
||||
@@ -45,22 +49,20 @@ class DataChannel {
|
||||
void close() const;
|
||||
|
||||
private:
|
||||
std::shared_ptr<RTCRuntime> rtc_runtime_;
|
||||
mutable webrtc::Mutex mutex_;
|
||||
std::shared_ptr<RtcRuntime> rtc_runtime_;
|
||||
rtc::scoped_refptr<webrtc::DataChannelInterface> data_channel_;
|
||||
mutable std::unique_ptr<NativeDataChannelObserver> observer_;
|
||||
};
|
||||
|
||||
std::unique_ptr<NativeDataChannelInit> create_data_channel_init(
|
||||
DataChannelInit init);
|
||||
|
||||
static std::shared_ptr<DataChannel> _shared_data_channel() {
|
||||
return nullptr; // Ignore
|
||||
}
|
||||
|
||||
class NativeDataChannelObserver : public webrtc::DataChannelObserver {
|
||||
public:
|
||||
explicit NativeDataChannelObserver(
|
||||
rust::Box<DataChannelObserverWrapper> observer,
|
||||
DataChannel* dc);
|
||||
NativeDataChannelObserver(rust::Box<DataChannelObserverWrapper> observer,
|
||||
const DataChannel* dc);
|
||||
|
||||
~NativeDataChannelObserver();
|
||||
|
||||
@@ -70,10 +72,7 @@ class NativeDataChannelObserver : public webrtc::DataChannelObserver {
|
||||
|
||||
private:
|
||||
rust::Box<DataChannelObserverWrapper> observer_;
|
||||
DataChannel* dc_;
|
||||
const DataChannel* dc_;
|
||||
};
|
||||
|
||||
std::shared_ptr<NativeDataChannelObserver> create_native_data_channel_observer(
|
||||
rust::Box<DataChannelObserverWrapper> observer,
|
||||
DataChannel* dc);
|
||||
} // namespace livekit
|
||||
|
||||
@@ -22,19 +22,19 @@
|
||||
#include "api/ref_counted_base.h"
|
||||
#include "api/set_local_description_observer_interface.h"
|
||||
#include "api/set_remote_description_observer_interface.h"
|
||||
#include "livekit/rtc_error.h"
|
||||
#include "rust/cxx.h"
|
||||
|
||||
namespace livekit {
|
||||
class IceCandidate;
|
||||
class SessionDescription;
|
||||
struct NativeCreateSdpObserverHandle;
|
||||
struct NativeSetLocalSdpObserverHandle;
|
||||
struct NativeSetRemoteSdpObserverHandle;
|
||||
}; // namespace livekit
|
||||
#include "webrtc-sys/src/jsep.rs.h"
|
||||
|
||||
namespace livekit {
|
||||
|
||||
class AsyncContext;
|
||||
|
||||
class IceCandidate {
|
||||
public:
|
||||
explicit IceCandidate(
|
||||
@@ -82,69 +82,51 @@ static std::unique_ptr<SessionDescription> _unique_session_description() {
|
||||
return nullptr; // Ignore
|
||||
}
|
||||
|
||||
// SetCreateSdpObserver
|
||||
|
||||
class NativeCreateSdpObserver
|
||||
: public webrtc::CreateSessionDescriptionObserver {
|
||||
public:
|
||||
explicit NativeCreateSdpObserver(
|
||||
rust::Box<CreateSdpObserverWrapper> observer);
|
||||
NativeCreateSdpObserver(
|
||||
rust::Box<AsyncContext> ctx,
|
||||
rust::Fn<void(rust::Box<AsyncContext> ctx,
|
||||
std::unique_ptr<SessionDescription>)> on_success,
|
||||
rust::Fn<void(rust::Box<AsyncContext> ctx, RtcError)> on_error);
|
||||
|
||||
void OnSuccess(webrtc::SessionDescriptionInterface* desc) override;
|
||||
void OnFailure(webrtc::RTCError error) override;
|
||||
|
||||
private:
|
||||
rust::Box<CreateSdpObserverWrapper> observer_;
|
||||
rust::Box<AsyncContext> ctx_;
|
||||
rust::Fn<void(rust::Box<AsyncContext>, std::unique_ptr<SessionDescription>)>
|
||||
on_success_;
|
||||
rust::Fn<void(rust::Box<AsyncContext>, RtcError)> on_error_;
|
||||
};
|
||||
|
||||
struct NativeCreateSdpObserverHandle {
|
||||
rtc::scoped_refptr<NativeCreateSdpObserver> observer;
|
||||
};
|
||||
|
||||
std::unique_ptr<NativeCreateSdpObserverHandle>
|
||||
create_native_create_sdp_observer(rust::Box<CreateSdpObserverWrapper> observer);
|
||||
|
||||
// SetLocalSdpObserver
|
||||
|
||||
class NativeSetLocalSdpObserver
|
||||
: public webrtc::SetLocalDescriptionObserverInterface {
|
||||
public:
|
||||
explicit NativeSetLocalSdpObserver(
|
||||
rust::Box<SetLocalSdpObserverWrapper> observer);
|
||||
NativeSetLocalSdpObserver(
|
||||
rust::Box<AsyncContext> ctx,
|
||||
rust::Fn<void(rust::Box<AsyncContext>, RtcError)> on_complete);
|
||||
|
||||
void OnSetLocalDescriptionComplete(webrtc::RTCError error) override;
|
||||
|
||||
private:
|
||||
rust::Box<SetLocalSdpObserverWrapper> observer_;
|
||||
rust::Box<AsyncContext> ctx_;
|
||||
rust::Fn<void(rust::Box<AsyncContext>, RtcError)> on_complete_;
|
||||
};
|
||||
|
||||
struct NativeSetLocalSdpObserverHandle {
|
||||
rtc::scoped_refptr<NativeSetLocalSdpObserver> observer;
|
||||
};
|
||||
|
||||
std::unique_ptr<NativeSetLocalSdpObserverHandle>
|
||||
create_native_set_local_sdp_observer(
|
||||
rust::Box<SetLocalSdpObserverWrapper> observer);
|
||||
|
||||
// SetRemoteSdpObserver
|
||||
|
||||
class NativeSetRemoteSdpObserver
|
||||
: public webrtc::SetRemoteDescriptionObserverInterface {
|
||||
public:
|
||||
explicit NativeSetRemoteSdpObserver(
|
||||
rust::Box<SetRemoteSdpObserverWrapper> observer);
|
||||
NativeSetRemoteSdpObserver(
|
||||
rust::Box<AsyncContext> ctx,
|
||||
rust::Fn<void(rust::Box<AsyncContext>, RtcError)> on_complete);
|
||||
|
||||
void OnSetRemoteDescriptionComplete(webrtc::RTCError error) override;
|
||||
|
||||
private:
|
||||
rust::Box<SetRemoteSdpObserverWrapper> observer_;
|
||||
rust::Box<AsyncContext> ctx_;
|
||||
rust::Fn<void(rust::Box<AsyncContext>, RtcError)> on_complete_;
|
||||
};
|
||||
|
||||
struct NativeSetRemoteSdpObserverHandle {
|
||||
rtc::scoped_refptr<NativeSetRemoteSdpObserver> observer;
|
||||
};
|
||||
|
||||
std::unique_ptr<NativeSetRemoteSdpObserverHandle>
|
||||
create_native_set_remote_sdp_observer(
|
||||
rust::Box<SetRemoteSdpObserverWrapper> observer);
|
||||
} // namespace livekit
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
* Copyright 2023 LiveKit
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the “License”);
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an “AS IS” BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "livekit/webrtc.h"
|
||||
#include "rtc_base/logging.h"
|
||||
#include "rust/cxx.h"
|
||||
|
||||
namespace livekit {
|
||||
class LogSink;
|
||||
} // namespace livekit
|
||||
#include "webrtc-sys/src/logsink.rs.h"
|
||||
|
||||
|
||||
namespace livekit {
|
||||
|
||||
class LogSink : public rtc::LogSink {
|
||||
public:
|
||||
LogSink(rust::Fn<void(rust::String message, LoggingSeverity severity)> fnc);
|
||||
~LogSink();
|
||||
|
||||
void OnLogMessage(const std::string& message, rtc::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);
|
||||
|
||||
} // namespace livekit
|
||||
|
||||
@@ -19,27 +19,12 @@
|
||||
#include <memory>
|
||||
|
||||
#include "api/media_stream_interface.h"
|
||||
#include "api/video/video_frame.h"
|
||||
#include "common_audio/resampler/include/push_resampler.h"
|
||||
#include "common_audio/ring_buffer.h"
|
||||
#include "livekit/helper.h"
|
||||
#include "livekit/video_frame.h"
|
||||
#include "media/base/adapted_video_track_source.h"
|
||||
#include "pc/local_audio_source.h"
|
||||
#include "rtc_base/synchronization/mutex.h"
|
||||
#include "rtc_base/timestamp_aligner.h"
|
||||
#include "livekit/webrtc.h"
|
||||
#include "rust/cxx.h"
|
||||
#include "system_wrappers/include/clock.h"
|
||||
|
||||
namespace livekit {
|
||||
class MediaStream;
|
||||
class MediaStreamTrack;
|
||||
class VideoTrack;
|
||||
class AudioTrack;
|
||||
class NativeVideoFrameSink;
|
||||
class NativeAudioSink;
|
||||
class AudioTrackSource;
|
||||
class AdaptedVideoTrackSource;
|
||||
} // namespace livekit
|
||||
#include "webrtc-sys/src/media_stream.rs.h"
|
||||
|
||||
@@ -47,7 +32,8 @@ namespace livekit {
|
||||
|
||||
class MediaStream {
|
||||
public:
|
||||
explicit MediaStream(rtc::scoped_refptr<webrtc::MediaStreamInterface> stream);
|
||||
MediaStream(std::shared_ptr<RtcRuntime> rtc_runtime,
|
||||
rtc::scoped_refptr<webrtc::MediaStreamInterface> stream);
|
||||
|
||||
rust::String id() const;
|
||||
rust::Vec<VideoTrackPtr> get_video_tracks() const;
|
||||
@@ -60,204 +46,10 @@ class MediaStream {
|
||||
bool remove_track(std::shared_ptr<MediaStreamTrack> track) const;
|
||||
|
||||
private:
|
||||
std::shared_ptr<RtcRuntime> rtc_runtime_;
|
||||
rtc::scoped_refptr<webrtc::MediaStreamInterface> media_stream_;
|
||||
};
|
||||
|
||||
class MediaStreamTrack {
|
||||
protected:
|
||||
explicit MediaStreamTrack(
|
||||
rtc::scoped_refptr<webrtc::MediaStreamTrackInterface> track);
|
||||
|
||||
public:
|
||||
static std::shared_ptr<MediaStreamTrack> from(
|
||||
rtc::scoped_refptr<webrtc::MediaStreamTrackInterface> track);
|
||||
|
||||
rust::String kind() const;
|
||||
rust::String id() const;
|
||||
|
||||
bool enabled() const;
|
||||
bool set_enabled(bool enable) const;
|
||||
|
||||
TrackState state() const;
|
||||
|
||||
rtc::scoped_refptr<webrtc::MediaStreamTrackInterface> get() const {
|
||||
return track_;
|
||||
}
|
||||
|
||||
protected:
|
||||
rtc::scoped_refptr<webrtc::MediaStreamTrackInterface> track_;
|
||||
};
|
||||
|
||||
class AudioTrack : public MediaStreamTrack {
|
||||
public:
|
||||
explicit AudioTrack(rtc::scoped_refptr<webrtc::AudioTrackInterface> track);
|
||||
|
||||
void add_sink(NativeAudioSink& sink) const;
|
||||
void remove_sink(NativeAudioSink& sink) const;
|
||||
|
||||
private:
|
||||
webrtc::AudioTrackInterface* track() const {
|
||||
return static_cast<webrtc::AudioTrackInterface*>(track_.get());
|
||||
}
|
||||
};
|
||||
|
||||
class NativeAudioSink : public webrtc::AudioTrackSinkInterface {
|
||||
public:
|
||||
explicit NativeAudioSink(rust::Box<AudioSinkWrapper> observer);
|
||||
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_;
|
||||
};
|
||||
|
||||
std::unique_ptr<NativeAudioSink> new_native_audio_sink(
|
||||
rust::Box<AudioSinkWrapper> observer);
|
||||
|
||||
class NativeAudioTrackSource : public webrtc::LocalAudioSource {
|
||||
public:
|
||||
NativeAudioTrackSource();
|
||||
|
||||
SourceState state() const override;
|
||||
bool remote() const override;
|
||||
|
||||
const cricket::AudioOptions options() const override;
|
||||
|
||||
void AddSink(webrtc::AudioTrackSinkInterface* sink) override;
|
||||
void RemoveSink(webrtc::AudioTrackSinkInterface* sink) override;
|
||||
|
||||
// AudioFrame should always contain 10 ms worth of data (see index.md of acm)
|
||||
void on_captured_frame(const int16_t* audio_data,
|
||||
int sample_rate,
|
||||
size_t number_of_channels,
|
||||
size_t number_of_frames);
|
||||
|
||||
private:
|
||||
webrtc::Mutex mutex_;
|
||||
std::vector<webrtc::AudioTrackSinkInterface*> sinks_;
|
||||
cricket::AudioOptions options_{};
|
||||
};
|
||||
|
||||
class AudioTrackSource {
|
||||
public:
|
||||
AudioTrackSource(rtc::scoped_refptr<NativeAudioTrackSource> source);
|
||||
|
||||
void on_captured_frame(const int16_t* audio_data,
|
||||
int sample_rate,
|
||||
size_t number_of_channels,
|
||||
size_t number_of_frames) const;
|
||||
|
||||
rtc::scoped_refptr<NativeAudioTrackSource> get() const;
|
||||
|
||||
private:
|
||||
rtc::scoped_refptr<NativeAudioTrackSource> source_;
|
||||
};
|
||||
|
||||
std::shared_ptr<AudioTrackSource> new_audio_track_source();
|
||||
|
||||
class VideoTrack : public MediaStreamTrack {
|
||||
public:
|
||||
explicit VideoTrack(rtc::scoped_refptr<webrtc::VideoTrackInterface> track);
|
||||
|
||||
void add_sink(NativeVideoFrameSink& sink) const;
|
||||
void remove_sink(NativeVideoFrameSink& 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());
|
||||
}
|
||||
};
|
||||
|
||||
class NativeVideoFrameSink
|
||||
: public rtc::VideoSinkInterface<webrtc::VideoFrame> {
|
||||
public:
|
||||
explicit NativeVideoFrameSink(rust::Box<VideoFrameSinkWrapper> observer);
|
||||
|
||||
void OnFrame(const webrtc::VideoFrame& frame) override;
|
||||
void OnDiscardedFrame() override;
|
||||
void OnConstraintsChanged(
|
||||
const webrtc::VideoTrackSourceConstraints& constraints) override;
|
||||
|
||||
private:
|
||||
rust::Box<VideoFrameSinkWrapper> observer_;
|
||||
};
|
||||
|
||||
std::unique_ptr<NativeVideoFrameSink> new_native_video_frame_sink(
|
||||
rust::Box<VideoFrameSinkWrapper> observer);
|
||||
|
||||
// Native impl of the WebRTC interface
|
||||
class NativeVideoTrackSource : public rtc::AdaptedVideoTrackSource {
|
||||
public:
|
||||
NativeVideoTrackSource();
|
||||
~NativeVideoTrackSource() override;
|
||||
|
||||
bool is_screencast() const override;
|
||||
absl::optional<bool> needs_denoising() const override;
|
||||
SourceState state() const override;
|
||||
bool remote() const override;
|
||||
|
||||
bool on_captured_frame(const webrtc::VideoFrame& frame);
|
||||
|
||||
private:
|
||||
webrtc::Mutex mutex_;
|
||||
rtc::TimestampAligner timestamp_aligner_;
|
||||
};
|
||||
|
||||
class AdaptedVideoTrackSource {
|
||||
public:
|
||||
AdaptedVideoTrackSource(rtc::scoped_refptr<NativeVideoTrackSource> source);
|
||||
|
||||
bool on_captured_frame(const std::unique_ptr<VideoFrame>& frame)
|
||||
const; // frames pushed from Rust (+interior mutability)
|
||||
|
||||
rtc::scoped_refptr<NativeVideoTrackSource> get() const;
|
||||
|
||||
private:
|
||||
rtc::scoped_refptr<NativeVideoTrackSource> source_;
|
||||
};
|
||||
|
||||
std::shared_ptr<AdaptedVideoTrackSource> new_adapted_video_track_source();
|
||||
|
||||
static std::shared_ptr<MediaStreamTrack> video_to_media(
|
||||
std::shared_ptr<VideoTrack> track) {
|
||||
return track;
|
||||
}
|
||||
|
||||
static std::shared_ptr<MediaStreamTrack> audio_to_media(
|
||||
std::shared_ptr<AudioTrack> 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<AudioTrack> media_to_audio(
|
||||
std::shared_ptr<MediaStreamTrack> track) {
|
||||
return std::static_pointer_cast<AudioTrack>(track);
|
||||
}
|
||||
|
||||
static std::shared_ptr<MediaStreamTrack> _shared_media_stream_track() {
|
||||
return nullptr; // Ignore
|
||||
}
|
||||
|
||||
static std::shared_ptr<AudioTrack> _shared_audio_track() {
|
||||
return nullptr; // Ignore
|
||||
}
|
||||
|
||||
static std::shared_ptr<VideoTrack> _shared_video_track() {
|
||||
return nullptr; // Ignore
|
||||
}
|
||||
|
||||
static std::shared_ptr<MediaStream> _shared_media_stream() {
|
||||
return nullptr; // Ignore
|
||||
}
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2023 LiveKit
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the “License”);
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an “AS IS” BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "api/media_stream_interface.h"
|
||||
#include "livekit/helper.h"
|
||||
#include "livekit/webrtc.h"
|
||||
#include "rust/cxx.h"
|
||||
|
||||
namespace livekit {
|
||||
class MediaStreamTrack;
|
||||
} // namespace livekit
|
||||
#include "webrtc-sys/src/media_stream_track.rs.h"
|
||||
|
||||
namespace livekit {
|
||||
|
||||
class MediaStreamTrack {
|
||||
protected:
|
||||
MediaStreamTrack(std::shared_ptr<RtcRuntime>,
|
||||
rtc::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;
|
||||
|
||||
rtc::scoped_refptr<webrtc::MediaStreamTrackInterface> rtc_track() const {
|
||||
return track_;
|
||||
}
|
||||
|
||||
protected:
|
||||
std::shared_ptr<RtcRuntime> rtc_runtime_;
|
||||
rtc::scoped_refptr<webrtc::MediaStreamTrackInterface> track_;
|
||||
};
|
||||
|
||||
static std::shared_ptr<MediaStreamTrack> _shared_media_stream_track() {
|
||||
return nullptr; // Ignore
|
||||
}
|
||||
|
||||
} // namespace livekit
|
||||
@@ -23,46 +23,62 @@
|
||||
#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 {
|
||||
class NativeAddIceCandidateObserver;
|
||||
class PeerConnection;
|
||||
class NativeAddIceCandidateObserver;
|
||||
class NativePeerConnectionObserver;
|
||||
} // namespace livekit
|
||||
#include "webrtc-sys/src/peer_connection.rs.h"
|
||||
|
||||
namespace livekit {
|
||||
|
||||
class PeerConnectionFactory;
|
||||
|
||||
class PeerConnection {
|
||||
public:
|
||||
explicit PeerConnection(
|
||||
std::shared_ptr<RTCRuntime> rtc_runtime,
|
||||
PeerConnection(
|
||||
std::shared_ptr<RtcRuntime> rtc_runtime,
|
||||
std::unique_ptr<NativePeerConnectionObserver> observer,
|
||||
rtc::scoped_refptr<webrtc::PeerConnectionInterface> peer_connection);
|
||||
|
||||
void create_offer(NativeCreateSdpObserverHandle& observer,
|
||||
RTCOfferAnswerOptions options) const;
|
||||
void create_offer(
|
||||
RtcOfferAnswerOptions options,
|
||||
rust::Box<AsyncContext> ctx,
|
||||
rust::Fn<void(rust::Box<AsyncContext>,
|
||||
std::unique_ptr<SessionDescription>)> on_success,
|
||||
rust::Fn<void(rust::Box<AsyncContext>, RtcError)> on_error) const;
|
||||
|
||||
void create_answer(NativeCreateSdpObserverHandle& observer,
|
||||
RTCOfferAnswerOptions options) const;
|
||||
void create_answer(
|
||||
RtcOfferAnswerOptions options,
|
||||
rust::Box<AsyncContext> ctx,
|
||||
rust::Fn<void(rust::Box<AsyncContext>,
|
||||
std::unique_ptr<SessionDescription>)> on_success,
|
||||
rust::Fn<void(rust::Box<AsyncContext>, RtcError)> on_error) const;
|
||||
|
||||
void set_local_description(std::unique_ptr<SessionDescription> desc,
|
||||
NativeSetLocalSdpObserverHandle& observer) const;
|
||||
void set_local_description(
|
||||
std::unique_ptr<SessionDescription> desc,
|
||||
rust::Box<AsyncContext> ctx,
|
||||
rust::Fn<void(rust::Box<AsyncContext>, RtcError)> on_complete) const;
|
||||
|
||||
void set_remote_description(std::unique_ptr<SessionDescription> desc,
|
||||
NativeSetRemoteSdpObserverHandle& observer) const;
|
||||
void set_remote_description(
|
||||
std::unique_ptr<SessionDescription> desc,
|
||||
rust::Box<AsyncContext> ctx,
|
||||
rust::Fn<void(rust::Box<AsyncContext>, RtcError)> on_complete) const;
|
||||
|
||||
std::shared_ptr<DataChannel> create_data_channel(
|
||||
rust::String label,
|
||||
std::unique_ptr<NativeDataChannelInit> init) const;
|
||||
std::shared_ptr<DataChannel> create_data_channel(rust::String label,
|
||||
DataChannelInit init) const;
|
||||
|
||||
void add_ice_candidate(std::shared_ptr<IceCandidate> candidate,
|
||||
NativeAddIceCandidateObserver& observer) const;
|
||||
void add_ice_candidate(
|
||||
std::shared_ptr<IceCandidate> candidate,
|
||||
rust::Box<AsyncContext> ctx,
|
||||
rust::Fn<void(rust::Box<AsyncContext>, RtcError)> on_complete) const;
|
||||
|
||||
std::shared_ptr<RtpSender> add_track(
|
||||
std::shared_ptr<MediaStreamTrack> track,
|
||||
@@ -107,7 +123,8 @@ class PeerConnection {
|
||||
void close() const;
|
||||
|
||||
private:
|
||||
std::shared_ptr<RTCRuntime> rtc_runtime_;
|
||||
std::shared_ptr<RtcRuntime> rtc_runtime_;
|
||||
std::unique_ptr<NativePeerConnectionObserver> observer_;
|
||||
rtc::scoped_refptr<webrtc::PeerConnectionInterface> peer_connection_;
|
||||
};
|
||||
|
||||
@@ -115,25 +132,9 @@ static std::shared_ptr<PeerConnection> _shared_peer_connection() {
|
||||
return nullptr; // Ignore
|
||||
}
|
||||
|
||||
class NativeAddIceCandidateObserver {
|
||||
public:
|
||||
explicit NativeAddIceCandidateObserver(
|
||||
rust::Box<AddIceCandidateObserverWrapper> observer);
|
||||
|
||||
void OnComplete(const RTCError& error);
|
||||
|
||||
private:
|
||||
rust::Box<AddIceCandidateObserverWrapper> observer_;
|
||||
};
|
||||
|
||||
std::unique_ptr<NativeAddIceCandidateObserver>
|
||||
create_native_add_ice_candidate_observer(
|
||||
rust::Box<AddIceCandidateObserverWrapper> observer);
|
||||
|
||||
class NativePeerConnectionObserver : public webrtc::PeerConnectionObserver {
|
||||
public:
|
||||
explicit NativePeerConnectionObserver(
|
||||
std::shared_ptr<RTCRuntime> rtc_runtime,
|
||||
NativePeerConnectionObserver(
|
||||
rust::Box<PeerConnectionObserverWrapper> observer);
|
||||
|
||||
~NativePeerConnectionObserver();
|
||||
@@ -196,12 +197,15 @@ class NativePeerConnectionObserver : public webrtc::PeerConnectionObserver {
|
||||
void OnInterestingUsage(int usage_pattern) override;
|
||||
|
||||
private:
|
||||
std::shared_ptr<RTCRuntime> rtc_runtime_;
|
||||
friend PeerConnectionFactory;
|
||||
// The RtcRuntime is set inside PeerConnectionFactory, we can simplify that
|
||||
// once create_native_connection_observer is removed
|
||||
std::shared_ptr<RtcRuntime> rtc_runtime_;
|
||||
rust::Box<PeerConnectionObserverWrapper> observer_;
|
||||
};
|
||||
|
||||
std::shared_ptr<NativePeerConnectionObserver>
|
||||
std::unique_ptr<NativePeerConnectionObserver>
|
||||
create_native_peer_connection_observer(
|
||||
std::shared_ptr<RTCRuntime> rtc_runtime,
|
||||
rust::Box<PeerConnectionObserverWrapper> observer);
|
||||
|
||||
} // namespace livekit
|
||||
|
||||
@@ -21,30 +21,31 @@
|
||||
#include "peer_connection.h"
|
||||
#include "rtp_parameters.h"
|
||||
#include "rust/cxx.h"
|
||||
#include "webrtc-sys/src/peer_connection.rs.h"
|
||||
#include "webrtc.h"
|
||||
|
||||
namespace livekit {
|
||||
using NativeRTCConfiguration =
|
||||
webrtc::PeerConnectionInterface::RTCConfiguration;
|
||||
|
||||
class PeerConnectionFactory;
|
||||
} // namespace livekit
|
||||
#include "webrtc-sys/src/peer_connection_factory.rs.h"
|
||||
|
||||
namespace livekit {
|
||||
|
||||
webrtc::PeerConnectionInterface::RTCConfiguration to_native_rtc_configuration(
|
||||
RtcConfiguration config);
|
||||
|
||||
class PeerConnectionFactory {
|
||||
public:
|
||||
explicit PeerConnectionFactory(std::shared_ptr<RTCRuntime> rtc_runtime);
|
||||
explicit PeerConnectionFactory(std::shared_ptr<RtcRuntime> rtc_runtime);
|
||||
~PeerConnectionFactory();
|
||||
|
||||
std::shared_ptr<PeerConnection> create_peer_connection(
|
||||
std::unique_ptr<NativeRTCConfiguration> config,
|
||||
NativePeerConnectionObserver* observer) const;
|
||||
RtcConfiguration config,
|
||||
std::unique_ptr<NativePeerConnectionObserver> observer) const;
|
||||
|
||||
std::shared_ptr<VideoTrack> create_video_track(
|
||||
rust::String label,
|
||||
std::shared_ptr<AdaptedVideoTrackSource> source) const;
|
||||
std::shared_ptr<VideoTrackSource> source) const;
|
||||
|
||||
std::shared_ptr<AudioTrack> create_audio_track(
|
||||
rust::String label,
|
||||
@@ -55,12 +56,9 @@ class PeerConnectionFactory {
|
||||
RtpCapabilities get_rtp_receiver_capabilities(MediaType type) const;
|
||||
|
||||
private:
|
||||
std::shared_ptr<RTCRuntime> rtc_runtime_;
|
||||
std::shared_ptr<RtcRuntime> rtc_runtime_;
|
||||
rtc::scoped_refptr<webrtc::PeerConnectionFactoryInterface> peer_factory_;
|
||||
};
|
||||
|
||||
std::shared_ptr<PeerConnectionFactory> create_peer_connection_factory(
|
||||
std::shared_ptr<RTCRuntime> rtc_runtime);
|
||||
std::unique_ptr<NativeRTCConfiguration> create_rtc_configuration(
|
||||
RTCConfiguration conf);
|
||||
std::shared_ptr<PeerConnectionFactory> create_peer_connection_factory();
|
||||
} // namespace livekit
|
||||
|
||||
@@ -22,9 +22,9 @@
|
||||
|
||||
namespace livekit {
|
||||
|
||||
RTCError to_error(const webrtc::RTCError& error);
|
||||
RtcError to_error(const webrtc::RTCError& error);
|
||||
std::string serialize_error(
|
||||
const RTCError& error); // to be used inside cxx::Exception msg
|
||||
const RtcError& error); // to be used inside cxx::Exception msg
|
||||
|
||||
#ifdef LIVEKIT_TEST
|
||||
rust::String serialize_deserialize();
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "livekit/helper.h"
|
||||
#include "livekit/media_stream.h"
|
||||
#include "livekit/rtp_parameters.h"
|
||||
#include "livekit/webrtc.h"
|
||||
#include "rust/cxx.h"
|
||||
|
||||
namespace livekit {
|
||||
@@ -35,8 +36,8 @@ namespace livekit {
|
||||
// TODO(theomonnom): FrameTransformer & FrameDecryptor interface
|
||||
class RtpReceiver {
|
||||
public:
|
||||
explicit RtpReceiver(
|
||||
rtc::scoped_refptr<webrtc::RtpReceiverInterface> receiver);
|
||||
RtpReceiver(std::shared_ptr<RtcRuntime> rtc_runtime,
|
||||
rtc::scoped_refptr<webrtc::RtpReceiverInterface> receiver);
|
||||
|
||||
std::shared_ptr<MediaStreamTrack> track() const;
|
||||
|
||||
@@ -53,7 +54,12 @@ class RtpReceiver {
|
||||
void set_jitter_buffer_minimum_delay(bool is_some,
|
||||
double delay_seconds) const;
|
||||
|
||||
rtc::scoped_refptr<webrtc::RtpReceiverInterface> rtc_receiver() const {
|
||||
return receiver_;
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<RtcRuntime> rtc_runtime_;
|
||||
rtc::scoped_refptr<webrtc::RtpReceiverInterface> receiver_;
|
||||
};
|
||||
|
||||
|
||||
@@ -34,7 +34,8 @@ namespace livekit {
|
||||
// TODO(theomonnom): FrameTransformer & FrameEncryptor interface
|
||||
class RtpSender {
|
||||
public:
|
||||
explicit RtpSender(rtc::scoped_refptr<webrtc::RtpSenderInterface> sender);
|
||||
RtpSender(std::shared_ptr<RtcRuntime> rtc_runtime,
|
||||
rtc::scoped_refptr<webrtc::RtpSenderInterface> sender);
|
||||
|
||||
bool set_track(std::shared_ptr<MediaStreamTrack> track) const;
|
||||
|
||||
@@ -56,9 +57,12 @@ class RtpSender {
|
||||
|
||||
void set_parameters(RtpParameters params) const;
|
||||
|
||||
rtc::scoped_refptr<webrtc::RtpSenderInterface> get() const { return sender_; }
|
||||
rtc::scoped_refptr<webrtc::RtpSenderInterface> rtc_sender() const {
|
||||
return sender_;
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<RtcRuntime> rtc_runtime_;
|
||||
rtc::scoped_refptr<webrtc::RtpSenderInterface> sender_;
|
||||
};
|
||||
|
||||
|
||||
@@ -39,7 +39,8 @@ webrtc::RtpTransceiverInit to_native_rtp_transceiver_init(
|
||||
|
||||
class RtpTransceiver {
|
||||
public:
|
||||
explicit RtpTransceiver(
|
||||
RtpTransceiver(
|
||||
std::shared_ptr<RtcRuntime> rtc_runtime,
|
||||
rtc::scoped_refptr<webrtc::RtpTransceiverInterface> transceiver);
|
||||
|
||||
MediaType media_type() const;
|
||||
@@ -76,6 +77,7 @@ class RtpTransceiver {
|
||||
rust::Vec<RtpHeaderExtensionCapability> header_extensions_to_offer) const;
|
||||
|
||||
private:
|
||||
std::shared_ptr<RtcRuntime> rtc_runtime_;
|
||||
rtc::scoped_refptr<webrtc::RtpTransceiverInterface> transceiver_;
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* Copyright 2023 LiveKit
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the “License”);
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an “AS IS” BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <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 {
|
||||
class VideoTrack;
|
||||
class NativeVideoSink;
|
||||
class VideoTrackSource;
|
||||
} // namespace livekit
|
||||
#include "webrtc-sys/src/video_track.rs.h"
|
||||
|
||||
namespace livekit {
|
||||
|
||||
class VideoTrack : public MediaStreamTrack {
|
||||
private:
|
||||
friend RtcRuntime;
|
||||
VideoTrack(std::shared_ptr<RtcRuntime> rtc_runtime,
|
||||
rtc::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 rtc::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 rtc::AdaptedVideoTrackSource {
|
||||
public:
|
||||
InternalSource();
|
||||
~InternalSource() override;
|
||||
|
||||
bool is_screencast() const override;
|
||||
absl::optional<bool> needs_denoising() const override;
|
||||
SourceState state() const override;
|
||||
bool remote() const override;
|
||||
|
||||
bool on_captured_frame(const webrtc::VideoFrame& frame);
|
||||
|
||||
private:
|
||||
webrtc::Mutex mutex_;
|
||||
rtc::TimestampAligner timestamp_aligner_;
|
||||
};
|
||||
|
||||
public:
|
||||
VideoTrackSource();
|
||||
|
||||
bool on_captured_frame(const std::unique_ptr<VideoFrame>& frame)
|
||||
const; // frames pushed from Rust (+interior mutability)
|
||||
|
||||
rtc::scoped_refptr<InternalSource> get() const;
|
||||
|
||||
private:
|
||||
rtc::scoped_refptr<InternalSource> source_;
|
||||
};
|
||||
|
||||
std::shared_ptr<VideoTrackSource> new_video_track_source();
|
||||
|
||||
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
|
||||
@@ -16,6 +16,13 @@
|
||||
|
||||
#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 "rust/cxx.h"
|
||||
@@ -25,29 +32,60 @@
|
||||
#endif
|
||||
|
||||
namespace livekit {
|
||||
class RTCRuntime;
|
||||
}
|
||||
class RtcRuntime;
|
||||
class LogSink;
|
||||
} // namespace livekit
|
||||
#include "webrtc-sys/src/webrtc.rs.h"
|
||||
|
||||
namespace livekit {
|
||||
|
||||
class RTCRuntime {
|
||||
public:
|
||||
RTCRuntime();
|
||||
~RTCRuntime();
|
||||
class MediaStreamTrack;
|
||||
class RtpReceiver;
|
||||
class RtpSender;
|
||||
|
||||
RTCRuntime(const RTCRuntime&) = delete;
|
||||
RTCRuntime& operator=(const RTCRuntime&) = delete;
|
||||
// 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();
|
||||
|
||||
rtc::Thread* network_thread() const;
|
||||
rtc::Thread* worker_thread() const;
|
||||
rtc::Thread* signaling_thread() const;
|
||||
|
||||
std::shared_ptr<MediaStreamTrack> get_or_create_media_stream_track(
|
||||
rtc::scoped_refptr<webrtc::MediaStreamTrackInterface> track);
|
||||
|
||||
std::shared_ptr<AudioTrack> get_or_create_audio_track(
|
||||
rtc::scoped_refptr<webrtc::AudioTrackInterface> track);
|
||||
|
||||
std::shared_ptr<VideoTrack> get_or_create_video_track(
|
||||
rtc::scoped_refptr<webrtc::VideoTrackInterface> track);
|
||||
|
||||
private:
|
||||
RtcRuntime();
|
||||
|
||||
std::unique_ptr<rtc::Thread> network_thread_;
|
||||
std::unique_ptr<rtc::Thread> worker_thread_;
|
||||
std::unique_ptr<rtc::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::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)
|
||||
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
|
||||
rtc::WinsockInitializer winsock_;
|
||||
rtc::PhysicalSocketServer ss_;
|
||||
@@ -55,8 +93,22 @@ class RTCRuntime {
|
||||
#endif
|
||||
};
|
||||
|
||||
class LogSink : public rtc::LogSink {
|
||||
public:
|
||||
LogSink(rust::Fn<void(rust::String message, LoggingSeverity severity)> fnc);
|
||||
~LogSink();
|
||||
|
||||
void OnLogMessage(const std::string& message,
|
||||
rtc::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();
|
||||
|
||||
std::shared_ptr<RTCRuntime> create_rtc_runtime();
|
||||
|
||||
} // namespace livekit
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* Copyright 2023 LiveKit
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the “License”);
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an “AS IS” BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "livekit/audio_track.h"
|
||||
|
||||
#include <sys/_types/_int16_t.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
|
||||
#include "api/media_stream_interface.h"
|
||||
#include "audio/remix_resample.h"
|
||||
#include "common_audio/include/audio_util.h"
|
||||
#include "rtc_base/logging.h"
|
||||
#include "rtc_base/ref_counted_object.h"
|
||||
#include "rtc_base/synchronization/mutex.h"
|
||||
#include "rtc_base/time_utils.h"
|
||||
#include "rust/cxx.h"
|
||||
|
||||
namespace livekit {
|
||||
|
||||
AudioTrack::AudioTrack(std::shared_ptr<RtcRuntime> rtc_runtime,
|
||||
rtc::scoped_refptr<webrtc::AudioTrackInterface> track)
|
||||
: MediaStreamTrack(rtc_runtime, std::move(track)) {}
|
||||
|
||||
AudioTrack::~AudioTrack() {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
for (auto& sink : sinks_) {
|
||||
track()->RemoveSink(sink.get());
|
||||
}
|
||||
}
|
||||
|
||||
void AudioTrack::add_sink(const std::shared_ptr<NativeAudioSink>& sink) const {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
track()->AddSink(sink.get());
|
||||
sinks_.push_back(sink);
|
||||
}
|
||||
|
||||
void AudioTrack::remove_sink(
|
||||
const std::shared_ptr<NativeAudioSink>& sink) const {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
track()->RemoveSink(sink.get());
|
||||
sinks_.erase(std::remove(sinks_.begin(), sinks_.end(), sink), sinks_.end());
|
||||
}
|
||||
|
||||
NativeAudioSink::NativeAudioSink(rust::Box<AudioSinkWrapper> observer)
|
||||
: observer_(std::move(observer)) {}
|
||||
|
||||
void NativeAudioSink::OnData(const void* audio_data,
|
||||
int bits_per_sample,
|
||||
int sample_rate,
|
||||
size_t number_of_channels,
|
||||
size_t number_of_frames) {
|
||||
RTC_CHECK_EQ(16, bits_per_sample);
|
||||
rust::Slice<const int16_t> data(static_cast<const int16_t*>(audio_data),
|
||||
number_of_channels * number_of_frames);
|
||||
observer_->on_data(data, sample_rate, number_of_channels, number_of_frames);
|
||||
}
|
||||
|
||||
std::shared_ptr<NativeAudioSink> new_native_audio_sink(
|
||||
rust::Box<AudioSinkWrapper> observer) {
|
||||
return std::make_shared<NativeAudioSink>(std::move(observer));
|
||||
}
|
||||
|
||||
AudioTrackSource::InternalSource::InternalSource() {
|
||||
options_.echo_cancellation = false;
|
||||
options_.auto_gain_control = false;
|
||||
options_.noise_suppression = false;
|
||||
}
|
||||
|
||||
webrtc::MediaSourceInterface::SourceState
|
||||
AudioTrackSource::InternalSource::state() const {
|
||||
return webrtc::MediaSourceInterface::SourceState::kLive;
|
||||
}
|
||||
|
||||
bool AudioTrackSource::InternalSource::remote() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
const cricket::AudioOptions AudioTrackSource::InternalSource::options() const {
|
||||
return options_;
|
||||
}
|
||||
|
||||
void AudioTrackSource::InternalSource::AddSink(
|
||||
webrtc::AudioTrackSinkInterface* sink) {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
sinks_.push_back(sink);
|
||||
}
|
||||
|
||||
void AudioTrackSource::InternalSource::RemoveSink(
|
||||
webrtc::AudioTrackSinkInterface* sink) {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
sinks_.erase(std::remove(sinks_.begin(), sinks_.end(), sink), sinks_.end());
|
||||
}
|
||||
|
||||
void AudioTrackSource::InternalSource::on_captured_frame(
|
||||
rust::Slice<const int16_t> data,
|
||||
int sample_rate,
|
||||
size_t number_of_channels,
|
||||
size_t number_of_frames) {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
for (auto sink : sinks_) {
|
||||
sink->OnData(data.data(), 16, sample_rate, number_of_channels,
|
||||
number_of_frames);
|
||||
}
|
||||
}
|
||||
|
||||
AudioTrackSource::AudioTrackSource() {
|
||||
source_ = rtc::make_ref_counted<InternalSource>();
|
||||
}
|
||||
|
||||
void AudioTrackSource::on_captured_frame(rust::Slice<const int16_t> audio_data,
|
||||
int sample_rate,
|
||||
size_t number_of_channels,
|
||||
size_t number_of_frames) const {
|
||||
source_->on_captured_frame(audio_data, sample_rate, number_of_channels,
|
||||
number_of_frames);
|
||||
}
|
||||
|
||||
rtc::scoped_refptr<AudioTrackSource::InternalSource> AudioTrackSource::get()
|
||||
const {
|
||||
return source_;
|
||||
}
|
||||
|
||||
std::shared_ptr<AudioTrackSource> new_audio_track_source() {
|
||||
return std::make_shared<AudioTrackSource>();
|
||||
}
|
||||
|
||||
} // namespace livekit
|
||||
@@ -0,0 +1,71 @@
|
||||
use crate::impl_thread_safety;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[cxx::bridge(namespace = "livekit")]
|
||||
pub mod ffi {
|
||||
extern "C++" {
|
||||
include!("livekit/media_stream_track.h");
|
||||
|
||||
type MediaStreamTrack = crate::media_stream_track::ffi::MediaStreamTrack;
|
||||
}
|
||||
|
||||
unsafe extern "C++" {
|
||||
include!("livekit/audio_track.h");
|
||||
|
||||
type AudioTrack;
|
||||
type NativeAudioSink;
|
||||
type AudioTrackSource;
|
||||
|
||||
fn add_sink(self: &AudioTrack, sink: &SharedPtr<NativeAudioSink>);
|
||||
fn remove_sink(self: &AudioTrack, sink: &SharedPtr<NativeAudioSink>);
|
||||
fn new_native_audio_sink(observer: Box<AudioSinkWrapper>) -> SharedPtr<NativeAudioSink>;
|
||||
|
||||
fn on_captured_frame(
|
||||
self: &AudioTrackSource,
|
||||
data: &[i16],
|
||||
sample_rate: i32,
|
||||
nb_channels: usize,
|
||||
nb_frames: usize,
|
||||
);
|
||||
fn new_audio_track_source() -> SharedPtr<AudioTrackSource>;
|
||||
|
||||
fn audio_to_media(track: SharedPtr<AudioTrack>) -> SharedPtr<MediaStreamTrack>;
|
||||
unsafe fn media_to_audio(track: SharedPtr<MediaStreamTrack>) -> SharedPtr<AudioTrack>;
|
||||
fn _shared_audio_track() -> SharedPtr<AudioTrack>;
|
||||
}
|
||||
|
||||
extern "Rust" {
|
||||
type AudioSinkWrapper;
|
||||
|
||||
fn on_data(
|
||||
self: &AudioSinkWrapper,
|
||||
data: &[i16],
|
||||
sample_rate: i32,
|
||||
nb_channels: usize,
|
||||
nb_frames: usize,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl_thread_safety!(ffi::AudioTrack, Send + Sync);
|
||||
impl_thread_safety!(ffi::NativeAudioSink, Send + Sync);
|
||||
impl_thread_safety!(ffi::AudioTrackSource, Send + Sync);
|
||||
|
||||
pub trait AudioSink: Send {
|
||||
fn on_data(&self, data: &[i16], sample_rate: i32, nb_channels: usize, nb_frames: usize);
|
||||
}
|
||||
|
||||
pub struct AudioSinkWrapper {
|
||||
observer: Arc<dyn AudioSink>,
|
||||
}
|
||||
|
||||
impl AudioSinkWrapper {
|
||||
pub fn new(observer: Arc<dyn AudioSink>) -> Self {
|
||||
Self { observer }
|
||||
}
|
||||
|
||||
fn on_data(&self, data: &[i16], sample_rate: i32, nb_channels: usize, nb_frames: usize) {
|
||||
self.observer
|
||||
.on_data(data, sample_rate, nb_channels, nb_frames);
|
||||
}
|
||||
}
|
||||
@@ -18,22 +18,50 @@
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "rtc_base/synchronization/mutex.h"
|
||||
#include "webrtc-sys/src/data_channel.rs.h"
|
||||
|
||||
namespace livekit {
|
||||
|
||||
DataChannel::DataChannel(
|
||||
std::shared_ptr<RTCRuntime> rtc_runtime,
|
||||
rtc::scoped_refptr<webrtc::DataChannelInterface> data_channel)
|
||||
: rtc_runtime_(std::move(rtc_runtime)),
|
||||
data_channel_(std::move(data_channel)) {}
|
||||
webrtc::DataChannelInit to_native_data_channel_init(DataChannelInit init) {
|
||||
webrtc::DataChannelInit rtc_init{};
|
||||
rtc_init.id = init.id;
|
||||
rtc_init.negotiated = init.negotiated;
|
||||
rtc_init.ordered = init.ordered;
|
||||
rtc_init.protocol = init.protocol.c_str();
|
||||
|
||||
void DataChannel::register_observer(NativeDataChannelObserver* observer) const {
|
||||
data_channel_->RegisterObserver(observer);
|
||||
if (init.has_max_retransmit_time)
|
||||
rtc_init.maxRetransmitTime = init.max_retransmit_time;
|
||||
|
||||
if (init.has_max_retransmits)
|
||||
rtc_init.maxRetransmits = init.max_retransmits;
|
||||
|
||||
if (init.has_priority)
|
||||
rtc_init.priority = static_cast<webrtc::Priority>(init.priority);
|
||||
|
||||
return rtc_init;
|
||||
}
|
||||
|
||||
DataChannel::DataChannel(
|
||||
std::shared_ptr<RtcRuntime> rtc_runtime,
|
||||
rtc::scoped_refptr<webrtc::DataChannelInterface> data_channel)
|
||||
: rtc_runtime_(rtc_runtime), data_channel_(std::move(data_channel)) {}
|
||||
|
||||
void DataChannel::register_observer(
|
||||
rust::Box<DataChannelObserverWrapper> observer) const {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
|
||||
data_channel_->UnregisterObserver();
|
||||
|
||||
observer_ =
|
||||
std::make_unique<NativeDataChannelObserver>(std::move(observer), this);
|
||||
data_channel_->RegisterObserver(observer_.get());
|
||||
}
|
||||
|
||||
void DataChannel::unregister_observer() const {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
data_channel_->UnregisterObserver();
|
||||
observer_ = nullptr;
|
||||
}
|
||||
|
||||
bool DataChannel::send(const DataBuffer& buffer) const {
|
||||
@@ -53,29 +81,9 @@ void DataChannel::close() const {
|
||||
return data_channel_->Close();
|
||||
}
|
||||
|
||||
std::unique_ptr<NativeDataChannelInit> create_data_channel_init(
|
||||
DataChannelInit init) {
|
||||
auto rtc_init = std::make_unique<webrtc::DataChannelInit>();
|
||||
rtc_init->id = init.id;
|
||||
rtc_init->negotiated = init.negotiated;
|
||||
rtc_init->ordered = init.ordered;
|
||||
rtc_init->protocol = init.protocol.c_str();
|
||||
|
||||
if (init.has_max_retransmit_time)
|
||||
rtc_init->maxRetransmitTime = init.max_retransmit_time;
|
||||
|
||||
if (init.has_max_retransmits)
|
||||
rtc_init->maxRetransmits = init.max_retransmits;
|
||||
|
||||
if (init.has_priority)
|
||||
rtc_init->priority = static_cast<webrtc::Priority>(init.priority);
|
||||
|
||||
return rtc_init;
|
||||
}
|
||||
|
||||
NativeDataChannelObserver::NativeDataChannelObserver(
|
||||
rust::Box<DataChannelObserverWrapper> observer,
|
||||
DataChannel* dc)
|
||||
const DataChannel* dc)
|
||||
: observer_(std::move(observer)), dc_(dc) {}
|
||||
|
||||
NativeDataChannelObserver::~NativeDataChannelObserver() {
|
||||
@@ -99,9 +107,4 @@ void NativeDataChannelObserver::OnBufferedAmountChange(
|
||||
observer_->on_buffered_amount_change(sent_data_size);
|
||||
}
|
||||
|
||||
std::shared_ptr<NativeDataChannelObserver> create_native_data_channel_observer(
|
||||
rust::Box<DataChannelObserverWrapper> observer,
|
||||
DataChannel* dc) {
|
||||
return std::make_shared<NativeDataChannelObserver>(std::move(observer), dc);
|
||||
}
|
||||
} // namespace livekit
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use crate::impl_thread_safety;
|
||||
use std::slice;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[cxx::bridge(namespace = "livekit")]
|
||||
@@ -43,6 +42,22 @@ pub mod ffi {
|
||||
Closed,
|
||||
}
|
||||
|
||||
unsafe extern "C++" {
|
||||
include!("livekit/data_channel.h");
|
||||
|
||||
type DataChannel;
|
||||
|
||||
fn register_observer(self: &DataChannel, observer: Box<DataChannelObserverWrapper>);
|
||||
fn unregister_observer(self: &DataChannel);
|
||||
|
||||
fn send(self: &DataChannel, data: &DataBuffer) -> bool;
|
||||
fn label(self: &DataChannel) -> String;
|
||||
fn state(self: &DataChannel) -> DataState;
|
||||
fn close(self: &DataChannel);
|
||||
|
||||
fn _shared_data_channel() -> SharedPtr<DataChannel>; // Ignore
|
||||
}
|
||||
|
||||
extern "Rust" {
|
||||
type DataChannelObserverWrapper;
|
||||
|
||||
@@ -50,38 +65,9 @@ pub mod ffi {
|
||||
fn on_message(self: &DataChannelObserverWrapper, buffer: DataBuffer);
|
||||
fn on_buffered_amount_change(self: &DataChannelObserverWrapper, sent_data_size: u64);
|
||||
}
|
||||
|
||||
unsafe extern "C++" {
|
||||
include!("livekit/data_channel.h");
|
||||
|
||||
type DataChannel;
|
||||
type NativeDataChannelInit;
|
||||
type NativeDataChannelObserver;
|
||||
|
||||
/// SAFETY
|
||||
/// The observer must live as long as the datachannel uses it
|
||||
unsafe fn register_observer(self: &DataChannel, observer: *mut NativeDataChannelObserver);
|
||||
|
||||
fn unregister_observer(self: &DataChannel);
|
||||
fn send(self: &DataChannel, data: &DataBuffer) -> bool;
|
||||
fn label(self: &DataChannel) -> String;
|
||||
fn state(self: &DataChannel) -> DataState;
|
||||
fn close(self: &DataChannel);
|
||||
|
||||
fn create_data_channel_init(init: DataChannelInit) -> UniquePtr<NativeDataChannelInit>;
|
||||
unsafe fn create_native_data_channel_observer(
|
||||
observer: Box<DataChannelObserverWrapper>,
|
||||
dc: *mut DataChannel,
|
||||
) -> SharedPtr<NativeDataChannelObserver>;
|
||||
|
||||
fn _shared_data_channel() -> SharedPtr<DataChannel>; // Ignore
|
||||
}
|
||||
}
|
||||
|
||||
impl_thread_safety!(ffi::DataChannel, Send + Sync);
|
||||
impl_thread_safety!(ffi::NativeDataChannelObserver, Send + Sync);
|
||||
|
||||
// DataChannelObserver
|
||||
|
||||
pub trait DataChannelObserver: Send + Sync {
|
||||
fn on_state_change(&self, state: ffi::DataState);
|
||||
@@ -104,7 +90,7 @@ impl DataChannelObserverWrapper {
|
||||
|
||||
fn on_message(&self, buffer: ffi::DataBuffer) {
|
||||
unsafe {
|
||||
let data = slice::from_raw_parts(buffer.ptr, buffer.len);
|
||||
let data = std::slice::from_raw_parts(buffer.ptr, buffer.len);
|
||||
self.observer.on_message(data, buffer.binary);
|
||||
}
|
||||
}
|
||||
|
||||
+18
-43
@@ -21,6 +21,7 @@
|
||||
|
||||
#include "livekit/rtc_error.h"
|
||||
#include "rtc_base/ref_counted_object.h"
|
||||
#include "rust/cxx.h"
|
||||
|
||||
namespace livekit {
|
||||
|
||||
@@ -109,69 +110,43 @@ std::unique_ptr<SessionDescription> create_session_description(
|
||||
return std::make_unique<SessionDescription>(std::move(rtc_sdp));
|
||||
}
|
||||
|
||||
// CreateSdpObserver
|
||||
|
||||
NativeCreateSdpObserver::NativeCreateSdpObserver(
|
||||
rust::Box<CreateSdpObserverWrapper> observer)
|
||||
: observer_(std::move(observer)) {}
|
||||
rust::Box<AsyncContext> ctx,
|
||||
rust::Fn<void(rust::Box<AsyncContext>, std::unique_ptr<SessionDescription>)>
|
||||
on_success,
|
||||
rust::Fn<void(rust::Box<AsyncContext>, RtcError)> on_error)
|
||||
: ctx_(std::move(ctx)), on_success_(on_success), on_error_(on_error) {}
|
||||
|
||||
void NativeCreateSdpObserver::OnSuccess(
|
||||
webrtc::SessionDescriptionInterface* desc) {
|
||||
// We have ownership of desc
|
||||
observer_->on_success(std::make_unique<SessionDescription>(
|
||||
std::unique_ptr<webrtc::SessionDescriptionInterface>(desc)));
|
||||
on_success_(std::move(ctx_),
|
||||
std::make_unique<SessionDescription>(
|
||||
std::unique_ptr<webrtc::SessionDescriptionInterface>(desc)));
|
||||
}
|
||||
|
||||
void NativeCreateSdpObserver::OnFailure(webrtc::RTCError error) {
|
||||
observer_->on_failure(to_error(error));
|
||||
on_error_(std::move(ctx_), to_error(error));
|
||||
}
|
||||
|
||||
std::unique_ptr<NativeCreateSdpObserverHandle>
|
||||
create_native_create_sdp_observer(
|
||||
rust::Box<CreateSdpObserverWrapper> observer) {
|
||||
return std::make_unique<NativeCreateSdpObserverHandle>(
|
||||
NativeCreateSdpObserverHandle{
|
||||
rtc::make_ref_counted<NativeCreateSdpObserver>(std::move(observer))});
|
||||
}
|
||||
|
||||
// SetLocalSdpObserver
|
||||
|
||||
NativeSetLocalSdpObserver::NativeSetLocalSdpObserver(
|
||||
rust::Box<SetLocalSdpObserverWrapper> observer)
|
||||
: observer_(std::move(observer)) {}
|
||||
rust::Box<AsyncContext> ctx,
|
||||
rust::Fn<void(rust::Box<AsyncContext>, RtcError)> on_complete)
|
||||
: ctx_(std::move(ctx)), on_complete_(on_complete) {}
|
||||
|
||||
void NativeSetLocalSdpObserver::OnSetLocalDescriptionComplete(
|
||||
webrtc::RTCError error) {
|
||||
observer_->on_set_local_description_complete(to_error(error));
|
||||
on_complete_(std::move(ctx_), to_error(error));
|
||||
}
|
||||
|
||||
std::unique_ptr<NativeSetLocalSdpObserverHandle>
|
||||
create_native_set_local_sdp_observer(
|
||||
rust::Box<SetLocalSdpObserverWrapper> observer) {
|
||||
return std::make_unique<NativeSetLocalSdpObserverHandle>(
|
||||
NativeSetLocalSdpObserverHandle{
|
||||
rtc::make_ref_counted<NativeSetLocalSdpObserver>(
|
||||
std::move(observer))});
|
||||
}
|
||||
|
||||
// SetRemoteSdpObserver
|
||||
|
||||
NativeSetRemoteSdpObserver::NativeSetRemoteSdpObserver(
|
||||
rust::Box<SetRemoteSdpObserverWrapper> observer)
|
||||
: observer_(std::move(observer)) {}
|
||||
rust::Box<AsyncContext> ctx,
|
||||
rust::Fn<void(rust::Box<AsyncContext>, RtcError)> on_complete)
|
||||
: ctx_(std::move(ctx)), on_complete_(on_complete) {}
|
||||
|
||||
void NativeSetRemoteSdpObserver::OnSetRemoteDescriptionComplete(
|
||||
webrtc::RTCError error) {
|
||||
observer_->on_set_remote_description_complete(to_error(error));
|
||||
}
|
||||
|
||||
std::unique_ptr<NativeSetRemoteSdpObserverHandle>
|
||||
create_native_set_remote_sdp_observer(
|
||||
rust::Box<SetRemoteSdpObserverWrapper> observer) {
|
||||
return std::make_unique<NativeSetRemoteSdpObserverHandle>(
|
||||
NativeSetRemoteSdpObserverHandle{
|
||||
rtc::make_ref_counted<NativeSetRemoteSdpObserver>(
|
||||
std::move(observer))});
|
||||
on_complete_(std::move(ctx_), to_error(error));
|
||||
}
|
||||
|
||||
} // namespace livekit
|
||||
|
||||
+2
-78
@@ -1,10 +1,6 @@
|
||||
use crate::impl_thread_safety;
|
||||
use cxx::UniquePtr;
|
||||
use std::error::Error;
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::mem::ManuallyDrop;
|
||||
|
||||
use crate::rtc_error::ffi::RTCError;
|
||||
|
||||
#[cxx::bridge(namespace = "livekit")]
|
||||
pub mod ffi {
|
||||
@@ -23,25 +19,10 @@ pub mod ffi {
|
||||
pub description: String,
|
||||
}
|
||||
|
||||
extern "Rust" {
|
||||
type CreateSdpObserverWrapper;
|
||||
fn on_success(
|
||||
self: &CreateSdpObserverWrapper,
|
||||
session_description: UniquePtr<SessionDescription>,
|
||||
);
|
||||
fn on_failure(self: &CreateSdpObserverWrapper, error: RTCError);
|
||||
|
||||
type SetLocalSdpObserverWrapper;
|
||||
fn on_set_local_description_complete(self: &SetLocalSdpObserverWrapper, error: RTCError);
|
||||
|
||||
type SetRemoteSdpObserverWrapper;
|
||||
fn on_set_remote_description_complete(self: &SetRemoteSdpObserverWrapper, error: RTCError);
|
||||
}
|
||||
|
||||
extern "C++" {
|
||||
include!("livekit/rtc_error.h");
|
||||
|
||||
type RTCError = crate::rtc_error::ffi::RTCError;
|
||||
type RtcError = crate::rtc_error::ffi::RtcError;
|
||||
}
|
||||
|
||||
unsafe extern "C++" {
|
||||
@@ -49,9 +30,6 @@ pub mod ffi {
|
||||
|
||||
type IceCandidate;
|
||||
type SessionDescription;
|
||||
type NativeCreateSdpObserverHandle;
|
||||
type NativeSetLocalSdpObserverHandle;
|
||||
type NativeSetRemoteSdpObserverHandle;
|
||||
|
||||
fn sdp_mid(self: &IceCandidate) -> String;
|
||||
fn sdp_mline_index(self: &IceCandidate) -> i32;
|
||||
@@ -62,21 +40,12 @@ pub mod ffi {
|
||||
fn stringify(self: &SessionDescription) -> String;
|
||||
fn clone(self: &SessionDescription) -> UniquePtr<SessionDescription>;
|
||||
|
||||
fn create_native_create_sdp_observer(
|
||||
observer: Box<CreateSdpObserverWrapper>,
|
||||
) -> UniquePtr<NativeCreateSdpObserverHandle>;
|
||||
fn create_native_set_local_sdp_observer(
|
||||
observer: Box<SetLocalSdpObserverWrapper>,
|
||||
) -> UniquePtr<NativeSetLocalSdpObserverHandle>;
|
||||
fn create_native_set_remote_sdp_observer(
|
||||
observer: Box<SetRemoteSdpObserverWrapper>,
|
||||
) -> UniquePtr<NativeSetRemoteSdpObserverHandle>;
|
||||
|
||||
fn create_ice_candidate(
|
||||
sdp_mid: String,
|
||||
sdp_mline_index: i32,
|
||||
sdp: String,
|
||||
) -> Result<SharedPtr<IceCandidate>>;
|
||||
|
||||
fn create_session_description(
|
||||
sdp_type: SdpType,
|
||||
sdp: String,
|
||||
@@ -115,51 +84,6 @@ impl ffi::SdpParseError {
|
||||
}
|
||||
}
|
||||
|
||||
// CreateSdpObserver
|
||||
|
||||
pub struct CreateSdpObserverWrapper {
|
||||
pub on_success: ManuallyDrop<Box<dyn FnOnce(UniquePtr<ffi::SessionDescription>) + Send>>,
|
||||
pub on_failure: ManuallyDrop<Box<dyn FnOnce(RTCError) + Send>>,
|
||||
}
|
||||
|
||||
impl CreateSdpObserverWrapper {
|
||||
fn on_success(&self, session_description: UniquePtr<ffi::SessionDescription>) {
|
||||
unsafe {
|
||||
std::ptr::read(&*self.on_success)(session_description);
|
||||
}
|
||||
}
|
||||
|
||||
fn on_failure(&self, error: RTCError) {
|
||||
unsafe {
|
||||
std::ptr::read(&*self.on_failure)(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SetLocalSdpObserver
|
||||
|
||||
pub struct SetLocalSdpObserverWrapper(pub ManuallyDrop<Box<dyn FnOnce(RTCError) + Send>>);
|
||||
|
||||
impl SetLocalSdpObserverWrapper {
|
||||
fn on_set_local_description_complete(&self, error: RTCError) {
|
||||
unsafe {
|
||||
std::ptr::read(&*self.0)(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SetRemoteSdpObserver
|
||||
|
||||
pub struct SetRemoteSdpObserverWrapper(pub ManuallyDrop<Box<dyn FnOnce(RTCError) + Send>>);
|
||||
|
||||
impl SetRemoteSdpObserverWrapper {
|
||||
fn on_set_remote_description_complete(&self, error: RTCError) {
|
||||
unsafe {
|
||||
std::ptr::read(&*self.0)(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use log::info;
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
pub mod audio_resampler;
|
||||
pub mod audio_track;
|
||||
pub mod candidate;
|
||||
pub mod data_channel;
|
||||
pub mod helper;
|
||||
pub mod jsep;
|
||||
pub mod logsink;
|
||||
pub mod media_stream;
|
||||
pub mod media_stream_track;
|
||||
pub mod peer_connection;
|
||||
pub mod peer_connection_factory;
|
||||
pub mod rtc_error;
|
||||
@@ -14,6 +15,7 @@ pub mod rtp_sender;
|
||||
pub mod rtp_transceiver;
|
||||
pub mod video_frame;
|
||||
pub mod video_frame_buffer;
|
||||
pub mod video_track;
|
||||
pub mod webrtc;
|
||||
pub mod yuv_helper;
|
||||
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
* Copyright 2023 LiveKit
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the “License”);
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an “AS IS” BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "livekit/logsink.h"
|
||||
|
||||
namespace livekit {
|
||||
|
||||
LogSink::LogSink(rust::Fn<void(rust::String message, LoggingSeverity severity)> fnc) : fnc_(fnc) {
|
||||
rtc::LogMessage::AddLogToStream(this, rtc::LoggingSeverity::LS_VERBOSE);
|
||||
}
|
||||
|
||||
LogSink::~LogSink() {
|
||||
rtc::LogMessage::RemoveLogToStream(this);
|
||||
}
|
||||
|
||||
void LogSink::OnLogMessage(const std::string& message, rtc::LoggingSeverity severity) {
|
||||
fnc_(rust::String(message), static_cast<LoggingSeverity>(severity));
|
||||
}
|
||||
|
||||
std::unique_ptr<LogSink> new_log_sink(rust::Fn<void (rust::String, LoggingSeverity)> fnc) {
|
||||
return std::make_unique<LogSink>(fnc);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
use crate::impl_thread_safety;
|
||||
|
||||
#[cxx::bridge(namespace = "livekit")]
|
||||
pub mod ffi {
|
||||
|
||||
#[derive(Debug)]
|
||||
#[repr(i32)]
|
||||
pub enum LoggingSeverity {
|
||||
Verbose,
|
||||
Info,
|
||||
Warning,
|
||||
Error,
|
||||
None,
|
||||
}
|
||||
|
||||
unsafe extern "C++" {
|
||||
include!("livekit/logsink.h");
|
||||
|
||||
type LogSink;
|
||||
fn new_log_sink(fnc: fn(String, LoggingSeverity)) -> UniquePtr<LogSink>;
|
||||
}
|
||||
}
|
||||
|
||||
impl_thread_safety!(ffi::LogSink, Send + Sync);
|
||||
+16
-275
@@ -32,8 +32,9 @@
|
||||
namespace livekit {
|
||||
|
||||
MediaStream::MediaStream(
|
||||
std::shared_ptr<RtcRuntime> rtc_runtime,
|
||||
rtc::scoped_refptr<webrtc::MediaStreamInterface> stream)
|
||||
: media_stream_(std::move(stream)) {}
|
||||
: rtc_runtime_(rtc_runtime), media_stream_(std::move(stream)) {}
|
||||
|
||||
rust::String MediaStream::id() const {
|
||||
return media_stream_->id();
|
||||
@@ -42,7 +43,8 @@ rust::String MediaStream::id() const {
|
||||
rust::Vec<VideoTrackPtr> MediaStream::get_video_tracks() const {
|
||||
rust::Vec<VideoTrackPtr> rust;
|
||||
for (auto video : media_stream_->GetVideoTracks())
|
||||
rust.push_back(VideoTrackPtr{std::make_shared<VideoTrack>(video)});
|
||||
rust.push_back(
|
||||
VideoTrackPtr{rtc_runtime_->get_or_create_video_track(video)});
|
||||
|
||||
return rust;
|
||||
}
|
||||
@@ -50,20 +52,21 @@ rust::Vec<VideoTrackPtr> MediaStream::get_video_tracks() const {
|
||||
rust::Vec<AudioTrackPtr> MediaStream::get_audio_tracks() const {
|
||||
rust::Vec<AudioTrackPtr> rust;
|
||||
for (auto audio : media_stream_->GetAudioTracks())
|
||||
rust.push_back(AudioTrackPtr{std::make_shared<AudioTrack>(audio)});
|
||||
rust.push_back(
|
||||
AudioTrackPtr{rtc_runtime_->get_or_create_audio_track(audio)});
|
||||
|
||||
return rust;
|
||||
}
|
||||
|
||||
std::shared_ptr<AudioTrack> MediaStream::find_audio_track(
|
||||
rust::String track_id) const {
|
||||
return std::make_shared<AudioTrack>(
|
||||
return rtc_runtime_->get_or_create_audio_track(
|
||||
media_stream_->FindAudioTrack(track_id.c_str()));
|
||||
}
|
||||
|
||||
std::shared_ptr<VideoTrack> MediaStream::find_video_track(
|
||||
rust::String track_id) const {
|
||||
return std::make_shared<VideoTrack>(
|
||||
return rtc_runtime_->get_or_create_video_track(
|
||||
media_stream_->FindVideoTrack(track_id.c_str()));
|
||||
}
|
||||
|
||||
@@ -71,11 +74,13 @@ bool MediaStream::add_track(std::shared_ptr<MediaStreamTrack> track) const {
|
||||
if (track->kind() == webrtc::MediaStreamTrackInterface::kVideoKind) {
|
||||
return media_stream_->AddTrack(
|
||||
rtc::scoped_refptr<webrtc::VideoTrackInterface>(
|
||||
static_cast<webrtc::VideoTrackInterface*>(track->get().get())));
|
||||
static_cast<webrtc::VideoTrackInterface*>(
|
||||
track->rtc_track().get())));
|
||||
} else {
|
||||
return media_stream_->AddTrack(
|
||||
rtc::scoped_refptr<webrtc::AudioTrackInterface>(
|
||||
static_cast<webrtc::AudioTrackInterface*>(track->get().get())));
|
||||
static_cast<webrtc::AudioTrackInterface*>(
|
||||
track->rtc_track().get())));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,278 +88,14 @@ bool MediaStream::remove_track(std::shared_ptr<MediaStreamTrack> track) const {
|
||||
if (track->kind() == webrtc::MediaStreamTrackInterface::kVideoKind) {
|
||||
return media_stream_->RemoveTrack(
|
||||
rtc::scoped_refptr<webrtc::VideoTrackInterface>(
|
||||
static_cast<webrtc::VideoTrackInterface*>(track->get().get())));
|
||||
static_cast<webrtc::VideoTrackInterface*>(
|
||||
track->rtc_track().get())));
|
||||
} else {
|
||||
return media_stream_->RemoveTrack(
|
||||
rtc::scoped_refptr<webrtc::AudioTrackInterface>(
|
||||
static_cast<webrtc::AudioTrackInterface*>(track->get().get())));
|
||||
static_cast<webrtc::AudioTrackInterface*>(
|
||||
track->rtc_track().get())));
|
||||
}
|
||||
}
|
||||
|
||||
MediaStreamTrack::MediaStreamTrack(
|
||||
rtc::scoped_refptr<webrtc::MediaStreamTrackInterface> track)
|
||||
: track_(std::move(track)) {}
|
||||
|
||||
std::shared_ptr<MediaStreamTrack> MediaStreamTrack::from(
|
||||
rtc::scoped_refptr<webrtc::MediaStreamTrackInterface> track) {
|
||||
if (track->kind() == webrtc::MediaStreamTrackInterface::kVideoKind) {
|
||||
return std::make_shared<VideoTrack>(
|
||||
rtc::scoped_refptr<webrtc::VideoTrackInterface>(
|
||||
static_cast<webrtc::VideoTrackInterface*>(track.get())));
|
||||
} else {
|
||||
return std::make_shared<AudioTrack>(
|
||||
rtc::scoped_refptr<webrtc::AudioTrackInterface>(
|
||||
static_cast<webrtc::AudioTrackInterface*>(track.get())));
|
||||
}
|
||||
}
|
||||
|
||||
rust::String MediaStreamTrack::kind() const {
|
||||
return track_->kind();
|
||||
}
|
||||
|
||||
rust::String MediaStreamTrack::id() const {
|
||||
return track_->id();
|
||||
}
|
||||
|
||||
bool MediaStreamTrack::enabled() const {
|
||||
return track_->enabled();
|
||||
}
|
||||
|
||||
bool MediaStreamTrack::set_enabled(bool enable) const {
|
||||
return track_->set_enabled(enable);
|
||||
}
|
||||
|
||||
TrackState MediaStreamTrack::state() const {
|
||||
return static_cast<TrackState>(track_->state());
|
||||
}
|
||||
|
||||
AudioTrack::AudioTrack(rtc::scoped_refptr<webrtc::AudioTrackInterface> track)
|
||||
: MediaStreamTrack(std::move(track)) {}
|
||||
|
||||
void AudioTrack::add_sink(NativeAudioSink& sink) const {
|
||||
track()->AddSink(&sink);
|
||||
}
|
||||
|
||||
void AudioTrack::remove_sink(NativeAudioSink& sink) const {
|
||||
track()->RemoveSink(&sink);
|
||||
}
|
||||
|
||||
NativeAudioSink::NativeAudioSink(rust::Box<AudioSinkWrapper> observer)
|
||||
: observer_(std::move(observer)) {}
|
||||
|
||||
void NativeAudioSink::OnData(const void* audio_data,
|
||||
int bits_per_sample,
|
||||
int sample_rate,
|
||||
size_t number_of_channels,
|
||||
size_t number_of_frames) {
|
||||
RTC_CHECK_EQ(16, bits_per_sample);
|
||||
|
||||
observer_->on_data(static_cast<const int16_t*>(audio_data), sample_rate,
|
||||
number_of_channels, number_of_frames);
|
||||
}
|
||||
|
||||
std::unique_ptr<NativeAudioSink> new_native_audio_sink(
|
||||
rust::Box<AudioSinkWrapper> observer) {
|
||||
return std::make_unique<NativeAudioSink>(std::move(observer));
|
||||
}
|
||||
|
||||
NativeAudioTrackSource::NativeAudioTrackSource() {
|
||||
options_.echo_cancellation = false;
|
||||
options_.auto_gain_control = false;
|
||||
options_.noise_suppression = false;
|
||||
}
|
||||
|
||||
webrtc::MediaSourceInterface::SourceState NativeAudioTrackSource::state()
|
||||
const {
|
||||
return webrtc::MediaSourceInterface::SourceState::kLive;
|
||||
}
|
||||
|
||||
bool NativeAudioTrackSource::remote() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
const cricket::AudioOptions NativeAudioTrackSource::options() const {
|
||||
return options_;
|
||||
}
|
||||
|
||||
void NativeAudioTrackSource::AddSink(webrtc::AudioTrackSinkInterface* sink) {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
sinks_.push_back(sink);
|
||||
}
|
||||
|
||||
void NativeAudioTrackSource::RemoveSink(webrtc::AudioTrackSinkInterface* sink) {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
sinks_.erase(std::remove(sinks_.begin(), sinks_.end(), sink), sinks_.end());
|
||||
}
|
||||
|
||||
void NativeAudioTrackSource::on_captured_frame(const int16_t* data,
|
||||
int sample_rate,
|
||||
size_t number_of_channels,
|
||||
size_t number_of_frames) {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
for (auto sink : sinks_) {
|
||||
sink->OnData(data, 16, sample_rate, number_of_channels, number_of_frames);
|
||||
}
|
||||
}
|
||||
|
||||
AudioTrackSource::AudioTrackSource(
|
||||
rtc::scoped_refptr<NativeAudioTrackSource> source)
|
||||
: source_(std::move(source)) {}
|
||||
|
||||
void AudioTrackSource::on_captured_frame(const int16_t* audio_data,
|
||||
int sample_rate,
|
||||
size_t number_of_channels,
|
||||
size_t number_of_frames) const {
|
||||
source_->on_captured_frame(audio_data, sample_rate, number_of_channels,
|
||||
number_of_frames);
|
||||
}
|
||||
|
||||
rtc::scoped_refptr<NativeAudioTrackSource> AudioTrackSource::get() const {
|
||||
return source_;
|
||||
}
|
||||
|
||||
std::shared_ptr<AudioTrackSource> new_audio_track_source() {
|
||||
return std::make_shared<AudioTrackSource>(
|
||||
rtc::make_ref_counted<NativeAudioTrackSource>());
|
||||
}
|
||||
|
||||
VideoTrack::VideoTrack(rtc::scoped_refptr<webrtc::VideoTrackInterface> track)
|
||||
: MediaStreamTrack(std::move(track)) {}
|
||||
|
||||
void VideoTrack::add_sink(NativeVideoFrameSink& sink) const {
|
||||
track()->AddOrUpdateSink(&sink, rtc::VideoSinkWants());
|
||||
}
|
||||
|
||||
void VideoTrack::remove_sink(NativeVideoFrameSink& sink) const {
|
||||
track()->RemoveSink(&sink);
|
||||
}
|
||||
|
||||
void VideoTrack::set_should_receive(bool should_receive) const {
|
||||
track()->set_should_receive(should_receive);
|
||||
}
|
||||
|
||||
bool VideoTrack::should_receive() const {
|
||||
return track()->should_receive();
|
||||
}
|
||||
|
||||
ContentHint VideoTrack::content_hint() const {
|
||||
return static_cast<ContentHint>(track()->content_hint());
|
||||
}
|
||||
|
||||
void VideoTrack::set_content_hint(ContentHint hint) const {
|
||||
track()->set_content_hint(
|
||||
static_cast<webrtc::VideoTrackInterface::ContentHint>(hint));
|
||||
}
|
||||
|
||||
NativeVideoFrameSink::NativeVideoFrameSink(
|
||||
rust::Box<VideoFrameSinkWrapper> observer)
|
||||
: observer_(std::move(observer)) {}
|
||||
|
||||
void NativeVideoFrameSink::OnFrame(const webrtc::VideoFrame& frame) {
|
||||
observer_->on_frame(std::make_unique<VideoFrame>(frame));
|
||||
}
|
||||
|
||||
void NativeVideoFrameSink::OnDiscardedFrame() {
|
||||
observer_->on_discarded_frame();
|
||||
}
|
||||
|
||||
void NativeVideoFrameSink::OnConstraintsChanged(
|
||||
const webrtc::VideoTrackSourceConstraints& constraints) {
|
||||
VideoTrackSourceConstraints cst;
|
||||
cst.min_fps = constraints.min_fps.value_or(-1);
|
||||
cst.max_fps = constraints.max_fps.value_or(-1);
|
||||
observer_->on_constraints_changed(cst);
|
||||
}
|
||||
|
||||
std::unique_ptr<NativeVideoFrameSink> new_native_video_frame_sink(
|
||||
rust::Box<VideoFrameSinkWrapper> observer) {
|
||||
return std::make_unique<NativeVideoFrameSink>(std::move(observer));
|
||||
}
|
||||
|
||||
NativeVideoTrackSource::NativeVideoTrackSource()
|
||||
: rtc::AdaptedVideoTrackSource(4) {}
|
||||
|
||||
NativeVideoTrackSource::~NativeVideoTrackSource() {}
|
||||
|
||||
bool NativeVideoTrackSource::is_screencast() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
absl::optional<bool> NativeVideoTrackSource::needs_denoising() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
webrtc::MediaSourceInterface::SourceState NativeVideoTrackSource::state()
|
||||
const {
|
||||
return SourceState::kLive;
|
||||
}
|
||||
|
||||
bool NativeVideoTrackSource::remote() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool NativeVideoTrackSource::on_captured_frame(
|
||||
const webrtc::VideoFrame& frame) {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
|
||||
int64_t aligned_timestamp_us = timestamp_aligner_.TranslateTimestamp(
|
||||
frame.timestamp_us(), rtc::TimeMicros());
|
||||
|
||||
rtc::scoped_refptr<webrtc::VideoFrameBuffer> buffer =
|
||||
frame.video_frame_buffer();
|
||||
|
||||
int adapted_width, adapted_height, crop_width, crop_height, crop_x, crop_y;
|
||||
if (!AdaptFrame(buffer->width(), buffer->height(), aligned_timestamp_us,
|
||||
&adapted_width, &adapted_height, &crop_width, &crop_height,
|
||||
&crop_x, &crop_y)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (adapted_width != frame.width() || adapted_height != frame.height()) {
|
||||
buffer = buffer->CropAndScale(crop_x, crop_y, crop_width, crop_height,
|
||||
adapted_width, adapted_height);
|
||||
}
|
||||
|
||||
webrtc::VideoRotation rotation = frame.rotation();
|
||||
if (apply_rotation() && rotation != webrtc::kVideoRotation_0) {
|
||||
// If the buffer is I420, rtc::AdaptedVideoTrackSource will handle the
|
||||
// rotation for us.
|
||||
buffer = buffer->ToI420();
|
||||
}
|
||||
|
||||
OnFrame(webrtc::VideoFrame::Builder()
|
||||
.set_video_frame_buffer(buffer)
|
||||
.set_rotation(rotation)
|
||||
.set_timestamp_us(aligned_timestamp_us)
|
||||
.build());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
AdaptedVideoTrackSource::AdaptedVideoTrackSource(
|
||||
rtc::scoped_refptr<NativeVideoTrackSource> source)
|
||||
: source_(source) {}
|
||||
|
||||
bool AdaptedVideoTrackSource::on_captured_frame(
|
||||
const std::unique_ptr<VideoFrame>& frame) const {
|
||||
auto rtc_frame = frame->get();
|
||||
rtc_frame.set_timestamp_us(rtc::TimeMicros());
|
||||
|
||||
// auto buffer = webrtc::I420Buffer::Create(1280, 720);
|
||||
// webrtc::I420Buffer::SetBlack(buffer.get());
|
||||
// rtc_frame.set_video_frame_buffer(buffer);
|
||||
|
||||
return source_->on_captured_frame(rtc_frame);
|
||||
}
|
||||
|
||||
rtc::scoped_refptr<NativeVideoTrackSource> AdaptedVideoTrackSource::get()
|
||||
const {
|
||||
return source_;
|
||||
}
|
||||
|
||||
std::shared_ptr<AdaptedVideoTrackSource> new_adapted_video_track_source() {
|
||||
return std::make_shared<AdaptedVideoTrackSource>(
|
||||
rtc::make_ref_counted<NativeVideoTrackSource>());
|
||||
}
|
||||
|
||||
} // namespace livekit
|
||||
|
||||
@@ -1,38 +1,16 @@
|
||||
use crate::impl_thread_safety;
|
||||
use crate::video_frame::ffi::VideoFrame;
|
||||
use cxx::UniquePtr;
|
||||
|
||||
#[cxx::bridge(namespace = "livekit")]
|
||||
pub mod ffi {
|
||||
|
||||
#[derive(Debug)]
|
||||
#[repr(i32)]
|
||||
pub enum TrackState {
|
||||
Live,
|
||||
Ended,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[repr(i32)]
|
||||
pub enum ContentHint {
|
||||
None,
|
||||
Fluid,
|
||||
Detailed,
|
||||
Text,
|
||||
}
|
||||
|
||||
// -1 = optional
|
||||
#[derive(Debug)]
|
||||
pub struct VideoTrackSourceConstraints {
|
||||
pub min_fps: f64,
|
||||
pub max_fps: f64,
|
||||
}
|
||||
|
||||
extern "C++" {
|
||||
include!("livekit/video_frame.h");
|
||||
include!("livekit/helper.h");
|
||||
include!("livekit/media_stream_track.h");
|
||||
include!("livekit/audio_track.h");
|
||||
include!("livekit/video_track.h");
|
||||
|
||||
type VideoFrame = crate::video_frame::ffi::VideoFrame;
|
||||
type MediaStreamTrack = crate::media_stream_track::ffi::MediaStreamTrack;
|
||||
type AudioTrack = crate::audio_track::ffi::AudioTrack;
|
||||
type VideoTrack = crate::video_track::ffi::VideoTrack;
|
||||
type VideoTrackPtr = crate::helper::ffi::VideoTrackPtr;
|
||||
type AudioTrackPtr = crate::helper::ffi::AudioTrackPtr;
|
||||
}
|
||||
@@ -40,14 +18,7 @@ pub mod ffi {
|
||||
unsafe extern "C++" {
|
||||
include!("livekit/media_stream.h");
|
||||
|
||||
type NativeAudioSink;
|
||||
type NativeVideoFrameSink;
|
||||
type MediaStreamTrack;
|
||||
type MediaStream;
|
||||
type AudioTrack;
|
||||
type VideoTrack;
|
||||
type AudioTrackSource;
|
||||
type AdaptedVideoTrackSource;
|
||||
|
||||
fn id(self: &MediaStream) -> String;
|
||||
fn get_audio_tracks(self: &MediaStream) -> Vec<AudioTrackPtr>;
|
||||
@@ -57,140 +28,8 @@ pub mod ffi {
|
||||
fn add_track(self: &MediaStream, audio_track: SharedPtr<MediaStreamTrack>) -> bool;
|
||||
fn remove_track(self: &MediaStream, audio_track: SharedPtr<MediaStreamTrack>) -> bool;
|
||||
|
||||
fn kind(self: &MediaStreamTrack) -> String;
|
||||
fn id(self: &MediaStreamTrack) -> String;
|
||||
fn enabled(self: &MediaStreamTrack) -> bool;
|
||||
fn set_enabled(self: &MediaStreamTrack, enable: bool) -> bool;
|
||||
fn state(self: &MediaStreamTrack) -> TrackState;
|
||||
|
||||
unsafe fn add_sink(self: &AudioTrack, sink: Pin<&mut NativeAudioSink>);
|
||||
unsafe fn remove_sink(self: &AudioTrack, sink: Pin<&mut NativeAudioSink>);
|
||||
|
||||
fn new_native_audio_sink(observer: Box<AudioSinkWrapper>) -> UniquePtr<NativeAudioSink>;
|
||||
|
||||
unsafe fn on_captured_frame(
|
||||
self: &AudioTrackSource,
|
||||
data: *const i16,
|
||||
sample_rate: i32,
|
||||
nb_channels: usize,
|
||||
nb_frames: usize,
|
||||
);
|
||||
|
||||
fn new_audio_track_source() -> SharedPtr<AudioTrackSource>;
|
||||
|
||||
unsafe fn add_sink(self: &VideoTrack, sink: Pin<&mut NativeVideoFrameSink>);
|
||||
unsafe fn remove_sink(self: &VideoTrack, sink: Pin<&mut NativeVideoFrameSink>);
|
||||
|
||||
fn set_should_receive(self: &VideoTrack, should_receive: bool);
|
||||
fn should_receive(self: &VideoTrack) -> bool;
|
||||
fn content_hint(self: &VideoTrack) -> ContentHint;
|
||||
fn set_content_hint(self: &VideoTrack, hint: ContentHint);
|
||||
|
||||
fn new_native_video_frame_sink(
|
||||
observer: Box<VideoFrameSinkWrapper>,
|
||||
) -> UniquePtr<NativeVideoFrameSink>;
|
||||
|
||||
fn on_captured_frame(self: &AdaptedVideoTrackSource, frame: &UniquePtr<VideoFrame>)
|
||||
-> bool;
|
||||
|
||||
fn new_adapted_video_track_source() -> SharedPtr<AdaptedVideoTrackSource>;
|
||||
|
||||
fn video_to_media(track: SharedPtr<VideoTrack>) -> SharedPtr<MediaStreamTrack>;
|
||||
fn audio_to_media(track: SharedPtr<AudioTrack>) -> SharedPtr<MediaStreamTrack>;
|
||||
fn media_to_video(track: SharedPtr<MediaStreamTrack>) -> SharedPtr<VideoTrack>;
|
||||
fn media_to_audio(track: SharedPtr<MediaStreamTrack>) -> SharedPtr<AudioTrack>;
|
||||
|
||||
fn _shared_media_stream_track() -> SharedPtr<MediaStreamTrack>;
|
||||
fn _shared_audio_track() -> SharedPtr<AudioTrack>;
|
||||
fn _shared_video_track() -> SharedPtr<VideoTrack>;
|
||||
fn _shared_media_stream() -> SharedPtr<MediaStream>;
|
||||
}
|
||||
|
||||
extern "Rust" {
|
||||
type AudioSinkWrapper;
|
||||
type VideoFrameSinkWrapper;
|
||||
|
||||
unsafe fn on_data(
|
||||
self: &AudioSinkWrapper,
|
||||
data: *const i16,
|
||||
sample_rate: i32,
|
||||
nb_channels: usize,
|
||||
nb_frames: usize,
|
||||
);
|
||||
|
||||
fn on_frame(self: &VideoFrameSinkWrapper, frame: UniquePtr<VideoFrame>);
|
||||
fn on_discarded_frame(self: &VideoFrameSinkWrapper);
|
||||
fn on_constraints_changed(
|
||||
self: &VideoFrameSinkWrapper,
|
||||
constraints: VideoTrackSourceConstraints,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl_thread_safety!(ffi::MediaStreamTrack, Send + Sync);
|
||||
impl_thread_safety!(ffi::MediaStream, Send + Sync);
|
||||
impl_thread_safety!(ffi::AudioTrack, Send + Sync);
|
||||
impl_thread_safety!(ffi::VideoTrack, Send + Sync);
|
||||
impl_thread_safety!(ffi::NativeVideoFrameSink, Send + Sync);
|
||||
impl_thread_safety!(ffi::NativeAudioSink, Send + Sync);
|
||||
impl_thread_safety!(ffi::AudioTrackSource, Send + Sync);
|
||||
impl_thread_safety!(ffi::AdaptedVideoTrackSource, Send + Sync);
|
||||
|
||||
pub trait AudioSink: Send {
|
||||
fn on_data(&self, data: &[i16], sample_rate: i32, nb_channels: usize, nb_frames: usize);
|
||||
}
|
||||
|
||||
pub struct AudioSinkWrapper {
|
||||
observer: *mut dyn AudioSink,
|
||||
}
|
||||
|
||||
impl AudioSinkWrapper {
|
||||
/// # Safety
|
||||
/// AudioSink must lives as long as AudioSinkWrapper does
|
||||
pub unsafe fn new(observer: *mut dyn AudioSink) -> Self {
|
||||
Self { observer }
|
||||
}
|
||||
|
||||
fn on_data(&self, data: *const i16, sample_rate: i32, nb_channels: usize, nb_frames: usize) {
|
||||
unsafe {
|
||||
let data = std::slice::from_raw_parts(data, nb_channels * nb_frames);
|
||||
(*self.observer).on_data(data, sample_rate, nb_channels, nb_frames);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait VideoFrameSink: Send {
|
||||
fn on_frame(&self, frame: UniquePtr<VideoFrame>);
|
||||
fn on_discarded_frame(&self);
|
||||
fn on_constraints_changed(&self, constraints: ffi::VideoTrackSourceConstraints);
|
||||
}
|
||||
|
||||
pub struct VideoFrameSinkWrapper {
|
||||
observer: *mut dyn VideoFrameSink,
|
||||
}
|
||||
|
||||
impl VideoFrameSinkWrapper {
|
||||
/// # Safety
|
||||
/// VideoFrameSink must lives as long as VideoSinkInterfaceWrapper does
|
||||
pub unsafe fn new(observer: *mut dyn VideoFrameSink) -> Self {
|
||||
Self { observer }
|
||||
}
|
||||
|
||||
fn on_frame(&self, frame: UniquePtr<VideoFrame>) {
|
||||
unsafe {
|
||||
(*self.observer).on_frame(frame);
|
||||
}
|
||||
}
|
||||
|
||||
fn on_discarded_frame(&self) {
|
||||
unsafe {
|
||||
(*self.observer).on_discarded_frame();
|
||||
}
|
||||
}
|
||||
|
||||
fn on_constraints_changed(&self, constraints: ffi::VideoTrackSourceConstraints) {
|
||||
unsafe {
|
||||
(*self.observer).on_constraints_changed(constraints);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright 2023 LiveKit
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the “License”);
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an “AS IS” BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
|
||||
#include "api/media_stream_interface.h"
|
||||
#include "api/video/video_frame.h"
|
||||
#include "api/video/video_rotation.h"
|
||||
#include "audio/remix_resample.h"
|
||||
#include "common_audio/include/audio_util.h"
|
||||
#include "livekit/media_stream.h"
|
||||
#include "rtc_base/logging.h"
|
||||
#include "rtc_base/ref_counted_object.h"
|
||||
#include "rtc_base/time_utils.h"
|
||||
|
||||
namespace livekit {
|
||||
|
||||
MediaStreamTrack::MediaStreamTrack(
|
||||
std::shared_ptr<RtcRuntime> rtc_runtime,
|
||||
rtc::scoped_refptr<webrtc::MediaStreamTrackInterface> track)
|
||||
: rtc_runtime_(rtc_runtime), track_(std::move(track)) {}
|
||||
|
||||
rust::String MediaStreamTrack::kind() const {
|
||||
return track_->kind();
|
||||
}
|
||||
|
||||
rust::String MediaStreamTrack::id() const {
|
||||
return track_->id();
|
||||
}
|
||||
|
||||
bool MediaStreamTrack::enabled() const {
|
||||
return track_->enabled();
|
||||
}
|
||||
|
||||
bool MediaStreamTrack::set_enabled(bool enable) const {
|
||||
return track_->set_enabled(enable);
|
||||
}
|
||||
|
||||
TrackState MediaStreamTrack::state() const {
|
||||
return static_cast<TrackState>(track_->state());
|
||||
}
|
||||
|
||||
} // namespace livekit
|
||||
@@ -0,0 +1,26 @@
|
||||
use crate::impl_thread_safety;
|
||||
|
||||
#[cxx::bridge(namespace = "livekit")]
|
||||
pub mod ffi {
|
||||
#[repr(i32)]
|
||||
pub enum TrackState {
|
||||
Live,
|
||||
Ended,
|
||||
}
|
||||
|
||||
unsafe extern "C++" {
|
||||
include!("livekit/media_stream_track.h");
|
||||
|
||||
type MediaStreamTrack;
|
||||
|
||||
fn kind(self: &MediaStreamTrack) -> String;
|
||||
fn id(self: &MediaStreamTrack) -> String;
|
||||
fn enabled(self: &MediaStreamTrack) -> bool;
|
||||
fn set_enabled(self: &MediaStreamTrack, enable: bool) -> bool;
|
||||
fn state(self: &MediaStreamTrack) -> TrackState;
|
||||
|
||||
fn _shared_media_stream_track() -> SharedPtr<MediaStreamTrack>;
|
||||
}
|
||||
}
|
||||
|
||||
impl_thread_safety!(ffi::MediaStreamTrack, Send + Sync);
|
||||
@@ -16,14 +16,22 @@
|
||||
|
||||
#include "livekit/peer_connection.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "api/data_channel_interface.h"
|
||||
#include "api/scoped_refptr.h"
|
||||
#include "livekit/data_channel.h"
|
||||
#include "livekit/jsep.h"
|
||||
#include "livekit/media_stream.h"
|
||||
#include "livekit/rtc_error.h"
|
||||
#include "livekit/rtp_transceiver.h"
|
||||
#include "webrtc-sys/src/peer_connection.rs.h"
|
||||
#include "webrtc-sys/src/rtc_error.rs.h"
|
||||
|
||||
namespace livekit {
|
||||
|
||||
inline webrtc::PeerConnectionInterface::RTCOfferAnswerOptions
|
||||
toNativeOfferAnswerOptions(const RTCOfferAnswerOptions& options) {
|
||||
to_native_offer_answer_options(const RtcOfferAnswerOptions& options) {
|
||||
webrtc::PeerConnectionInterface::RTCOfferAnswerOptions rtc_options;
|
||||
rtc_options.offer_to_receive_video = options.offer_to_receive_video;
|
||||
rtc_options.offer_to_receive_audio = options.offer_to_receive_audio;
|
||||
@@ -37,44 +45,79 @@ toNativeOfferAnswerOptions(const RTCOfferAnswerOptions& options) {
|
||||
}
|
||||
|
||||
PeerConnection::PeerConnection(
|
||||
std::shared_ptr<RTCRuntime> rtc_runtime,
|
||||
std::shared_ptr<RtcRuntime> rtc_runtime,
|
||||
std::unique_ptr<NativePeerConnectionObserver> observer,
|
||||
rtc::scoped_refptr<webrtc::PeerConnectionInterface> peer_connection)
|
||||
: rtc_runtime_(std::move(rtc_runtime)),
|
||||
: rtc_runtime_(rtc_runtime),
|
||||
observer_(std::move(observer)),
|
||||
peer_connection_(std::move(peer_connection)) {}
|
||||
|
||||
void PeerConnection::create_offer(
|
||||
NativeCreateSdpObserverHandle& observer_handle,
|
||||
RTCOfferAnswerOptions options) const {
|
||||
peer_connection_->CreateOffer(observer_handle.observer.get(),
|
||||
toNativeOfferAnswerOptions(options));
|
||||
RtcOfferAnswerOptions options,
|
||||
rust::Box<AsyncContext> ctx,
|
||||
rust::Fn<void(rust::Box<AsyncContext>, std::unique_ptr<SessionDescription>)>
|
||||
on_success,
|
||||
rust::Fn<void(rust::Box<AsyncContext>, RtcError)> on_error) const {
|
||||
rtc::scoped_refptr<NativeCreateSdpObserver> observer =
|
||||
rtc::make_ref_counted<NativeCreateSdpObserver>(std::move(ctx), on_success,
|
||||
on_error);
|
||||
|
||||
peer_connection_->CreateOffer(observer.get(),
|
||||
to_native_offer_answer_options(options));
|
||||
}
|
||||
|
||||
void PeerConnection::create_answer(
|
||||
NativeCreateSdpObserverHandle& observer_handle,
|
||||
RTCOfferAnswerOptions options) const {
|
||||
peer_connection_->CreateAnswer(observer_handle.observer.get(),
|
||||
toNativeOfferAnswerOptions(options));
|
||||
RtcOfferAnswerOptions options,
|
||||
rust::Box<AsyncContext> ctx,
|
||||
rust::Fn<void(rust::Box<AsyncContext>, std::unique_ptr<SessionDescription>)>
|
||||
on_success,
|
||||
rust::Fn<void(rust::Box<AsyncContext>, RtcError)> on_error) const {
|
||||
rtc::scoped_refptr<NativeCreateSdpObserver> observer =
|
||||
rtc::make_ref_counted<NativeCreateSdpObserver>(std::move(ctx), on_success,
|
||||
on_error);
|
||||
|
||||
peer_connection_->CreateAnswer(observer.get(),
|
||||
to_native_offer_answer_options(options));
|
||||
}
|
||||
|
||||
void PeerConnection::set_local_description(
|
||||
std::unique_ptr<SessionDescription> desc,
|
||||
NativeSetLocalSdpObserverHandle& observer) const {
|
||||
peer_connection_->SetLocalDescription(desc->clone()->release(),
|
||||
observer.observer);
|
||||
rust::Box<AsyncContext> ctx,
|
||||
rust::Fn<void(rust::Box<AsyncContext>, RtcError)> on_complete) const {
|
||||
rtc::scoped_refptr<NativeSetLocalSdpObserver> observer =
|
||||
rtc::make_ref_counted<NativeSetLocalSdpObserver>(std::move(ctx),
|
||||
on_complete);
|
||||
|
||||
peer_connection_->SetLocalDescription(desc->clone()->release(), observer);
|
||||
}
|
||||
|
||||
void PeerConnection::set_remote_description(
|
||||
std::unique_ptr<SessionDescription> desc,
|
||||
NativeSetRemoteSdpObserverHandle& observer) const {
|
||||
peer_connection_->SetRemoteDescription(desc->clone()->release(),
|
||||
observer.observer);
|
||||
rust::Box<AsyncContext> ctx,
|
||||
rust::Fn<void(rust::Box<AsyncContext>, RtcError)> on_complete) const {
|
||||
rtc::scoped_refptr<NativeSetRemoteSdpObserver> observer =
|
||||
rtc::make_ref_counted<NativeSetRemoteSdpObserver>(std::move(ctx),
|
||||
on_complete);
|
||||
|
||||
peer_connection_->SetRemoteDescription(desc->clone()->release(), observer);
|
||||
}
|
||||
|
||||
void PeerConnection::add_ice_candidate(
|
||||
std::shared_ptr<IceCandidate> candidate,
|
||||
rust::Box<AsyncContext> ctx,
|
||||
rust::Fn<void(rust::Box<AsyncContext>, RtcError)> on_complete) const {
|
||||
peer_connection_->AddIceCandidate(
|
||||
candidate->release(), [&](const webrtc::RTCError& err) {
|
||||
on_complete(std::move(ctx), to_error(err));
|
||||
});
|
||||
}
|
||||
|
||||
std::shared_ptr<DataChannel> PeerConnection::create_data_channel(
|
||||
rust::String label,
|
||||
std::unique_ptr<NativeDataChannelInit> init) const {
|
||||
DataChannelInit init) const {
|
||||
webrtc::DataChannelInit rtc_init = to_native_data_channel_init(init);
|
||||
auto result =
|
||||
peer_connection_->CreateDataChannelOrError(label.c_str(), init.get());
|
||||
peer_connection_->CreateDataChannelOrError(label.c_str(), &rtc_init);
|
||||
|
||||
if (!result.ok()) {
|
||||
throw std::runtime_error(serialize_error(to_error(result.error())));
|
||||
@@ -87,16 +130,16 @@ std::shared_ptr<RtpSender> PeerConnection::add_track(
|
||||
std::shared_ptr<MediaStreamTrack> track,
|
||||
const rust::Vec<rust::String>& stream_ids) const {
|
||||
std::vector<std::string> std_stream_ids(stream_ids.begin(), stream_ids.end());
|
||||
auto result = peer_connection_->AddTrack(track->get(), std_stream_ids);
|
||||
auto result = peer_connection_->AddTrack(track->rtc_track(), std_stream_ids);
|
||||
if (!result.ok()) {
|
||||
throw std::runtime_error(serialize_error(to_error(result.error())));
|
||||
}
|
||||
|
||||
return std::make_shared<RtpSender>(result.value());
|
||||
return std::make_shared<RtpSender>(rtc_runtime_, result.value());
|
||||
}
|
||||
|
||||
void PeerConnection::remove_track(std::shared_ptr<RtpSender> sender) const {
|
||||
auto error = peer_connection_->RemoveTrackOrError(sender->get());
|
||||
auto error = peer_connection_->RemoveTrackOrError(sender->rtc_sender());
|
||||
if (!error.ok())
|
||||
throw std::runtime_error(serialize_error(to_error(error)));
|
||||
}
|
||||
@@ -105,11 +148,11 @@ std::shared_ptr<RtpTransceiver> PeerConnection::add_transceiver(
|
||||
std::shared_ptr<MediaStreamTrack> track,
|
||||
RtpTransceiverInit init) const {
|
||||
auto result = peer_connection_->AddTransceiver(
|
||||
track->get(), to_native_rtp_transceiver_init(init));
|
||||
track->rtc_track(), to_native_rtp_transceiver_init(init));
|
||||
if (!result.ok())
|
||||
throw std::runtime_error(serialize_error(to_error(result.error())));
|
||||
|
||||
return std::make_shared<RtpTransceiver>(result.value());
|
||||
return std::make_shared<RtpTransceiver>(rtc_runtime_, result.value());
|
||||
}
|
||||
|
||||
std::shared_ptr<RtpTransceiver> PeerConnection::add_transceiver_for_media(
|
||||
@@ -122,13 +165,14 @@ std::shared_ptr<RtpTransceiver> PeerConnection::add_transceiver_for_media(
|
||||
if (!result.ok())
|
||||
throw std::runtime_error(serialize_error(to_error(result.error())));
|
||||
|
||||
return std::make_shared<RtpTransceiver>(result.value());
|
||||
return std::make_shared<RtpTransceiver>(rtc_runtime_, result.value());
|
||||
}
|
||||
|
||||
rust::Vec<RtpSenderPtr> PeerConnection::get_senders() const {
|
||||
rust::Vec<RtpSenderPtr> vec;
|
||||
for (auto sender : peer_connection_->GetSenders())
|
||||
vec.push_back(RtpSenderPtr{std::make_shared<RtpSender>(sender)});
|
||||
vec.push_back(
|
||||
RtpSenderPtr{std::make_shared<RtpSender>(rtc_runtime_, sender)});
|
||||
|
||||
return vec;
|
||||
}
|
||||
@@ -136,7 +180,8 @@ rust::Vec<RtpSenderPtr> PeerConnection::get_senders() const {
|
||||
rust::Vec<RtpReceiverPtr> PeerConnection::get_receivers() const {
|
||||
rust::Vec<RtpReceiverPtr> vec;
|
||||
for (auto receiver : peer_connection_->GetReceivers())
|
||||
vec.push_back(RtpReceiverPtr{std::make_shared<RtpReceiver>(receiver)});
|
||||
vec.push_back(
|
||||
RtpReceiverPtr{std::make_shared<RtpReceiver>(rtc_runtime_, receiver)});
|
||||
|
||||
return vec;
|
||||
}
|
||||
@@ -144,20 +189,12 @@ rust::Vec<RtpReceiverPtr> PeerConnection::get_receivers() const {
|
||||
rust::Vec<RtpTransceiverPtr> PeerConnection::get_transceivers() const {
|
||||
rust::Vec<RtpTransceiverPtr> vec;
|
||||
for (auto transceiver : peer_connection_->GetTransceivers())
|
||||
vec.push_back(
|
||||
RtpTransceiverPtr{std::make_shared<RtpTransceiver>(transceiver)});
|
||||
vec.push_back(RtpTransceiverPtr{
|
||||
std::make_shared<RtpTransceiver>(rtc_runtime_, transceiver)});
|
||||
|
||||
return vec;
|
||||
}
|
||||
|
||||
void PeerConnection::add_ice_candidate(
|
||||
std::shared_ptr<IceCandidate> candidate,
|
||||
NativeAddIceCandidateObserver& observer) const {
|
||||
peer_connection_->AddIceCandidate(
|
||||
candidate->release(),
|
||||
[&](const webrtc::RTCError& err) { observer.OnComplete(to_error(err)); });
|
||||
}
|
||||
|
||||
std::unique_ptr<SessionDescription> PeerConnection::current_local_description()
|
||||
const {
|
||||
auto local_description = peer_connection_->current_local_description();
|
||||
@@ -233,28 +270,11 @@ void PeerConnection::close() const {
|
||||
peer_connection_->Close();
|
||||
}
|
||||
|
||||
// AddIceCandidateObserver
|
||||
|
||||
NativeAddIceCandidateObserver::NativeAddIceCandidateObserver(
|
||||
rust::Box<AddIceCandidateObserverWrapper> observer)
|
||||
: observer_(std::move(observer)) {}
|
||||
|
||||
void NativeAddIceCandidateObserver::OnComplete(const RTCError& error) {
|
||||
observer_->on_complete(error);
|
||||
}
|
||||
|
||||
std::unique_ptr<NativeAddIceCandidateObserver>
|
||||
create_native_add_ice_candidate_observer(
|
||||
rust::Box<AddIceCandidateObserverWrapper> observer) {
|
||||
return std::make_unique<NativeAddIceCandidateObserver>(std::move(observer));
|
||||
}
|
||||
|
||||
// PeerConnectionObserver
|
||||
|
||||
NativePeerConnectionObserver::NativePeerConnectionObserver(
|
||||
std::shared_ptr<RTCRuntime> rtc_runtime,
|
||||
rust::Box<PeerConnectionObserverWrapper> observer)
|
||||
: rtc_runtime_(std::move(rtc_runtime)), observer_(std::move(observer)) {
|
||||
: observer_(std::move(observer)) {
|
||||
RTC_LOG(LS_INFO) << "NativePeerConnectionObserver()";
|
||||
}
|
||||
|
||||
@@ -269,12 +289,14 @@ void NativePeerConnectionObserver::OnSignalingChange(
|
||||
|
||||
void NativePeerConnectionObserver::OnAddStream(
|
||||
rtc::scoped_refptr<webrtc::MediaStreamInterface> stream) {
|
||||
observer_->on_add_stream(std::make_unique<MediaStream>(stream));
|
||||
observer_->on_add_stream(std::make_unique<MediaStream>(rtc_runtime_, stream));
|
||||
}
|
||||
|
||||
void NativePeerConnectionObserver::OnRemoveStream(
|
||||
rtc::scoped_refptr<webrtc::MediaStreamInterface> stream) {
|
||||
observer_->on_remove_stream(std::make_unique<MediaStream>(stream));
|
||||
// Find current MediaStream
|
||||
// observer_->on_remove_stream(std::make_unique<MediaStream>(rtc_runtime_,
|
||||
// stream));
|
||||
}
|
||||
|
||||
void NativePeerConnectionObserver::OnDataChannel(
|
||||
@@ -349,7 +371,7 @@ void NativePeerConnectionObserver::OnIceConnectionReceivingChange(
|
||||
|
||||
void NativePeerConnectionObserver::OnIceSelectedCandidatePairChanged(
|
||||
const cricket::CandidatePairChangeEvent& event) {
|
||||
CandidatePairChangeEvent e;
|
||||
CandidatePairChangeEvent e{};
|
||||
e.selected_candidate_pair.local =
|
||||
std::make_unique<Candidate>(event.selected_candidate_pair.local);
|
||||
e.selected_candidate_pair.remote =
|
||||
@@ -368,32 +390,34 @@ void NativePeerConnectionObserver::OnAddTrack(
|
||||
rust::Vec<MediaStreamPtr> vec;
|
||||
|
||||
for (const auto& item : streams) {
|
||||
vec.push_back(MediaStreamPtr{std::make_unique<MediaStream>(item)});
|
||||
vec.push_back(
|
||||
MediaStreamPtr{std::make_unique<MediaStream>(rtc_runtime_, item)});
|
||||
}
|
||||
|
||||
observer_->on_add_track(std::make_unique<RtpReceiver>(receiver),
|
||||
observer_->on_add_track(std::make_unique<RtpReceiver>(rtc_runtime_, receiver),
|
||||
std::move(vec));
|
||||
}
|
||||
|
||||
void NativePeerConnectionObserver::OnTrack(
|
||||
rtc::scoped_refptr<webrtc::RtpTransceiverInterface> transceiver) {
|
||||
observer_->on_track(std::make_unique<RtpTransceiver>(transceiver));
|
||||
observer_->on_track(
|
||||
std::make_unique<RtpTransceiver>(rtc_runtime_, transceiver));
|
||||
}
|
||||
|
||||
void NativePeerConnectionObserver::OnRemoveTrack(
|
||||
rtc::scoped_refptr<webrtc::RtpReceiverInterface> receiver) {
|
||||
observer_->on_remove_track(std::make_unique<RtpReceiver>(receiver));
|
||||
observer_->on_remove_track(
|
||||
std::make_unique<RtpReceiver>(rtc_runtime_, receiver));
|
||||
}
|
||||
|
||||
void NativePeerConnectionObserver::OnInterestingUsage(int usage_pattern) {
|
||||
observer_->on_interesting_usage(usage_pattern);
|
||||
}
|
||||
|
||||
std::shared_ptr<NativePeerConnectionObserver>
|
||||
std::unique_ptr<NativePeerConnectionObserver>
|
||||
create_native_peer_connection_observer(
|
||||
std::shared_ptr<RTCRuntime> rtc_runtime,
|
||||
rust::Box<PeerConnectionObserverWrapper> observer) {
|
||||
return std::make_shared<NativePeerConnectionObserver>(rtc_runtime,
|
||||
std::move(observer));
|
||||
return std::make_unique<NativePeerConnectionObserver>(std::move(observer));
|
||||
}
|
||||
|
||||
} // namespace livekit
|
||||
|
||||
@@ -3,28 +3,26 @@ use crate::data_channel::ffi::DataChannel;
|
||||
use crate::impl_thread_safety;
|
||||
use crate::jsep::ffi::IceCandidate;
|
||||
use crate::media_stream::ffi::MediaStream;
|
||||
use crate::rtc_error::ffi::RTCError;
|
||||
use crate::rtp_receiver::ffi::RtpReceiver;
|
||||
use crate::rtp_transceiver::ffi::RtpTransceiver;
|
||||
use cxx::SharedPtr;
|
||||
use std::mem::ManuallyDrop;
|
||||
use std::any::Any;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[cxx::bridge(namespace = "livekit")]
|
||||
pub mod ffi {
|
||||
struct CandidatePair {
|
||||
pub struct CandidatePair {
|
||||
local: SharedPtr<Candidate>,
|
||||
remote: SharedPtr<Candidate>,
|
||||
}
|
||||
|
||||
struct CandidatePairChangeEvent {
|
||||
pub struct CandidatePairChangeEvent {
|
||||
selected_candidate_pair: CandidatePair,
|
||||
last_data_received_ms: i64,
|
||||
reason: String,
|
||||
estimated_disconnected_time_ms: i64,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[repr(i32)]
|
||||
pub enum PeerConnectionState {
|
||||
New,
|
||||
@@ -35,7 +33,6 @@ pub mod ffi {
|
||||
Closed,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[repr(i32)]
|
||||
pub enum SignalingState {
|
||||
Stable,
|
||||
@@ -46,7 +43,6 @@ pub mod ffi {
|
||||
Closed,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[repr(i32)]
|
||||
pub enum IceConnectionState {
|
||||
IceConnectionNew,
|
||||
@@ -59,7 +55,6 @@ pub mod ffi {
|
||||
IceConnectionMax,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[repr(i32)]
|
||||
pub enum IceGatheringState {
|
||||
IceGatheringNew,
|
||||
@@ -67,8 +62,7 @@ pub mod ffi {
|
||||
IceGatheringComplete,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct RTCOfferAnswerOptions {
|
||||
pub struct RtcOfferAnswerOptions {
|
||||
offer_to_receive_video: i32,
|
||||
offer_to_receive_audio: i32,
|
||||
voice_activity_detection: bool,
|
||||
@@ -96,133 +90,104 @@ pub mod ffi {
|
||||
type RtpSenderPtr = crate::helper::ffi::RtpSenderPtr;
|
||||
type RtpReceiverPtr = crate::helper::ffi::RtpReceiverPtr;
|
||||
type RtpTransceiverPtr = crate::helper::ffi::RtpTransceiverPtr;
|
||||
type RTCError = crate::rtc_error::ffi::RTCError;
|
||||
type RtcError = crate::rtc_error::ffi::RtcError;
|
||||
type Candidate = crate::candidate::ffi::Candidate;
|
||||
type IceCandidate = crate::jsep::ffi::IceCandidate;
|
||||
type DataChannel = crate::data_channel::ffi::DataChannel;
|
||||
type DataChannelInit = crate::data_channel::ffi::DataChannelInit;
|
||||
type RtpSender = crate::rtp_sender::ffi::RtpSender;
|
||||
type RtpReceiver = crate::rtp_receiver::ffi::RtpReceiver;
|
||||
type RtpTransceiver = crate::rtp_transceiver::ffi::RtpTransceiver;
|
||||
type RtpTransceiverInit = crate::rtp_transceiver::ffi::RtpTransceiverInit;
|
||||
type MediaStream = crate::media_stream::ffi::MediaStream;
|
||||
type MediaStreamTrack = crate::media_stream::ffi::MediaStreamTrack;
|
||||
type NativeCreateSdpObserverHandle = crate::jsep::ffi::NativeCreateSdpObserverHandle;
|
||||
type NativeSetLocalSdpObserverHandle = crate::jsep::ffi::NativeSetLocalSdpObserverHandle;
|
||||
type NativeSetRemoteSdpObserverHandle = crate::jsep::ffi::NativeSetRemoteSdpObserverHandle;
|
||||
type NativeDataChannelInit = crate::data_channel::ffi::NativeDataChannelInit;
|
||||
type SessionDescription = crate::jsep::ffi::SessionDescription;
|
||||
type MediaType = crate::webrtc::ffi::MediaType;
|
||||
type RTCRuntime = crate::webrtc::ffi::RTCRuntime;
|
||||
}
|
||||
|
||||
unsafe extern "C++" {
|
||||
include!("livekit/peer_connection.h");
|
||||
|
||||
type NativeAddIceCandidateObserver;
|
||||
type NativePeerConnectionObserver;
|
||||
type PeerConnection;
|
||||
|
||||
/// SAFETY
|
||||
/// The observer must live as long as the operation ends
|
||||
unsafe fn create_offer(
|
||||
self: &PeerConnection,
|
||||
observer: Pin<&mut NativeCreateSdpObserverHandle>,
|
||||
options: RTCOfferAnswerOptions,
|
||||
);
|
||||
// The reason we still expose NativePeerConnectionObserver is because cxx doeesn't support Rust type alias
|
||||
// So we can't share NativePeerConnectionWrapper in peer_connection_factory.rs
|
||||
// (It is technically possible to get the Opaque C++ Type, but in this case, we can't use Box<T>)
|
||||
// We can delete create_native_peer_connection_observer once cxx supports Rust type alias
|
||||
type NativePeerConnectionObserver;
|
||||
fn create_native_peer_connection_observer(
|
||||
observer: Box<PeerConnectionObserverWrapper>,
|
||||
) -> UniquePtr<NativePeerConnectionObserver>;
|
||||
|
||||
/// SAFETY
|
||||
/// The observer must live as long as the operation ends
|
||||
unsafe fn create_answer(
|
||||
fn create_offer(
|
||||
self: &PeerConnection,
|
||||
observer: Pin<&mut NativeCreateSdpObserverHandle>,
|
||||
options: RTCOfferAnswerOptions,
|
||||
options: RtcOfferAnswerOptions,
|
||||
ctx: Box<AsyncContext>,
|
||||
on_success: fn(ctx: Box<AsyncContext>, sdp: UniquePtr<SessionDescription>),
|
||||
on_error: fn(ctx: Box<AsyncContext>, error: RtcError),
|
||||
);
|
||||
|
||||
/// SAFETY
|
||||
/// The observer must live as long as the operation ends
|
||||
unsafe fn set_local_description(
|
||||
fn create_answer(
|
||||
self: &PeerConnection,
|
||||
options: RtcOfferAnswerOptions,
|
||||
ctx: Box<AsyncContext>,
|
||||
on_success: fn(ctx: Box<AsyncContext>, sdp: UniquePtr<SessionDescription>),
|
||||
on_error: fn(ctx: Box<AsyncContext>, error: RtcError),
|
||||
);
|
||||
fn set_local_description(
|
||||
self: &PeerConnection,
|
||||
desc: UniquePtr<SessionDescription>,
|
||||
observer: Pin<&mut NativeSetLocalSdpObserverHandle>,
|
||||
ctx: Box<AsyncContext>,
|
||||
on_complete: fn(ctx: Box<AsyncContext>, error: RtcError),
|
||||
);
|
||||
|
||||
/// SAFETY
|
||||
/// The observer must live as long as the operation ends
|
||||
unsafe fn set_remote_description(
|
||||
fn set_remote_description(
|
||||
self: &PeerConnection,
|
||||
desc: UniquePtr<SessionDescription>,
|
||||
observer: Pin<&mut NativeSetRemoteSdpObserverHandle>,
|
||||
ctx: Box<AsyncContext>,
|
||||
on_complete: fn(ctx: Box<AsyncContext>, error: RtcError),
|
||||
);
|
||||
|
||||
fn add_track(
|
||||
self: &PeerConnection,
|
||||
track: SharedPtr<MediaStreamTrack>,
|
||||
stream_ids: &Vec<String>,
|
||||
) -> Result<SharedPtr<RtpSender>>;
|
||||
|
||||
fn remove_track(self: &PeerConnection, sender: SharedPtr<RtpSender>) -> Result<()>;
|
||||
|
||||
fn add_transceiver(
|
||||
self: &PeerConnection,
|
||||
track: SharedPtr<MediaStreamTrack>,
|
||||
init: RtpTransceiverInit,
|
||||
) -> Result<SharedPtr<RtpTransceiver>>;
|
||||
|
||||
fn add_transceiver_for_media(
|
||||
self: &PeerConnection,
|
||||
media_type: MediaType,
|
||||
init: RtpTransceiverInit,
|
||||
) -> Result<SharedPtr<RtpTransceiver>>;
|
||||
|
||||
fn get_senders(self: &PeerConnection) -> Vec<RtpSenderPtr>;
|
||||
|
||||
fn get_receivers(self: &PeerConnection) -> Vec<RtpReceiverPtr>;
|
||||
|
||||
fn get_transceivers(self: &PeerConnection) -> Vec<RtpTransceiverPtr>;
|
||||
|
||||
fn create_data_channel(
|
||||
self: &PeerConnection,
|
||||
label: String,
|
||||
init: UniquePtr<NativeDataChannelInit>,
|
||||
init: DataChannelInit,
|
||||
) -> Result<SharedPtr<DataChannel>>;
|
||||
|
||||
fn add_ice_candidate(
|
||||
self: &PeerConnection,
|
||||
candidate: SharedPtr<IceCandidate>,
|
||||
observer: Pin<&mut NativeAddIceCandidateObserver>,
|
||||
ctx: Box<AsyncContext>,
|
||||
on_complete: fn(ctx: Box<AsyncContext>, error: RtcError),
|
||||
);
|
||||
|
||||
fn current_local_description(self: &PeerConnection) -> UniquePtr<SessionDescription>;
|
||||
|
||||
fn current_remote_description(self: &PeerConnection) -> UniquePtr<SessionDescription>;
|
||||
|
||||
fn connection_state(self: &PeerConnection) -> PeerConnectionState;
|
||||
|
||||
fn signaling_state(self: &PeerConnection) -> SignalingState;
|
||||
|
||||
fn ice_gathering_state(self: &PeerConnection) -> IceGatheringState;
|
||||
|
||||
fn ice_connection_state(self: &PeerConnection) -> IceConnectionState;
|
||||
|
||||
fn close(self: &PeerConnection);
|
||||
|
||||
fn create_native_peer_connection_observer(
|
||||
rtc_runtime: SharedPtr<RTCRuntime>,
|
||||
observer: Box<PeerConnectionObserverWrapper>,
|
||||
) -> SharedPtr<NativePeerConnectionObserver>;
|
||||
|
||||
fn create_native_add_ice_candidate_observer(
|
||||
observer: Box<AddIceCandidateObserverWrapper>,
|
||||
) -> UniquePtr<NativeAddIceCandidateObserver>;
|
||||
|
||||
fn _shared_peer_connection() -> SharedPtr<PeerConnection>; // Ignore
|
||||
}
|
||||
|
||||
extern "Rust" {
|
||||
type AddIceCandidateObserverWrapper;
|
||||
|
||||
fn on_complete(self: &AddIceCandidateObserverWrapper, error: RTCError);
|
||||
|
||||
type AsyncContext;
|
||||
type PeerConnectionObserverWrapper;
|
||||
|
||||
fn on_signaling_change(self: &PeerConnectionObserverWrapper, new_state: SignalingState);
|
||||
@@ -285,20 +250,16 @@ pub mod ffi {
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct AsyncContext(pub Box<dyn Any + Send>);
|
||||
|
||||
// https://webrtc.github.io/webrtc-org/native-code/native-apis/
|
||||
impl_thread_safety!(ffi::PeerConnection, Send + Sync);
|
||||
impl_thread_safety!(ffi::NativePeerConnectionObserver, Send + Sync);
|
||||
impl_thread_safety!(ffi::NativeAddIceCandidateObserver, Send + Sync);
|
||||
impl_thread_safety!(ffi::NativeSetRemoteSdpObserverHandle, Send + Sync);
|
||||
impl_thread_safety!(ffi::NativeSetLocalSdpObserverHandle, Send + Sync);
|
||||
impl_thread_safety!(ffi::NativeCreateSdpObserverHandle, Send + Sync);
|
||||
|
||||
impl Default for ffi::RTCOfferAnswerOptions {
|
||||
/*
|
||||
static const int kUndefined = -1;
|
||||
static const int kMaxOfferToReceiveMedia = 1;
|
||||
static const int kOfferToReceiveMediaTrue = 1;
|
||||
*/
|
||||
impl Default for ffi::RtcOfferAnswerOptions {
|
||||
// static const int kUndefined = -1;
|
||||
// static const int kMaxOfferToReceiveMedia = 1;
|
||||
// static const int kOfferToReceiveMediaTrue = 1;
|
||||
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
@@ -314,16 +275,6 @@ impl Default for ffi::RTCOfferAnswerOptions {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AddIceCandidateObserverWrapper(pub ManuallyDrop<Box<dyn FnOnce(RTCError) + Send>>);
|
||||
|
||||
impl AddIceCandidateObserverWrapper {
|
||||
fn on_complete(&self, error: RTCError) {
|
||||
unsafe {
|
||||
std::ptr::read(&*self.0)(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait PeerConnectionObserver: Send + Sync {
|
||||
fn on_signaling_change(&self, new_state: ffi::SignalingState);
|
||||
fn on_add_stream(&self, stream: SharedPtr<MediaStream>);
|
||||
@@ -353,7 +304,8 @@ pub trait PeerConnectionObserver: Send + Sync {
|
||||
fn on_interesting_usage(&self, usage_pattern: i32);
|
||||
}
|
||||
|
||||
// Thread safety is handled inside PeerConnectionObserver
|
||||
// Wrapper for PeerConnectionObserver because cxx doesn't support dyn Trait on c++
|
||||
// https://github.com/dtolnay/cxx/issues/665
|
||||
pub struct PeerConnectionObserverWrapper {
|
||||
observer: Arc<dyn PeerConnectionObserver>,
|
||||
}
|
||||
@@ -420,14 +372,9 @@ impl PeerConnectionObserverWrapper {
|
||||
.on_ice_candidate_error(address, port, url, error_code, error_text);
|
||||
}
|
||||
|
||||
fn on_ice_candidates_removed(&self, removed: Vec<ffi::CandidatePtr>) {
|
||||
let mut vec = Vec::new();
|
||||
|
||||
for v in removed {
|
||||
vec.push(v.ptr);
|
||||
}
|
||||
|
||||
self.observer.on_ice_candidates_removed(vec);
|
||||
fn on_ice_candidates_removed(&self, candidates: Vec<ffi::CandidatePtr>) {
|
||||
self.observer
|
||||
.on_ice_candidates_removed(candidates.into_iter().map(|v| v.ptr).collect());
|
||||
}
|
||||
|
||||
fn on_ice_connection_receiving_change(&self, receiving: bool) {
|
||||
@@ -439,13 +386,8 @@ impl PeerConnectionObserverWrapper {
|
||||
}
|
||||
|
||||
fn on_add_track(&self, receiver: SharedPtr<RtpReceiver>, streams: Vec<ffi::MediaStreamPtr>) {
|
||||
let mut vec = Vec::new();
|
||||
|
||||
for v in streams {
|
||||
vec.push(v.ptr);
|
||||
}
|
||||
|
||||
self.observer.on_add_track(receiver, vec);
|
||||
self.observer
|
||||
.on_add_track(receiver, streams.into_iter().map(|v| v.ptr).collect());
|
||||
}
|
||||
|
||||
fn on_track(&self, transceiver: SharedPtr<RtpTransceiver>) {
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
#include "livekit/peer_connection_factory.h"
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include "api/audio_codecs/builtin_audio_decoder_factory.h"
|
||||
@@ -26,19 +27,49 @@
|
||||
#include "api/video_codecs/builtin_video_decoder_factory.h"
|
||||
#include "api/video_codecs/builtin_video_encoder_factory.h"
|
||||
#include "livekit/audio_device.h"
|
||||
#include "livekit/peer_connection.h"
|
||||
#include "livekit/rtc_error.h"
|
||||
#include "livekit/rtp_parameters.h"
|
||||
#include "livekit/video_decoder_factory.h"
|
||||
#include "livekit/video_encoder_factory.h"
|
||||
#include "livekit/webrtc.h"
|
||||
#include "media/engine/webrtc_media_engine.h"
|
||||
#include "rtc_base/location.h"
|
||||
#include "rtc_base/thread.h"
|
||||
#include "webrtc-sys/src/peer_connection.rs.h"
|
||||
#include "webrtc-sys/src/peer_connection_factory.rs.h"
|
||||
|
||||
namespace livekit {
|
||||
|
||||
webrtc::PeerConnectionInterface::RTCConfiguration to_native_rtc_configuration(
|
||||
RtcConfiguration config) {
|
||||
webrtc::PeerConnectionInterface::RTCConfiguration rtc_config{};
|
||||
|
||||
for (auto item : config.ice_servers) {
|
||||
webrtc::PeerConnectionInterface::IceServer ice_server;
|
||||
ice_server.username = item.username.c_str();
|
||||
ice_server.password = item.password.c_str();
|
||||
|
||||
for (auto url : item.urls)
|
||||
ice_server.urls.emplace_back(url.c_str());
|
||||
|
||||
rtc_config.servers.push_back(ice_server);
|
||||
}
|
||||
|
||||
rtc_config.continual_gathering_policy =
|
||||
static_cast<webrtc::PeerConnectionInterface::ContinualGatheringPolicy>(
|
||||
config.continual_gathering_policy);
|
||||
|
||||
rtc_config.type =
|
||||
static_cast<webrtc::PeerConnectionInterface::IceTransportsType>(
|
||||
config.ice_transport_type);
|
||||
|
||||
return rtc_config;
|
||||
}
|
||||
|
||||
PeerConnectionFactory::PeerConnectionFactory(
|
||||
std::shared_ptr<RTCRuntime> rtc_runtime)
|
||||
: rtc_runtime_(std::move(rtc_runtime)) {
|
||||
std::shared_ptr<RtcRuntime> rtc_runtime)
|
||||
: rtc_runtime_(rtc_runtime) {
|
||||
RTC_LOG(LS_INFO) << "PeerConnectionFactory::PeerConnectionFactory()";
|
||||
|
||||
webrtc::PeerConnectionFactoryDependencies dependencies;
|
||||
@@ -87,31 +118,35 @@ PeerConnectionFactory::~PeerConnectionFactory() {
|
||||
}
|
||||
|
||||
std::shared_ptr<PeerConnection> PeerConnectionFactory::create_peer_connection(
|
||||
std::unique_ptr<webrtc::PeerConnectionInterface::RTCConfiguration> config,
|
||||
NativePeerConnectionObserver* observer) const {
|
||||
webrtc::PeerConnectionDependencies deps{observer};
|
||||
auto result =
|
||||
peer_factory_->CreatePeerConnectionOrError(*config, std::move(deps));
|
||||
RtcConfiguration config,
|
||||
std::unique_ptr<NativePeerConnectionObserver> observer) const {
|
||||
observer->rtc_runtime_ = rtc_runtime_; // See peer_connection.h
|
||||
webrtc::PeerConnectionDependencies deps{observer.get()};
|
||||
auto result = peer_factory_->CreatePeerConnectionOrError(
|
||||
to_native_rtc_configuration(config), std::move(deps));
|
||||
|
||||
if (!result.ok()) {
|
||||
throw std::runtime_error(serialize_error(to_error(result.error())));
|
||||
}
|
||||
|
||||
return std::make_shared<PeerConnection>(rtc_runtime_, result.value());
|
||||
return std::make_shared<PeerConnection>(rtc_runtime_, std::move(observer),
|
||||
result.value());
|
||||
}
|
||||
|
||||
std::shared_ptr<VideoTrack> PeerConnectionFactory::create_video_track(
|
||||
rust::String label,
|
||||
std::shared_ptr<AdaptedVideoTrackSource> source) const {
|
||||
return std::make_shared<VideoTrack>(
|
||||
peer_factory_->CreateVideoTrack(label.c_str(), source->get().get()));
|
||||
std::shared_ptr<VideoTrackSource> source) const {
|
||||
return std::static_pointer_cast<VideoTrack>(
|
||||
rtc_runtime_->get_or_create_media_stream_track(
|
||||
peer_factory_->CreateVideoTrack(label.c_str(), source->get().get())));
|
||||
}
|
||||
|
||||
std::shared_ptr<AudioTrack> PeerConnectionFactory::create_audio_track(
|
||||
rust::String label,
|
||||
std::shared_ptr<AudioTrackSource> source) const {
|
||||
return std::make_shared<AudioTrack>(
|
||||
peer_factory_->CreateAudioTrack(label.c_str(), source->get().get()));
|
||||
return std::static_pointer_cast<AudioTrack>(
|
||||
rtc_runtime_->get_or_create_media_stream_track(
|
||||
peer_factory_->CreateAudioTrack(label.c_str(), source->get().get())));
|
||||
}
|
||||
|
||||
RtpCapabilities PeerConnectionFactory::get_rtp_sender_capabilities(
|
||||
@@ -126,35 +161,8 @@ RtpCapabilities PeerConnectionFactory::get_rtp_receiver_capabilities(
|
||||
static_cast<cricket::MediaType>(type)));
|
||||
}
|
||||
|
||||
std::shared_ptr<PeerConnectionFactory> create_peer_connection_factory(
|
||||
std::shared_ptr<RTCRuntime> rtc_runtime) {
|
||||
return std::make_shared<PeerConnectionFactory>(std::move(rtc_runtime));
|
||||
std::shared_ptr<PeerConnectionFactory> create_peer_connection_factory() {
|
||||
return std::make_shared<PeerConnectionFactory>(RtcRuntime::create());
|
||||
}
|
||||
|
||||
std::unique_ptr<NativeRTCConfiguration> create_rtc_configuration(
|
||||
RTCConfiguration conf) {
|
||||
auto rtc =
|
||||
std::make_unique<webrtc::PeerConnectionInterface::RTCConfiguration>();
|
||||
|
||||
for (auto item : conf.ice_servers) {
|
||||
webrtc::PeerConnectionInterface::IceServer ice_server;
|
||||
ice_server.username = item.username.c_str();
|
||||
ice_server.password = item.password.c_str();
|
||||
|
||||
for (auto url : item.urls) {
|
||||
ice_server.urls.emplace_back(url.c_str());
|
||||
}
|
||||
|
||||
rtc->servers.push_back(ice_server);
|
||||
}
|
||||
|
||||
rtc->continual_gathering_policy =
|
||||
static_cast<webrtc::PeerConnectionInterface::ContinualGatheringPolicy>(
|
||||
conf.continual_gathering_policy);
|
||||
|
||||
rtc->type = static_cast<webrtc::PeerConnectionInterface::IceTransportsType>(
|
||||
conf.ice_transport_type);
|
||||
|
||||
return rtc;
|
||||
}
|
||||
} // namespace livekit
|
||||
|
||||
@@ -2,21 +2,18 @@ use crate::impl_thread_safety;
|
||||
|
||||
#[cxx::bridge(namespace = "livekit")]
|
||||
pub mod ffi {
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ICEServer {
|
||||
pub struct IceServer {
|
||||
pub urls: Vec<String>,
|
||||
pub username: String,
|
||||
pub password: String,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[repr(i32)]
|
||||
pub enum ContinualGatheringPolicy {
|
||||
GatherOnce,
|
||||
GatherContinually,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[repr(i32)]
|
||||
pub enum IceTransportsType {
|
||||
None,
|
||||
@@ -25,9 +22,8 @@ pub mod ffi {
|
||||
All,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RTCConfiguration {
|
||||
pub ice_servers: Vec<ICEServer>,
|
||||
pub struct RtcConfiguration {
|
||||
pub ice_servers: Vec<IceServer>,
|
||||
pub continual_gathering_policy: ContinualGatheringPolicy,
|
||||
pub ice_transport_type: IceTransportsType,
|
||||
}
|
||||
@@ -37,41 +33,34 @@ pub mod ffi {
|
||||
include!("livekit/webrtc.h");
|
||||
include!("livekit/rtp_parameters.h");
|
||||
|
||||
type AudioTrackSource = crate::media_stream::ffi::AudioTrackSource;
|
||||
type AdaptedVideoTrackSource = crate::media_stream::ffi::AdaptedVideoTrackSource;
|
||||
type AudioTrack = crate::media_stream::ffi::AudioTrack;
|
||||
type VideoTrack = crate::media_stream::ffi::VideoTrack;
|
||||
type AudioTrackSource = crate::audio_track::ffi::AudioTrackSource;
|
||||
type VideoTrackSource = crate::video_track::ffi::VideoTrackSource;
|
||||
type AudioTrack = crate::audio_track::ffi::AudioTrack;
|
||||
type VideoTrack = crate::video_track::ffi::VideoTrack;
|
||||
type RtpCapabilities = crate::rtp_parameters::ffi::RtpCapabilities;
|
||||
type MediaType = crate::webrtc::ffi::MediaType;
|
||||
type NativePeerConnectionObserver =
|
||||
crate::peer_connection::ffi::NativePeerConnectionObserver;
|
||||
}
|
||||
|
||||
unsafe extern "C++" {
|
||||
include!("livekit/peer_connection_factory.h");
|
||||
|
||||
type PeerConnection = crate::peer_connection::ffi::PeerConnection;
|
||||
type NativePeerConnectionObserver =
|
||||
crate::peer_connection::ffi::NativePeerConnectionObserver;
|
||||
type PeerConnectionFactory;
|
||||
type NativeRTCConfiguration;
|
||||
type RTCRuntime = crate::webrtc::ffi::RTCRuntime;
|
||||
|
||||
fn create_peer_connection_factory(
|
||||
runtime: SharedPtr<RTCRuntime>,
|
||||
) -> SharedPtr<PeerConnectionFactory>;
|
||||
fn create_rtc_configuration(conf: RTCConfiguration) -> UniquePtr<NativeRTCConfiguration>;
|
||||
fn create_peer_connection_factory() -> SharedPtr<PeerConnectionFactory>;
|
||||
|
||||
/// # Safety
|
||||
/// The observer must live as long as the PeerConnection does
|
||||
unsafe fn create_peer_connection(
|
||||
fn create_peer_connection(
|
||||
self: &PeerConnectionFactory,
|
||||
config: UniquePtr<NativeRTCConfiguration>,
|
||||
observer: *mut NativePeerConnectionObserver,
|
||||
config: RtcConfiguration,
|
||||
observer: UniquePtr<NativePeerConnectionObserver>,
|
||||
) -> Result<SharedPtr<PeerConnection>>;
|
||||
|
||||
fn create_video_track(
|
||||
self: &PeerConnectionFactory,
|
||||
label: String,
|
||||
source: SharedPtr<AdaptedVideoTrackSource>,
|
||||
source: SharedPtr<VideoTrackSource>,
|
||||
) -> SharedPtr<VideoTrack>;
|
||||
|
||||
fn create_audio_track(
|
||||
|
||||
@@ -22,17 +22,17 @@
|
||||
|
||||
namespace livekit {
|
||||
|
||||
RTCError to_error(const webrtc::RTCError& error) {
|
||||
RTCError lk_error;
|
||||
lk_error.error_detail = static_cast<RTCErrorDetailType>(error.error_detail());
|
||||
lk_error.error_type = static_cast<RTCErrorType>(error.type());
|
||||
RtcError to_error(const webrtc::RTCError& error) {
|
||||
RtcError lk_error;
|
||||
lk_error.error_detail = static_cast<RtcErrorDetailType>(error.error_detail());
|
||||
lk_error.error_type = static_cast<RtcErrorType>(error.type());
|
||||
lk_error.has_sctp_cause_code = error.sctp_cause_code().has_value();
|
||||
lk_error.sctp_cause_code = error.sctp_cause_code().value_or(0);
|
||||
lk_error.message = error.message();
|
||||
return lk_error;
|
||||
}
|
||||
|
||||
std::string serialize_error(const RTCError& error) {
|
||||
std::string serialize_error(const RtcError& error) {
|
||||
std::stringstream ss;
|
||||
ss << std::hex << std::setfill('0');
|
||||
ss << std::setw(8) << (uint32_t)error.error_type;
|
||||
@@ -45,9 +45,9 @@ std::string serialize_error(const RTCError& error) {
|
||||
|
||||
#ifdef LIVEKIT_TEST
|
||||
rust::String serialize_deserialize() {
|
||||
RTCError lk_error;
|
||||
lk_error.error_type = RTCErrorType::InternalError;
|
||||
lk_error.error_detail = RTCErrorDetailType::DataChannelFailure;
|
||||
RtcError lk_error;
|
||||
lk_error.error_type = RtcErrorType::InternalError;
|
||||
lk_error.error_detail = RtcErrorDetailType::DataChannelFailure;
|
||||
lk_error.has_sctp_cause_code = true;
|
||||
lk_error.sctp_cause_code = 24;
|
||||
lk_error.message = "this is not a test, I repeat, this is not a test";
|
||||
@@ -55,9 +55,9 @@ rust::String serialize_deserialize() {
|
||||
}
|
||||
|
||||
void throw_error() {
|
||||
RTCError lk_error;
|
||||
lk_error.error_type = RTCErrorType::InvalidModification;
|
||||
lk_error.error_detail = RTCErrorDetailType::None;
|
||||
RtcError lk_error;
|
||||
lk_error.error_type = RtcErrorType::InvalidModification;
|
||||
lk_error.error_detail = RtcErrorDetailType::None;
|
||||
lk_error.has_sctp_cause_code = false;
|
||||
lk_error.sctp_cause_code = 0;
|
||||
lk_error.message = "exception is thrown!";
|
||||
|
||||
+18
-20
@@ -1,15 +1,13 @@
|
||||
use std::error::Error;
|
||||
use std::fmt::{Display, Formatter};
|
||||
|
||||
use crate::rtc_error::ffi::RTCErrorType;
|
||||
|
||||
// cxx doesn't support custom Exception type, so we serialize RTCError inside the cxx::Exception "what" string
|
||||
// cxx doesn't support custom Exception type, so we serialize RtcError inside the cxx::Exception "what" string
|
||||
|
||||
#[cxx::bridge(namespace = "livekit")]
|
||||
pub mod ffi {
|
||||
#[derive(Debug)]
|
||||
#[repr(i32)]
|
||||
pub enum RTCErrorType {
|
||||
pub enum RtcErrorType {
|
||||
None,
|
||||
UnsupportedOperation,
|
||||
UnsupportedParameter,
|
||||
@@ -26,7 +24,7 @@ pub mod ffi {
|
||||
|
||||
#[derive(Debug)]
|
||||
#[repr(i32)]
|
||||
pub enum RTCErrorDetailType {
|
||||
pub enum RtcErrorDetailType {
|
||||
None,
|
||||
DataChannelFailure,
|
||||
DtlsFailure,
|
||||
@@ -38,17 +36,17 @@ pub mod ffi {
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct RTCError {
|
||||
pub error_type: RTCErrorType,
|
||||
pub struct RtcError {
|
||||
pub error_type: RtcErrorType,
|
||||
pub message: String,
|
||||
pub error_detail: RTCErrorDetailType,
|
||||
pub has_sctp_cause_code: bool,
|
||||
pub error_detail: RtcErrorDetailType,
|
||||
// cxx doesn't support the Option trait
|
||||
pub has_sctp_cause_code: bool,
|
||||
pub sctp_cause_code: u16,
|
||||
}
|
||||
}
|
||||
|
||||
impl ffi::RTCError {
|
||||
impl ffi::RtcError {
|
||||
/// # Safety
|
||||
/// The value must be correctly encoded
|
||||
pub unsafe fn from(value: &str) -> Self {
|
||||
@@ -69,13 +67,13 @@ impl ffi::RTCError {
|
||||
}
|
||||
|
||||
pub fn ok(&self) -> bool {
|
||||
self.error_type == RTCErrorType::None
|
||||
self.error_type == ffi::RtcErrorType::None
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for ffi::RTCError {}
|
||||
impl Error for ffi::RtcError {}
|
||||
|
||||
impl Display for ffi::RTCError {
|
||||
impl Display for ffi::RtcError {
|
||||
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
@@ -87,7 +85,7 @@ impl Display for ffi::RTCError {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::rtc_error::ffi::{RTCError, RTCErrorDetailType, RTCErrorType};
|
||||
use crate::rtc_error::ffi::{RtcError, RtcErrorDetailType, RtcErrorType};
|
||||
|
||||
#[cxx::bridge(namespace = "livekit")]
|
||||
pub mod ffi {
|
||||
@@ -102,10 +100,10 @@ mod tests {
|
||||
#[test]
|
||||
fn serialize_deserialize() {
|
||||
let str = ffi::serialize_deserialize();
|
||||
let error = unsafe { RTCError::from(&str) };
|
||||
let error = unsafe { RtcError::from(&str) };
|
||||
|
||||
assert_eq!(error.error_type, RTCErrorType::InternalError);
|
||||
assert_eq!(error.error_detail, RTCErrorDetailType::DataChannelFailure);
|
||||
assert_eq!(error.error_type, RtcErrorType::InternalError);
|
||||
assert_eq!(error.error_detail, RtcErrorDetailType::DataChannelFailure);
|
||||
assert_eq!(error.has_sctp_cause_code, true);
|
||||
assert_eq!(error.sctp_cause_code, 24);
|
||||
assert_eq!(
|
||||
@@ -117,10 +115,10 @@ mod tests {
|
||||
#[test]
|
||||
fn throw_error() {
|
||||
let exc: cxx::Exception = ffi::throw_error().err().unwrap();
|
||||
let error = unsafe { RTCError::from(exc.what()) };
|
||||
let error = unsafe { RtcError::from(exc.what()) };
|
||||
|
||||
assert_eq!(error.error_type, RTCErrorType::InvalidModification);
|
||||
assert_eq!(error.error_detail, RTCErrorDetailType::None);
|
||||
assert_eq!(error.error_type, RtcErrorType::InvalidModification);
|
||||
assert_eq!(error.error_detail, RtcErrorDetailType::None);
|
||||
assert_eq!(error.has_sctp_cause_code, false);
|
||||
assert_eq!(error.sctp_cause_code, 0);
|
||||
assert_eq!(error.message, "exception is thrown!");
|
||||
|
||||
@@ -183,7 +183,7 @@ pub mod ffi {
|
||||
}
|
||||
|
||||
extern "C++" {
|
||||
include!("webrtc-sys/src/webrtc.rs.h");
|
||||
include!("livekit/webrtc.h");
|
||||
|
||||
type Priority = crate::webrtc::ffi::Priority;
|
||||
type MediaType = crate::webrtc::ffi::MediaType;
|
||||
|
||||
@@ -16,16 +16,19 @@
|
||||
|
||||
#include "livekit/rtp_receiver.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "absl/types/optional.h"
|
||||
|
||||
namespace livekit {
|
||||
|
||||
RtpReceiver::RtpReceiver(
|
||||
std::shared_ptr<RtcRuntime> rtc_runtime,
|
||||
rtc::scoped_refptr<webrtc::RtpReceiverInterface> receiver)
|
||||
: receiver_(std::move(receiver)) {}
|
||||
: rtc_runtime_(rtc_runtime), receiver_(std::move(receiver)) {}
|
||||
|
||||
std::shared_ptr<MediaStreamTrack> RtpReceiver::track() const {
|
||||
return MediaStreamTrack::from(receiver_->track());
|
||||
return rtc_runtime_->get_or_create_media_stream_track(receiver_->track());
|
||||
}
|
||||
|
||||
rust::Vec<rust::String> RtpReceiver::stream_ids() const {
|
||||
@@ -38,7 +41,8 @@ rust::Vec<rust::String> RtpReceiver::stream_ids() const {
|
||||
rust::Vec<MediaStreamPtr> RtpReceiver::streams() const {
|
||||
rust::Vec<MediaStreamPtr> rust;
|
||||
for (auto stream : receiver_->streams())
|
||||
rust.push_back(MediaStreamPtr{std::make_shared<MediaStream>(stream)});
|
||||
rust.push_back(
|
||||
MediaStreamPtr{std::make_shared<MediaStream>(rtc_runtime_, stream)});
|
||||
return rust;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,15 +18,16 @@
|
||||
|
||||
namespace livekit {
|
||||
|
||||
RtpSender::RtpSender(rtc::scoped_refptr<webrtc::RtpSenderInterface> sender)
|
||||
: sender_(std::move(sender)) {}
|
||||
RtpSender::RtpSender(std::shared_ptr<RtcRuntime> rtc_runtime,
|
||||
rtc::scoped_refptr<webrtc::RtpSenderInterface> sender)
|
||||
: rtc_runtime_(rtc_runtime), sender_(std::move(sender)) {}
|
||||
|
||||
bool RtpSender::set_track(std::shared_ptr<MediaStreamTrack> track) const {
|
||||
return sender_->SetTrack(track->get().get());
|
||||
return sender_->SetTrack(track->rtc_track().get());
|
||||
}
|
||||
|
||||
std::shared_ptr<MediaStreamTrack> RtpSender::track() const {
|
||||
return MediaStreamTrack::from(sender_->track());
|
||||
return rtc_runtime_->get_or_create_media_stream_track(sender_->track());
|
||||
}
|
||||
|
||||
uint32_t RtpSender::ssrc() const {
|
||||
|
||||
@@ -34,8 +34,9 @@ webrtc::RtpTransceiverInit to_native_rtp_transceiver_init(
|
||||
}
|
||||
|
||||
RtpTransceiver::RtpTransceiver(
|
||||
std::shared_ptr<RtcRuntime> rtc_runtime,
|
||||
rtc::scoped_refptr<webrtc::RtpTransceiverInterface> transceiver)
|
||||
: transceiver_(std::move(transceiver)) {}
|
||||
: rtc_runtime_(rtc_runtime), transceiver_(std::move(transceiver)) {}
|
||||
|
||||
MediaType RtpTransceiver::media_type() const {
|
||||
return static_cast<MediaType>(transceiver_->media_type());
|
||||
@@ -48,11 +49,11 @@ rust::String RtpTransceiver::mid() const {
|
||||
}
|
||||
|
||||
std::shared_ptr<RtpSender> RtpTransceiver::sender() const {
|
||||
return std::make_shared<RtpSender>(transceiver_->sender());
|
||||
return std::make_shared<RtpSender>(rtc_runtime_, transceiver_->sender());
|
||||
}
|
||||
|
||||
std::shared_ptr<RtpReceiver> RtpTransceiver::receiver() const {
|
||||
return std::make_shared<RtpReceiver>(transceiver_->receiver());
|
||||
return std::make_shared<RtpReceiver>(rtc_runtime_, transceiver_->receiver());
|
||||
}
|
||||
|
||||
bool RtpTransceiver::stopped() const {
|
||||
|
||||
@@ -24,7 +24,7 @@ pub mod ffi {
|
||||
crate::rtp_parameters::ffi::RtpHeaderExtensionCapability;
|
||||
type RtpSender = crate::rtp_sender::ffi::RtpSender;
|
||||
type RtpReceiver = crate::rtp_receiver::ffi::RtpReceiver;
|
||||
type RTCError = crate::rtc_error::ffi::RTCError;
|
||||
type RtcError = crate::rtc_error::ffi::RtcError;
|
||||
}
|
||||
|
||||
unsafe extern "C++" {
|
||||
|
||||
@@ -0,0 +1,188 @@
|
||||
/*
|
||||
* Copyright 2023 LiveKit
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the “License”);
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an “AS IS” BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "livekit/video_track.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
|
||||
#include "api/media_stream_interface.h"
|
||||
#include "api/video/video_frame.h"
|
||||
#include "api/video/video_rotation.h"
|
||||
#include "audio/remix_resample.h"
|
||||
#include "common_audio/include/audio_util.h"
|
||||
#include "livekit/media_stream.h"
|
||||
#include "livekit/video_track.h"
|
||||
#include "rtc_base/logging.h"
|
||||
#include "rtc_base/ref_counted_object.h"
|
||||
#include "rtc_base/synchronization/mutex.h"
|
||||
#include "rtc_base/time_utils.h"
|
||||
|
||||
namespace livekit {
|
||||
|
||||
VideoTrack::VideoTrack(std::shared_ptr<RtcRuntime> rtc_runtime,
|
||||
rtc::scoped_refptr<webrtc::VideoTrackInterface> track)
|
||||
: MediaStreamTrack(rtc_runtime, std::move(track)) {}
|
||||
|
||||
VideoTrack::~VideoTrack() {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
for (auto& sink : sinks_) {
|
||||
track()->RemoveSink(sink.get());
|
||||
}
|
||||
}
|
||||
|
||||
void VideoTrack::add_sink(const std::shared_ptr<NativeVideoSink>& sink) const {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
track()->AddOrUpdateSink(sink.get(),
|
||||
rtc::VideoSinkWants()); // TODO(theomonnom): Expose
|
||||
// VideoSinkWants to Rust?
|
||||
sinks_.push_back(sink);
|
||||
}
|
||||
|
||||
void VideoTrack::remove_sink(
|
||||
const std::shared_ptr<NativeVideoSink>& sink) const {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
track()->RemoveSink(sink.get());
|
||||
sinks_.erase(std::remove(sinks_.begin(), sinks_.end(), sink), sinks_.end());
|
||||
}
|
||||
|
||||
void VideoTrack::set_should_receive(bool should_receive) const {
|
||||
track()->set_should_receive(should_receive);
|
||||
}
|
||||
|
||||
bool VideoTrack::should_receive() const {
|
||||
return track()->should_receive();
|
||||
}
|
||||
|
||||
ContentHint VideoTrack::content_hint() const {
|
||||
return static_cast<ContentHint>(track()->content_hint());
|
||||
}
|
||||
|
||||
void VideoTrack::set_content_hint(ContentHint hint) const {
|
||||
track()->set_content_hint(
|
||||
static_cast<webrtc::VideoTrackInterface::ContentHint>(hint));
|
||||
}
|
||||
|
||||
NativeVideoSink::NativeVideoSink(rust::Box<VideoSinkWrapper> observer)
|
||||
: observer_(std::move(observer)) {}
|
||||
|
||||
void NativeVideoSink::OnFrame(const webrtc::VideoFrame& frame) {
|
||||
observer_->on_frame(std::make_unique<VideoFrame>(frame));
|
||||
}
|
||||
|
||||
void NativeVideoSink::OnDiscardedFrame() {
|
||||
observer_->on_discarded_frame();
|
||||
}
|
||||
|
||||
void NativeVideoSink::OnConstraintsChanged(
|
||||
const webrtc::VideoTrackSourceConstraints& constraints) {
|
||||
VideoTrackSourceConstraints cst;
|
||||
cst.has_min_fps = constraints.min_fps.has_value();
|
||||
cst.min_fps = constraints.min_fps.value_or(0);
|
||||
cst.has_max_fps = constraints.max_fps.has_value();
|
||||
cst.max_fps = constraints.max_fps.value_or(0);
|
||||
observer_->on_constraints_changed(cst);
|
||||
}
|
||||
|
||||
std::shared_ptr<NativeVideoSink> new_native_video_sink(
|
||||
rust::Box<VideoSinkWrapper> observer) {
|
||||
return std::make_shared<NativeVideoSink>(std::move(observer));
|
||||
}
|
||||
|
||||
VideoTrackSource::InternalSource::InternalSource()
|
||||
: rtc::AdaptedVideoTrackSource(4) {}
|
||||
|
||||
VideoTrackSource::InternalSource::~InternalSource() {}
|
||||
|
||||
bool VideoTrackSource::InternalSource::is_screencast() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
absl::optional<bool> VideoTrackSource::InternalSource::needs_denoising() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
webrtc::MediaSourceInterface::SourceState
|
||||
VideoTrackSource::InternalSource::state() const {
|
||||
return SourceState::kLive;
|
||||
}
|
||||
|
||||
bool VideoTrackSource::InternalSource::remote() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool VideoTrackSource::InternalSource::on_captured_frame(
|
||||
const webrtc::VideoFrame& frame) {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
|
||||
int64_t aligned_timestamp_us = timestamp_aligner_.TranslateTimestamp(
|
||||
frame.timestamp_us(), rtc::TimeMicros());
|
||||
|
||||
rtc::scoped_refptr<webrtc::VideoFrameBuffer> buffer =
|
||||
frame.video_frame_buffer();
|
||||
|
||||
int adapted_width, adapted_height, crop_width, crop_height, crop_x, crop_y;
|
||||
if (!AdaptFrame(buffer->width(), buffer->height(), aligned_timestamp_us,
|
||||
&adapted_width, &adapted_height, &crop_width, &crop_height,
|
||||
&crop_x, &crop_y)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (adapted_width != frame.width() || adapted_height != frame.height()) {
|
||||
buffer = buffer->CropAndScale(crop_x, crop_y, crop_width, crop_height,
|
||||
adapted_width, adapted_height);
|
||||
}
|
||||
|
||||
webrtc::VideoRotation rotation = frame.rotation();
|
||||
if (apply_rotation() && rotation != webrtc::kVideoRotation_0) {
|
||||
// If the buffer is I420, rtc::AdaptedVideoTrackSource will handle the
|
||||
// rotation for us.
|
||||
buffer = buffer->ToI420();
|
||||
}
|
||||
|
||||
OnFrame(webrtc::VideoFrame::Builder()
|
||||
.set_video_frame_buffer(buffer)
|
||||
.set_rotation(rotation)
|
||||
.set_timestamp_us(aligned_timestamp_us)
|
||||
.build());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
VideoTrackSource::VideoTrackSource() {
|
||||
source_ = rtc::make_ref_counted<InternalSource>();
|
||||
}
|
||||
|
||||
bool VideoTrackSource::on_captured_frame(
|
||||
const std::unique_ptr<VideoFrame>& frame) const {
|
||||
auto rtc_frame = frame->get();
|
||||
rtc_frame.set_timestamp_us(
|
||||
rtc::TimeMicros()); // TODO(theomonnom): Expore capture ts to Rust
|
||||
|
||||
return source_->on_captured_frame(rtc_frame);
|
||||
}
|
||||
|
||||
rtc::scoped_refptr<VideoTrackSource::InternalSource> VideoTrackSource::get()
|
||||
const {
|
||||
return source_;
|
||||
}
|
||||
|
||||
std::shared_ptr<VideoTrackSource> new_video_track_source() {
|
||||
return std::make_shared<VideoTrackSource>();
|
||||
}
|
||||
|
||||
} // namespace livekit
|
||||
@@ -0,0 +1,97 @@
|
||||
use crate::impl_thread_safety;
|
||||
use crate::video_frame::ffi::VideoFrame;
|
||||
use cxx::UniquePtr;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[cxx::bridge(namespace = "livekit")]
|
||||
pub mod ffi {
|
||||
#[repr(i32)]
|
||||
pub enum ContentHint {
|
||||
None,
|
||||
Fluid,
|
||||
Detailed,
|
||||
Text,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct VideoTrackSourceConstraints {
|
||||
pub has_min_fps: bool,
|
||||
pub min_fps: f64,
|
||||
pub has_max_fps: bool,
|
||||
pub max_fps: f64,
|
||||
}
|
||||
|
||||
extern "C++" {
|
||||
include!("livekit/video_frame.h");
|
||||
include!("livekit/media_stream_track.h");
|
||||
|
||||
type VideoFrame = crate::video_frame::ffi::VideoFrame;
|
||||
type MediaStreamTrack = crate::media_stream_track::ffi::MediaStreamTrack;
|
||||
}
|
||||
|
||||
unsafe extern "C++" {
|
||||
include!("livekit/video_track.h");
|
||||
|
||||
type VideoTrack;
|
||||
type NativeVideoSink;
|
||||
type VideoTrackSource;
|
||||
|
||||
fn add_sink(self: &VideoTrack, sink: &SharedPtr<NativeVideoSink>);
|
||||
fn remove_sink(self: &VideoTrack, sink: &SharedPtr<NativeVideoSink>);
|
||||
fn set_should_receive(self: &VideoTrack, should_receive: bool);
|
||||
fn should_receive(self: &VideoTrack) -> bool;
|
||||
fn content_hint(self: &VideoTrack) -> ContentHint;
|
||||
fn set_content_hint(self: &VideoTrack, hint: ContentHint);
|
||||
fn new_native_video_sink(observer: Box<VideoSinkWrapper>) -> SharedPtr<NativeVideoSink>;
|
||||
|
||||
fn on_captured_frame(self: &VideoTrackSource, frame: &UniquePtr<VideoFrame>) -> bool;
|
||||
fn new_video_track_source() -> SharedPtr<VideoTrackSource>;
|
||||
|
||||
fn video_to_media(track: SharedPtr<VideoTrack>) -> SharedPtr<MediaStreamTrack>;
|
||||
unsafe fn media_to_video(track: SharedPtr<MediaStreamTrack>) -> SharedPtr<VideoTrack>;
|
||||
fn _shared_video_track() -> SharedPtr<VideoTrack>;
|
||||
}
|
||||
|
||||
extern "Rust" {
|
||||
type VideoSinkWrapper;
|
||||
|
||||
fn on_frame(self: &VideoSinkWrapper, frame: UniquePtr<VideoFrame>);
|
||||
fn on_discarded_frame(self: &VideoSinkWrapper);
|
||||
fn on_constraints_changed(
|
||||
self: &VideoSinkWrapper,
|
||||
constraints: VideoTrackSourceConstraints,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl_thread_safety!(ffi::VideoTrack, Send + Sync);
|
||||
impl_thread_safety!(ffi::NativeVideoSink, Send + Sync);
|
||||
impl_thread_safety!(ffi::VideoTrackSource, Send + Sync);
|
||||
|
||||
pub trait VideoSink: Send {
|
||||
fn on_frame(&self, frame: UniquePtr<VideoFrame>);
|
||||
fn on_discarded_frame(&self);
|
||||
fn on_constraints_changed(&self, constraints: ffi::VideoTrackSourceConstraints);
|
||||
}
|
||||
|
||||
pub struct VideoSinkWrapper {
|
||||
observer: Arc<dyn VideoSink>,
|
||||
}
|
||||
|
||||
impl VideoSinkWrapper {
|
||||
pub fn new(observer: Arc<dyn VideoSink>) -> Self {
|
||||
Self { observer }
|
||||
}
|
||||
|
||||
fn on_frame(&self, frame: UniquePtr<VideoFrame>) {
|
||||
self.observer.on_frame(frame);
|
||||
}
|
||||
|
||||
fn on_discarded_frame(&self) {
|
||||
self.observer.on_discarded_frame();
|
||||
}
|
||||
|
||||
fn on_constraints_changed(&self, constraints: ffi::VideoTrackSourceConstraints) {
|
||||
self.observer.on_constraints_changed(constraints);
|
||||
}
|
||||
}
|
||||
+86
-14
@@ -16,13 +16,20 @@
|
||||
|
||||
#include "livekit/webrtc.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "livekit/audio_track.h"
|
||||
#include "livekit/media_stream_track.h"
|
||||
#include "livekit/rtp_receiver.h"
|
||||
#include "livekit/rtp_sender.h"
|
||||
#include "livekit/video_track.h"
|
||||
#include "rtc_base/helpers.h"
|
||||
#include "rtc_base/logging.h"
|
||||
#include "rtc_base/synchronization/mutex.h"
|
||||
|
||||
namespace livekit {
|
||||
RTCRuntime::RTCRuntime() {
|
||||
// rtc::LogMessage::LogToDebug(rtc::LS_INFO);
|
||||
RTC_LOG(LS_INFO) << "RTCRuntime()";
|
||||
RtcRuntime::RtcRuntime() {
|
||||
RTC_LOG(LS_INFO) << "RtcRuntime()";
|
||||
RTC_CHECK(rtc::InitializeSSL()) << "Failed to InitializeSSL()";
|
||||
|
||||
network_thread_ = rtc::Thread::CreateWithSocketServer();
|
||||
@@ -36,34 +43,99 @@ RTCRuntime::RTCRuntime() {
|
||||
signaling_thread_->Start();
|
||||
}
|
||||
|
||||
RTCRuntime::~RTCRuntime() {
|
||||
RTC_LOG(LS_INFO) << "~RTCRuntime()";
|
||||
RtcRuntime::~RtcRuntime() {
|
||||
RTC_LOG(LS_INFO) << "~RtcRuntime()";
|
||||
|
||||
rtc::ThreadManager::Instance()->SetCurrentThread(nullptr);
|
||||
RTC_CHECK(rtc::CleanupSSL()) << "Failed to CleanupSSL()";
|
||||
|
||||
worker_thread_->Stop();
|
||||
signaling_thread_->Stop();
|
||||
network_thread_->Stop();
|
||||
worker_thread_->Quit();
|
||||
signaling_thread_->Quit();
|
||||
network_thread_->Quit();
|
||||
}
|
||||
|
||||
rtc::Thread* RTCRuntime::network_thread() const {
|
||||
rtc::Thread* RtcRuntime::network_thread() const {
|
||||
return network_thread_.get();
|
||||
}
|
||||
|
||||
rtc::Thread* RTCRuntime::worker_thread() const {
|
||||
rtc::Thread* RtcRuntime::worker_thread() const {
|
||||
return worker_thread_.get();
|
||||
}
|
||||
|
||||
rtc::Thread* RTCRuntime::signaling_thread() const {
|
||||
rtc::Thread* RtcRuntime::signaling_thread() const {
|
||||
return signaling_thread_.get();
|
||||
}
|
||||
|
||||
std::shared_ptr<MediaStreamTrack> RtcRuntime::get_or_create_media_stream_track(
|
||||
rtc::scoped_refptr<webrtc::MediaStreamTrackInterface> rtc_track) {
|
||||
webrtc::MutexLock lock(&mutex_);
|
||||
for (std::weak_ptr<MediaStreamTrack> weak_existing_track :
|
||||
media_stream_tracks_) {
|
||||
if (std::shared_ptr<MediaStreamTrack> existing_track =
|
||||
weak_existing_track.lock()) {
|
||||
if (existing_track->rtc_track() == rtc_track) {
|
||||
return existing_track;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (rtc_track->kind() == webrtc::MediaStreamTrackInterface::kVideoKind) {
|
||||
std::shared_ptr<VideoTrack> video_track =
|
||||
std::shared_ptr<VideoTrack>(new VideoTrack(
|
||||
shared_from_this(),
|
||||
rtc::scoped_refptr<webrtc::VideoTrackInterface>(
|
||||
static_cast<webrtc::VideoTrackInterface*>(rtc_track.get()))));
|
||||
|
||||
media_stream_tracks_.push_back(
|
||||
std::static_pointer_cast<MediaStreamTrack>(video_track));
|
||||
return video_track;
|
||||
} else {
|
||||
std::shared_ptr<AudioTrack> audio_track =
|
||||
std::shared_ptr<AudioTrack>(new AudioTrack(
|
||||
shared_from_this(),
|
||||
rtc::scoped_refptr<webrtc::AudioTrackInterface>(
|
||||
static_cast<webrtc::AudioTrackInterface*>(rtc_track.get()))));
|
||||
|
||||
media_stream_tracks_.push_back(
|
||||
std::static_pointer_cast<MediaStreamTrack>(audio_track));
|
||||
return audio_track;
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<AudioTrack> RtcRuntime::get_or_create_audio_track(
|
||||
rtc::scoped_refptr<webrtc::AudioTrackInterface> track) {
|
||||
return std::static_pointer_cast<AudioTrack>(
|
||||
get_or_create_media_stream_track(track));
|
||||
}
|
||||
|
||||
std::shared_ptr<VideoTrack> RtcRuntime::get_or_create_video_track(
|
||||
rtc::scoped_refptr<webrtc::VideoTrackInterface> track) {
|
||||
return std::static_pointer_cast<VideoTrack>(
|
||||
get_or_create_media_stream_track(track));
|
||||
}
|
||||
|
||||
LogSink::LogSink(
|
||||
rust::Fn<void(rust::String message, LoggingSeverity severity)> fnc)
|
||||
: fnc_(fnc) {
|
||||
rtc::LogMessage::AddLogToStream(this, rtc::LoggingSeverity::LS_VERBOSE);
|
||||
}
|
||||
|
||||
LogSink::~LogSink() {
|
||||
rtc::LogMessage::RemoveLogToStream(this);
|
||||
}
|
||||
|
||||
void LogSink::OnLogMessage(const std::string& message,
|
||||
rtc::LoggingSeverity severity) {
|
||||
fnc_(rust::String(message), static_cast<LoggingSeverity>(severity));
|
||||
}
|
||||
|
||||
std::unique_ptr<LogSink> new_log_sink(
|
||||
rust::Fn<void(rust::String, LoggingSeverity)> fnc) {
|
||||
return std::make_unique<LogSink>(fnc);
|
||||
}
|
||||
|
||||
rust::String create_random_uuid() {
|
||||
return rtc::CreateRandomUuid();
|
||||
}
|
||||
|
||||
std::shared_ptr<RTCRuntime> create_rtc_runtime() {
|
||||
return std::make_shared<RTCRuntime>();
|
||||
}
|
||||
} // namespace livekit
|
||||
|
||||
@@ -2,7 +2,6 @@ use crate::impl_thread_safety;
|
||||
|
||||
#[cxx::bridge(namespace = "livekit")]
|
||||
pub mod ffi {
|
||||
|
||||
#[derive(Debug)]
|
||||
#[repr(i32)]
|
||||
pub enum MediaType {
|
||||
@@ -31,14 +30,24 @@ pub mod ffi {
|
||||
Stopped,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[repr(i32)]
|
||||
pub enum LoggingSeverity {
|
||||
Verbose,
|
||||
Info,
|
||||
Warning,
|
||||
Error,
|
||||
None,
|
||||
}
|
||||
|
||||
unsafe extern "C++" {
|
||||
include!("livekit/webrtc.h");
|
||||
type RTCRuntime;
|
||||
|
||||
type LogSink;
|
||||
|
||||
fn create_random_uuid() -> String;
|
||||
|
||||
fn create_rtc_runtime() -> SharedPtr<RTCRuntime>;
|
||||
fn new_log_sink(fnc: fn(String, LoggingSeverity)) -> UniquePtr<LogSink>;
|
||||
}
|
||||
}
|
||||
|
||||
impl_thread_safety!(ffi::RTCRuntime, Send + Sync);
|
||||
impl_thread_safety!(ffi::LogSink, Send + Sync);
|
||||
|
||||
Reference in New Issue
Block a user