Initial downstream tracks

This commit is contained in:
Théo Monnom
2022-11-22 17:59:55 +01:00
parent 82ec808dee
commit ae776f04d0
49 changed files with 1910 additions and 406 deletions
@@ -0,0 +1,277 @@
use cxx::UniquePtr;
use libwebrtc_sys::media_stream as sys_ms;
use libwebrtc_sys::MEDIA_TYPE_VIDEO;
use std::fmt::{Debug, Formatter};
use std::pin::Pin;
use std::sync::{Arc, Mutex};
pub use sys_ms::ffi::ContentHint;
pub use sys_ms::ffi::TrackState;
use crate::video_frame::VideoFrame;
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;
}
pub enum MediaStreamTrack {
Audio(Arc<AudioTrack>),
Video(Arc<VideoTrack>),
}
macro_rules! shared_getter {
($x:ident, $ret:ty) => {
fn $x(&self) -> $ret {
match self {
Self::Video(inner) => inner.$x(),
Self::Audio(inner) => inner.$x(),
}
}
};
}
impl MediaStreamTrack {
pub(crate) fn new(cxx_handle: UniquePtr<sys_ms::ffi::MediaStreamTrack>) -> Self {
unsafe {
if cxx_handle.kind() == MEDIA_TYPE_VIDEO {
Self::Video(Arc::new(VideoTrack::new(UniquePtr::from_raw(
sys_ms::ffi::media_to_video(cxx_handle.into_raw())
as *mut sys_ms::ffi::VideoTrack,
))))
} else {
Self::Audio(Arc::new(AudioTrack::new(UniquePtr::from_raw(
sys_ms::ffi::media_to_audio(cxx_handle.into_raw())
as *mut sys_ms::ffi::AudioTrack,
))))
}
}
}
}
impl Debug for MediaStreamTrack {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
f.debug_struct("MediaStreamTrack")
.field("id", &self.id())
.field("kind", &self.kind())
.field("enabled", &self.enabled())
.field("state", &self.state())
.finish()
}
}
impl MediaStreamTrackTrait for MediaStreamTrack {
shared_getter!(kind, String);
shared_getter!(id, String);
shared_getter!(enabled, bool);
shared_getter!(state, TrackState);
fn set_enabled(&self, enabled: bool) -> bool {
match self {
Self::Video(inner) => inner.set_enabled(enabled),
Self::Audio(inner) => inner.set_enabled(enabled),
}
}
}
pub struct AudioTrack {
cxx_handle: Mutex<UniquePtr<sys_ms::ffi::AudioTrack>>,
}
impl AudioTrack {
pub(crate) fn new(cxx_handle: UniquePtr<sys_ms::ffi::AudioTrack>) -> Self {
Self {
cxx_handle: Mutex::new(cxx_handle),
}
}
}
pub struct VideoTrack {
cxx_handle: Mutex<UniquePtr<sys_ms::ffi::VideoTrack>>,
observer: Box<InternalVideoTrackSink>,
// Keep alive for c++
native_observer: UniquePtr<sys_ms::ffi::NativeVideoFrameSink>,
}
macro_rules! impl_media_stream_track_trait {
($x:ty, $cast:ident) => {
impl MediaStreamTrackTrait for $x {
fn kind(&self) -> String {
unsafe { (*sys_ms::ffi::$cast(&**self.cxx_handle.lock().unwrap())).kind() }
}
fn id(&self) -> String {
unsafe { (*sys_ms::ffi::$cast(&**self.cxx_handle.lock().unwrap())).id() }
}
fn enabled(&self) -> bool {
unsafe { (*sys_ms::ffi::$cast(&**self.cxx_handle.lock().unwrap())).enabled() }
}
fn set_enabled(&self, enabled: bool) -> bool {
unsafe {
let media =
sys_ms::ffi::$cast(&**self.cxx_handle.lock().unwrap()) as *mut sys_ms::ffi::MediaStreamTrack;
Pin::new_unchecked(&mut *media).set_enabled(enabled)
}
}
fn state(&self) -> TrackState {
unsafe { (*sys_ms::ffi::$cast(&**self.cxx_handle.lock().unwrap())).state() }
}
}
};
}
impl_media_stream_track_trait!(VideoTrack, video_to_media);
impl_media_stream_track_trait!(AudioTrack, audio_to_media);
pub type OnFrameHandler = Box<dyn FnMut(VideoFrame) + Send + Sync>;
pub type OnDiscardedFrameHandler = Box<dyn FnMut() + Send + Sync>;
pub type OnConstraintsChanged = 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<OnConstraintsChanged>>,
}
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<libwebrtc_sys::video_frame::ffi::VideoFrame>) {
if let Some(cb) = self.on_frame_handler.lock().unwrap().as_mut() {
cb(VideoFrame::new(frame));
}
}
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());
}
}
}
impl VideoTrack {
fn new(cxx_handle: UniquePtr<sys_ms::ffi::VideoTrack>) -> Self {
let mut observer = Box::new(InternalVideoTrackSink::default());
let mut track = unsafe {
Self {
cxx_handle: Mutex::new(cxx_handle),
native_observer: sys_ms::ffi::create_native_video_frame_sink(Box::new(
sys_ms::VideoFrameSinkWrapper::new(&mut *observer),
)),
observer,
}
};
unsafe {
track
.cxx_handle
.lock()
.unwrap()
.pin_mut()
.add_sink(track.native_observer.pin_mut());
}
track
}
pub fn set_should_receive(&mut self, should_receive: bool) {
self.cxx_handle
.lock()
.unwrap()
.pin_mut()
.set_should_receive(should_receive)
}
pub fn set_content_hint(&mut self, hint: ContentHint) {
self.cxx_handle
.lock()
.unwrap()
.pin_mut()
.set_content_hint(hint)
}
pub fn should_receive(&self) -> bool {
self.cxx_handle.lock().unwrap().should_receive()
}
pub fn content_hint(&self) -> ContentHint {
self.cxx_handle.lock().unwrap().content_hint()
}
pub fn on_frame(&mut self, handler: OnFrameHandler) {
*self.observer.on_frame_handler.lock().unwrap() = Some(handler);
}
pub fn on_discarded_frame(&mut self, handler: OnDiscardedFrameHandler) {
*self.observer.on_discarded_frame_handler.lock().unwrap() = Some(handler);
}
pub fn on_constraints_changed(&mut self, handler: OnConstraintsChanged) {
*self.observer.on_constraints_changed_handler.lock().unwrap() = Some(handler);
}
}
impl Drop for VideoTrack {
fn drop(&mut self) {
unsafe {
self.cxx_handle
.lock()
.unwrap()
.pin_mut()
.remove_sink(self.native_observer.pin_mut());
}
}
}
pub struct MediaStream {
cxx_handle: UniquePtr<sys_ms::ffi::MediaStream>,
}
impl Debug for MediaStream {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
f.debug_struct("MediaStream")
.field("id", &self.id())
.finish()
}
}
impl MediaStream {
pub(crate) fn new(cxx_handle: UniquePtr<sys_ms::ffi::MediaStream>) -> Self {
Self { cxx_handle }
}
pub fn id(&self) -> String {
self.cxx_handle.id()
}
}
+4 -14
View File
@@ -109,13 +109,13 @@ impl Drop for DataChannel {
pub type OnStateChangeHandler = Box<dyn FnMut() + Send + Sync>;
pub type OnMessageHandler = Box<dyn FnMut(&[u8], bool) + Send + Sync>;
// data, is_binary
pub type OnBufferedAmountChangeHandler = Box<dyn FnMut(u64) + Send + Sync>;
#[derive(Default)]
struct InternalDataChannelObserver {
on_state_change_handler: Arc<Mutex<Option<OnStateChangeHandler>>>,
on_message_handler: Arc<Mutex<Option<OnMessageHandler>>>,
on_buffered_amount_change_handler: Arc<Mutex<Option<OnBufferedAmountChangeHandler>>>,
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 {
@@ -144,16 +144,6 @@ impl sys_dc::DataChannelObserver for InternalDataChannelObserver {
}
}
impl Default for InternalDataChannelObserver {
fn default() -> Self {
Self {
on_state_change_handler: Arc::new(Default::default()),
on_message_handler: Arc::new(Default::default()),
on_buffered_amount_change_handler: Arc::new(Default::default()),
}
}
}
#[derive(Debug)]
pub struct DataChannelInit {
#[deprecated]
+1 -1
View File
@@ -1,4 +1,4 @@
use std::fmt::{Debug, Display, Formatter};
use std::fmt::{Debug, Formatter};
use cxx::UniquePtr;
+2
View File
@@ -6,4 +6,6 @@ pub mod peer_connection_factory;
pub mod rtc_error;
pub mod rtp_receiver;
pub mod rtp_transceiver;
pub mod video_frame;
pub mod video_frame_buffer;
pub mod webrtc;
+225 -18
View File
@@ -1,14 +1,59 @@
use cxx::UniquePtr;
use std::fmt::{Debug, Formatter};
use libwebrtc_sys::media_stream as sys_ms;
use libwebrtc_sys::MEDIA_TYPE_VIDEO;
use std::fmt::{Debug, Formatter};
use std::pin::Pin;
use std::sync::{Arc, Mutex};
pub use sys_ms::ffi::ContentHint;
pub use sys_ms::ffi::TrackState;
pub struct MediaStreamTrack {
cxx_handle: UniquePtr<sys_ms::ffi::MediaStreamTrack>,
use crate::video_frame::VideoFrame;
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;
}
impl Debug for MediaStreamTrack {
#[derive(Clone)]
pub enum MediaStreamTrackHandle {
Audio(Arc<AudioTrack>),
Video(Arc<VideoTrack>),
}
macro_rules! shared_getter {
($x:ident, $ret:ty) => {
fn $x(&self) -> $ret {
match self {
Self::Video(inner) => inner.$x(),
Self::Audio(inner) => inner.$x(),
}
}
};
}
impl MediaStreamTrackHandle {
pub(crate) fn new(cxx_handle: UniquePtr<sys_ms::ffi::MediaStreamTrack>) -> Self {
unsafe {
if cxx_handle.kind() == MEDIA_TYPE_VIDEO {
Self::Video(VideoTrack::new(UniquePtr::from_raw(
sys_ms::ffi::media_to_video(cxx_handle.into_raw())
as *mut sys_ms::ffi::VideoTrack,
)))
} else {
Self::Audio(AudioTrack::new(UniquePtr::from_raw(
sys_ms::ffi::media_to_audio(cxx_handle.into_raw())
as *mut sys_ms::ffi::AudioTrack,
)))
}
}
}
}
impl Debug for MediaStreamTrackHandle {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
f.debug_struct("MediaStreamTrack")
.field("id", &self.id())
@@ -19,29 +64,191 @@ impl Debug for MediaStreamTrack {
}
}
impl MediaStreamTrack {
pub(crate) fn new(cxx_handle: UniquePtr<sys_ms::ffi::MediaStreamTrack>) -> Self {
Self { cxx_handle }
impl MediaStreamTrackTrait for MediaStreamTrackHandle {
shared_getter!(kind, String);
shared_getter!(id, String);
shared_getter!(enabled, bool);
shared_getter!(state, TrackState);
fn set_enabled(&self, enabled: bool) -> bool {
match self {
Self::Video(inner) => inner.set_enabled(enabled),
Self::Audio(inner) => inner.set_enabled(enabled),
}
}
}
pub struct AudioTrack {
cxx_handle: Mutex<UniquePtr<sys_ms::ffi::AudioTrack>>,
}
impl AudioTrack {
pub(crate) fn new(cxx_handle: UniquePtr<sys_ms::ffi::AudioTrack>) -> Arc<Self> {
Arc::new(Self {
cxx_handle: Mutex::new(cxx_handle),
})
}
}
pub struct VideoTrack {
cxx_handle: Mutex<UniquePtr<sys_ms::ffi::VideoTrack>>,
observer: Box<InternalVideoTrackSink>,
// Keep alive for c++
native_observer: UniquePtr<sys_ms::ffi::NativeVideoFrameSink>,
}
macro_rules! impl_media_stream_track_trait {
($x:ty, $cast:ident) => {
impl MediaStreamTrackTrait for $x {
fn kind(&self) -> String {
unsafe { (*sys_ms::ffi::$cast(&**self.cxx_handle.lock().unwrap())).kind() }
}
fn id(&self) -> String {
unsafe { (*sys_ms::ffi::$cast(&**self.cxx_handle.lock().unwrap())).id() }
}
fn enabled(&self) -> bool {
unsafe { (*sys_ms::ffi::$cast(&**self.cxx_handle.lock().unwrap())).enabled() }
}
fn set_enabled(&self, enabled: bool) -> bool {
unsafe {
let media =
sys_ms::ffi::$cast(&**self.cxx_handle.lock().unwrap()) as *mut sys_ms::ffi::MediaStreamTrack;
Pin::new_unchecked(&mut *media).set_enabled(enabled)
}
}
fn state(&self) -> TrackState {
unsafe { (*sys_ms::ffi::$cast(&**self.cxx_handle.lock().unwrap())).state() }
}
}
};
}
impl_media_stream_track_trait!(VideoTrack, video_to_media);
impl_media_stream_track_trait!(AudioTrack, audio_to_media);
pub type OnFrameHandler = Box<dyn FnMut(VideoFrame) + Send + Sync>;
pub type OnDiscardedFrameHandler = Box<dyn FnMut() + Send + Sync>;
pub type OnConstraintsChanged = 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<OnConstraintsChanged>>,
}
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<libwebrtc_sys::video_frame::ffi::VideoFrame>) {
if let Some(cb) = self.on_frame_handler.lock().unwrap().as_mut() {
cb(VideoFrame::new(frame));
}
}
fn kind(&self) -> String {
self.cxx_handle.kind()
fn on_discarded_frame(&self) {
if let Some(cb) = self.on_discarded_frame_handler.lock().unwrap().as_mut() {
cb();
}
}
fn id(&self) -> String {
self.cxx_handle.id()
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());
}
}
}
impl VideoTrack {
fn new(cxx_handle: UniquePtr<sys_ms::ffi::VideoTrack>) -> Arc<Self> {
let mut observer = Box::new(InternalVideoTrackSink::default());
let mut track = unsafe {
Self {
cxx_handle: Mutex::new(cxx_handle),
native_observer: sys_ms::ffi::create_native_video_frame_sink(Box::new(
sys_ms::VideoFrameSinkWrapper::new(&mut *observer),
)),
observer,
}
};
unsafe {
track
.cxx_handle
.lock()
.unwrap()
.pin_mut()
.add_sink(track.native_observer.pin_mut());
}
Arc::new(track)
}
fn enabled(&self) -> bool {
self.cxx_handle.enabled()
pub fn set_should_receive(&mut self, should_receive: bool) {
self.cxx_handle
.lock()
.unwrap()
.pin_mut()
.set_should_receive(should_receive)
}
fn set_enabled(&mut self, enable: bool) -> bool {
self.cxx_handle.pin_mut().set_enabled(enable)
pub fn set_content_hint(&mut self, hint: ContentHint) {
self.cxx_handle
.lock()
.unwrap()
.pin_mut()
.set_content_hint(hint)
}
fn state(&self) -> TrackState {
self.cxx_handle.state()
pub fn should_receive(&self) -> bool {
self.cxx_handle.lock().unwrap().should_receive()
}
pub fn content_hint(&self) -> ContentHint {
self.cxx_handle.lock().unwrap().content_hint()
}
pub fn on_frame(&mut self, handler: OnFrameHandler) {
*self.observer.on_frame_handler.lock().unwrap() = Some(handler);
}
pub fn on_discarded_frame(&mut self, handler: OnDiscardedFrameHandler) {
*self.observer.on_discarded_frame_handler.lock().unwrap() = Some(handler);
}
pub fn on_constraints_changed(&mut self, handler: OnConstraintsChanged) {
*self.observer.on_constraints_changed_handler.lock().unwrap() = Some(handler);
}
}
impl Drop for VideoTrack {
fn drop(&mut self) {
unsafe {
self.cxx_handle
.lock()
.unwrap()
.pin_mut()
.remove_sink(self.native_observer.pin_mut());
}
}
}
+1 -1
View File
@@ -18,7 +18,7 @@ pub use libwebrtc_sys::peer_connection::ffi::SignalingState;
use crate::data_channel::{DataChannel, DataChannelInit};
use crate::jsep::{IceCandidate, SessionDescription};
use crate::media_stream::MediaStream;
use crate::media_stream::{MediaStream, VideoTrack, AudioTrack};
use crate::rtc_error::RTCError;
use crate::rtp_receiver::RtpReceiver;
use crate::rtp_transceiver::RtpTransceiver;
+6 -4
View File
@@ -1,4 +1,4 @@
use crate::media_stream::MediaStreamTrack;
use crate::media_stream::{MediaStreamTrackHandle, MediaStreamTrackTrait};
use cxx::UniquePtr;
use libwebrtc_sys::rtp_receiver as sys_rec;
use std::fmt::{Debug, Formatter};
@@ -17,10 +17,12 @@ impl Debug for RtpReceiver {
impl RtpReceiver {
pub(crate) fn new(cxx_handle: UniquePtr<sys_rec::ffi::RtpReceiver>) -> Self {
Self { cxx_handle }
Self {
cxx_handle,
}
}
pub fn track(&self) -> MediaStreamTrack {
MediaStreamTrack::new(self.cxx_handle.track())
pub fn track(&self) -> MediaStreamTrackHandle {
MediaStreamTrackHandle::new(self.cxx_handle.track())
}
}
+50
View File
@@ -0,0 +1,50 @@
use cxx::UniquePtr;
use libwebrtc_sys::video_frame as vf_sys;
pub use vf_sys::ffi::VideoRotation;
pub struct VideoFrame {
cxx_handle: UniquePtr<vf_sys::ffi::VideoFrame>,
}
impl VideoFrame {
pub(crate) fn new(cxx_handle: UniquePtr<vf_sys::ffi::VideoFrame>) -> Self {
Self { cxx_handle }
}
pub fn width(&self) -> i32 {
self.cxx_handle.width()
}
pub fn height(&self) -> i32 {
self.cxx_handle.height()
}
pub fn size(&self) -> u32 {
self.cxx_handle.size()
}
pub fn id(&self) -> u16 {
self.cxx_handle.id()
}
pub fn timestamp_us(&self) -> i64 {
self.cxx_handle.timestamp_us()
}
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()
}
}
@@ -0,0 +1,18 @@
use cxx::UniquePtr;
use libwebrtc_sys::video_frame_buffer as vfb_sys;
pub use vfb_sys::ffi::VideoFrameBufferType;
pub struct VideoFrameBuffer {
cxx_handle: UniquePtr<vfb_sys::ffi::VideoFrameBuffer>,
}
impl VideoFrameBuffer {
pub fn new(cxx_handle: UniquePtr<vfb_sys::ffi::VideoFrameBuffer>) -> Self {
Self { cxx_handle }
}
pub fn buffer_type(&self) -> VideoFrameBufferType {
self.cxx_handle.buffer_type()
}
}