cleanup: webrtc-sys & fix RtcRuntime disposing crashes (#81)

This commit is contained in:
Théo Monnom
2023-06-04 01:02:00 +02:00
committed by GitHub
parent 05ad1c95af
commit 55bda13069
80 changed files with 2209 additions and 1954 deletions
+143
View File
@@ -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
+71
View File
@@ -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);
}
}
+36 -33
View File
@@ -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
+17 -31
View File
@@ -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
View File
@@ -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
View File
@@ -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;
+3 -1
View File
@@ -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;
-40
View File
@@ -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);
}
}
-24
View File
@@ -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
View File
@@ -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
+6 -167
View File
@@ -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);
}
}
}
+58
View File
@@ -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
+26
View File
@@ -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);
+90 -66
View File
@@ -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
+50 -108
View File
@@ -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>) {
+50 -42
View File
@@ -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
+14 -25
View File
@@ -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(
+11 -11
View File
@@ -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
View File
@@ -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!");
+1 -1
View File
@@ -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;
+7 -3
View File
@@ -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;
}
+5 -4
View File
@@ -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 {
+4 -3
View File
@@ -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 {
+1 -1
View File
@@ -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++" {
+188
View File
@@ -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
+97
View File
@@ -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
View File
@@ -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
+14 -5
View File
@@ -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);