diff --git a/gstreamer/src/enums.rs b/gstreamer/src/enums.rs index c10c079d2..f124df683 100644 --- a/gstreamer/src/enums.rs +++ b/gstreamer/src/enums.rs @@ -311,3 +311,23 @@ impl Ord for ::Rank { self.to_glib().cmp(&other.to_glib()) } } + +#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] +#[must_use] +pub enum TagError { + TypeMismatch, +} + +impl fmt::Display for TagError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Tag error: {}", self.description()) + } +} + +impl Error for TagError { + fn description(&self) -> &str { + match *self { + TagError::TypeMismatch => "The value type doesn't match with the specified Tag", + } + } +} diff --git a/gstreamer/src/lib.rs b/gstreamer/src/lib.rs index c4caa697f..9608751d5 100644 --- a/gstreamer/src/lib.rs +++ b/gstreamer/src/lib.rs @@ -154,7 +154,7 @@ pub use child_proxy::ChildProxyExtManual; pub use clock_time::ClockTime; pub use device_provider::DeviceProviderExtManual; pub use enums::{ClockError, ClockSuccess, FlowError, FlowSuccess, PadLinkError, PadLinkSuccess, - StateChangeError, StateChangeSuccess}; + StateChangeError, StateChangeSuccess, TagError}; pub use gobject::GObjectExtManualGst; pub use pad::{PadExtManual, PadProbeData, PadProbeId, PadProbeInfo}; pub use parse_context::ParseContext; diff --git a/gstreamer/src/tags.rs b/gstreamer/src/tags.rs index 8ec8c2c83..d5b95a72a 100644 --- a/gstreamer/src/tags.rs +++ b/gstreamer/src/tags.rs @@ -20,6 +20,7 @@ use glib::StaticType; use miniobject::*; use Sample; +use TagError; use TagMergeMode; pub trait Tag<'a> { @@ -188,14 +189,25 @@ impl TagListRef { } } - pub fn add_dynamic<'a, T>(&'a mut self, tag_name : &str, value: &T, mode: TagMergeMode) + pub fn add_generic( + &mut self, + tag_name : &str, + value: &T, + mode: TagMergeMode, + ) -> Result<(), TagError> where T: ToSendValue { - /* TODO: implement limiting values for tags? */ unsafe { let v = value.to_send_value(); + let tag_type: glib::Type = from_glib( + ffi::gst_tag_get_type(tag_name.as_ptr() as *const i8) + ); + if tag_type != v.type_() { + return Err(TagError::TypeMismatch) + } + ffi::gst_tag_list_add_value( self.as_mut_ptr(), mode.to_glib(), @@ -203,6 +215,8 @@ impl TagListRef { v.to_glib_none().0, ); } + + Ok(()) } pub fn get<'a, T: Tag<'a>>(&self) -> Option> { @@ -223,34 +237,32 @@ impl TagListRef { } } - pub fn n_tags<'a>(&'a self) -> i32 { - unsafe { ffi::gst_tag_list_n_tags(self.as_ptr()) } - } - - pub fn nth_tag<'a>(&'a self, idx: u32) -> &str { - unsafe { CStr::from_ptr(ffi::gst_tag_list_nth_tag_name(self.as_ptr(), idx)).to_str().unwrap() } - } - - pub fn get_size_dynamic<'a>(&'a self, tag_name: &str) -> u32 { - unsafe { ffi::gst_tag_list_get_tag_size(self.as_ptr(), tag_name.to_glib_none().0) } - } - - pub fn get_index_dynamic<'a>(&'a self, tag_name: &str, idx: u32) -> Option<&'a Value> { + pub fn get_generic(&self, tag_name: &str) -> Option { unsafe { - let value = ffi::gst_tag_list_get_value_index( + let mut value: Value = mem::zeroed(); + + let found: bool = from_glib(ffi::gst_tag_list_copy_value( + value.to_glib_none_mut().0, self.as_ptr(), tag_name.to_glib_none().0, - idx, - ); + )); - if value.is_null() { + if !found { return None; } - Some(&*(value as *const Value)) + Some(value) } } + pub fn n_tags(&self) -> i32 { + unsafe { ffi::gst_tag_list_n_tags(self.as_ptr()) } + } + + pub fn nth_tag_name<'a>(&'a self, idx: u32) -> &'a str { + unsafe { CStr::from_ptr(ffi::gst_tag_list_nth_tag_name(self.as_ptr(), idx)).to_str().unwrap() } + } + pub fn get_index<'a, T: Tag<'a>>(&'a self, idx: u32) -> Option<&'a TypedValue> { unsafe { let value = ffi::gst_tag_list_get_value_index( @@ -267,14 +279,42 @@ impl TagListRef { } } - pub fn get_size<'a, T: Tag<'a>>(&'a self) -> u32 { + pub fn get_index_generic<'a>(&'a self, tag_name: &str, idx: u32) -> Option<&'a Value> { + unsafe { + let value = ffi::gst_tag_list_get_value_index( + self.as_ptr(), + tag_name.to_glib_none().0, + idx, + ); + + if value.is_null() { + return None; + } + + Some(&*(value as *const Value)) + } + } + + pub fn get_size<'a, T: Tag<'a>>(&self) -> u32 { unsafe { ffi::gst_tag_list_get_tag_size(self.as_ptr(), T::tag_name().to_glib_none().0) } } + pub fn get_size_by_name(&self, tag_name: &str) -> u32 { + unsafe { ffi::gst_tag_list_get_tag_size(self.as_ptr(), tag_name.to_glib_none().0) } + } + pub fn iter_tag<'a, T: Tag<'a>>(&'a self) -> TagIterator<'a, T> { TagIterator::new(self) } + pub fn iter_tag_generic<'a>(&'a self, tag_name: &'a str) -> GenericTagIterator<'a> { + GenericTagIterator::new(self, tag_name) + } + + pub fn iter_tag_list<'a>(&'a self) -> TagListIterator<'a> { + TagListIterator::new(self) + } + pub fn to_string(&self) -> String { unsafe { from_glib_full(ffi::gst_tag_list_to_string(self.as_ptr())) } } @@ -401,6 +441,127 @@ where { } +pub struct GenericTagIterator<'a> { + taglist: &'a TagListRef, + name: &'a str, + idx: u32, + size: u32, +} + +impl<'a> GenericTagIterator<'a> { + fn new(taglist: &'a TagListRef, name: &'a str) -> GenericTagIterator<'a> { + skip_assert_initialized!(); + GenericTagIterator { + taglist: taglist, + name, + idx: 0, + size: taglist.get_size_by_name(name), + } + } +} + +impl<'a> Iterator for GenericTagIterator<'a> { + type Item = &'a Value; + + fn next(&mut self) -> Option { + if self.idx >= self.size { + return None; + } + + let item = self.taglist.get_index_generic(self.name, self.idx); + self.idx += 1; + + item + } + + fn size_hint(&self) -> (usize, Option) { + if self.idx == self.size { + return (0, Some(0)); + } + + let remaining = (self.size - self.idx) as usize; + + (remaining, Some(remaining)) + } +} + +impl<'a> DoubleEndedIterator for GenericTagIterator<'a> { + fn next_back(&mut self) -> Option { + if self.idx == self.size { + return None; + } + + self.size -= 1; + self.taglist.get_index_generic(self.name, self.size) + } +} + +impl<'a> ExactSizeIterator for GenericTagIterator<'a> { +} + +pub struct TagListIterator<'a> { + taglist: &'a TagListRef, + idx: u32, + size: u32, +} + +impl<'a> TagListIterator<'a> { + fn new(taglist: &'a TagListRef) -> TagListIterator<'a> { + skip_assert_initialized!(); + let size = taglist.n_tags(); + TagListIterator { + taglist: taglist, + idx: 0, + size: if size > 0 { + size as u32 + } else { + 0 + }, + } + } +} + +impl<'a> Iterator for TagListIterator<'a> { + type Item = (&'a str, GenericTagIterator<'a>); + + fn next(&mut self) -> Option { + if self.idx >= self.size { + return None; + } + + let name = self.taglist.nth_tag_name(self.idx); + let item = (name, self.taglist.iter_tag_generic(name)); + self.idx += 1; + + Some(item) + } + + fn size_hint(&self) -> (usize, Option) { + if self.idx == self.size { + return (0, Some(0)); + } + + let remaining = (self.size - self.idx) as usize; + + (remaining, Some(remaining)) + } +} + +impl<'a> DoubleEndedIterator for TagListIterator<'a> { + fn next_back(&mut self) -> Option { + if self.idx == self.size { + return None; + } + + self.size -= 1; + let name = self.taglist.nth_tag_name(self.idx); + Some((name, self.taglist.iter_tag_generic(name))) + } +} + +impl<'a> ExactSizeIterator for TagListIterator<'a> { +} + #[cfg(test)] mod tests { use super::*; @@ -447,19 +608,72 @@ mod tests { } #[test] - fn test_dynamic() { + fn test_generic() { ::init().unwrap(); let mut tags = TagList::new(); { let tags = tags.get_mut().unwrap(); - tags.add_dynamic(&TAG_TITLE, &"some title", TagMergeMode::Append); - tags.add_dynamic(&TAG_TITLE, &"second title", TagMergeMode::Append); - tags.add_dynamic(&TAG_DURATION, &(::SECOND * 120), TagMergeMode::Append); + assert!(tags.add_generic(&TAG_TITLE, &"some title", TagMergeMode::Append).is_ok()); + assert!(tags.add_generic(&TAG_TITLE, &"second title", TagMergeMode::Append).is_ok()); + assert!(tags.add_generic(&TAG_DURATION, &(::SECOND * 120), TagMergeMode::Append).is_ok()); + assert!(tags.add_generic(&TAG_TITLE, &"third title", TagMergeMode::Append).is_ok()); + + assert_eq!( + tags.add_generic( + &TAG_IMAGE, + &"`&[str] instead of `Sample`", + TagMergeMode::Append + ), + Err(TagError::TypeMismatch), + ); } - assert_eq!(tags.get_index_dynamic(&TAG_TITLE, 0).unwrap().get(), Some("some title")); - assert_eq!(tags.get_index_dynamic(&TAG_TITLE, 1).unwrap().get(), Some("second title")); - assert_eq!(tags.get_index_dynamic(&TAG_DURATION, 0).unwrap().get(), Some(::SECOND * 120)); + assert_eq!(tags.get_index_generic(&TAG_TITLE, 0).unwrap().get(), Some("some title")); + assert_eq!(tags.get_index_generic(&TAG_TITLE, 1).unwrap().get(), Some("second title")); + assert_eq!(tags.get_index_generic(&TAG_DURATION, 0).unwrap().get(), Some(::SECOND * 120)); + assert_eq!(tags.get_index_generic(&TAG_TITLE, 2).unwrap().get(), Some("third title")); + + assert_eq!( + tags.get_generic(&TAG_TITLE).unwrap().get(), + Some("some title, second title, third title"), + ); + + assert_eq!(tags.n_tags(), 2); + assert_eq!(tags.nth_tag_name(0), *TAG_TITLE); + assert_eq!(tags.get_size_by_name(&TAG_TITLE), 3); + assert_eq!(tags.nth_tag_name(1), *TAG_DURATION); + assert_eq!(tags.get_size_by_name(&TAG_DURATION), 1); + + // GenericTagIterator + let mut title_iter = tags.iter_tag_generic(&TAG_TITLE); + assert_eq!(title_iter.size_hint(), (3, Some(3))); + let first_title = title_iter.next().unwrap(); + assert_eq!(first_title.get(), Some("some title")); + let second_title = title_iter.next().unwrap(); + assert_eq!(second_title.get(), Some("second title")); + let third_title = title_iter.next().unwrap(); + assert_eq!(third_title.get(), Some("third title")); + assert!(title_iter.next().is_none()); + + // TagListIterator + let mut tag_list_iter = tags.iter_tag_list(); + assert_eq!(tag_list_iter.size_hint(), (2, Some(2))); + + let (tag_name, mut tag_iter) = tag_list_iter.next().unwrap(); + assert_eq!(tag_name, *TAG_TITLE); + let first_title = tag_iter.next().unwrap(); + assert_eq!(first_title.get(), Some("some title")); + let second_title = tag_iter.next().unwrap(); + assert_eq!(second_title.get(), Some("second title")); + let third_title = tag_iter.next().unwrap(); + assert_eq!(third_title.get(), Some("third title")); + assert!(tag_iter.next().is_none()); + + let (tag_name, mut tag_iter) = tag_list_iter.next().unwrap(); + assert_eq!(tag_name, *TAG_DURATION); + let first_duration = tag_iter.next().unwrap(); + assert_eq!(first_duration.get(), Some(::SECOND * 120)); + assert!(tag_iter.next().is_none()); } }