diff --git a/gst/src/bin.rs b/gst/src/bin.rs index 94f6824..7f59d49 100644 --- a/gst/src/bin.rs +++ b/gst/src/bin.rs @@ -1,48 +1,28 @@ -use crate::*; +use crate::priv_prelude::*; -#[repr(transparent)] -pub struct Bin { - inner: gstreamer::Bin, -} +wrap_gst!(Bin); +parent_child!(Element, Bin); -impl From for Bin { - fn from(inner: gstreamer::Bin) -> Self { - Bin { inner } - } -} - -impl IsElement for Bin { - fn as_element(&self) -> &Element { - let element = self.inner.upcast_ref::(); - unsafe { core::mem::transmute(element) } - } - - fn into_element(self) -> Element { - Element { - inner: self.inner.into(), - } - } -} impl Bin { pub fn new(name: impl AsRef) -> Self { let bin = gstreamer::Bin::with_name(name.as_ref()); Bin { inner: bin } } - pub fn add(&mut self, element: &impl IsElement) -> Result<&mut Self> { + pub fn add(&mut self, element: &impl ChildOf) -> Result<&mut Self> { self.inner - .add(&element.as_element().inner) + .add(&element.upcast_ref().inner) .change_context(Error) .attach("Failed to add element to bin")?; Ok(self) } - pub fn add_many<'a, E: IsElement + 'a>( + pub fn add_many<'a, E: ChildOf + 'a>( &mut self, elements: impl IntoIterator, ) -> Result<&mut Self> { self.inner - .add_many(elements.into_iter().map(|e| &e.as_element().inner)) + .add_many(elements.into_iter().map(|e| &e.upcast_ref().inner)) .change_context(Error) .attach("Failed to add elements to bin")?; Ok(self) diff --git a/gst/src/element.rs b/gst/src/element.rs index 230351d..e816e3e 100644 --- a/gst/src/element.rs +++ b/gst/src/element.rs @@ -1,43 +1,45 @@ -use crate::{Bin, Error, Pad, Result, ResultExt}; -#[repr(transparent)] -pub struct Element { - pub(crate) inner: gstreamer::Element, -} +use crate::priv_prelude::*; +use crate::wrap_gst; -pub trait IsElement { - fn as_element(&self) -> ∈ - fn into_element(self) -> Element; - fn pad(&self, name: &str) -> Option { +wrap_gst!(Element, gstreamer::Element); + +// pub trait IsElement { +// fn upcast_ref(&self) -> ∈ +// fn into_element(self) -> Element; +// fn pad(&self, name: &str) -> Option { +// use gstreamer::prelude::*; +// self.upcast_ref().inner.static_pad(name).map(Pad::from) +// } +// } + +// impl IsElement for Element { +// fn upcast_ref(&self) -> &Element { +// self +// } +// +// fn into_element(self) -> Element { +// self +// } +// } + +impl Element { + pub fn pad(&self, name: impl AsRef) -> Option { use gstreamer::prelude::*; - self.as_element().inner.static_pad(name).map(Pad::from) + self.inner.static_pad(name.as_ref()).map(Pad::from) } } -impl IsElement for Element { - fn as_element(&self) -> &Element { - self - } - - fn into_element(self) -> Element { - self - } -} - -pub trait Sink: IsElement { +pub trait Sink: ChildOf { fn sink(&self, name: impl AsRef) -> Pad { - use gstreamer::prelude::*; - self.as_element() - .pad(name.as_ref()) - .map(From::from) + self.upcast_ref() + .pad(name) .expect("Sink element has no sink pad") } } -pub trait Source: IsElement { +pub trait Source: ChildOf { fn source(&self, name: impl AsRef) -> Pad { - use gstreamer::prelude::*; - self.as_element() - .pad(name.as_ref()) - .map(From::from) + self.upcast_ref() + .pad(name) .expect("Source element has no src pad") } @@ -46,13 +48,13 @@ pub trait Source: IsElement { Self: Sized, { use gstreamer::prelude::*; - if let Ok(bin) = self.as_element().inner.clone().downcast::() { - bin.add(&sink.as_element().inner) + if let Ok(bin) = self.upcast_ref().inner.clone().downcast::() { + bin.add(&sink.upcast_ref().inner) .change_context(Error) .attach("Failed to add sink to bin")?; - self.as_element() + self.upcast_ref() .inner - .link(&sink.as_element().inner) + .link(&sink.upcast_ref().inner) .change_context(Error) .attach("Failed to link elements")?; Ok(Bin::from(bin)) @@ -60,22 +62,22 @@ pub trait Source: IsElement { let bin = gstreamer::Bin::builder() .name(format!( "{}-link-{}", - self.as_element().inner.name(), - sink.as_element().inner.name() + self.upcast_ref().inner.name(), + sink.upcast_ref().inner.name() )) .build(); - bin.add(&self.as_element().inner) + bin.add(&self.upcast_ref().inner) .change_context(Error) .attach("Failed to add source to bin")?; - bin.add(&sink.as_element().inner) + bin.add(&sink.upcast_ref().inner) .change_context(Error) .attach("Failed to add sink to bin")?; - self.as_element() + self.upcast_ref() .inner - .link(&sink.as_element().inner) + .link(&sink.upcast_ref().inner) .change_context(Error) .attach("Failed to link elements")?; - if let Some(sink_pad) = self.as_element().pad("sink") { + if let Some(sink_pad) = self.upcast_ref().pad("sink") { let ghost_pad = Pad::ghost(&sink_pad)?; bin.add_pad(&ghost_pad.inner) .change_context(Error) @@ -89,12 +91,12 @@ pub trait Source: IsElement { // fn link_pad(&self, sink: &S, src_pad_name: &str, sink_pad_name: &str) -> Result<()> { // use gstreamer::prelude::*; // let src_pad = self - // .as_element() + // .upcast_ref() // .pad(src_pad_name) // .ok_or(Error) // .attach("Source pad not found")?; // let sink_pad = sink - // .as_element() + // .upcast_ref() // .pad(sink_pad_name) // .ok_or(Error) // .attach("Sink pad not found")?; diff --git a/gst/src/isa.rs b/gst/src/isa.rs deleted file mode 100644 index 4392bb4..0000000 --- a/gst/src/isa.rs +++ /dev/null @@ -1,19 +0,0 @@ -// use crate::errors::*; -// /// This trait is used for implementing parent traits methods on children. -// pub trait Upcastable { -// #[track_caller] -// fn upcast(self) -> T; -// fn upcast_ref(&self) -> &T; -// } -// -// // impl Upcastable for crate::playback::Playbin3 {} -// impl core::ops::Deref for C -// where -// C: Upcastable

, -// { -// type Target = P; -// -// fn deref(&self) -> &Self::Target { -// todo!() -// } -// } diff --git a/gst/src/lib.rs b/gst/src/lib.rs index 72a92d0..9f7067f 100644 --- a/gst/src/lib.rs +++ b/gst/src/lib.rs @@ -3,10 +3,11 @@ pub mod bus; pub mod caps; pub mod element; pub mod errors; -pub mod isa; pub mod pad; pub mod pipeline; pub mod plugins; +#[macro_use] +pub mod wrapper; pub use bin::*; pub use bus::*; @@ -18,6 +19,7 @@ pub use plugins::*; pub(crate) mod priv_prelude { pub use crate::errors::*; + pub use crate::wrapper::*; pub use crate::*; pub use gstreamer::prelude::*; #[track_caller] diff --git a/gst/src/pad.rs b/gst/src/pad.rs index f947774..91d658a 100644 --- a/gst/src/pad.rs +++ b/gst/src/pad.rs @@ -1,13 +1,35 @@ -use crate::*; -/// Pads are link points between elements +use crate::priv_prelude::*; + +#[derive(Debug)] #[repr(transparent)] pub struct Pad { pub(crate) inner: gstreamer::Pad, } - impl From for Pad { fn from(inner: gstreamer::Pad) -> Self { - Pad { inner } + Self { inner } + } +} +impl From for gstreamer::Pad { + fn from(wrapper: Pad) -> Self { + wrapper.inner + } +} +impl Pad { + pub fn into_inner(self) -> gstreamer::Pad { + self.inner + } +} +impl GstWrapper for Pad { + type GstType = gstreamer::Pad; + fn from_gst(gst: Self::GstType) -> Self { + Self { inner: gst } + } + fn into_gst(self) -> Self::GstType { + self.inner + } + fn as_gst_ref(&self) -> &Self::GstType { + &self.inner } } @@ -30,6 +52,15 @@ impl Pad { Ok(()) } + pub fn current_caps(&self) -> Result { + let caps = self + .inner + .current_caps() + .ok_or(Error) + .attach("Failed to get pad caps")?; + Ok(Caps { inner: caps }) + } + pub fn activate(&self, activate: bool) -> Result<()> { use gstreamer::prelude::*; self.inner diff --git a/gst/src/pipeline.rs b/gst/src/pipeline.rs index 2a88acf..43d9ee1 100644 --- a/gst/src/pipeline.rs +++ b/gst/src/pipeline.rs @@ -1,19 +1,9 @@ use crate::{playback::Playbin3, priv_prelude::*}; use gstreamer::State; -#[repr(transparent)] -pub struct Pipeline { - inner: gstreamer::Pipeline, -} - -impl core::fmt::Debug for Pipeline { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - f.debug_struct("Pipeline") - .field("pipeline", &self.inner) - // .field("state", &self.pipeline.state(gstreamer::ClockTime::NONE)) - .finish() - } -} +wrap_gst!(Pipeline); +parent_child!(Element, Pipeline); +parent_child!(Bin, Pipeline); impl Drop for Pipeline { fn drop(&mut self) { @@ -41,55 +31,56 @@ impl Pipeline { Ok(current) } - pub fn wait_non_null_sync(&self) -> Result<()> { - if self - .state(core::time::Duration::ZERO) - .change_context(Error) - .attach("Failed to get video context")? - != gstreamer::State::Null - { - Ok(()) - } else { - let bus = self.bus()?; - for message in bus.iter_timed(core::time::Duration::from_secs(1)) { - let view = message.view(); - dbg!(&view); - if let gstreamer::MessageView::StateChanged(change) = view - && change.current() != State::Null - { - break; - } - } - Ok(()) - } - } + // pub fn wait_non_null_sync(&self) -> Result<()> { + // if dbg!( + // self.state(core::time::Duration::ZERO) + // .change_context(Error) + // .attach("Failed to get video context") + // )? != gstreamer::State::Null + // { + // Ok(()) + // } else { + // let bus = self.bus()?; + // for message in bus.iter_timed(None) { + // let view = message.view(); + // dbg!(&view); + // panic!(); + // if let gstreamer::MessageView::StateChanged(change) = view + // && change.current() != State::Null + // { + // break; + // } + // } + // Ok(()) + // } + // } - /// Waits for the pipeline to be ready - pub async fn wait_non_null(&self) -> Result<()> { - if self - .state(None) - .change_context(Error) - .attach("Failed to get video context")? - != gstreamer::State::Null - { - Ok(()) - } else { - use futures::StreamExt; - self.bus()? - .stream() - .filter(|message: &gstreamer::Message| { - let view = message.view(); - if let gstreamer::MessageView::StateChanged(change) = view { - core::future::ready(change.current() != gstreamer::State::Null) - } else { - core::future::ready(false) - } - }) - .next() - .await; - Ok(()) - } - } + // Waits for the pipeline to be ready + // pub async fn wait_non_null(&self) -> Result<()> { + // if self + // .state(None) + // .change_context(Error) + // .attach("Failed to get video context")? + // != gstreamer::State::Null + // { + // Ok(()) + // } else { + // use futures::StreamExt; + // self.bus()? + // .stream() + // .filter(|message: &gstreamer::Message| { + // let view = message.view(); + // if let gstreamer::MessageView::StateChanged(change) = view { + // core::future::ready(change.current() != gstreamer::State::Null) + // } else { + // core::future::ready(false) + // } + // }) + // .next() + // .await; + // Ok(()) + // } + // } pub fn play(&self) -> Result<()> { self.inner @@ -111,14 +102,11 @@ impl Pipeline { self.inner .set_state(gstreamer::State::Ready) .change_context(Error) - .attach("Failed to set pipeline to Paused state")?; + .attach("Failed to set pipeline to Ready state")?; Ok(()) } - pub unsafe fn set_state( - &self, - state: gstreamer::State, - ) -> Result { + pub fn set_state(&self, state: gstreamer::State) -> Result { let result = self .inner .set_state(state) @@ -127,15 +115,3 @@ impl Pipeline { Ok(result) } } - -impl core::ops::Deref for Playbin3 { - type Target = Pipeline; - - fn deref(&self) -> &Self::Target { - let gp = self - .inner - .downcast_ref::() - .expect("BUG: Playbin3 must be a pipeline"); - unsafe { &*(gp as *const _ as *const Pipeline) } - } -} diff --git a/gst/src/plugins/app/appsink.rs b/gst/src/plugins/app/appsink.rs index 38e32d1..84c5a02 100644 --- a/gst/src/plugins/app/appsink.rs +++ b/gst/src/plugins/app/appsink.rs @@ -1,19 +1,8 @@ use crate::priv_prelude::*; -#[derive(Debug, Clone)] -pub struct AppSink { - inner: gstreamer::Element, -} - -impl IsElement for AppSink { - fn as_element(&self) -> &Element { - unsafe { core::mem::transmute(&self.inner) } - } - - fn into_element(self) -> Element { - Element { inner: self.inner } - } -} +wrap_gst!(AppSink, gstreamer::Element); +parent_child!(Pipeline, AppSink, downcast); // since AppSink is an Element internaly +parent_child!(Element, AppSink); impl Sink for AppSink {} @@ -136,28 +125,8 @@ fn test_appsink() { .expect("Link videoconvert to appsink"); let playbin3 = playbin3.with_video_sink(&video_sink); + playbin3.play().expect("Play video"); let bus = playbin3.bus().unwrap(); - // playbin3.play().expect("Play playbin3"); - - std::thread::spawn({ - let playbin3 = playbin3.clone(); - move || { - loop { - std::thread::sleep(std::time::Duration::from_secs(5)); - playbin3.play(); - playbin3.wait_non_null_sync(); - let sample = appsink - .try_pull_sample(core::time::Duration::from_secs(5)) - .expect("Pull sample from appsink") - .expect("No sample received from appsink"); - - dbg!(sample.caps()); - - playbin3.play(); - tracing::info!("Played"); - } - } - }); for msg in bus.iter_timed(None) { match msg.view() { gstreamer::MessageView::Eos(..) => { diff --git a/gst/src/plugins/autodetect/autovideosink.rs b/gst/src/plugins/autodetect/autovideosink.rs index 43f1aee..6138fcb 100644 --- a/gst/src/plugins/autodetect/autovideosink.rs +++ b/gst/src/plugins/autodetect/autovideosink.rs @@ -1,19 +1,8 @@ -use crate::*; +use crate::priv_prelude::*; -#[repr(transparent)] -pub struct AutoVideoSink { - inner: gstreamer::Element, -} - -impl IsElement for AutoVideoSink { - fn as_element(&self) -> &Element { - unsafe { core::mem::transmute(&self.inner) } - } - - fn into_element(self) -> Element { - Element { inner: self.inner } - } -} +wrap_gst!(AutoVideoSink, gstreamer::Element); +parent_child!(Element, AutoVideoSink); +parent_child!(Bin, AutoVideoSink, downcast); impl Sink for AutoVideoSink {} diff --git a/gst/src/plugins/playback/playbin3.rs b/gst/src/plugins/playback/playbin3.rs index f003e21..348f7ed 100644 --- a/gst/src/plugins/playback/playbin3.rs +++ b/gst/src/plugins/playback/playbin3.rs @@ -1,19 +1,13 @@ use crate::priv_prelude::*; -#[derive(Debug, Clone)] -#[repr(transparent)] -pub struct Playbin3 { - pub(crate) inner: gstreamer::Element, -} +wrap_gst!(Playbin3, gstreamer::Element); +parent_child!(Element, Playbin3); +parent_child!(Pipeline, Playbin3, downcast); +parent_child!(Bin, Playbin3, downcast); impl Drop for Playbin3 { fn drop(&mut self) { - let _ = self - .inner - .set_state(gstreamer::State::Null) - .inspect_err(|e| { - tracing::error!("Failed to set playbin3 to Null state on drop: {:?}", e) - }); + self.set_state(gstreamer::State::Null).ok(); } } @@ -33,24 +27,24 @@ impl Playbin3 { self } - pub fn with_video_sink(self, video_sink: &impl IsElement) -> Self { + pub fn with_video_sink(self, video_sink: &impl ChildOf) -> Self { use gstreamer::prelude::*; self.inner - .set_property("video-sink", &video_sink.as_element().inner); + .set_property("video-sink", &video_sink.upcast_ref().inner); self } - pub fn with_text_sink(self, text_sink: &impl IsElement) -> Self { + pub fn with_text_sink(self, text_sink: &impl ChildOf) -> Self { use gstreamer::prelude::*; self.inner - .set_property("text-sink", &text_sink.as_element().inner); + .set_property("text-sink", &text_sink.upcast_ref().inner); self } - pub fn with_audio_sink(self, audio_sink: &impl IsElement) -> Self { + pub fn with_audio_sink(self, audio_sink: &impl ChildOf) -> Self { use gstreamer::prelude::*; self.inner - .set_property("audio-sink", &audio_sink.as_element().inner); + .set_property("audio-sink", &audio_sink.upcast_ref().inner); self } @@ -63,93 +57,16 @@ impl Playbin3 { use gstreamer::prelude::*; self.inner.property::("volume") } - - // pub fn play(&self) -> Result<()> { - // use gstreamer::prelude::*; - // self.inner - // .set_state(gstreamer::State::Playing) - // .change_context(Error) - // .attach("Failed to set playbin3 to Playing state")?; - // Ok(()) - // } - // - // pub fn pause(&self) -> Result<()> { - // use gstreamer::prelude::*; - // self.inner - // .set_state(gstreamer::State::Paused) - // .change_context(Error) - // .attach("Failed to set playbin3 to Paused state")?; - // Ok(()) - // } - // - // pub fn bus(&self) -> Result { - // let bus = self - // .inner - // .bus() - // .ok_or(Error) - // .attach("Failed to get bus from playbin3")?; - // Ok(Bus { bus }) - // } } -// #[test] -// fn test_playbin3() { -// use gstreamer::prelude::*; -// use tracing_subscriber::prelude::*; -// tracing_subscriber::registry() -// .with( -// tracing_subscriber::fmt::layer() -// .with_thread_ids(true) -// .with_file(true), -// ) -// .init(); -// tracing::info!("Linking videoconvert to appsink"); -// Gst::new(); -// let playbin3 = Playbin3::new("pppppppppppppppppppppppppppppp").unwrap().with_uri("https://jellyfin.tsuba.darksailor.dev/Items/6010382cf25273e624d305907010d773/Download?api_key=036c140222464878862231ef66a2bc9c"); -// -// let video_convert = plugins::videoconvertscale::VideoConvert::new("vcvcvcvcvcvcvcvcvcvcvcvcvc") -// .expect("Create videoconvert"); -// let appsink = -// autodetect::AutoVideoSink::new("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").expect("Create appsink"); -// -// let mut video_sink = video_convert -// .link(&appsink) -// .expect("Link videoconvert to appsink"); -// -// let playbin3 = playbin3.with_video_sink(&video_sink); -// let bus = playbin3.bus().unwrap(); -// playbin3.play().expect("Play playbin3"); -// std::thread::spawn(move || loop { -// std::thread::sleep(std::time::Duration::from_secs(15)); -// playbin3.play(); -// tracing::info!("Played"); -// }); -// for msg in bus.iter_timed(None) { -// match msg.view() { -// gstreamer::MessageView::Eos(..) => { -// tracing::info!("End of stream reached"); -// break; -// } -// gstreamer::MessageView::Error(err) => { -// tracing::error!( -// "Error from {:?}: {} ({:?})", -// err.src().map(|s| s.path_string()), -// err.error(), -// err.debug() -// ); -// break; -// } -// gstreamer::MessageView::StateChanged(state) => { -// eprintln!( -// "State changed from {:?} to \x1b[33m{:?}\x1b[0m for {:?}", -// state.old(), -// state.current(), -// state.src().map(|s| s.path_string()) -// ); -// } -// _ => {} -// } -// // tracing::info!("{:#?}", &msg.view()); -// } -// // std::thread::sleep(std::time::Duration::from_secs(5)); -// } +impl core::ops::Deref for Playbin3 { + type Target = Pipeline; + + fn deref(&self) -> &Self::Target { + let gp = self + .inner + .downcast_ref::() + .expect("BUG: Playbin3 must be a pipeline"); + unsafe { &*(gp as *const _ as *const Pipeline) } + } +} diff --git a/gst/src/plugins/videoconvertscale/videoconvert.rs b/gst/src/plugins/videoconvertscale/videoconvert.rs index 1bf578b..b986e3b 100644 --- a/gst/src/plugins/videoconvertscale/videoconvert.rs +++ b/gst/src/plugins/videoconvertscale/videoconvert.rs @@ -1,22 +1,9 @@ -use crate::*; +use crate::priv_prelude::*; #[doc(inline)] pub use gstreamer_video::VideoFormat; -#[repr(transparent)] -#[derive(Debug, Clone)] -pub struct VideoConvert { - inner: gstreamer::Element, -} - -impl IsElement for VideoConvert { - fn as_element(&self) -> &Element { - unsafe { core::mem::transmute(&self.inner) } - } - - fn into_element(self) -> Element { - Element { inner: self.inner } - } -} +wrap_gst!(VideoConvert, gstreamer::Element); +parent_child!(Element, VideoConvert); impl Sink for VideoConvert {} impl Source for VideoConvert {} diff --git a/gst/src/wrapper.rs b/gst/src/wrapper.rs new file mode 100644 index 0000000..6228767 --- /dev/null +++ b/gst/src/wrapper.rs @@ -0,0 +1,97 @@ +pub trait GstWrapper { + type GstType: glib::prelude::ObjectType; + fn from_gst(gst: Self::GstType) -> Self; + fn into_gst(self) -> Self::GstType; + fn as_gst_ref(&self) -> &Self::GstType; +} + +#[macro_export] +macro_rules! wrap_gst { + ($name:ident) => { + wrap_gst!($name, gstreamer::$name); + }; + ($name:ident, $inner:ty) => { + #[derive(Debug, Clone)] + #[repr(transparent)] + pub struct $name { + pub(crate) inner: $inner, + } + + impl From<$inner> for $name { + fn from(inner: $inner) -> Self { + Self { inner } + } + } + impl From<$name> for $inner { + fn from(wrapper: $name) -> Self { + wrapper.into_inner() + } + } + + impl $name { + pub fn into_inner(self) -> $inner { + self.inner.clone() + } + } + + impl $crate::wrapper::GstWrapper for $name { + type GstType = $inner; + + fn from_gst(gst: Self::GstType) -> Self { + Self { inner: gst } + } + + fn into_gst(self) -> Self::GstType { + self.inner.clone() + } + + fn as_gst_ref(&self) -> &Self::GstType { + &self.inner + } + } + + impl ChildOf<$name> for $name { + fn upcast_ref(&self) -> &$name { + self + } + } + }; +} + +/// A trait for types that can be upcasted to type T. +pub trait ChildOf { + fn upcast_ref(&self) -> &T; +} + +#[macro_export] +macro_rules! parent_child { + ($parent:ty, $child:ty) => { + impl ChildOf<$parent> for $child + where + $child: GstWrapper, + $parent: GstWrapper, + { + fn upcast_ref(&self) -> &$parent { + let upcasted = self.inner.upcast_ref::<<$parent as GstWrapper>::GstType>(); + unsafe { &*(upcasted as *const <$parent as GstWrapper>::GstType as *const $parent) } + } + } + }; + ($parent:ty, $child:ty, downcast) => { + impl ChildOf<$parent> for $child + where + $child: GstWrapper, + $parent: GstWrapper, + { + fn upcast_ref(&self) -> &$parent { + let downcasted = self + .inner + .downcast_ref::<<$parent as GstWrapper>::GstType>() + .expect("BUG: Failed to downcast GStreamer type from child to parent"); + unsafe { + &*(downcasted as *const <$parent as GstWrapper>::GstType as *const $parent) + } + } + } + }; +}