feat: video publishing (#42)
- Prepare webrtc abstraction ( for future wasm support ) - Added track publish support for videos - Added LogoTrack example to simple_room demo - Lot of cleanup - There are compiler warnings I'll solve on our v1 release
This commit is contained in:
@@ -1,152 +1,9 @@
|
||||
use std::error::Error;
|
||||
use std::fmt::{Debug, Display, Formatter};
|
||||
use std::sync::Mutex;
|
||||
use crate::{imp::data_channel as dc_imp, rtp_parameters::Priority};
|
||||
use std::{fmt::Debug, str::Utf8Error};
|
||||
use thiserror::Error;
|
||||
|
||||
use cxx::UniquePtr;
|
||||
use log::trace;
|
||||
|
||||
pub use sys_dc::ffi::{DataState, Priority};
|
||||
use webrtc_sys::data_channel as sys_dc;
|
||||
|
||||
pub struct DataChannel {
|
||||
cxx_handle: UniquePtr<sys_dc::ffi::DataChannel>,
|
||||
observer: Box<InternalDataChannelObserver>,
|
||||
|
||||
// Keep alive for C++
|
||||
native_observer: UniquePtr<sys_dc::ffi::NativeDataChannelObserver>,
|
||||
}
|
||||
|
||||
impl Debug for DataChannel {
|
||||
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
|
||||
f.debug_struct("DataChannel")
|
||||
.field("label", &self.label())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DataSendError;
|
||||
|
||||
impl Display for DataSendError {
|
||||
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
|
||||
write!(f, "failed to send data to the DataChannel")
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for DataSendError {}
|
||||
|
||||
impl DataChannel {
|
||||
pub(crate) fn new(cxx_handle: UniquePtr<sys_dc::ffi::DataChannel>) -> Self {
|
||||
let mut observer = Box::new(InternalDataChannelObserver::default());
|
||||
|
||||
let mut dc = unsafe {
|
||||
Self {
|
||||
cxx_handle,
|
||||
native_observer: sys_dc::ffi::create_native_data_channel_observer(Box::new(
|
||||
sys_dc::DataChannelObserverWrapper::new(&mut *observer),
|
||||
)),
|
||||
observer,
|
||||
}
|
||||
};
|
||||
|
||||
unsafe {
|
||||
dc.cxx_handle
|
||||
.pin_mut()
|
||||
.register_observer(dc.native_observer.pin_mut());
|
||||
}
|
||||
|
||||
dc
|
||||
}
|
||||
|
||||
pub fn send(&self, data: &[u8], binary: bool) -> Result<(), DataSendError> {
|
||||
let buffer = sys_dc::ffi::DataBuffer {
|
||||
ptr: data.as_ptr(),
|
||||
len: data.len(),
|
||||
binary,
|
||||
};
|
||||
|
||||
self.cxx_handle
|
||||
.send(&buffer)
|
||||
.then_some(())
|
||||
.ok_or(DataSendError {})
|
||||
}
|
||||
|
||||
pub fn label(&self) -> String {
|
||||
self.cxx_handle.label()
|
||||
}
|
||||
|
||||
pub fn state(&self) -> DataState {
|
||||
self.cxx_handle.state()
|
||||
}
|
||||
|
||||
pub fn close(&self) {
|
||||
self.cxx_handle.close();
|
||||
}
|
||||
|
||||
pub fn on_state_change(&mut self, handler: OnStateChangeHandler) {
|
||||
*self.observer.on_state_change_handler.lock().unwrap() = Some(handler);
|
||||
}
|
||||
|
||||
pub fn on_message(&mut self, handler: OnMessageHandler) {
|
||||
*self.observer.on_message_handler.lock().unwrap() = Some(handler);
|
||||
}
|
||||
|
||||
pub fn on_buffered_amount_change(&mut self, handler: OnBufferedAmountChangeHandler) {
|
||||
*self
|
||||
.observer
|
||||
.on_buffered_amount_change_handler
|
||||
.lock()
|
||||
.unwrap() = Some(handler);
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for DataChannel {
|
||||
fn drop(&mut self) {
|
||||
self.cxx_handle.pin_mut().unregister_observer();
|
||||
}
|
||||
}
|
||||
|
||||
pub type OnStateChangeHandler = Box<dyn FnMut() + Send + Sync>;
|
||||
pub type OnMessageHandler = Box<dyn FnMut(&[u8], bool) + Send + Sync>;
|
||||
pub type OnBufferedAmountChangeHandler = Box<dyn FnMut(u64) + Send + Sync>;
|
||||
|
||||
#[derive(Default)]
|
||||
struct InternalDataChannelObserver {
|
||||
on_state_change_handler: Mutex<Option<OnStateChangeHandler>>,
|
||||
on_message_handler: Mutex<Option<OnMessageHandler>>,
|
||||
on_buffered_amount_change_handler: Mutex<Option<OnBufferedAmountChangeHandler>>,
|
||||
}
|
||||
|
||||
impl sys_dc::DataChannelObserver for InternalDataChannelObserver {
|
||||
fn on_state_change(&self) {
|
||||
trace!("DataChannel: on_state_change");
|
||||
let mut handler = self.on_state_change_handler.lock().unwrap();
|
||||
if let Some(f) = handler.as_mut() {
|
||||
f();
|
||||
}
|
||||
}
|
||||
|
||||
fn on_message(&self, data: &[u8], is_binary: bool) {
|
||||
trace!("DataChannel: on_message");
|
||||
let mut handler = self.on_message_handler.lock().unwrap();
|
||||
if let Some(f) = handler.as_mut() {
|
||||
f(data, is_binary);
|
||||
}
|
||||
}
|
||||
|
||||
fn on_buffered_amount_change(&self, sent_data_size: u64) {
|
||||
trace!("DataChannel: on_buffered_amount_change");
|
||||
let mut handler = self.on_buffered_amount_change_handler.lock().unwrap();
|
||||
if let Some(f) = handler.as_mut() {
|
||||
f(sent_data_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct DataChannelInit {
|
||||
#[deprecated]
|
||||
pub reliable: bool,
|
||||
pub ordered: bool,
|
||||
pub max_retransmit_time: Option<i32>,
|
||||
pub max_retransmits: Option<i32>,
|
||||
@@ -159,11 +16,10 @@ pub struct DataChannelInit {
|
||||
impl Default for DataChannelInit {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
reliable: false,
|
||||
ordered: true,
|
||||
max_retransmit_time: None,
|
||||
max_retransmits: None,
|
||||
protocol: "".to_string(),
|
||||
protocol: String::new(),
|
||||
negotiated: false,
|
||||
id: -1,
|
||||
priority: None,
|
||||
@@ -171,20 +27,72 @@ impl Default for DataChannelInit {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<DataChannelInit> for sys_dc::ffi::DataChannelInit {
|
||||
fn from(init: DataChannelInit) -> Self {
|
||||
Self {
|
||||
reliable: init.reliable,
|
||||
ordered: init.ordered,
|
||||
has_max_retransmit_time: init.max_retransmit_time.is_some(),
|
||||
max_retransmit_time: init.max_retransmit_time.unwrap_or_default(),
|
||||
has_max_retransmits: init.max_retransmits.is_some(),
|
||||
max_retransmits: init.max_retransmits.unwrap_or_default(),
|
||||
protocol: init.protocol,
|
||||
negotiated: init.negotiated,
|
||||
id: init.id,
|
||||
has_priority: init.priority.is_some(),
|
||||
priority: init.priority.unwrap_or(Priority::Low),
|
||||
}
|
||||
#[derive(Debug, Error)]
|
||||
pub enum DataChannelError {
|
||||
#[error("failed to send data, dc not open? send buffer is full ?")]
|
||||
Send,
|
||||
#[error("only utf8 strings can be sent")]
|
||||
Utf8(#[from] Utf8Error),
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum DataState {
|
||||
Connecting,
|
||||
Open,
|
||||
Closing,
|
||||
Closed,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DataBuffer<'a> {
|
||||
pub data: &'a [u8],
|
||||
pub binary: bool,
|
||||
}
|
||||
|
||||
pub type OnStateChange = Box<dyn FnMut(DataState) + Send + Sync>;
|
||||
pub type OnMessage = Box<dyn FnMut(DataBuffer) + Send + Sync>;
|
||||
pub type OnBufferedAmountChange = Box<dyn FnMut(u64) + Send + Sync>;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct DataChannel {
|
||||
pub(crate) handle: dc_imp::DataChannel,
|
||||
}
|
||||
|
||||
impl DataChannel {
|
||||
pub fn send(&self, data: &[u8], binary: bool) -> Result<(), DataChannelError> {
|
||||
self.handle.send(data, binary)
|
||||
}
|
||||
|
||||
pub fn label(&self) -> String {
|
||||
self.handle.label()
|
||||
}
|
||||
|
||||
pub fn state(&self) -> DataState {
|
||||
self.handle.state()
|
||||
}
|
||||
|
||||
pub fn close(&self) {
|
||||
self.handle.close()
|
||||
}
|
||||
|
||||
pub fn on_state_change(&self, callback: Option<OnStateChange>) {
|
||||
self.handle.on_state_change(callback)
|
||||
}
|
||||
|
||||
pub fn on_message(&self, callback: Option<OnMessage>) {
|
||||
self.handle.on_message(callback)
|
||||
}
|
||||
|
||||
pub fn on_buffered_amount_change(&self, callback: Option<OnBufferedAmountChange>) {
|
||||
self.handle.on_buffered_amount_change(callback)
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for DataChannel {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("DataChannel")
|
||||
.field("label", &self.label())
|
||||
.field("state", &self.state())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
use crate::{imp::ice_candidate as imp_ic, session_description::SdpParseError};
|
||||
use std::fmt::Debug;
|
||||
|
||||
pub struct IceCandidate {
|
||||
pub(crate) handle: imp_ic::IceCandidate,
|
||||
}
|
||||
|
||||
impl IceCandidate {
|
||||
pub fn parse(
|
||||
sdp_mid: &str,
|
||||
sdp_mline_index: i32,
|
||||
sdp: &str,
|
||||
) -> Result<IceCandidate, SdpParseError> {
|
||||
imp_ic::IceCandidate::parse(sdp_mid, sdp_mline_index, sdp)
|
||||
}
|
||||
|
||||
pub fn sdp_mid(&self) -> String {
|
||||
self.handle.sdp_mid()
|
||||
}
|
||||
|
||||
pub fn sdp_mline_index(&self) -> i32 {
|
||||
self.handle.sdp_mline_index()
|
||||
}
|
||||
|
||||
pub fn candidate(&self) -> String {
|
||||
self.handle.candidate()
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for IceCandidate {
|
||||
fn to_string(&self) -> String {
|
||||
self.handle.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for IceCandidate {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("IceCandidate").finish()
|
||||
}
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
use std::fmt::{Debug, Formatter};
|
||||
|
||||
use cxx::{SharedPtr, UniquePtr};
|
||||
|
||||
pub use sys_jsep::ffi::{SdpParseError, SdpType};
|
||||
use webrtc_sys::jsep as sys_jsep;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct IceCandidate {
|
||||
cxx_handle: SharedPtr<sys_jsep::ffi::IceCandidate>,
|
||||
}
|
||||
|
||||
impl Debug for IceCandidate {
|
||||
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
|
||||
write!(f, "IceCandidate[{}]", self.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl IceCandidate {
|
||||
pub fn from(
|
||||
sdp_mid: &str,
|
||||
sdp_mline_index: i32,
|
||||
sdp: &str,
|
||||
) -> Result<IceCandidate, SdpParseError> {
|
||||
let res = sys_jsep::ffi::create_ice_candidate(
|
||||
sdp_mid.to_string(),
|
||||
sdp_mline_index,
|
||||
sdp.to_string(),
|
||||
);
|
||||
|
||||
match res {
|
||||
Ok(cxx_handle) => Ok(IceCandidate::new(cxx_handle)),
|
||||
Err(e) => Err(unsafe { SdpParseError::from(e.what()) }),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn new(cxx_handle: SharedPtr<sys_jsep::ffi::IceCandidate>) -> Self {
|
||||
Self { cxx_handle }
|
||||
}
|
||||
|
||||
pub(crate) fn release(self) -> SharedPtr<sys_jsep::ffi::IceCandidate> {
|
||||
self.cxx_handle
|
||||
}
|
||||
|
||||
pub fn sdp_mid(&self) -> String {
|
||||
self.cxx_handle.sdp_mid()
|
||||
}
|
||||
|
||||
pub fn sdp_mline_index(&self) -> i32 {
|
||||
self.cxx_handle.sdp_mline_index()
|
||||
}
|
||||
|
||||
pub fn candidate(&self) -> String {
|
||||
self.cxx_handle.candidate()
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for IceCandidate {
|
||||
fn to_string(&self) -> String {
|
||||
self.cxx_handle.stringify()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SessionDescription {
|
||||
cxx_handle: UniquePtr<sys_jsep::ffi::SessionDescription>,
|
||||
}
|
||||
|
||||
impl Debug for SessionDescription {
|
||||
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
|
||||
write!(f, "SessionDescription[{}]", self.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl SessionDescription {
|
||||
pub fn from(sdp_type: SdpType, description: &str) -> Result<SessionDescription, SdpParseError> {
|
||||
let res = sys_jsep::ffi::create_session_description(sdp_type, description.to_string());
|
||||
|
||||
match res {
|
||||
Ok(cxx_handle) => Ok(SessionDescription::new(cxx_handle)),
|
||||
Err(e) => Err(unsafe { SdpParseError::from(e.what()) }),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn new(cxx_handle: UniquePtr<sys_jsep::ffi::SessionDescription>) -> Self {
|
||||
Self { cxx_handle }
|
||||
}
|
||||
|
||||
pub(crate) fn release(self) -> UniquePtr<sys_jsep::ffi::SessionDescription> {
|
||||
self.cxx_handle
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for SessionDescription {
|
||||
fn to_string(&self) -> String {
|
||||
self.cxx_handle.stringify()
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for SessionDescription {
|
||||
fn clone(&self) -> Self {
|
||||
SessionDescription::new(self.cxx_handle.clone())
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,48 @@
|
||||
use thiserror::Error;
|
||||
|
||||
#[cfg_attr(target_arch = "wasm32", path = "web/mod.rs")]
|
||||
#[cfg_attr(not(target_arch = "wasm32"), path = "native/mod.rs")]
|
||||
mod imp;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum MediaType {
|
||||
Audio,
|
||||
Video,
|
||||
Data,
|
||||
Unsupported,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum RtcErrorType {
|
||||
Internal,
|
||||
InvalidSdp,
|
||||
InvalidState,
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error("an RtcError occured: {error_type:?} - {message}")]
|
||||
pub struct RtcError {
|
||||
pub error_type: RtcErrorType,
|
||||
pub message: String,
|
||||
}
|
||||
|
||||
pub mod data_channel;
|
||||
pub mod jsep;
|
||||
pub mod ice_candidate;
|
||||
pub mod media_stream;
|
||||
pub mod peer_connection;
|
||||
pub mod peer_connection_factory;
|
||||
pub mod prelude;
|
||||
pub mod rtc_error;
|
||||
pub mod rtp_parameters;
|
||||
pub mod rtp_receiver;
|
||||
pub mod rtp_sender;
|
||||
pub mod rtp_transceiver;
|
||||
pub mod session_description;
|
||||
pub mod video_frame;
|
||||
pub mod video_frame_buffer;
|
||||
pub mod webrtc;
|
||||
pub mod yuv_helper;
|
||||
pub mod video_source;
|
||||
pub mod video_stream;
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub mod native {
|
||||
pub use crate::imp::yuv_helper;
|
||||
pub use webrtc_sys::webrtc::ffi::create_random_uuid;
|
||||
}
|
||||
|
||||
+111
-226
@@ -1,254 +1,139 @@
|
||||
use cxx::{SharedPtr, UniquePtr};
|
||||
use crate::imp::media_stream as imp_ms;
|
||||
use livekit_utils::enum_dispatch;
|
||||
use std::fmt::{Debug, Formatter};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use webrtc_sys::media_stream as sys_ms;
|
||||
use webrtc_sys::MEDIA_TYPE_VIDEO;
|
||||
use std::fmt::Debug;
|
||||
|
||||
pub use sys_ms::ffi::ContentHint;
|
||||
pub use sys_ms::ffi::TrackState;
|
||||
|
||||
use crate::video_frame::VideoFrame;
|
||||
use crate::video_frame_buffer::VideoFrameBuffer;
|
||||
|
||||
pub trait MediaStreamTrackTrait {
|
||||
fn kind(&self) -> String;
|
||||
fn id(&self) -> String;
|
||||
fn enabled(&self) -> bool;
|
||||
fn set_enabled(&self, enabled: bool) -> bool;
|
||||
fn state(&self) -> TrackState;
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum RtcTrackState {
|
||||
Live,
|
||||
Ended,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum MediaStreamTrackHandle {
|
||||
Audio(Arc<AudioTrack>),
|
||||
Video(Arc<VideoTrack>),
|
||||
pub struct MediaStream {
|
||||
pub(crate) handle: imp_ms::MediaStream,
|
||||
}
|
||||
|
||||
impl MediaStreamTrackHandle {
|
||||
pub(crate) fn new(cxx_handle: SharedPtr<sys_ms::ffi::MediaStreamTrack>) -> Self {
|
||||
if cxx_handle.kind() == MEDIA_TYPE_VIDEO {
|
||||
Self::Video(VideoTrack::new(cxx_handle))
|
||||
} else {
|
||||
Self::Audio(AudioTrack::new(cxx_handle))
|
||||
}
|
||||
impl MediaStream {
|
||||
pub fn id(&self) -> String {
|
||||
self.handle.id()
|
||||
}
|
||||
|
||||
// TODO(theomonnom): enum_dispatch with visibility support?
|
||||
pub(crate) fn cxx_handle(&self) -> SharedPtr<sys_ms::ffi::MediaStreamTrack> {
|
||||
match self {
|
||||
Self::Video(video) => video.cxx_handle(),
|
||||
Self::Audio(audio) => audio.cxx_handle(),
|
||||
}
|
||||
pub fn audio_tracks(&self) -> Vec<RtcAudioTrack> {
|
||||
self.handle.audio_tracks()
|
||||
}
|
||||
|
||||
pub fn video_tracks(&self) -> Vec<RtcVideoTrack> {
|
||||
self.handle.video_tracks()
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for MediaStreamTrackHandle {
|
||||
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
|
||||
f.debug_struct("MediaStreamTrack")
|
||||
impl Debug for MediaStream {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("MediaStream")
|
||||
.field("id", &self.id())
|
||||
.field("audio_tracks", &self.audio_tracks())
|
||||
.field("video_tracks", &self.video_tracks())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct RtcVideoTrack {
|
||||
pub(crate) handle: imp_ms::RtcVideoTrack,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct RtcAudioTrack {
|
||||
pub(crate) handle: imp_ms::RtcAudioTrack,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum MediaStreamTrack {
|
||||
Video(RtcVideoTrack),
|
||||
Audio(RtcAudioTrack),
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
impl MediaStreamTrack {
|
||||
enum_dispatch!(
|
||||
[Video, Audio];
|
||||
pub(crate) fn sys_handle(self: &Self) -> cxx::SharedPtr<webrtc_sys::media_stream::ffi::MediaStreamTrack>;
|
||||
);
|
||||
}
|
||||
|
||||
impl MediaStreamTrack {
|
||||
enum_dispatch!(
|
||||
[Video, Audio];
|
||||
pub fn id(self: &Self) -> String;
|
||||
pub fn enabled(self: &Self) -> bool;
|
||||
pub fn set_enabled(self: &Self, enabled: bool) -> bool;
|
||||
pub fn state(self: &Self) -> RtcTrackState;
|
||||
);
|
||||
}
|
||||
|
||||
macro_rules! media_stream_track {
|
||||
() => {
|
||||
pub fn id(&self) -> String {
|
||||
self.handle.id()
|
||||
}
|
||||
|
||||
pub fn enabled(&self) -> bool {
|
||||
self.handle.enabled()
|
||||
}
|
||||
|
||||
pub fn set_enabled(&self, enabled: bool) -> bool {
|
||||
self.handle.set_enabled(enabled)
|
||||
}
|
||||
|
||||
pub fn state(&self) -> RtcTrackState {
|
||||
self.handle.state().into()
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub(crate) fn sys_handle(
|
||||
&self,
|
||||
) -> cxx::SharedPtr<webrtc_sys::media_stream::ffi::MediaStreamTrack> {
|
||||
self.handle.sys_handle()
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl RtcVideoTrack {
|
||||
media_stream_track!();
|
||||
}
|
||||
|
||||
impl RtcAudioTrack {
|
||||
media_stream_track!();
|
||||
}
|
||||
|
||||
impl Debug for RtcAudioTrack {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("RtcAudioTrack")
|
||||
.field("id", &self.id())
|
||||
.field("kind", &self.kind())
|
||||
.field("enabled", &self.enabled())
|
||||
.field("state", &self.state())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl MediaStreamTrackTrait for MediaStreamTrackHandle {
|
||||
enum_dispatch!(
|
||||
[Audio, Video]
|
||||
fnc!(kind, &Self, [], String);
|
||||
fnc!(id, &Self, [], String);
|
||||
fnc!(enabled, &Self, [], bool);
|
||||
fnc!(state, &Self, [], TrackState);
|
||||
fnc!(set_enabled, &Self, [enabled: bool], bool);
|
||||
);
|
||||
}
|
||||
|
||||
pub struct AudioTrack {
|
||||
cxx_handle: SharedPtr<sys_ms::ffi::MediaStreamTrack>,
|
||||
}
|
||||
|
||||
impl AudioTrack {
|
||||
fn new(cxx_handle: SharedPtr<sys_ms::ffi::MediaStreamTrack>) -> Arc<Self> {
|
||||
Arc::new(Self { cxx_handle })
|
||||
}
|
||||
|
||||
pub(crate) fn cxx_handle(&self) -> SharedPtr<sys_ms::ffi::MediaStreamTrack> {
|
||||
self.cxx_handle.clone()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct VideoTrack {
|
||||
cxx_handle: SharedPtr<sys_ms::ffi::MediaStreamTrack>,
|
||||
observer: Box<InternalVideoTrackSink>,
|
||||
|
||||
// Keep alive for c++
|
||||
native_observer: UniquePtr<sys_ms::ffi::NativeVideoFrameSink>,
|
||||
}
|
||||
|
||||
impl VideoTrack {
|
||||
fn new(cxx_handle: SharedPtr<sys_ms::ffi::MediaStreamTrack>) -> Arc<Self> {
|
||||
let mut observer = Box::new(InternalVideoTrackSink::default());
|
||||
|
||||
let mut track = unsafe {
|
||||
Self {
|
||||
cxx_handle,
|
||||
native_observer: {
|
||||
sys_ms::ffi::create_native_video_frame_sink(Box::new(
|
||||
sys_ms::VideoFrameSinkWrapper::new(&mut *observer),
|
||||
))
|
||||
},
|
||||
observer,
|
||||
}
|
||||
};
|
||||
|
||||
unsafe {
|
||||
(*track.video_handle()).add_sink(track.native_observer.pin_mut());
|
||||
}
|
||||
|
||||
Arc::new(track)
|
||||
}
|
||||
|
||||
pub(crate) fn cxx_handle(&self) -> SharedPtr<sys_ms::ffi::MediaStreamTrack> {
|
||||
self.cxx_handle.clone()
|
||||
}
|
||||
|
||||
fn video_handle(&self) -> *const sys_ms::ffi::VideoTrack {
|
||||
unsafe { sys_ms::ffi::media_to_video(&*self.cxx_handle) }
|
||||
}
|
||||
|
||||
pub fn set_should_receive(&self, should_receive: bool) {
|
||||
unsafe { (*self.video_handle()).set_should_receive(should_receive) }
|
||||
}
|
||||
|
||||
pub fn set_content_hint(&self, hint: ContentHint) {
|
||||
unsafe { (*self.video_handle()).set_content_hint(hint) }
|
||||
}
|
||||
|
||||
pub fn should_receive(&self) -> bool {
|
||||
unsafe { (*self.video_handle()).should_receive() }
|
||||
}
|
||||
|
||||
pub fn content_hint(&self) -> ContentHint {
|
||||
unsafe { (*self.video_handle()).content_hint() }
|
||||
}
|
||||
|
||||
pub fn on_frame(&self, handler: OnFrameHandler) {
|
||||
*self.observer.on_frame_handler.lock().unwrap() = Some(handler);
|
||||
}
|
||||
|
||||
pub fn on_discarded_frame(&self, handler: OnDiscardedFrameHandler) {
|
||||
*self.observer.on_discarded_frame_handler.lock().unwrap() = Some(handler);
|
||||
}
|
||||
|
||||
pub fn on_constraints_changed(&self, handler: OnConstraintsChangedHandler) {
|
||||
*self.observer.on_constraints_changed_handler.lock().unwrap() = Some(handler);
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for VideoTrack {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
(*self.video_handle()).remove_sink(self.native_observer.pin_mut());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_media_stream_track_trait {
|
||||
($x:ty) => {
|
||||
impl MediaStreamTrackTrait for $x {
|
||||
fn kind(&self) -> String {
|
||||
self.cxx_handle.kind()
|
||||
}
|
||||
|
||||
fn id(&self) -> String {
|
||||
self.cxx_handle.id()
|
||||
}
|
||||
|
||||
fn enabled(&self) -> bool {
|
||||
self.cxx_handle.enabled()
|
||||
}
|
||||
|
||||
fn set_enabled(&self, enabled: bool) -> bool {
|
||||
self.cxx_handle.set_enabled(enabled)
|
||||
}
|
||||
|
||||
fn state(&self) -> TrackState {
|
||||
self.cxx_handle.state()
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_media_stream_track_trait!(VideoTrack);
|
||||
impl_media_stream_track_trait!(AudioTrack);
|
||||
|
||||
pub type OnFrameHandler = Box<dyn FnMut(VideoFrame, VideoFrameBuffer) + Send + Sync>;
|
||||
pub type OnDiscardedFrameHandler = Box<dyn FnMut() + Send + Sync>;
|
||||
pub type OnConstraintsChangedHandler = Box<dyn FnMut(VideoTrackSourceConstraints) + Send + Sync>;
|
||||
|
||||
#[derive(Default)]
|
||||
struct InternalVideoTrackSink {
|
||||
on_frame_handler: Mutex<Option<OnFrameHandler>>,
|
||||
on_discarded_frame_handler: Mutex<Option<OnDiscardedFrameHandler>>,
|
||||
on_constraints_changed_handler: Mutex<Option<OnConstraintsChangedHandler>>,
|
||||
}
|
||||
|
||||
pub struct VideoTrackSourceConstraints {
|
||||
pub min_fps: Option<f64>,
|
||||
pub max_fps: Option<f64>,
|
||||
}
|
||||
|
||||
impl From<sys_ms::ffi::VideoTrackSourceConstraints> for VideoTrackSourceConstraints {
|
||||
fn from(cst: sys_ms::ffi::VideoTrackSourceConstraints) -> Self {
|
||||
Self {
|
||||
min_fps: (cst.min_fps != 1.0).then_some(cst.min_fps),
|
||||
max_fps: (cst.max_fps != 1.0).then_some(cst.max_fps),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl sys_ms::VideoFrameSink for InternalVideoTrackSink {
|
||||
fn on_frame(&self, frame: UniquePtr<webrtc_sys::video_frame::ffi::VideoFrame>) {
|
||||
if let Some(cb) = self.on_frame_handler.lock().unwrap().as_mut() {
|
||||
let frame = VideoFrame::new(frame);
|
||||
let video_frame_buffer = unsafe { frame.video_frame_buffer() };
|
||||
cb(frame, video_frame_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
fn on_discarded_frame(&self) {
|
||||
if let Some(cb) = self.on_discarded_frame_handler.lock().unwrap().as_mut() {
|
||||
cb();
|
||||
}
|
||||
}
|
||||
|
||||
fn on_constraints_changed(&self, constraints: sys_ms::ffi::VideoTrackSourceConstraints) {
|
||||
if let Some(cb) = self.on_constraints_changed_handler.lock().unwrap().as_mut() {
|
||||
cb(constraints.into());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MediaStream {
|
||||
cxx_handle: SharedPtr<sys_ms::ffi::MediaStream>,
|
||||
}
|
||||
|
||||
impl Debug for MediaStream {
|
||||
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
|
||||
f.debug_struct("MediaStream")
|
||||
impl Debug for RtcVideoTrack {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("RtcVideoTrack")
|
||||
.field("id", &self.id())
|
||||
.field("enabled", &self.enabled())
|
||||
.field("state", &self.state())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl MediaStream {
|
||||
pub(crate) fn new(cxx_handle: SharedPtr<sys_ms::ffi::MediaStream>) -> Self {
|
||||
Self { cxx_handle }
|
||||
impl From<RtcAudioTrack> for MediaStreamTrack {
|
||||
fn from(track: RtcAudioTrack) -> Self {
|
||||
Self::Audio(track)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn id(&self) -> String {
|
||||
self.cxx_handle.id()
|
||||
impl From<RtcVideoTrack> for MediaStreamTrack {
|
||||
fn from(track: RtcVideoTrack) -> Self {
|
||||
Self::Video(track)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
use crate::data_channel::{
|
||||
DataBuffer, DataChannelError, DataChannelInit, DataState, OnBufferedAmountChange, OnMessage,
|
||||
OnStateChange,
|
||||
};
|
||||
use cxx::SharedPtr;
|
||||
use std::str;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use webrtc_sys::data_channel as sys_dc;
|
||||
|
||||
impl From<sys_dc::ffi::DataState> for DataState {
|
||||
fn from(value: sys_dc::ffi::DataState) -> Self {
|
||||
match value {
|
||||
sys_dc::ffi::DataState::Connecting => Self::Connecting,
|
||||
sys_dc::ffi::DataState::Open => Self::Open,
|
||||
sys_dc::ffi::DataState::Closing => Self::Closing,
|
||||
sys_dc::ffi::DataState::Closed => Self::Closed,
|
||||
_ => panic!("unknown data channel state"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<DataChannelInit> for sys_dc::ffi::DataChannelInit {
|
||||
fn from(value: DataChannelInit) -> Self {
|
||||
Self {
|
||||
ordered: value.ordered,
|
||||
has_max_retransmit_time: value.max_retransmit_time.is_some(),
|
||||
max_retransmit_time: value.max_retransmit_time.unwrap_or_default(),
|
||||
has_max_retransmits: value.max_retransmits.is_some(),
|
||||
max_retransmits: value.max_retransmits.unwrap_or_default(),
|
||||
protocol: value.protocol,
|
||||
id: value.id,
|
||||
has_priority: false,
|
||||
priority: sys_dc::ffi::Priority::Medium,
|
||||
negotiated: value.negotiated,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct DataChannel {
|
||||
#[allow(dead_code)]
|
||||
native_observer: SharedPtr<sys_dc::ffi::NativeDataChannelObserver>,
|
||||
observer: Arc<DataChannelObserver>,
|
||||
|
||||
pub(crate) sys_handle: SharedPtr<sys_dc::ffi::DataChannel>,
|
||||
}
|
||||
|
||||
impl DataChannel {
|
||||
pub fn configure(sys_handle: SharedPtr<sys_dc::ffi::DataChannel>) -> Self {
|
||||
unsafe {
|
||||
let observer = Arc::new(DataChannelObserver::default());
|
||||
let dc = Self {
|
||||
sys_handle: sys_handle.clone(),
|
||||
native_observer: sys_dc::ffi::create_native_data_channel_observer(
|
||||
Box::new(sys_dc::DataChannelObserverWrapper::new(observer.clone())),
|
||||
&*sys_handle as *const _ as *mut _,
|
||||
),
|
||||
observer,
|
||||
};
|
||||
|
||||
dc.sys_handle
|
||||
.register_observer(&*dc.native_observer as *const _ as *mut _);
|
||||
dc
|
||||
}
|
||||
}
|
||||
|
||||
pub fn send(&self, data: &[u8], binary: bool) -> Result<(), DataChannelError> {
|
||||
if !binary {
|
||||
str::from_utf8(data)?;
|
||||
}
|
||||
|
||||
let buffer = sys_dc::ffi::DataBuffer {
|
||||
ptr: data.as_ptr(),
|
||||
len: data.len(),
|
||||
binary,
|
||||
};
|
||||
|
||||
self.sys_handle
|
||||
.send(&buffer)
|
||||
.then_some(())
|
||||
.ok_or(DataChannelError::Send)
|
||||
}
|
||||
|
||||
pub fn label(&self) -> String {
|
||||
self.sys_handle.label()
|
||||
}
|
||||
|
||||
pub fn state(&self) -> DataState {
|
||||
self.sys_handle.state().into()
|
||||
}
|
||||
|
||||
pub fn close(&self) {
|
||||
self.sys_handle.close();
|
||||
}
|
||||
|
||||
pub fn on_state_change(&self, handler: Option<OnStateChange>) {
|
||||
*self.observer.state_change_handler.lock().unwrap() = handler;
|
||||
}
|
||||
|
||||
pub fn on_message(&self, handler: Option<OnMessage>) {
|
||||
*self.observer.message_handler.lock().unwrap() = handler;
|
||||
}
|
||||
|
||||
pub fn on_buffered_amount_change(&self, handler: Option<OnBufferedAmountChange>) {
|
||||
*self.observer.buffered_amount_change_handler.lock().unwrap() = handler;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct DataChannelObserver {
|
||||
state_change_handler: Mutex<Option<OnStateChange>>,
|
||||
message_handler: Mutex<Option<OnMessage>>,
|
||||
buffered_amount_change_handler: Mutex<Option<OnBufferedAmountChange>>,
|
||||
}
|
||||
|
||||
impl sys_dc::DataChannelObserver for DataChannelObserver {
|
||||
fn on_state_change(&self, state: sys_dc::ffi::DataState) {
|
||||
let mut handler = self.state_change_handler.lock().unwrap();
|
||||
if let Some(f) = handler.as_mut() {
|
||||
f(state.into());
|
||||
}
|
||||
}
|
||||
|
||||
fn on_message(&self, data: &[u8], binary: bool) {
|
||||
let mut handler = self.message_handler.lock().unwrap();
|
||||
if let Some(f) = handler.as_mut() {
|
||||
f(DataBuffer { data, binary });
|
||||
}
|
||||
}
|
||||
|
||||
fn on_buffered_amount_change(&self, sent_data_size: u64) {
|
||||
let mut handler = self.buffered_amount_change_handler.lock().unwrap();
|
||||
if let Some(f) = handler.as_mut() {
|
||||
f(sent_data_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
use crate::ice_candidate as ic;
|
||||
use crate::session_description::SdpParseError;
|
||||
use cxx::SharedPtr;
|
||||
use webrtc_sys::jsep as sys_jsep;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct IceCandidate {
|
||||
pub(crate) sys_handle: SharedPtr<sys_jsep::ffi::IceCandidate>,
|
||||
}
|
||||
|
||||
impl IceCandidate {
|
||||
pub fn parse(
|
||||
sdp_mid: &str,
|
||||
sdp_mline_index: i32,
|
||||
sdp: &str,
|
||||
) -> Result<ic::IceCandidate, SdpParseError> {
|
||||
let res = sys_jsep::ffi::create_ice_candidate(
|
||||
sdp_mid.to_string(),
|
||||
sdp_mline_index,
|
||||
sdp.to_string(),
|
||||
);
|
||||
|
||||
match res {
|
||||
Ok(sys_handle) => Ok(ic::IceCandidate {
|
||||
handle: IceCandidate { sys_handle },
|
||||
}),
|
||||
Err(e) => Err(unsafe { sys_jsep::ffi::SdpParseError::from(e.what()).into() }),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sdp_mid(&self) -> String {
|
||||
self.sys_handle.sdp_mid()
|
||||
}
|
||||
|
||||
pub fn sdp_mline_index(&self) -> i32 {
|
||||
self.sys_handle.sdp_mline_index()
|
||||
}
|
||||
|
||||
pub fn candidate(&self) -> String {
|
||||
self.sys_handle.candidate()
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for IceCandidate {
|
||||
fn to_string(&self) -> String {
|
||||
self.sys_handle.stringify()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
use crate::media_stream::{self, MediaStreamTrack, RtcTrackState};
|
||||
use cxx::SharedPtr;
|
||||
use webrtc_sys::media_stream as sys_ms;
|
||||
use webrtc_sys::media_stream::ffi::{
|
||||
audio_to_media, media_to_audio, media_to_video, video_to_media,
|
||||
};
|
||||
use webrtc_sys::{MEDIA_TYPE_AUDIO, MEDIA_TYPE_VIDEO};
|
||||
|
||||
impl From<sys_ms::ffi::TrackState> for RtcTrackState {
|
||||
fn from(state: sys_ms::ffi::TrackState) -> Self {
|
||||
match state {
|
||||
sys_ms::ffi::TrackState::Live => RtcTrackState::Live,
|
||||
sys_ms::ffi::TrackState::Ended => RtcTrackState::Ended,
|
||||
_ => panic!("unknown TrackState"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct MediaStream {
|
||||
pub(crate) sys_handle: SharedPtr<sys_ms::ffi::MediaStream>,
|
||||
}
|
||||
|
||||
impl MediaStream {
|
||||
pub fn id(&self) -> String {
|
||||
self.sys_handle.id()
|
||||
}
|
||||
|
||||
pub fn audio_tracks(&self) -> Vec<media_stream::RtcAudioTrack> {
|
||||
self.sys_handle
|
||||
.get_audio_tracks()
|
||||
.into_iter()
|
||||
.map(|t| media_stream::RtcAudioTrack {
|
||||
handle: RtcAudioTrack { sys_handle: t.ptr },
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn video_tracks(&self) -> Vec<media_stream::RtcVideoTrack> {
|
||||
self.sys_handle
|
||||
.get_video_tracks()
|
||||
.into_iter()
|
||||
.map(|t| media_stream::RtcVideoTrack {
|
||||
handle: RtcVideoTrack { sys_handle: t.ptr },
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_media_stream_track(
|
||||
sys_handle: SharedPtr<sys_ms::ffi::MediaStreamTrack>,
|
||||
) -> MediaStreamTrack {
|
||||
if sys_handle.kind() == MEDIA_TYPE_AUDIO {
|
||||
MediaStreamTrack::Audio(media_stream::RtcAudioTrack {
|
||||
handle: RtcAudioTrack {
|
||||
sys_handle: media_to_audio(sys_handle),
|
||||
},
|
||||
})
|
||||
} else if sys_handle.kind() == MEDIA_TYPE_VIDEO {
|
||||
MediaStreamTrack::Video(media_stream::RtcVideoTrack {
|
||||
handle: RtcVideoTrack {
|
||||
sys_handle: media_to_video(sys_handle),
|
||||
},
|
||||
})
|
||||
} else {
|
||||
panic!("unknown track kind")
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_media_stream_track {
|
||||
($cast:ident) => {
|
||||
pub fn id(&self) -> String {
|
||||
let ptr = sys_ms::ffi::$cast(self.sys_handle.clone());
|
||||
ptr.id()
|
||||
}
|
||||
|
||||
pub fn enabled(&self) -> bool {
|
||||
let ptr = sys_ms::ffi::$cast(self.sys_handle.clone());
|
||||
ptr.enabled()
|
||||
}
|
||||
|
||||
pub fn set_enabled(&self, enabled: bool) -> bool {
|
||||
let ptr = sys_ms::ffi::$cast(self.sys_handle.clone());
|
||||
ptr.set_enabled(enabled)
|
||||
}
|
||||
|
||||
pub fn state(&self) -> RtcTrackState {
|
||||
let ptr = sys_ms::ffi::$cast(self.sys_handle.clone());
|
||||
ptr.state().into()
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct RtcVideoTrack {
|
||||
pub(crate) sys_handle: SharedPtr<sys_ms::ffi::VideoTrack>,
|
||||
}
|
||||
|
||||
impl RtcVideoTrack {
|
||||
impl_media_stream_track!(video_to_media);
|
||||
|
||||
pub fn sys_handle(&self) -> SharedPtr<sys_ms::ffi::MediaStreamTrack> {
|
||||
video_to_media(self.sys_handle.clone())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct RtcAudioTrack {
|
||||
pub(crate) sys_handle: SharedPtr<sys_ms::ffi::AudioTrack>,
|
||||
}
|
||||
|
||||
impl RtcAudioTrack {
|
||||
impl_media_stream_track!(audio_to_media);
|
||||
|
||||
pub fn sys_handle(&self) -> SharedPtr<sys_ms::ffi::MediaStreamTrack> {
|
||||
audio_to_media(self.sys_handle.clone())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
pub mod data_channel;
|
||||
pub mod ice_candidate;
|
||||
pub mod media_stream;
|
||||
pub mod peer_connection;
|
||||
pub mod peer_connection_factory;
|
||||
pub mod rtp_parameters;
|
||||
pub mod rtp_receiver;
|
||||
pub mod rtp_sender;
|
||||
pub mod rtp_transceiver;
|
||||
pub mod session_description;
|
||||
pub mod video_frame;
|
||||
pub mod video_source;
|
||||
pub mod video_stream;
|
||||
pub mod yuv_helper;
|
||||
|
||||
use crate::MediaType;
|
||||
use crate::{RtcError, RtcErrorType};
|
||||
use webrtc_sys::rtc_error as sys_err;
|
||||
use webrtc_sys::webrtc as sys_rtc;
|
||||
|
||||
impl From<sys_err::ffi::RTCErrorType> for RtcErrorType {
|
||||
fn from(value: sys_err::ffi::RTCErrorType) -> Self {
|
||||
match value {
|
||||
sys_err::ffi::RTCErrorType::InvalidState => Self::InvalidState,
|
||||
_ => Self::Internal,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<sys_err::ffi::RTCError> for RtcError {
|
||||
fn from(value: sys_err::ffi::RTCError) -> Self {
|
||||
Self {
|
||||
error_type: value.error_type.into(),
|
||||
message: value.message,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<MediaType> for sys_rtc::ffi::MediaType {
|
||||
fn from(value: MediaType) -> Self {
|
||||
match value {
|
||||
MediaType::Audio => Self::Audio,
|
||||
MediaType::Video => Self::Video,
|
||||
MediaType::Data => Self::Data,
|
||||
MediaType::Unsupported => Self::Unsupported,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,579 @@
|
||||
use crate::data_channel::DataChannel;
|
||||
use crate::data_channel::DataChannelInit;
|
||||
use crate::ice_candidate::IceCandidate;
|
||||
use crate::imp::data_channel as imp_dc;
|
||||
use crate::imp::ice_candidate as imp_ic;
|
||||
use crate::imp::media_stream as imp_ms;
|
||||
use crate::imp::rtp_receiver as imp_rr;
|
||||
use crate::imp::rtp_sender as imp_rs;
|
||||
use crate::imp::rtp_transceiver as imp_rt;
|
||||
use crate::imp::session_description as imp_sdp;
|
||||
use crate::media_stream::{MediaStream, MediaStreamTrack};
|
||||
use crate::peer_connection::{
|
||||
AnswerOptions, IceCandidateError, IceConnectionState, IceGatheringState, OfferOptions,
|
||||
OnConnectionChange, OnDataChannel, OnIceCandidate, OnIceCandidateError, OnIceConnectionChange,
|
||||
OnIceGatheringChange, OnNegotiationNeeded, OnSignalingChange, OnTrack, PeerConnectionState,
|
||||
SignalingState, TrackEvent,
|
||||
};
|
||||
use crate::rtp_receiver::RtpReceiver;
|
||||
use crate::rtp_sender::RtpSender;
|
||||
use crate::rtp_transceiver::RtpTransceiver;
|
||||
use crate::rtp_transceiver::RtpTransceiverInit;
|
||||
use crate::MediaType;
|
||||
use crate::{session_description::SessionDescription, RtcError};
|
||||
use cxx::{SharedPtr, UniquePtr};
|
||||
use futures::channel::oneshot;
|
||||
use std::mem::ManuallyDrop;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use webrtc_sys::data_channel as sys_dc;
|
||||
use webrtc_sys::jsep as sys_jsep;
|
||||
use webrtc_sys::peer_connection as sys_pc;
|
||||
use webrtc_sys::rtc_error as sys_err;
|
||||
|
||||
impl From<OfferOptions> for sys_pc::ffi::RTCOfferAnswerOptions {
|
||||
fn from(options: OfferOptions) -> Self {
|
||||
Self {
|
||||
ice_restart: options.ice_restart,
|
||||
offer_to_receive_audio: options.offer_to_receive_audio as i32,
|
||||
offer_to_receive_video: options.offer_to_receive_video as i32,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<AnswerOptions> for sys_pc::ffi::RTCOfferAnswerOptions {
|
||||
fn from(_options: AnswerOptions) -> Self {
|
||||
Self::default()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<sys_pc::ffi::PeerConnectionState> for PeerConnectionState {
|
||||
fn from(state: sys_pc::ffi::PeerConnectionState) -> Self {
|
||||
match state {
|
||||
sys_pc::ffi::PeerConnectionState::New => PeerConnectionState::New,
|
||||
sys_pc::ffi::PeerConnectionState::Connecting => PeerConnectionState::Connecting,
|
||||
sys_pc::ffi::PeerConnectionState::Connected => PeerConnectionState::Connected,
|
||||
sys_pc::ffi::PeerConnectionState::Disconnected => PeerConnectionState::Disconnected,
|
||||
sys_pc::ffi::PeerConnectionState::Failed => PeerConnectionState::Failed,
|
||||
sys_pc::ffi::PeerConnectionState::Closed => PeerConnectionState::Closed,
|
||||
_ => panic!("unknown PeerConnectionState"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<sys_pc::ffi::IceConnectionState> for IceConnectionState {
|
||||
fn from(state: sys_pc::ffi::IceConnectionState) -> Self {
|
||||
match state {
|
||||
sys_pc::ffi::IceConnectionState::IceConnectionNew => IceConnectionState::New,
|
||||
sys_pc::ffi::IceConnectionState::IceConnectionChecking => IceConnectionState::Checking,
|
||||
sys_pc::ffi::IceConnectionState::IceConnectionConnected => {
|
||||
IceConnectionState::Connected
|
||||
}
|
||||
sys_pc::ffi::IceConnectionState::IceConnectionCompleted => {
|
||||
IceConnectionState::Completed
|
||||
}
|
||||
sys_pc::ffi::IceConnectionState::IceConnectionFailed => IceConnectionState::Failed,
|
||||
sys_pc::ffi::IceConnectionState::IceConnectionDisconnected => {
|
||||
IceConnectionState::Disconnected
|
||||
}
|
||||
sys_pc::ffi::IceConnectionState::IceConnectionClosed => IceConnectionState::Closed,
|
||||
sys_pc::ffi::IceConnectionState::IceConnectionMax => IceConnectionState::Max,
|
||||
_ => panic!("unknown IceConnectionState"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<sys_pc::ffi::IceGatheringState> for IceGatheringState {
|
||||
fn from(state: sys_pc::ffi::IceGatheringState) -> Self {
|
||||
match state {
|
||||
sys_pc::ffi::IceGatheringState::IceGatheringNew => IceGatheringState::New,
|
||||
sys_pc::ffi::IceGatheringState::IceGatheringGathering => IceGatheringState::Gathering,
|
||||
sys_pc::ffi::IceGatheringState::IceGatheringComplete => IceGatheringState::Complete,
|
||||
_ => panic!("unknown IceGatheringState"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<sys_pc::ffi::SignalingState> for SignalingState {
|
||||
fn from(state: sys_pc::ffi::SignalingState) -> Self {
|
||||
match state {
|
||||
sys_pc::ffi::SignalingState::Stable => SignalingState::Stable,
|
||||
sys_pc::ffi::SignalingState::HaveLocalOffer => SignalingState::HaveLocalOffer,
|
||||
sys_pc::ffi::SignalingState::HaveRemoteOffer => SignalingState::HaveRemoteOffer,
|
||||
sys_pc::ffi::SignalingState::HaveLocalPrAnswer => SignalingState::HaveLocalPrAnswer,
|
||||
sys_pc::ffi::SignalingState::HaveRemotePrAnswer => SignalingState::HaveRemotePrAnswer,
|
||||
sys_pc::ffi::SignalingState::Closed => SignalingState::Closed,
|
||||
_ => panic!("unknown SignalingState"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct PeerConnection {
|
||||
native_observer: SharedPtr<sys_pc::ffi::NativePeerConnectionObserver>,
|
||||
observer: Arc<PeerObserver>,
|
||||
|
||||
pub(crate) sys_handle: SharedPtr<sys_pc::ffi::PeerConnection>,
|
||||
}
|
||||
|
||||
impl PeerConnection {
|
||||
pub fn configure(
|
||||
sys_handle: SharedPtr<sys_pc::ffi::PeerConnection>,
|
||||
observer: Arc<PeerObserver>,
|
||||
native_observer: SharedPtr<sys_pc::ffi::NativePeerConnectionObserver>,
|
||||
) -> Self {
|
||||
Self {
|
||||
sys_handle,
|
||||
observer,
|
||||
native_observer,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn create_offer(
|
||||
&self,
|
||||
options: OfferOptions,
|
||||
) -> Result<SessionDescription, RtcError> {
|
||||
let (mut native_wrapper, mut sdp_rx, mut err_rx) = create_sdp_observer();
|
||||
|
||||
unsafe {
|
||||
self.sys_handle
|
||||
.create_offer(native_wrapper.pin_mut(), options.into());
|
||||
}
|
||||
|
||||
futures::select! {
|
||||
sdp = sdp_rx => Ok(sdp.unwrap()),
|
||||
err = err_rx => Err(err.unwrap()),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn create_answer(
|
||||
&self,
|
||||
options: AnswerOptions,
|
||||
) -> Result<SessionDescription, RtcError> {
|
||||
let (mut native_wrapper, mut sdp_rx, mut err_rx) = create_sdp_observer();
|
||||
|
||||
unsafe {
|
||||
self.sys_handle
|
||||
.create_answer(native_wrapper.pin_mut(), options.into());
|
||||
}
|
||||
|
||||
futures::select! {
|
||||
sdp = sdp_rx => Ok(sdp.unwrap()),
|
||||
err = err_rx => Err(err.unwrap()),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn set_local_description(&self, desc: SessionDescription) -> Result<(), RtcError> {
|
||||
let (tx, rx) = oneshot::channel();
|
||||
let wrapper =
|
||||
sys_jsep::SetLocalSdpObserverWrapper(ManuallyDrop::new(Box::new(move |error| {
|
||||
let _ = tx.send(if error.ok() { Ok(()) } else { Err(error) });
|
||||
})));
|
||||
|
||||
let mut native_wrapper =
|
||||
sys_jsep::ffi::create_native_set_local_sdp_observer(Box::new(wrapper));
|
||||
|
||||
unsafe {
|
||||
self.sys_handle
|
||||
.set_local_description(desc.handle.sys_handle, native_wrapper.pin_mut());
|
||||
}
|
||||
|
||||
rx.await.unwrap().map_err(Into::into)
|
||||
}
|
||||
|
||||
pub async fn set_remote_description(&self, desc: SessionDescription) -> Result<(), RtcError> {
|
||||
let (tx, rx) = oneshot::channel();
|
||||
let wrapper =
|
||||
sys_jsep::SetRemoteSdpObserverWrapper(ManuallyDrop::new(Box::new(move |error| {
|
||||
let _ = tx.send(if error.ok() { Ok(()) } else { Err(error) });
|
||||
})));
|
||||
|
||||
let mut native_wrapper =
|
||||
sys_jsep::ffi::create_native_set_remote_sdp_observer(Box::new(wrapper));
|
||||
|
||||
unsafe {
|
||||
self.sys_handle
|
||||
.set_remote_description(desc.handle.sys_handle, native_wrapper.pin_mut());
|
||||
}
|
||||
|
||||
rx.await.unwrap().map_err(Into::into)
|
||||
}
|
||||
|
||||
pub async fn add_ice_candidate(&self, candidate: IceCandidate) -> Result<(), RtcError> {
|
||||
let (tx, rx) = oneshot::channel();
|
||||
let observer =
|
||||
sys_pc::AddIceCandidateObserverWrapper(ManuallyDrop::new(Box::new(|error| {
|
||||
let _ = tx.send(if error.ok() { Ok(()) } else { Err(error) });
|
||||
})));
|
||||
|
||||
let mut native_observer =
|
||||
sys_pc::ffi::create_native_add_ice_candidate_observer(Box::new(observer));
|
||||
self.sys_handle
|
||||
.add_ice_candidate(candidate.handle.sys_handle, native_observer.pin_mut());
|
||||
|
||||
rx.await.unwrap().map_err(Into::into)
|
||||
}
|
||||
|
||||
pub fn create_data_channel(
|
||||
&self,
|
||||
label: &str,
|
||||
init: DataChannelInit,
|
||||
) -> Result<DataChannel, RtcError> {
|
||||
let native_init = sys_dc::ffi::create_data_channel_init(init.into());
|
||||
let res = self
|
||||
.sys_handle
|
||||
.create_data_channel(label.to_string(), native_init);
|
||||
|
||||
match res {
|
||||
Ok(sys_handle) => Ok(DataChannel {
|
||||
handle: imp_dc::DataChannel::configure(sys_handle),
|
||||
}),
|
||||
Err(e) => Err(unsafe { sys_err::ffi::RTCError::from(e.what()).into() }),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_track<T: AsRef<str>>(
|
||||
&self,
|
||||
track: MediaStreamTrack,
|
||||
stream_ids: &[T],
|
||||
) -> Result<RtpSender, RtcError> {
|
||||
let stream_ids = stream_ids.iter().map(|s| s.as_ref().to_owned()).collect();
|
||||
let res = self.sys_handle.add_track(track.sys_handle(), &stream_ids);
|
||||
|
||||
match res {
|
||||
Ok(sys_handle) => Ok(RtpSender {
|
||||
handle: imp_rs::RtpSender { sys_handle },
|
||||
}),
|
||||
Err(e) => unsafe { Err(sys_err::ffi::RTCError::from(e.what()).into()) },
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_transceiver(
|
||||
&self,
|
||||
track: MediaStreamTrack,
|
||||
init: RtpTransceiverInit,
|
||||
) -> Result<RtpTransceiver, RtcError> {
|
||||
let res = self
|
||||
.sys_handle
|
||||
.add_transceiver(track.sys_handle(), init.into());
|
||||
|
||||
match res {
|
||||
Ok(sys_handle) => Ok(RtpTransceiver {
|
||||
handle: imp_rt::RtpTransceiver {
|
||||
sys_handle: sys_handle,
|
||||
},
|
||||
}),
|
||||
Err(e) => unsafe { Err(sys_err::ffi::RTCError::from(e.what()).into()) },
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_transceiver_for_media(
|
||||
&self,
|
||||
media_type: MediaType,
|
||||
init: RtpTransceiverInit,
|
||||
) -> Result<RtpTransceiver, RtcError> {
|
||||
let res = self
|
||||
.sys_handle
|
||||
.add_transceiver_for_media(media_type.into(), init.into());
|
||||
|
||||
match res {
|
||||
Ok(cxx_handle) => Ok(RtpTransceiver {
|
||||
handle: imp_rt::RtpTransceiver {
|
||||
sys_handle: cxx_handle,
|
||||
},
|
||||
}),
|
||||
Err(e) => unsafe { Err(sys_err::ffi::RTCError::from(e.what()).into()) },
|
||||
}
|
||||
}
|
||||
|
||||
pub fn close(&self) {
|
||||
self.sys_handle.close();
|
||||
}
|
||||
|
||||
pub fn connection_state(&self) -> PeerConnectionState {
|
||||
self.sys_handle.connection_state().into()
|
||||
}
|
||||
|
||||
pub fn ice_connection_state(&self) -> IceConnectionState {
|
||||
self.sys_handle.ice_connection_state().into()
|
||||
}
|
||||
|
||||
pub fn ice_gathering_state(&self) -> IceGatheringState {
|
||||
self.sys_handle.ice_gathering_state().into()
|
||||
}
|
||||
|
||||
pub fn signaling_state(&self) -> SignalingState {
|
||||
self.sys_handle.signaling_state().into()
|
||||
}
|
||||
|
||||
pub fn current_local_description(&self) -> Option<SessionDescription> {
|
||||
let sdp = self.sys_handle.current_local_description();
|
||||
if sdp.is_null() {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(SessionDescription {
|
||||
handle: imp_sdp::SessionDescription { sys_handle: sdp },
|
||||
})
|
||||
}
|
||||
|
||||
pub fn current_remote_description(&self) -> Option<SessionDescription> {
|
||||
let sdp = self.sys_handle.current_remote_description();
|
||||
if sdp.is_null() {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(SessionDescription {
|
||||
handle: imp_sdp::SessionDescription { sys_handle: sdp },
|
||||
})
|
||||
}
|
||||
|
||||
pub fn remove_track(&self, sender: RtpSender) -> Result<(), RtcError> {
|
||||
self.sys_handle
|
||||
.remove_track(sender.handle.sys_handle)
|
||||
.map_err(|e| unsafe { sys_err::ffi::RTCError::from(e.what()).into() })
|
||||
}
|
||||
|
||||
pub fn senders(&self) -> Vec<RtpSender> {
|
||||
self.sys_handle
|
||||
.get_senders()
|
||||
.into_iter()
|
||||
.map(|sender| RtpSender {
|
||||
handle: imp_rs::RtpSender {
|
||||
sys_handle: sender.ptr,
|
||||
},
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn receivers(&self) -> Vec<RtpReceiver> {
|
||||
self.sys_handle
|
||||
.get_receivers()
|
||||
.into_iter()
|
||||
.map(|receiver| RtpReceiver {
|
||||
handle: imp_rr::RtpReceiver {
|
||||
sys_handle: receiver.ptr,
|
||||
},
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn transceivers(&self) -> Vec<RtpTransceiver> {
|
||||
self.sys_handle
|
||||
.get_transceivers()
|
||||
.into_iter()
|
||||
.map(|transceiver| RtpTransceiver {
|
||||
handle: imp_rt::RtpTransceiver {
|
||||
sys_handle: transceiver.ptr,
|
||||
},
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn on_connection_state_change(&self, f: Option<OnConnectionChange>) {
|
||||
*self.observer.connection_change_handler.lock().unwrap() = f;
|
||||
}
|
||||
|
||||
pub fn on_data_channel(&self, f: Option<OnDataChannel>) {
|
||||
*self.observer.data_channel_handler.lock().unwrap() = f;
|
||||
}
|
||||
|
||||
pub fn on_ice_candidate(&self, f: Option<OnIceCandidate>) {
|
||||
*self.observer.ice_candidate_handler.lock().unwrap() = f;
|
||||
}
|
||||
|
||||
pub fn on_ice_candidate_error(&self, f: Option<OnIceCandidateError>) {
|
||||
*self.observer.ice_candidate_error_handler.lock().unwrap() = f;
|
||||
}
|
||||
|
||||
pub fn on_ice_connection_state_change(&self, f: Option<OnIceConnectionChange>) {
|
||||
*self.observer.ice_connection_change_handler.lock().unwrap() = f;
|
||||
}
|
||||
|
||||
pub fn on_ice_gathering_state_change(&self, f: Option<OnIceGatheringChange>) {
|
||||
*self.observer.ice_gathering_change_handler.lock().unwrap() = f;
|
||||
}
|
||||
|
||||
pub fn on_negotiation_needed(&self, f: Option<OnNegotiationNeeded>) {
|
||||
*self.observer.negotiation_needed_handler.lock().unwrap() = f;
|
||||
}
|
||||
|
||||
pub fn on_signaling_state_change(&self, f: Option<OnSignalingChange>) {
|
||||
*self.observer.signaling_change_handler.lock().unwrap() = f;
|
||||
}
|
||||
|
||||
pub fn on_track(&self, f: Option<OnTrack>) {
|
||||
*self.observer.track_handler.lock().unwrap() = f;
|
||||
}
|
||||
}
|
||||
|
||||
fn create_sdp_observer() -> (
|
||||
UniquePtr<sys_pc::ffi::NativeCreateSdpObserverHandle>,
|
||||
oneshot::Receiver<SessionDescription>,
|
||||
oneshot::Receiver<RtcError>,
|
||||
) {
|
||||
let (sdp_tx, sdp_rx) = oneshot::channel();
|
||||
let (err_tx, err_rx) = oneshot::channel();
|
||||
|
||||
let wrapper = sys_jsep::CreateSdpObserverWrapper {
|
||||
on_success: ManuallyDrop::new(Box::new(move |session_description| {
|
||||
let _ = sdp_tx.send(SessionDescription {
|
||||
handle: imp_sdp::SessionDescription {
|
||||
sys_handle: session_description,
|
||||
},
|
||||
});
|
||||
})),
|
||||
on_failure: ManuallyDrop::new(Box::new(move |error| {
|
||||
let _ = err_tx.send(error.into());
|
||||
})),
|
||||
};
|
||||
|
||||
(
|
||||
sys_jsep::ffi::create_native_create_sdp_observer(Box::new(wrapper)),
|
||||
sdp_rx,
|
||||
err_rx,
|
||||
)
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct PeerObserver {
|
||||
pub connection_change_handler: Mutex<Option<OnConnectionChange>>,
|
||||
pub data_channel_handler: Mutex<Option<OnDataChannel>>,
|
||||
pub ice_candidate_handler: Mutex<Option<OnIceCandidate>>,
|
||||
pub ice_candidate_error_handler: Mutex<Option<OnIceCandidateError>>,
|
||||
pub ice_connection_change_handler: Mutex<Option<OnIceConnectionChange>>,
|
||||
pub ice_gathering_change_handler: Mutex<Option<OnIceGatheringChange>>,
|
||||
pub negotiation_needed_handler: Mutex<Option<OnNegotiationNeeded>>,
|
||||
pub signaling_change_handler: Mutex<Option<OnSignalingChange>>,
|
||||
pub track_handler: Mutex<Option<OnTrack>>,
|
||||
}
|
||||
|
||||
impl sys_pc::PeerConnectionObserver for PeerObserver {
|
||||
fn on_signaling_change(&self, new_state: sys_pc::ffi::SignalingState) {
|
||||
if let Some(f) = self.signaling_change_handler.lock().unwrap().as_mut() {
|
||||
f(new_state.into());
|
||||
}
|
||||
}
|
||||
|
||||
fn on_add_stream(&self, _stream: SharedPtr<webrtc_sys::media_stream::ffi::MediaStream>) {}
|
||||
|
||||
fn on_remove_stream(&self, _stream: SharedPtr<webrtc_sys::media_stream::ffi::MediaStream>) {}
|
||||
|
||||
fn on_data_channel(&self, data_channel: SharedPtr<sys_dc::ffi::DataChannel>) {
|
||||
if let Some(f) = self.data_channel_handler.lock().unwrap().as_mut() {
|
||||
f(DataChannel {
|
||||
handle: imp_dc::DataChannel::configure(data_channel),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn on_renegotiation_needed(&self) {}
|
||||
|
||||
fn on_negotiation_needed_event(&self, event: u32) {
|
||||
if let Some(f) = self.negotiation_needed_handler.lock().unwrap().as_mut() {
|
||||
f(event);
|
||||
}
|
||||
}
|
||||
|
||||
fn on_ice_connection_change(&self, _new_state: sys_pc::ffi::IceConnectionState) {}
|
||||
|
||||
fn on_standardized_ice_connection_change(&self, new_state: sys_pc::ffi::IceConnectionState) {
|
||||
if let Some(f) = self.ice_connection_change_handler.lock().unwrap().as_mut() {
|
||||
f(new_state.into());
|
||||
}
|
||||
}
|
||||
|
||||
fn on_connection_change(&self, new_state: sys_pc::ffi::PeerConnectionState) {
|
||||
if let Some(f) = self.connection_change_handler.lock().unwrap().as_mut() {
|
||||
f(new_state.into());
|
||||
}
|
||||
}
|
||||
|
||||
fn on_ice_gathering_change(&self, new_state: sys_pc::ffi::IceGatheringState) {
|
||||
if let Some(f) = self.ice_gathering_change_handler.lock().unwrap().as_mut() {
|
||||
f(new_state.into());
|
||||
}
|
||||
}
|
||||
|
||||
fn on_ice_candidate(&self, candidate: SharedPtr<sys_jsep::ffi::IceCandidate>) {
|
||||
if let Some(f) = self.ice_candidate_handler.lock().unwrap().as_mut() {
|
||||
f(IceCandidate {
|
||||
handle: imp_ic::IceCandidate {
|
||||
sys_handle: candidate,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn on_ice_candidate_error(
|
||||
&self,
|
||||
address: String,
|
||||
port: i32,
|
||||
url: String,
|
||||
error_code: i32,
|
||||
error_text: String,
|
||||
) {
|
||||
if let Some(f) = self.ice_candidate_error_handler.lock().unwrap().as_mut() {
|
||||
f(IceCandidateError {
|
||||
address,
|
||||
port,
|
||||
url,
|
||||
error_code,
|
||||
error_text,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn on_ice_candidates_removed(
|
||||
&self,
|
||||
_removed: Vec<SharedPtr<webrtc_sys::candidate::ffi::Candidate>>,
|
||||
) {
|
||||
}
|
||||
|
||||
fn on_ice_connection_receiving_change(&self, _receiving: bool) {}
|
||||
|
||||
fn on_ice_selected_candidate_pair_changed(
|
||||
&self,
|
||||
_event: sys_pc::ffi::CandidatePairChangeEvent,
|
||||
) {
|
||||
}
|
||||
|
||||
fn on_add_track(
|
||||
&self,
|
||||
_receiver: SharedPtr<webrtc_sys::rtp_receiver::ffi::RtpReceiver>,
|
||||
_streams: Vec<SharedPtr<webrtc_sys::media_stream::ffi::MediaStream>>,
|
||||
) {
|
||||
}
|
||||
|
||||
fn on_track(&self, transceiver: SharedPtr<webrtc_sys::rtp_transceiver::ffi::RtpTransceiver>) {
|
||||
if let Some(f) = self.track_handler.lock().unwrap().as_mut() {
|
||||
let receiver = transceiver.receiver();
|
||||
let streams = receiver.streams();
|
||||
let track = receiver.track();
|
||||
|
||||
f(TrackEvent {
|
||||
receiver: RtpReceiver {
|
||||
handle: imp_rr::RtpReceiver {
|
||||
sys_handle: receiver,
|
||||
},
|
||||
},
|
||||
streams: streams
|
||||
.into_iter()
|
||||
.map(|s| MediaStream {
|
||||
handle: imp_ms::MediaStream { sys_handle: s.ptr },
|
||||
})
|
||||
.collect(),
|
||||
track: imp_ms::new_media_stream_track(track),
|
||||
transceiver: RtpTransceiver {
|
||||
handle: imp_rt::RtpTransceiver {
|
||||
sys_handle: transceiver,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn on_remove_track(&self, _receiver: SharedPtr<webrtc_sys::rtp_receiver::ffi::RtpReceiver>) {}
|
||||
|
||||
fn on_interesting_usage(&self, _usage_pattern: i32) {}
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
use crate::imp::media_stream as imp_ms;
|
||||
use crate::imp::peer_connection as imp_pc;
|
||||
use crate::media_stream::RtcVideoTrack;
|
||||
use crate::peer_connection::PeerConnection;
|
||||
use crate::peer_connection_factory::{
|
||||
ContinualGatheringPolicy, IceServer, IceTransportsType, RtcConfiguration,
|
||||
};
|
||||
use crate::rtp_parameters::RtpCapabilities;
|
||||
use crate::video_source::native::NativeVideoSource;
|
||||
use crate::MediaType;
|
||||
use crate::RtcError;
|
||||
use cxx::SharedPtr;
|
||||
use std::sync::Arc;
|
||||
use webrtc_sys::peer_connection as sys_pc;
|
||||
use webrtc_sys::peer_connection_factory as sys_pcf;
|
||||
use webrtc_sys::rtc_error as sys_err;
|
||||
use webrtc_sys::webrtc as sys_webrtc;
|
||||
|
||||
impl From<IceServer> for sys_pcf::ffi::ICEServer {
|
||||
fn from(value: IceServer) -> Self {
|
||||
sys_pcf::ffi::ICEServer {
|
||||
urls: value.urls,
|
||||
username: value.username,
|
||||
password: value.password,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ContinualGatheringPolicy> for sys_pcf::ffi::ContinualGatheringPolicy {
|
||||
fn from(value: ContinualGatheringPolicy) -> Self {
|
||||
match value {
|
||||
ContinualGatheringPolicy::GatherOnce => {
|
||||
sys_pcf::ffi::ContinualGatheringPolicy::GatherOnce
|
||||
}
|
||||
ContinualGatheringPolicy::GatherContinually => {
|
||||
sys_pcf::ffi::ContinualGatheringPolicy::GatherContinually
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<IceTransportsType> for sys_pcf::ffi::IceTransportsType {
|
||||
fn from(value: IceTransportsType) -> Self {
|
||||
match value {
|
||||
IceTransportsType::None => sys_pcf::ffi::IceTransportsType::None,
|
||||
IceTransportsType::Relay => sys_pcf::ffi::IceTransportsType::Relay,
|
||||
IceTransportsType::NoHost => sys_pcf::ffi::IceTransportsType::NoHost,
|
||||
IceTransportsType::All => sys_pcf::ffi::IceTransportsType::All,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RtcConfiguration> for sys_pcf::ffi::RTCConfiguration {
|
||||
fn from(value: RtcConfiguration) -> Self {
|
||||
Self {
|
||||
ice_servers: value.ice_servers.into_iter().map(Into::into).collect(),
|
||||
continual_gathering_policy: value.continual_gathering_policy.into(),
|
||||
ice_transport_type: value.ice_transport_type.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct RTCRuntime {
|
||||
pub(crate) sys_handle: SharedPtr<sys_webrtc::ffi::RTCRuntime>,
|
||||
}
|
||||
|
||||
impl Default for RTCRuntime {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
sys_handle: sys_webrtc::ffi::create_rtc_runtime(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct PeerConnectionFactory {
|
||||
sys_handle: SharedPtr<sys_pcf::ffi::PeerConnectionFactory>,
|
||||
|
||||
#[allow(unused)]
|
||||
runtime: RTCRuntime,
|
||||
}
|
||||
|
||||
impl Default for PeerConnectionFactory {
|
||||
fn default() -> Self {
|
||||
let runtime = RTCRuntime::default();
|
||||
Self {
|
||||
sys_handle: sys_pcf::ffi::create_peer_connection_factory(runtime.sys_handle.clone()),
|
||||
runtime,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PeerConnectionFactory {
|
||||
pub fn create_peer_connection(
|
||||
&self,
|
||||
config: RtcConfiguration,
|
||||
) -> Result<PeerConnection, RtcError> {
|
||||
let native_config = sys_pcf::ffi::create_rtc_configuration(config.into());
|
||||
|
||||
unsafe {
|
||||
let observer = Arc::new(imp_pc::PeerObserver::default());
|
||||
let native_observer = sys_pc::ffi::create_native_peer_connection_observer(
|
||||
self.runtime.clone().sys_handle,
|
||||
Box::new(sys_pc::PeerConnectionObserverWrapper::new(observer.clone())),
|
||||
);
|
||||
|
||||
let res = self
|
||||
.sys_handle
|
||||
.create_peer_connection(native_config, &*native_observer as *const _ as *mut _);
|
||||
|
||||
match res {
|
||||
Ok(sys_handle) => Ok(PeerConnection {
|
||||
handle: imp_pc::PeerConnection::configure(
|
||||
sys_handle,
|
||||
observer,
|
||||
native_observer,
|
||||
),
|
||||
}),
|
||||
Err(e) => Err(sys_err::ffi::RTCError::from(e.what()).into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_video_track(&self, label: &str, source: NativeVideoSource) -> RtcVideoTrack {
|
||||
RtcVideoTrack {
|
||||
handle: imp_ms::RtcVideoTrack {
|
||||
sys_handle: self
|
||||
.sys_handle
|
||||
.create_video_track(label.to_string(), source.handle.sys_handle()),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_rtp_sender_capabilities(&self, media_type: MediaType) -> RtpCapabilities {
|
||||
self.sys_handle
|
||||
.get_rtp_sender_capabilities(media_type.into())
|
||||
.into()
|
||||
}
|
||||
|
||||
pub fn get_rtp_receiver_capabilities(&self, media_type: MediaType) -> RtpCapabilities {
|
||||
self.sys_handle
|
||||
.get_rtp_receiver_capabilities(media_type.into())
|
||||
.into()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,285 @@
|
||||
use crate::rtp_parameters::*;
|
||||
use crate::MediaType;
|
||||
use webrtc_sys::rtp_parameters as sys_rp;
|
||||
use webrtc_sys::webrtc as sys_webrtc;
|
||||
|
||||
impl From<sys_webrtc::ffi::Priority> for Priority {
|
||||
fn from(value: sys_webrtc::ffi::Priority) -> Self {
|
||||
match value {
|
||||
sys_webrtc::ffi::Priority::VeryLow => Self::VeryLow,
|
||||
sys_webrtc::ffi::Priority::Low => Self::Low,
|
||||
sys_webrtc::ffi::Priority::Medium => Self::Medium,
|
||||
sys_webrtc::ffi::Priority::High => Self::High,
|
||||
_ => panic!("unknown Priority"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<sys_rp::ffi::RtpExtension> for RtpHeaderExtensionParameters {
|
||||
fn from(value: sys_rp::ffi::RtpExtension) -> Self {
|
||||
Self {
|
||||
uri: value.uri,
|
||||
id: value.id,
|
||||
encrypted: value.encrypt,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<sys_rp::ffi::RtpParameters> for RtpParameters {
|
||||
fn from(value: sys_rp::ffi::RtpParameters) -> Self {
|
||||
Self {
|
||||
codecs: value.codecs.into_iter().map(Into::into).collect(),
|
||||
header_extensions: value
|
||||
.header_extensions
|
||||
.into_iter()
|
||||
.map(Into::into)
|
||||
.collect(),
|
||||
rtcp: value.rtcp.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<sys_rp::ffi::RtpCodecParameters> for RtpCodecParameters {
|
||||
fn from(value: sys_rp::ffi::RtpCodecParameters) -> Self {
|
||||
Self {
|
||||
mime_type: value.mime_type,
|
||||
payload_type: value.payload_type as u8,
|
||||
clock_rate: value.has_clock_rate.then_some(value.clock_rate as u64),
|
||||
channels: value.has_num_channels.then_some(value.num_channels as u16),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<sys_rp::ffi::RtcpParameters> for RtcpParameters {
|
||||
fn from(value: sys_rp::ffi::RtcpParameters) -> Self {
|
||||
Self {
|
||||
cname: value.cname,
|
||||
reduced_size: value.reduced_size,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<sys_rp::ffi::RtpEncodingParameters> for RtpEncodingParameters {
|
||||
fn from(value: sys_rp::ffi::RtpEncodingParameters) -> Self {
|
||||
Self {
|
||||
active: value.active,
|
||||
max_bitrate: value
|
||||
.has_max_bitrate_bps
|
||||
.then_some(value.max_bitrate_bps as u64),
|
||||
max_framerate: value.has_max_framerate.then_some(value.max_framerate),
|
||||
priority: value.network_priority.into(),
|
||||
rid: value.rid,
|
||||
scale_resolution_down_by: value
|
||||
.has_scale_resolution_down_by
|
||||
.then_some(value.scale_resolution_down_by),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<sys_rp::ffi::RtpCodecCapability> for RtpCodecCapability {
|
||||
fn from(value: sys_rp::ffi::RtpCodecCapability) -> Self {
|
||||
Self {
|
||||
channels: value.has_num_channels.then_some(value.num_channels as u16),
|
||||
mime_type: value.mime_type,
|
||||
clock_rate: value.has_clock_rate.then_some(value.clock_rate as u64),
|
||||
sdp_fmtp_line: {
|
||||
let parameters: Vec<String> = value
|
||||
.parameters
|
||||
.into_iter()
|
||||
.map(|key_value| {
|
||||
if !key_value.key.is_empty() {
|
||||
format!("{}={}", key_value.key, key_value.value)
|
||||
} else {
|
||||
key_value.value
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
if !parameters.is_empty() {
|
||||
Some(parameters.join(";"))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<sys_rp::ffi::RtpHeaderExtensionCapability> for RtpHeaderExtensionCapability {
|
||||
fn from(value: sys_rp::ffi::RtpHeaderExtensionCapability) -> Self {
|
||||
Self {
|
||||
direction: value.direction.into(),
|
||||
uri: value.uri,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<sys_rp::ffi::RtpCapabilities> for RtpCapabilities {
|
||||
fn from(value: sys_rp::ffi::RtpCapabilities) -> Self {
|
||||
Self {
|
||||
codecs: value.codecs.into_iter().map(Into::into).collect(),
|
||||
header_extensions: value
|
||||
.header_extensions
|
||||
.into_iter()
|
||||
.map(Into::into)
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Priority> for sys_webrtc::ffi::Priority {
|
||||
fn from(value: Priority) -> Self {
|
||||
match value {
|
||||
Priority::VeryLow => Self::VeryLow,
|
||||
Priority::Low => Self::Low,
|
||||
Priority::Medium => Self::Medium,
|
||||
Priority::High => Self::High,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RtpHeaderExtensionParameters> for sys_rp::ffi::RtpExtension {
|
||||
fn from(value: RtpHeaderExtensionParameters) -> Self {
|
||||
Self {
|
||||
uri: value.uri,
|
||||
id: value.id,
|
||||
encrypt: value.encrypted,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RtpParameters> for sys_rp::ffi::RtpParameters {
|
||||
fn from(value: RtpParameters) -> Self {
|
||||
Self {
|
||||
codecs: value.codecs.into_iter().map(Into::into).collect(),
|
||||
header_extensions: value
|
||||
.header_extensions
|
||||
.into_iter()
|
||||
.map(Into::into)
|
||||
.collect(),
|
||||
encodings: Vec::new(),
|
||||
rtcp: value.rtcp.into(),
|
||||
transaction_id: "".to_string(),
|
||||
mid: "".to_string(),
|
||||
has_degradation_preference: false,
|
||||
degradation_preference: sys_rp::ffi::DegradationPreference::Balanced,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RtpCodecParameters> for sys_rp::ffi::RtpCodecParameters {
|
||||
fn from(value: RtpCodecParameters) -> Self {
|
||||
Self {
|
||||
payload_type: value.payload_type as i32,
|
||||
mime_type: value.mime_type,
|
||||
has_clock_rate: value.clock_rate.is_some(),
|
||||
clock_rate: value.clock_rate.unwrap_or_default() as i32,
|
||||
has_num_channels: value.channels.is_some(),
|
||||
num_channels: value.channels.unwrap_or_default() as i32,
|
||||
name: "".to_string(),
|
||||
kind: sys_rp::ffi::MediaType::Audio,
|
||||
has_max_ptime: false,
|
||||
max_ptime: 0,
|
||||
has_ptime: false,
|
||||
ptime: 0,
|
||||
rtcp_feedback: Vec::new(),
|
||||
parameters: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RtcpParameters> for sys_rp::ffi::RtcpParameters {
|
||||
fn from(value: RtcpParameters) -> Self {
|
||||
Self {
|
||||
cname: value.cname,
|
||||
reduced_size: value.reduced_size,
|
||||
has_ssrc: false,
|
||||
ssrc: 0,
|
||||
mux: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RtpEncodingParameters> for sys_rp::ffi::RtpEncodingParameters {
|
||||
fn from(value: RtpEncodingParameters) -> Self {
|
||||
Self {
|
||||
active: value.active,
|
||||
has_max_bitrate_bps: value.max_bitrate.is_some(),
|
||||
max_bitrate_bps: value.max_bitrate.unwrap_or_default() as i32,
|
||||
has_max_framerate: value.max_framerate.is_some(),
|
||||
max_framerate: value.max_framerate.unwrap_or_default(),
|
||||
network_priority: value.priority.into(),
|
||||
rid: value.rid,
|
||||
has_scale_resolution_down_by: value.scale_resolution_down_by.is_some(),
|
||||
scale_resolution_down_by: value.scale_resolution_down_by.unwrap_or_default(),
|
||||
adaptive_ptime: false,
|
||||
bitrate_priority: sys_rp::DEFAULT_BITRATE_PRIORITY,
|
||||
has_min_bitrate_bps: false,
|
||||
min_bitrate_bps: 0,
|
||||
has_num_temporal_layers: false,
|
||||
num_temporal_layers: 0,
|
||||
has_scalability_mode: false,
|
||||
scalability_mode: "".to_string(),
|
||||
has_ssrc: false,
|
||||
ssrc: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RtpCodecCapability> for sys_rp::ffi::RtpCodecCapability {
|
||||
fn from(value: RtpCodecCapability) -> Self {
|
||||
let mime_type: Vec<&str> = value.mime_type.split('/').collect();
|
||||
let kind = match mime_type[0] {
|
||||
"audio" => sys_webrtc::ffi::MediaType::Audio,
|
||||
"video" => sys_webrtc::ffi::MediaType::Video,
|
||||
_ => panic!("invalid media type"),
|
||||
};
|
||||
let name = mime_type[1].to_string();
|
||||
|
||||
Self {
|
||||
name,
|
||||
kind,
|
||||
has_clock_rate: value.clock_rate.is_some(),
|
||||
clock_rate: value.clock_rate.unwrap_or_default() as i32,
|
||||
has_num_channels: value.channels.is_some(),
|
||||
num_channels: value.channels.unwrap_or_default() as i32,
|
||||
parameters: {
|
||||
value
|
||||
.sdp_fmtp_line
|
||||
.map(|sdp_fmtp_line| {
|
||||
sdp_fmtp_line
|
||||
.split(';')
|
||||
.map(|v| {
|
||||
let key_value: Vec<&str> = v.split('=').collect();
|
||||
if key_value.len() == 2 {
|
||||
sys_rp::ffi::StringKeyValue {
|
||||
key: key_value[0].to_string(),
|
||||
value: key_value[1].to_string(),
|
||||
}
|
||||
} else {
|
||||
sys_rp::ffi::StringKeyValue {
|
||||
key: "".to_string(),
|
||||
value: key_value[0].to_string(),
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
.unwrap_or_default()
|
||||
},
|
||||
// Ignore
|
||||
mime_type: String::default(), // !!
|
||||
has_preferred_payload_type: false,
|
||||
preferred_payload_type: 0,
|
||||
has_max_ptime: false,
|
||||
max_ptime: 0,
|
||||
has_ptime: false,
|
||||
ptime: 0,
|
||||
rtcp_feedback: Vec::default(),
|
||||
options: Vec::default(),
|
||||
max_temporal_layer_extensions: 0,
|
||||
max_spatial_layer_extensions: 0,
|
||||
svc_multi_stream_support: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
use super::media_stream::new_media_stream_track;
|
||||
use crate::{media_stream::MediaStreamTrack, rtp_parameters::RtpParameters};
|
||||
use cxx::SharedPtr;
|
||||
use webrtc_sys::rtp_receiver as sys_rr;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct RtpReceiver {
|
||||
pub(crate) sys_handle: SharedPtr<sys_rr::ffi::RtpReceiver>,
|
||||
}
|
||||
|
||||
impl RtpReceiver {
|
||||
pub fn track(&self) -> Option<MediaStreamTrack> {
|
||||
let track_handle = self.sys_handle.track();
|
||||
if track_handle.is_null() {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(new_media_stream_track(track_handle))
|
||||
}
|
||||
|
||||
pub fn parameters(&self) -> RtpParameters {
|
||||
self.sys_handle.get_parameters().into()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
use super::media_stream::new_media_stream_track;
|
||||
use crate::{
|
||||
media_stream::MediaStreamTrack, rtp_parameters::RtpParameters, RtcError, RtcErrorType,
|
||||
};
|
||||
use cxx::SharedPtr;
|
||||
use webrtc_sys::{rtc_error::ffi::RTCError, rtp_sender as sys_rs};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct RtpSender {
|
||||
pub(crate) sys_handle: SharedPtr<sys_rs::ffi::RtpSender>,
|
||||
}
|
||||
|
||||
impl RtpSender {
|
||||
pub fn track(&self) -> Option<MediaStreamTrack> {
|
||||
let track_handle = self.sys_handle.track();
|
||||
if track_handle.is_null() {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(new_media_stream_track(track_handle))
|
||||
}
|
||||
|
||||
pub fn set_track(&self, track: Option<MediaStreamTrack>) -> Result<(), RtcError> {
|
||||
if !self
|
||||
.sys_handle
|
||||
.set_track(track.map_or(SharedPtr::null(), |t| t.sys_handle()))
|
||||
{
|
||||
return Err(RtcError {
|
||||
error_type: RtcErrorType::InvalidState,
|
||||
message: "Failed to set track".to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn parameters(&self) -> RtpParameters {
|
||||
self.sys_handle.get_parameters().into()
|
||||
}
|
||||
|
||||
pub fn set_parameters(&self, parameters: RtpParameters) -> Result<(), RtcError> {
|
||||
self.sys_handle
|
||||
.set_parameters(parameters.into())
|
||||
.map_err(|e| unsafe { RTCError::from(e.what()).into() })
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
use crate::imp::rtp_receiver::RtpReceiver;
|
||||
use crate::imp::rtp_sender::RtpSender;
|
||||
use crate::rtp_parameters::RtpCodecCapability;
|
||||
use crate::rtp_receiver;
|
||||
use crate::rtp_sender;
|
||||
use crate::rtp_transceiver::RtpTransceiverDirection;
|
||||
use crate::rtp_transceiver::RtpTransceiverInit;
|
||||
use crate::MediaType;
|
||||
use crate::RtcError;
|
||||
use cxx::SharedPtr;
|
||||
use webrtc_sys::rtc_error as sys_err;
|
||||
use webrtc_sys::rtp_transceiver as sys_rt;
|
||||
use webrtc_sys::webrtc as sys_webrtc;
|
||||
|
||||
impl From<sys_webrtc::ffi::RtpTransceiverDirection> for RtpTransceiverDirection {
|
||||
fn from(value: sys_webrtc::ffi::RtpTransceiverDirection) -> Self {
|
||||
match value {
|
||||
sys_webrtc::ffi::RtpTransceiverDirection::SendRecv => Self::SendRecv,
|
||||
sys_webrtc::ffi::RtpTransceiverDirection::SendOnly => Self::SendOnly,
|
||||
sys_webrtc::ffi::RtpTransceiverDirection::RecvOnly => Self::RecvOnly,
|
||||
sys_webrtc::ffi::RtpTransceiverDirection::Inactive => Self::Inactive,
|
||||
_ => panic!("unknown RtpTransceiverDirection"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RtpTransceiverDirection> for sys_webrtc::ffi::RtpTransceiverDirection {
|
||||
fn from(value: RtpTransceiverDirection) -> Self {
|
||||
match value {
|
||||
RtpTransceiverDirection::SendRecv => Self::SendRecv,
|
||||
RtpTransceiverDirection::SendOnly => Self::SendOnly,
|
||||
RtpTransceiverDirection::RecvOnly => Self::RecvOnly,
|
||||
RtpTransceiverDirection::Inactive => Self::Inactive,
|
||||
_ => panic!("unknown RtpTransceiverDirection"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RtpTransceiverInit> for sys_rt::ffi::RtpTransceiverInit {
|
||||
fn from(value: RtpTransceiverInit) -> Self {
|
||||
Self {
|
||||
direction: value.direction.into(),
|
||||
stream_ids: value.stream_ids,
|
||||
send_encodings: value.send_encodings.into_iter().map(Into::into).collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct RtpTransceiver {
|
||||
pub(crate) sys_handle: SharedPtr<sys_rt::ffi::RtpTransceiver>,
|
||||
}
|
||||
|
||||
impl RtpTransceiver {
|
||||
pub fn mid(&self) -> Option<String> {
|
||||
self.sys_handle.mid().ok()
|
||||
}
|
||||
|
||||
pub fn current_direction(&self) -> Option<RtpTransceiverDirection> {
|
||||
self.sys_handle.current_direction().ok().map(Into::into)
|
||||
}
|
||||
|
||||
pub fn direction(&self) -> RtpTransceiverDirection {
|
||||
self.sys_handle.direction().into()
|
||||
}
|
||||
|
||||
pub fn sender(&self) -> rtp_sender::RtpSender {
|
||||
rtp_sender::RtpSender {
|
||||
handle: RtpSender {
|
||||
sys_handle: self.sys_handle.sender(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn receiver(&self) -> rtp_receiver::RtpReceiver {
|
||||
rtp_receiver::RtpReceiver {
|
||||
handle: RtpReceiver {
|
||||
sys_handle: self.sys_handle.receiver(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_codec_preferences(&self, codecs: Vec<RtpCodecCapability>) -> Result<(), RtcError> {
|
||||
self.sys_handle
|
||||
.set_codec_preferences(codecs.into_iter().map(Into::into).collect())
|
||||
.map_err(|e| unsafe { sys_err::ffi::RTCError::from(e.what()).into() })
|
||||
}
|
||||
|
||||
pub fn stop(&self) -> Result<(), RtcError> {
|
||||
self.sys_handle
|
||||
.stop_standard()
|
||||
.map_err(|e| unsafe { sys_err::ffi::RTCError::from(e.what()).into() })
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
use crate::session_description::{self, SdpParseError, SdpType};
|
||||
use cxx::UniquePtr;
|
||||
use webrtc_sys::jsep as sys_jsep;
|
||||
|
||||
impl From<sys_jsep::ffi::SdpType> for SdpType {
|
||||
fn from(sdp_type: sys_jsep::ffi::SdpType) -> Self {
|
||||
match sdp_type {
|
||||
sys_jsep::ffi::SdpType::Offer => SdpType::Offer,
|
||||
sys_jsep::ffi::SdpType::PrAnswer => SdpType::PrAnswer,
|
||||
sys_jsep::ffi::SdpType::Answer => SdpType::Answer,
|
||||
sys_jsep::ffi::SdpType::Rollback => SdpType::Rollback,
|
||||
_ => panic!("unknown SdpType"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SdpType> for sys_jsep::ffi::SdpType {
|
||||
fn from(sdp_type: SdpType) -> Self {
|
||||
match sdp_type {
|
||||
SdpType::Offer => sys_jsep::ffi::SdpType::Offer,
|
||||
SdpType::PrAnswer => sys_jsep::ffi::SdpType::PrAnswer,
|
||||
SdpType::Answer => sys_jsep::ffi::SdpType::Answer,
|
||||
SdpType::Rollback => sys_jsep::ffi::SdpType::Rollback,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<sys_jsep::ffi::SdpParseError> for SdpParseError {
|
||||
fn from(e: sys_jsep::ffi::SdpParseError) -> Self {
|
||||
Self {
|
||||
line: e.line,
|
||||
description: e.description,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SessionDescription {
|
||||
pub(crate) sys_handle: UniquePtr<sys_jsep::ffi::SessionDescription>,
|
||||
}
|
||||
|
||||
impl SessionDescription {
|
||||
pub fn parse(
|
||||
sdp: &str,
|
||||
sdp_type: SdpType,
|
||||
) -> Result<session_description::SessionDescription, SdpParseError> {
|
||||
let res = sys_jsep::ffi::create_session_description(sdp_type.into(), sdp.to_owned());
|
||||
match res {
|
||||
Ok(sys_handle) => Ok(session_description::SessionDescription {
|
||||
handle: SessionDescription { sys_handle },
|
||||
}),
|
||||
Err(e) => Err(unsafe { sys_jsep::ffi::SdpParseError::from(e.what()).into() }),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sdp_type(&self) -> SdpType {
|
||||
self.sys_handle.sdp_type().into()
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for SessionDescription {
|
||||
fn to_string(&self) -> String {
|
||||
self.sys_handle.stringify()
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for SessionDescription {
|
||||
fn clone(&self) -> Self {
|
||||
SessionDescription {
|
||||
sys_handle: self.sys_handle.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,764 @@
|
||||
use super::yuv_helper::{self, ConvertError};
|
||||
use crate::video_frame::VideoRotation;
|
||||
use crate::video_frame::{self as vf, VideoFormatType};
|
||||
use cxx::UniquePtr;
|
||||
use std::slice;
|
||||
use webrtc_sys::video_frame as vf_sys;
|
||||
use webrtc_sys::video_frame_buffer as vfb_sys;
|
||||
|
||||
/// We don't use vf::VideoFrameBuffer trait for the types inside this module to avoid confusion
|
||||
/// because irectly using platform specific types is not valid (e.g user callback)
|
||||
/// All the types inside this module are only used internally. For public types, see the top level video_frame.rs
|
||||
|
||||
pub fn new_video_frame_buffer(
|
||||
mut sys_handle: UniquePtr<vfb_sys::ffi::VideoFrameBuffer>,
|
||||
) -> Box<dyn vf::VideoFrameBuffer + Send + Sync> {
|
||||
unsafe {
|
||||
match sys_handle.buffer_type().into() {
|
||||
vfb_sys::ffi::VideoFrameBufferType::Native => Box::new(vf::native::NativeBuffer {
|
||||
handle: NativeBuffer { sys_handle },
|
||||
}),
|
||||
vfb_sys::ffi::VideoFrameBufferType::I420 => Box::new(vf::I420Buffer {
|
||||
handle: I420Buffer {
|
||||
sys_handle: sys_handle.pin_mut().get_i420(),
|
||||
},
|
||||
}),
|
||||
vfb_sys::ffi::VideoFrameBufferType::I420A => Box::new(vf::I420ABuffer {
|
||||
handle: I420ABuffer {
|
||||
sys_handle: sys_handle.pin_mut().get_i420a(),
|
||||
},
|
||||
}),
|
||||
vfb_sys::ffi::VideoFrameBufferType::I422 => Box::new(vf::I422Buffer {
|
||||
handle: I422Buffer {
|
||||
sys_handle: sys_handle.pin_mut().get_i422(),
|
||||
},
|
||||
}),
|
||||
vfb_sys::ffi::VideoFrameBufferType::I444 => Box::new(vf::I444Buffer {
|
||||
handle: I444Buffer {
|
||||
sys_handle: sys_handle.pin_mut().get_i444(),
|
||||
},
|
||||
}),
|
||||
vfb_sys::ffi::VideoFrameBufferType::I010 => Box::new(vf::I010Buffer {
|
||||
handle: I010Buffer {
|
||||
sys_handle: sys_handle.pin_mut().get_i010(),
|
||||
},
|
||||
}),
|
||||
vfb_sys::ffi::VideoFrameBufferType::NV12 => Box::new(vf::NV12Buffer {
|
||||
handle: NV12Buffer {
|
||||
sys_handle: sys_handle.pin_mut().get_nv12(),
|
||||
},
|
||||
}),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<vf_sys::ffi::VideoRotation> for VideoRotation {
|
||||
fn from(rotation: vf_sys::ffi::VideoRotation) -> Self {
|
||||
match rotation {
|
||||
vf_sys::ffi::VideoRotation::VideoRotation0 => Self::VideoRotation0,
|
||||
vf_sys::ffi::VideoRotation::VideoRotation90 => Self::VideoRotation90,
|
||||
vf_sys::ffi::VideoRotation::VideoRotation180 => Self::VideoRotation180,
|
||||
vf_sys::ffi::VideoRotation::VideoRotation270 => Self::VideoRotation270,
|
||||
_ => panic!("invalid VideoRotation"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<VideoRotation> for vf_sys::ffi::VideoRotation {
|
||||
fn from(rotation: VideoRotation) -> Self {
|
||||
match rotation {
|
||||
VideoRotation::VideoRotation0 => Self::VideoRotation0,
|
||||
VideoRotation::VideoRotation90 => Self::VideoRotation90,
|
||||
VideoRotation::VideoRotation180 => Self::VideoRotation180,
|
||||
VideoRotation::VideoRotation270 => Self::VideoRotation270,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! recursive_cast {
|
||||
($ptr:expr $(, $fnc:ident)*) => {
|
||||
{
|
||||
let ptr = $ptr;
|
||||
$(
|
||||
let ptr = vfb_sys::ffi::$fnc(ptr);
|
||||
)*
|
||||
ptr
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub struct NativeBuffer {
|
||||
sys_handle: UniquePtr<vfb_sys::ffi::VideoFrameBuffer>,
|
||||
}
|
||||
|
||||
pub struct I420Buffer {
|
||||
sys_handle: UniquePtr<vfb_sys::ffi::I420Buffer>,
|
||||
}
|
||||
|
||||
pub struct I420ABuffer {
|
||||
sys_handle: UniquePtr<vfb_sys::ffi::I420ABuffer>,
|
||||
}
|
||||
|
||||
pub struct I422Buffer {
|
||||
sys_handle: UniquePtr<vfb_sys::ffi::I422Buffer>,
|
||||
}
|
||||
|
||||
pub struct I444Buffer {
|
||||
sys_handle: UniquePtr<vfb_sys::ffi::I444Buffer>,
|
||||
}
|
||||
|
||||
pub struct I010Buffer {
|
||||
sys_handle: UniquePtr<vfb_sys::ffi::I010Buffer>,
|
||||
}
|
||||
|
||||
pub struct NV12Buffer {
|
||||
sys_handle: UniquePtr<vfb_sys::ffi::NV12Buffer>,
|
||||
}
|
||||
|
||||
macro_rules! impl_to_argb {
|
||||
(I420Buffer [$($variant:ident: $fnc:ident),+], $format:ident, $self:ident, $dst:ident, $dst_stride:ident, $dst_width:ident, $dst_height:ident) => {
|
||||
match $format {
|
||||
$(
|
||||
VideoFormatType::$variant => {
|
||||
let (data_y, data_u, data_v) = $self.data();
|
||||
yuv_helper::$fnc(
|
||||
data_y,
|
||||
$self.stride_y(),
|
||||
data_u,
|
||||
$self.stride_u(),
|
||||
data_v,
|
||||
$self.stride_v(),
|
||||
$dst,
|
||||
$dst_stride,
|
||||
$dst_width,
|
||||
$dst_height,
|
||||
)
|
||||
}
|
||||
)+
|
||||
}
|
||||
};
|
||||
(I420ABuffer) => {
|
||||
todo!();
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused_unsafe)]
|
||||
impl NativeBuffer {
|
||||
pub fn sys_handle(&self) -> &vfb_sys::ffi::VideoFrameBuffer {
|
||||
&*self.sys_handle
|
||||
}
|
||||
|
||||
pub fn width(&self) -> i32 {
|
||||
self.sys_handle.width()
|
||||
}
|
||||
|
||||
pub fn height(&self) -> i32 {
|
||||
self.sys_handle.height()
|
||||
}
|
||||
|
||||
pub fn to_i420(&self) -> I420Buffer {
|
||||
I420Buffer {
|
||||
sys_handle: unsafe { self.sys_handle.to_i420() },
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_argb(
|
||||
&self,
|
||||
format: VideoFormatType,
|
||||
dst: &mut [u8],
|
||||
dst_stride: i32,
|
||||
dst_width: i32,
|
||||
dst_height: i32,
|
||||
) -> Result<(), ConvertError> {
|
||||
self.to_i420()
|
||||
.to_argb(format, dst, dst_stride, dst_width, dst_height)
|
||||
}
|
||||
}
|
||||
|
||||
impl I420Buffer {
|
||||
pub fn new(width: u32, height: u32) -> vf::I420Buffer {
|
||||
vf::I420Buffer {
|
||||
handle: I420Buffer {
|
||||
sys_handle: vfb_sys::ffi::new_i420_buffer(
|
||||
width.try_into().unwrap(),
|
||||
height.try_into().unwrap(),
|
||||
),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sys_handle(&self) -> &vfb_sys::ffi::VideoFrameBuffer {
|
||||
unsafe { &*recursive_cast!(&*self.sys_handle, i420_to_yuv8, yuv8_to_yuv, yuv_to_vfb) }
|
||||
}
|
||||
|
||||
pub fn width(&self) -> i32 {
|
||||
unsafe {
|
||||
let ptr = recursive_cast!(&*self.sys_handle, i420_to_yuv8, yuv8_to_yuv, yuv_to_vfb);
|
||||
(*ptr).width()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn height(&self) -> i32 {
|
||||
unsafe {
|
||||
let ptr = recursive_cast!(&*self.sys_handle, i420_to_yuv8, yuv8_to_yuv, yuv_to_vfb);
|
||||
(*ptr).height()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn chroma_width(&self) -> i32 {
|
||||
unsafe {
|
||||
let ptr = recursive_cast!(&*self.sys_handle, i420_to_yuv8, yuv8_to_yuv);
|
||||
(*ptr).chroma_width()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn chroma_height(&self) -> i32 {
|
||||
unsafe {
|
||||
let ptr = recursive_cast!(&*self.sys_handle, i420_to_yuv8, yuv8_to_yuv);
|
||||
(*ptr).chroma_height()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stride_y(&self) -> i32 {
|
||||
unsafe {
|
||||
let ptr = recursive_cast!(&*self.sys_handle, i420_to_yuv8, yuv8_to_yuv);
|
||||
(*ptr).stride_y()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stride_u(&self) -> i32 {
|
||||
unsafe {
|
||||
let ptr = recursive_cast!(&*self.sys_handle, i420_to_yuv8, yuv8_to_yuv);
|
||||
(*ptr).stride_u()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stride_v(&self) -> i32 {
|
||||
unsafe {
|
||||
let ptr = recursive_cast!(&*self.sys_handle, i420_to_yuv8, yuv8_to_yuv);
|
||||
(*ptr).stride_v()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_i420(&self) -> I420Buffer {
|
||||
I420Buffer {
|
||||
sys_handle: unsafe {
|
||||
// We make a copy of the buffer because internally, when calling ToI420()
|
||||
// if the buffer is of type I420, libwebrtc will reuse the same underlying pointer
|
||||
// for the new created type
|
||||
let copy = vfb_sys::ffi::copy_i420_buffer(&self.sys_handle);
|
||||
let ptr = recursive_cast!(&*copy, i420_to_yuv8, yuv8_to_yuv, yuv_to_vfb);
|
||||
(*ptr).to_i420()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_argb(
|
||||
&self,
|
||||
format: VideoFormatType,
|
||||
dst: &mut [u8],
|
||||
dst_stride: i32,
|
||||
dst_width: i32,
|
||||
dst_height: i32,
|
||||
) -> Result<(), ConvertError> {
|
||||
impl_to_argb!(
|
||||
I420Buffer
|
||||
[
|
||||
ARGB: i420_to_argb,
|
||||
BGRA: i420_to_bgra,
|
||||
ABGR: i420_to_abgr,
|
||||
RGBA: i420_to_rgba
|
||||
],
|
||||
format, self, dst, dst_stride, dst_width, dst_height
|
||||
)
|
||||
}
|
||||
|
||||
pub fn data(&self) -> (&[u8], &[u8], &[u8]) {
|
||||
unsafe {
|
||||
let ptr = recursive_cast!(&*self.sys_handle, i420_to_yuv8);
|
||||
let chroma_height = (self.height() + 1) / 2;
|
||||
(
|
||||
slice::from_raw_parts((*ptr).data_y(), (self.stride_y() * self.height()) as usize),
|
||||
slice::from_raw_parts((*ptr).data_u(), (self.stride_u() * chroma_height) as usize),
|
||||
slice::from_raw_parts((*ptr).data_v(), (self.stride_v() * chroma_height) as usize),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl I420ABuffer {
|
||||
pub fn sys_handle(&self) -> &vfb_sys::ffi::VideoFrameBuffer {
|
||||
unsafe { &*recursive_cast!(&*self.sys_handle, i420a_to_yuv8, yuv8_to_yuv, yuv_to_vfb) }
|
||||
}
|
||||
|
||||
pub fn width(&self) -> i32 {
|
||||
unsafe {
|
||||
let ptr = recursive_cast!(&*self.sys_handle, i420a_to_yuv8, yuv8_to_yuv, yuv_to_vfb);
|
||||
(*ptr).width()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn height(&self) -> i32 {
|
||||
unsafe {
|
||||
let ptr = recursive_cast!(&*self.sys_handle, i420a_to_yuv8, yuv8_to_yuv, yuv_to_vfb);
|
||||
(*ptr).height()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn chroma_width(&self) -> i32 {
|
||||
unsafe {
|
||||
let ptr = recursive_cast!(&*self.sys_handle, i420a_to_yuv8, yuv8_to_yuv);
|
||||
(*ptr).chroma_width()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn chroma_height(&self) -> i32 {
|
||||
unsafe {
|
||||
let ptr = recursive_cast!(&*self.sys_handle, i420a_to_yuv8, yuv8_to_yuv);
|
||||
(*ptr).chroma_height()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stride_y(&self) -> i32 {
|
||||
unsafe {
|
||||
let ptr = recursive_cast!(&*self.sys_handle, i420a_to_yuv8, yuv8_to_yuv);
|
||||
(*ptr).stride_y()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stride_u(&self) -> i32 {
|
||||
unsafe {
|
||||
let ptr = recursive_cast!(&*self.sys_handle, i420a_to_yuv8, yuv8_to_yuv);
|
||||
(*ptr).stride_u()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stride_v(&self) -> i32 {
|
||||
unsafe {
|
||||
let ptr = recursive_cast!(&*self.sys_handle, i420a_to_yuv8, yuv8_to_yuv);
|
||||
(*ptr).stride_v()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stride_a(&self) -> i32 {
|
||||
self.sys_handle.stride_a()
|
||||
}
|
||||
|
||||
pub fn to_i420(&self) -> I420Buffer {
|
||||
I420Buffer {
|
||||
sys_handle: unsafe {
|
||||
let ptr =
|
||||
recursive_cast!(&*self.sys_handle, i420a_to_yuv8, yuv8_to_yuv, yuv_to_vfb);
|
||||
(*ptr).to_i420()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_argb(
|
||||
&self,
|
||||
format: VideoFormatType,
|
||||
dst: &mut [u8],
|
||||
dst_stride: i32,
|
||||
dst_width: i32,
|
||||
dst_height: i32,
|
||||
) -> Result<(), ConvertError> {
|
||||
self.to_i420()
|
||||
.to_argb(format, dst, dst_stride, dst_width, dst_height)
|
||||
}
|
||||
|
||||
pub fn data(&self) -> (&[u8], &[u8], &[u8], Option<&[u8]>) {
|
||||
unsafe {
|
||||
let ptr = recursive_cast!(&*self.sys_handle, i420a_to_yuv8);
|
||||
let chroma_height = (self.height() + 1) / 2;
|
||||
let data_a = self.sys_handle.data_a();
|
||||
let has_data_a = !data_a.is_null();
|
||||
(
|
||||
slice::from_raw_parts((*ptr).data_y(), (self.stride_y() * self.height()) as usize),
|
||||
slice::from_raw_parts((*ptr).data_u(), (self.stride_u() * chroma_height) as usize),
|
||||
slice::from_raw_parts((*ptr).data_v(), (self.stride_v() * chroma_height) as usize),
|
||||
has_data_a.then_some(slice::from_raw_parts(
|
||||
data_a,
|
||||
(self.stride_a() * self.height()) as usize,
|
||||
)),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl I422Buffer {
|
||||
pub fn sys_handle(&self) -> &vfb_sys::ffi::VideoFrameBuffer {
|
||||
unsafe { &*recursive_cast!(&*self.sys_handle, i422_to_yuv8, yuv8_to_yuv, yuv_to_vfb) }
|
||||
}
|
||||
|
||||
pub fn width(&self) -> i32 {
|
||||
unsafe {
|
||||
let ptr = recursive_cast!(&*self.sys_handle, i422_to_yuv8, yuv8_to_yuv, yuv_to_vfb);
|
||||
(*ptr).width()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn height(&self) -> i32 {
|
||||
unsafe {
|
||||
let ptr = recursive_cast!(&*self.sys_handle, i422_to_yuv8, yuv8_to_yuv, yuv_to_vfb);
|
||||
(*ptr).height()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn chroma_width(&self) -> i32 {
|
||||
unsafe {
|
||||
let ptr = recursive_cast!(&*self.sys_handle, i422_to_yuv8, yuv8_to_yuv);
|
||||
(*ptr).chroma_width()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn chroma_height(&self) -> i32 {
|
||||
unsafe {
|
||||
let ptr = recursive_cast!(&*self.sys_handle, i422_to_yuv8, yuv8_to_yuv);
|
||||
(*ptr).chroma_height()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stride_y(&self) -> i32 {
|
||||
unsafe {
|
||||
let ptr = recursive_cast!(&*self.sys_handle, i422_to_yuv8, yuv8_to_yuv);
|
||||
(*ptr).stride_y()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stride_u(&self) -> i32 {
|
||||
unsafe {
|
||||
let ptr = recursive_cast!(&*self.sys_handle, i422_to_yuv8, yuv8_to_yuv);
|
||||
(*ptr).stride_u()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stride_v(&self) -> i32 {
|
||||
unsafe {
|
||||
let ptr = recursive_cast!(&*self.sys_handle, i422_to_yuv8, yuv8_to_yuv);
|
||||
(*ptr).stride_v()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_i420(&self) -> I420Buffer {
|
||||
I420Buffer {
|
||||
sys_handle: unsafe {
|
||||
let ptr = recursive_cast!(&*self.sys_handle, i422_to_yuv8, yuv8_to_yuv, yuv_to_vfb);
|
||||
(*ptr).to_i420()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_argb(
|
||||
&self,
|
||||
format: VideoFormatType,
|
||||
dst: &mut [u8],
|
||||
dst_stride: i32,
|
||||
dst_width: i32,
|
||||
dst_height: i32,
|
||||
) -> Result<(), ConvertError> {
|
||||
self.to_i420()
|
||||
.to_argb(format, dst, dst_stride, dst_width, dst_height)
|
||||
}
|
||||
|
||||
pub fn data(&self) -> (&[u8], &[u8], &[u8]) {
|
||||
unsafe {
|
||||
let ptr = recursive_cast!(&*self.sys_handle, i422_to_yuv8);
|
||||
(
|
||||
slice::from_raw_parts((*ptr).data_y(), (self.stride_y() * self.height()) as usize),
|
||||
slice::from_raw_parts((*ptr).data_u(), (self.stride_u() * self.height()) as usize),
|
||||
slice::from_raw_parts((*ptr).data_v(), (self.stride_v() * self.height()) as usize),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
impl I444Buffer {
|
||||
pub fn sys_handle(&self) -> &vfb_sys::ffi::VideoFrameBuffer {
|
||||
unsafe { &*recursive_cast!(&*self.sys_handle, i444_to_yuv8, yuv8_to_yuv, yuv_to_vfb) }
|
||||
}
|
||||
|
||||
pub fn width(&self) -> i32 {
|
||||
unsafe {
|
||||
let ptr = recursive_cast!(&*self.sys_handle, i444_to_yuv8, yuv8_to_yuv, yuv_to_vfb);
|
||||
(*ptr).width()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn height(&self) -> i32 {
|
||||
unsafe {
|
||||
let ptr = recursive_cast!(&*self.sys_handle, i444_to_yuv8, yuv8_to_yuv, yuv_to_vfb);
|
||||
(*ptr).height()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn chroma_width(&self) -> i32 {
|
||||
unsafe {
|
||||
let ptr = recursive_cast!(&*self.sys_handle, i444_to_yuv8, yuv8_to_yuv);
|
||||
(*ptr).chroma_width()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn chroma_height(&self) -> i32 {
|
||||
unsafe {
|
||||
let ptr = recursive_cast!(&*self.sys_handle, i444_to_yuv8, yuv8_to_yuv);
|
||||
(*ptr).chroma_height()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stride_y(&self) -> i32 {
|
||||
unsafe {
|
||||
let ptr = recursive_cast!(&*self.sys_handle, i444_to_yuv8, yuv8_to_yuv);
|
||||
(*ptr).stride_y()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stride_u(&self) -> i32 {
|
||||
unsafe {
|
||||
let ptr = recursive_cast!(&*self.sys_handle, i444_to_yuv8, yuv8_to_yuv);
|
||||
(*ptr).stride_u()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stride_v(&self) -> i32 {
|
||||
unsafe {
|
||||
let ptr = recursive_cast!(&*self.sys_handle, i444_to_yuv8, yuv8_to_yuv);
|
||||
(*ptr).stride_v()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_i420(&self) -> I420Buffer {
|
||||
I420Buffer {
|
||||
sys_handle: unsafe {
|
||||
let ptr = recursive_cast!(&*self.sys_handle, i444_to_yuv8, yuv8_to_yuv, yuv_to_vfb);
|
||||
(*ptr).to_i420()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_argb(
|
||||
&self,
|
||||
format: VideoFormatType,
|
||||
dst: &mut [u8],
|
||||
dst_stride: i32,
|
||||
dst_width: i32,
|
||||
dst_height: i32,
|
||||
) -> Result<(), ConvertError> {
|
||||
self.to_i420()
|
||||
.to_argb(format, dst, dst_stride, dst_width, dst_height)
|
||||
}
|
||||
|
||||
pub fn data(&self) -> (&[u8], &[u8], &[u8]) {
|
||||
unsafe {
|
||||
let ptr = recursive_cast!(&*self.sys_handle, i444_to_yuv8);
|
||||
(
|
||||
slice::from_raw_parts((*ptr).data_y(), (self.stride_y() * self.height()) as usize),
|
||||
slice::from_raw_parts((*ptr).data_u(), (self.stride_u() * self.height()) as usize),
|
||||
slice::from_raw_parts((*ptr).data_v(), (self.stride_v() * self.height()) as usize),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl I010Buffer {
|
||||
pub fn sys_handle(&self) -> &vfb_sys::ffi::VideoFrameBuffer {
|
||||
unsafe { &*recursive_cast!(&*self.sys_handle, i010_to_yuv16b, yuv16b_to_yuv, yuv_to_vfb) }
|
||||
}
|
||||
|
||||
pub fn width(&self) -> i32 {
|
||||
unsafe {
|
||||
let ptr = recursive_cast!(&*self.sys_handle, i010_to_yuv16b, yuv16b_to_yuv, yuv_to_vfb);
|
||||
(*ptr).width()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn height(&self) -> i32 {
|
||||
unsafe {
|
||||
let ptr = recursive_cast!(&*self.sys_handle, i010_to_yuv16b, yuv16b_to_yuv, yuv_to_vfb);
|
||||
(*ptr).height()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn chroma_width(&self) -> i32 {
|
||||
unsafe {
|
||||
let ptr = recursive_cast!(&*self.sys_handle, i010_to_yuv16b, yuv16b_to_yuv);
|
||||
(*ptr).chroma_width()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn chroma_height(&self) -> i32 {
|
||||
unsafe {
|
||||
let ptr = recursive_cast!(&*self.sys_handle, i010_to_yuv16b, yuv16b_to_yuv);
|
||||
(*ptr).chroma_height()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stride_y(&self) -> i32 {
|
||||
unsafe {
|
||||
let ptr = recursive_cast!(&*self.sys_handle, i010_to_yuv16b, yuv16b_to_yuv);
|
||||
(*ptr).stride_y()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stride_u(&self) -> i32 {
|
||||
unsafe {
|
||||
let ptr = recursive_cast!(&*self.sys_handle, i010_to_yuv16b, yuv16b_to_yuv);
|
||||
(*ptr).stride_u()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stride_v(&self) -> i32 {
|
||||
unsafe {
|
||||
let ptr = recursive_cast!(&*self.sys_handle, i010_to_yuv16b, yuv16b_to_yuv);
|
||||
(*ptr).stride_v()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_i420(&self) -> I420Buffer {
|
||||
I420Buffer {
|
||||
sys_handle: unsafe {
|
||||
let ptr =
|
||||
recursive_cast!(&*self.sys_handle, i010_to_yuv16b, yuv16b_to_yuv, yuv_to_vfb);
|
||||
(*ptr).to_i420()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_argb(
|
||||
&self,
|
||||
format: VideoFormatType,
|
||||
dst: &mut [u8],
|
||||
dst_stride: i32,
|
||||
dst_width: i32,
|
||||
dst_height: i32,
|
||||
) -> Result<(), ConvertError> {
|
||||
self.to_i420()
|
||||
.to_argb(format, dst, dst_stride, dst_width, dst_height)
|
||||
}
|
||||
|
||||
pub fn data(&self) -> (&[u16], &[u16], &[u16]) {
|
||||
unsafe {
|
||||
let ptr = recursive_cast!(&*self.sys_handle, i010_to_yuv16b);
|
||||
let chroma_height = (self.height() + 1) / 2;
|
||||
(
|
||||
slice::from_raw_parts(
|
||||
(*ptr).data_y(),
|
||||
(self.stride_y() * self.height()) as usize / 2,
|
||||
),
|
||||
slice::from_raw_parts(
|
||||
(*ptr).data_u(),
|
||||
(self.stride_u() * chroma_height) as usize / 2,
|
||||
),
|
||||
slice::from_raw_parts(
|
||||
(*ptr).data_v(),
|
||||
(self.stride_v() * chroma_height) as usize / 2,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl NV12Buffer {
|
||||
pub fn sys_handle(&self) -> &vfb_sys::ffi::VideoFrameBuffer {
|
||||
unsafe {
|
||||
&*recursive_cast!(
|
||||
&*self.sys_handle,
|
||||
nv12_to_biyuv8,
|
||||
biyuv8_to_biyuv,
|
||||
biyuv_to_vfb
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn width(&self) -> i32 {
|
||||
unsafe {
|
||||
let ptr = recursive_cast!(
|
||||
&*self.sys_handle,
|
||||
nv12_to_biyuv8,
|
||||
biyuv8_to_biyuv,
|
||||
biyuv_to_vfb
|
||||
);
|
||||
(*ptr).width()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn height(&self) -> i32 {
|
||||
unsafe {
|
||||
let ptr = recursive_cast!(
|
||||
&*self.sys_handle,
|
||||
nv12_to_biyuv8,
|
||||
biyuv8_to_biyuv,
|
||||
biyuv_to_vfb
|
||||
);
|
||||
(*ptr).height()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn chroma_width(&self) -> i32 {
|
||||
unsafe {
|
||||
let ptr = recursive_cast!(&*self.sys_handle, nv12_to_biyuv8, biyuv8_to_biyuv);
|
||||
(*ptr).chroma_width()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn chroma_height(&self) -> i32 {
|
||||
unsafe {
|
||||
let ptr = recursive_cast!(&*self.sys_handle, nv12_to_biyuv8, biyuv8_to_biyuv);
|
||||
(*ptr).chroma_height()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stride_y(&self) -> i32 {
|
||||
unsafe {
|
||||
let ptr = recursive_cast!(&*self.sys_handle, nv12_to_biyuv8, biyuv8_to_biyuv);
|
||||
(*ptr).stride_y()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stride_uv(&self) -> i32 {
|
||||
unsafe {
|
||||
let ptr = recursive_cast!(&*self.sys_handle, nv12_to_biyuv8, biyuv8_to_biyuv);
|
||||
(*ptr).stride_uv()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_i420(&self) -> I420Buffer {
|
||||
I420Buffer {
|
||||
sys_handle: unsafe {
|
||||
let ptr = recursive_cast!(
|
||||
&*self.sys_handle,
|
||||
nv12_to_biyuv8,
|
||||
biyuv8_to_biyuv,
|
||||
biyuv_to_vfb
|
||||
);
|
||||
(*ptr).to_i420()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_argb(
|
||||
&self,
|
||||
format: VideoFormatType,
|
||||
dst: &mut [u8],
|
||||
dst_stride: i32,
|
||||
dst_width: i32,
|
||||
dst_height: i32,
|
||||
) -> Result<(), ConvertError> {
|
||||
self.to_i420()
|
||||
.to_argb(format, dst, dst_stride, dst_width, dst_height)
|
||||
}
|
||||
|
||||
pub fn data(&self) -> (&[u8], &[u8]) {
|
||||
unsafe {
|
||||
let ptr = recursive_cast!(&*self.sys_handle, nv12_to_biyuv8);
|
||||
let chroma_height = (self.height() + 1) / 2;
|
||||
|
||||
(
|
||||
slice::from_raw_parts((*ptr).data_y(), (self.stride_y() * self.height()) as usize),
|
||||
slice::from_raw_parts(
|
||||
(*ptr).data_uv(),
|
||||
(self.stride_uv() * chroma_height) as usize,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
use crate::video_frame::{VideoFrame, VideoFrameBuffer};
|
||||
use cxx::SharedPtr;
|
||||
use webrtc_sys::media_stream as ms_sys;
|
||||
use webrtc_sys::video_frame as vf_sys;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct NativeVideoSource {
|
||||
sys_handle: SharedPtr<ms_sys::ffi::AdaptedVideoTrackSource>,
|
||||
}
|
||||
|
||||
impl Default for NativeVideoSource {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
sys_handle: ms_sys::ffi::new_adapted_video_track_source(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl NativeVideoSource {
|
||||
pub fn sys_handle(&self) -> SharedPtr<ms_sys::ffi::AdaptedVideoTrackSource> {
|
||||
self.sys_handle.clone()
|
||||
}
|
||||
|
||||
pub fn capture_frame<T: VideoFrameBuffer>(&self, frame: &VideoFrame<T>) {
|
||||
let mut builder = vf_sys::ffi::new_video_frame_builder();
|
||||
builder.pin_mut().set_rotation(frame.rotation.into());
|
||||
builder
|
||||
.pin_mut()
|
||||
.set_video_frame_buffer(frame.buffer.sys_handle());
|
||||
|
||||
let frame = builder.pin_mut().build();
|
||||
self.sys_handle.on_captured_frame(&frame);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
use super::video_frame::new_video_frame_buffer;
|
||||
use crate::media_stream::RtcVideoTrack;
|
||||
use crate::video_frame::{BoxVideoFrame, VideoFrame};
|
||||
use cxx::UniquePtr;
|
||||
use futures::stream::Stream;
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
use tokio::sync::mpsc;
|
||||
use webrtc_sys::media_stream as sys_ms;
|
||||
|
||||
pub struct NativeVideoStream {
|
||||
native_observer: UniquePtr<sys_ms::ffi::NativeVideoFrameSink>,
|
||||
_observer: Box<VideoTrackObserver>,
|
||||
video_track: RtcVideoTrack,
|
||||
frame_rx: mpsc::UnboundedReceiver<BoxVideoFrame>,
|
||||
}
|
||||
|
||||
impl NativeVideoStream {
|
||||
pub fn new(video_track: RtcVideoTrack) -> Self {
|
||||
let (frame_tx, frame_rx) = mpsc::unbounded_channel();
|
||||
let mut observer = Box::new(VideoTrackObserver { frame_tx });
|
||||
let mut native_observer = unsafe {
|
||||
sys_ms::ffi::new_native_video_frame_sink(Box::new(sys_ms::VideoFrameSinkWrapper::new(
|
||||
&mut *observer,
|
||||
)))
|
||||
};
|
||||
|
||||
unsafe {
|
||||
sys_ms::ffi::media_to_video(video_track.sys_handle())
|
||||
.add_sink(native_observer.pin_mut());
|
||||
}
|
||||
|
||||
Self {
|
||||
native_observer,
|
||||
_observer: observer,
|
||||
video_track,
|
||||
frame_rx,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn track(&self) -> RtcVideoTrack {
|
||||
self.video_track.clone()
|
||||
}
|
||||
|
||||
pub fn close(&mut self) {
|
||||
self.frame_rx.close();
|
||||
unsafe {
|
||||
sys_ms::ffi::media_to_video(self.video_track.sys_handle())
|
||||
.remove_sink(self.native_observer.pin_mut());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for NativeVideoStream {
|
||||
fn drop(&mut self) {
|
||||
self.close();
|
||||
}
|
||||
}
|
||||
|
||||
impl Stream for NativeVideoStream {
|
||||
type Item = BoxVideoFrame;
|
||||
|
||||
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Option<Self::Item>> {
|
||||
self.frame_rx.poll_recv(cx)
|
||||
}
|
||||
}
|
||||
|
||||
struct VideoTrackObserver {
|
||||
frame_tx: mpsc::UnboundedSender<BoxVideoFrame>,
|
||||
}
|
||||
|
||||
impl sys_ms::VideoFrameSink for VideoTrackObserver {
|
||||
fn on_frame(&self, frame: UniquePtr<webrtc_sys::video_frame::ffi::VideoFrame>) {
|
||||
let _ = self.frame_tx.send(VideoFrame {
|
||||
rotation: frame.rotation().into(),
|
||||
timestamp: frame.timestamp_us(),
|
||||
buffer: new_video_frame_buffer(unsafe { frame.video_frame_buffer() }),
|
||||
});
|
||||
}
|
||||
|
||||
fn on_discarded_frame(&self) {}
|
||||
|
||||
fn on_constraints_changed(&self, _constraints: sys_ms::ffi::VideoTrackSourceConstraints) {}
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
use thiserror::Error;
|
||||
use webrtc_sys::yuv_helper as yuv_sys;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum ConvertError {
|
||||
#[error("conversion failed: {0}")]
|
||||
Convert(&'static str),
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn argb_assert_safety(
|
||||
src: &[u8],
|
||||
src_stride: i32,
|
||||
_width: i32,
|
||||
height: i32,
|
||||
) -> Result<(), ConvertError> {
|
||||
let min = (src_stride * height) as usize;
|
||||
|
||||
if src.len() < min {
|
||||
return Err(ConvertError::Convert("dst isn't large enough"));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn i420_assert_safety(
|
||||
src_y: &[u8],
|
||||
src_stride_y: i32,
|
||||
src_u: &[u8],
|
||||
src_stride_u: i32,
|
||||
src_v: &[u8],
|
||||
src_stride_v: i32,
|
||||
_width: i32,
|
||||
height: i32,
|
||||
) -> Result<(), ConvertError> {
|
||||
let chroma_height = (height + 1) / 2;
|
||||
let min_y = (src_stride_y * height) as usize;
|
||||
let min_u = (src_stride_u * chroma_height) as usize;
|
||||
let min_v = (src_stride_v * chroma_height) as usize;
|
||||
|
||||
if src_y.len() < min_y {
|
||||
return Err(ConvertError::Convert("src_y isn't large enough"));
|
||||
}
|
||||
|
||||
if src_u.len() < min_u {
|
||||
return Err(ConvertError::Convert("src_u isn't large enough"));
|
||||
}
|
||||
|
||||
if src_v.len() < min_v {
|
||||
return Err(ConvertError::Convert("src_v isn't large enough"));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
macro_rules! i420_to_x {
|
||||
($x:ident) => {
|
||||
pub fn $x(
|
||||
src_y: &[u8],
|
||||
src_stride_y: i32,
|
||||
src_u: &[u8],
|
||||
src_stride_u: i32,
|
||||
src_v: &[u8],
|
||||
src_stride_v: i32,
|
||||
dst: &mut [u8],
|
||||
dst_stride: i32,
|
||||
width: i32,
|
||||
height: i32,
|
||||
) -> Result<(), ConvertError> {
|
||||
argb_assert_safety(dst, dst_stride, width, height)?;
|
||||
i420_assert_safety(
|
||||
src_y,
|
||||
src_stride_y,
|
||||
src_u,
|
||||
src_stride_u,
|
||||
src_v,
|
||||
src_stride_v,
|
||||
width,
|
||||
height,
|
||||
)?;
|
||||
|
||||
unsafe {
|
||||
yuv_sys::ffi::$x(
|
||||
src_y.as_ptr(),
|
||||
src_stride_y,
|
||||
src_u.as_ptr(),
|
||||
src_stride_u,
|
||||
src_v.as_ptr(),
|
||||
src_stride_v,
|
||||
dst.as_mut_ptr(),
|
||||
dst_stride,
|
||||
width,
|
||||
height,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! x_to_i420 {
|
||||
($x:ident) => {
|
||||
pub fn $x(
|
||||
src_argb: &[u8],
|
||||
src_stride_argb: i32,
|
||||
dst_y: &mut [u8],
|
||||
dst_stride_y: i32,
|
||||
dst_u: &mut [u8],
|
||||
dst_stride_u: i32,
|
||||
dst_v: &mut [u8],
|
||||
dst_stride_v: i32,
|
||||
width: i32,
|
||||
height: i32,
|
||||
) -> Result<(), ConvertError> {
|
||||
argb_assert_safety(src_argb, src_stride_argb, width, height)?;
|
||||
i420_assert_safety(
|
||||
dst_y,
|
||||
dst_stride_y,
|
||||
dst_u,
|
||||
dst_stride_u,
|
||||
dst_v,
|
||||
dst_stride_v,
|
||||
width,
|
||||
height,
|
||||
)?;
|
||||
|
||||
unsafe {
|
||||
yuv_sys::ffi::$x(
|
||||
src_argb.as_ptr(),
|
||||
src_stride_argb,
|
||||
dst_y.as_mut_ptr(),
|
||||
dst_stride_y,
|
||||
dst_u.as_mut_ptr(),
|
||||
dst_stride_u,
|
||||
dst_v.as_mut_ptr(),
|
||||
dst_stride_v,
|
||||
width,
|
||||
height,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn argb_to_rgb24(
|
||||
src_argb: &[u8],
|
||||
src_stride_argb: i32,
|
||||
dst_rgb24: &mut [u8],
|
||||
dst_stride_rgb24: i32,
|
||||
width: i32,
|
||||
height: i32,
|
||||
) -> Result<(), ConvertError> {
|
||||
argb_assert_safety(src_argb, src_stride_argb, width, height)?;
|
||||
argb_assert_safety(dst_rgb24, dst_stride_rgb24, width, height)?;
|
||||
|
||||
unsafe {
|
||||
yuv_sys::ffi::argb_to_rgb24(
|
||||
src_argb.as_ptr(),
|
||||
src_stride_argb,
|
||||
dst_rgb24.as_mut_ptr(),
|
||||
dst_stride_rgb24,
|
||||
width,
|
||||
height,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
x_to_i420!(argb_to_i420);
|
||||
x_to_i420!(abgr_to_i420);
|
||||
|
||||
i420_to_x!(i420_to_argb);
|
||||
i420_to_x!(i420_to_bgra);
|
||||
i420_to_x!(i420_to_abgr);
|
||||
i420_to_x!(i420_to_rgba);
|
||||
@@ -1,737 +1,240 @@
|
||||
use crate::prelude::*;
|
||||
use cxx::{SharedPtr, UniquePtr};
|
||||
use log::trace;
|
||||
use std::fmt::{Debug, Formatter};
|
||||
use std::mem::ManuallyDrop;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use tokio::sync::{mpsc, oneshot};
|
||||
use std::fmt::Debug;
|
||||
|
||||
use webrtc_sys::candidate as sys_ca;
|
||||
use webrtc_sys::data_channel as sys_dc;
|
||||
use webrtc_sys::jsep as sys_jsep;
|
||||
use webrtc_sys::media_stream as sys_ms;
|
||||
use webrtc_sys::peer_connection as sys_pc;
|
||||
use webrtc_sys::rtp_receiver as sys_rr;
|
||||
use webrtc_sys::rtp_sender as sys_rs;
|
||||
use webrtc_sys::rtp_transceiver as sys_rt;
|
||||
use crate::data_channel::{DataChannel, DataChannelInit};
|
||||
use crate::ice_candidate::IceCandidate;
|
||||
use crate::imp::peer_connection as imp_pc;
|
||||
use crate::media_stream::{MediaStream, MediaStreamTrack};
|
||||
use crate::rtp_receiver::RtpReceiver;
|
||||
use crate::rtp_sender::RtpSender;
|
||||
use crate::rtp_transceiver::{RtpTransceiver, RtpTransceiverInit};
|
||||
use crate::session_description::SessionDescription;
|
||||
use crate::{MediaType, RtcError};
|
||||
|
||||
pub use webrtc_sys::peer_connection::ffi::IceConnectionState;
|
||||
pub use webrtc_sys::peer_connection::ffi::IceGatheringState;
|
||||
pub use webrtc_sys::peer_connection::ffi::PeerConnectionState;
|
||||
pub use webrtc_sys::peer_connection::ffi::RTCOfferAnswerOptions;
|
||||
pub use webrtc_sys::peer_connection::ffi::SignalingState;
|
||||
|
||||
pub struct PeerConnection {
|
||||
cxx_handle: UniquePtr<sys_pc::ffi::PeerConnection>,
|
||||
observer: Box<InternalObserver>,
|
||||
|
||||
// Keep alive for C++
|
||||
#[allow(unused)]
|
||||
native_observer: UniquePtr<sys_pc::ffi::NativePeerConnectionObserver>,
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum PeerConnectionState {
|
||||
New,
|
||||
Connecting,
|
||||
Connected,
|
||||
Disconnected,
|
||||
Failed,
|
||||
Closed,
|
||||
}
|
||||
|
||||
impl Debug for PeerConnection {
|
||||
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
|
||||
f.debug_struct("PeerConnection")
|
||||
.field("signaling_state", &self.signaling_state())
|
||||
.field("ice_connection_state", &self.ice_connection_state())
|
||||
.field("ice_gathering_state", &self.ice_gathering_state())
|
||||
.finish()
|
||||
}
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum IceConnectionState {
|
||||
New,
|
||||
Checking,
|
||||
Connected,
|
||||
Completed,
|
||||
Failed,
|
||||
Disconnected,
|
||||
Closed,
|
||||
Max,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum IceGatheringState {
|
||||
New,
|
||||
Gathering,
|
||||
Complete,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum SignalingState {
|
||||
Stable,
|
||||
HaveLocalOffer,
|
||||
HaveLocalPrAnswer,
|
||||
HaveRemoteOffer,
|
||||
HaveRemotePrAnswer,
|
||||
Closed,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct OfferOptions {
|
||||
pub ice_restart: bool,
|
||||
pub offer_to_receive_audio: bool,
|
||||
pub offer_to_receive_video: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct AnswerOptions {}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct IceCandidateError {
|
||||
pub address: String,
|
||||
pub port: i32,
|
||||
pub url: String,
|
||||
pub error_code: i32,
|
||||
pub error_text: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TrackEvent {
|
||||
pub receiver: RtpReceiver,
|
||||
pub streams: Vec<MediaStream>,
|
||||
pub track: MediaStreamTrack,
|
||||
pub transceiver: RtpTransceiver,
|
||||
}
|
||||
|
||||
pub type OnConnectionChange = Box<dyn FnMut(PeerConnectionState) + Send + Sync>;
|
||||
pub type OnDataChannel = Box<dyn FnMut(DataChannel) + Send + Sync>;
|
||||
pub type OnIceCandidate = Box<dyn FnMut(IceCandidate) + Send + Sync>;
|
||||
pub type OnIceCandidateError = Box<dyn FnMut(IceCandidateError) + Send + Sync>;
|
||||
pub type OnIceConnectionChange = Box<dyn FnMut(IceConnectionState) + Send + Sync>;
|
||||
pub type OnIceGatheringChange = Box<dyn FnMut(IceGatheringState) + Send + Sync>;
|
||||
pub type OnNegotiationNeeded = Box<dyn FnMut(u32) + Send + Sync>;
|
||||
pub type OnSignalingChange = Box<dyn FnMut(SignalingState) + Send + Sync>;
|
||||
pub type OnTrack = Box<dyn FnMut(TrackEvent) + Send + Sync>;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct PeerConnection {
|
||||
pub(crate) handle: imp_pc::PeerConnection,
|
||||
}
|
||||
|
||||
impl PeerConnection {
|
||||
pub(crate) fn new(
|
||||
cxx_handle: UniquePtr<sys_pc::ffi::PeerConnection>,
|
||||
observer: Box<InternalObserver>,
|
||||
native_observer: UniquePtr<sys_pc::ffi::NativePeerConnectionObserver>,
|
||||
) -> Self {
|
||||
Self {
|
||||
cxx_handle,
|
||||
observer,
|
||||
native_observer,
|
||||
}
|
||||
}
|
||||
|
||||
fn create_sdp_observer() -> (
|
||||
UniquePtr<sys_pc::ffi::NativeCreateSdpObserverHandle>,
|
||||
mpsc::Receiver<Result<SessionDescription, RTCError>>,
|
||||
) {
|
||||
let (tx, rx) = mpsc::channel(1);
|
||||
let wrapper = sys_jsep::CreateSdpObserverWrapper {
|
||||
on_success: ManuallyDrop::new(Box::new({
|
||||
let tx = tx.clone();
|
||||
move |session_description| {
|
||||
let _ = tx.blocking_send(Ok(SessionDescription::new(session_description)));
|
||||
}
|
||||
})),
|
||||
on_failure: ManuallyDrop::new(Box::new(move |error| {
|
||||
let _ = tx.blocking_send(Err(error));
|
||||
})),
|
||||
};
|
||||
|
||||
(
|
||||
sys_jsep::ffi::create_native_create_sdp_observer(Box::new(wrapper)),
|
||||
rx,
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn create_offer(
|
||||
&self,
|
||||
options: RTCOfferAnswerOptions,
|
||||
) -> Result<SessionDescription, RTCError> {
|
||||
let (mut native_wrapper, mut rx) = Self::create_sdp_observer();
|
||||
|
||||
unsafe {
|
||||
self.cxx_handle
|
||||
.create_offer(native_wrapper.pin_mut(), options);
|
||||
}
|
||||
|
||||
rx.recv().await.unwrap()
|
||||
options: OfferOptions,
|
||||
) -> Result<SessionDescription, RtcError> {
|
||||
self.handle.create_offer(options).await
|
||||
}
|
||||
|
||||
pub async fn create_answer(
|
||||
&self,
|
||||
options: RTCOfferAnswerOptions,
|
||||
) -> Result<SessionDescription, RTCError> {
|
||||
let (mut native_wrapper, mut rx) = Self::create_sdp_observer();
|
||||
|
||||
unsafe {
|
||||
self.cxx_handle
|
||||
.create_answer(native_wrapper.pin_mut(), options);
|
||||
}
|
||||
|
||||
rx.recv().await.unwrap()
|
||||
options: AnswerOptions,
|
||||
) -> Result<SessionDescription, RtcError> {
|
||||
self.handle.create_answer(options).await
|
||||
}
|
||||
|
||||
pub async fn set_local_description(&self, desc: SessionDescription) -> Result<(), RTCError> {
|
||||
let (tx, rx) = oneshot::channel();
|
||||
let wrapper =
|
||||
sys_jsep::SetLocalSdpObserverWrapper(ManuallyDrop::new(Box::new(move |error| {
|
||||
let _ = tx.send(if error.ok() { Ok(()) } else { Err(error) });
|
||||
})));
|
||||
let mut native_wrapper =
|
||||
sys_jsep::ffi::create_native_set_local_sdp_observer(Box::new(wrapper));
|
||||
|
||||
unsafe {
|
||||
self.cxx_handle
|
||||
.set_local_description(desc.release(), native_wrapper.pin_mut());
|
||||
}
|
||||
|
||||
rx.await.unwrap()
|
||||
pub async fn set_local_description(&self, desc: SessionDescription) -> Result<(), RtcError> {
|
||||
self.handle.set_local_description(desc).await
|
||||
}
|
||||
|
||||
pub async fn set_remote_description(&self, desc: SessionDescription) -> Result<(), RTCError> {
|
||||
let (tx, rx) = oneshot::channel();
|
||||
let wrapper =
|
||||
sys_jsep::SetRemoteSdpObserverWrapper(ManuallyDrop::new(Box::new(move |error| {
|
||||
let _ = tx.send(if error.ok() { Ok(()) } else { Err(error) });
|
||||
})));
|
||||
let mut native_wrapper =
|
||||
sys_jsep::ffi::create_native_set_remote_sdp_observer(Box::new(wrapper));
|
||||
|
||||
unsafe {
|
||||
self.cxx_handle
|
||||
.set_remote_description(desc.release(), native_wrapper.pin_mut());
|
||||
}
|
||||
|
||||
rx.await.unwrap()
|
||||
pub async fn set_remote_description(&self, desc: SessionDescription) -> Result<(), RtcError> {
|
||||
self.handle.set_remote_description(desc).await
|
||||
}
|
||||
|
||||
pub fn add_track(
|
||||
&self,
|
||||
track: MediaStreamTrackHandle,
|
||||
stream_ids: &Vec<String>,
|
||||
) -> Result<RtpSender, RTCError> {
|
||||
let res = self.cxx_handle.add_track(track.cxx_handle(), stream_ids);
|
||||
match res {
|
||||
Ok(cxx_handle) => Ok(RtpSender::new(cxx_handle)),
|
||||
Err(e) => unsafe { Err(RTCError::from(e.what())) },
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remove_track(&self, sender: RtpSender) -> Result<(), RTCError> {
|
||||
self.cxx_handle
|
||||
.remove_track(sender.cxx_handle())
|
||||
.map_err(|e| unsafe { RTCError::from(e.what()) })
|
||||
}
|
||||
|
||||
pub fn add_transceiver(
|
||||
&self,
|
||||
track: MediaStreamTrackHandle,
|
||||
init: RtpTransceiverInit,
|
||||
) -> Result<RtpTransceiver, RTCError> {
|
||||
let res = self
|
||||
.cxx_handle
|
||||
.add_transceiver(track.cxx_handle(), init.into());
|
||||
|
||||
match res {
|
||||
Ok(cxx_handle) => Ok(RtpTransceiver::new(cxx_handle)),
|
||||
Err(e) => unsafe { Err(RTCError::from(e.what())) },
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_transceiver_for_media(
|
||||
&self,
|
||||
media_type: MediaType,
|
||||
init: RtpTransceiverInit,
|
||||
) -> Result<RtpTransceiver, RTCError> {
|
||||
let res = self
|
||||
.cxx_handle
|
||||
.add_transceiver_for_media(media_type, init.into());
|
||||
|
||||
match res {
|
||||
Ok(cxx_handle) => Ok(RtpTransceiver::new(cxx_handle)),
|
||||
Err(e) => unsafe { Err(RTCError::from(e.what())) },
|
||||
}
|
||||
}
|
||||
|
||||
pub fn senders(&self) -> Vec<RtpSender> {
|
||||
self.cxx_handle
|
||||
.get_senders()
|
||||
.into_iter()
|
||||
.map(|sender| RtpSender::new(sender.ptr))
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn receivers(&self) -> Vec<RtpReceiver> {
|
||||
self.cxx_handle
|
||||
.get_receivers()
|
||||
.into_iter()
|
||||
.map(|receiver| RtpReceiver::new(receiver.ptr))
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn transceivers(&self) -> Vec<RtpTransceiver> {
|
||||
self.cxx_handle
|
||||
.get_transceivers()
|
||||
.into_iter()
|
||||
.map(|transceiver| RtpTransceiver::new(transceiver.ptr))
|
||||
.collect()
|
||||
pub async fn add_ice_candidate(&self, candidate: IceCandidate) -> Result<(), RtcError> {
|
||||
self.handle.add_ice_candidate(candidate).await
|
||||
}
|
||||
|
||||
pub fn create_data_channel(
|
||||
&self,
|
||||
label: &str,
|
||||
init: DataChannelInit,
|
||||
) -> Result<DataChannel, RTCError> {
|
||||
let native_init = sys_dc::ffi::create_data_channel_init(init.into());
|
||||
let res = self
|
||||
.cxx_handle
|
||||
.create_data_channel(label.to_string(), native_init);
|
||||
|
||||
match res {
|
||||
Ok(cxx_handle) => Ok(DataChannel::new(cxx_handle)),
|
||||
Err(e) => Err(unsafe { RTCError::from(e.what()) }),
|
||||
}
|
||||
) -> Result<DataChannel, RtcError> {
|
||||
self.handle.create_data_channel(label, init)
|
||||
}
|
||||
|
||||
// TODO(theomonnom) Use IceCandidateInit instead of IceCandidate
|
||||
pub async fn add_ice_candidate(&self, candidate: IceCandidate) -> Result<(), RTCError> {
|
||||
let (tx, rx) = oneshot::channel();
|
||||
let observer =
|
||||
sys_pc::AddIceCandidateObserverWrapper(ManuallyDrop::new(Box::new(|error| {
|
||||
let _ = tx.send(if error.ok() { Ok(()) } else { Err(error) });
|
||||
})));
|
||||
|
||||
let mut native_observer =
|
||||
sys_pc::ffi::create_native_add_ice_candidate_observer(Box::new(observer));
|
||||
self.cxx_handle
|
||||
.add_ice_candidate(candidate.release(), native_observer.pin_mut());
|
||||
|
||||
rx.await.unwrap()
|
||||
pub fn add_track<T: AsRef<str>>(
|
||||
&self,
|
||||
track: MediaStreamTrack,
|
||||
streams_ids: &[T],
|
||||
) -> Result<RtpSender, RtcError> {
|
||||
self.handle.add_track(track, streams_ids)
|
||||
}
|
||||
|
||||
pub fn local_description(&self) -> Option<SessionDescription> {
|
||||
let local_description = self.cxx_handle.local_description();
|
||||
if local_description.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(SessionDescription::new(local_description))
|
||||
}
|
||||
pub fn remove_track(&self, sender: RtpSender) -> Result<(), RtcError> {
|
||||
self.handle.remove_track(sender)
|
||||
}
|
||||
|
||||
pub fn remote_description(&self) -> Option<SessionDescription> {
|
||||
let remote_description = self.cxx_handle.remote_description();
|
||||
if remote_description.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(SessionDescription::new(remote_description))
|
||||
}
|
||||
pub fn add_transceiver(
|
||||
&self,
|
||||
track: MediaStreamTrack,
|
||||
init: RtpTransceiverInit,
|
||||
) -> Result<RtpTransceiver, RtcError> {
|
||||
self.handle.add_transceiver(track, init)
|
||||
}
|
||||
|
||||
pub fn signaling_state(&self) -> SignalingState {
|
||||
self.cxx_handle.signaling_state()
|
||||
pub fn add_transceiver_for_media(
|
||||
&self,
|
||||
media_type: MediaType,
|
||||
init: RtpTransceiverInit,
|
||||
) -> Result<RtpTransceiver, RtcError> {
|
||||
self.handle.add_transceiver_for_media(media_type, init)
|
||||
}
|
||||
pub fn close(&self) {
|
||||
self.handle.close()
|
||||
}
|
||||
|
||||
pub fn ice_gathering_state(&self) -> IceGatheringState {
|
||||
self.cxx_handle.ice_gathering_state()
|
||||
pub fn connection_state(&self) -> PeerConnectionState {
|
||||
self.handle.connection_state()
|
||||
}
|
||||
|
||||
pub fn ice_connection_state(&self) -> IceConnectionState {
|
||||
self.cxx_handle.ice_connection_state()
|
||||
self.handle.ice_connection_state()
|
||||
}
|
||||
|
||||
pub fn close(&mut self) {
|
||||
self.cxx_handle.pin_mut().close();
|
||||
pub fn ice_gathering_state(&self) -> IceGatheringState {
|
||||
self.handle.ice_gathering_state()
|
||||
}
|
||||
|
||||
pub fn on_signaling_change(&mut self, handler: OnSignalingChangeHandler) {
|
||||
*self.observer.on_signaling_change_handler.lock().unwrap() = Some(handler);
|
||||
pub fn signaling_state(&self) -> SignalingState {
|
||||
self.handle.signaling_state()
|
||||
}
|
||||
|
||||
pub fn on_add_stream(&mut self, handler: OnAddStreamHandler) {
|
||||
*self.observer.on_add_stream_handler.lock().unwrap() = Some(handler);
|
||||
pub fn current_local_description(&self) -> Option<SessionDescription> {
|
||||
self.handle.current_local_description()
|
||||
}
|
||||
|
||||
pub fn on_remove_stream(&mut self, handler: OnRemoveStreamHandler) {
|
||||
*self.observer.on_remove_stream_handler.lock().unwrap() = Some(handler);
|
||||
pub fn current_remote_description(&self) -> Option<SessionDescription> {
|
||||
self.handle.current_remote_description()
|
||||
}
|
||||
|
||||
pub fn on_data_channel(&mut self, handler: OnDataChannelHandler) {
|
||||
*self.observer.on_data_channel_handler.lock().unwrap() = Some(handler);
|
||||
pub fn senders(&self) -> Vec<RtpSender> {
|
||||
self.handle.senders()
|
||||
}
|
||||
|
||||
pub fn on_renegotiation_needed(&mut self, handler: OnRenegotiationNeededHandler) {
|
||||
*self
|
||||
.observer
|
||||
.on_renegotiation_needed_handler
|
||||
.lock()
|
||||
.unwrap() = Some(handler);
|
||||
pub fn receivers(&self) -> Vec<RtpReceiver> {
|
||||
self.handle.receivers()
|
||||
}
|
||||
|
||||
pub fn on_ice_connection_change(&mut self, handler: OnIceConnectionChangeHandler) {
|
||||
*self
|
||||
.observer
|
||||
.on_ice_connection_change_handler
|
||||
.lock()
|
||||
.unwrap() = Some(handler);
|
||||
pub fn transceivers(&self) -> Vec<RtpTransceiver> {
|
||||
self.handle.transceivers()
|
||||
}
|
||||
|
||||
pub fn on_standardized_ice_connection_change(
|
||||
&mut self,
|
||||
handler: OnStandardizedIceConnectionChangeHandler,
|
||||
) {
|
||||
*self
|
||||
.observer
|
||||
.on_standardized_ice_connection_change_handler
|
||||
.lock()
|
||||
.unwrap() = Some(handler);
|
||||
pub fn on_connection_state_change(&self, f: Option<OnConnectionChange>) {
|
||||
self.handle.on_connection_state_change(f)
|
||||
}
|
||||
|
||||
pub fn on_connection_change(&mut self, handler: OnConnectionChangeHandler) {
|
||||
*self.observer.on_connection_change_handler.lock().unwrap() = Some(handler);
|
||||
pub fn on_data_channel(&self, f: Option<OnDataChannel>) {
|
||||
self.handle.on_data_channel(f)
|
||||
}
|
||||
|
||||
pub fn on_ice_gathering_change(&mut self, handler: OnIceGatheringChangeHandler) {
|
||||
*self
|
||||
.observer
|
||||
.on_ice_gathering_change_handler
|
||||
.lock()
|
||||
.unwrap() = Some(handler);
|
||||
pub fn on_ice_candidate(&self, f: Option<OnIceCandidate>) {
|
||||
self.handle.on_ice_candidate(f)
|
||||
}
|
||||
|
||||
pub fn on_ice_candidate(&mut self, handler: OnIceCandidateHandler) {
|
||||
*self.observer.on_ice_candidate_handler.lock().unwrap() = Some(handler);
|
||||
pub fn on_ice_candidate_error(&self, f: Option<OnIceCandidateError>) {
|
||||
self.handle.on_ice_candidate_error(f)
|
||||
}
|
||||
|
||||
pub fn on_ice_candidate_error(&mut self, handler: OnIceCandidateErrorHandler) {
|
||||
*self.observer.on_ice_candidate_error_handler.lock().unwrap() = Some(handler);
|
||||
pub fn on_ice_connection_state_change(&self, f: Option<OnIceConnectionChange>) {
|
||||
self.handle.on_ice_connection_state_change(f)
|
||||
}
|
||||
|
||||
pub fn on_ice_candidates_removed(&mut self, handler: OnIceCandidatesRemovedHandler) {
|
||||
*self
|
||||
.observer
|
||||
.on_ice_candidates_removed_handler
|
||||
.lock()
|
||||
.unwrap() = Some(handler);
|
||||
pub fn on_ice_gathering_state_change(&self, f: Option<OnIceGatheringChange>) {
|
||||
self.handle.on_ice_gathering_state_change(f)
|
||||
}
|
||||
|
||||
pub fn on_ice_connection_receiving_change(
|
||||
&mut self,
|
||||
handler: OnIceConnectionReceivingChangeHandler,
|
||||
) {
|
||||
*self
|
||||
.observer
|
||||
.on_ice_connection_receiving_change_handler
|
||||
.lock()
|
||||
.unwrap() = Some(handler);
|
||||
pub fn on_negotiation_needed(&self, f: Option<OnNegotiationNeeded>) {
|
||||
self.handle.on_negotiation_needed(f)
|
||||
}
|
||||
|
||||
pub fn on_ice_selected_candidate_pair_changed(
|
||||
&mut self,
|
||||
handler: OnIceSelectedCandidatePairChangedHandler,
|
||||
) {
|
||||
*self
|
||||
.observer
|
||||
.on_ice_selected_candidate_pair_changed_handler
|
||||
.lock()
|
||||
.unwrap() = Some(handler);
|
||||
pub fn on_signaling_state_change(&self, f: Option<OnSignalingChange>) {
|
||||
self.handle.on_signaling_state_change(f)
|
||||
}
|
||||
|
||||
pub fn on_add_track(&mut self, handler: OnAddTrackHandler) {
|
||||
*self.observer.on_add_track_handler.lock().unwrap() = Some(handler);
|
||||
}
|
||||
|
||||
pub fn on_track(&mut self, handler: OnTrackHandler) {
|
||||
*self.observer.on_track_handler.lock().unwrap() = Some(handler);
|
||||
}
|
||||
|
||||
pub fn on_remove_track(&mut self, handler: OnRemoveTrackHandler) {
|
||||
*self.observer.on_remove_track_handler.lock().unwrap() = Some(handler);
|
||||
}
|
||||
|
||||
pub fn on_interesting_usage(&mut self, handler: OnInterestingUsageHandler) {
|
||||
*self.observer.on_interesting_usage_handler.lock().unwrap() = Some(handler);
|
||||
pub fn on_track(&self, f: Option<OnTrack>) {
|
||||
self.handle.on_track(f)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(theomonnom) Should we return futures?
|
||||
pub type OnSignalingChangeHandler = Box<dyn FnMut(SignalingState) + Send + Sync>;
|
||||
pub type OnAddStreamHandler = Box<dyn FnMut(MediaStream) + Send + Sync>;
|
||||
pub type OnRemoveStreamHandler = Box<dyn FnMut(MediaStream) + Send + Sync>;
|
||||
pub type OnDataChannelHandler = Box<dyn FnMut(DataChannel) + Send + Sync>;
|
||||
pub type OnRenegotiationNeededHandler = Box<dyn FnMut() + Send + Sync>;
|
||||
pub type OnNegotiationNeededEventHandler = Box<dyn FnMut(u32) + Send + Sync>;
|
||||
pub type OnIceConnectionChangeHandler = Box<dyn FnMut(IceConnectionState) + Send + Sync>;
|
||||
pub type OnStandardizedIceConnectionChangeHandler =
|
||||
Box<dyn FnMut(IceConnectionState) + Send + Sync>;
|
||||
pub type OnConnectionChangeHandler = Box<dyn FnMut(PeerConnectionState) + Send + Sync>;
|
||||
pub type OnIceGatheringChangeHandler = Box<dyn FnMut(IceGatheringState) + Send + Sync>;
|
||||
pub type OnIceCandidateHandler = Box<dyn FnMut(IceCandidate) + Send + Sync>;
|
||||
pub type OnIceCandidateErrorHandler =
|
||||
Box<dyn FnMut(String, i32, String, i32, String) + Send + Sync>;
|
||||
pub type OnIceCandidatesRemovedHandler = Box<dyn FnMut(Vec<IceCandidate>) + Send + Sync>;
|
||||
pub type OnIceConnectionReceivingChangeHandler = Box<dyn FnMut(bool) + Send + Sync>;
|
||||
pub type OnIceSelectedCandidatePairChangedHandler =
|
||||
Box<dyn FnMut(webrtc_sys::peer_connection::ffi::CandidatePairChangeEvent) + Send + Sync>;
|
||||
pub type OnAddTrackHandler = Box<dyn FnMut(RtpReceiver, Vec<MediaStream>) + Send + Sync>;
|
||||
pub type OnTrackHandler = Box<dyn FnMut(RtpTransceiver) + Send + Sync>;
|
||||
pub type OnRemoveTrackHandler = Box<dyn FnMut(RtpReceiver) + Send + Sync>;
|
||||
pub type OnInterestingUsageHandler = Box<dyn FnMut(i32) + Send + Sync>;
|
||||
|
||||
pub(crate) struct InternalObserver {
|
||||
on_signaling_change_handler: Arc<Mutex<Option<OnSignalingChangeHandler>>>,
|
||||
on_add_stream_handler: Arc<Mutex<Option<OnAddStreamHandler>>>,
|
||||
on_remove_stream_handler: Arc<Mutex<Option<OnRemoveStreamHandler>>>,
|
||||
on_data_channel_handler: Arc<Mutex<Option<OnDataChannelHandler>>>,
|
||||
on_renegotiation_needed_handler: Arc<Mutex<Option<OnRenegotiationNeededHandler>>>,
|
||||
on_negotiation_needed_event_handler: Arc<Mutex<Option<OnNegotiationNeededEventHandler>>>,
|
||||
on_ice_connection_change_handler: Arc<Mutex<Option<OnIceConnectionChangeHandler>>>,
|
||||
on_standardized_ice_connection_change_handler:
|
||||
Arc<Mutex<Option<OnStandardizedIceConnectionChangeHandler>>>,
|
||||
on_connection_change_handler: Arc<Mutex<Option<OnConnectionChangeHandler>>>,
|
||||
on_ice_gathering_change_handler: Arc<Mutex<Option<OnIceGatheringChangeHandler>>>,
|
||||
on_ice_candidate_handler: Arc<Mutex<Option<OnIceCandidateHandler>>>,
|
||||
on_ice_candidate_error_handler: Arc<Mutex<Option<OnIceCandidateErrorHandler>>>,
|
||||
on_ice_candidates_removed_handler: Arc<Mutex<Option<OnIceCandidatesRemovedHandler>>>,
|
||||
on_ice_connection_receiving_change_handler:
|
||||
Arc<Mutex<Option<OnIceConnectionReceivingChangeHandler>>>,
|
||||
on_ice_selected_candidate_pair_changed_handler:
|
||||
Arc<Mutex<Option<OnIceSelectedCandidatePairChangedHandler>>>,
|
||||
on_add_track_handler: Arc<Mutex<Option<OnAddTrackHandler>>>,
|
||||
on_track_handler: Arc<Mutex<Option<OnTrackHandler>>>,
|
||||
on_remove_track_handler: Arc<Mutex<Option<OnRemoveTrackHandler>>>,
|
||||
on_interesting_usage_handler: Arc<Mutex<Option<OnInterestingUsageHandler>>>,
|
||||
}
|
||||
|
||||
impl Default for InternalObserver {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
on_signaling_change_handler: Arc::new(Default::default()),
|
||||
on_add_stream_handler: Arc::new(Default::default()),
|
||||
on_remove_stream_handler: Arc::new(Default::default()),
|
||||
on_data_channel_handler: Arc::new(Default::default()),
|
||||
on_renegotiation_needed_handler: Arc::new(Default::default()),
|
||||
on_negotiation_needed_event_handler: Arc::new(Default::default()),
|
||||
on_ice_connection_change_handler: Arc::new(Default::default()),
|
||||
on_standardized_ice_connection_change_handler: Arc::new(Default::default()),
|
||||
on_connection_change_handler: Arc::new(Default::default()),
|
||||
on_ice_gathering_change_handler: Arc::new(Default::default()),
|
||||
on_ice_candidate_handler: Arc::new(Default::default()),
|
||||
on_ice_candidate_error_handler: Arc::new(Default::default()),
|
||||
on_ice_candidates_removed_handler: Arc::new(Default::default()),
|
||||
on_ice_connection_receiving_change_handler: Arc::new(Default::default()),
|
||||
on_ice_selected_candidate_pair_changed_handler: Arc::new(Default::default()),
|
||||
on_add_track_handler: Arc::new(Default::default()),
|
||||
on_track_handler: Arc::new(Default::default()),
|
||||
on_remove_track_handler: Arc::new(Default::default()),
|
||||
on_interesting_usage_handler: Arc::new(Default::default()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Observers are being called on the Signaling Thread
|
||||
impl sys_pc::PeerConnectionObserver for InternalObserver {
|
||||
fn on_signaling_change(&self, new_state: SignalingState) {
|
||||
trace!("on_signaling_change, {:?}", new_state);
|
||||
let mut handler = self.on_signaling_change_handler.lock().unwrap();
|
||||
if let Some(f) = handler.as_mut() {
|
||||
f(new_state);
|
||||
}
|
||||
}
|
||||
|
||||
fn on_add_stream(&self, stream: SharedPtr<sys_ms::ffi::MediaStream>) {
|
||||
trace!("on_add_stream");
|
||||
let mut handler = self.on_add_stream_handler.lock().unwrap();
|
||||
if let Some(f) = handler.as_mut() {
|
||||
// TODO(theomonnom)
|
||||
}
|
||||
}
|
||||
|
||||
fn on_remove_stream(&self, stream: SharedPtr<sys_ms::ffi::MediaStream>) {
|
||||
trace!("on_remove_stream");
|
||||
let mut handler = self.on_remove_stream_handler.lock().unwrap();
|
||||
if let Some(f) = handler.as_mut() {
|
||||
// TODO(theomonnom)
|
||||
}
|
||||
}
|
||||
|
||||
fn on_data_channel(&self, data_channel: UniquePtr<sys_dc::ffi::DataChannel>) {
|
||||
trace!("on_data_channel");
|
||||
let mut handler = self.on_data_channel_handler.lock().unwrap();
|
||||
if let Some(f) = handler.as_mut() {
|
||||
f(DataChannel::new(data_channel));
|
||||
}
|
||||
}
|
||||
|
||||
fn on_renegotiation_needed(&self) {
|
||||
trace!("on_renegotiation_needed");
|
||||
let mut handler = self.on_renegotiation_needed_handler.lock().unwrap();
|
||||
if let Some(f) = handler.as_mut() {
|
||||
f();
|
||||
}
|
||||
}
|
||||
|
||||
fn on_negotiation_needed_event(&self, event: u32) {
|
||||
trace!("on_negotiation_needed_event");
|
||||
let mut handler = self.on_negotiation_needed_event_handler.lock().unwrap();
|
||||
if let Some(f) = handler.as_mut() {
|
||||
f(event);
|
||||
}
|
||||
}
|
||||
|
||||
fn on_ice_connection_change(&self, new_state: IceConnectionState) {
|
||||
trace!("on_ice_connection_change (new_state: {:?})", new_state);
|
||||
let mut handler = self.on_ice_connection_change_handler.lock().unwrap();
|
||||
if let Some(f) = handler.as_mut() {
|
||||
f(new_state);
|
||||
}
|
||||
}
|
||||
|
||||
fn on_standardized_ice_connection_change(&self, new_state: IceConnectionState) {
|
||||
trace!(
|
||||
"on_standardized_ice_connection_change (new_state: {:?}",
|
||||
new_state
|
||||
);
|
||||
let mut handler = self
|
||||
.on_standardized_ice_connection_change_handler
|
||||
.lock()
|
||||
.unwrap();
|
||||
if let Some(f) = handler.as_mut() {
|
||||
f(new_state);
|
||||
}
|
||||
}
|
||||
|
||||
fn on_connection_change(&self, new_state: PeerConnectionState) {
|
||||
trace!("on_connection_change (new_state: {:?})", new_state);
|
||||
let mut handler = self.on_connection_change_handler.lock().unwrap();
|
||||
if let Some(f) = handler.as_mut() {
|
||||
f(new_state);
|
||||
}
|
||||
}
|
||||
|
||||
fn on_ice_gathering_change(&self, new_state: IceGatheringState) {
|
||||
trace!("on_ice_gathering_change (new_state: {:?}", new_state);
|
||||
let mut handler = self.on_ice_gathering_change_handler.lock().unwrap();
|
||||
if let Some(f) = handler.as_mut() {
|
||||
f(new_state);
|
||||
}
|
||||
}
|
||||
|
||||
fn on_ice_candidate(&self, candidate: SharedPtr<sys_jsep::ffi::IceCandidate>) {
|
||||
trace!("on_ice_candidate");
|
||||
let mut handler = self.on_ice_candidate_handler.lock().unwrap();
|
||||
if let Some(f) = handler.as_mut() {
|
||||
f(IceCandidate::new(candidate));
|
||||
}
|
||||
}
|
||||
|
||||
fn on_ice_candidate_error(
|
||||
&self,
|
||||
address: String,
|
||||
port: i32,
|
||||
url: String,
|
||||
error_code: i32,
|
||||
error_text: String,
|
||||
) {
|
||||
trace!("on_ice_candidate_error");
|
||||
let mut handler = self.on_ice_candidate_error_handler.lock().unwrap();
|
||||
if let Some(f) = handler.as_mut() {
|
||||
f(address, port, url, error_code, error_text);
|
||||
}
|
||||
}
|
||||
|
||||
fn on_ice_candidates_removed(&self, removed: Vec<SharedPtr<sys_ca::ffi::Candidate>>) {
|
||||
trace!("on_ice_candidates_removed");
|
||||
let mut handler = self.on_ice_candidates_removed_handler.lock().unwrap();
|
||||
if let Some(f) = handler.as_mut() {
|
||||
// TODO(theomonnom)
|
||||
}
|
||||
}
|
||||
|
||||
fn on_ice_connection_receiving_change(&self, receiving: bool) {
|
||||
trace!("on_ice_connection_receiving_change");
|
||||
let mut handler = self
|
||||
.on_ice_connection_receiving_change_handler
|
||||
.lock()
|
||||
.unwrap();
|
||||
if let Some(f) = handler.as_mut() {
|
||||
f(receiving);
|
||||
}
|
||||
}
|
||||
|
||||
fn on_ice_selected_candidate_pair_changed(&self, event: sys_pc::ffi::CandidatePairChangeEvent) {
|
||||
trace!("on_ice_selected_candidate_pair_changed");
|
||||
let mut handler = self
|
||||
.on_ice_selected_candidate_pair_changed_handler
|
||||
.lock()
|
||||
.unwrap();
|
||||
if let Some(f) = handler.as_mut() {
|
||||
f(event);
|
||||
}
|
||||
}
|
||||
|
||||
fn on_add_track(
|
||||
&self,
|
||||
receiver: SharedPtr<sys_rr::ffi::RtpReceiver>,
|
||||
streams: Vec<SharedPtr<sys_ms::ffi::MediaStream>>,
|
||||
) {
|
||||
trace!("on_add_track");
|
||||
let mut handler = self.on_add_track_handler.lock().unwrap();
|
||||
if let Some(f) = handler.as_mut() {
|
||||
let streams = streams.into_iter().map(MediaStream::new).collect();
|
||||
f(RtpReceiver::new(receiver), streams)
|
||||
}
|
||||
}
|
||||
|
||||
fn on_track(&self, transceiver: SharedPtr<sys_rt::ffi::RtpTransceiver>) {
|
||||
trace!("on_track");
|
||||
let mut handler = self.on_track_handler.lock().unwrap();
|
||||
if let Some(f) = handler.as_mut() {
|
||||
// TODO(theomonnom)
|
||||
}
|
||||
}
|
||||
|
||||
fn on_remove_track(&self, receiver: SharedPtr<sys_rr::ffi::RtpReceiver>) {
|
||||
trace!("on_remove_track");
|
||||
let mut handler = self.on_remove_track_handler.lock().unwrap();
|
||||
if let Some(f) = handler.as_mut() {
|
||||
// TODO(theomonnom)
|
||||
}
|
||||
}
|
||||
|
||||
fn on_interesting_usage(&self, usage_pattern: i32) {
|
||||
trace!("on_interesting_usage");
|
||||
let mut handler = self.on_interesting_usage_handler.lock().unwrap();
|
||||
if let Some(f) = handler.as_mut() {
|
||||
f(usage_pattern);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use log::trace;
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
use webrtc_sys::peer_connection::ffi::RTCOfferAnswerOptions;
|
||||
use webrtc_sys::peer_connection_factory::ffi::{ContinualGatheringPolicy, IceTransportsType};
|
||||
|
||||
use crate::data_channel::{DataChannel, DataChannelInit};
|
||||
use crate::jsep::IceCandidate;
|
||||
use crate::peer_connection_factory::{ICEServer, PeerConnectionFactory, RTCConfiguration};
|
||||
use crate::webrtc::RTCRuntime;
|
||||
|
||||
fn init_log() {
|
||||
let _ = env_logger::builder().is_test(true).try_init();
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn create_pc() {
|
||||
init_log();
|
||||
|
||||
let rtc_runtime = RTCRuntime::new();
|
||||
|
||||
let factory = PeerConnectionFactory::new(rtc_runtime);
|
||||
let config = RTCConfiguration {
|
||||
ice_servers: vec![ICEServer {
|
||||
urls: vec!["stun:stun1.l.google.com:19302".to_string()],
|
||||
username: "".into(),
|
||||
password: "".into(),
|
||||
}],
|
||||
continual_gathering_policy: ContinualGatheringPolicy::GatherOnce,
|
||||
ice_transport_type: IceTransportsType::All,
|
||||
};
|
||||
|
||||
let mut bob = factory.create_peer_connection(config.clone()).unwrap();
|
||||
let mut alice = factory.create_peer_connection(config.clone()).unwrap();
|
||||
|
||||
let (bob_ice_tx, mut bob_ice_rx) = mpsc::channel::<IceCandidate>(16);
|
||||
let (alice_ice_tx, mut alice_ice_rx) = mpsc::channel::<IceCandidate>(16);
|
||||
let (alice_dc_tx, mut alice_dc_rx) = mpsc::channel::<DataChannel>(16);
|
||||
|
||||
bob.on_ice_candidate(Box::new(move |candidate| {
|
||||
bob_ice_tx.blocking_send(candidate).unwrap();
|
||||
}));
|
||||
|
||||
alice.on_ice_candidate(Box::new(move |candidate| {
|
||||
alice_ice_tx.blocking_send(candidate).unwrap();
|
||||
}));
|
||||
|
||||
alice.on_data_channel(Box::new(move |dc| {
|
||||
alice_dc_tx.blocking_send(dc).unwrap();
|
||||
}));
|
||||
|
||||
let mut bob_dc = bob
|
||||
.create_data_channel("test_dc", DataChannelInit::default())
|
||||
.unwrap();
|
||||
|
||||
let offer = bob
|
||||
.create_offer(RTCOfferAnswerOptions::default())
|
||||
.await
|
||||
.unwrap();
|
||||
trace!("Bob offer: {:?}", offer);
|
||||
bob.set_local_description(offer.clone()).await.unwrap();
|
||||
alice.set_remote_description(offer).await.unwrap();
|
||||
|
||||
let answer = alice
|
||||
.create_answer(RTCOfferAnswerOptions::default())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
trace!("Alice answer: {:?}", answer);
|
||||
alice.set_local_description(answer.clone()).await.unwrap();
|
||||
bob.set_remote_description(answer).await.unwrap();
|
||||
|
||||
let bob_ice = bob_ice_rx.recv().await.unwrap();
|
||||
let alice_ice = alice_ice_rx.recv().await.unwrap();
|
||||
|
||||
bob.add_ice_candidate(alice_ice).await.unwrap();
|
||||
alice.add_ice_candidate(bob_ice).await.unwrap();
|
||||
|
||||
let (data_tx, mut data_rx) = mpsc::channel::<String>(1);
|
||||
let mut alice_dc = alice_dc_rx.recv().await.unwrap();
|
||||
alice_dc.on_message(Box::new(move |data, _| {
|
||||
data_tx
|
||||
.blocking_send(String::from_utf8_lossy(data).to_string())
|
||||
.unwrap();
|
||||
}));
|
||||
|
||||
bob_dc.send(b"This is a test", true).unwrap();
|
||||
assert_eq!(data_rx.recv().await.unwrap(), "This is a test");
|
||||
|
||||
alice.close();
|
||||
bob.close();
|
||||
impl Debug for PeerConnection {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("PeerConnection")
|
||||
.field("state", &self.connection_state())
|
||||
.field("ice_state", &self.ice_connection_state())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,51 +1,78 @@
|
||||
use cxx::UniquePtr;
|
||||
use crate::imp::peer_connection_factory as imp_pcf;
|
||||
use crate::peer_connection::PeerConnection;
|
||||
use crate::rtp_parameters::RtpCapabilities;
|
||||
use crate::MediaType;
|
||||
use crate::RtcError;
|
||||
use std::fmt::Debug;
|
||||
|
||||
pub use sys_factory::ffi::{
|
||||
ContinualGatheringPolicy, ICEServer, IceTransportsType, RTCConfiguration,
|
||||
};
|
||||
use webrtc_sys::peer_connection as sys_pc;
|
||||
use webrtc_sys::peer_connection_factory as sys_factory;
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct IceServer {
|
||||
pub urls: Vec<String>,
|
||||
pub username: String,
|
||||
pub password: String,
|
||||
}
|
||||
|
||||
use crate::peer_connection::{InternalObserver, PeerConnection};
|
||||
use crate::rtc_error::RTCError;
|
||||
use crate::webrtc::RTCRuntime;
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum ContinualGatheringPolicy {
|
||||
GatherOnce,
|
||||
GatherContinually,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum IceTransportsType {
|
||||
None,
|
||||
Relay,
|
||||
NoHost,
|
||||
All,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RtcConfiguration {
|
||||
pub ice_servers: Vec<IceServer>,
|
||||
pub continual_gathering_policy: ContinualGatheringPolicy,
|
||||
pub ice_transport_type: IceTransportsType,
|
||||
}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct PeerConnectionFactory {
|
||||
cxx_handle: UniquePtr<sys_factory::ffi::PeerConnectionFactory>,
|
||||
rtc_runtime: RTCRuntime,
|
||||
pub(crate) handle: imp_pcf::PeerConnectionFactory,
|
||||
}
|
||||
|
||||
impl Debug for PeerConnectionFactory {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
f.debug_struct("PeerConnectionFactory").finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl PeerConnectionFactory {
|
||||
pub fn new(rtc_runtime: RTCRuntime) -> Self {
|
||||
Self {
|
||||
cxx_handle: sys_factory::ffi::create_peer_connection_factory(
|
||||
rtc_runtime.clone().release(),
|
||||
),
|
||||
rtc_runtime,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_peer_connection(
|
||||
&self,
|
||||
config: RTCConfiguration,
|
||||
) -> Result<PeerConnection, RTCError> {
|
||||
let native_config = sys_factory::ffi::create_rtc_configuration(config);
|
||||
config: RtcConfiguration,
|
||||
) -> Result<PeerConnection, RtcError> {
|
||||
self.handle.create_peer_connection(config)
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let mut observer = Box::new(InternalObserver::default());
|
||||
let mut native_observer = sys_pc::ffi::create_native_peer_connection_observer(
|
||||
self.rtc_runtime.clone().release(),
|
||||
Box::new(sys_pc::PeerConnectionObserverWrapper::new(&mut *observer)),
|
||||
);
|
||||
pub fn get_rtp_sender_capabilities(&self, media_type: MediaType) -> RtpCapabilities {
|
||||
self.handle.get_rtp_sender_capabilities(media_type)
|
||||
}
|
||||
|
||||
let res = self
|
||||
.cxx_handle
|
||||
.create_peer_connection(native_config, native_observer.pin_mut());
|
||||
pub fn get_rtp_receiver_capabilities(&self, media_type: MediaType) -> RtpCapabilities {
|
||||
self.handle.get_rtp_receiver_capabilities(media_type)
|
||||
}
|
||||
}
|
||||
|
||||
match res {
|
||||
Ok(cxx_handle) => Ok(PeerConnection::new(cxx_handle, observer, native_observer)),
|
||||
Err(e) => Err(RTCError::from(e.what())),
|
||||
}
|
||||
pub mod native {
|
||||
use super::PeerConnectionFactory;
|
||||
use crate::media_stream::RtcVideoTrack;
|
||||
use crate::video_source::native::NativeVideoSource;
|
||||
|
||||
pub trait PeerConnectionFactoryExt {
|
||||
fn create_video_track(&self, label: &str, source: NativeVideoSource) -> RtcVideoTrack;
|
||||
}
|
||||
|
||||
impl PeerConnectionFactoryExt for PeerConnectionFactory {
|
||||
fn create_video_track(&self, label: &str, source: NativeVideoSource) -> RtcVideoTrack {
|
||||
self.handle.create_video_track(label, source)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,22 +1,24 @@
|
||||
pub use crate::data_channel::{DataChannel, DataChannelInit, DataState};
|
||||
pub use crate::jsep::{IceCandidate, SessionDescription};
|
||||
pub use crate::data_channel::{
|
||||
DataBuffer, DataChannel, DataChannelError, DataChannelInit, DataState,
|
||||
};
|
||||
pub use crate::ice_candidate::IceCandidate;
|
||||
pub use crate::media_stream::{
|
||||
AudioTrack, MediaStream, MediaStreamTrackHandle, MediaStreamTrackTrait,
|
||||
OnConstraintsChangedHandler, OnDiscardedFrameHandler, OnFrameHandler, VideoTrack,
|
||||
MediaStream, MediaStreamTrack, RtcAudioTrack, RtcTrackState, RtcVideoTrack,
|
||||
};
|
||||
pub use crate::peer_connection::{
|
||||
IceConnectionState, IceGatheringState, PeerConnection, PeerConnectionState,
|
||||
RTCOfferAnswerOptions, SignalingState,
|
||||
AnswerOptions, IceConnectionState, IceGatheringState, OfferOptions, PeerConnection,
|
||||
PeerConnectionState, SignalingState,
|
||||
};
|
||||
pub use crate::peer_connection_factory::{
|
||||
ContinualGatheringPolicy, ICEServer, IceTransportsType, PeerConnectionFactory, RTCConfiguration,
|
||||
ContinualGatheringPolicy, IceServer, IceTransportsType, PeerConnectionFactory, RtcConfiguration,
|
||||
};
|
||||
pub use crate::rtc_error::RTCError;
|
||||
pub use crate::rtp_parameters::*;
|
||||
pub use crate::rtp_receiver::RtpReceiver;
|
||||
pub use crate::rtp_sender::RtpSender;
|
||||
pub use crate::rtp_transceiver::{RtpTransceiver, RtpTransceiverInit};
|
||||
pub use crate::video_frame::{VideoFrame, VideoRotation};
|
||||
pub use crate::video_frame_buffer::*;
|
||||
pub use crate::webrtc::*;
|
||||
pub use crate::yuv_helper::ConvertError;
|
||||
pub use crate::rtp_transceiver::{RtpTransceiver, RtpTransceiverDirection, RtpTransceiverInit};
|
||||
pub use crate::session_description::{SdpType, SessionDescription};
|
||||
pub use crate::video_frame::{
|
||||
BoxVideoFrame, I010Buffer, I420ABuffer, I420Buffer, I422Buffer, I444Buffer, NV12Buffer,
|
||||
VideoFormatType, VideoFrame, VideoFrameBuffer, VideoFrameBufferType, VideoRotation,
|
||||
};
|
||||
pub use crate::{RtcError, RtcErrorType};
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
// TODO(theomonnom) Wrap the RTCError ffi so we can use Option(u16)
|
||||
pub use webrtc_sys::rtc_error::ffi::RTCError;
|
||||
@@ -1,462 +1,91 @@
|
||||
use crate::prelude::*;
|
||||
use std::collections::HashMap;
|
||||
use std::vec::Vec;
|
||||
use webrtc_sys::rtp_parameters as ps_sys;
|
||||
use crate::rtp_transceiver::RtpTransceiverDirection;
|
||||
|
||||
// Don't exporting structs here (only enum), cxx doesn't support Option and HashMap
|
||||
pub use ps_sys::ffi::{
|
||||
DegradationPreference, FecMechanism, RtcpFeedbackMessageType, RtcpFeedbackType,
|
||||
RtpExtensionFilter,
|
||||
};
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum Priority {
|
||||
VeryLow,
|
||||
Low,
|
||||
Medium,
|
||||
High,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RtcpFeedback {
|
||||
pub feedback_type: RtcpFeedbackType,
|
||||
pub message_type: Option<RtcpFeedbackMessageType>,
|
||||
pub struct RtpHeaderExtensionParameters {
|
||||
pub uri: String,
|
||||
pub id: i32,
|
||||
pub encrypted: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct RtpParameters {
|
||||
pub codecs: Vec<RtpCodecParameters>,
|
||||
pub header_extensions: Vec<RtpHeaderExtensionParameters>,
|
||||
pub rtcp: RtcpParameters,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RtpCodecParameters {
|
||||
pub payload_type: u8,
|
||||
pub mime_type: String, // read-only
|
||||
pub clock_rate: Option<u64>,
|
||||
pub channels: Option<u16>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct RtcpParameters {
|
||||
pub cname: String,
|
||||
pub reduced_size: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RtpEncodingParameters {
|
||||
pub active: bool,
|
||||
pub max_bitrate: Option<u64>,
|
||||
pub max_framerate: Option<f64>,
|
||||
pub priority: Priority,
|
||||
pub rid: String,
|
||||
pub scale_resolution_down_by: Option<f64>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RtpCodecCapability {
|
||||
pub channels: Option<u16>,
|
||||
pub clock_rate: Option<u64>,
|
||||
pub mime_type: String,
|
||||
pub name: String,
|
||||
pub kind: MediaType,
|
||||
pub clock_rate: Option<i32>,
|
||||
pub preferred_payload_type: Option<i32>,
|
||||
pub max_ptime: Option<i32>,
|
||||
pub ptime: Option<i32>,
|
||||
pub num_channels: Option<i32>,
|
||||
pub rtcp_feedback: Vec<RtcpFeedback>,
|
||||
pub parameters: HashMap<String, String>,
|
||||
pub options: HashMap<String, String>,
|
||||
pub max_temporal_layer_extensions: i32,
|
||||
pub max_spatial_layer_extensions: i32,
|
||||
pub svc_multi_stream_support: bool,
|
||||
pub sdp_fmtp_line: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RtpHeaderExtensionCapability {
|
||||
pub uri: String,
|
||||
pub preferred_id: Option<i32>,
|
||||
pub preferred_encrypt: bool,
|
||||
pub direction: RtpTransceiverDirection,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RtpExtension {
|
||||
pub uri: String,
|
||||
pub id: i32,
|
||||
pub encrypt: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RtpFecParameters {
|
||||
pub ssrc: Option<u32>,
|
||||
pub mechanism: FecMechanism,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RtpRtxParameters {
|
||||
pub ssrc: Option<u32>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RtpEncodingParameters {
|
||||
pub ssrc: Option<u32>,
|
||||
pub bitrate_priority: f64,
|
||||
pub network_priority: Priority,
|
||||
pub max_bitrate_bps: Option<i32>,
|
||||
pub min_bitrate_bps: Option<i32>,
|
||||
pub max_framerate: Option<f64>,
|
||||
pub num_temporal_layers: Option<i32>,
|
||||
pub scale_resolution_down_by: Option<f64>,
|
||||
pub scalability_mode: Option<String>,
|
||||
pub active: bool,
|
||||
pub rid: String,
|
||||
pub adaptive_ptime: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RtpCodecParameters {
|
||||
pub mime_type: String,
|
||||
pub name: String,
|
||||
pub kind: MediaType,
|
||||
pub payload_type: i32,
|
||||
pub clock_rate: Option<i32>,
|
||||
pub num_channels: Option<i32>,
|
||||
pub max_ptime: Option<i32>,
|
||||
pub ptime: Option<i32>,
|
||||
pub rtcp_feedback: Vec<RtcpFeedback>,
|
||||
pub parameters: HashMap<String, String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RtpCapabilities {
|
||||
pub codecs: Vec<RtpCodecCapability>,
|
||||
pub header_extensions: Vec<RtpHeaderExtensionCapability>,
|
||||
pub fec: Vec<FecMechanism>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RtcpParameters {
|
||||
pub ssrc: Option<u32>,
|
||||
pub cname: String,
|
||||
pub reduced_size: bool,
|
||||
pub mux: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RtpParameters {
|
||||
pub transaction_id: String,
|
||||
pub mid: String,
|
||||
pub codecs: Vec<RtpCodecParameters>,
|
||||
pub header_extensions: Vec<RtpExtension>,
|
||||
pub encodings: Vec<RtpEncodingParameters>,
|
||||
pub rtcp: RtcpParameters,
|
||||
pub degradation_preference: Option<DegradationPreference>,
|
||||
}
|
||||
|
||||
fn into_map(vec: Vec<ps_sys::ffi::StringKeyValue>) -> HashMap<String, String> {
|
||||
let mut map = HashMap::with_capacity(vec.len());
|
||||
for pair in vec {
|
||||
map.insert(pair.key, pair.value);
|
||||
}
|
||||
map
|
||||
}
|
||||
|
||||
impl From<ps_sys::ffi::RtcpFeedback> for RtcpFeedback {
|
||||
fn from(value: ps_sys::ffi::RtcpFeedback) -> Self {
|
||||
impl Default for RtpCodecParameters {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
feedback_type: value.feedback_type,
|
||||
message_type: value.has_message_type.then_some(value.message_type),
|
||||
payload_type: 0,
|
||||
mime_type: String::default(),
|
||||
clock_rate: None,
|
||||
channels: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ps_sys::ffi::RtpCodecCapability> for RtpCodecCapability {
|
||||
fn from(value: ps_sys::ffi::RtpCodecCapability) -> Self {
|
||||
impl Default for RtpEncodingParameters {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
mime_type: value.mime_type,
|
||||
name: value.name,
|
||||
kind: value.kind,
|
||||
clock_rate: value.has_clock_rate.then_some(value.clock_rate),
|
||||
preferred_payload_type: value
|
||||
.has_preferred_payload_type
|
||||
.then_some(value.preferred_payload_type),
|
||||
max_ptime: value.has_max_ptime.then_some(value.max_ptime),
|
||||
ptime: value.has_ptime.then_some(value.ptime),
|
||||
num_channels: value.has_num_channels.then_some(value.num_channels),
|
||||
rtcp_feedback: value.rtcp_feedback.into_iter().map(Into::into).collect(),
|
||||
parameters: into_map(value.parameters),
|
||||
options: into_map(value.options),
|
||||
max_temporal_layer_extensions: value.max_temporal_layer_extensions,
|
||||
max_spatial_layer_extensions: value.max_spatial_layer_extensions,
|
||||
svc_multi_stream_support: value.svc_multi_stream_support,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ps_sys::ffi::RtpHeaderExtensionCapability> for RtpHeaderExtensionCapability {
|
||||
fn from(value: ps_sys::ffi::RtpHeaderExtensionCapability) -> Self {
|
||||
Self {
|
||||
uri: value.uri,
|
||||
preferred_id: value.has_preferred_id.then_some(value.preferred_id),
|
||||
preferred_encrypt: value.preferred_encrypt,
|
||||
direction: value.direction,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ps_sys::ffi::RtpExtension> for RtpExtension {
|
||||
fn from(value: ps_sys::ffi::RtpExtension) -> Self {
|
||||
Self {
|
||||
uri: value.uri,
|
||||
id: value.id,
|
||||
encrypt: value.encrypt,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ps_sys::ffi::RtpFecParameters> for RtpFecParameters {
|
||||
fn from(value: ps_sys::ffi::RtpFecParameters) -> Self {
|
||||
Self {
|
||||
ssrc: value.has_ssrc.then_some(value.ssrc),
|
||||
mechanism: value.mechanism,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ps_sys::ffi::RtpRtxParameters> for RtpRtxParameters {
|
||||
fn from(value: ps_sys::ffi::RtpRtxParameters) -> Self {
|
||||
Self {
|
||||
ssrc: value.has_ssrc.then_some(value.ssrc),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ps_sys::ffi::RtpEncodingParameters> for RtpEncodingParameters {
|
||||
fn from(value: ps_sys::ffi::RtpEncodingParameters) -> Self {
|
||||
Self {
|
||||
ssrc: value.has_ssrc.then_some(value.ssrc),
|
||||
bitrate_priority: value.bitrate_priority,
|
||||
network_priority: value.network_priority,
|
||||
max_bitrate_bps: value.has_max_bitrate_bps.then_some(value.max_bitrate_bps),
|
||||
min_bitrate_bps: value.has_min_bitrate_bps.then_some(value.min_bitrate_bps),
|
||||
max_framerate: value.has_max_framerate.then_some(value.max_framerate),
|
||||
num_temporal_layers: value
|
||||
.has_num_temporal_layers
|
||||
.then_some(value.num_temporal_layers),
|
||||
scale_resolution_down_by: value
|
||||
.has_scale_resolution_down_by
|
||||
.then_some(value.scale_resolution_down_by),
|
||||
scalability_mode: value.has_scalability_mode.then_some(value.scalability_mode),
|
||||
active: value.active,
|
||||
rid: value.rid,
|
||||
adaptive_ptime: value.adaptive_ptime,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ps_sys::ffi::RtpCodecParameters> for RtpCodecParameters {
|
||||
fn from(value: ps_sys::ffi::RtpCodecParameters) -> Self {
|
||||
Self {
|
||||
mime_type: value.mime_type,
|
||||
name: value.name,
|
||||
kind: value.kind,
|
||||
payload_type: value.payload_type,
|
||||
clock_rate: value.has_clock_rate.then_some(value.clock_rate),
|
||||
num_channels: value.has_num_channels.then_some(value.num_channels),
|
||||
max_ptime: value.has_max_ptime.then_some(value.max_ptime),
|
||||
ptime: value.has_ptime.then_some(value.ptime),
|
||||
rtcp_feedback: value.rtcp_feedback.into_iter().map(Into::into).collect(),
|
||||
parameters: into_map(value.parameters),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ps_sys::ffi::RtpCapabilities> for RtpCapabilities {
|
||||
fn from(value: ps_sys::ffi::RtpCapabilities) -> Self {
|
||||
Self {
|
||||
codecs: value.codecs.into_iter().map(Into::into).collect(),
|
||||
header_extensions: value
|
||||
.header_extensions
|
||||
.into_iter()
|
||||
.map(Into::into)
|
||||
.collect(),
|
||||
fec: value.fec.into_iter().map(Into::into).collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ps_sys::ffi::RtcpParameters> for RtcpParameters {
|
||||
fn from(value: ps_sys::ffi::RtcpParameters) -> Self {
|
||||
Self {
|
||||
ssrc: value.has_ssrc.then_some(value.ssrc),
|
||||
cname: value.cname,
|
||||
reduced_size: value.reduced_size,
|
||||
mux: value.mux,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ps_sys::ffi::RtpParameters> for RtpParameters {
|
||||
fn from(value: ps_sys::ffi::RtpParameters) -> Self {
|
||||
Self {
|
||||
transaction_id: value.transaction_id,
|
||||
mid: value.mid,
|
||||
codecs: value.codecs.into_iter().map(Into::into).collect(),
|
||||
header_extensions: value
|
||||
.header_extensions
|
||||
.into_iter()
|
||||
.map(Into::into)
|
||||
.collect(),
|
||||
encodings: value.encodings.into_iter().map(Into::into).collect(),
|
||||
rtcp: value.rtcp.into(),
|
||||
degradation_preference: value
|
||||
.has_degradation_preference
|
||||
.then_some(value.degradation_preference),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ignore the value inside unwrap_or for the following implementations
|
||||
|
||||
fn into_vec(map: HashMap<String, String>) -> Vec<ps_sys::ffi::StringKeyValue> {
|
||||
let mut vec = Vec::with_capacity(map.len());
|
||||
for (key, value) in map {
|
||||
vec.push(ps_sys::ffi::StringKeyValue { key, value })
|
||||
}
|
||||
vec
|
||||
}
|
||||
|
||||
impl From<RtcpFeedback> for ps_sys::ffi::RtcpFeedback {
|
||||
fn from(value: RtcpFeedback) -> Self {
|
||||
Self {
|
||||
feedback_type: value.feedback_type,
|
||||
has_message_type: value.message_type.is_some(),
|
||||
message_type: value
|
||||
.message_type
|
||||
.unwrap_or(RtcpFeedbackMessageType::GenericNACK),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RtpCodecCapability> for ps_sys::ffi::RtpCodecCapability {
|
||||
fn from(value: RtpCodecCapability) -> Self {
|
||||
Self {
|
||||
mime_type: value.mime_type,
|
||||
name: value.name,
|
||||
kind: value.kind,
|
||||
has_clock_rate: value.clock_rate.is_some(),
|
||||
clock_rate: value.clock_rate.unwrap_or(0),
|
||||
has_preferred_payload_type: value.preferred_payload_type.is_some(),
|
||||
preferred_payload_type: value.preferred_payload_type.unwrap_or(0),
|
||||
has_max_ptime: value.max_ptime.is_some(),
|
||||
max_ptime: value.max_ptime.unwrap_or(0),
|
||||
has_ptime: value.ptime.is_some(),
|
||||
ptime: value.ptime.unwrap_or(0),
|
||||
has_num_channels: value.num_channels.is_some(),
|
||||
num_channels: value.num_channels.unwrap_or(0),
|
||||
rtcp_feedback: value.rtcp_feedback.into_iter().map(Into::into).collect(),
|
||||
parameters: into_vec(value.parameters),
|
||||
options: into_vec(value.options),
|
||||
max_temporal_layer_extensions: value.max_temporal_layer_extensions,
|
||||
max_spatial_layer_extensions: value.max_spatial_layer_extensions,
|
||||
svc_multi_stream_support: value.svc_multi_stream_support,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RtpHeaderExtensionCapability> for ps_sys::ffi::RtpHeaderExtensionCapability {
|
||||
fn from(value: RtpHeaderExtensionCapability) -> Self {
|
||||
Self {
|
||||
uri: value.uri,
|
||||
has_preferred_id: value.preferred_id.is_some(),
|
||||
preferred_id: value.preferred_id.unwrap_or(0),
|
||||
preferred_encrypt: value.preferred_encrypt,
|
||||
direction: value.direction,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RtpExtension> for ps_sys::ffi::RtpExtension {
|
||||
fn from(value: RtpExtension) -> Self {
|
||||
Self {
|
||||
uri: value.uri,
|
||||
id: value.id,
|
||||
encrypt: value.encrypt,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RtpFecParameters> for ps_sys::ffi::RtpFecParameters {
|
||||
fn from(value: RtpFecParameters) -> Self {
|
||||
Self {
|
||||
has_ssrc: value.ssrc.is_some(),
|
||||
ssrc: value.ssrc.unwrap_or(0),
|
||||
mechanism: value.mechanism,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RtpRtxParameters> for ps_sys::ffi::RtpRtxParameters {
|
||||
fn from(value: RtpRtxParameters) -> Self {
|
||||
Self {
|
||||
has_ssrc: value.ssrc.is_some(),
|
||||
ssrc: value.ssrc.unwrap_or(0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RtpEncodingParameters> for ps_sys::ffi::RtpEncodingParameters {
|
||||
fn from(value: RtpEncodingParameters) -> Self {
|
||||
Self {
|
||||
has_ssrc: value.ssrc.is_some(),
|
||||
ssrc: value.ssrc.unwrap_or(0),
|
||||
bitrate_priority: value.bitrate_priority,
|
||||
network_priority: value.network_priority,
|
||||
has_max_bitrate_bps: value.max_bitrate_bps.is_some(),
|
||||
max_bitrate_bps: value.max_bitrate_bps.unwrap_or(0),
|
||||
has_min_bitrate_bps: value.min_bitrate_bps.is_some(),
|
||||
min_bitrate_bps: value.min_bitrate_bps.unwrap_or(0),
|
||||
has_max_framerate: value.max_framerate.is_some(),
|
||||
max_framerate: value.max_framerate.unwrap_or(0.0),
|
||||
has_num_temporal_layers: value.num_temporal_layers.is_some(),
|
||||
num_temporal_layers: value.num_temporal_layers.unwrap_or(0),
|
||||
has_scale_resolution_down_by: value.scale_resolution_down_by.is_some(),
|
||||
scale_resolution_down_by: value.scale_resolution_down_by.unwrap_or(0.0),
|
||||
has_scalability_mode: value.scalability_mode.is_some(),
|
||||
scalability_mode: value.scalability_mode.unwrap_or(String::new()),
|
||||
active: value.active,
|
||||
rid: value.rid,
|
||||
adaptive_ptime: value.adaptive_ptime,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RtpCodecParameters> for ps_sys::ffi::RtpCodecParameters {
|
||||
fn from(value: RtpCodecParameters) -> Self {
|
||||
Self {
|
||||
mime_type: value.mime_type,
|
||||
name: value.name,
|
||||
kind: value.kind,
|
||||
payload_type: value.payload_type,
|
||||
has_clock_rate: value.clock_rate.is_some(),
|
||||
clock_rate: value.clock_rate.unwrap_or(0),
|
||||
has_num_channels: value.num_channels.is_some(),
|
||||
num_channels: value.num_channels.unwrap_or(0),
|
||||
has_max_ptime: value.max_ptime.is_some(),
|
||||
max_ptime: value.max_ptime.unwrap_or(0),
|
||||
has_ptime: value.ptime.is_some(),
|
||||
ptime: value.ptime.unwrap_or(0),
|
||||
rtcp_feedback: value.rtcp_feedback.into_iter().map(Into::into).collect(),
|
||||
parameters: into_vec(value.parameters),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RtpCapabilities> for ps_sys::ffi::RtpCapabilities {
|
||||
fn from(value: RtpCapabilities) -> Self {
|
||||
Self {
|
||||
codecs: value.codecs.into_iter().map(Into::into).collect(),
|
||||
header_extensions: value
|
||||
.header_extensions
|
||||
.into_iter()
|
||||
.map(Into::into)
|
||||
.collect(),
|
||||
fec: value.fec.into_iter().map(Into::into).collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RtcpParameters> for ps_sys::ffi::RtcpParameters {
|
||||
fn from(value: RtcpParameters) -> Self {
|
||||
Self {
|
||||
has_ssrc: value.ssrc.is_some(),
|
||||
ssrc: value.ssrc.unwrap_or(0),
|
||||
cname: value.cname,
|
||||
reduced_size: value.reduced_size,
|
||||
mux: value.mux,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RtpParameters> for ps_sys::ffi::RtpParameters {
|
||||
fn from(value: RtpParameters) -> Self {
|
||||
Self {
|
||||
transaction_id: value.transaction_id,
|
||||
mid: value.mid,
|
||||
codecs: value.codecs.into_iter().map(Into::into).collect(),
|
||||
header_extensions: value
|
||||
.header_extensions
|
||||
.into_iter()
|
||||
.map(Into::into)
|
||||
.collect(),
|
||||
encodings: value.encodings.into_iter().map(Into::into).collect(),
|
||||
rtcp: value.rtcp.into(),
|
||||
has_degradation_preference: value.degradation_preference.is_some(),
|
||||
degradation_preference: value
|
||||
.degradation_preference
|
||||
.unwrap_or(DegradationPreference::Balanced),
|
||||
active: true,
|
||||
max_bitrate: None,
|
||||
max_framerate: None,
|
||||
priority: Priority::Low,
|
||||
rid: String::default(),
|
||||
scale_resolution_down_by: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,67 +1,29 @@
|
||||
use crate::media_stream::{MediaStream, MediaStreamTrackHandle};
|
||||
use crate::rtp_parameters::RtpParameters;
|
||||
use cxx::SharedPtr;
|
||||
use std::fmt::{Debug, Formatter};
|
||||
use webrtc_sys::rtp_receiver as sys_rec;
|
||||
use webrtc_sys::webrtc as sys_webrtc;
|
||||
use std::fmt::Debug;
|
||||
|
||||
pub use sys_webrtc::ffi::MediaType;
|
||||
use crate::{
|
||||
imp::rtp_receiver as imp_rr, media_stream::MediaStreamTrack, rtp_parameters::RtpParameters,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct RtpReceiver {
|
||||
cxx_handle: SharedPtr<sys_rec::ffi::RtpReceiver>,
|
||||
}
|
||||
|
||||
impl Debug for RtpReceiver {
|
||||
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
|
||||
f.debug_struct("RtpReceiver")
|
||||
.field("track", &self.track())
|
||||
.field("media_type", &self.media_type())
|
||||
.field("id", &self.id())
|
||||
.finish()
|
||||
}
|
||||
pub(crate) handle: imp_rr::RtpReceiver,
|
||||
}
|
||||
|
||||
impl RtpReceiver {
|
||||
pub(crate) fn new(cxx_handle: SharedPtr<sys_rec::ffi::RtpReceiver>) -> Self {
|
||||
Self { cxx_handle }
|
||||
}
|
||||
|
||||
pub(crate) fn cxx_handle(&self) -> SharedPtr<sys_rec::ffi::RtpReceiver> {
|
||||
self.cxx_handle.clone()
|
||||
}
|
||||
|
||||
pub fn track(&self) -> MediaStreamTrackHandle {
|
||||
MediaStreamTrackHandle::new(self.cxx_handle.track())
|
||||
}
|
||||
|
||||
pub fn stream_ids(&self) -> Vec<String> {
|
||||
self.cxx_handle.stream_ids()
|
||||
}
|
||||
|
||||
pub fn streams(&self) -> Vec<MediaStream> {
|
||||
let ptrs = self.cxx_handle.streams();
|
||||
let mut vec = Vec::with_capacity(ptrs.len());
|
||||
for stream in ptrs {
|
||||
vec.push(MediaStream::new(stream.ptr));
|
||||
}
|
||||
vec
|
||||
}
|
||||
|
||||
pub fn media_type(&self) -> MediaType {
|
||||
self.cxx_handle.media_type()
|
||||
}
|
||||
|
||||
pub fn id(&self) -> String {
|
||||
self.cxx_handle.id()
|
||||
pub fn track(&self) -> Option<MediaStreamTrack> {
|
||||
self.handle.track()
|
||||
}
|
||||
|
||||
pub fn parameters(&self) -> RtpParameters {
|
||||
self.cxx_handle.get_parameters().into()
|
||||
self.handle.parameters()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_jitter_buffer_minimum_delay(&self, delay_seconds: Option<f64>) {
|
||||
self.cxx_handle
|
||||
.set_jitter_buffer_minimum_delay(delay_seconds.is_some(), delay_seconds.unwrap_or(0.0));
|
||||
impl Debug for RtpReceiver {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("RtpReceiver")
|
||||
.field("track", &self.track())
|
||||
.field("cname", &self.parameters().rtcp.cname)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,81 +1,37 @@
|
||||
use crate::media_stream::{MediaStream, MediaStreamTrackHandle};
|
||||
use crate::prelude::*;
|
||||
use crate::rtp_parameters::{RtpEncodingParameters, RtpParameters};
|
||||
use cxx::SharedPtr;
|
||||
use std::fmt::{Debug, Formatter};
|
||||
use webrtc_sys::rtp_sender as sys_rs;
|
||||
use webrtc_sys::webrtc as sys_webrtc;
|
||||
use std::fmt::Debug;
|
||||
|
||||
pub use sys_webrtc::ffi::MediaType;
|
||||
use crate::{
|
||||
imp::rtp_sender as imp_rs, media_stream::MediaStreamTrack, rtp_parameters::RtpParameters,
|
||||
RtcError,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct RtpSender {
|
||||
cxx_handle: SharedPtr<sys_rs::ffi::RtpSender>,
|
||||
}
|
||||
|
||||
impl Debug for RtpSender {
|
||||
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
|
||||
f.debug_struct("RtpSender")
|
||||
.field("track", &self.track())
|
||||
.field("media_type", &self.media_type())
|
||||
.field("ssrc", &self.ssrc())
|
||||
.field("id", &self.id())
|
||||
.finish()
|
||||
}
|
||||
pub(crate) handle: imp_rs::RtpSender,
|
||||
}
|
||||
|
||||
impl RtpSender {
|
||||
pub(crate) fn new(cxx_handle: SharedPtr<sys_rs::ffi::RtpSender>) -> Self {
|
||||
Self { cxx_handle }
|
||||
pub fn track(&self) -> Option<MediaStreamTrack> {
|
||||
self.handle.track()
|
||||
}
|
||||
|
||||
pub(crate) fn cxx_handle(&self) -> SharedPtr<sys_rs::ffi::RtpSender> {
|
||||
self.cxx_handle.clone()
|
||||
}
|
||||
|
||||
pub fn set_track(&self, track: MediaStreamTrackHandle) -> bool {
|
||||
self.cxx_handle.set_track(track.cxx_handle())
|
||||
}
|
||||
|
||||
pub fn track(&self) -> MediaStreamTrackHandle {
|
||||
MediaStreamTrackHandle::new(self.cxx_handle.track())
|
||||
}
|
||||
|
||||
pub fn ssrc(&self) -> u32 {
|
||||
self.cxx_handle.ssrc()
|
||||
}
|
||||
|
||||
pub fn media_type(&self) -> MediaType {
|
||||
self.cxx_handle.media_type()
|
||||
}
|
||||
|
||||
pub fn id(&self) -> String {
|
||||
self.cxx_handle.id()
|
||||
}
|
||||
|
||||
pub fn stream_ids(&self) -> Vec<String> {
|
||||
self.cxx_handle.stream_ids()
|
||||
}
|
||||
|
||||
pub fn set_streams(&self, stream_ids: &Vec<String>) {
|
||||
self.cxx_handle.set_streams(stream_ids);
|
||||
}
|
||||
|
||||
pub fn init_send_encodings(&self) -> Vec<RtpEncodingParameters> {
|
||||
self.cxx_handle
|
||||
.init_send_encodings()
|
||||
.into_iter()
|
||||
.map(Into::into)
|
||||
.collect()
|
||||
pub fn set_track(&self, track: Option<MediaStreamTrack>) -> Result<(), RtcError> {
|
||||
self.handle.set_track(track)
|
||||
}
|
||||
|
||||
pub fn parameters(&self) -> RtpParameters {
|
||||
self.cxx_handle.get_parameters().into()
|
||||
self.handle.parameters()
|
||||
}
|
||||
|
||||
pub fn set_parameters(&self, params: RtpParameters) -> Result<(), RTCError> {
|
||||
self.cxx_handle
|
||||
.set_parameters(params.into())
|
||||
.map_err(|e| unsafe { RTCError::from(e.what()) })
|
||||
pub fn set_parameters(&self, parameters: RtpParameters) -> Result<(), RtcError> {
|
||||
self.handle.set_parameters(parameters)
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for RtpSender {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("RtpReceiver")
|
||||
.field("cname", &self.parameters().rtcp.cname)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,119 +1,68 @@
|
||||
use crate::prelude::*;
|
||||
use cxx::SharedPtr;
|
||||
use std::fmt::{Debug, Formatter};
|
||||
use webrtc_sys::rtp_transceiver as sys_rt;
|
||||
use crate::imp::rtp_transceiver as imp_rt;
|
||||
use crate::rtp_parameters::{RtpCodecCapability, RtpEncodingParameters};
|
||||
use crate::rtp_receiver::RtpReceiver;
|
||||
use crate::rtp_sender::RtpSender;
|
||||
use crate::RtcError;
|
||||
use std::fmt::Debug;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RtpTransceiverInit {
|
||||
pub direction: RtpTransceiverDirection,
|
||||
pub stream_ids: Vec<String>,
|
||||
pub send_encodings: Vec<RtpEncodingParameters>,
|
||||
}
|
||||
|
||||
impl From<RtpTransceiverInit> for sys_rt::ffi::RtpTransceiverInit {
|
||||
fn from(value: RtpTransceiverInit) -> Self {
|
||||
Self {
|
||||
direction: value.direction,
|
||||
stream_ids: value.stream_ids,
|
||||
send_encodings: value.send_encodings.into_iter().map(Into::into).collect()
|
||||
}
|
||||
}
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum RtpTransceiverDirection {
|
||||
SendRecv,
|
||||
SendOnly,
|
||||
RecvOnly,
|
||||
Inactive,
|
||||
Stopped,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct RtpTransceiver {
|
||||
cxx_handle: SharedPtr<sys_rt::ffi::RtpTransceiver>,
|
||||
}
|
||||
|
||||
impl Debug for RtpTransceiver {
|
||||
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
|
||||
f.debug_struct("RtpTransceiver")
|
||||
.field("media_type", &self.media_type())
|
||||
.field("mid", &self.mid())
|
||||
.field("direction", &self.direction())
|
||||
.field("stopped", &self.stopped())
|
||||
.field("stopping", &self.stopping())
|
||||
.finish()
|
||||
}
|
||||
pub(crate) handle: imp_rt::RtpTransceiver,
|
||||
}
|
||||
|
||||
impl RtpTransceiver {
|
||||
pub(crate) fn new(cxx_handle: SharedPtr<sys_rt::ffi::RtpTransceiver>) -> Self {
|
||||
Self { cxx_handle }
|
||||
}
|
||||
|
||||
pub(crate) fn cxx_handle(&self) -> SharedPtr<sys_rt::ffi::RtpTransceiver> {
|
||||
self.cxx_handle.clone()
|
||||
}
|
||||
|
||||
pub fn media_type(&self) -> MediaType {
|
||||
self.cxx_handle.media_type()
|
||||
}
|
||||
|
||||
pub fn mid(&self) -> Option<String> {
|
||||
self.cxx_handle.mid().ok()
|
||||
}
|
||||
|
||||
pub fn sender(&self) -> RtpSender {
|
||||
RtpSender::new(self.cxx_handle.sender())
|
||||
}
|
||||
|
||||
pub fn receiver(&self) -> RtpReceiver {
|
||||
RtpReceiver::new(self.cxx_handle.receiver())
|
||||
}
|
||||
|
||||
pub fn stopped(&self) -> bool {
|
||||
self.cxx_handle.stopped()
|
||||
}
|
||||
|
||||
pub fn stopping(&self) -> bool {
|
||||
self.cxx_handle.stopping()
|
||||
}
|
||||
|
||||
pub fn direction(&self) -> RtpTransceiverDirection {
|
||||
self.cxx_handle.direction()
|
||||
}
|
||||
|
||||
pub fn set_direction(&self, direction: RtpTransceiverDirection) -> Result<(), RTCError> {
|
||||
self.cxx_handle.set_direction(direction)
|
||||
.map_err(|e| unsafe { RTCError::from(e.what()) })
|
||||
self.handle.mid()
|
||||
}
|
||||
|
||||
pub fn current_direction(&self) -> Option<RtpTransceiverDirection> {
|
||||
self.cxx_handle.current_direction().ok()
|
||||
self.handle.current_direction()
|
||||
}
|
||||
|
||||
pub fn fired_direction(&self) -> Option<RtpTransceiverDirection> {
|
||||
self.cxx_handle.fired_direction().ok()
|
||||
pub fn direction(&self) -> RtpTransceiverDirection {
|
||||
self.handle.direction()
|
||||
}
|
||||
|
||||
pub fn stop_standard(&self) -> Result<(), RTCError> {
|
||||
self.cxx_handle.stop_standard()
|
||||
.map_err(|e| unsafe { RTCError::from(e.what()) })
|
||||
pub fn sender(&self) -> RtpSender {
|
||||
self.handle.sender()
|
||||
}
|
||||
|
||||
pub fn set_codec_preferences(&self, codecs: Vec<RtpCodecCapability>) -> Result<(), RTCError> {
|
||||
let ffi_codecs = codecs.into_iter().map(Into::into).collect();
|
||||
self.cxx_handle.set_codec_preferences(ffi_codecs)
|
||||
.map_err(|e| unsafe { RTCError::from(e.what()) })
|
||||
pub fn receiver(&self) -> RtpReceiver {
|
||||
self.handle.receiver()
|
||||
}
|
||||
|
||||
pub fn codec_preferences(&self) -> Vec<RtpCodecCapability> {
|
||||
self.cxx_handle.codec_preferences().into_iter().map(Into::into).collect()
|
||||
pub fn set_codec_preferences(&self, codecs: Vec<RtpCodecCapability>) -> Result<(), RtcError> {
|
||||
self.handle.set_codec_preferences(codecs)
|
||||
}
|
||||
|
||||
pub fn header_extensions_to_offer(&self) -> Vec<RtpHeaderExtensionCapability> {
|
||||
self.cxx_handle.header_extensions_to_offer().into_iter().map(Into::into).collect()
|
||||
pub fn stop(&self) -> Result<(), RtcError> {
|
||||
self.handle.stop()
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for RtpTransceiver {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("RtpTransceiver")
|
||||
.field("mid", &self.mid())
|
||||
.field("direction", &self.direction())
|
||||
.field("sender", &self.sender())
|
||||
.field("receiver", &self.receiver())
|
||||
.finish()
|
||||
}
|
||||
|
||||
pub fn header_extensions_negotiated(&self) -> Vec<RtpHeaderExtensionCapability> {
|
||||
self.cxx_handle.header_extensions_negotiated().into_iter().map(Into::into).collect()
|
||||
}
|
||||
|
||||
pub fn set_offered_rtp_header_extensions(&self, headers: Vec<RtpHeaderExtensionCapability>) -> Result<(), RTCError> {
|
||||
let ffi_headers = headers.into_iter().map(Into::into).collect();
|
||||
self.cxx_handle.set_offered_rtp_header_extensions(ffi_headers)
|
||||
.map_err(|e| unsafe { RTCError::from(e.what()) })
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
use crate::imp::session_description as sd_imp;
|
||||
use std::{fmt::Debug, str::FromStr};
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum SdpType {
|
||||
Offer,
|
||||
PrAnswer,
|
||||
Answer,
|
||||
Rollback,
|
||||
}
|
||||
|
||||
impl FromStr for SdpType {
|
||||
type Err = &'static str;
|
||||
|
||||
fn from_str(sdp_type: &str) -> Result<Self, Self::Err> {
|
||||
match sdp_type {
|
||||
"offer" => Ok(Self::Offer),
|
||||
"pranswer" => Ok(Self::PrAnswer),
|
||||
"answer" => Ok(Self::Answer),
|
||||
"rollback" => Ok(Self::Rollback),
|
||||
_ => Err("invalid SdpType"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SessionDescription {
|
||||
pub(crate) handle: sd_imp::SessionDescription,
|
||||
}
|
||||
|
||||
#[derive(Clone, Error, Debug)]
|
||||
#[error("Failed to parse sdp: {line} - {description}")]
|
||||
pub struct SdpParseError {
|
||||
pub line: String,
|
||||
pub description: String,
|
||||
}
|
||||
|
||||
impl SessionDescription {
|
||||
pub fn parse(sdp: &str, sdp_type: SdpType) -> Result<Self, SdpParseError> {
|
||||
sd_imp::SessionDescription::parse(sdp, sdp_type)
|
||||
}
|
||||
|
||||
pub fn sdp_type(&self) -> SdpType {
|
||||
self.handle.sdp_type()
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for SessionDescription {
|
||||
fn to_string(&self) -> String {
|
||||
self.handle.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for SessionDescription {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("SessionDescription")
|
||||
.field("sdp_type", &self.sdp_type())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
+452
-109
@@ -1,9 +1,14 @@
|
||||
use crate::video_frame_buffer::VideoFrameBuffer;
|
||||
use cxx::UniquePtr;
|
||||
use std::fmt::{Debug, Formatter};
|
||||
use webrtc_sys::video_frame as vf_sys;
|
||||
use crate::imp::video_frame as vf_imp;
|
||||
use std::fmt::Debug;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Error)]
|
||||
pub enum SinkError {
|
||||
#[error("platform error: {0}")]
|
||||
Platform(String),
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum VideoRotation {
|
||||
VideoRotation0 = 0,
|
||||
VideoRotation90 = 90,
|
||||
@@ -11,141 +16,479 @@ pub enum VideoRotation {
|
||||
VideoRotation270 = 270,
|
||||
}
|
||||
|
||||
impl From<vf_sys::ffi::VideoRotation> for VideoRotation {
|
||||
fn from(rotation: vf_sys::ffi::VideoRotation) -> Self {
|
||||
match rotation {
|
||||
vf_sys::ffi::VideoRotation::VideoRotation0 => Self::VideoRotation0,
|
||||
vf_sys::ffi::VideoRotation::VideoRotation90 => Self::VideoRotation90,
|
||||
vf_sys::ffi::VideoRotation::VideoRotation180 => Self::VideoRotation180,
|
||||
vf_sys::ffi::VideoRotation::VideoRotation270 => Self::VideoRotation270,
|
||||
_ => unreachable!(),
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum VideoFormatType {
|
||||
ARGB,
|
||||
BGRA,
|
||||
ABGR,
|
||||
RGBA,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
#[non_exhaustive]
|
||||
pub enum VideoFrameBufferType {
|
||||
Native,
|
||||
I420,
|
||||
I420A,
|
||||
I422,
|
||||
I444,
|
||||
I010,
|
||||
NV12,
|
||||
WebGl,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct VideoFrame<T>
|
||||
where
|
||||
T: VideoFrameBuffer,
|
||||
{
|
||||
pub rotation: VideoRotation,
|
||||
pub timestamp: i64, // When the frame was captured
|
||||
pub buffer: T,
|
||||
}
|
||||
|
||||
pub type BoxVideoFrame = VideoFrame<Box<dyn VideoFrameBuffer + Send + Sync>>;
|
||||
|
||||
macro_rules! new_buffer_type {
|
||||
($type:ident, $variant:ident, $as:ident) => {
|
||||
pub struct $type {
|
||||
pub(crate) handle: vf_imp::$type,
|
||||
}
|
||||
|
||||
impl $crate::video_frame::internal::BufferInternal for $type {
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
fn sys_handle(&self) -> &webrtc_sys::video_frame_buffer::ffi::VideoFrameBuffer {
|
||||
self.handle.sys_handle()
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
fn to_i420(&self) -> I420Buffer {
|
||||
I420Buffer {
|
||||
handle: self.handle.to_i420(),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
fn to_argb(
|
||||
&self,
|
||||
format: VideoFormatType,
|
||||
dst: &mut [u8],
|
||||
stride: i32,
|
||||
width: i32,
|
||||
height: i32,
|
||||
) -> Result<(), $crate::video_frame::native::ConvertError> {
|
||||
self.handle.to_argb(format, dst, stride, width, height)
|
||||
}
|
||||
}
|
||||
|
||||
impl VideoFrameBuffer for $type {
|
||||
fn width(&self) -> i32 {
|
||||
self.handle.width()
|
||||
}
|
||||
|
||||
fn height(&self) -> i32 {
|
||||
self.handle.height()
|
||||
}
|
||||
|
||||
fn buffer_type(&self) -> VideoFrameBufferType {
|
||||
VideoFrameBufferType::$variant
|
||||
}
|
||||
|
||||
fn $as(&self) -> Option<&$type> {
|
||||
Some(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for $type {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct(stringify!($type))
|
||||
.field("width", &self.width())
|
||||
.field("height", &self.height())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub(crate) mod internal {
|
||||
use super::{I420Buffer, VideoFormatType};
|
||||
|
||||
pub trait BufferInternal {
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
fn sys_handle(&self) -> &webrtc_sys::video_frame_buffer::ffi::VideoFrameBuffer;
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
fn to_i420(&self) -> I420Buffer;
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
fn to_argb(
|
||||
&self,
|
||||
format: VideoFormatType,
|
||||
dst: &mut [u8],
|
||||
dst_stride: i32,
|
||||
dst_width: i32,
|
||||
dst_height: i32,
|
||||
) -> Result<(), super::native::ConvertError>;
|
||||
}
|
||||
}
|
||||
|
||||
pub trait VideoFrameBuffer: internal::BufferInternal + Debug {
|
||||
fn width(&self) -> i32;
|
||||
fn height(&self) -> i32;
|
||||
fn buffer_type(&self) -> VideoFrameBufferType;
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
fn as_native(&self) -> Option<&native::NativeBuffer> {
|
||||
None
|
||||
}
|
||||
|
||||
fn as_i420(&self) -> Option<&I420Buffer> {
|
||||
None
|
||||
}
|
||||
|
||||
fn as_i420a(&self) -> Option<&I420ABuffer> {
|
||||
None
|
||||
}
|
||||
|
||||
fn as_i422(&self) -> Option<&I422Buffer> {
|
||||
None
|
||||
}
|
||||
|
||||
fn as_i444(&self) -> Option<&I444Buffer> {
|
||||
None
|
||||
}
|
||||
|
||||
fn as_i010(&self) -> Option<&I010Buffer> {
|
||||
None
|
||||
}
|
||||
|
||||
fn as_nv12(&self) -> Option<&NV12Buffer> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
new_buffer_type!(I420Buffer, I420, as_i420);
|
||||
new_buffer_type!(I420ABuffer, I420A, as_i420a);
|
||||
new_buffer_type!(I422Buffer, I422, as_i422);
|
||||
new_buffer_type!(I444Buffer, I444, as_i444);
|
||||
new_buffer_type!(I010Buffer, I010, as_i010);
|
||||
new_buffer_type!(NV12Buffer, NV12, as_nv12);
|
||||
|
||||
impl I420Buffer {
|
||||
pub fn chroma_width(&self) -> i32 {
|
||||
self.handle.chroma_width()
|
||||
}
|
||||
|
||||
pub fn chroma_height(&self) -> i32 {
|
||||
self.handle.chroma_height()
|
||||
}
|
||||
|
||||
pub fn stride_y(&self) -> i32 {
|
||||
self.handle.stride_y()
|
||||
}
|
||||
|
||||
pub fn stride_u(&self) -> i32 {
|
||||
self.handle.stride_u()
|
||||
}
|
||||
|
||||
pub fn stride_v(&self) -> i32 {
|
||||
self.handle.stride_v()
|
||||
}
|
||||
|
||||
pub fn data(&self) -> (&[u8], &[u8], &[u8]) {
|
||||
self.handle.data()
|
||||
}
|
||||
|
||||
pub fn data_mut(&mut self) -> (&mut [u8], &mut [u8], &mut [u8]) {
|
||||
let (data_y, data_u, data_v) = self.handle.data();
|
||||
unsafe {
|
||||
(
|
||||
std::slice::from_raw_parts_mut(data_y.as_ptr() as *mut u8, data_y.len()),
|
||||
std::slice::from_raw_parts_mut(data_u.as_ptr() as *mut u8, data_u.len()),
|
||||
std::slice::from_raw_parts_mut(data_v.as_ptr() as *mut u8, data_v.len()),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<VideoRotation> for vf_sys::ffi::VideoRotation {
|
||||
fn from(rotation: VideoRotation) -> Self {
|
||||
match rotation {
|
||||
VideoRotation::VideoRotation0 => Self::VideoRotation0,
|
||||
VideoRotation::VideoRotation90 => Self::VideoRotation90,
|
||||
VideoRotation::VideoRotation180 => Self::VideoRotation180,
|
||||
VideoRotation::VideoRotation270 => Self::VideoRotation270,
|
||||
impl I420ABuffer {
|
||||
pub fn chroma_width(&self) -> i32 {
|
||||
self.handle.chroma_width()
|
||||
}
|
||||
|
||||
pub fn chroma_height(&self) -> i32 {
|
||||
self.handle.chroma_height()
|
||||
}
|
||||
|
||||
pub fn stride_y(&self) -> i32 {
|
||||
self.handle.stride_y()
|
||||
}
|
||||
|
||||
pub fn stride_u(&self) -> i32 {
|
||||
self.handle.stride_u()
|
||||
}
|
||||
|
||||
pub fn stride_v(&self) -> i32 {
|
||||
self.handle.stride_v()
|
||||
}
|
||||
|
||||
pub fn stride_a(&self) -> i32 {
|
||||
self.handle.stride_a()
|
||||
}
|
||||
|
||||
pub fn data(&self) -> (&[u8], &[u8], &[u8], Option<&[u8]>) {
|
||||
self.handle.data()
|
||||
}
|
||||
|
||||
pub fn data_mut(&self) -> (&mut [u8], &mut [u8], &mut [u8], Option<&mut [u8]>) {
|
||||
let (data_y, data_u, data_v, data_a) = self.handle.data();
|
||||
unsafe {
|
||||
(
|
||||
std::slice::from_raw_parts_mut(data_y.as_ptr() as *mut u8, data_y.len()),
|
||||
std::slice::from_raw_parts_mut(data_u.as_ptr() as *mut u8, data_u.len()),
|
||||
std::slice::from_raw_parts_mut(data_v.as_ptr() as *mut u8, data_v.len()),
|
||||
data_a.map(|data_a| {
|
||||
std::slice::from_raw_parts_mut(data_a.as_ptr() as *mut u8, data_a.len())
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct VideoFrame {
|
||||
cxx_handle: UniquePtr<vf_sys::ffi::VideoFrame>,
|
||||
}
|
||||
|
||||
impl Debug for VideoFrame {
|
||||
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
|
||||
f.debug_struct("VideoFrame")
|
||||
.field("width", &self.width())
|
||||
.field("height", &self.height())
|
||||
.field("id", &self.id())
|
||||
.field("rotation", &self.rotation())
|
||||
.field("timestamp", &self.timestamp())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl VideoFrame {
|
||||
pub(crate) fn new(cxx_handle: UniquePtr<vf_sys::ffi::VideoFrame>) -> Self {
|
||||
Self { cxx_handle }
|
||||
impl I422Buffer {
|
||||
pub fn chroma_width(&self) -> i32 {
|
||||
self.handle.chroma_width()
|
||||
}
|
||||
|
||||
pub fn width(&self) -> i32 {
|
||||
self.cxx_handle.width()
|
||||
pub fn chroma_height(&self) -> i32 {
|
||||
self.handle.chroma_height()
|
||||
}
|
||||
|
||||
pub fn height(&self) -> i32 {
|
||||
self.cxx_handle.height()
|
||||
pub fn stride_y(&self) -> i32 {
|
||||
self.handle.stride_y()
|
||||
}
|
||||
|
||||
pub fn size(&self) -> u32 {
|
||||
self.cxx_handle.size()
|
||||
pub fn stride_u(&self) -> i32 {
|
||||
self.handle.stride_u()
|
||||
}
|
||||
|
||||
pub fn id(&self) -> u16 {
|
||||
self.cxx_handle.id()
|
||||
pub fn stride_v(&self) -> i32 {
|
||||
self.handle.stride_v()
|
||||
}
|
||||
|
||||
pub fn timestamp_us(&self) -> i64 {
|
||||
self.cxx_handle.timestamp_us()
|
||||
pub fn data(&self) -> (&[u8], &[u8], &[u8]) {
|
||||
self.handle.data()
|
||||
}
|
||||
|
||||
pub fn ntp_time_ms(&self) -> i64 {
|
||||
self.cxx_handle.ntp_time_ms()
|
||||
}
|
||||
|
||||
pub fn transport_frame_id(&self) -> u32 {
|
||||
self.cxx_handle.transport_frame_id()
|
||||
}
|
||||
|
||||
pub fn timestamp(&self) -> u32 {
|
||||
self.cxx_handle.timestamp()
|
||||
}
|
||||
|
||||
pub fn rotation(&self) -> VideoRotation {
|
||||
self.cxx_handle.rotation().into()
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
/// Must be called only once, this function create the safe Rust
|
||||
/// wrapper around a VideoFrameBuffer.
|
||||
/// Only one wrapper musts exist at a time.
|
||||
pub(crate) unsafe fn video_frame_buffer(&self) -> VideoFrameBuffer {
|
||||
VideoFrameBuffer::new(self.cxx_handle.video_frame_buffer())
|
||||
}
|
||||
|
||||
pub fn builder() -> VideoFrameBuilder {
|
||||
VideoFrameBuilder::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct VideoFrameBuilder {
|
||||
cxx_handle: UniquePtr<vf_sys::ffi::VideoFrameBuilder>,
|
||||
}
|
||||
|
||||
impl Debug for VideoFrameBuilder {
|
||||
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
|
||||
f.debug_struct("VideoFrameBuilder").finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for VideoFrameBuilder {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
cxx_handle: vf_sys::ffi::create_video_frame_builder(),
|
||||
pub fn data_mut(&mut self) -> (&mut [u8], &mut [u8], &mut [u8]) {
|
||||
let (data_y, data_u, data_v) = self.handle.data();
|
||||
unsafe {
|
||||
(
|
||||
std::slice::from_raw_parts_mut(data_y.as_ptr() as *mut u8, data_y.len()),
|
||||
std::slice::from_raw_parts_mut(data_u.as_ptr() as *mut u8, data_u.len()),
|
||||
std::slice::from_raw_parts_mut(data_v.as_ptr() as *mut u8, data_v.len()),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl VideoFrameBuilder {
|
||||
pub fn set_video_frame_buffer(mut self, buffer: VideoFrameBuffer) -> Self {
|
||||
self.cxx_handle
|
||||
.pin_mut()
|
||||
.set_video_frame_buffer(buffer.release());
|
||||
self
|
||||
impl I444Buffer {
|
||||
pub fn chroma_width(&self) -> i32 {
|
||||
self.handle.chroma_width()
|
||||
}
|
||||
|
||||
pub fn set_timestamp_us(mut self, ts_us: i64) -> Self {
|
||||
self.cxx_handle.pin_mut().set_timestamp_us(ts_us);
|
||||
self
|
||||
pub fn chroma_height(&self) -> i32 {
|
||||
self.handle.chroma_height()
|
||||
}
|
||||
|
||||
pub fn set_rotation(mut self, rotation: VideoRotation) -> Self {
|
||||
self.cxx_handle.pin_mut().set_rotation(rotation.into());
|
||||
self
|
||||
pub fn stride_y(&self) -> i32 {
|
||||
self.handle.stride_y()
|
||||
}
|
||||
|
||||
pub fn set_id(mut self, id: u16) -> Self {
|
||||
self.cxx_handle.pin_mut().set_id(id);
|
||||
self
|
||||
pub fn stride_u(&self) -> i32 {
|
||||
self.handle.stride_u()
|
||||
}
|
||||
|
||||
pub fn build(mut self) -> VideoFrame {
|
||||
VideoFrame::new(self.cxx_handle.pin_mut().build())
|
||||
pub fn stride_v(&self) -> i32 {
|
||||
self.handle.stride_v()
|
||||
}
|
||||
|
||||
pub fn data(&self) -> (&[u8], &[u8], &[u8]) {
|
||||
self.handle.data()
|
||||
}
|
||||
|
||||
pub fn data_mut(&mut self) -> (&mut [u8], &mut [u8], &mut [u8]) {
|
||||
let (data_y, data_u, data_v) = self.handle.data();
|
||||
unsafe {
|
||||
(
|
||||
std::slice::from_raw_parts_mut(data_y.as_ptr() as *mut u8, data_y.len()),
|
||||
std::slice::from_raw_parts_mut(data_u.as_ptr() as *mut u8, data_u.len()),
|
||||
std::slice::from_raw_parts_mut(data_v.as_ptr() as *mut u8, data_v.len()),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl I010Buffer {
|
||||
pub fn chroma_width(&self) -> i32 {
|
||||
self.handle.chroma_width()
|
||||
}
|
||||
|
||||
pub fn chroma_height(&self) -> i32 {
|
||||
self.handle.chroma_height()
|
||||
}
|
||||
|
||||
pub fn stride_y(&self) -> i32 {
|
||||
self.handle.stride_y()
|
||||
}
|
||||
|
||||
pub fn stride_u(&self) -> i32 {
|
||||
self.handle.stride_u()
|
||||
}
|
||||
|
||||
pub fn stride_v(&self) -> i32 {
|
||||
self.handle.stride_v()
|
||||
}
|
||||
|
||||
pub fn data(&self) -> (&[u16], &[u16], &[u16]) {
|
||||
self.handle.data()
|
||||
}
|
||||
|
||||
pub fn data_mut(&mut self) -> (&mut [u16], &mut [u16], &mut [u16]) {
|
||||
let (data_y, data_u, data_v) = self.handle.data();
|
||||
unsafe {
|
||||
(
|
||||
std::slice::from_raw_parts_mut(data_y.as_ptr() as *mut u16, data_y.len()),
|
||||
std::slice::from_raw_parts_mut(data_u.as_ptr() as *mut u16, data_u.len()),
|
||||
std::slice::from_raw_parts_mut(data_v.as_ptr() as *mut u16, data_v.len()),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl NV12Buffer {
|
||||
pub fn chroma_width(&self) -> i32 {
|
||||
self.handle.chroma_width()
|
||||
}
|
||||
|
||||
pub fn chroma_height(&self) -> i32 {
|
||||
self.handle.chroma_height()
|
||||
}
|
||||
|
||||
pub fn stride_y(&self) -> i32 {
|
||||
self.handle.stride_y()
|
||||
}
|
||||
|
||||
pub fn stride_uv(&self) -> i32 {
|
||||
self.handle.stride_uv()
|
||||
}
|
||||
|
||||
pub fn data(&self) -> (&[u8], &[u8]) {
|
||||
self.handle.data()
|
||||
}
|
||||
|
||||
pub fn data_mut(&mut self) -> (&mut [u8], &mut [u8]) {
|
||||
let (data_y, data_uv) = self.handle.data();
|
||||
unsafe {
|
||||
(
|
||||
std::slice::from_raw_parts_mut(data_y.as_ptr() as *mut u8, data_y.len()),
|
||||
std::slice::from_raw_parts_mut(data_uv.as_ptr() as *mut u8, data_uv.len()),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub mod native {
|
||||
use super::{vf_imp, I420Buffer, VideoFormatType, VideoFrameBuffer, VideoFrameBufferType};
|
||||
use std::fmt::Debug;
|
||||
|
||||
pub use crate::imp::yuv_helper::ConvertError;
|
||||
|
||||
new_buffer_type!(NativeBuffer, Native, as_native);
|
||||
|
||||
pub trait I420BufferExt {
|
||||
fn new(width: u32, height: u32) -> I420Buffer;
|
||||
}
|
||||
|
||||
impl I420BufferExt for I420Buffer {
|
||||
fn new(width: u32, height: u32) -> I420Buffer {
|
||||
vf_imp::I420Buffer::new(width, height)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait VideoFrameBufferExt: VideoFrameBuffer {
|
||||
fn to_i420(&self) -> I420Buffer;
|
||||
fn to_argb(
|
||||
&self,
|
||||
format: VideoFormatType,
|
||||
dst: &mut [u8],
|
||||
dst_stride: i32,
|
||||
dst_width: i32,
|
||||
dst_height: i32,
|
||||
) -> Result<(), ConvertError>;
|
||||
}
|
||||
|
||||
impl<T: VideoFrameBuffer> VideoFrameBufferExt for T {
|
||||
fn to_i420(&self) -> I420Buffer {
|
||||
self.to_i420()
|
||||
}
|
||||
|
||||
fn to_argb(
|
||||
&self,
|
||||
format: VideoFormatType,
|
||||
dst: &mut [u8],
|
||||
dst_stride: i32,
|
||||
dst_width: i32,
|
||||
dst_height: i32,
|
||||
) -> Result<(), ConvertError> {
|
||||
self.to_argb(format, dst, dst_stride, dst_width, dst_height)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: VideoFrameBuffer + ?Sized> internal::BufferInternal for Box<T> {
|
||||
fn sys_handle(&self) -> &webrtc_sys::video_frame_buffer::ffi::VideoFrameBuffer {
|
||||
self.as_ref().sys_handle()
|
||||
}
|
||||
|
||||
fn to_i420(&self) -> I420Buffer {
|
||||
self.as_ref().to_i420()
|
||||
}
|
||||
|
||||
fn to_argb(
|
||||
&self,
|
||||
format: VideoFormatType,
|
||||
dst: &mut [u8],
|
||||
dst_stride: i32,
|
||||
dst_width: i32,
|
||||
dst_height: i32,
|
||||
) -> Result<(), self::native::ConvertError> {
|
||||
self.as_ref()
|
||||
.to_argb(format, dst, dst_stride, dst_width, dst_height)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: VideoFrameBuffer + ?Sized> VideoFrameBuffer for Box<T> {
|
||||
fn width(&self) -> i32 {
|
||||
self.as_ref().width()
|
||||
}
|
||||
|
||||
fn height(&self) -> i32 {
|
||||
self.as_ref().height()
|
||||
}
|
||||
|
||||
fn buffer_type(&self) -> VideoFrameBufferType {
|
||||
self.as_ref().buffer_type()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub mod web {
|
||||
use super::VideoFrameBuffer;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct WebGlBuffer {}
|
||||
|
||||
impl VideoFrameBuffer for WebGlBuffer {}
|
||||
}
|
||||
|
||||
@@ -1,578 +0,0 @@
|
||||
use cxx::UniquePtr;
|
||||
use livekit_utils::enum_dispatch;
|
||||
use std::pin::Pin;
|
||||
use std::slice;
|
||||
use webrtc_sys::video_frame_buffer as vfb_sys;
|
||||
|
||||
use crate::yuv_helper::{self, ConvertError};
|
||||
|
||||
macro_rules! recursive_cast {
|
||||
($ptr:expr $(, $fnc:ident)*) => {
|
||||
{
|
||||
let ptr = $ptr;
|
||||
$(
|
||||
let ptr = unsafe { vfb_sys::ffi::$fnc(ptr) };
|
||||
)*
|
||||
ptr
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum VideoFrameBufferType {
|
||||
Native,
|
||||
I420,
|
||||
I420A,
|
||||
I422,
|
||||
I444,
|
||||
I010,
|
||||
NV12,
|
||||
}
|
||||
|
||||
// types to convert to
|
||||
#[derive(Debug)]
|
||||
pub enum VideoFormatType {
|
||||
ARGB,
|
||||
BGRA,
|
||||
ABGR,
|
||||
RGBA,
|
||||
}
|
||||
|
||||
impl From<vfb_sys::ffi::VideoFrameBufferType> for VideoFrameBufferType {
|
||||
fn from(buffer_type: vfb_sys::ffi::VideoFrameBufferType) -> Self {
|
||||
match buffer_type {
|
||||
vfb_sys::ffi::VideoFrameBufferType::Native => Self::Native,
|
||||
vfb_sys::ffi::VideoFrameBufferType::I420 => Self::I420,
|
||||
vfb_sys::ffi::VideoFrameBufferType::I420A => Self::I420A,
|
||||
vfb_sys::ffi::VideoFrameBufferType::I422 => Self::I422,
|
||||
vfb_sys::ffi::VideoFrameBufferType::I444 => Self::I444,
|
||||
vfb_sys::ffi::VideoFrameBufferType::I010 => Self::I010,
|
||||
vfb_sys::ffi::VideoFrameBufferType::NV12 => Self::NV12,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait VideoFrameBufferTrait {
|
||||
fn buffer_type(&self) -> VideoFrameBufferType; // Useful for the FFI
|
||||
fn width(&self) -> i32;
|
||||
fn height(&self) -> i32;
|
||||
fn to_i420(self) -> I420Buffer;
|
||||
}
|
||||
|
||||
pub trait PlanarYuvBuffer: VideoFrameBufferTrait {
|
||||
fn chroma_width(&self) -> i32;
|
||||
fn chroma_height(&self) -> i32;
|
||||
fn stride_y(&self) -> i32;
|
||||
fn stride_u(&self) -> i32;
|
||||
fn stride_v(&self) -> i32;
|
||||
}
|
||||
|
||||
pub trait PlanarYuv8Buffer: PlanarYuvBuffer {
|
||||
fn data_y(&self) -> &[u8];
|
||||
fn data_u(&self) -> &[u8];
|
||||
fn data_v(&self) -> &[u8];
|
||||
}
|
||||
|
||||
pub trait PlanarYuv16BBuffer: PlanarYuvBuffer {
|
||||
fn data_y(&self) -> &[u16];
|
||||
fn data_u(&self) -> &[u16];
|
||||
fn data_v(&self) -> &[u16];
|
||||
}
|
||||
|
||||
pub trait BiplanarYuvBuffer: VideoFrameBufferTrait {
|
||||
fn chroma_width(&self) -> i32;
|
||||
fn chroma_height(&self) -> i32;
|
||||
fn stride_y(&self) -> i32;
|
||||
fn stride_uv(&self) -> i32;
|
||||
}
|
||||
|
||||
pub trait BiplanarYuv8Buffer: BiplanarYuvBuffer {
|
||||
fn data_y(&self) -> &[u8];
|
||||
fn data_uv(&self) -> &[u8];
|
||||
}
|
||||
|
||||
pub enum VideoFrameBuffer {
|
||||
Native(NativeBuffer),
|
||||
I420(I420Buffer),
|
||||
I420A(I420ABuffer),
|
||||
I422(I422Buffer),
|
||||
I444(I444Buffer),
|
||||
I010(I010Buffer),
|
||||
NV12(NV12Buffer),
|
||||
}
|
||||
|
||||
impl VideoFrameBuffer {
|
||||
pub(crate) fn new(mut cxx_handle: UniquePtr<vfb_sys::ffi::VideoFrameBuffer>) -> Self {
|
||||
unsafe {
|
||||
match cxx_handle.buffer_type().into() {
|
||||
VideoFrameBufferType::Native => Self::Native(NativeBuffer::from(cxx_handle)),
|
||||
VideoFrameBufferType::I420 => {
|
||||
Self::I420(I420Buffer::from(cxx_handle.pin_mut().get_i420()))
|
||||
}
|
||||
VideoFrameBufferType::I420A => {
|
||||
Self::I420A(I420ABuffer::from(cxx_handle.pin_mut().get_i420a()))
|
||||
}
|
||||
VideoFrameBufferType::I422 => {
|
||||
Self::I422(I422Buffer::from(cxx_handle.pin_mut().get_i422()))
|
||||
}
|
||||
VideoFrameBufferType::I444 => {
|
||||
Self::I444(I444Buffer::from(cxx_handle.pin_mut().get_i444()))
|
||||
}
|
||||
VideoFrameBufferType::I010 => {
|
||||
Self::I010(I010Buffer::from(cxx_handle.pin_mut().get_i010()))
|
||||
}
|
||||
VideoFrameBufferType::NV12 => {
|
||||
Self::NV12(NV12Buffer::from(cxx_handle.pin_mut().get_nv12()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused_unsafe)]
|
||||
pub(crate) fn release(self) -> UniquePtr<vfb_sys::ffi::VideoFrameBuffer> {
|
||||
unsafe {
|
||||
match self {
|
||||
VideoFrameBuffer::Native(native) => native.release(),
|
||||
VideoFrameBuffer::I420(i420) => UniquePtr::from_raw(recursive_cast!(
|
||||
i420.release().into_raw(),
|
||||
i420_to_yuv8,
|
||||
yuv8_to_yuv,
|
||||
yuv_to_vfb
|
||||
) as *mut _),
|
||||
VideoFrameBuffer::I420A(i420a) => UniquePtr::from_raw(recursive_cast!(
|
||||
i420a.release().into_raw(),
|
||||
i420a_to_yuv8,
|
||||
yuv8_to_yuv,
|
||||
yuv_to_vfb
|
||||
) as *mut _),
|
||||
VideoFrameBuffer::I422(i422) => UniquePtr::from_raw(recursive_cast!(
|
||||
i422.release().into_raw(),
|
||||
i422_to_yuv8,
|
||||
yuv8_to_yuv,
|
||||
yuv_to_vfb
|
||||
) as *mut _),
|
||||
VideoFrameBuffer::I444(i444) => UniquePtr::from_raw(recursive_cast!(
|
||||
i444.release().into_raw(),
|
||||
i444_to_yuv8,
|
||||
yuv8_to_yuv,
|
||||
yuv_to_vfb
|
||||
) as *mut _),
|
||||
VideoFrameBuffer::I010(i010) => UniquePtr::from_raw(recursive_cast!(
|
||||
i010.release().into_raw(),
|
||||
i010_to_yuv16b,
|
||||
yuv16b_to_yuv,
|
||||
yuv_to_vfb
|
||||
) as *mut _),
|
||||
VideoFrameBuffer::NV12(nv12) => UniquePtr::from_raw(recursive_cast!(
|
||||
nv12.release().into_raw(),
|
||||
nv12_to_biyuv8,
|
||||
biyuv8_to_biyuv,
|
||||
biyuv_to_vfb
|
||||
) as *mut _),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_argb(
|
||||
&self,
|
||||
format: VideoFormatType,
|
||||
dst: &mut [u8],
|
||||
dst_stride: i32,
|
||||
dst_width: i32,
|
||||
dst_height: i32,
|
||||
) -> Result<(), ConvertError> {
|
||||
match self {
|
||||
Self::I420(i420) => match format {
|
||||
VideoFormatType::ARGB => yuv_helper::i420_to_argb(
|
||||
i420.data_y(),
|
||||
i420.stride_y(),
|
||||
i420.data_u(),
|
||||
i420.stride_u(),
|
||||
i420.data_v(),
|
||||
i420.stride_v(),
|
||||
dst,
|
||||
dst_stride,
|
||||
dst_width,
|
||||
dst_height,
|
||||
)?,
|
||||
VideoFormatType::BGRA => yuv_helper::i420_to_bgra(
|
||||
i420.data_y(),
|
||||
i420.stride_y(),
|
||||
i420.data_u(),
|
||||
i420.stride_u(),
|
||||
i420.data_v(),
|
||||
i420.stride_v(),
|
||||
dst,
|
||||
dst_stride,
|
||||
dst_width,
|
||||
dst_height,
|
||||
)?,
|
||||
VideoFormatType::ABGR => yuv_helper::i420_to_abgr(
|
||||
i420.data_y(),
|
||||
i420.stride_y(),
|
||||
i420.data_u(),
|
||||
i420.stride_u(),
|
||||
i420.data_v(),
|
||||
i420.stride_v(),
|
||||
dst,
|
||||
dst_stride,
|
||||
dst_width,
|
||||
dst_height,
|
||||
)?,
|
||||
VideoFormatType::RGBA => yuv_helper::i420_to_rgba(
|
||||
i420.data_y(),
|
||||
i420.stride_y(),
|
||||
i420.data_u(),
|
||||
i420.stride_u(),
|
||||
i420.data_v(),
|
||||
i420.stride_v(),
|
||||
dst,
|
||||
dst_stride,
|
||||
dst_width,
|
||||
dst_height,
|
||||
)?,
|
||||
},
|
||||
_ => {
|
||||
// TODO(theomonnom): Support other buffer types
|
||||
}
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl VideoFrameBufferTrait for VideoFrameBuffer {
|
||||
enum_dispatch!(
|
||||
[Native, I420, I420A, I422, I444, I010, NV12]
|
||||
fnc!(buffer_type, &Self, [], VideoFrameBufferType);
|
||||
fnc!(width, &Self, [], i32);
|
||||
fnc!(height, &Self, [], i32);
|
||||
fnc!(to_i420, Self, [], I420Buffer);
|
||||
);
|
||||
}
|
||||
|
||||
macro_rules! impl_video_frame_buffer {
|
||||
($x:ty $(, $cast:ident)*) => {
|
||||
|
||||
// Allow unused_unsafe when we don't do any cast ( e.g. NativeBuffer )
|
||||
#[allow(unused_unsafe)]
|
||||
impl VideoFrameBufferTrait for $x {
|
||||
fn buffer_type(&self) -> VideoFrameBufferType {
|
||||
let ptr = recursive_cast!(&*self.cxx_handle $(, $cast)*);
|
||||
unsafe {
|
||||
(*ptr).buffer_type().into()
|
||||
}
|
||||
}
|
||||
|
||||
fn width(&self) -> i32 {
|
||||
let ptr = recursive_cast!(&*self.cxx_handle $(, $cast)*);
|
||||
unsafe {
|
||||
(*ptr).width()
|
||||
}
|
||||
}
|
||||
|
||||
fn height(&self) -> i32 {
|
||||
let ptr = recursive_cast!(&*self.cxx_handle $(, $cast)*);
|
||||
unsafe {
|
||||
(*ptr).height()
|
||||
}
|
||||
}
|
||||
|
||||
// Require ownership because libwebrtc uses the same pointers
|
||||
fn to_i420(self) -> I420Buffer {
|
||||
let ptr = recursive_cast!(&*self.cxx_handle $(, $cast)*)
|
||||
as *const vfb_sys::ffi::VideoFrameBuffer
|
||||
as *mut vfb_sys::ffi::VideoFrameBuffer;
|
||||
|
||||
unsafe {
|
||||
I420Buffer::from(Pin::new_unchecked(&mut *ptr).to_i420())
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! impl_yuv_buffer {
|
||||
($x:ty $(, $cast:ident)*) => {
|
||||
impl PlanarYuvBuffer for $x {
|
||||
fn chroma_width(&self) -> i32 {
|
||||
let ptr = recursive_cast!(&*self.cxx_handle $(, $cast)*);
|
||||
unsafe {
|
||||
(*ptr).chroma_width()
|
||||
}
|
||||
}
|
||||
|
||||
fn chroma_height(&self) -> i32 {
|
||||
let ptr = recursive_cast!(&*self.cxx_handle $(, $cast)*);
|
||||
unsafe {
|
||||
(*ptr).chroma_height()
|
||||
}
|
||||
}
|
||||
|
||||
fn stride_y(&self) -> i32 {
|
||||
let ptr = recursive_cast!(&*self.cxx_handle $(, $cast)*);
|
||||
unsafe {
|
||||
(*ptr).stride_y()
|
||||
}
|
||||
}
|
||||
|
||||
fn stride_u(&self) -> i32 {
|
||||
let ptr = recursive_cast!(&*self.cxx_handle $(, $cast)*);
|
||||
unsafe {
|
||||
(*ptr).stride_u()
|
||||
}
|
||||
}
|
||||
|
||||
fn stride_v(&self) -> i32 {
|
||||
let ptr = recursive_cast!(&*self.cxx_handle $(, $cast)*);
|
||||
unsafe {
|
||||
(*ptr).stride_v()
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! impl_yuv8_buffer {
|
||||
($x:ty $(, $cast:ident)*) => {
|
||||
impl PlanarYuv8Buffer for $x {
|
||||
fn data_y(&self) -> &[u8] {
|
||||
let ptr = recursive_cast!(&*self.cxx_handle $(, $cast)*);
|
||||
unsafe {
|
||||
slice::from_raw_parts((*ptr).data_y(), (self.width() * self.height()) as usize)
|
||||
}
|
||||
}
|
||||
|
||||
fn data_u(&self) -> &[u8] {
|
||||
let ptr = recursive_cast!(&*self.cxx_handle $(, $cast)*);
|
||||
unsafe {
|
||||
let chroma_height = (self.height() + 1) / 2;
|
||||
slice::from_raw_parts((*ptr).data_u(), (self.stride_u() * chroma_height) as usize)
|
||||
}
|
||||
}
|
||||
|
||||
fn data_v(&self) -> &[u8] {
|
||||
let ptr = recursive_cast!(&*self.cxx_handle $(, $cast)*);
|
||||
unsafe {
|
||||
let chroma_height = (self.height() + 1) / 2;
|
||||
slice::from_raw_parts((*ptr).data_v(), (self.stride_v() * chroma_height) as usize)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! impl_yuv16_buffer {
|
||||
($x:ty $(, $cast:ident)*) => {
|
||||
impl PlanarYuv16BBuffer for $x {
|
||||
fn data_y(&self) -> &[u16] {
|
||||
let ptr = recursive_cast!(&*self.cxx_handle $(, $cast)*);
|
||||
unsafe {
|
||||
slice::from_raw_parts((*ptr).data_y(), (self.width() * self.height()) as usize)
|
||||
}
|
||||
}
|
||||
|
||||
fn data_u(&self) -> &[u16] {
|
||||
let ptr = recursive_cast!(&*self.cxx_handle $(, $cast)*);
|
||||
unsafe {
|
||||
let chroma_height = (self.height() + 1) / 2;
|
||||
slice::from_raw_parts((*ptr).data_u(), (self.stride_u() * chroma_height) as usize)
|
||||
}
|
||||
}
|
||||
|
||||
fn data_v(&self) -> &[u16] {
|
||||
let ptr = recursive_cast!(&*self.cxx_handle $(, $cast)*);
|
||||
unsafe {
|
||||
let chroma_height = (self.height() + 1) / 2;
|
||||
slice::from_raw_parts((*ptr).data_v(), (self.stride_v() * chroma_height) as usize)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! impl_biyuv_buffer {
|
||||
($x:ty $(, $cast:ident)*) => {
|
||||
impl BiplanarYuvBuffer for $x {
|
||||
fn chroma_width(&self) -> i32 {
|
||||
let ptr = recursive_cast!(&*self.cxx_handle $(, $cast)*);
|
||||
unsafe {
|
||||
(*ptr).chroma_width()
|
||||
}
|
||||
}
|
||||
|
||||
fn chroma_height(&self) -> i32 {
|
||||
let ptr = recursive_cast!(&*self.cxx_handle $(, $cast)*);
|
||||
unsafe {
|
||||
(*ptr).chroma_height()
|
||||
}
|
||||
}
|
||||
|
||||
fn stride_y(&self) -> i32 {
|
||||
let ptr = recursive_cast!(&*self.cxx_handle $(, $cast)*);
|
||||
unsafe {
|
||||
(*ptr).stride_y()
|
||||
}
|
||||
}
|
||||
|
||||
fn stride_uv(&self) -> i32 {
|
||||
let ptr = recursive_cast!(&*self.cxx_handle $(, $cast)*);
|
||||
unsafe {
|
||||
(*ptr).stride_uv()
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! impl_biyuv8_buffer {
|
||||
($x:ty $(, $cast:ident)*) => {
|
||||
impl BiplanarYuv8Buffer for $x {
|
||||
fn data_y(&self) -> &[u8] {
|
||||
let ptr = recursive_cast!(&*self.cxx_handle $(, $cast)*);
|
||||
unsafe {
|
||||
slice::from_raw_parts((*ptr).data_y(), (self.width() * self.height()) as usize)
|
||||
}
|
||||
}
|
||||
|
||||
fn data_uv(&self) -> &[u8] {
|
||||
let ptr = recursive_cast!(&*self.cxx_handle $(, $cast)*);
|
||||
unsafe {
|
||||
let chroma_height = (self.height() + 1) / 2;
|
||||
slice::from_raw_parts((*ptr).data_uv(), (self.stride_uv() * chroma_height) as usize)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub struct NativeBuffer {
|
||||
cxx_handle: UniquePtr<vfb_sys::ffi::VideoFrameBuffer>,
|
||||
}
|
||||
|
||||
pub struct I420Buffer {
|
||||
cxx_handle: UniquePtr<vfb_sys::ffi::I420Buffer>,
|
||||
}
|
||||
|
||||
pub struct I420ABuffer {
|
||||
cxx_handle: UniquePtr<vfb_sys::ffi::I420ABuffer>,
|
||||
}
|
||||
|
||||
pub struct I422Buffer {
|
||||
cxx_handle: UniquePtr<vfb_sys::ffi::I422Buffer>,
|
||||
}
|
||||
|
||||
pub struct I444Buffer {
|
||||
cxx_handle: UniquePtr<vfb_sys::ffi::I444Buffer>,
|
||||
}
|
||||
|
||||
pub struct I010Buffer {
|
||||
cxx_handle: UniquePtr<vfb_sys::ffi::I010Buffer>,
|
||||
}
|
||||
|
||||
pub struct NV12Buffer {
|
||||
cxx_handle: UniquePtr<vfb_sys::ffi::NV12Buffer>,
|
||||
}
|
||||
|
||||
impl_video_frame_buffer!(NativeBuffer);
|
||||
impl_video_frame_buffer!(I420Buffer, i420_to_yuv8, yuv8_to_yuv, yuv_to_vfb);
|
||||
impl_video_frame_buffer!(I420ABuffer, i420a_to_yuv8, yuv8_to_yuv, yuv_to_vfb);
|
||||
impl_video_frame_buffer!(I422Buffer, i422_to_yuv8, yuv8_to_yuv, yuv_to_vfb);
|
||||
impl_video_frame_buffer!(I444Buffer, i444_to_yuv8, yuv8_to_yuv, yuv_to_vfb);
|
||||
impl_video_frame_buffer!(I010Buffer, i010_to_yuv16b, yuv16b_to_yuv, yuv_to_vfb);
|
||||
impl_video_frame_buffer!(NV12Buffer, nv12_to_biyuv8, biyuv8_to_biyuv, biyuv_to_vfb);
|
||||
|
||||
impl_yuv_buffer!(I420Buffer, i420_to_yuv8, yuv8_to_yuv);
|
||||
impl_yuv_buffer!(I420ABuffer, i420a_to_yuv8, yuv8_to_yuv);
|
||||
impl_yuv_buffer!(I422Buffer, i422_to_yuv8, yuv8_to_yuv);
|
||||
impl_yuv_buffer!(I444Buffer, i444_to_yuv8, yuv8_to_yuv);
|
||||
impl_yuv_buffer!(I010Buffer, i010_to_yuv16b, yuv16b_to_yuv);
|
||||
|
||||
impl_yuv8_buffer!(I420Buffer, i420_to_yuv8);
|
||||
impl_yuv8_buffer!(I420ABuffer, i420a_to_yuv8);
|
||||
impl_yuv8_buffer!(I422Buffer, i422_to_yuv8);
|
||||
impl_yuv8_buffer!(I444Buffer, i444_to_yuv8);
|
||||
|
||||
impl_yuv16_buffer!(I010Buffer, i010_to_yuv16b);
|
||||
|
||||
impl_biyuv_buffer!(NV12Buffer, nv12_to_biyuv8, biyuv8_to_biyuv);
|
||||
|
||||
impl_biyuv8_buffer!(NV12Buffer, nv12_to_biyuv8);
|
||||
|
||||
impl NativeBuffer {
|
||||
fn from(cxx_handle: UniquePtr<vfb_sys::ffi::VideoFrameBuffer>) -> Self {
|
||||
Self { cxx_handle }
|
||||
}
|
||||
|
||||
pub(crate) fn release(self) -> UniquePtr<vfb_sys::ffi::VideoFrameBuffer> {
|
||||
self.cxx_handle
|
||||
}
|
||||
}
|
||||
|
||||
impl I420Buffer {
|
||||
pub fn new(width: u32, height: u32) -> Self {
|
||||
Self::from(vfb_sys::ffi::create_i420_buffer(
|
||||
width as i32,
|
||||
height as i32,
|
||||
))
|
||||
}
|
||||
|
||||
fn from(cxx_handle: UniquePtr<vfb_sys::ffi::I420Buffer>) -> Self {
|
||||
Self { cxx_handle }
|
||||
}
|
||||
|
||||
pub(crate) fn release(self) -> UniquePtr<vfb_sys::ffi::I420Buffer> {
|
||||
self.cxx_handle
|
||||
}
|
||||
}
|
||||
|
||||
impl I420ABuffer {
|
||||
fn from(cxx_handle: UniquePtr<vfb_sys::ffi::I420ABuffer>) -> Self {
|
||||
Self { cxx_handle }
|
||||
}
|
||||
|
||||
pub(crate) fn release(self) -> UniquePtr<vfb_sys::ffi::I420ABuffer> {
|
||||
self.cxx_handle
|
||||
}
|
||||
}
|
||||
|
||||
impl I422Buffer {
|
||||
fn from(cxx_handle: UniquePtr<vfb_sys::ffi::I422Buffer>) -> Self {
|
||||
Self { cxx_handle }
|
||||
}
|
||||
|
||||
pub(crate) fn release(self) -> UniquePtr<vfb_sys::ffi::I422Buffer> {
|
||||
self.cxx_handle
|
||||
}
|
||||
}
|
||||
|
||||
impl I444Buffer {
|
||||
fn from(cxx_handle: UniquePtr<vfb_sys::ffi::I444Buffer>) -> Self {
|
||||
Self { cxx_handle }
|
||||
}
|
||||
|
||||
pub(crate) fn release(self) -> UniquePtr<vfb_sys::ffi::I444Buffer> {
|
||||
self.cxx_handle
|
||||
}
|
||||
}
|
||||
|
||||
impl I010Buffer {
|
||||
fn from(cxx_handle: UniquePtr<vfb_sys::ffi::I010Buffer>) -> Self {
|
||||
Self { cxx_handle }
|
||||
}
|
||||
|
||||
pub(crate) fn release(self) -> UniquePtr<vfb_sys::ffi::I010Buffer> {
|
||||
self.cxx_handle
|
||||
}
|
||||
}
|
||||
|
||||
impl NV12Buffer {
|
||||
fn from(cxx_handle: UniquePtr<vfb_sys::ffi::NV12Buffer>) -> Self {
|
||||
Self { cxx_handle }
|
||||
}
|
||||
|
||||
pub(crate) fn release(self) -> UniquePtr<vfb_sys::ffi::NV12Buffer> {
|
||||
self.cxx_handle
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
use crate::imp::video_source as vs_imp;
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub mod native {
|
||||
use super::vs_imp;
|
||||
use crate::video_frame::{VideoFrame, VideoFrameBuffer};
|
||||
use std::fmt::{Debug, Formatter};
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
pub struct NativeVideoSource {
|
||||
pub(crate) handle: vs_imp::NativeVideoSource,
|
||||
}
|
||||
|
||||
impl Debug for NativeVideoSource {
|
||||
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
|
||||
f.debug_struct("NativeVideoSource").finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl NativeVideoSource {
|
||||
pub fn capture_frame<T: VideoFrameBuffer>(&self, frame: &VideoFrame<T>) {
|
||||
self.handle.capture_frame(frame)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub mod web {}
|
||||
@@ -0,0 +1,54 @@
|
||||
use crate::imp::video_stream as stream_imp;
|
||||
|
||||
// There is no shared sink between native and web platforms.
|
||||
// Each platform requires different configuration (e.g: WebGlContext, ..)
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub mod native {
|
||||
use super::stream_imp;
|
||||
use crate::media_stream::RtcVideoTrack;
|
||||
use crate::video_frame::BoxVideoFrame;
|
||||
use futures::stream::Stream;
|
||||
use std::fmt::Debug;
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
pub struct NativeVideoStream {
|
||||
pub(crate) handle: stream_imp::NativeVideoStream,
|
||||
}
|
||||
|
||||
impl Debug for NativeVideoStream {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
f.debug_struct("NativeVideoStream")
|
||||
.field("track", &self.track())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl NativeVideoStream {
|
||||
pub fn new(video_track: RtcVideoTrack) -> Self {
|
||||
Self {
|
||||
handle: stream_imp::NativeVideoStream::new(video_track),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn track(&self) -> RtcVideoTrack {
|
||||
self.handle.track()
|
||||
}
|
||||
|
||||
pub fn close(&mut self) {
|
||||
self.handle.close();
|
||||
}
|
||||
}
|
||||
|
||||
impl Stream for NativeVideoStream {
|
||||
type Item = BoxVideoFrame;
|
||||
|
||||
fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Option<Self::Item>> {
|
||||
Pin::new(&mut self.get_mut().handle).poll_next(cx)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub mod web {}
|
||||
@@ -0,0 +1,103 @@
|
||||
use core::str;
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
use wasm_bindgen::prelude::*;
|
||||
use web_sys::{MessageEvent, RtcDataChannelEvent, RtcDataChannelState};
|
||||
|
||||
use crate::data_channel::{
|
||||
DataChannelError, DataChannelTrait, DataState, OnBufferedAmountChange, OnMessage, OnStateChange,
|
||||
};
|
||||
|
||||
impl From<RtcDataChannelState> for DataState {
|
||||
fn from(value: RtcDataChannelState) -> Self {
|
||||
match value {
|
||||
RtcDataChannelState::Connecting => Self::Connecting,
|
||||
RtcDataChannelState::Open => Self::Open,
|
||||
RtcDataChannelState::Closing => Self::Closing,
|
||||
RtcDataChannelState::Closed => Self::Closed,
|
||||
_ => panic!("unknown data channel state"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct DataChannel {
|
||||
sys_handle: web_sys::RtcDataChannel,
|
||||
on_closing: Rc<RefCell<Option<JsValue>>>,
|
||||
}
|
||||
|
||||
impl DataChannelTrait for DataChannel {
|
||||
fn send(&self, data: &[u8], binary: bool) -> Result<(), DataChannelError> {
|
||||
if binary {
|
||||
self.sys_handle
|
||||
.send_with_u8_array(data)
|
||||
.map_err(|_| DataChannelError::Send)
|
||||
} else {
|
||||
let utf8 = str::from_utf8(data)?;
|
||||
self.sys_handle
|
||||
.send_with_str(utf8)
|
||||
.map_err(|_| DataChannelError::Send)
|
||||
}
|
||||
}
|
||||
|
||||
fn label(&self) -> String {
|
||||
self.sys_handle.label()
|
||||
}
|
||||
|
||||
fn state(&self) -> DataState {
|
||||
self.sys_handle.ready_state().into()
|
||||
}
|
||||
|
||||
fn close(&self) {
|
||||
self.sys_handle.close();
|
||||
}
|
||||
|
||||
fn on_state_change(&self, callback: Option<OnStateChange>) {
|
||||
if let Some(mut callback) = callback {
|
||||
let dc = self.clone();
|
||||
let js_callback = Closure::new(move |_: RtcDataChannelEvent| {
|
||||
callback(dc.state());
|
||||
});
|
||||
let js_callback = js_callback.into_js_value();
|
||||
self.sys_handle
|
||||
.set_onopen(Some(js_callback.unchecked_ref()));
|
||||
self.sys_handle
|
||||
.set_onclose(Some(js_callback.unchecked_ref()));
|
||||
self.sys_handle
|
||||
.add_event_listener_with_callback("closing", js_callback.unchecked_ref())
|
||||
.unwrap();
|
||||
|
||||
self.on_closing.replace(Some(js_callback));
|
||||
} else {
|
||||
self.sys_handle.set_onopen(None);
|
||||
self.sys_handle.set_onclose(None);
|
||||
if let Some(on_closing) = self.on_closing.take() {
|
||||
self.sys_handle
|
||||
.remove_event_listener_with_callback("closing", on_closing.unchecked_ref())
|
||||
.unwrap();
|
||||
}
|
||||
self.on_closing.replace(None);
|
||||
}
|
||||
}
|
||||
|
||||
fn on_message(&self, callback: Option<OnMessage>) {
|
||||
let js_callback = callback.map(|mut callback| {
|
||||
Closure::new(move |event: MessageEvent| {
|
||||
if let Some(str) = event.as_string() {
|
||||
callback(str.as_bytes(), false);
|
||||
}
|
||||
})
|
||||
.into_js_value()
|
||||
});
|
||||
|
||||
self.sys_handle.set_onmessage(
|
||||
js_callback
|
||||
.as_ref()
|
||||
.map(|callback| callback.unchecked_ref()),
|
||||
);
|
||||
}
|
||||
|
||||
fn on_buffered_amount_change(&self, _callback: Option<OnBufferedAmountChange>) {
|
||||
todo!("onbufferedamountlow instead?")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,358 @@
|
||||
use super::VideoTrack;
|
||||
use crate::{
|
||||
media_stream::{
|
||||
BiplanarYuv8Buffer, BiplanarYuvBuffer, PlanarYuv16BBuffer, PlanarYuv8Buffer,
|
||||
PlanarYuvBuffer, VideoFrameBuffer,
|
||||
},
|
||||
video_frame::{BiplanarYuv8Buffer, I420Buffer, SinkError, VideoFrame, VideoFrameBuffer},
|
||||
I010Buffer, I420ABuffer, I422Buffer, I444Buffer, NV12Buffer,
|
||||
};
|
||||
use std::sync::mpsc;
|
||||
use web_sys::{WebGlRenderingContext, WebGlTexture};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct WebGlVideoSink {
|
||||
track: Arc<VideoTrack>,
|
||||
gl_ctx: WebGlRenderingContext,
|
||||
tex: WebGlTexture,
|
||||
}
|
||||
|
||||
/// Create a new WebGL texture and update it inside requestAnimationFrame
|
||||
impl WebGlVideoSink {
|
||||
pub fn new(
|
||||
track: Arc<VideoTrack>,
|
||||
gl_ctx: WebGlRenderingContext,
|
||||
) -> Result<(Self, mpsc::Receiver<VideoFrame<WebGlBuffer>>), SinkError> {
|
||||
let (sender, receiver) = mpsc::channel();
|
||||
let tex = gl_ctx.create_texture()?;
|
||||
Ok((Self { track, gl_ctx, tex }, receiver))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct WebGlBuffer {
|
||||
width: i32,
|
||||
height: i32,
|
||||
tex: WebGlTexture,
|
||||
}
|
||||
|
||||
impl VideoFrameBuffer for WebGlBuffer {
|
||||
fn width(&self) -> i32 {
|
||||
self.width
|
||||
}
|
||||
|
||||
fn height(&self) -> i32 {
|
||||
self.height
|
||||
}
|
||||
}
|
||||
|
||||
/// The following types could be implemented if we want
|
||||
/// to support VideoFrame with WebCodecs
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct I420Buffer {}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct I420ABuffer {}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct I422Buffer {}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct I444Buffer {}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct I010Buffer {}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct NV12Buffer {}
|
||||
|
||||
impl VideoFrameBuffer for I420Buffer {
|
||||
fn width(&self) -> i32 {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn height(&self) -> i32 {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl VideoFrameBuffer for I420ABuffer {
|
||||
fn width(&self) -> i32 {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn height(&self) -> i32 {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl VideoFrameBuffer for I422Buffer {
|
||||
fn width(&self) -> i32 {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn height(&self) -> i32 {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl VideoFrameBuffer for I444Buffer {
|
||||
fn width(&self) -> i32 {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn height(&self) -> i32 {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl VideoFrameBuffer for I010Buffer {
|
||||
fn width(&self) -> i32 {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn height(&self) -> i32 {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl VideoFrameBuffer for NV12Buffer {
|
||||
fn width(&self) -> i32 {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn height(&self) -> i32 {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl PlanarYuvBuffer for I420Buffer {
|
||||
fn chroma_width(&self) -> i32 {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn chroma_height(&self) -> i32 {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn stride_y(&self) -> i32 {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn stride_u(&self) -> i32 {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn stride_v(&self) -> i32 {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl PlanarYuvBuffer for I420ABuffer {
|
||||
fn chroma_width(&self) -> i32 {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn chroma_height(&self) -> i32 {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn stride_y(&self) -> i32 {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn stride_u(&self) -> i32 {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn stride_v(&self) -> i32 {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl PlanarYuvBuffer for I422Buffer {
|
||||
fn chroma_width(&self) -> i32 {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn chroma_height(&self) -> i32 {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn stride_y(&self) -> i32 {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn stride_u(&self) -> i32 {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn stride_v(&self) -> i32 {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl PlanarYuvBuffer for I444Buffer {
|
||||
fn chroma_width(&self) -> i32 {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn chroma_height(&self) -> i32 {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn stride_y(&self) -> i32 {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn stride_u(&self) -> i32 {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn stride_v(&self) -> i32 {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl PlanarYuvBuffer for I010Buffer {
|
||||
fn chroma_width(&self) -> i32 {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn chroma_height(&self) -> i32 {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn stride_y(&self) -> i32 {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn stride_u(&self) -> i32 {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn stride_v(&self) -> i32 {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl PlanarYuvBuffer for NV12Buffer {
|
||||
fn chroma_width(&self) -> i32 {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn chroma_height(&self) -> i32 {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn stride_y(&self) -> i32 {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn stride_u(&self) -> i32 {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn stride_v(&self) -> i32 {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl PlanarYuv8Buffer for I420Buffer {
|
||||
fn data_y(&self) -> &[u8] {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn data_u(&self) -> &[u8] {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn data_v(&self) -> &[u8] {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl PlanarYuv8Buffer for I420ABuffer {
|
||||
fn data_y(&self) -> &[u8] {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn data_u(&self) -> &[u8] {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn data_v(&self) -> &[u8] {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl PlanarYuv8Buffer for I422Buffer {
|
||||
fn data_y(&self) -> &[u8] {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn data_u(&self) -> &[u8] {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn data_v(&self) -> &[u8] {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl PlanarYuv8Buffer for I444Buffer {
|
||||
fn data_y(&self) -> &[u8] {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn data_u(&self) -> &[u8] {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn data_v(&self) -> &[u8] {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl PlanarYuv16BBuffer for I010Buffer {
|
||||
fn data_y(&self) -> &[u16] {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn data_u(&self) -> &[u16] {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn data_v(&self) -> &[u16] {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl BiplanarYuvBuffer for NV12Buffer {
|
||||
fn chroma_width(&self) -> i32 {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn chroma_height(&self) -> i32 {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn stride_y(&self) -> i32 {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn stride_uv(&self) -> i32 {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl BiplanarYuv8Buffer for NV12Buffer {
|
||||
fn data_y(&self) -> &[u8] {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn data_uv(&self) -> &[u8] {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
unimplemented!()
|
||||
@@ -1,24 +0,0 @@
|
||||
use cxx::SharedPtr;
|
||||
|
||||
use webrtc_sys::webrtc as sys_rtc;
|
||||
|
||||
pub use sys_rtc::ffi::MediaType;
|
||||
pub use sys_rtc::ffi::Priority;
|
||||
pub use sys_rtc::ffi::RtpTransceiverDirection;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct RTCRuntime {
|
||||
cxx_handle: SharedPtr<sys_rtc::ffi::RTCRuntime>,
|
||||
}
|
||||
|
||||
impl RTCRuntime {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
cxx_handle: sys_rtc::ffi::create_rtc_runtime(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn release(self) -> SharedPtr<sys_rtc::ffi::RTCRuntime> {
|
||||
self.cxx_handle
|
||||
}
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
use thiserror::Error;
|
||||
|
||||
use webrtc_sys::yuv_helper as yuv_sys;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum ConvertError {
|
||||
#[error("conversion failed: {0}")]
|
||||
Convert(&'static str),
|
||||
}
|
||||
|
||||
fn i420_safety(
|
||||
src_y: &[u8],
|
||||
src_stride_y: i32,
|
||||
src_u: &[u8],
|
||||
src_stride_u: i32,
|
||||
src_v: &[u8],
|
||||
src_stride_v: i32,
|
||||
dst: &mut [u8],
|
||||
dst_stride: i32,
|
||||
_width: i32,
|
||||
height: i32,
|
||||
) -> Result<(), ConvertError> {
|
||||
let chroma_height = (height + 1) / 2;
|
||||
let min_y = (src_stride_y * height) as usize;
|
||||
let min_u = (src_stride_u * chroma_height) as usize;
|
||||
let min_v = (src_stride_v * chroma_height) as usize;
|
||||
let min_dst = (dst_stride * height) as usize;
|
||||
|
||||
if src_y.len() < min_y {
|
||||
return Err(ConvertError::Convert("src_y isn't large enough"));
|
||||
}
|
||||
|
||||
if src_u.len() < min_u {
|
||||
return Err(ConvertError::Convert("src_u isn't large enough"));
|
||||
}
|
||||
|
||||
if src_v.len() < min_v {
|
||||
return Err(ConvertError::Convert("src_v isn't large enough"));
|
||||
}
|
||||
|
||||
if dst.len() < min_dst {
|
||||
return Err(ConvertError::Convert("dst isn't large enough"));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
macro_rules! i420_to_x {
|
||||
($x:ident) => {
|
||||
pub fn $x(
|
||||
src_y: &[u8],
|
||||
src_stride_y: i32,
|
||||
src_u: &[u8],
|
||||
src_stride_u: i32,
|
||||
src_v: &[u8],
|
||||
src_stride_v: i32,
|
||||
dst: &mut [u8],
|
||||
dst_stride: i32,
|
||||
width: i32,
|
||||
height: i32,
|
||||
) -> Result<(), ConvertError> {
|
||||
i420_safety(
|
||||
src_y,
|
||||
src_stride_y,
|
||||
src_u,
|
||||
src_stride_u,
|
||||
src_v,
|
||||
src_stride_v,
|
||||
dst,
|
||||
dst_stride,
|
||||
width,
|
||||
height,
|
||||
)?;
|
||||
|
||||
unsafe {
|
||||
yuv_sys::ffi::$x(
|
||||
src_y.as_ptr(),
|
||||
src_stride_y,
|
||||
src_u.as_ptr(),
|
||||
src_stride_u,
|
||||
src_v.as_ptr(),
|
||||
src_stride_v,
|
||||
dst.as_mut_ptr(),
|
||||
dst_stride,
|
||||
width,
|
||||
height,
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
i420_to_x!(i420_to_argb);
|
||||
i420_to_x!(i420_to_bgra);
|
||||
i420_to_x!(i420_to_abgr);
|
||||
i420_to_x!(i420_to_rgba);
|
||||
Reference in New Issue
Block a user