From a753c65d3370a5e317796f4d207ab2e601f7522e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Sat, 15 Dec 2018 14:50:11 +0200 Subject: [PATCH] video: Add VideoTimeCode bindings There's a VideoTimeCode type that represents any kind of timecodes, including invalid ones, and which allows to change each field individually. And ValidVideoTimeCode that has all fields validated and that can be used with most of the API. In C, validation of the timecodes is left to the user and most functions assert on getting passed valid timecodes. --- Gir_GstVideo.toml | 4 + gstreamer-video/src/auto/flags.rs | 28 + gstreamer-video/src/auto/mod.rs | 2 + gstreamer-video/src/lib.rs | 8 + gstreamer-video/src/video_time_code.rs | 492 ++++++++++++++++++ .../src/video_time_code_interval.rs | 239 +++++++++ 6 files changed, 773 insertions(+) create mode 100644 gstreamer-video/src/video_time_code.rs create mode 100644 gstreamer-video/src/video_time_code_interval.rs diff --git a/Gir_GstVideo.toml b/Gir_GstVideo.toml index 56a6a52c9..555950c7e 100644 --- a/Gir_GstVideo.toml +++ b/Gir_GstVideo.toml @@ -34,9 +34,11 @@ generate = [ "GstVideo.VideoMultiviewFramePacking", "GstVideo.VideoFilter", "GstVideo.VideoOverlayFormatFlags", + "GstVideo.VideoTimeCodeFlags", ] manual = [ + "GLib.DateTime", "GObject.Object", "Gst.Object", "Gst.Element", @@ -46,6 +48,8 @@ manual = [ "GstVideo.VideoColorimetry", "GstVideo.VideoColorRange", "GstVideo.VideoFrame", + "GstVideo.VideoTimeCode", + "GstVideo.VideoTimeCodeInterval", ] [[object]] diff --git a/gstreamer-video/src/auto/flags.rs b/gstreamer-video/src/auto/flags.rs index d5b131f02..31dd9f5ca 100644 --- a/gstreamer-video/src/auto/flags.rs +++ b/gstreamer-video/src/auto/flags.rs @@ -303,3 +303,31 @@ impl FromGlib for VideoOverlayFormatFlags { } } +#[cfg(any(feature = "v1_10", feature = "dox"))] +bitflags! { + pub struct VideoTimeCodeFlags: u32 { + const NONE = 0; + const DROP_FRAME = 1; + const INTERLACED = 2; + } +} + +#[cfg(any(feature = "v1_10", feature = "dox"))] +#[doc(hidden)] +impl ToGlib for VideoTimeCodeFlags { + type GlibType = ffi::GstVideoTimeCodeFlags; + + fn to_glib(&self) -> ffi::GstVideoTimeCodeFlags { + self.bits() + } +} + +#[cfg(any(feature = "v1_10", feature = "dox"))] +#[doc(hidden)] +impl FromGlib for VideoTimeCodeFlags { + fn from_glib(value: ffi::GstVideoTimeCodeFlags) -> VideoTimeCodeFlags { + skip_assert_initialized!(); + VideoTimeCodeFlags::from_bits_truncate(value) + } +} + diff --git a/gstreamer-video/src/auto/mod.rs b/gstreamer-video/src/auto/mod.rs index 9d5c3fbc9..8329cdeae 100644 --- a/gstreamer-video/src/auto/mod.rs +++ b/gstreamer-video/src/auto/mod.rs @@ -28,6 +28,8 @@ pub use self::flags::VideoFormatFlags; pub use self::flags::VideoFrameFlags; pub use self::flags::VideoMultiviewFlags; pub use self::flags::VideoOverlayFormatFlags; +#[cfg(any(feature = "v1_10", feature = "dox"))] +pub use self::flags::VideoTimeCodeFlags; #[doc(hidden)] pub mod traits { diff --git a/gstreamer-video/src/lib.rs b/gstreamer-video/src/lib.rs index 48368ac4e..01fed64c4 100644 --- a/gstreamer-video/src/lib.rs +++ b/gstreamer-video/src/lib.rs @@ -62,6 +62,14 @@ mod video_overlay_composition; pub use video_overlay_composition::*; mod video_meta; pub use video_meta::*; +#[cfg(any(feature = "v1_10", feature = "dox"))] +mod video_time_code; +#[cfg(any(feature = "v1_10", feature = "dox"))] +pub use video_time_code::{ValidVideoTimeCode, VideoTimeCode}; +#[cfg(any(feature = "v1_12", feature = "dox"))] +mod video_time_code_interval; +#[cfg(any(feature = "v1_12", feature = "dox"))] +pub use video_time_code_interval::VideoTimeCodeInterval; // Re-export all the traits in a prelude module, so that applications // can always "use gst::prelude::*" without getting conflicts diff --git a/gstreamer-video/src/video_time_code.rs b/gstreamer-video/src/video_time_code.rs new file mode 100644 index 000000000..f059e3973 --- /dev/null +++ b/gstreamer-video/src/video_time_code.rs @@ -0,0 +1,492 @@ +// Copyright (C) 2018 Sebastian Dröge +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use ffi; +use glib; +use glib::prelude::*; +use glib::translate::*; +use glib::value; +use glib_ffi; +use gobject_ffi; +use gst; +use gst::prelude::*; +use std::cmp; +use std::fmt; +use std::mem; +use std::ptr; +use std::str; + +use VideoTimeCodeFlags; +#[cfg(any(feature = "v1_12", feature = "dox"))] +use VideoTimeCodeInterval; + +pub struct VideoTimeCode(ffi::GstVideoTimeCode); +pub struct ValidVideoTimeCode(ffi::GstVideoTimeCode); + +impl VideoTimeCode { + pub fn new_empty() -> VideoTimeCode { + assert_initialized_main_thread!(); + unsafe { + let mut v = mem::zeroed(); + ffi::gst_video_time_code_clear(&mut v); + VideoTimeCode(v) + } + } + + pub fn new( + fps: gst::Fraction, + latest_daily_jam: Option<&glib::DateTime>, + flags: VideoTimeCodeFlags, + hours: u32, + minutes: u32, + seconds: u32, + frames: u32, + field_count: u32, + ) -> Self { + assert_initialized_main_thread!(); + unsafe { + let mut v = mem::zeroed(); + ffi::gst_video_time_code_init( + &mut v, + *fps.numer() as u32, + *fps.denom() as u32, + latest_daily_jam.to_glib_none().0, + flags.to_glib(), + hours, + minutes, + seconds, + frames, + field_count, + ); + + VideoTimeCode(v) + } + } + + // #[cfg(any(feature = "v1_16", feature = "dox"))] + // pub fn new_from_date_time( + // fps: gst::Fraction, + // dt: &glib::DateTime, + // flags: VideoTimeCodeFlags, + // field_count: u32, + // ) -> Option { + // assert_initialized_main_thread!(); + // assert!(fps_d > 0); + // unsafe { + // let mut v = mem::zeroed(); + // let res = ffi::gst_video_time_code_init_from_date_time_full( + // &mut v, + // *fps.numer() as u32, + // *fps.denom() as u32, + // dt.to_glib_none().0, + // flags.to_glib(), + // field_count, + // ); + // + // if res == glib_ffi::GFALSE { + // None + // } else { + // Some(VideoTimeCode(v)) + // } + // } + // } + + #[cfg(any(feature = "v1_12", feature = "dox"))] + pub fn from_string(tc_str: &str) -> Option { + assert_initialized_main_thread!(); + unsafe { + from_glib_full(ffi::gst_video_time_code_new_from_string( + tc_str.to_glib_none().0, + )) + } + } + + pub fn is_valid(&self) -> bool { + unsafe { from_glib(ffi::gst_video_time_code_is_valid(self.to_glib_none().0)) } + } + + pub fn set_fps(&mut self, fps: gst::Fraction) { + self.0.config.fps_n = *fps.numer() as u32; + self.0.config.fps_d = *fps.denom() as u32; + } + + pub fn set_flags(&mut self, flags: VideoTimeCodeFlags) { + self.0.config.flags = flags.to_glib() + } + + pub fn set_hours(&mut self, hours: u32) { + self.0.hours = hours + } + + pub fn set_minutes(&mut self, minutes: u32) { + assert!(minutes < 60); + self.0.minutes = minutes + } + + pub fn set_seconds(&mut self, seconds: u32) { + assert!(seconds < 60); + self.0.seconds = seconds + } + + pub fn set_frames(&mut self, frames: u32) { + self.0.frames = frames + } + + pub fn set_field_count(&mut self, field_count: u32) { + assert!(field_count <= 2); + self.0.field_count = field_count + } + + pub fn try_into(self) -> Result { + if self.is_valid() { + Ok(ValidVideoTimeCode(self.0)) + } else { + Err(self) + } + } +} + +impl ValidVideoTimeCode { + pub fn new( + fps: gst::Fraction, + latest_daily_jam: Option<&glib::DateTime>, + flags: VideoTimeCodeFlags, + hours: u32, + minutes: u32, + seconds: u32, + frames: u32, + field_count: u32, + ) -> Option { + let tc = VideoTimeCode::new( + fps, + latest_daily_jam, + flags, + hours, + minutes, + seconds, + frames, + field_count, + ); + tc.try_into().ok() + } + + // #[cfg(any(feature = "v1_16", feature = "dox"))] + // pub fn new_from_date_time( + // fps: gst::Fraction, + // dt: &glib::DateTime, + // flags: VideoTimeCodeFlags, + // field_count: u32, + // ) -> Option { + // let tc = VideoTimeCode::new_from_date_time(fps, dt, flags, field_count); + // tc.and_then(|tc| tc.try_into().ok()) + // } + + pub fn add_frames(&mut self, frames: i64) { + unsafe { + ffi::gst_video_time_code_add_frames(self.to_glib_none_mut().0, frames); + } + } + + #[cfg(any(feature = "v1_12", feature = "dox"))] + pub fn add_interval(&self, tc_inter: &VideoTimeCodeInterval) -> Option { + unsafe { + from_glib_full(ffi::gst_video_time_code_add_interval( + self.to_glib_none().0, + tc_inter.to_glib_none().0, + )) + } + } + + fn compare(&self, tc2: &ValidVideoTimeCode) -> i32 { + unsafe { ffi::gst_video_time_code_compare(self.to_glib_none().0, tc2.to_glib_none().0) } + } + + pub fn frames_since_daily_jam(&self) -> u64 { + unsafe { ffi::gst_video_time_code_frames_since_daily_jam(self.to_glib_none().0) } + } + + pub fn increment_frame(&mut self) { + unsafe { + ffi::gst_video_time_code_increment_frame(self.to_glib_none_mut().0); + } + } + + pub fn nsec_since_daily_jam(&self) -> u64 { + unsafe { ffi::gst_video_time_code_nsec_since_daily_jam(self.to_glib_none().0) } + } + + pub fn to_date_time(&self) -> Option { + unsafe { from_glib_full(ffi::gst_video_time_code_to_date_time(self.to_glib_none().0)) } + } +} + +macro_rules! generic_impl { + ($name:ident) => { + impl $name { + pub fn to_string(&self) -> String { + unsafe { from_glib_full(ffi::gst_video_time_code_to_string(self.to_glib_none().0)) } + } + + pub fn get_hours(&self) -> u32 { + self.0.hours + } + + pub fn get_minutes(&self) -> u32 { + self.0.minutes + } + + pub fn get_seconds(&self) -> u32 { + self.0.seconds + } + + pub fn get_frames(&self) -> u32 { + self.0.frames + } + + pub fn get_field_count(&self) -> u32 { + self.0.field_count + } + + pub fn get_fps(&self) -> gst::Fraction { + (self.0.config.fps_n as i32, self.0.config.fps_d as i32).into() + } + + pub fn get_flags(&self) -> VideoTimeCodeFlags { + from_glib(self.0.config.flags) + } + + pub fn get_latest_daily_jam(&self) -> Option { + unsafe { from_glib_none(self.0.config.latest_daily_jam) } + } + + pub fn set_latest_daily_jam(&mut self, latest_daily_jam: Option<&glib::DateTime>) { + unsafe { + if !self.0.config.latest_daily_jam.is_null() { + glib_ffi::g_date_time_unref(self.0.config.latest_daily_jam); + } + + self.0.config.latest_daily_jam = latest_daily_jam.to_glib_full() + } + } + } + + impl Clone for $name { + fn clone(&self) -> Self { + unsafe { + let v = self.0; + if !v.config.latest_daily_jam.is_null() { + glib_ffi::g_date_time_ref(v.config.latest_daily_jam); + } + + $name(v) + } + } + } + + impl Drop for $name { + fn drop(&mut self) { + unsafe { + if !self.0.config.latest_daily_jam.is_null() { + glib_ffi::g_date_time_unref(self.0.config.latest_daily_jam); + } + } + } + } + + impl fmt::Debug for $name { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + f.debug_struct("$name") + .field("fps", &self.get_fps()) + .field("flags", &self.get_flags()) + .field("latest_daily_jam", &self.get_latest_daily_jam()) + .field("hours", &self.get_hours()) + .field("minutes", &self.get_minutes()) + .field("seconds", &self.get_seconds()) + .field("frames", &self.get_frames()) + .field("field_count", &self.get_field_count()) + .finish() + } + } + + impl fmt::Display for $name { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.to_string()) + } + } + + unsafe impl Send for $name {} + unsafe impl Sync for $name {} + + #[doc(hidden)] + impl GlibPtrDefault for $name { + type GlibType = *mut ffi::GstVideoTimeCode; + } + + #[doc(hidden)] + impl<'a> ToGlibPtr<'a, *const ffi::GstVideoTimeCode> for $name { + type Storage = &'a Self; + + #[inline] + fn to_glib_none(&'a self) -> Stash<'a, *const ffi::GstVideoTimeCode, Self> { + Stash(&self.0 as *const _, self) + } + + #[inline] + fn to_glib_full(&self) -> *const ffi::GstVideoTimeCode { + unsafe { ffi::gst_video_time_code_copy(&self.0 as *const _) } + } + } + + #[doc(hidden)] + impl<'a> ToGlibPtrMut<'a, *mut ffi::GstVideoTimeCode> for $name { + type Storage = &'a mut Self; + + #[inline] + fn to_glib_none_mut(&'a mut self) -> StashMut<'a, *mut ffi::GstVideoTimeCode, Self> { + let ptr = &mut self.0 as *mut _; + StashMut(ptr, self) + } + } + + #[doc(hidden)] + impl FromGlibPtrNone<*mut ffi::GstVideoTimeCode> for $name { + #[inline] + unsafe fn from_glib_none(ptr: *mut ffi::GstVideoTimeCode) -> Self { + assert!(!ptr.is_null()); + let v = ptr::read(ptr); + if !v.config.latest_daily_jam.is_null() { + glib_ffi::g_date_time_ref(v.config.latest_daily_jam); + } + + $name(v) + } + } + + #[doc(hidden)] + impl FromGlibPtrNone<*const ffi::GstVideoTimeCode> for $name { + #[inline] + unsafe fn from_glib_none(ptr: *const ffi::GstVideoTimeCode) -> Self { + assert!(!ptr.is_null()); + let v = ptr::read(ptr); + if !v.config.latest_daily_jam.is_null() { + glib_ffi::g_date_time_ref(v.config.latest_daily_jam); + } + + $name(v) + } + } + + #[doc(hidden)] + impl FromGlibPtrFull<*mut ffi::GstVideoTimeCode> for $name { + #[inline] + unsafe fn from_glib_full(ptr: *mut ffi::GstVideoTimeCode) -> Self { + assert!(!ptr.is_null()); + let v = ptr::read(ptr); + if !v.config.latest_daily_jam.is_null() { + glib_ffi::g_date_time_ref(v.config.latest_daily_jam); + } + ffi::gst_video_time_code_free(ptr); + + $name(v) + } + } + + #[doc(hidden)] + impl FromGlibPtrBorrow<*mut ffi::GstVideoTimeCode> for $name { + #[inline] + unsafe fn from_glib_borrow(ptr: *mut ffi::GstVideoTimeCode) -> Self { + assert!(!ptr.is_null()); + let v = ptr::read(ptr); + if !v.config.latest_daily_jam.is_null() { + glib_ffi::g_date_time_ref(v.config.latest_daily_jam); + } + + $name(v) + } + } + + impl StaticType for $name { + fn static_type() -> glib::Type { + unsafe { from_glib(ffi::gst_video_time_code_get_type()) } + } + } + + #[doc(hidden)] + impl<'a> value::FromValueOptional<'a> for $name { + unsafe fn from_value_optional(value: &glib::Value) -> Option { + Option::<$name>::from_glib_none(gobject_ffi::g_value_get_boxed( + value.to_glib_none().0, + ) as *mut ffi::GstVideoTimeCode) + } + } + + #[doc(hidden)] + impl value::SetValue for $name { + unsafe fn set_value(value: &mut glib::Value, this: &Self) { + gobject_ffi::g_value_set_boxed( + value.to_glib_none_mut().0, + ToGlibPtr::<*const ffi::GstVideoTimeCode>::to_glib_none(this).0 + as glib_ffi::gpointer, + ) + } + } + + #[doc(hidden)] + impl value::SetValueOptional for $name { + unsafe fn set_value_optional(value: &mut glib::Value, this: Option<&Self>) { + gobject_ffi::g_value_set_boxed( + value.to_glib_none_mut().0, + ToGlibPtr::<*const ffi::GstVideoTimeCode>::to_glib_none(&this).0 + as glib_ffi::gpointer, + ) + } + } + }; +} + +generic_impl!(VideoTimeCode); +generic_impl!(ValidVideoTimeCode); + +impl str::FromStr for VideoTimeCode { + type Err = (); + + fn from_str(s: &str) -> Result { + skip_assert_initialized!(); + Self::from_string(s).ok_or(()) + } +} + +impl PartialEq for ValidVideoTimeCode { + #[inline] + fn eq(&self, other: &Self) -> bool { + self.compare(other) == 0 + } +} + +impl Eq for ValidVideoTimeCode {} + +impl PartialOrd for ValidVideoTimeCode { + #[inline] + fn partial_cmp(&self, other: &Self) -> Option { + self.compare(other).partial_cmp(&0) + } +} + +impl Ord for ValidVideoTimeCode { + #[inline] + fn cmp(&self, other: &Self) -> cmp::Ordering { + self.compare(other).cmp(&0) + } +} + +impl From for VideoTimeCode { + fn from(v: ValidVideoTimeCode) -> VideoTimeCode { + VideoTimeCode(v.0) + } +} diff --git a/gstreamer-video/src/video_time_code_interval.rs b/gstreamer-video/src/video_time_code_interval.rs new file mode 100644 index 000000000..a84b08927 --- /dev/null +++ b/gstreamer-video/src/video_time_code_interval.rs @@ -0,0 +1,239 @@ +// Copyright (C) 2018 Sebastian Dröge +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use ffi; +use glib; +use glib::prelude::*; +use glib::translate::*; +use glib::value; +use glib_ffi; +use gobject_ffi; +use gst; +use std::cmp; +use std::fmt; +use std::mem; +use std::ptr; + +#[derive(Clone)] +pub struct VideoTimeCodeInterval(ffi::GstVideoTimeCodeInterval); + +impl VideoTimeCodeInterval { + pub fn from_string(tc_inter_str: &str) -> Option { + assert_initialized_main_thread!(); + unsafe { + from_glib_full(ffi::gst_video_time_code_interval_new_from_string( + tc_inter_str.to_glib_none().0, + )) + } + } + + pub fn new(&mut self, hours: u32, minutes: u32, seconds: u32, frames: u32) -> Self { + assert_initialized_main_thread!(); + unsafe { + let mut v = mem::zeroed(); + ffi::gst_video_time_code_interval_init(&mut v, hours, minutes, seconds, frames); + VideoTimeCodeInterval(v) + } + } + + pub fn get_hours(&self) -> u32 { + self.0.hours + } + + pub fn set_hours(&mut self, hours: u32) { + self.0.hours = hours + } + + pub fn get_minutes(&self) -> u32 { + self.0.minutes + } + + pub fn set_minutes(&mut self, minutes: u32) { + assert!(minutes < 60); + self.0.minutes = minutes + } + + pub fn get_seconds(&self) -> u32 { + self.0.seconds + } + + pub fn set_seconds(&mut self, seconds: u32) { + assert!(seconds < 60); + self.0.seconds = seconds + } + + pub fn get_frames(&self) -> u32 { + self.0.frames + } + + pub fn set_frames(&mut self, hours: u32) { + self.0.frames = hours + } +} + +unsafe impl Send for VideoTimeCodeInterval {} +unsafe impl Sync for VideoTimeCodeInterval {} + +impl PartialEq for VideoTimeCodeInterval { + fn eq(&self, other: &Self) -> bool { + self.0.hours == other.0.hours + && self.0.minutes == other.0.hours + && self.0.seconds == other.0.seconds + && self.0.frames == other.0.frames + } +} + +impl Eq for VideoTimeCodeInterval {} + +impl PartialOrd for VideoTimeCodeInterval { + #[inline] + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for VideoTimeCodeInterval { + #[inline] + fn cmp(&self, other: &Self) -> cmp::Ordering { + self.0 + .hours + .cmp(&other.0.hours) + .then_with(|| self.0.minutes.cmp(&other.0.hours)) + .then_with(|| self.0.seconds.cmp(&other.0.seconds)) + .then_with(|| self.0.frames.cmp(&other.0.frames)) + } +} + +impl fmt::Debug for VideoTimeCodeInterval { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + f.debug_struct("VideoTimeCodeInterval") + .field("hours", &self.0.hours) + .field("minutes", &self.0.minutes) + .field("seconds", &self.0.seconds) + .field("frames", &self.0.frames) + .finish() + } +} + +impl fmt::Display for VideoTimeCodeInterval { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + write!( + f, + "{:02}:{:02}:{:02}:{:02}", + self.0.hours, self.0.minutes, self.0.seconds, self.0.frames + ) + } +} + +#[doc(hidden)] +impl GlibPtrDefault for VideoTimeCodeInterval { + type GlibType = *mut ffi::GstVideoTimeCodeInterval; +} + +#[doc(hidden)] +impl<'a> ToGlibPtr<'a, *const ffi::GstVideoTimeCodeInterval> for VideoTimeCodeInterval { + type Storage = &'a Self; + + #[inline] + fn to_glib_none(&'a self) -> Stash<'a, *const ffi::GstVideoTimeCodeInterval, Self> { + Stash(&self.0 as *const _, self) + } + + #[inline] + fn to_glib_full(&self) -> *const ffi::GstVideoTimeCodeInterval { + unsafe { ffi::gst_video_time_code_interval_copy(&self.0 as *const _) } + } +} + +#[doc(hidden)] +impl<'a> ToGlibPtrMut<'a, *mut ffi::GstVideoTimeCodeInterval> for VideoTimeCodeInterval { + type Storage = &'a mut Self; + + #[inline] + fn to_glib_none_mut(&'a mut self) -> StashMut<'a, *mut ffi::GstVideoTimeCodeInterval, Self> { + let ptr = &mut self.0 as *mut _; + StashMut(ptr, self) + } +} + +#[doc(hidden)] +impl FromGlibPtrNone<*mut ffi::GstVideoTimeCodeInterval> for VideoTimeCodeInterval { + #[inline] + unsafe fn from_glib_none(ptr: *mut ffi::GstVideoTimeCodeInterval) -> Self { + assert!(!ptr.is_null()); + VideoTimeCodeInterval(ptr::read(ptr)) + } +} + +#[doc(hidden)] +impl FromGlibPtrNone<*const ffi::GstVideoTimeCodeInterval> for VideoTimeCodeInterval { + #[inline] + unsafe fn from_glib_none(ptr: *const ffi::GstVideoTimeCodeInterval) -> Self { + assert!(!ptr.is_null()); + VideoTimeCodeInterval(ptr::read(ptr)) + } +} + +#[doc(hidden)] +impl FromGlibPtrFull<*mut ffi::GstVideoTimeCodeInterval> for VideoTimeCodeInterval { + #[inline] + unsafe fn from_glib_full(ptr: *mut ffi::GstVideoTimeCodeInterval) -> Self { + assert!(!ptr.is_null()); + let res = VideoTimeCodeInterval(ptr::read(ptr)); + ffi::gst_video_time_code_interval_free(ptr); + + res + } +} + +#[doc(hidden)] +impl FromGlibPtrBorrow<*mut ffi::GstVideoTimeCodeInterval> for VideoTimeCodeInterval { + #[inline] + unsafe fn from_glib_borrow(ptr: *mut ffi::GstVideoTimeCodeInterval) -> Self { + assert!(!ptr.is_null()); + VideoTimeCodeInterval(ptr::read(ptr)) + } +} + +impl StaticType for VideoTimeCodeInterval { + fn static_type() -> glib::Type { + unsafe { from_glib(ffi::gst_video_time_code_interval_get_type()) } + } +} + +#[doc(hidden)] +impl<'a> value::FromValueOptional<'a> for VideoTimeCodeInterval { + unsafe fn from_value_optional(value: &glib::Value) -> Option { + Option::::from_glib_full(gobject_ffi::g_value_dup_boxed( + value.to_glib_none().0, + ) + as *mut ffi::GstVideoTimeCodeInterval) + } +} + +#[doc(hidden)] +impl value::SetValue for VideoTimeCodeInterval { + unsafe fn set_value(value: &mut glib::Value, this: &Self) { + gobject_ffi::g_value_set_boxed( + value.to_glib_none_mut().0, + ToGlibPtr::<*const ffi::GstVideoTimeCodeInterval>::to_glib_none(this).0 + as glib_ffi::gpointer, + ) + } +} + +#[doc(hidden)] +impl value::SetValueOptional for VideoTimeCodeInterval { + unsafe fn set_value_optional(value: &mut glib::Value, this: Option<&Self>) { + gobject_ffi::g_value_set_boxed( + value.to_glib_none_mut().0, + ToGlibPtr::<*const ffi::GstVideoTimeCodeInterval>::to_glib_none(&this).0 + as glib_ffi::gpointer, + ) + } +}