diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 5f8c7adde..a4894b8a9 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -221,11 +221,14 @@ plugins-update-nightly: - | get_features() { crate=$1 - if [ "$crate" = "gstreamer" ]; then - echo "--features=ser_de,v1_22" - else - echo "--features=v1_22" - fi + case "$crate" in + gstreamer-audio|gstreamer-editing-services|gstreamer-gl|gstreamer-pbutils|gstreamer-rtp|gstreamer-rtsp|gstreamer-video|gstreamer) + echo "--features=ser_de,v1_22" + ;; + *) + echo "--features=v1_22" + ;; + esac } for crate in gstreamer* gstreamer-gl/{egl,wayland,x11}; do if [ -e $crate/Cargo.toml ]; then @@ -368,11 +371,14 @@ clippy: - | get_features() { crate=$1 - if [ "$crate" = "gstreamer" ]; then - echo "--features=ser_de,v1_22" - else - echo "--features=v1_22" - fi + case "$crate" in + gstreamer-audio|gstreamer-editing-services|gstreamer-gl|gstreamer-pbutils|gstreamer-rtp|gstreamer-rtsp|gstreamer-video|gstreamer) + echo "--features=ser_de,v1_22" + ;; + *) + echo "--features=v1_22" + ;; + esac } for crate in gstreamer* gstreamer-gl/{egl,wayland,x11}; do if [ -e $crate/Cargo.toml ]; then diff --git a/gstreamer-audio/Cargo.toml b/gstreamer-audio/Cargo.toml index 99a6c5612..1f8e5db24 100644 --- a/gstreamer-audio/Cargo.toml +++ b/gstreamer-audio/Cargo.toml @@ -23,9 +23,11 @@ gst = { package = "gstreamer", path = "../gstreamer" } gst-base = { package = "gstreamer-base", path = "../gstreamer-base" } array-init = "2.0" once_cell = "1.0" +serde = { version = "1.0", optional = true } [dev-dependencies] itertools = "0.10" +serde_json = "1.0" gir-format-check = "0.1" [features] @@ -34,7 +36,8 @@ v1_16 = ["gst/v1_16", "gst-base/v1_16", "ffi/v1_16"] v1_18 = ["gst/v1_18", "gst-base/v1_18", "ffi/v1_18", "v1_16"] v1_20 = ["gst/v1_20", "gst-base/v1_20", "ffi/v1_20", "v1_18"] v1_22 = ["gst/v1_22", "gst-base/v1_22", "ffi/v1_22", "v1_20"] -dox = ["v1_22", "ffi/dox", "glib/dox", "gst/dox", "gst-base/dox"] +dox = ["v1_22", "ffi/dox", "glib/dox", "gst/dox", "gst-base/dox", "ser_de"] +ser_de = ["serde", "gst/ser_de"] [package.metadata.docs.rs] features = ["dox"] diff --git a/gstreamer-audio/src/flag_serde.rs b/gstreamer-audio/src/flag_serde.rs new file mode 100644 index 000000000..8500d43d8 --- /dev/null +++ b/gstreamer-audio/src/flag_serde.rs @@ -0,0 +1,76 @@ +// Take a look at the license at the top of the repository in the LICENSE file. + +use glib::translate::{from_glib, ToGlibPtr}; +use glib::{FlagsClass, StaticType, ToValue}; +use gst::bitflags_serde_impl; + +bitflags_serde_impl!(crate::AudioFlags); +bitflags_serde_impl!(crate::AudioFormatFlags); +bitflags_serde_impl!(crate::AudioPackFlags); + +#[cfg(test)] +mod tests { + macro_rules! check_serialize { + ($flags:expr, $expected:expr) => { + let actual = serde_json::to_string(&$flags).unwrap(); + assert_eq!(actual, $expected); + }; + } + + macro_rules! check_deserialize { + ($ty:ty, $expected:expr, $json:expr) => { + let actual: $ty = serde_json::from_str(&$json).unwrap(); + assert_eq!(actual, $expected); + }; + } + + macro_rules! check_roundtrip { + ($ty:ty, $flags:expr) => { + let json = serde_json::to_string(&$flags).unwrap(); + let deserialized: $ty = serde_json::from_str(&json).unwrap(); + assert_eq!(deserialized, $flags); + }; + } + + #[test] + fn test_serialize() { + gst::init().unwrap(); + + check_serialize!(crate::AudioFlags::all(), "\"unpositioned\""); + check_serialize!( + crate::AudioFormatFlags::all(), + "\"integer+float+signed+complex+unpack\"" + ); + check_serialize!(crate::AudioPackFlags::all(), "\"truncate-range\""); + } + + #[test] + fn test_deserialize() { + gst::init().unwrap(); + + check_deserialize!( + crate::AudioFlags, + crate::AudioFlags::all(), + "\"unpositioned\"" + ); + check_deserialize!( + crate::AudioFormatFlags, + crate::AudioFormatFlags::all(), + "\"integer+float+signed+complex+unpack\"" + ); + check_deserialize!( + crate::AudioPackFlags, + crate::AudioPackFlags::all(), + "\"truncate-range\"" + ); + } + + #[test] + fn test_serde_roundtrip() { + gst::init().unwrap(); + + check_roundtrip!(crate::AudioFlags, crate::AudioFlags::all()); + check_roundtrip!(crate::AudioFormatFlags, crate::AudioFormatFlags::all()); + check_roundtrip!(crate::AudioPackFlags, crate::AudioPackFlags::all()); + } +} diff --git a/gstreamer-audio/src/lib.rs b/gstreamer-audio/src/lib.rs index 3f77d9c4f..357d5b016 100644 --- a/gstreamer-audio/src/lib.rs +++ b/gstreamer-audio/src/lib.rs @@ -31,6 +31,9 @@ macro_rules! skip_assert_initialized { mod auto; pub use crate::auto::*; +#[cfg(feature = "ser_de")] +mod flag_serde; + mod audio_format; pub use crate::audio_format::*; mod audio_format_info; diff --git a/gstreamer-editing-services/Cargo.toml b/gstreamer-editing-services/Cargo.toml index 9d5006dcb..8d89694b7 100644 --- a/gstreamer-editing-services/Cargo.toml +++ b/gstreamer-editing-services/Cargo.toml @@ -22,16 +22,19 @@ gio = { git = "https://github.com/gtk-rs/gtk-rs-core" } gst = { package = "gstreamer", path = "../gstreamer" } gst-base = { package = "gstreamer-base", path = "../gstreamer-base" } gst-pbutils = { package = "gstreamer-pbutils", path = "../gstreamer-pbutils" } +serde = { version = "1.0", optional = true } [dev-dependencies] gir-format-check = "0.1" +serde_json = "1.0" [features] v1_16 = ["gst/v1_16", "gst-base/v1_16", "gst-pbutils/v1_16", "ffi/v1_16"] v1_18 = ["gst/v1_18", "gst-base/v1_18", "gst-pbutils/v1_18", "ffi/v1_18", "v1_16"] v1_20 = ["gst/v1_20", "gst-base/v1_20", "gst-pbutils/v1_20", "ffi/v1_20", "v1_18"] v1_22 = ["gst/v1_22", "gst-base/v1_22", "gst-pbutils/v1_22", "ffi/v1_22", "v1_20"] -dox = ["v1_22", "ffi/dox", "glib/dox", "gio/dox", "gst/dox", "gst-base/dox", "gst-pbutils/dox"] +dox = ["v1_22", "ffi/dox", "glib/dox", "gio/dox", "gst/dox", "gst-base/dox", "gst-pbutils/dox", "ser_de"] +ser_de = ["serde", "gst/ser_de", "gst-pbutils/ser_de"] [package.metadata.docs.rs] features = ["dox"] diff --git a/gstreamer-editing-services/src/flag_serde.rs b/gstreamer-editing-services/src/flag_serde.rs new file mode 100644 index 000000000..a49a65982 --- /dev/null +++ b/gstreamer-editing-services/src/flag_serde.rs @@ -0,0 +1,90 @@ +// Take a look at the license at the top of the repository in the LICENSE file. + +use glib::translate::{from_glib, ToGlibPtr}; +use glib::{FlagsClass, StaticType, ToValue}; +use gst::bitflags_serde_impl; + +bitflags_serde_impl!(crate::MarkerFlags, "v1_20"); +bitflags_serde_impl!(crate::MetaFlag); +bitflags_serde_impl!(crate::PipelineFlags); +bitflags_serde_impl!(crate::TrackType); + +#[cfg(test)] +mod tests { + macro_rules! check_serialize { + ($flags:expr, $expected:expr) => { + let actual = serde_json::to_string(&$flags).unwrap(); + assert_eq!(actual, $expected); + }; + } + + macro_rules! check_deserialize { + ($ty:ty, $expected:expr, $json:expr) => { + let actual: $ty = serde_json::from_str(&$json).unwrap(); + assert_eq!(actual, $expected); + }; + } + + macro_rules! check_roundtrip { + ($ty:ty, $flags:expr) => { + let json = serde_json::to_string(&$flags).unwrap(); + let deserialized: $ty = serde_json::from_str(&json).unwrap(); + assert_eq!(deserialized, $flags); + }; + } + + #[test] + fn test_serialize() { + gst::init().unwrap(); + + #[cfg(feature = "v1_20")] + check_serialize!(crate::MarkerFlags::all(), "\"snappable\""); + check_serialize!(crate::MetaFlag::all(), "\"readable+writable\""); + check_serialize!( + crate::PipelineFlags::all(), + "\"audio_preview+video_preview+render+smart_render\"" + ); + check_serialize!( + crate::TrackType::all(), + "\"unknown+audio+video+text+custom\"" + ); + } + + #[test] + fn test_deserialize() { + gst::init().unwrap(); + + #[cfg(feature = "v1_20")] + check_deserialize!( + crate::MarkerFlags, + crate::MarkerFlags::all(), + "\"snappable\"" + ); + check_deserialize!( + crate::MetaFlag, + crate::MetaFlag::all(), + "\"readable+writable\"" + ); + check_deserialize!( + crate::PipelineFlags, + crate::PipelineFlags::all(), + "\"audio_preview+video_preview+render+smart_render\"" + ); + check_deserialize!( + crate::TrackType, + crate::TrackType::all(), + "\"unknown+audio+video+text+custom\"" + ); + } + + #[test] + fn test_serde_roundtrip() { + gst::init().unwrap(); + + #[cfg(feature = "v1_20")] + check_roundtrip!(crate::MarkerFlags, crate::MarkerFlags::all()); + check_roundtrip!(crate::MetaFlag, crate::MetaFlag::all()); + check_roundtrip!(crate::PipelineFlags, crate::PipelineFlags::all()); + check_roundtrip!(crate::TrackType, crate::TrackType::all()); + } +} diff --git a/gstreamer-editing-services/src/lib.rs b/gstreamer-editing-services/src/lib.rs index 9b18d7bd2..7cc21b047 100644 --- a/gstreamer-editing-services/src/lib.rs +++ b/gstreamer-editing-services/src/lib.rs @@ -62,6 +62,9 @@ macro_rules! skip_assert_initialized { mod auto; pub use crate::auto::*; +#[cfg(feature = "ser_de")] +mod flag_serde; + // Re-export all the traits in a prelude module, so that applications // can always "use ges::prelude::*" without getting conflicts pub mod prelude { diff --git a/gstreamer-gl/Cargo.toml b/gstreamer-gl/Cargo.toml index 925997d97..12cd6c7b4 100644 --- a/gstreamer-gl/Cargo.toml +++ b/gstreamer-gl/Cargo.toml @@ -26,9 +26,11 @@ glib = { git = "https://github.com/gtk-rs/gtk-rs-core" } gst = { package = "gstreamer", path = "../gstreamer" } gst-base = { package = "gstreamer-base", path = "../gstreamer-base" } gst-video = { package = "gstreamer-video", path = "../gstreamer-video" } +serde = { version = "1.0", optional = true } [dev-dependencies] gir-format-check = "0.1" +serde_json = "1.0" [features] default = [] @@ -36,7 +38,8 @@ v1_16 = ["gst/v1_16", "gst-base/v1_16", "gst-video/v1_16", "ffi/v1_16"] v1_18 = ["gst/v1_18", "gst-base/v1_18", "gst-video/v1_18", "ffi/v1_18", "v1_16"] v1_20 = ["gst/v1_20", "gst-base/v1_20", "gst-video/v1_20", "ffi/v1_20", "v1_18"] v1_22 = ["gst/v1_22", "gst-base/v1_22", "gst-video/v1_22", "ffi/v1_22", "v1_20"] -dox = ["v1_22", "ffi/dox", "glib/dox", "gst/dox", "gst-base/dox", "gst-video/dox"] +dox = ["v1_22", "ffi/dox", "glib/dox", "gst/dox", "gst-base/dox", "gst-video/dox", "ser_de"] +ser_de = ["serde", "gst/ser_de", "gst-video/ser_de"] [package.metadata.docs.rs] features = ["dox"] diff --git a/gstreamer-gl/src/flag_serde.rs b/gstreamer-gl/src/flag_serde.rs new file mode 100644 index 000000000..9a72df060 --- /dev/null +++ b/gstreamer-gl/src/flag_serde.rs @@ -0,0 +1,110 @@ +// Take a look at the license at the top of the repository in the LICENSE file. + +use glib::translate::{from_glib, ToGlibPtr}; +use glib::{FlagsClass, StaticType, ToValue}; +use gst::bitflags_serde_impl; + +bitflags_serde_impl!(crate::GLAPI); +bitflags_serde_impl!(crate::GLConfigSurfaceType, "v1_20"); +bitflags_serde_impl!(crate::GLDisplayType); +bitflags_serde_impl!(crate::GLPlatform); +bitflags_serde_impl!(crate::GLSLProfile); + +#[cfg(test)] +mod tests { + macro_rules! check_serialize { + ($flags:expr, $expected:expr) => { + let actual = serde_json::to_string(&$flags).unwrap(); + assert_eq!(actual, $expected); + }; + } + + macro_rules! check_deserialize { + ($ty:ty, $expected:expr, $json:expr) => { + let actual: $ty = serde_json::from_str(&$json).unwrap(); + assert_eq!(actual, $expected); + }; + } + + macro_rules! check_roundtrip { + ($ty:ty, $flags:expr) => { + let json = serde_json::to_string(&$flags).unwrap(); + let deserialized: $ty = serde_json::from_str(&json).unwrap(); + assert_eq!(deserialized, $flags); + }; + } + + #[test] + fn test_serialize() { + gst::init().unwrap(); + + check_serialize!(crate::GLAPI::all(), "\"opengl+opengl3+gles1+gles2\""); + #[cfg(feature = "v1_20")] + check_serialize!( + crate::GLConfigSurfaceType::all(), + "\"window+pbuffer+pixmap\"" + ); + #[cfg(feature = "v1_20")] + check_serialize!( + crate::GLDisplayType::all(), + concat!( + "\"x11+wayland+cocoa+win32+dispmanx+egl+viv-fb+gbm+egl-device", + "+eagl+winrt+android\"" + ) + ); + check_serialize!(crate::GLPlatform::all(), "\"egl+glx+wgl+cgl+eagl\""); + check_serialize!(crate::GLSLProfile::all(), "\"es+core+compatibility\""); + } + + #[test] + fn test_deserialize() { + gst::init().unwrap(); + + check_deserialize!( + crate::GLAPI, + crate::GLAPI::all(), + "\"opengl+opengl3+gles1+gles2\"" + ); + #[cfg(feature = "v1_20")] + check_deserialize!( + crate::GLConfigSurfaceType, + crate::GLConfigSurfaceType::all(), + "\"none+window+pbuffer+pixmap\"" + ); + #[cfg(feature = "v1_20")] + check_deserialize!( + crate::GLDisplayType, + crate::GLDisplayType::all(), + concat!( + "\"x11+wayland+cocoa+win32+dispmanx+egl+viv-fb+gbm+egl-device", + "+eagl+winrt+android\"" + ) + ); + check_deserialize!( + crate::GLPlatform, + crate::GLPlatform::all(), + "\"egl+glx+wgl+cgl+eagl\"" + ); + check_deserialize!( + crate::GLSLProfile, + crate::GLSLProfile::all(), + "\"es+core+compatibility\"" + ); + } + + #[test] + fn test_serde_roundtrip() { + gst::init().unwrap(); + + check_roundtrip!(crate::GLAPI, crate::GLAPI::all()); + #[cfg(feature = "v1_20")] + check_roundtrip!( + crate::GLConfigSurfaceType, + crate::GLConfigSurfaceType::all() + ); + #[cfg(feature = "v1_20")] + check_roundtrip!(crate::GLDisplayType, crate::GLDisplayType::all()); + check_roundtrip!(crate::GLPlatform, crate::GLPlatform::all()); + check_roundtrip!(crate::GLSLProfile, crate::GLSLProfile::all()); + } +} diff --git a/gstreamer-gl/src/lib.rs b/gstreamer-gl/src/lib.rs index 4023867cd..550ea40df 100644 --- a/gstreamer-gl/src/lib.rs +++ b/gstreamer-gl/src/lib.rs @@ -32,6 +32,9 @@ mod auto; pub use crate::auto::functions::*; pub use crate::auto::*; +#[cfg(feature = "ser_de")] +mod flag_serde; + mod caps_features; pub use crate::caps_features::CAPS_FEATURES_MEMORY_GL_MEMORY; mod context; diff --git a/gstreamer-pbutils/Cargo.toml b/gstreamer-pbutils/Cargo.toml index eeb67146f..3fcae2fb2 100644 --- a/gstreamer-pbutils/Cargo.toml +++ b/gstreamer-pbutils/Cargo.toml @@ -20,9 +20,11 @@ ffi = { package = "gstreamer-pbutils-sys", path = "sys" } glib = { git = "https://github.com/gtk-rs/gtk-rs-core" } gst = { package = "gstreamer", path = "../gstreamer" } thiserror = "1.0" +serde = { version = "1.0", optional = true } [dev-dependencies] gir-format-check = "0.1" +serde_json = "1.0" [features] default = [] @@ -30,7 +32,8 @@ v1_16 = ["gst/v1_16", "ffi/v1_16"] v1_18 = ["gst/v1_18", "ffi/v1_18", "v1_16"] v1_20 = ["gst/v1_20", "ffi/v1_20", "v1_18"] v1_22 = ["gst/v1_22", "ffi/v1_22", "v1_20"] -dox = ["v1_22", "ffi/dox", "glib/dox", "gst/dox"] +dox = ["v1_22", "ffi/dox", "glib/dox", "gst/dox", "ser_de"] +ser_de = ["serde", "gst/ser_de"] [package.metadata.docs.rs] features = ["dox"] diff --git a/gstreamer-pbutils/src/flag_serde.rs b/gstreamer-pbutils/src/flag_serde.rs new file mode 100644 index 000000000..7eb33564f --- /dev/null +++ b/gstreamer-pbutils/src/flag_serde.rs @@ -0,0 +1,77 @@ +// Take a look at the license at the top of the repository in the LICENSE file. + +use glib::translate::{from_glib, ToGlibPtr}; +use glib::{FlagsClass, StaticType, ToValue}; +use gst::bitflags_serde_impl; + +bitflags_serde_impl!(crate::DiscovererSerializeFlags); +bitflags_serde_impl!(crate::PbUtilsCapsDescriptionFlags, "v1_20"); + +#[cfg(test)] +mod tests { + macro_rules! check_serialize { + ($flags:expr, $expected:expr) => { + let actual = serde_json::to_string(&$flags).unwrap(); + assert_eq!(actual, $expected); + }; + } + + macro_rules! check_deserialize { + ($ty:ty, $expected:expr, $json:expr) => { + let actual: $ty = serde_json::from_str(&$json).unwrap(); + assert_eq!(actual, $expected); + }; + } + + macro_rules! check_roundtrip { + ($ty:ty, $flags:expr) => { + let json = serde_json::to_string(&$flags).unwrap(); + let deserialized: $ty = serde_json::from_str(&json).unwrap(); + assert_eq!(deserialized, $flags); + }; + } + + #[test] + fn test_serialize() { + gst::init().unwrap(); + + check_serialize!(crate::DiscovererSerializeFlags::all(), "\"caps+tags+misc\""); + #[cfg(feature = "v1_20")] + check_serialize!( + crate::PbUtilsCapsDescriptionFlags::all(), + "\"container+audio+video+image+subtitle+tag+generic\"" + ); + } + + #[test] + fn test_deserialize() { + gst::init().unwrap(); + + check_deserialize!( + crate::DiscovererSerializeFlags, + crate::DiscovererSerializeFlags::all(), + "\"caps+tags+misc\"" + ); + #[cfg(feature = "v1_20")] + check_deserialize!( + crate::PbUtilsCapsDescriptionFlags, + crate::PbUtilsCapsDescriptionFlags::all(), + "\"container+audio+video+image+subtitle+tag+generic\"" + ); + } + + #[test] + fn test_serde_roundtrip() { + gst::init().unwrap(); + + check_roundtrip!( + crate::DiscovererSerializeFlags, + crate::DiscovererSerializeFlags::all() + ); + #[cfg(feature = "v1_20")] + check_roundtrip!( + crate::PbUtilsCapsDescriptionFlags, + crate::PbUtilsCapsDescriptionFlags::all() + ); + } +} diff --git a/gstreamer-pbutils/src/lib.rs b/gstreamer-pbutils/src/lib.rs index 42f6f67dd..245a0163c 100644 --- a/gstreamer-pbutils/src/lib.rs +++ b/gstreamer-pbutils/src/lib.rs @@ -38,6 +38,9 @@ mod auto; pub use crate::auto::functions::*; pub use crate::auto::*; +#[cfg(feature = "ser_de")] +mod flag_serde; + mod discoverer; pub use crate::discoverer::*; diff --git a/gstreamer-rtp/Cargo.toml b/gstreamer-rtp/Cargo.toml index 0af9ce4a9..0aae28123 100644 --- a/gstreamer-rtp/Cargo.toml +++ b/gstreamer-rtp/Cargo.toml @@ -20,9 +20,11 @@ libc = "0.2" ffi = { package = "gstreamer-rtp-sys", path = "sys" } glib = { git = "https://github.com/gtk-rs/gtk-rs-core" } gst = { package = "gstreamer", path = "../gstreamer" } +serde = { version = "1.0", optional = true } [dev-dependencies] gir-format-check = "0.1" +serde_json = "1.0" [features] default = [] @@ -30,7 +32,8 @@ v1_16 = ["gst/v1_16", "ffi/v1_16"] v1_18 = ["gst/v1_18", "ffi/v1_18", "v1_16"] v1_20 = ["gst/v1_20", "ffi/v1_20", "v1_18"] v1_22 = ["gst/v1_22", "ffi/v1_22", "v1_20"] -dox = ["v1_22", "ffi/dox", "glib/dox", "gst/dox"] +dox = ["v1_22", "ffi/dox", "glib/dox", "gst/dox", "ser_de"] +ser_de = ["serde", "gst/ser_de"] [package.metadata.docs.rs] features = ["dox"] diff --git a/gstreamer-rtp/src/flag_serde.rs b/gstreamer-rtp/src/flag_serde.rs new file mode 100644 index 000000000..1aa945474 --- /dev/null +++ b/gstreamer-rtp/src/flag_serde.rs @@ -0,0 +1,114 @@ +// Take a look at the license at the top of the repository in the LICENSE file. + +use glib::translate::{from_glib, ToGlibPtr}; +use glib::{FlagsClass, StaticType, ToValue}; +use gst::{bitflags_deserialize_impl, bitflags_serde_impl, bitflags_serialize_impl}; + +bitflags_serialize_impl!(crate::RTPBufferFlags, by_ones_decreasing); +bitflags_deserialize_impl!(crate::RTPBufferFlags); +bitflags_serde_impl!(crate::RTPBufferMapFlags); +// use this implementation, since serializing to "sendonly+recvonly" +// wouldn't make much sense +bitflags_serialize_impl!( + crate::RTPHeaderExtensionDirection, + by_ones_decreasing, + "v1_20" +); +bitflags_deserialize_impl!(crate::RTPHeaderExtensionDirection, "v1_20"); +bitflags_serde_impl!(crate::RTPHeaderExtensionFlags, "v1_20"); + +#[cfg(test)] +mod tests { + macro_rules! check_serialize { + ($flags:expr, $expected:expr) => { + let actual = serde_json::to_string(&$flags).unwrap(); + assert_eq!(actual, $expected); + }; + } + + macro_rules! check_deserialize { + ($ty:ty, $expected:expr, $json:expr) => { + let actual: $ty = serde_json::from_str(&$json).unwrap(); + assert_eq!(actual, $expected); + }; + } + + macro_rules! check_roundtrip { + ($ty:ty, $flags:expr) => { + let json = serde_json::to_string(&$flags).unwrap(); + let deserialized: $ty = serde_json::from_str(&json).unwrap(); + assert_eq!(deserialized, $flags); + }; + } + + #[test] + fn test_serialize() { + gst::init().unwrap(); + + check_serialize!(crate::RTPBufferFlags::all(), "\"retransmission+redundant\""); + check_serialize!(crate::RTPBufferMapFlags::all(), "\"skip-padding\""); + #[cfg(feature = "v1_20")] + check_serialize!( + crate::RTPHeaderExtensionDirection::all(), + "\"sendrecv+inherited\"" + ); + #[cfg(feature = "v1_20")] + check_serialize!( + crate::RTPHeaderExtensionDirection::INACTIVE + | crate::RTPHeaderExtensionDirection::SENDONLY + | crate::RTPHeaderExtensionDirection::INHERITED, + "\"sendonly+inherited\"" + ); + #[cfg(feature = "v1_20")] + check_serialize!( + crate::RTPHeaderExtensionFlags::all(), + "\"one-byte+two-byte\"" + ); + } + + #[test] + fn test_deserialize() { + gst::init().unwrap(); + + check_deserialize!( + crate::RTPBufferFlags, + crate::RTPBufferFlags::all(), + "\"retransmission+redundant\"" + ); + check_deserialize!( + crate::RTPBufferMapFlags, + crate::RTPBufferMapFlags::all(), + "\"skip-padding\"" + ); + #[cfg(feature = "v1_20")] + check_deserialize!( + crate::RTPHeaderExtensionDirection, + crate::RTPHeaderExtensionDirection::all(), + "\"sendrecv+inactive+inherited\"" + ); + #[cfg(feature = "v1_20")] + check_deserialize!( + crate::RTPHeaderExtensionFlags, + crate::RTPHeaderExtensionFlags::all(), + "\"one-byte+two-byte\"" + ); + } + + #[test] + fn test_serde_roundtrip() { + gst::init().unwrap(); + + check_roundtrip!(crate::RTPBufferFlags, crate::RTPBufferFlags::all()); + check_roundtrip!(crate::RTPBufferMapFlags, crate::RTPBufferMapFlags::all()); + #[cfg(feature = "v1_20")] + check_roundtrip!( + crate::RTPHeaderExtensionDirection, + crate::RTPHeaderExtensionDirection::all() + ); + #[cfg(feature = "v1_20")] + check_roundtrip!( + crate::RTPHeaderExtensionFlags, + crate::RTPHeaderExtensionFlags::all() + ); + } +} diff --git a/gstreamer-rtp/src/lib.rs b/gstreamer-rtp/src/lib.rs index 8f84702c6..a4eb7abcb 100644 --- a/gstreamer-rtp/src/lib.rs +++ b/gstreamer-rtp/src/lib.rs @@ -32,6 +32,9 @@ mod auto; pub use crate::auto::functions::*; pub use crate::auto::*; +#[cfg(feature = "ser_de")] +mod flag_serde; + pub mod subclass; pub mod rtp_buffer; diff --git a/gstreamer-rtsp-server/Cargo.toml b/gstreamer-rtsp-server/Cargo.toml index 0a07b556b..720fbcd0b 100644 --- a/gstreamer-rtsp-server/Cargo.toml +++ b/gstreamer-rtsp-server/Cargo.toml @@ -24,9 +24,11 @@ gst = { package = "gstreamer", path = "../gstreamer" } gst-sdp = { package = "gstreamer-sdp", path = "../gstreamer-sdp" } gst-rtsp = { package = "gstreamer-rtsp", path = "../gstreamer-rtsp" } gst-net = { package = "gstreamer-net", path = "../gstreamer-net" } +serde = { version = "1.0", optional = true } [dev-dependencies] gir-format-check = "0.1" +serde_json = "1.0" [features] default = [] @@ -34,7 +36,8 @@ v1_16 = ["gst/v1_16", "gst-sdp/v1_16", "gst-rtsp/v1_16", "gst-net/v1_16", "ffi/v v1_18 = ["gst/v1_18", "gst-sdp/v1_18", "gst-rtsp/v1_18", "gst-net/v1_18", "ffi/v1_18", "v1_16"] v1_20 = ["gst/v1_20", "gst-sdp/v1_20", "gst-rtsp/v1_20", "gst-net/v1_20", "ffi/v1_20", "v1_18"] v1_22 = ["gst/v1_22", "gst-sdp/v1_22", "gst-rtsp/v1_22", "gst-net/v1_22", "ffi/v1_22", "v1_20"] -dox = ["v1_22", "ffi/dox", "glib/dox", "gio/dox", "gst/dox", "gst-sdp/dox", "gst-rtsp/dox", "gst-net/dox"] +dox = ["v1_22", "ffi/dox", "glib/dox", "gio/dox", "gst/dox", "gst-sdp/dox", "gst-rtsp/dox", "gst-net/dox", "ser_de"] +ser_de = ["serde", "gst/ser_de", "gst-rtsp/ser_de"] [package.metadata.docs.rs] features = ["dox"] diff --git a/gstreamer-rtsp-server/src/flag_serde.rs b/gstreamer-rtsp-server/src/flag_serde.rs new file mode 100644 index 000000000..79f7f9c4f --- /dev/null +++ b/gstreamer-rtsp-server/src/flag_serde.rs @@ -0,0 +1,57 @@ +// Take a look at the license at the top of the repository in the LICENSE file. + +use glib::translate::{from_glib, ToGlibPtr}; +use glib::{FlagsClass, StaticType, ToValue}; +use gst::bitflags_serde_impl; + +bitflags_serde_impl!(crate::RTSPTransportMode); + +#[cfg(test)] +mod tests { + macro_rules! check_serialize { + ($flags:expr, $expected:expr) => { + let actual = serde_json::to_string(&$flags).unwrap(); + assert_eq!(actual, $expected); + }; + } + + macro_rules! check_deserialize { + ($ty:ty, $expected:expr, $json:expr) => { + let actual: $ty = serde_json::from_str(&$json).unwrap(); + assert_eq!(actual, $expected); + }; + } + + macro_rules! check_roundtrip { + ($ty:ty, $flags:expr) => { + let json = serde_json::to_string(&$flags).unwrap(); + let deserialized: $ty = serde_json::from_str(&json).unwrap(); + assert_eq!(deserialized, $flags); + }; + } + + #[test] + fn test_serialize() { + gst::init().unwrap(); + + check_serialize!(crate::RTSPTransportMode::all(), "\"play+record\""); + } + + #[test] + fn test_deserialize() { + gst::init().unwrap(); + + check_deserialize!( + crate::RTSPTransportMode, + crate::RTSPTransportMode::all(), + "\"play+record\"" + ); + } + + #[test] + fn test_serde_roundtrip() { + gst::init().unwrap(); + + check_roundtrip!(crate::RTSPTransportMode, crate::RTSPTransportMode::all()); + } +} diff --git a/gstreamer-rtsp-server/src/lib.rs b/gstreamer-rtsp-server/src/lib.rs index 2c22c81b8..7f1907ba7 100644 --- a/gstreamer-rtsp-server/src/lib.rs +++ b/gstreamer-rtsp-server/src/lib.rs @@ -35,6 +35,9 @@ macro_rules! skip_assert_initialized { mod auto; pub use crate::auto::*; +#[cfg(feature = "ser_de")] +mod flag_serde; + mod rtsp_address_pool; mod rtsp_auth; mod rtsp_client; diff --git a/gstreamer-rtsp/Cargo.toml b/gstreamer-rtsp/Cargo.toml index 66df44709..748ecbb71 100644 --- a/gstreamer-rtsp/Cargo.toml +++ b/gstreamer-rtsp/Cargo.toml @@ -20,9 +20,11 @@ ffi = { package = "gstreamer-rtsp-sys", path = "sys" } glib = { git = "https://github.com/gtk-rs/gtk-rs-core" } gst = { package = "gstreamer", path = "../gstreamer" } gst-sdp = { package = "gstreamer-sdp", path = "../gstreamer-sdp" } +serde = { version = "1.0", optional = true } [dev-dependencies] gir-format-check = "0.1" +serde_json = "1.0" [features] default = [] @@ -30,7 +32,8 @@ v1_16 = ["gst/v1_16", "gst-sdp/v1_16", "ffi/v1_16"] v1_18 = ["gst/v1_18", "gst-sdp/v1_18", "ffi/v1_18", "v1_16"] v1_20 = ["gst/v1_20", "gst-sdp/v1_20", "ffi/v1_20", "v1_18"] v1_22 = ["gst/v1_22", "gst-sdp/v1_22", "ffi/v1_22", "v1_20"] -dox = ["v1_22", "ffi/dox", "glib/dox", "gst/dox", "gst-sdp/dox"] +dox = ["v1_22", "ffi/dox", "glib/dox", "gst/dox", "gst-sdp/dox", "ser_de"] +ser_de = ["serde", "gst/ser_de"] [package.metadata.docs.rs] features = ["dox"] diff --git a/gstreamer-rtsp/src/flag_serde.rs b/gstreamer-rtsp/src/flag_serde.rs new file mode 100644 index 000000000..6eed95e92 --- /dev/null +++ b/gstreamer-rtsp/src/flag_serde.rs @@ -0,0 +1,97 @@ +// Take a look at the license at the top of the repository in the LICENSE file. + +use glib::translate::{from_glib, ToGlibPtr}; +use glib::{FlagsClass, StaticType, ToValue}; +use gst::bitflags_serde_impl; + +bitflags_serde_impl!(crate::RTSPEvent); +bitflags_serde_impl!(crate::RTSPLowerTrans); +bitflags_serde_impl!(crate::RTSPMethod); +bitflags_serde_impl!(crate::RTSPProfile); +bitflags_serde_impl!(crate::RTSPTransMode); + +#[cfg(test)] +mod tests { + macro_rules! check_serialize { + ($flags:expr, $expected:expr) => { + let actual = serde_json::to_string(&$flags).unwrap(); + assert_eq!(actual, $expected); + }; + } + + macro_rules! check_deserialize { + ($ty:ty, $expected:expr, $json:expr) => { + let actual: $ty = serde_json::from_str(&$json).unwrap(); + assert_eq!(actual, $expected); + }; + } + + macro_rules! check_roundtrip { + ($ty:ty, $flags:expr) => { + let json = serde_json::to_string(&$flags).unwrap(); + let deserialized: $ty = serde_json::from_str(&json).unwrap(); + assert_eq!(deserialized, $flags); + }; + } + + #[test] + fn test_serialize() { + gst::init().unwrap(); + + check_serialize!(crate::RTSPEvent::all(), "\"read+write\""); + check_serialize!( + crate::RTSPLowerTrans::all(), + "\"udp+udp-mcast+tcp+http+tls\"" + ); + check_serialize!( + crate::RTSPMethod::all(), + concat!( + "\"describe+announce+get-parameter+options+pause+play+record", + "+redirect+setup+set-parameter+teardown+get+post\"" + ) + ); + check_serialize!(crate::RTSPProfile::all(), "\"avp+savp+avpf+savpf\""); + check_serialize!(crate::RTSPTransMode::all(), "\"rtp+rdt\""); + } + + #[test] + fn test_deserialize() { + gst::init().unwrap(); + + check_deserialize!(crate::RTSPEvent, crate::RTSPEvent::all(), "\"read+write\""); + check_deserialize!( + crate::RTSPLowerTrans, + crate::RTSPLowerTrans::all(), + "\"udp+udp-mcast+tcp+http+tls\"" + ); + check_deserialize!( + crate::RTSPMethod, + crate::RTSPMethod::all(), + concat!( + "\"describe+announce+get-parameter+options+pause+play+record", + "+redirect+setup+set-parameter+teardown+get+post\"" + ) + ); + check_deserialize!( + crate::RTSPProfile, + crate::RTSPProfile::all(), + "\"avp+savp+avpf+savpf\"" + ); + check_deserialize!( + crate::RTSPTransMode, + crate::RTSPTransMode::all(), + "\"rtp+rdt\"" + ); + } + + #[test] + fn test_serde_roundtrip() { + gst::init().unwrap(); + + check_roundtrip!(crate::RTSPEvent, crate::RTSPEvent::all()); + check_roundtrip!(crate::RTSPLowerTrans, crate::RTSPLowerTrans::all()); + check_roundtrip!(crate::RTSPMethod, crate::RTSPMethod::all()); + check_roundtrip!(crate::RTSPProfile, crate::RTSPProfile::all()); + check_roundtrip!(crate::RTSPTransMode, crate::RTSPTransMode::all()); + } +} diff --git a/gstreamer-rtsp/src/lib.rs b/gstreamer-rtsp/src/lib.rs index bc8e55cca..d096519ac 100644 --- a/gstreamer-rtsp/src/lib.rs +++ b/gstreamer-rtsp/src/lib.rs @@ -29,6 +29,9 @@ macro_rules! skip_assert_initialized { mod auto; pub use crate::auto::*; +#[cfg(feature = "ser_de")] +mod flag_serde; + // Re-export all the traits in a prelude module, so that applications // can always "use gst_rtsp::prelude::*" without getting conflicts pub mod prelude { diff --git a/gstreamer-video/Cargo.toml b/gstreamer-video/Cargo.toml index 772cf61a3..007b0f3cc 100644 --- a/gstreamer-video/Cargo.toml +++ b/gstreamer-video/Cargo.toml @@ -36,8 +36,8 @@ v1_16 = ["gst/v1_16", "gst-base/v1_16", "ffi/v1_16"] v1_18 = ["gst/v1_18", "gst-base/v1_18", "ffi/v1_18", "v1_16"] v1_20 = ["gst/v1_20", "gst-base/v1_20", "ffi/v1_20", "v1_18"] v1_22 = ["gst/v1_22", "gst-base/v1_22", "ffi/v1_22", "v1_20"] -dox = ["v1_22", "ffi/dox", "glib/dox", "gst/dox", "gst-base/dox"] -ser_de = ["serde"] +dox = ["v1_22", "ffi/dox", "glib/dox", "gst/dox", "gst-base/dox", "ser_de"] +ser_de = ["serde", "gst/ser_de"] [package.metadata.docs.rs] features = ["dox"] diff --git a/gstreamer-video/src/flag_serde.rs b/gstreamer-video/src/flag_serde.rs new file mode 100644 index 000000000..c6ab65652 --- /dev/null +++ b/gstreamer-video/src/flag_serde.rs @@ -0,0 +1,231 @@ +// Take a look at the license at the top of the repository in the LICENSE file. + +use glib::translate::{from_glib, ToGlibPtr}; +use glib::{FlagsClass, StaticType, ToValue}; +use gst::bitflags_serde_impl; + +bitflags_serde_impl!(crate::NavigationModifierType, "v1_22"); +bitflags_serde_impl!(crate::VideoBufferFlags); +bitflags_serde_impl!(crate::VideoChromaSite); +bitflags_serde_impl!(crate::VideoCodecFrameFlags, "v1_20"); +bitflags_serde_impl!(crate::VideoDecoderRequestSyncPointFlags, "v1_20"); +bitflags_serde_impl!(crate::VideoFlags); +bitflags_serde_impl!(crate::VideoFormatFlags); +bitflags_serde_impl!(crate::VideoFrameFlags); +bitflags_serde_impl!(crate::VideoMultiviewFlags); +bitflags_serde_impl!(crate::VideoOverlayFormatFlags, "v1_16"); +bitflags_serde_impl!(crate::VideoPackFlags); +bitflags_serde_impl!(crate::VideoTimeCodeFlags, "v1_18"); + +#[cfg(test)] +mod tests { + macro_rules! check_serialize { + ($flags:expr, $expected:expr) => { + let actual = serde_json::to_string(&$flags).unwrap(); + assert_eq!(actual, $expected); + }; + } + + macro_rules! check_deserialize { + ($ty:ty, $expected:expr, $json:expr) => { + let actual: $ty = serde_json::from_str(&$json).unwrap(); + assert_eq!(actual, $expected); + }; + } + + macro_rules! check_roundtrip { + ($ty:ty, $flags:expr) => { + let json = serde_json::to_string(&$flags).unwrap(); + let deserialized: $ty = serde_json::from_str(&json).unwrap(); + assert_eq!(deserialized, $flags); + }; + } + + #[test] + fn test_serialize() { + gst::init().unwrap(); + + #[cfg(feature = "v1_22")] + check_serialize!( + crate::NavigationModifierType::all(), + concat!( + "\"shift-mask+lock-mask+control-mask+alt-mask+button1-mask", + "+button2-mask+button3-mask+button4-mask+button5-mask", + "+super-mask+hyper-mask+meta-mask\"" + ) + ); + #[cfg(feature = "v1_18")] + check_serialize!( + crate::VideoBufferFlags::all(), + "\"interlaced+tff+rff+onefield+multiple-view+first-in-bundle+marker\"" + ); + check_serialize!( + crate::VideoChromaSite::all(), + "\"none+h-cosited+v-cosited+alt-line\"" + ); + #[cfg(feature = "v1_20")] + check_serialize!( + crate::VideoCodecFrameFlags::all(), + "\"decode-only+sync-point+force-keyframe+force-keyframe-headers+corrupted\"" + ); + #[cfg(feature = "v1_20")] + check_serialize!( + crate::VideoDecoderRequestSyncPointFlags::all(), + "\"discard-input+corrupt-output\"" + ); + check_serialize!( + crate::VideoFlags::all(), + "\"variable-fps+premultiplied-alpha\"" + ); + #[cfg(feature = "v1_22")] + check_serialize!( + crate::VideoFormatFlags::all(), + "\"yuv+rgb+gray+alpha+le+palette+complex+unpack+tiled+subtiles\"" + ); + check_serialize!( + crate::VideoFrameFlags::all(), + "\"interlaced+tff+rff+onefield+multiple-view+first-in-bundle\"" + ); + check_serialize!( + crate::VideoMultiviewFlags::all(), + concat!( + "\"right-view-first+left-flipped+left-flopped+right-flipped", + "+right-flopped+half-aspect+mixed-mono\"" + ) + ); + #[cfg(feature = "v1_16")] + check_serialize!( + crate::VideoOverlayFormatFlags::all(), + "\"premultiplied-alpha+global-alpha\"" + ); + check_serialize!( + crate::VideoPackFlags::all(), + "\"truncate-range+interlaced\"" + ); + #[cfg(feature = "v1_18")] + check_serialize!( + crate::VideoTimeCodeFlags::all(), + "\"drop-frame+interlaced\"" + ); + } + + #[test] + fn test_deserialize() { + gst::init().unwrap(); + + #[cfg(feature = "v1_22")] + check_deserialize!( + crate::NavigationModifierType, + crate::NavigationModifierType::all(), + concat!( + "\"shift-mask+lock-mask+control-mask+alt-mask+button1-mask", + "+button2-mask+button3-mask+button4-mask+button5-mask", + "+super-mask+hyper-mask+meta-mask\"" + ) + ); + #[cfg(feature = "v1_18")] + check_deserialize!( + crate::VideoBufferFlags, + crate::VideoBufferFlags::all(), + "\"interlaced+tff+rff+onefield+multiple-view+first-in-bundle+marker\"" + ); + check_deserialize!( + crate::VideoChromaSite, + crate::VideoChromaSite::all(), + "\"none+h-cosited+v-cosited+alt-line\"" + ); + #[cfg(feature = "v1_20")] + check_deserialize!( + crate::VideoCodecFrameFlags, + crate::VideoCodecFrameFlags::all(), + "\"decode-only+sync-point+force-keyframe+force-keyframe-headers+corrupted\"" + ); + #[cfg(feature = "v1_20")] + check_deserialize!( + crate::VideoDecoderRequestSyncPointFlags, + crate::VideoDecoderRequestSyncPointFlags::all(), + "\"discard-input+corrupt-output\"" + ); + check_deserialize!( + crate::VideoFlags, + crate::VideoFlags::all(), + "\"variable-fps+premultiplied-alpha\"" + ); + #[cfg(feature = "v1_22")] + check_deserialize!( + crate::VideoFormatFlags, + crate::VideoFormatFlags::all(), + "\"yuv+rgb+gray+alpha+le+palette+complex+unpack+tiled+subtiles\"" + ); + check_deserialize!( + crate::VideoFrameFlags, + crate::VideoFrameFlags::all(), + "\"interlaced+tff+rff+onefield+multiple-view+first-in-bundle\"" + ); + check_deserialize!( + crate::VideoMultiviewFlags, + crate::VideoMultiviewFlags::all(), + concat!( + "\"right-view-first+left-flipped+left-flopped+right-flipped", + "+right-flopped+half-aspect+mixed-mono\"" + ) + ); + #[cfg(feature = "v1_16")] + check_deserialize!( + crate::VideoOverlayFormatFlags, + crate::VideoOverlayFormatFlags::all(), + "\"premultiplied-alpha+global-alpha\"" + ); + check_deserialize!( + crate::VideoPackFlags, + crate::VideoPackFlags::all(), + "\"truncate-range+interlaced\"" + ); + #[cfg(feature = "v1_18")] + check_deserialize!( + crate::VideoTimeCodeFlags, + crate::VideoTimeCodeFlags::all(), + "\"drop-frame+interlaced\"" + ); + } + + #[test] + fn test_serde_roundtrip() { + gst::init().unwrap(); + + #[cfg(feature = "v1_22")] + check_roundtrip!( + crate::NavigationModifierType, + crate::NavigationModifierType::all() + ); + #[cfg(feature = "v1_18")] + check_roundtrip!(crate::VideoBufferFlags, crate::VideoBufferFlags::all()); + check_roundtrip!(crate::VideoChromaSite, crate::VideoChromaSite::all()); + #[cfg(feature = "v1_20")] + check_roundtrip!( + crate::VideoCodecFrameFlags, + crate::VideoCodecFrameFlags::all() + ); + #[cfg(feature = "v1_20")] + check_roundtrip!( + crate::VideoDecoderRequestSyncPointFlags, + crate::VideoDecoderRequestSyncPointFlags::all() + ); + check_roundtrip!(crate::VideoFlags, crate::VideoFlags::all()); + #[cfg(feature = "v1_22")] + check_roundtrip!(crate::VideoFormatFlags, crate::VideoFormatFlags::all()); + check_roundtrip!(crate::VideoFrameFlags, crate::VideoFrameFlags::all()); + check_roundtrip!( + crate::VideoMultiviewFlags, + crate::VideoMultiviewFlags::all() + ); + #[cfg(feature = "v1_16")] + check_roundtrip!( + crate::VideoOverlayFormatFlags, + crate::VideoOverlayFormatFlags::all() + ); + check_roundtrip!(crate::VideoPackFlags, crate::VideoPackFlags::all()); + #[cfg(feature = "v1_18")] + check_roundtrip!(crate::VideoTimeCodeFlags, crate::VideoTimeCodeFlags::all()); + } +} diff --git a/gstreamer-video/src/lib.rs b/gstreamer-video/src/lib.rs index 7e19a44e5..07b02dd44 100644 --- a/gstreamer-video/src/lib.rs +++ b/gstreamer-video/src/lib.rs @@ -32,6 +32,9 @@ macro_rules! skip_assert_initialized { mod auto; pub use crate::auto::*; +#[cfg(feature = "ser_de")] +mod flag_serde; + mod navigation; mod caps_features; diff --git a/gstreamer-video/src/video_event.rs b/gstreamer-video/src/video_event.rs index 30637fc9e..6aa1da197 100644 --- a/gstreamer-video/src/video_event.rs +++ b/gstreamer-video/src/video_event.rs @@ -9,10 +9,6 @@ use std::mem; #[cfg_attr(feature = "dox", doc(cfg(feature = "v1_22")))] use crate::NavigationModifierType; -#[cfg(all(feature = "ser_de", any(feature = "v1_22", feature = "dox")))] -#[cfg_attr(feature = "dox", feature = "ser_de", doc(cfg(feature = "v1_22")))] -use glib::{FlagsClass, StaticType}; - // FIXME: Copy from gstreamer/src/event.rs macro_rules! event_builder_generic_impl { ($new_fn:expr) => { @@ -1267,81 +1263,6 @@ impl NavigationEvent { } } -#[cfg(all(feature = "ser_de", any(feature = "v1_22", feature = "dox")))] -#[cfg_attr(feature = "dox", feature = "ser_de", doc(cfg(feature = "v1_22")))] -impl serde::Serialize for NavigationModifierType { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - let class = FlagsClass::new(NavigationModifierType::static_type()).unwrap(); - - let mut handled = NavigationModifierType::empty(); - let mut res = "".to_owned(); - for v in class.values() { - let value = v.value(); - if value.count_ones() != 1 || value & handled.bits() != 0 { - continue; - } else if (value & self.bits()) == value { - if !res.is_empty() { - res.push('+'); - } - res.push_str(v.nick()); - handled.insert(NavigationModifierType::from_bits(value).unwrap()); - } - } - - serializer.serialize_str(&res) - } -} - -#[cfg(all(feature = "ser_de", any(feature = "v1_22", feature = "dox")))] -#[cfg_attr(feature = "dox", feature = "ser_de", doc(cfg(feature = "v1_22")))] -struct NavigationModifierTypeVisitor; - -#[cfg(all(feature = "ser_de", any(feature = "v1_22", feature = "dox")))] -#[cfg_attr(feature = "dox", feature = "ser_de", doc(cfg(feature = "v1_22")))] -impl<'de> serde::de::Visitor<'de> for NavigationModifierTypeVisitor { - type Value = NavigationModifierType; - - fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { - formatter.write_str("one or more mask names separated by plus signs, or the empty string") - } - - fn visit_str(self, value: &str) -> std::result::Result { - if value.is_empty() { - return Ok(NavigationModifierType::empty()); - } - - let mut gvalue = glib::Value::from_type(NavigationModifierType::static_type()); - let tokens = value.split('+'); - let class = FlagsClass::new(NavigationModifierType::static_type()).unwrap(); - - for token in tokens { - gvalue = class - .set_by_nick(gvalue, token) - .map_err(|_| serde::de::Error::custom(&format!("Invalid value: {}", token)))?; - } - - Ok(unsafe { - from_glib(glib::gobject_ffi::g_value_get_flags( - gvalue.to_glib_none().0, - )) - }) - } -} - -#[cfg(all(feature = "ser_de", any(feature = "v1_22", feature = "dox")))] -#[cfg_attr(feature = "dox", feature = "ser_de", doc(cfg(feature = "v1_22")))] -impl<'de> serde::Deserialize<'de> for NavigationModifierType { - fn deserialize>( - deserializer: D, - ) -> std::result::Result { - skip_assert_initialized!(); - deserializer.deserialize_str(NavigationModifierTypeVisitor) - } -} - #[cfg(test)] mod tests { #[test] diff --git a/gstreamer/src/buffer_serde.rs b/gstreamer/src/buffer_serde.rs index bef430a52..2b3becc3b 100644 --- a/gstreamer/src/buffer_serde.rs +++ b/gstreamer/src/buffer_serde.rs @@ -104,9 +104,7 @@ mod tests { " duration: Some(5),", " offset: 3,", " offset_end: 4,", - " flags: (", - " bits: 80,", - " ),", + " flags: \"live+discont\",", " buffer: \"AQIDBA==\",", ")" ) @@ -123,7 +121,7 @@ mod tests { "\"duration\":5,", "\"offset\":3,", "\"offset_end\":4,", - "\"flags\":{\"bits\":80},", + "\"flags\":\"live+discont\",", "\"buffer\":[1,2,3,4]", "}" ) @@ -143,9 +141,7 @@ mod tests { duration: Some(5), offset: 3, offset_end: 4, - flags: ( - bits: 80, - ), + flags: "live+discont", buffer: "AQIDBA==", ) "#; @@ -168,7 +164,7 @@ mod tests { "duration":5, "offset":3, "offset_end":4, - "flags":{"bits":80}, + "flags":"live+discont", "buffer":[1,2,3,4] } "#; diff --git a/gstreamer/src/bufferlist_serde.rs b/gstreamer/src/bufferlist_serde.rs index d327e11ce..b46a85a98 100644 --- a/gstreamer/src/bufferlist_serde.rs +++ b/gstreamer/src/bufferlist_serde.rs @@ -107,9 +107,7 @@ mod tests { " duration: Some(4),", " offset: 0,", " offset_end: 4,", - " flags: (", - " bits: 0,", - " ),", + " flags: \"\",", " buffer: \"AQIDBA==\",", " ),", " (", @@ -118,9 +116,7 @@ mod tests { " duration: Some(2),", " offset: 4,", " offset_end: 6,", - " flags: (", - " bits: 0,", - " ),", + " flags: \"\",", " buffer: \"BQY=\",", " ),", "]" @@ -142,9 +138,7 @@ mod tests { duration: Some(4), offset: 0, offset_end: 4, - flags: ( - bits: 0, - ), + flags: "", buffer: "AQIDBA==", ), ( @@ -153,9 +147,7 @@ mod tests { duration: Some(2), offset: 4, offset_end: 6, - flags: ( - bits: 0, - ), + flags: "", buffer: "BQY=", ), ] diff --git a/gstreamer/src/flag_serde.rs b/gstreamer/src/flag_serde.rs new file mode 100644 index 000000000..ebb5eddd1 --- /dev/null +++ b/gstreamer/src/flag_serde.rs @@ -0,0 +1,533 @@ +// Take a look at the license at the top of the repository in the LICENSE file. + +use glib::translate::{from_glib, ToGlibPtr}; +use glib::{FlagsClass, StaticType, ToValue}; + +bitflags_serde_impl!(crate::BinFlags); +bitflags_serde_impl!(crate::BufferCopyFlags); +bitflags_serde_impl!(crate::BufferFlags); +bitflags_serde_impl!(crate::BufferPoolAcquireFlags); +bitflags_serde_impl!(crate::ClockFlags); + +impl serde::Serialize for crate::DebugColorFlags { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.serialize_str(&format!( + "{}+{}{}{}", + match *self & Self::from_bits(0b111).expect("Failed to create value from fg-color mask") + { + Self::FG_BLACK => "fg-black", + Self::FG_RED => "fg-red", + Self::FG_GREEN => "fg-green", + Self::FG_YELLOW => "fg-yellow", + Self::FG_BLUE => "fg-blue", + Self::FG_MAGENTA => "fg-magenta", + Self::FG_CYAN => "fg-cyan", + Self::FG_WHITE => "fg-white", + _ => unreachable!(), + }, + match *self + & Self::from_bits(0b111_0000).expect("Failed to create value from bg-color mask") + { + Self::BG_BLACK => "bg-black", + Self::BG_RED => "bg-red", + Self::BG_GREEN => "bg-green", + Self::BG_YELLOW => "bg-yellow", + Self::BG_BLUE => "bg-blue", + Self::BG_MAGENTA => "bg-magenta", + Self::BG_CYAN => "bg-cyan", + Self::BG_WHITE => "bg-white", + _ => unreachable!(), + }, + if self.contains(Self::BOLD) { + "+bold" + } else { + "" + }, + if self.contains(Self::UNDERLINE) { + "+underline" + } else { + "" + } + )) + } +} + +bitflags_deserialize_impl!(crate::DebugColorFlags); +bitflags_serialize_impl!(crate::DebugGraphDetails, by_ones_decreasing); +bitflags_deserialize_impl!(crate::DebugGraphDetails); +bitflags_serde_impl!(crate::ElementFlags); +bitflags_serde_impl!(crate::EventTypeFlags); +bitflags_serde_impl!(crate::GapFlags, "v1_20"); +bitflags_serde_impl!(crate::MemoryFlags); +bitflags_serde_impl!(crate::MetaFlags); +bitflags_serde_impl!(crate::ObjectFlags); +bitflags_serde_impl!(crate::PadFlags); +bitflags_serde_impl!(crate::PadLinkCheck); +bitflags_serde_impl!(crate::PadProbeType); +bitflags_serde_impl!(crate::ParseFlags); +bitflags_serde_impl!(crate::PluginAPIFlags, "v1_18"); +bitflags_serde_impl!(crate::PluginDependencyFlags); +bitflags_serde_impl!(crate::PluginFlags); +bitflags_serde_impl!(crate::SchedulingFlags); +bitflags_serde_impl!(crate::SeekFlags); +bitflags_serde_impl!(crate::SegmentFlags); +bitflags_serde_impl!(crate::SerializeFlags, "v1_20"); +bitflags_serde_impl!(crate::StackTraceFlags); +bitflags_serde_impl!(crate::StreamFlags); +bitflags_serde_impl!(crate::StreamType); + +#[cfg(test)] +mod tests { + macro_rules! check_serialize { + ($flags:expr, $expected:expr) => { + let actual = serde_json::to_string(&$flags).unwrap(); + assert_eq!(actual, $expected); + }; + } + + macro_rules! check_deserialize { + ($ty:ty, $expected:expr, $json:expr) => { + let actual: $ty = serde_json::from_str(&$json).unwrap(); + assert_eq!(actual, $expected); + }; + } + + macro_rules! check_roundtrip { + ($ty:ty, $flags:expr) => { + let json = serde_json::to_string(&$flags).unwrap(); + let deserialized: $ty = serde_json::from_str(&json).unwrap(); + assert_eq!(deserialized, $flags); + }; + } + + #[test] + fn test_serialize() { + crate::init().unwrap(); + + check_serialize!(crate::BinFlags::empty(), "\"\""); + check_serialize!(crate::BinFlags::all(), "\"no-resync+streams-aware\""); + check_serialize!( + crate::BufferCopyFlags::all(), + "\"flags+timestamps+meta+memory+merge+deep\"" + ); + check_serialize!( + crate::BufferFlags::all(), + concat!( + "\"live+decode-only+discont+resync+corrupted+marker+header+gap", + "+droppable+delta-unit+tag-memory+sync-after+non-droppable\"" + ) + ); + check_serialize!( + crate::BufferPoolAcquireFlags::all(), + "\"key-unit+dontwait+discont\"" + ); + check_serialize!( + crate::ClockFlags::all(), + concat!( + "\"can-do-single-sync+can-do-single-async", + "+can-do-periodic-sync+can-do-periodic-async", + "+can-set-resolution+can-set-master+needs-startup-sync\"" + ) + ); + + check_serialize!( + crate::DebugColorFlags::all(), + "\"fg-white+bg-white+bold+underline\"" + ); + check_serialize!( + crate::DebugColorFlags::FG_MAGENTA | crate::DebugColorFlags::BOLD, + "\"fg-magenta+bg-black+bold\"" + ); + check_serialize!( + crate::DebugColorFlags::FG_RED + | crate::DebugColorFlags::FG_BLUE + | crate::DebugColorFlags::BG_BLACK, + "\"fg-magenta+bg-black\"" + ); + + check_serialize!(crate::DebugGraphDetails::all(), "\"verbose\""); + check_serialize!( + crate::DebugGraphDetails::MEDIA_TYPE + | crate::DebugGraphDetails::CAPS_DETAILS + | crate::DebugGraphDetails::NON_DEFAULT_PARAMS + | crate::DebugGraphDetails::STATES + | crate::DebugGraphDetails::FULL_PARAMS + | crate::DebugGraphDetails::ALL, + "\"all+full-params\"" + ); + check_serialize!( + crate::DebugGraphDetails::MEDIA_TYPE + | crate::DebugGraphDetails::CAPS_DETAILS + | crate::DebugGraphDetails::NON_DEFAULT_PARAMS + | crate::DebugGraphDetails::STATES + | crate::DebugGraphDetails::FULL_PARAMS, + "\"all+full-params\"" + ); + + check_serialize!( + crate::ElementFlags::all(), + "\"locked-state+sink+source+provide-clock+require-clock+indexable\"" + ); + check_serialize!( + crate::EventTypeFlags::all(), + "\"upstream+downstream+serialized+sticky+sticky-multi\"" + ); + #[cfg(feature = "v1_20")] + check_serialize!(crate::GapFlags::all(), "\"data\""); + check_serialize!( + crate::MemoryFlags::all(), + concat!( + "\"readonly+no-share+zero-prefixed+zero-padded", + "+physically-contiguous+not-mappable\"" + ) + ); + check_serialize!(crate::MetaFlags::all(), "\"readonly+pooled+locked\""); + check_serialize!(crate::ObjectFlags::all(), "\"may-be-leaked\""); + check_serialize!( + crate::PadFlags::all(), + concat!( + "\"blocked+flushing+eos+blocking+need-parent+need-reconfigure", + "+pending-events+fixed-caps+proxy-caps+proxy-allocation", + "+proxy-scheduling+accept-intersect+accept-template\"" + ) + ); + check_serialize!( + crate::PadLinkCheck::all(), + "\"hierarchy+template-caps+caps+no-reconfigure\"" + ); + check_serialize!( + crate::PadProbeType::all(), + concat!( + "\"idle+block+buffer+buffer-list+event-downstream", + "+event-upstream+event-flush+query-downstream+query-upstream", + "+push+pull\"" + ) + ); + check_serialize!( + crate::ParseFlags::all(), + "\"fatal-errors+no-single-element-bins+place-in-bin\"" + ); + #[cfg(feature = "v1_18")] + check_serialize!(crate::PluginAPIFlags::all(), "\"members\""); + check_serialize!( + crate::PluginDependencyFlags::all(), + concat!( + "\"recurse+paths-are-default-only+file-name-is-suffix", + "+file-name-is-prefix+paths-are-relative-to-exe\"" + ) + ); + check_serialize!(crate::PluginFlags::all(), "\"cached+blacklisted\""); + check_serialize!( + crate::SchedulingFlags::all(), + "\"seekable+sequential+bandwidth-limited\"" + ); + #[cfg(feature = "v1_18")] + check_serialize!( + crate::SeekFlags::all(), + concat!( + "\"flush+accurate+key-unit+segment+trickmode+snap-before", + "+snap-after+trickmode-key-units+trickmode-no-audio", + "+trickmode-forward-predicted+instant-rate-change\"" + ) + ); + #[cfg(feature = "v1_18")] + check_serialize!( + crate::SegmentFlags::all(), + concat!( + "\"reset+trickmode+segment+trickmode-key-units", + "+trickmode-forward-predicted+trickmode-no-audio\"" + ) + ); + #[cfg(feature = "v1_20")] + check_serialize!(crate::SerializeFlags::all(), "\"backward-compat\""); + check_serialize!(crate::StackTraceFlags::all(), "\"full\""); + check_serialize!(crate::StreamFlags::all(), "\"sparse+select+unselect\""); + check_serialize!( + crate::StreamType::all(), + "\"unknown+audio+video+container+text\"" + ); + } + + #[test] + fn test_deserialize() { + crate::init().unwrap(); + + check_deserialize!(crate::BinFlags, crate::BinFlags::empty(), "\"\""); + check_deserialize!( + crate::BinFlags, + crate::BinFlags::all(), + "\"no-resync+streams-aware\"" + ); + check_deserialize!( + crate::BufferCopyFlags, + crate::BufferCopyFlags::all(), + "\"flags+timestamps+meta+memory+merge+deep\"" + ); + check_deserialize!( + crate::BufferFlags, + crate::BufferFlags::all(), + concat!( + "\"live+decode-only+discont+resync+corrupted+marker+header+gap", + "+droppable+delta-unit+tag-memory+sync-after+non-droppable\"" + ) + ); + check_deserialize!( + crate::BufferPoolAcquireFlags, + crate::BufferPoolAcquireFlags::all(), + "\"key-unit+dontwait+discont\"" + ); + check_deserialize!( + crate::ClockFlags, + crate::ClockFlags::all(), + concat!( + "\"can-do-single-sync+can-do-single-async", + "+can-do-periodic-sync+can-do-periodic-async", + "+can-set-resolution+can-set-master+needs-startup-sync\"" + ) + ); + + check_deserialize!( + crate::DebugColorFlags, + crate::DebugColorFlags::all(), + "\"fg-white+bg-white+bold+underline\"" + ); + check_deserialize!( + crate::DebugColorFlags, + crate::DebugColorFlags::FG_MAGENTA | crate::DebugColorFlags::BOLD, + "\"fg-magenta+bg-black+bold\"" + ); + check_deserialize!( + crate::DebugColorFlags, + crate::DebugColorFlags::FG_RED + | crate::DebugColorFlags::FG_BLUE + | crate::DebugColorFlags::BG_BLACK, + "\"fg-magenta+bg-black\"" + ); + + check_deserialize!( + crate::DebugGraphDetails, + crate::DebugGraphDetails::all(), + "\"verbose\"" + ); + check_deserialize!( + crate::DebugGraphDetails, + crate::DebugGraphDetails::MEDIA_TYPE + | crate::DebugGraphDetails::CAPS_DETAILS + | crate::DebugGraphDetails::NON_DEFAULT_PARAMS + | crate::DebugGraphDetails::STATES + | crate::DebugGraphDetails::FULL_PARAMS + | crate::DebugGraphDetails::ALL, + "\"all+full-params\"" + ); + check_deserialize!( + crate::DebugGraphDetails, + crate::DebugGraphDetails::MEDIA_TYPE + | crate::DebugGraphDetails::CAPS_DETAILS + | crate::DebugGraphDetails::NON_DEFAULT_PARAMS + | crate::DebugGraphDetails::STATES + | crate::DebugGraphDetails::FULL_PARAMS, + "\"all+full-params\"" + ); + + check_deserialize!( + crate::ElementFlags, + crate::ElementFlags::all(), + "\"locked-state+sink+source+provide-clock+require-clock+indexable\"" + ); + check_deserialize!( + crate::EventTypeFlags, + crate::EventTypeFlags::all(), + "\"upstream+downstream+serialized+sticky+sticky-multi\"" + ); + #[cfg(feature = "v1_20")] + check_deserialize!(crate::GapFlags, crate::GapFlags::all(), "\"data\""); + check_deserialize!( + crate::MemoryFlags, + crate::MemoryFlags::all(), + concat!( + "\"readonly+no-share+zero-prefixed+zero-padded", + "+physically-contiguous+not-mappable\"" + ) + ); + check_deserialize!( + crate::MetaFlags, + crate::MetaFlags::all(), + "\"readonly+pooled+locked\"" + ); + check_deserialize!( + crate::ObjectFlags, + crate::ObjectFlags::all(), + "\"may-be-leaked\"" + ); + check_deserialize!( + crate::PadFlags, + crate::PadFlags::all(), + concat!( + "\"blocked+flushing+eos+blocking+need-parent+need-reconfigure", + "+pending-events+fixed-caps+proxy-caps+proxy-allocation", + "+proxy-scheduling+accept-intersect+accept-template\"" + ) + ); + check_deserialize!( + crate::PadLinkCheck, + crate::PadLinkCheck::all(), + "\"hierarchy+template-caps+caps+no-reconfigure\"" + ); + check_deserialize!( + crate::PadProbeType, + crate::PadProbeType::all(), + concat!( + "\"idle+block+buffer+buffer-list+event-downstream", + "+event-upstream+event-flush+query-downstream+query-upstream", + "+push+pull\"" + ) + ); + check_deserialize!( + crate::ParseFlags, + crate::ParseFlags::all(), + "\"fatal-errors+no-single-element-bins+place-in-bin\"" + ); + #[cfg(feature = "v1_18")] + check_deserialize!( + crate::PluginAPIFlags, + crate::PluginAPIFlags::all(), + "\"members\"" + ); + check_deserialize!( + crate::PluginDependencyFlags, + crate::PluginDependencyFlags::all(), + concat!( + "\"recurse+paths-are-default-only+file-name-is-suffix", + "+file-name-is-prefix+paths-are-relative-to-exe\"" + ) + ); + check_deserialize!( + crate::PluginFlags, + crate::PluginFlags::all(), + "\"cached+blacklisted\"" + ); + check_deserialize!( + crate::SchedulingFlags, + crate::SchedulingFlags::all(), + "\"seekable+sequential+bandwidth-limited\"" + ); + #[cfg(feature = "v1_18")] + check_deserialize!( + crate::SeekFlags, + crate::SeekFlags::all(), + concat!( + "\"flush+accurate+key-unit+segment+trickmode+snap-before", + "+snap-after+trickmode-key-units+trickmode-no-audio", + "+trickmode-forward-predicted+instant-rate-change\"" + ) + ); + #[cfg(feature = "v1_18")] + check_deserialize!( + crate::SegmentFlags, + crate::SegmentFlags::all(), + concat!( + "\"reset+trickmode+segment+trickmode-key-units", + "+trickmode-forward-predicted+trickmode-no-audio\"" + ) + ); + #[cfg(feature = "v1_20")] + check_deserialize!( + crate::SerializeFlags, + crate::SerializeFlags::all(), + "\"backward-compat\"" + ); + check_deserialize!( + crate::StackTraceFlags, + crate::StackTraceFlags::all(), + "\"full\"" + ); + check_deserialize!( + crate::StreamFlags, + crate::StreamFlags::all(), + "\"sparse+select+unselect\"" + ); + check_deserialize!( + crate::StreamType, + crate::StreamType::all(), + "\"unknown+audio+video+container+text\"" + ); + } + + #[test] + fn test_serde_roundtrip() { + crate::init().unwrap(); + + check_roundtrip!(crate::BinFlags, crate::BinFlags::empty()); + check_roundtrip!(crate::BinFlags, crate::BinFlags::all()); + check_roundtrip!(crate::BufferCopyFlags, crate::BufferCopyFlags::all()); + check_roundtrip!(crate::BufferFlags, crate::BufferFlags::all()); + check_roundtrip!( + crate::BufferPoolAcquireFlags, + crate::BufferPoolAcquireFlags::all() + ); + check_roundtrip!(crate::ClockFlags, crate::ClockFlags::all()); + + check_roundtrip!(crate::DebugColorFlags, crate::DebugColorFlags::all()); + check_roundtrip!( + crate::DebugColorFlags, + crate::DebugColorFlags::FG_MAGENTA | crate::DebugColorFlags::BOLD + ); + check_roundtrip!( + crate::DebugColorFlags, + crate::DebugColorFlags::FG_RED + | crate::DebugColorFlags::FG_BLUE + | crate::DebugColorFlags::BG_BLACK + ); + + check_roundtrip!(crate::DebugGraphDetails, crate::DebugGraphDetails::all()); + check_roundtrip!( + crate::DebugGraphDetails, + crate::DebugGraphDetails::MEDIA_TYPE + | crate::DebugGraphDetails::CAPS_DETAILS + | crate::DebugGraphDetails::NON_DEFAULT_PARAMS + | crate::DebugGraphDetails::STATES + | crate::DebugGraphDetails::FULL_PARAMS + | crate::DebugGraphDetails::ALL + ); + check_roundtrip!( + crate::DebugGraphDetails, + crate::DebugGraphDetails::MEDIA_TYPE + | crate::DebugGraphDetails::CAPS_DETAILS + | crate::DebugGraphDetails::NON_DEFAULT_PARAMS + | crate::DebugGraphDetails::STATES + | crate::DebugGraphDetails::FULL_PARAMS + ); + + check_roundtrip!(crate::ElementFlags, crate::ElementFlags::all()); + check_roundtrip!(crate::EventTypeFlags, crate::EventTypeFlags::all()); + #[cfg(feature = "v1_20")] + check_roundtrip!(crate::GapFlags, crate::GapFlags::all()); + check_roundtrip!(crate::MemoryFlags, crate::MemoryFlags::all()); + check_roundtrip!(crate::MetaFlags, crate::MetaFlags::all()); + check_roundtrip!(crate::ObjectFlags, crate::ObjectFlags::all()); + check_roundtrip!(crate::PadFlags, crate::PadFlags::all()); + check_roundtrip!(crate::PadLinkCheck, crate::PadLinkCheck::all()); + check_roundtrip!(crate::PadProbeType, crate::PadProbeType::all()); + check_roundtrip!(crate::ParseFlags, crate::ParseFlags::all()); + #[cfg(feature = "v1_18")] + check_roundtrip!(crate::PluginAPIFlags, crate::PluginAPIFlags::all()); + check_roundtrip!( + crate::PluginDependencyFlags, + crate::PluginDependencyFlags::all() + ); + check_roundtrip!(crate::PluginFlags, crate::PluginFlags::all()); + check_roundtrip!(crate::SchedulingFlags, crate::SchedulingFlags::all()); + #[cfg(feature = "v1_18")] + check_roundtrip!(crate::SeekFlags, crate::SeekFlags::all()); + #[cfg(feature = "v1_18")] + check_roundtrip!(crate::SegmentFlags, crate::SegmentFlags::all()); + #[cfg(feature = "v1_20")] + check_roundtrip!(crate::SerializeFlags, crate::SerializeFlags::all()); + check_roundtrip!(crate::StackTraceFlags, crate::StackTraceFlags::all()); + check_roundtrip!(crate::StreamFlags, crate::StreamFlags::all()); + check_roundtrip!(crate::StreamType, crate::StreamType::all()); + } +} diff --git a/gstreamer/src/lib.rs b/gstreamer/src/lib.rs index 5706bd422..10bf2dd86 100644 --- a/gstreamer/src/lib.rs +++ b/gstreamer/src/lib.rs @@ -40,6 +40,12 @@ pub use crate::auto::*; #[macro_use] mod macros; +#[macro_use] +#[cfg(feature = "ser_de")] +mod serde_macros; +#[cfg(feature = "ser_de")] +pub use crate::serde_macros::*; + #[macro_use] mod log; pub use crate::log::*; @@ -62,6 +68,9 @@ pub use crate::value::{ #[macro_use] mod value_serde; +#[cfg(feature = "ser_de")] +mod flag_serde; + pub mod structure; pub use crate::structure::{Structure, StructureRef}; #[cfg(feature = "ser_de")] diff --git a/gstreamer/src/sample_serde.rs b/gstreamer/src/sample_serde.rs index eb809069b..88c378269 100644 --- a/gstreamer/src/sample_serde.rs +++ b/gstreamer/src/sample_serde.rs @@ -142,9 +142,7 @@ mod tests { " duration: Some(4),", " offset: 0,", " offset_end: 4,", - " flags: (", - " bits: 0,", - " ),", + " flags: \"\",", " buffer: \"AQIDBA==\",", " )),", " buffer_list: None,", @@ -155,9 +153,7 @@ mod tests { " ]), None),", " ])),", " segment: Some((", - " flags: (", - " bits: 9,", - " ),", + " flags: \"reset+segment\",", " rate: 1,", " applied_rate: 0.9,", " format: Time,", @@ -202,17 +198,13 @@ mod tests { " duration: Some(4),", " offset: 0,", " offset_end: 4,", - " flags: (", - " bits: 0,", - " ),", + " flags: \"\",", " buffer: \"AQIDBA==\",", " )),", " buffer_list: None,", " caps: None,", " segment: Some((", - " flags: (", - " bits: 0,", - " ),", + " flags: \"\",", " rate: 1,", " applied_rate: 1,", " format: Time,", @@ -244,9 +236,7 @@ mod tests { duration: Some(4), offset: 0, offset_end: 4, - flags: ( - bits: 0, - ), + flags: "", buffer: "AQIDBA==", )), buffer_list: None, @@ -257,9 +247,7 @@ mod tests { ]), None), ])), segment: Some(( - flags: ( - bits: 0, - ), + flags: "", rate: 1, applied_rate: 0.9, format: Time, @@ -298,9 +286,7 @@ mod tests { duration: Some(4), offset: 0, offset_end: 4, - flags: ( - bits: 0, - ), + flags: "", buffer: "AQIDBA==", ), ]), diff --git a/gstreamer/src/segment_serde.rs b/gstreamer/src/segment_serde.rs index 88a5eae4d..367615c83 100644 --- a/gstreamer/src/segment_serde.rs +++ b/gstreamer/src/segment_serde.rs @@ -138,9 +138,7 @@ mod tests { assert_eq!( Ok(concat!( "(", - " flags: (", - " bits: 9,", - " ),", + " flags: \"reset+segment\",", " rate: 1,", " applied_rate: 0.9,", " format: Time,", @@ -164,9 +162,7 @@ mod tests { let segment_ron = r#" ( - flags: ( - bits: 9, - ), + flags: "reset+segment", rate: 1, applied_rate: 0.9, format: Time, @@ -228,9 +224,7 @@ mod tests { let segment_ron = r#" ( - flags: ( - bits: 9, - ), + flags: "reset+segment", rate: 1, applied_rate: 0.9, format: Time, diff --git a/gstreamer/src/serde_macros.rs b/gstreamer/src/serde_macros.rs new file mode 100644 index 000000000..d632431ab --- /dev/null +++ b/gstreamer/src/serde_macros.rs @@ -0,0 +1,145 @@ +// Take a look at the license at the top of the repository in the LICENSE file. + +#[macro_export] +macro_rules! bitflags_serialize_impl { + // this implementation serializes only flags using only one bit, + // ignoring all other flags + ($type:ty, single_bit_flags$(, $feature:expr)?) => { + $(#[cfg(any(feature = $feature, feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = $feature)))])? + impl serde::Serialize for $type { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let class = FlagsClass::new(Self::static_type()).unwrap(); + let this = self.to_value(); + + let mut handled = Self::empty().to_value(); + let mut res = String::new(); + + for v in class.values() { + let value = v.value(); + if value.count_ones() == 1 && class.is_set(&this, value) && !class.is_set(&handled, value) { + if !res.is_empty() { + res.push('+'); + } + res.push_str(v.nick()); + handled = class.set(handled, value).expect("Failed to set flag"); + } + } + + serializer.serialize_str(&res) + } + } + }; + + // considers the flags using the most bits first + ($type:ty, by_ones_decreasing$(, $feature:expr)?) => { + $(#[cfg(any(feature = $feature, feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = $feature)))])? + impl serde::Serialize for $type { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + use once_cell::sync::Lazy; + + let mut handled = Self::empty(); + let mut res = String::new(); + + static SORTED_VALUES: Lazy> = Lazy::new(|| { + let class = FlagsClass::new(<$type>::static_type()).unwrap(); + let mut sorted_values: Vec<(u32, String)> = + class.values().iter() + .map(|f| (f.value(), f.nick().to_owned())) + .collect(); + + sorted_values.sort_by(|(a, _), (b, _)| { + b.count_ones().cmp(&a.count_ones()) + }); + + sorted_values + }); + + for (bits, nick) in SORTED_VALUES.iter() { + // not all values defined in the class are always also defined + // in the bitflags struct, see RTPBufferFlags for example + if let Some(value) = Self::from_bits(*bits) { + if !value.is_empty() && self.contains(value) && !handled.intersects(value) { + if !res.is_empty() { + res.push('+'); + } + res.push_str(nick); + handled.insert(value); + } + } + } + + serializer.serialize_str(&res) + } + } + }; +} + +#[macro_export] +macro_rules! bitflags_deserialize_impl { + ($type:ty$(, $feature:expr)?) => { + $(#[cfg(any(feature = $feature, feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = $feature)))])? + impl<'de> serde::Deserialize<'de> for $type { + fn deserialize>( + deserializer: D, + ) -> std::result::Result { + skip_assert_initialized!(); + + struct FlagsVisitor; + + impl<'de> serde::de::Visitor<'de> for FlagsVisitor { + type Value = $type; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str( + "one or more mask names separated by plus signs, or the empty string", + ) + } + + fn visit_str( + self, + value: &str, + ) -> std::result::Result { + if value.is_empty() { + return Ok(Self::Value::empty()); + } + + let mut gvalue = glib::Value::from_type(Self::Value::static_type()); + let tokens = value.split('+'); + let class = FlagsClass::new(Self::Value::static_type()).unwrap(); + + for token in tokens { + gvalue = class.set_by_nick(gvalue, token).map_err(|_| { + serde::de::Error::custom(&format!("Invalid value: {}", token)) + })?; + } + + Ok(unsafe { + from_glib(glib::gobject_ffi::g_value_get_flags( + gvalue.to_glib_none().0, + )) + }) + } + } + + deserializer.deserialize_str(FlagsVisitor) + } + } + }; +} + +#[macro_export] +macro_rules! bitflags_serde_impl { + ($type:ty$(, $feature:expr)?) => { + $crate::bitflags_serialize_impl!($type, single_bit_flags$(, $feature)?); + $crate::bitflags_deserialize_impl!($type$(, $feature)?); + }; +} diff --git a/gstreamer/src/tags_serde.rs b/gstreamer/src/tags_serde.rs index d1bf28591..77853fcc1 100644 --- a/gstreamer/src/tags_serde.rs +++ b/gstreamer/src/tags_serde.rs @@ -387,17 +387,13 @@ mod tests { r#" duration: None,"#, r#" offset: 0,"#, r#" offset_end: 0,"#, - r#" flags: ("#, - r#" bits: 0,"#, - r#" ),"#, + r#" flags: "","#, r#" buffer: "AQIDBA==","#, r#" )),"#, r#" buffer_list: None,"#, r#" caps: None,"#, r#" segment: Some(("#, - r#" flags: ("#, - r#" bits: 0,"#, - r#" ),"#, + r#" flags: "","#, r#" rate: 1,"#, r#" applied_rate: 1,"#, r#" format: Time,"#, @@ -449,9 +445,7 @@ mod tests { duration: None, offset: 0, offset_end: 0, - flags: ( - bits: 0, - ), + flags: "", buffer: "AQIDBA==", )), buffer_list: None, @@ -500,7 +494,7 @@ mod tests { ["replaygain-track-gain", [1.0]], ["date",[{"YMD":[2018,5,28]}]], ["datetime",[{"YMD":[2018,5,28]}]], - ["image",[{"buffer":{"pts":null,"dts":null,"duration":null,"offset":0,"offset_end":0,"flags":{"bits":0},"buffer":[1,2,3,4]},"buffer_list":null,"caps":null,"segment":null,"info":null}]] + ["image",[{"buffer":{"pts":null,"dts":null,"duration":null,"offset":0,"offset_end":0,"flags":"","buffer":[1,2,3,4]},"buffer_list":null,"caps":null,"segment":null,"info":null}]] ] } "#;