forked from mirrors/gstreamer-rs
Tags: implement generic iterators
This is the continuation of PR #107 by @ystreet
This commit is contained in:
parent
2e1f1eadac
commit
be9ef0d152
3 changed files with 263 additions and 29 deletions
|
@ -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",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue