cleanup: webrtc-sys & fix RtcRuntime disposing crashes (#81)
This commit is contained in:
@@ -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