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: <https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/-/merge_requests/1577>
This commit is contained in:
Sebastian Dröge 2024-10-26 12:22:16 +03:00 committed by GStreamer Marge Bot
parent 0b1be11789
commit 75095b03ec
3 changed files with 353 additions and 73 deletions

View file

@ -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;
}

View file

@ -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

View file

@ -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<gst::ClockTime> },
PositionUpdated(&'a PositionUpdated),
#[doc(alias = "GST_PLAY_MESSAGE_DURATION_CHANGED")]
DurationChanged { duration: Option<gst::ClockTime> },
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<gst::Structure>,
},
Error(&'a Error),
#[doc(alias = "GST_PLAY_MESSAGE_WARNING")]
Warning {
error: glib::Error,
details: Option<gst::Structure>,
},
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 = gst::MessageRef>(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<gst::Message>;
#[inline]
fn to_owned(&self) -> Self::Owned {
$name::<gst::Message>(self.copy())
}
}
impl std::ops::Deref for $name<gst::Message> {
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<gst::Message> {
#[inline]
fn borrow(&self) -> &$name {
&*self
}
}
impl From<$name<gst::Message>> for gst::Message {
#[inline]
fn from(concrete: $name<gst::Message>) -> 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<gst::ClockTime> {
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<gst::ClockTime> {
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<gst::ClockTime> {
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<Self, glib::error::BoolError> {
#[doc(alias = "gst_play_message_parse_seek_done")]
pub fn parse(msg: &gst::Message) -> Result<PlayMessage, glib::error::BoolError> {
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")),
}
}
}