From 75095b03ec80df7ab1331f273b250b3b2c828d8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Sat, 26 Oct 2024 12:22:16 +0300 Subject: [PATCH] play: Improve API of PlayMessage This is closer to the gst::Message API now, exposes all fields of the different message types and also allows for extensions with more fields later without breaking API. Because of https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/7754 the fields are read directly from the structure instead of going via the parsing functions. Part-of: --- examples/src/bin/play.rs | 6 +- gstreamer-play/src/lib.rs | 3 +- gstreamer-play/src/play_message.rs | 417 ++++++++++++++++++++++++----- 3 files changed, 353 insertions(+), 73 deletions(-) diff --git a/examples/src/bin/play.rs b/examples/src/bin/play.rs index cc12e3421..af5a4649c 100644 --- a/examples/src/bin/play.rs +++ b/examples/src/bin/play.rs @@ -24,12 +24,12 @@ fn main_loop(uri: &str) -> Result<(), Error> { let mut result = Ok(()); for msg in play.message_bus().iter_timed(gst::ClockTime::NONE) { match PlayMessage::parse(&msg) { - Ok(PlayMessage::EndOfStream) => { + Ok(PlayMessage::EndOfStream(_)) => { play.stop(); break; } - Ok(PlayMessage::Error { error, details: _ }) => { - result = Err(error); + Ok(PlayMessage::Error(msg)) => { + result = Err(msg.error().clone()); play.stop(); break; } diff --git a/gstreamer-play/src/lib.rs b/gstreamer-play/src/lib.rs index f1e7c2ae5..7c136959d 100644 --- a/gstreamer-play/src/lib.rs +++ b/gstreamer-play/src/lib.rs @@ -21,6 +21,7 @@ macro_rules! assert_initialized_main_thread { } #[allow(clippy::needless_borrow)] +#[allow(unused)] mod auto; pub(crate) use crate::auto::PlayMessage as PlayMessageType; pub use crate::auto::*; @@ -37,7 +38,7 @@ mod play_signal_adapter; mod play_video_overlay_video_renderer; mod play_visualization; -mod play_message; +pub mod play_message; pub use crate::play_message::PlayMessage; // Re-export all the traits in a prelude module, so that applications diff --git a/gstreamer-play/src/play_message.rs b/gstreamer-play/src/play_message.rs index 69416c5e7..3b547df2e 100644 --- a/gstreamer-play/src/play_message.rs +++ b/gstreamer-play/src/play_message.rs @@ -1,47 +1,351 @@ -use crate::{PlayMediaInfo, PlayMessageType, PlayState}; +use crate::{Play, PlayMediaInfo, PlayMessageType, PlayState}; -#[derive(Clone, PartialEq, Debug)] +#[derive(Debug)] #[non_exhaustive] #[doc(alias = "GstPlayMessage")] -pub enum PlayMessage { +pub enum PlayMessage<'a> { #[doc(alias = "GST_PLAY_MESSAGE_URI_LOADED")] - UriLoaded, + UriLoaded(&'a UriLoaded), #[doc(alias = "GST_PLAY_MESSAGE_POSITION_UPDATED")] - PositionUpdated { position: Option }, + PositionUpdated(&'a PositionUpdated), #[doc(alias = "GST_PLAY_MESSAGE_DURATION_CHANGED")] - DurationChanged { duration: Option }, + DurationChanged(&'a DurationChanged), #[doc(alias = "GST_PLAY_MESSAGE_STATE_CHANGED")] - StateChanged { state: PlayState }, + StateChanged(&'a StateChanged), #[doc(alias = "GST_PLAY_MESSAGE_BUFFERING")] - Buffering { percent: u32 }, + Buffering(&'a Buffering), #[doc(alias = "GST_PLAY_MESSAGE_END_OF_STREAM")] - EndOfStream, + EndOfStream(&'a EndOfStream), #[doc(alias = "GST_PLAY_MESSAGE_ERROR")] - Error { - error: glib::Error, - details: Option, - }, + Error(&'a Error), #[doc(alias = "GST_PLAY_MESSAGE_WARNING")] - Warning { - error: glib::Error, - details: Option, - }, + Warning(&'a Warning), #[doc(alias = "GST_PLAY_MESSAGE_VIDEO_DIMENSIONS_CHANGED")] - VideoDimensionsChanged { width: u32, height: u32 }, + VideoDimensionsChanged(&'a VideoDimensionsChanged), #[doc(alias = "GST_PLAY_MESSAGE_MEDIA_INFO_UPDATED")] - MediaInfoUpdated { info: PlayMediaInfo }, + MediaInfoUpdated(&'a MediaInfoUpdated), #[doc(alias = "GST_PLAY_MESSAGE_VOLUME_CHANGED")] - VolumeChanged { volume: f64 }, + VolumeChanged(&'a VolumeChanged), #[doc(alias = "GST_PLAY_MESSAGE_MUTE_CHANGED")] - MuteChanged { muted: bool }, + MuteChanged(&'a MuteChanged), #[doc(alias = "GST_PLAY_MESSAGE_SEEK_DONE")] - SeekDone, + SeekDone(&'a SeekDone), + Other(&'a Other), } -impl PlayMessage { +macro_rules! declare_concrete_message( + ($name:ident) => { + #[repr(transparent)] + pub struct $name(T); + + impl $name { + #[inline] + pub fn message(&self) -> &gst::MessageRef { + unsafe { &*(self as *const Self as *const gst::MessageRef) } + } + + #[inline] + unsafe fn view(message: &gst::MessageRef) -> PlayMessage<'_> { + let message = &*(message as *const gst::MessageRef as *const Self); + PlayMessage::$name(message) + } + } + + impl std::ops::Deref for $name { + type Target = gst::MessageRef; + + #[inline] + fn deref(&self) -> &Self::Target { + unsafe { + &*(self as *const Self as *const Self::Target) + } + } + } + + impl ToOwned for $name { + type Owned = $name; + + #[inline] + fn to_owned(&self) -> Self::Owned { + $name::(self.copy()) + } + } + + impl std::ops::Deref for $name { + type Target = $name; + + #[inline] + fn deref(&self) -> &Self::Target { + unsafe { &*(self.0.as_ptr() as *const Self::Target) } + } + } + + impl std::borrow::Borrow<$name> for $name { + #[inline] + fn borrow(&self) -> &$name { + &*self + } + } + + impl From<$name> for gst::Message { + #[inline] + fn from(concrete: $name) -> Self { + skip_assert_initialized!(); + concrete.0 + } + } + } +); + +declare_concrete_message!(UriLoaded); +impl UriLoaded { + pub fn uri(&self) -> &glib::GStr { + self.message().structure().unwrap().get("uri").unwrap() + } +} +impl std::fmt::Debug for UriLoaded { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("UriLoaded") + .field("structure", &self.message().structure()) + .field("uri", &self.uri()) + .finish() + } +} + +declare_concrete_message!(PositionUpdated); +impl PositionUpdated { + pub fn position(&self) -> Option { + self.message().structure().unwrap().get("position").unwrap() + } +} +impl std::fmt::Debug for PositionUpdated { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("PositionUpdated") + .field("structure", &self.message().structure()) + .field("position", &self.position()) + .finish() + } +} + +declare_concrete_message!(DurationChanged); +impl DurationChanged { + pub fn duration(&self) -> Option { + self.message().structure().unwrap().get("duration").unwrap() + } +} +impl std::fmt::Debug for DurationChanged { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("DurationChanged") + .field("structure", &self.message().structure()) + .field("duration", &self.duration()) + .finish() + } +} + +declare_concrete_message!(StateChanged); +impl StateChanged { + pub fn state(&self) -> PlayState { + self.message() + .structure() + .unwrap() + .get("play-state") + .unwrap() + } +} +impl std::fmt::Debug for StateChanged { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("StateChanged") + .field("structure", &self.message().structure()) + .field("state", &self.state()) + .finish() + } +} + +declare_concrete_message!(Buffering); +impl Buffering { + pub fn percent(&self) -> u32 { + self.message() + .structure() + .unwrap() + // Typo in the library + .get("bufferring-percent") + .unwrap() + } +} +impl std::fmt::Debug for Buffering { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Buffering") + .field("structure", &self.message().structure()) + .field("percent", &self.percent()) + .finish() + } +} + +declare_concrete_message!(EndOfStream); +impl std::fmt::Debug for EndOfStream { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("EndOfStream") + .field("structure", &self.message().structure()) + .finish() + } +} + +declare_concrete_message!(Error); +impl Error { + pub fn error(&self) -> &glib::Error { + self.message().structure().unwrap().get("error").unwrap() + } + + pub fn details(&self) -> Option<&gst::StructureRef> { + self.message() + .structure() + .unwrap() + .get_optional("error-details") + .unwrap() + } +} +impl std::fmt::Debug for Error { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Error") + .field("structure", &self.message().structure()) + .field("error", &self.error()) + .field("details", &self.details()) + .finish() + } +} + +declare_concrete_message!(Warning); +impl Warning { + pub fn error(&self) -> &glib::Error { + self.message().structure().unwrap().get("warning").unwrap() + } + + pub fn details(&self) -> Option<&gst::StructureRef> { + self.message() + .structure() + .unwrap() + .get_optional("warning-details") + .unwrap() + } +} +impl std::fmt::Debug for Warning { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Warning") + .field("structure", &self.message().structure()) + .field("error", &self.error()) + .field("details", &self.details()) + .finish() + } +} + +declare_concrete_message!(VideoDimensionsChanged); +impl VideoDimensionsChanged { + pub fn width(&self) -> u32 { + self.message() + .structure() + .unwrap() + .get("video-width") + .unwrap() + } + + pub fn height(&self) -> u32 { + self.message() + .structure() + .unwrap() + .get("video-height") + .unwrap() + } +} +impl std::fmt::Debug for VideoDimensionsChanged { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("VideoDimensionsChanged") + .field("structure", &self.message().structure()) + .field("width", &self.width()) + .field("height", &self.height()) + .finish() + } +} + +declare_concrete_message!(MediaInfoUpdated); +impl MediaInfoUpdated { + pub fn media_info(&self) -> &PlayMediaInfo { + self.message() + .structure() + .unwrap() + .get("media-info") + .unwrap() + } +} +impl std::fmt::Debug for MediaInfoUpdated { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("MediaInfoUpdated") + .field("structure", &self.message().structure()) + .field("media_info", &self.media_info()) + .finish() + } +} + +declare_concrete_message!(VolumeChanged); +impl VolumeChanged { + pub fn volume(&self) -> f64 { + self.message().structure().unwrap().get("volume").unwrap() + } +} +impl std::fmt::Debug for VolumeChanged { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("VolumeChanged") + .field("structure", &self.message().structure()) + .field("volume", &self.volume()) + .finish() + } +} + +declare_concrete_message!(MuteChanged); +impl MuteChanged { + pub fn is_muted(&self) -> bool { + self.message().structure().unwrap().get("is-muted").unwrap() + } +} +impl std::fmt::Debug for MuteChanged { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("MuteChanged") + .field("structure", &self.message().structure()) + .field("is_muted", &self.is_muted()) + .finish() + } +} + +declare_concrete_message!(SeekDone); +impl SeekDone { + pub fn position(&self) -> Option { + self.message().structure().unwrap().get("position").unwrap() + } +} +impl std::fmt::Debug for SeekDone { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("SeekDone") + .field("structure", &self.message().structure()) + .field("position", &self.position()) + .finish() + } +} + +declare_concrete_message!(Other); +impl std::fmt::Debug for Other { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Other") + .field("structure", &self.message().structure()) + .finish() + } +} + +impl<'a> PlayMessage<'a> { + #[doc(alias = "gst_play_message_parse_uri_loaded")] #[doc(alias = "gst_play_message_parse_position_updated")] #[doc(alias = "gst_play_message_parse_duration_updated")] + #[doc(alias = "gst_play_message_parse_duration_changed")] #[doc(alias = "gst_play_message_parse_state_changed")] + #[doc(alias = "gst_play_message_parse_buffering")] #[doc(alias = "gst_play_message_parse_buffering_percent")] #[doc(alias = "gst_play_message_parse_error")] #[doc(alias = "gst_play_message_parse_warning")] @@ -49,56 +353,31 @@ impl PlayMessage { #[doc(alias = "gst_play_message_parse_media_info_updated")] #[doc(alias = "gst_play_message_parse_muted_changed")] #[doc(alias = "gst_play_message_parse_volume_changed")] - pub fn parse(msg: &gst::Message) -> Result { + #[doc(alias = "gst_play_message_parse_seek_done")] + pub fn parse(msg: &gst::Message) -> Result { skip_assert_initialized!(); - if msg.type_() != gst::MessageType::Application { + + if !Play::is_play_message(msg) { return Err(glib::bool_error!("Invalid play message")); } - match PlayMessageType::parse_type(msg) { - PlayMessageType::UriLoaded => Ok(Self::UriLoaded), - PlayMessageType::PositionUpdated => { - let position = PlayMessageType::parse_position_updated(msg); - Ok(Self::PositionUpdated { position }) + + unsafe { + match PlayMessageType::parse_type(msg) { + PlayMessageType::UriLoaded => Ok(UriLoaded::view(msg)), + PlayMessageType::PositionUpdated => Ok(PositionUpdated::view(msg)), + PlayMessageType::DurationChanged => Ok(DurationChanged::view(msg)), + PlayMessageType::StateChanged => Ok(StateChanged::view(msg)), + PlayMessageType::Buffering => Ok(Buffering::view(msg)), + PlayMessageType::EndOfStream => Ok(EndOfStream::view(msg)), + PlayMessageType::Error => Ok(Error::view(msg)), + PlayMessageType::Warning => Ok(Warning::view(msg)), + PlayMessageType::VideoDimensionsChanged => Ok(VideoDimensionsChanged::view(msg)), + PlayMessageType::MediaInfoUpdated => Ok(MediaInfoUpdated::view(msg)), + PlayMessageType::VolumeChanged => Ok(VolumeChanged::view(msg)), + PlayMessageType::MuteChanged => Ok(MuteChanged::view(msg)), + PlayMessageType::SeekDone => Ok(SeekDone::view(msg)), + _ => Ok(Other::view(msg)), } - PlayMessageType::DurationChanged => { - let duration = PlayMessageType::parse_duration_updated(msg); - Ok(Self::DurationChanged { duration }) - } - PlayMessageType::StateChanged => { - let state = PlayMessageType::parse_state_changed(msg); - Ok(Self::StateChanged { state }) - } - PlayMessageType::Buffering => { - let percent = PlayMessageType::parse_buffering_percent(msg); - Ok(Self::Buffering { percent }) - } - PlayMessageType::EndOfStream => Ok(Self::EndOfStream), - PlayMessageType::Error => { - let (error, details) = PlayMessageType::parse_error(msg); - Ok(Self::Error { error, details }) - } - PlayMessageType::Warning => { - let (error, details) = PlayMessageType::parse_warning(msg); - Ok(Self::Warning { error, details }) - } - PlayMessageType::VideoDimensionsChanged => { - let (width, height) = PlayMessageType::parse_video_dimensions_changed(msg); - Ok(Self::VideoDimensionsChanged { width, height }) - } - PlayMessageType::MediaInfoUpdated => { - let info = PlayMessageType::parse_media_info_updated(msg); - Ok(Self::MediaInfoUpdated { info }) - } - PlayMessageType::VolumeChanged => { - let volume = PlayMessageType::parse_volume_changed(msg); - Ok(Self::VolumeChanged { volume }) - } - PlayMessageType::MuteChanged => { - let muted = PlayMessageType::parse_muted_changed(msg); - Ok(Self::MuteChanged { muted }) - } - PlayMessageType::SeekDone => Ok(Self::SeekDone), - _ => Err(glib::bool_error!("Invalid play message")), } } }