Tags: implement generic iterators

This is the continuation of PR #107 by @ystreet
This commit is contained in:
François Laignel 2018-07-08 18:42:06 +02:00 committed by Sebastian Dröge
parent 2e1f1eadac
commit be9ef0d152
3 changed files with 263 additions and 29 deletions

View file

@ -311,3 +311,23 @@ impl Ord for ::Rank {
self.to_glib().cmp(&other.to_glib()) 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",
}
}
}

View file

@ -154,7 +154,7 @@ pub use child_proxy::ChildProxyExtManual;
pub use clock_time::ClockTime; pub use clock_time::ClockTime;
pub use device_provider::DeviceProviderExtManual; pub use device_provider::DeviceProviderExtManual;
pub use enums::{ClockError, ClockSuccess, FlowError, FlowSuccess, PadLinkError, PadLinkSuccess, pub use enums::{ClockError, ClockSuccess, FlowError, FlowSuccess, PadLinkError, PadLinkSuccess,
StateChangeError, StateChangeSuccess}; StateChangeError, StateChangeSuccess, TagError};
pub use gobject::GObjectExtManualGst; pub use gobject::GObjectExtManualGst;
pub use pad::{PadExtManual, PadProbeData, PadProbeId, PadProbeInfo}; pub use pad::{PadExtManual, PadProbeData, PadProbeId, PadProbeInfo};
pub use parse_context::ParseContext; pub use parse_context::ParseContext;

View file

@ -20,6 +20,7 @@ use glib::StaticType;
use miniobject::*; use miniobject::*;
use Sample; use Sample;
use TagError;
use TagMergeMode; use TagMergeMode;
pub trait Tag<'a> { 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<T>(
&mut self,
tag_name : &str,
value: &T,
mode: TagMergeMode,
) -> Result<(), TagError>
where where
T: ToSendValue T: ToSendValue
{ {
/* TODO: implement limiting values for tags? */
unsafe { unsafe {
let v = value.to_send_value(); 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( ffi::gst_tag_list_add_value(
self.as_mut_ptr(), self.as_mut_ptr(),
mode.to_glib(), mode.to_glib(),
@ -203,6 +215,8 @@ impl TagListRef {
v.to_glib_none().0, v.to_glib_none().0,
); );
} }
Ok(())
} }
pub fn get<'a, T: Tag<'a>>(&self) -> Option<TypedValue<T::TagType>> { pub fn get<'a, T: Tag<'a>>(&self) -> Option<TypedValue<T::TagType>> {
@ -223,34 +237,32 @@ impl TagListRef {
} }
} }
pub fn n_tags<'a>(&'a self) -> i32 { pub fn get_generic(&self, tag_name: &str) -> Option<Value> {
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> {
unsafe { 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(), self.as_ptr(),
tag_name.to_glib_none().0, tag_name.to_glib_none().0,
idx, ));
);
if value.is_null() { if !found {
return None; 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<T::TagType>> { pub fn get_index<'a, T: Tag<'a>>(&'a self, idx: u32) -> Option<&'a TypedValue<T::TagType>> {
unsafe { unsafe {
let value = ffi::gst_tag_list_get_value_index( 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) } 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> { pub fn iter_tag<'a, T: Tag<'a>>(&'a self) -> TagIterator<'a, T> {
TagIterator::new(self) 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 { pub fn to_string(&self) -> String {
unsafe { from_glib_full(ffi::gst_tag_list_to_string(self.as_ptr())) } 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<Self::Item> {
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<usize>) {
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<Self::Item> {
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<Self::Item> {
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<usize>) {
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<Self::Item> {
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)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
@ -447,19 +608,72 @@ mod tests {
} }
#[test] #[test]
fn test_dynamic() { fn test_generic() {
::init().unwrap(); ::init().unwrap();
let mut tags = TagList::new(); let mut tags = TagList::new();
{ {
let tags = tags.get_mut().unwrap(); let tags = tags.get_mut().unwrap();
tags.add_dynamic(&TAG_TITLE, &"some title", TagMergeMode::Append); assert!(tags.add_generic(&TAG_TITLE, &"some title", TagMergeMode::Append).is_ok());
tags.add_dynamic(&TAG_TITLE, &"second title", TagMergeMode::Append); assert!(tags.add_generic(&TAG_TITLE, &"second title", TagMergeMode::Append).is_ok());
tags.add_dynamic(&TAG_DURATION, &(::SECOND * 120), TagMergeMode::Append); 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_generic(&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_generic(&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_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());
} }
} }