diff --git a/Gir_Gst.toml b/Gir_Gst.toml index 3c0925e98..352febbd5 100644 --- a/Gir_Gst.toml +++ b/Gir_Gst.toml @@ -60,6 +60,10 @@ generate = [ "Gst.TagSetter", "Gst.QOSType", "Gst.EventType", + "Gst.TocScope", + "Gst.TocEntryType", + "Gst.TocLoopType", + "Gst.TocSetter", ] manual = [ @@ -161,6 +165,11 @@ name = "Gst.Context" status = "manual" ref_mode = "ref" +[[object]] +name = "Gst.Toc" +status = "manual" +ref_mode = "ref" + [[object]] name = "Gst.DateTime" status = "generate" diff --git a/examples/src/bin/toc.rs b/examples/src/bin/toc.rs new file mode 100644 index 000000000..1933c9882 --- /dev/null +++ b/examples/src/bin/toc.rs @@ -0,0 +1,143 @@ +extern crate gstreamer as gst; +use gst::*; + +extern crate glib; +use glib::*; + +use std::env; +use std::u64; + +fn main() { + gst::init().unwrap(); + + let args: Vec<_> = env::args().collect(); + let uri: &str = if args.len() == 2 { + args[1].as_ref() + } else { + panic!("Usage: toc file_path"); + }; + + let pipeline = gst::Pipeline::new(None); + let src = gst::ElementFactory::make("filesrc", None).unwrap(); + let decodebin = gst::ElementFactory::make("decodebin", None).unwrap(); + + src.set_property("location", &Value::from(uri)).unwrap(); + + pipeline.add_many(&[&src, &decodebin]).unwrap(); + gst::Element::link_many(&[&src, &decodebin]).unwrap(); + + // Need to move a new reference into the closure + let pipeline_clone = pipeline.clone(); + decodebin.connect_pad_added(move |_, src_pad| { + let pipeline = &pipeline_clone; + + let (is_audio, is_video) = { + let caps = src_pad.get_current_caps().unwrap(); + let structure = caps.get_structure(0).unwrap(); + let name = structure.get_name(); + + (name.starts_with("audio/"), name.starts_with("video/")) + }; + + if is_audio { + let queue = gst::ElementFactory::make("queue", None).unwrap(); + let sink = gst::ElementFactory::make("fakesink", None).unwrap(); + + let elements = &[&queue, &sink]; + pipeline.add_many(elements).unwrap(); + gst::Element::link_many(elements).unwrap(); + + for e in elements { + e.sync_state_with_parent().unwrap(); + } + + let sink_pad = queue.get_static_pad("sink").unwrap(); + assert_eq!(src_pad.link(&sink_pad), gst::PadLinkReturn::Ok); + } else if is_video { + let queue = gst::ElementFactory::make("queue", None).unwrap(); + let sink = gst::ElementFactory::make("fakesink", None).unwrap(); + + let elements = &[&queue, &sink]; + pipeline.add_many(elements).unwrap(); + gst::Element::link_many(elements).unwrap(); + + for e in elements { + e.sync_state_with_parent().unwrap(); + } + + let sink_pad = queue.get_static_pad("sink").unwrap(); + assert_eq!(src_pad.link(&sink_pad), gst::PadLinkReturn::Ok); + } + }); + + assert_ne!( + pipeline.set_state(gst::State::Paused), + gst::StateChangeReturn::Failure + ); + + let bus = pipeline.get_bus().unwrap(); + + loop { + let msg = match bus.timed_pop(u64::MAX) { + None => break, + Some(msg) => msg, + }; + + match msg.view() { + MessageView::Eos(..) => break, + MessageView::Error(err) => { + println!( + "Error from {}: {} ({:?})", + msg.get_src().get_path_string(), + err.get_error(), + err.get_debug() + ); + break; + } + MessageView::AsyncDone(_) => break, + MessageView::Toc(msg_toc) => { + let (toc, updated) = msg_toc.get_toc(); + println!( + "\nReceived toc: {:?} - updated: {}", + toc.get_scope(), + updated + ); + if let Some(tags) = toc.get_tags() { + println!("- tags: {}", tags.to_string()); + } + for toc_entry in toc.get_entries() { + println!( + "\t{:?} - {}", + toc_entry.get_entry_type(), + toc_entry.get_uid() + ); + if let Some((start, stop)) = toc_entry.get_start_stop_times() { + println!("\t- start: {}, stop: {}", start, stop); + } + if let Some(tags) = toc_entry.get_tags() { + println!("\t- tags: {}", tags.to_string()); + } + for toc_sub_entry in toc_entry.get_sub_entries() { + println!( + "\n\t\t{:?} - {}", + toc_sub_entry.get_entry_type(), + toc_sub_entry.get_uid() + ); + if let Some((start, stop)) = toc_sub_entry.get_start_stop_times() { + println!("\t\t- start: {}, stop: {}", start, stop); + } + if let Some(tags) = toc_sub_entry.get_tags() { + println!("\t\t- tags: {:?}", tags.to_string()); + } + } + } + } + _ => (), + } + } + + assert_ne!( + pipeline.set_state(gst::State::Null), + gst::StateChangeReturn::Failure + ); +} diff --git a/gstreamer/src/auto/enums.rs b/gstreamer/src/auto/enums.rs index 4b031dfc7..cbb7f5234 100644 --- a/gstreamer/src/auto/enums.rs +++ b/gstreamer/src/auto/enums.rs @@ -2126,6 +2126,198 @@ impl SetValue for TagMergeMode { } } +#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] +pub enum TocEntryType { + Angle, + Version, + Edition, + Invalid, + Title, + Track, + Chapter, + #[doc(hidden)] + __Unknown(i32), +} + +#[doc(hidden)] +impl ToGlib for TocEntryType { + type GlibType = ffi::GstTocEntryType; + + fn to_glib(&self) -> ffi::GstTocEntryType { + match *self { + TocEntryType::Angle => ffi::GST_TOC_ENTRY_TYPE_ANGLE, + TocEntryType::Version => ffi::GST_TOC_ENTRY_TYPE_VERSION, + TocEntryType::Edition => ffi::GST_TOC_ENTRY_TYPE_EDITION, + TocEntryType::Invalid => ffi::GST_TOC_ENTRY_TYPE_INVALID, + TocEntryType::Title => ffi::GST_TOC_ENTRY_TYPE_TITLE, + TocEntryType::Track => ffi::GST_TOC_ENTRY_TYPE_TRACK, + TocEntryType::Chapter => ffi::GST_TOC_ENTRY_TYPE_CHAPTER, + TocEntryType::__Unknown(value) => unsafe{std::mem::transmute(value)} + } + } +} + +#[doc(hidden)] +impl FromGlib for TocEntryType { + fn from_glib(value: ffi::GstTocEntryType) -> Self { + skip_assert_initialized!(); + match value as i32 { + -3 => TocEntryType::Angle, + -2 => TocEntryType::Version, + -1 => TocEntryType::Edition, + 0 => TocEntryType::Invalid, + 1 => TocEntryType::Title, + 2 => TocEntryType::Track, + 3 => TocEntryType::Chapter, + value => TocEntryType::__Unknown(value), + } + } +} + +impl StaticType for TocEntryType { + fn static_type() -> Type { + unsafe { from_glib(ffi::gst_toc_entry_type_get_type()) } + } +} + +impl<'a> FromValueOptional<'a> for TocEntryType { + unsafe fn from_value_optional(value: &Value) -> Option { + Some(FromValue::from_value(value)) + } +} + +impl<'a> FromValue<'a> for TocEntryType { + unsafe fn from_value(value: &Value) -> Self { + from_glib(std::mem::transmute::(gobject_ffi::g_value_get_enum(value.to_glib_none().0))) + } +} + +impl SetValue for TocEntryType { + unsafe fn set_value(value: &mut Value, this: &Self) { + gobject_ffi::g_value_set_enum(value.to_glib_none_mut().0, this.to_glib() as i32) + } +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] +pub enum TocLoopType { + None, + Forward, + Reverse, + PingPong, + #[doc(hidden)] + __Unknown(i32), +} + +#[doc(hidden)] +impl ToGlib for TocLoopType { + type GlibType = ffi::GstTocLoopType; + + fn to_glib(&self) -> ffi::GstTocLoopType { + match *self { + TocLoopType::None => ffi::GST_TOC_LOOP_NONE, + TocLoopType::Forward => ffi::GST_TOC_LOOP_FORWARD, + TocLoopType::Reverse => ffi::GST_TOC_LOOP_REVERSE, + TocLoopType::PingPong => ffi::GST_TOC_LOOP_PING_PONG, + TocLoopType::__Unknown(value) => unsafe{std::mem::transmute(value)} + } + } +} + +#[doc(hidden)] +impl FromGlib for TocLoopType { + fn from_glib(value: ffi::GstTocLoopType) -> Self { + skip_assert_initialized!(); + match value as i32 { + 0 => TocLoopType::None, + 1 => TocLoopType::Forward, + 2 => TocLoopType::Reverse, + 3 => TocLoopType::PingPong, + value => TocLoopType::__Unknown(value), + } + } +} + +impl StaticType for TocLoopType { + fn static_type() -> Type { + unsafe { from_glib(ffi::gst_toc_loop_type_get_type()) } + } +} + +impl<'a> FromValueOptional<'a> for TocLoopType { + unsafe fn from_value_optional(value: &Value) -> Option { + Some(FromValue::from_value(value)) + } +} + +impl<'a> FromValue<'a> for TocLoopType { + unsafe fn from_value(value: &Value) -> Self { + from_glib(std::mem::transmute::(gobject_ffi::g_value_get_enum(value.to_glib_none().0))) + } +} + +impl SetValue for TocLoopType { + unsafe fn set_value(value: &mut Value, this: &Self) { + gobject_ffi::g_value_set_enum(value.to_glib_none_mut().0, this.to_glib() as i32) + } +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] +pub enum TocScope { + Global, + Current, + #[doc(hidden)] + __Unknown(i32), +} + +#[doc(hidden)] +impl ToGlib for TocScope { + type GlibType = ffi::GstTocScope; + + fn to_glib(&self) -> ffi::GstTocScope { + match *self { + TocScope::Global => ffi::GST_TOC_SCOPE_GLOBAL, + TocScope::Current => ffi::GST_TOC_SCOPE_CURRENT, + TocScope::__Unknown(value) => unsafe{std::mem::transmute(value)} + } + } +} + +#[doc(hidden)] +impl FromGlib for TocScope { + fn from_glib(value: ffi::GstTocScope) -> Self { + skip_assert_initialized!(); + match value as i32 { + 1 => TocScope::Global, + 2 => TocScope::Current, + value => TocScope::__Unknown(value), + } + } +} + +impl StaticType for TocScope { + fn static_type() -> Type { + unsafe { from_glib(ffi::gst_toc_scope_get_type()) } + } +} + +impl<'a> FromValueOptional<'a> for TocScope { + unsafe fn from_value_optional(value: &Value) -> Option { + Some(FromValue::from_value(value)) + } +} + +impl<'a> FromValue<'a> for TocScope { + unsafe fn from_value(value: &Value) -> Self { + from_glib(std::mem::transmute::(gobject_ffi::g_value_get_enum(value.to_glib_none().0))) + } +} + +impl SetValue for TocScope { + unsafe fn set_value(value: &mut Value, this: &Self) { + gobject_ffi::g_value_set_enum(value.to_glib_none_mut().0, this.to_glib() as i32) + } +} + #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] pub enum URIError { UnsupportedProtocol, diff --git a/gstreamer/src/auto/mod.rs b/gstreamer/src/auto/mod.rs index 53c6a8134..544c647fc 100644 --- a/gstreamer/src/auto/mod.rs +++ b/gstreamer/src/auto/mod.rs @@ -88,6 +88,10 @@ mod tag_setter; pub use self::tag_setter::TagSetter; pub use self::tag_setter::TagSetterExt; +mod toc_setter; +pub use self::toc_setter::TocSetter; +pub use self::toc_setter::TocSetterExt; + mod u_r_i_handler; pub use self::u_r_i_handler::URIHandler; pub use self::u_r_i_handler::URIHandlerExt; @@ -123,6 +127,9 @@ pub use self::enums::StreamError; pub use self::enums::StreamStatusType; pub use self::enums::StructureChangeType; pub use self::enums::TagMergeMode; +pub use self::enums::TocEntryType; +pub use self::enums::TocLoopType; +pub use self::enums::TocScope; pub use self::enums::URIError; pub use self::enums::URIType; @@ -229,5 +236,6 @@ pub mod traits { #[cfg(feature = "v1_10")] pub use super::StreamCollectionExt; pub use super::TagSetterExt; + pub use super::TocSetterExt; pub use super::URIHandlerExt; } diff --git a/gstreamer/src/auto/toc_setter.rs b/gstreamer/src/auto/toc_setter.rs new file mode 100644 index 000000000..a6ff3460a --- /dev/null +++ b/gstreamer/src/auto/toc_setter.rs @@ -0,0 +1,54 @@ +// This file was generated by gir (3294959) from gir-files (???) +// DO NOT EDIT + +use Element; +use Object; +use Toc; +use ffi; +use glib::object::IsA; +use glib::translate::*; +use glib_ffi; +use gobject_ffi; +use std::mem; +use std::ptr; + +glib_wrapper! { + pub struct TocSetter(Object): Element, Object; + + match fn { + get_type => || ffi::gst_toc_setter_get_type(), + } +} + +unsafe impl Send for TocSetter {} +unsafe impl Sync for TocSetter {} + +pub trait TocSetterExt { + fn get_toc(&self) -> Option; + + fn reset(&self); + + fn set_toc<'a, P: Into>>(&self, toc: P); +} + +impl> TocSetterExt for O { + fn get_toc(&self) -> Option { + unsafe { + from_glib_full(ffi::gst_toc_setter_get_toc(self.to_glib_none().0)) + } + } + + fn reset(&self) { + unsafe { + ffi::gst_toc_setter_reset(self.to_glib_none().0); + } + } + + fn set_toc<'a, P: Into>>(&self, toc: P) { + let toc = toc.into(); + let toc = toc.to_glib_none(); + unsafe { + ffi::gst_toc_setter_set_toc(self.to_glib_none().0, toc.0); + } + } +} diff --git a/gstreamer/src/event.rs b/gstreamer/src/event.rs index 2986c7f08..143831063 100644 --- a/gstreamer/src/event.rs +++ b/gstreamer/src/event.rs @@ -187,7 +187,7 @@ impl Event { EosBuilder::new() } - pub fn new_toc(toc: (), updated: bool) -> TocBuilder { + pub fn new_toc(toc: &::Toc, updated: bool) -> TocBuilder { TocBuilder::new(toc, updated) } @@ -468,8 +468,18 @@ impl<'a> StreamGroupDone<'a> { pub struct Eos<'a>(&'a EventRef); -// TODO pub struct Toc<'a>(&'a EventRef); +impl<'a> Toc<'a> { + pub fn get_toc(&self) -> (&'a ::TocRef, bool) { + unsafe { + let mut toc = ptr::null_mut(); + let mut updated = mem::uninitialized(); + + ffi::gst_event_parse_toc(self.0.as_mut_ptr(), &mut toc, &mut updated); + (::TocRef::from_ptr(toc), from_glib(updated)) + } + } +} pub struct Protection<'a>(&'a EventRef); impl<'a> Protection<'a> { @@ -932,16 +942,14 @@ impl EosBuilder { event_builder_generic_impl!(|_| ffi::gst_event_new_eos()); } -// TODO Toc -pub struct TocBuilder { +pub struct TocBuilder<'a> { seqnum: Option, running_time_offset: Option, - #[allow(unused)] - toc: (), + toc: &'a ::Toc, updated: bool, } -impl TocBuilder { - pub fn new(toc: (), updated: bool) -> Self { +impl<'a> TocBuilder<'a> { + pub fn new(toc: &'a ::Toc, updated: bool) -> Self { Self { seqnum: None, running_time_offset: None, @@ -951,7 +959,7 @@ impl TocBuilder { } event_builder_generic_impl!(|s: &Self| { - ffi::gst_event_new_toc(ptr::null_mut(), s.updated.to_glib()) + ffi::gst_event_new_toc(s.toc.to_glib_none().0, s.updated.to_glib()) }); } diff --git a/gstreamer/src/lib.rs b/gstreamer/src/lib.rs index 67f256b33..1df804bbd 100644 --- a/gstreamer/src/lib.rs +++ b/gstreamer/src/lib.rs @@ -100,6 +100,9 @@ pub use value::*; mod segment; pub use segment::*; +pub mod toc; +pub use toc::{Toc, TocEntry, TocEntryRef, TocRef}; + use std::ptr; pub fn init() -> Result<(), glib::Error> { diff --git a/gstreamer/src/message.rs b/gstreamer/src/message.rs index 0b6d4b287..36c9da1c2 100644 --- a/gstreamer/src/message.rs +++ b/gstreamer/src/message.rs @@ -249,7 +249,7 @@ impl Message { QosBuilder::new(live, running_time, stream_time, timestamp, duration) } - pub fn new_toc(toc: (), updated: bool) -> TocBuilder { + pub fn new_toc(toc: &::Toc, updated: bool) -> TocBuilder { TocBuilder::new(toc, updated) } @@ -878,7 +878,14 @@ impl<'a> Progress<'a> { pub struct Toc<'a>(&'a MessageRef); impl<'a> Toc<'a> { - // TODO get_toc() + pub fn get_toc(&self) -> (::Toc, bool) { + unsafe { + let mut toc = ptr::null_mut(); + let mut updated = mem::uninitialized(); + ffi::gst_message_parse_toc(self.0.as_mut_ptr(), &mut toc, &mut updated); + (from_glib_full(toc), from_glib(updated)) + } + } } pub struct ResetTime<'a>(&'a MessageRef); @@ -1895,16 +1902,14 @@ impl<'a> ProgressBuilder<'a> { }); } -// TODO Toc -pub struct TocBuilder { +pub struct TocBuilder<'a> { src: Option, seqnum: Option, - #[allow(unused)] - toc: (), + toc: &'a ::Toc, updated: bool, } -impl TocBuilder { - pub fn new(toc: () /* &'a Toc */, updated: bool) -> Self { +impl<'a> TocBuilder<'a> { + pub fn new(toc: &'a ::Toc, updated: bool) -> Self { Self { src: None, seqnum: None, @@ -1913,12 +1918,8 @@ impl TocBuilder { } } - message_builder_generic_impl!(|s: &mut Self, src| { - ffi::gst_message_new_toc( - src, - ptr::null_mut(), /*s.structure.to_glib_full()*/ - s.updated.to_glib(), - ) + message_builder_generic_impl!(|s: &Self, src| { + ffi::gst_message_new_toc(src, s.toc.to_glib_none().0, s.updated.to_glib()) }); } diff --git a/gstreamer/src/toc.rs b/gstreamer/src/toc.rs new file mode 100644 index 000000000..ba0628241 --- /dev/null +++ b/gstreamer/src/toc.rs @@ -0,0 +1,316 @@ +// Copyright (C) 2016-2017 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 std::ffi::CStr; +use std::mem; + +use ffi; + +use glib; +use glib::translate::{from_glib, from_glib_full, FromGlibPtrContainer, ToGlib, ToGlibPtr}; + +use miniobject::*; +use TocEntryType; +use TocScope; +use TocLoopType; +use TagList; +use TagMergeMode; + +pub type Toc = GstRc; +pub struct TocRef(ffi::GstToc); + +unsafe impl MiniObject for TocRef { + type GstType = ffi::GstToc; +} + +impl GstRc { + pub fn new(scope: TocScope) -> Self { + assert_initialized_main_thread!(); + unsafe { from_glib_full(ffi::gst_toc_new(scope.to_glib())) } + } +} + +impl TocRef { + pub fn get_scope(&self) -> TocScope { + unsafe { from_glib(ffi::gst_toc_get_scope(self.as_ptr())) } + } + + pub fn find_entry(&self, uid: &str) -> Option { + unsafe { + let toc_entry = ffi::gst_toc_find_entry(self.as_ptr(), uid.to_glib_none().0); + if toc_entry.is_null() { + return None; + } + + Some(TocEntry::from_glib_none( + toc_entry as *const ffi::GstTocEntry, + )) + } + } + + pub fn get_entries(&self) -> Vec { + unsafe { FromGlibPtrContainer::from_glib_none(ffi::gst_toc_get_entries(self.as_ptr())) } + } + + pub fn append_entry(&mut self, entry: TocEntry) { + unsafe { + ffi::gst_toc_append_entry(self.as_mut_ptr(), entry.into_ptr()); + } + } + + pub fn get_tags(&self) -> Option { + unsafe { + let tags = ffi::gst_toc_get_tags(self.as_ptr()); + if tags.is_null() { + return None; + } + + Some(TagList::from_glib_none(tags as *const ffi::GstTagList)) + } + } + + pub fn set_tags(&mut self, tag_list: TagList) { + unsafe { + ffi::gst_toc_set_tags(self.as_mut_ptr(), tag_list.into_ptr()); + } + } + + pub fn merge_tags(&mut self, tag_list: TagList, mode: TagMergeMode) { + unsafe { + ffi::gst_toc_merge_tags(self.as_mut_ptr(), tag_list.into_ptr(), mode.to_glib()); + } + } + + pub fn dump(&self) { + unsafe { + ffi::gst_toc_dump(self.as_mut_ptr()); + } + } +} + +impl glib::types::StaticType for GstRc { + fn static_type() -> glib::types::Type { + unsafe { from_glib(ffi::gst_toc_get_type()) } + } +} + +impl ToOwned for TocRef { + type Owned = GstRc; + + fn to_owned(&self) -> GstRc { + unsafe { + from_glib_full(ffi::gst_mini_object_copy(self.as_ptr() as *const _) as + *mut _) + } + } +} + +unsafe impl Sync for TocRef {} +unsafe impl Send for TocRef {} + +pub type TocEntry = GstRc; +pub struct TocEntryRef(ffi::GstTocEntry); + +unsafe impl MiniObject for TocEntryRef { + type GstType = ffi::GstTocEntry; +} + +impl GstRc { + pub fn new(type_: TocEntryType, uid: &str) -> Self { + assert_initialized_main_thread!(); + unsafe { + from_glib_full(ffi::gst_toc_entry_new( + type_.to_glib(), + uid.to_glib_none().0, + )) + } + } +} + +impl TocEntryRef { + pub fn get_entry_type(&self) -> TocEntryType { + unsafe { from_glib(ffi::gst_toc_entry_get_entry_type(self.as_ptr())) } + } + + pub fn get_uid(&self) -> &str { + unsafe { + CStr::from_ptr(ffi::gst_toc_entry_get_uid(self.as_ptr())) + .to_str() + .unwrap() + } + } + + pub fn append_sub_entry(&mut self, subentry: TocEntry) { + unsafe { + ffi::gst_toc_entry_append_sub_entry(self.as_mut_ptr(), subentry.into_ptr()); + } + } + + pub fn get_sub_entries(&self) -> Vec { + unsafe { + FromGlibPtrContainer::from_glib_none(ffi::gst_toc_entry_get_sub_entries(self.as_ptr())) + } + } + + pub fn get_parent(&self) -> Option { + unsafe { + let parent = ffi::gst_toc_entry_get_parent(self.as_mut_ptr()); + if parent.is_null() { + return None; + } + + Some(TocEntry::from_glib_none(parent as *const ffi::GstTocEntry)) + } + } + + pub fn get_start_stop_times(&self) -> Option<(i64, i64)> { + unsafe { + let mut start = mem::uninitialized(); + let mut stop = mem::uninitialized(); + + if from_glib(ffi::gst_toc_entry_get_start_stop_times( + self.as_ptr(), + &mut start, + &mut stop, + )) { + Some((start, stop)) + } else { + None + } + } + } + + pub fn set_start_stop_times(&mut self, start: i64, stop: i64) { + unsafe { + ffi::gst_toc_entry_set_start_stop_times(self.as_mut_ptr(), start, stop); + } + } + + pub fn get_tags(&self) -> Option { + unsafe { + let tags = ffi::gst_toc_entry_get_tags(self.as_ptr()); + if tags.is_null() { + return None; + } + + Some(TagList::from_glib_none(tags as *const ffi::GstTagList)) + } + } + + pub fn set_tags(&mut self, tag_list: TagList) { + unsafe { + ffi::gst_toc_entry_set_tags(self.as_mut_ptr(), tag_list.into_ptr()); + } + } + + pub fn merge_tags(&mut self, tag_list: TagList, mode: TagMergeMode) { + unsafe { + ffi::gst_toc_entry_merge_tags(self.as_mut_ptr(), tag_list.as_mut_ptr(), mode.to_glib()); + } + } + + pub fn is_alternative(&self) -> bool { + unsafe { from_glib(ffi::gst_toc_entry_is_alternative(self.as_ptr())) } + } + + pub fn is_sequence(&self) -> bool { + unsafe { from_glib(ffi::gst_toc_entry_is_sequence(self.as_ptr())) } + } + + pub fn get_loop(&self) -> Option<(TocLoopType, i32)> { + unsafe { + let mut loop_type = mem::uninitialized(); + let mut repeat_count = mem::uninitialized(); + if from_glib(ffi::gst_toc_entry_get_loop( + self.as_ptr(), + &mut loop_type, + &mut repeat_count, + )) { + Some((from_glib(loop_type), repeat_count)) + } else { + None + } + } + } + + pub fn set_loop(&mut self, loop_type: TocLoopType, repeat_count: i32) { + unsafe { + ffi::gst_toc_entry_set_loop(self.as_mut_ptr(), loop_type.to_glib(), repeat_count); + } + } +} + +impl glib::types::StaticType for GstRc { + fn static_type() -> glib::types::Type { + unsafe { from_glib(ffi::gst_toc_entry_get_type()) } + } +} + +impl ToOwned for TocEntryRef { + type Owned = GstRc; + + fn to_owned(&self) -> GstRc { + unsafe { + from_glib_full(ffi::gst_mini_object_copy(self.as_ptr() as *const _) as + *mut _) + } + } +} + +unsafe impl Sync for TocEntryRef {} +unsafe impl Send for TocEntryRef {} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_simple() { + ::init().unwrap(); + + // Top level toc entry + let mut toc_entry = TocEntry::new(TocEntryType::Chapter, "chapter"); + toc_entry.get_mut().unwrap().set_start_stop_times(1, 10); + + // Toc sub entry + let toc_sub_entry = TocEntry::new(TocEntryType::Angle, "angle"); + let parent = toc_sub_entry.get_parent(); + assert!(parent.is_none()); + + // Append sub entry + toc_entry.get_mut().unwrap().append_sub_entry(toc_sub_entry); + + // Toc + let mut toc = Toc::new(TocScope::Global); + assert_eq!(toc.get_scope(), TocScope::Global); + + // Append toc entry + toc.get_mut().unwrap().append_entry(toc_entry); + assert_eq!(toc.get_scope(), TocScope::Global); + + // Check toc entries + let toc_entries = toc.get_entries(); + assert_eq!(toc_entries.len(), 1); + + let toc_parent_entry = &toc_entries[0]; + assert_eq!(toc_parent_entry.get_entry_type(), TocEntryType::Chapter); + assert_eq!(toc_parent_entry.get_uid(), "chapter"); + let start_stop_times = toc_parent_entry.get_start_stop_times(); + assert!(start_stop_times.is_some()); + assert_eq!(start_stop_times.unwrap(), (1, 10)); + + // Check sub entry + let toc_sub_entries = toc_parent_entry.get_sub_entries(); + assert_eq!(toc_sub_entries.len(), 1); + let toc_sub_entry = &toc_sub_entries[0]; + assert_eq!(toc_sub_entry.get_entry_type(), TocEntryType::Angle); + let parent = toc_sub_entry.get_parent(); + assert!(parent.is_some()); + assert_eq!(parent.unwrap().get_entry_type(), TocEntryType::Chapter); + } +}