1 // Copyright (C) 2016-2018 Sebastian Dröge <sebastian@centricular.com>
2 //
3 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6 // option. This file may not be copied, modified, or distributed
7 // except according to those terms.
8 
9 use std::ffi::CStr;
10 use std::fmt;
11 use std::marker::PhantomData;
12 use std::mem;
13 
14 use glib;
15 use glib::translate::{
16     from_glib, from_glib_full, FromGlibPtrFull, ToGlib, ToGlibPtr, ToGlibPtrMut,
17 };
18 use glib::value::{FromValueOptional, SendValue, SetValue, ToSendValue, TypedValue, Value};
19 use glib::StaticType;
20 use gobject_sys;
21 use gst_sys;
22 
23 use miniobject::*;
24 
25 use Sample;
26 use TagError;
27 use TagMergeMode;
28 use TagScope;
29 
30 pub trait Tag<'a> {
31     type TagType: FromValueOptional<'a> + SetValue + Send;
tag_name<'b>() -> &'b str32     fn tag_name<'b>() -> &'b str;
33 }
34 
35 macro_rules! impl_tag(
36     ($name:ident, $t:ty, $rust_tag:ident, $gst_tag:ident) => {
37         pub enum $name {}
38         impl<'a> Tag<'a> for $name {
39             type TagType = $t;
40             fn tag_name<'b>() -> &'b str {
41                 *$rust_tag
42             }
43         }
44 
45         lazy_static! {
46             pub(crate) static ref $rust_tag: &'static str =
47                 unsafe { CStr::from_ptr(gst_sys::$gst_tag).to_str().unwrap() };
48         }
49     };
50 );
51 
52 impl_tag!(Title, &'a str, TAG_TITLE, GST_TAG_TITLE);
53 impl_tag!(
54     TitleSortname,
55     &'a str,
56     TAG_TITLE_SORTNAME,
57     GST_TAG_TITLE_SORTNAME
58 );
59 impl_tag!(Artist, &'a str, TAG_ARTIST, GST_TAG_ARTIST);
60 impl_tag!(
61     ArtistSortname,
62     &'a str,
63     TAG_ARTIST_SORTNAME,
64     GST_TAG_ARTIST_SORTNAME
65 );
66 impl_tag!(Album, &'a str, TAG_ALBUM, GST_TAG_ALBUM);
67 impl_tag!(
68     AlbumSortname,
69     &'a str,
70     TAG_ALBUM_SORTNAME,
71     GST_TAG_ALBUM_SORTNAME
72 );
73 impl_tag!(AlbumArtist, &'a str, TAG_ALBUM_ARTIST, GST_TAG_ALBUM_ARTIST);
74 impl_tag!(
75     AlbumArtistSortname,
76     &'a str,
77     TAG_ALBUM_ARTIST_SORTNAME,
78     GST_TAG_ALBUM_ARTIST_SORTNAME
79 );
80 impl_tag!(Date, glib::Date, TAG_DATE, GST_TAG_DATE);
81 impl_tag!(DateTime, ::auto::DateTime, TAG_DATE_TIME, GST_TAG_DATE_TIME);
82 impl_tag!(Genre, &'a str, TAG_GENRE, GST_TAG_GENRE);
83 impl_tag!(Comment, &'a str, TAG_COMMENT, GST_TAG_COMMENT);
84 impl_tag!(
85     ExtendedComment,
86     &'a str,
87     TAG_EXTENDED_COMMENT,
88     GST_TAG_EXTENDED_COMMENT
89 );
90 impl_tag!(TrackNumber, u32, TAG_TRACK_NUMBER, GST_TAG_TRACK_NUMBER);
91 impl_tag!(TrackCount, u32, TAG_TRACK_COUNT, GST_TAG_TRACK_COUNT);
92 impl_tag!(
93     AlbumVolumeNumber,
94     u32,
95     TAG_ALBUM_VOLUME_NUMBER,
96     GST_TAG_ALBUM_VOLUME_NUMBER
97 );
98 impl_tag!(
99     AlbumVolumeCount,
100     u32,
101     TAG_ALBUM_VOLUME_COUNT,
102     GST_TAG_ALBUM_VOLUME_COUNT
103 );
104 impl_tag!(Location, &'a str, TAG_LOCATION, GST_TAG_LOCATION);
105 impl_tag!(Homepage, &'a str, TAG_HOMEPAGE, GST_TAG_HOMEPAGE);
106 impl_tag!(Description, &'a str, TAG_DESCRIPTION, GST_TAG_DESCRIPTION);
107 impl_tag!(Version, &'a str, TAG_VERSION, GST_TAG_VERSION);
108 impl_tag!(ISRC, &'a str, TAG_ISRC, GST_TAG_ISRC);
109 impl_tag!(
110     Organization,
111     &'a str,
112     TAG_ORGANIZATION,
113     GST_TAG_ORGANIZATION
114 );
115 impl_tag!(Copyright, &'a str, TAG_COPYRIGHT, GST_TAG_COPYRIGHT);
116 impl_tag!(
117     CopyrightUri,
118     &'a str,
119     TAG_COPYRIGHT_URI,
120     GST_TAG_COPYRIGHT_URI
121 );
122 impl_tag!(EncodedBy, &'a str, TAG_ENCODED_BY, GST_TAG_ENCODED_BY);
123 impl_tag!(Composer, &'a str, TAG_COMPOSER, GST_TAG_COMPOSER);
124 impl_tag!(Conductor, &'a str, TAG_CONDUCTOR, GST_TAG_CONDUCTOR);
125 impl_tag!(Contact, &'a str, TAG_CONTACT, GST_TAG_CONTACT);
126 impl_tag!(License, &'a str, TAG_LICENSE, GST_TAG_LICENSE);
127 impl_tag!(LicenseUri, &'a str, TAG_LICENSE_URI, GST_TAG_LICENSE_URI);
128 impl_tag!(Performer, &'a str, TAG_PERFORMER, GST_TAG_PERFORMER);
129 impl_tag!(Duration, ::ClockTime, TAG_DURATION, GST_TAG_DURATION);
130 impl_tag!(Codec, &'a str, TAG_CODEC, GST_TAG_CODEC);
131 impl_tag!(VideoCodec, &'a str, TAG_VIDEO_CODEC, GST_TAG_VIDEO_CODEC);
132 impl_tag!(AudioCodec, &'a str, TAG_AUDIO_CODEC, GST_TAG_AUDIO_CODEC);
133 impl_tag!(
134     SubtitleCodec,
135     &'a str,
136     TAG_SUBTITLE_CODEC,
137     GST_TAG_SUBTITLE_CODEC
138 );
139 impl_tag!(
140     ContainerFormat,
141     &'a str,
142     TAG_CONTAINER_FORMAT,
143     GST_TAG_CONTAINER_FORMAT
144 );
145 impl_tag!(Bitrate, u32, TAG_BITRATE, GST_TAG_BITRATE);
146 impl_tag!(
147     NominalBitrate,
148     u32,
149     TAG_NOMINAL_BITRATE,
150     GST_TAG_NOMINAL_BITRATE
151 );
152 impl_tag!(
153     MinimumBitrate,
154     u32,
155     TAG_MINIMUM_BITRATE,
156     GST_TAG_MINIMUM_BITRATE
157 );
158 impl_tag!(
159     MaximumBitrate,
160     u32,
161     TAG_MAXIMUM_BITRATE,
162     GST_TAG_MAXIMUM_BITRATE
163 );
164 impl_tag!(Serial, u32, TAG_SERIAL, GST_TAG_SERIAL);
165 impl_tag!(Encoder, &'a str, TAG_ENCODER, GST_TAG_ENCODER);
166 impl_tag!(
167     EncoderVersion,
168     u32,
169     TAG_ENCODER_VERSION,
170     GST_TAG_ENCODER_VERSION
171 );
172 impl_tag!(TrackGain, f64, TAG_TRACK_GAIN, GST_TAG_TRACK_GAIN);
173 impl_tag!(TrackPeak, f64, TAG_TRACK_PEAK, GST_TAG_TRACK_PEAK);
174 impl_tag!(AlbumGain, f64, TAG_ALBUM_GAIN, GST_TAG_ALBUM_GAIN);
175 impl_tag!(AlbumPeak, f64, TAG_ALBUM_PEAK, GST_TAG_ALBUM_PEAK);
176 impl_tag!(
177     ReferenceLevel,
178     f64,
179     TAG_REFERENCE_LEVEL,
180     GST_TAG_REFERENCE_LEVEL
181 );
182 // TODO: Should ideally enforce this to be ISO-639
183 impl_tag!(
184     LanguageCode,
185     &'a str,
186     TAG_LANGUAGE_CODE,
187     GST_TAG_LANGUAGE_CODE
188 );
189 impl_tag!(
190     LanguageName,
191     &'a str,
192     TAG_LANGUAGE_NAME,
193     GST_TAG_LANGUAGE_NAME
194 );
195 impl_tag!(Image, Sample, TAG_IMAGE, GST_TAG_IMAGE);
196 impl_tag!(
197     PreviewImage,
198     Sample,
199     TAG_PREVIEW_IMAGE,
200     GST_TAG_PREVIEW_IMAGE
201 );
202 impl_tag!(Attachment, Sample, TAG_ATTACHMENT, GST_TAG_ATTACHMENT);
203 impl_tag!(
204     BeatsPerMinute,
205     f64,
206     TAG_BEATS_PER_MINUTE,
207     GST_TAG_BEATS_PER_MINUTE
208 );
209 impl_tag!(Keywords, &'a str, TAG_KEYWORDS, GST_TAG_KEYWORDS);
210 impl_tag!(
211     GeoLocationName,
212     &'a str,
213     TAG_GEO_LOCATION_NAME,
214     GST_TAG_GEO_LOCATION_NAME
215 );
216 impl_tag!(
217     GeoLocationLatitude,
218     f64,
219     TAG_GEO_LOCATION_LATITUDE,
220     GST_TAG_GEO_LOCATION_LATITUDE
221 );
222 impl_tag!(
223     GeoLocationLongitute,
224     f64,
225     TAG_GEO_LOCATION_LONGITUDE,
226     GST_TAG_GEO_LOCATION_LONGITUDE
227 );
228 impl_tag!(
229     GeoLocationElevation,
230     f64,
231     TAG_GEO_LOCATION_ELEVATION,
232     GST_TAG_GEO_LOCATION_ELEVATION
233 );
234 impl_tag!(
235     GeoLocationCity,
236     &'a str,
237     TAG_GEO_LOCATION_CITY,
238     GST_TAG_GEO_LOCATION_CITY
239 );
240 impl_tag!(
241     GeoLocationCountry,
242     &'a str,
243     TAG_GEO_LOCATION_COUNTRY,
244     GST_TAG_GEO_LOCATION_COUNTRY
245 );
246 impl_tag!(
247     GeoLocationSublocation,
248     &'a str,
249     TAG_GEO_LOCATION_SUBLOCATION,
250     GST_TAG_GEO_LOCATION_SUBLOCATION
251 );
252 impl_tag!(
253     GeoLocationHorizontalError,
254     f64,
255     TAG_GEO_LOCATION_HORIZONTAL_ERROR,
256     GST_TAG_GEO_LOCATION_HORIZONTAL_ERROR
257 );
258 impl_tag!(
259     GeoLocationMovementDirection,
260     f64,
261     TAG_GEO_LOCATION_MOVEMENT_DIRECTION,
262     GST_TAG_GEO_LOCATION_MOVEMENT_DIRECTION
263 );
264 impl_tag!(
265     GeoLocationMovementSpeed,
266     f64,
267     TAG_GEO_LOCATION_MOVEMENT_SPEED,
268     GST_TAG_GEO_LOCATION_MOVEMENT_SPEED
269 );
270 impl_tag!(
271     GeoLocationCaptureDirection,
272     f64,
273     TAG_GEO_LOCATION_CAPTURE_DIRECTION,
274     GST_TAG_GEO_LOCATION_CAPTURE_DIRECTION
275 );
276 impl_tag!(ShowName, &'a str, TAG_SHOW_NAME, GST_TAG_SHOW_NAME);
277 impl_tag!(
278     ShowSortname,
279     &'a str,
280     TAG_SHOW_SORTNAME,
281     GST_TAG_SHOW_SORTNAME
282 );
283 impl_tag!(
284     ShowEpisodeNumber,
285     u32,
286     TAG_SHOW_EPISODE_NUMBER,
287     GST_TAG_SHOW_EPISODE_NUMBER
288 );
289 impl_tag!(
290     ShowSeasonNumber,
291     u32,
292     TAG_SHOW_SEASON_NUMBER,
293     GST_TAG_SHOW_SEASON_NUMBER
294 );
295 impl_tag!(Lyrics, &'a str, TAG_LYRICS, GST_TAG_LYRICS);
296 impl_tag!(
297     ComposerSortname,
298     &'a str,
299     TAG_COMPOSER_SORTNAME,
300     GST_TAG_COMPOSER_SORTNAME
301 );
302 impl_tag!(Grouping, &'a str, TAG_GROUPING, GST_TAG_GROUPING);
303 impl_tag!(UserRating, u32, TAG_USER_RATING, GST_TAG_USER_RATING);
304 impl_tag!(
305     DeviceManufacturer,
306     &'a str,
307     TAG_DEVICE_MANUFACTURER,
308     GST_TAG_DEVICE_MANUFACTURER
309 );
310 impl_tag!(DeviceModel, &'a str, TAG_DEVICE_MODEL, GST_TAG_DEVICE_MODEL);
311 impl_tag!(
312     ApplicationName,
313     &'a str,
314     TAG_APPLICATION_NAME,
315     GST_TAG_APPLICATION_NAME
316 );
317 impl_tag!(
318     ApplicationData,
319     Sample,
320     TAG_APPLICATION_DATA,
321     GST_TAG_APPLICATION_DATA
322 );
323 impl_tag!(
324     ImageOrientation,
325     &'a str,
326     TAG_IMAGE_ORIENTATION,
327     GST_TAG_IMAGE_ORIENTATION
328 );
329 impl_tag!(Publisher, &'a str, TAG_PUBLISHER, GST_TAG_PUBLISHER);
330 impl_tag!(
331     InterpretedBy,
332     &'a str,
333     TAG_INTERPRETED_BY,
334     GST_TAG_INTERPRETED_BY
335 );
336 impl_tag!(
337     MidiBaseNote,
338     &'a str,
339     TAG_MIDI_BASE_NOTE,
340     GST_TAG_MIDI_BASE_NOTE
341 );
342 impl_tag!(PrivateData, Sample, TAG_PRIVATE_DATA, GST_TAG_PRIVATE_DATA);
343 
344 gst_define_mini_object_wrapper!(
345     TagList,
346     TagListRef,
347     gst_sys::GstTagList,
348     [Debug, PartialEq, Eq,],
349     || gst_sys::gst_tag_list_get_type()
350 );
351 
352 impl TagList {
new() -> Self353     pub fn new() -> Self {
354         assert_initialized_main_thread!();
355         unsafe { from_glib_full(gst_sys::gst_tag_list_new_empty()) }
356     }
357 }
358 
359 impl Default for TagList {
default() -> Self360     fn default() -> Self {
361         Self::new()
362     }
363 }
364 
365 impl fmt::Display for TagList {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result366     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
367         self.0.fmt(f)
368     }
369 }
370 
371 impl TagListRef {
add<'a, T: Tag<'a>>(&mut self, value: &T::TagType, mode: TagMergeMode) where T::TagType: ToSendValue,372     pub fn add<'a, T: Tag<'a>>(&mut self, value: &T::TagType, mode: TagMergeMode)
373     where
374         T::TagType: ToSendValue,
375     {
376         // result can be safely ignored here as `value`'s type is tied to `T::tag_name()`
377         let _res = self.add_generic(T::tag_name(), value, mode);
378     }
379 
add_generic<T>( &mut self, tag_name: &str, value: &T, mode: TagMergeMode, ) -> Result<(), TagError> where T: ToSendValue,380     pub fn add_generic<T>(
381         &mut self,
382         tag_name: &str,
383         value: &T,
384         mode: TagMergeMode,
385     ) -> Result<(), TagError>
386     where
387         T: ToSendValue,
388     {
389         unsafe {
390             let v = value.to_send_value();
391 
392             let tag_name = tag_name.to_glib_none();
393 
394             let tag_type: glib::Type = from_glib(gst_sys::gst_tag_get_type(tag_name.0));
395             if tag_type != v.type_() {
396                 return Err(TagError::TypeMismatch);
397             }
398 
399             gst_sys::gst_tag_list_add_value(
400                 self.as_mut_ptr(),
401                 mode.to_glib(),
402                 tag_name.0,
403                 v.to_glib_none().0,
404             );
405         }
406 
407         Ok(())
408     }
409 
get<'a, T: Tag<'a>>(&self) -> Option<TypedValue<T::TagType>>410     pub fn get<'a, T: Tag<'a>>(&self) -> Option<TypedValue<T::TagType>> {
411         self.get_generic(T::tag_name()).map(|value| {
412             value.downcast().unwrap_or_else(|_| {
413                 panic!("TagListRef::get type mismatch for tag {}", T::tag_name())
414             })
415         })
416     }
417 
get_generic(&self, tag_name: &str) -> Option<SendValue>418     pub fn get_generic(&self, tag_name: &str) -> Option<SendValue> {
419         unsafe {
420             let mut value: mem::MaybeUninit<SendValue> = mem::MaybeUninit::zeroed();
421 
422             let found: bool = from_glib(gst_sys::gst_tag_list_copy_value(
423                 (*value.as_mut_ptr()).to_glib_none_mut().0,
424                 self.as_ptr(),
425                 tag_name.to_glib_none().0,
426             ));
427 
428             if !found {
429                 return None;
430             }
431 
432             Some(value.assume_init())
433         }
434     }
435 
n_tags(&self) -> i32436     pub fn n_tags(&self) -> i32 {
437         unsafe { gst_sys::gst_tag_list_n_tags(self.as_ptr()) }
438     }
439 
nth_tag_name(&self, idx: u32) -> &str440     pub fn nth_tag_name(&self, idx: u32) -> &str {
441         unsafe {
442             CStr::from_ptr(gst_sys::gst_tag_list_nth_tag_name(self.as_ptr(), idx))
443                 .to_str()
444                 .unwrap()
445         }
446     }
447 
get_index<'a, T: Tag<'a>>(&'a self, idx: u32) -> Option<&'a TypedValue<T::TagType>>448     pub fn get_index<'a, T: Tag<'a>>(&'a self, idx: u32) -> Option<&'a TypedValue<T::TagType>> {
449         self.get_index_generic(T::tag_name(), idx)
450             .and_then(|value| value.downcast_ref())
451     }
452 
get_index_generic<'a>(&'a self, tag_name: &str, idx: u32) -> Option<&'a SendValue>453     pub fn get_index_generic<'a>(&'a self, tag_name: &str, idx: u32) -> Option<&'a SendValue> {
454         unsafe {
455             let value = gst_sys::gst_tag_list_get_value_index(
456                 self.as_ptr(),
457                 tag_name.to_glib_none().0,
458                 idx,
459             );
460 
461             if value.is_null() {
462                 return None;
463             }
464 
465             Some(&*(value as *const SendValue))
466         }
467     }
468 
get_size<'a, T: Tag<'a>>(&self) -> u32469     pub fn get_size<'a, T: Tag<'a>>(&self) -> u32 {
470         self.get_size_by_name(T::tag_name())
471     }
472 
get_size_by_name(&self, tag_name: &str) -> u32473     pub fn get_size_by_name(&self, tag_name: &str) -> u32 {
474         unsafe { gst_sys::gst_tag_list_get_tag_size(self.as_ptr(), tag_name.to_glib_none().0) }
475     }
476 
iter_tag<'a, T: Tag<'a>>(&'a self) -> TagIter<'a, T>477     pub fn iter_tag<'a, T: Tag<'a>>(&'a self) -> TagIter<'a, T> {
478         TagIter::new(self)
479     }
480 
iter_tag_generic<'a>(&'a self, tag_name: &'a str) -> GenericTagIter<'a>481     pub fn iter_tag_generic<'a>(&'a self, tag_name: &'a str) -> GenericTagIter<'a> {
482         GenericTagIter::new(self, tag_name)
483     }
484 
iter_generic(&self) -> GenericIter485     pub fn iter_generic(&self) -> GenericIter {
486         GenericIter::new(self)
487     }
488 
iter(&self) -> Iter489     pub fn iter(&self) -> Iter {
490         Iter::new(self)
491     }
492 
insert(&mut self, other: &TagListRef, mode: TagMergeMode)493     pub fn insert(&mut self, other: &TagListRef, mode: TagMergeMode) {
494         unsafe { gst_sys::gst_tag_list_insert(self.as_mut_ptr(), other.as_ptr(), mode.to_glib()) }
495     }
496 
merge(&self, other: &TagListRef, mode: TagMergeMode) -> TagList497     pub fn merge(&self, other: &TagListRef, mode: TagMergeMode) -> TagList {
498         unsafe {
499             from_glib_full(gst_sys::gst_tag_list_merge(
500                 self.as_ptr(),
501                 other.as_ptr(),
502                 mode.to_glib(),
503             ))
504         }
505     }
506 
get_scope(&self) -> TagScope507     pub fn get_scope(&self) -> TagScope {
508         unsafe { from_glib(gst_sys::gst_tag_list_get_scope(self.as_ptr())) }
509     }
510 
set_scope(&mut self, scope: TagScope)511     pub fn set_scope(&mut self, scope: TagScope) {
512         unsafe { gst_sys::gst_tag_list_set_scope(self.as_mut_ptr(), scope.to_glib()) }
513     }
514 }
515 
516 impl fmt::Debug for TagListRef {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result517     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
518         f.debug_tuple("TagList").field(&self.to_string()).finish()
519     }
520 }
521 
522 impl fmt::Display for TagListRef {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result523     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
524         let s = unsafe {
525             glib::GString::from_glib_full(gst_sys::gst_tag_list_to_string(self.as_ptr()))
526         };
527         f.write_str(&s)
528     }
529 }
530 
531 impl PartialEq for TagListRef {
eq(&self, other: &TagListRef) -> bool532     fn eq(&self, other: &TagListRef) -> bool {
533         unsafe {
534             from_glib(gst_sys::gst_tag_list_is_equal(
535                 self.as_ptr(),
536                 other.as_ptr(),
537             ))
538         }
539     }
540 }
541 
542 impl Eq for TagListRef {}
543 
544 #[derive(Debug)]
545 pub struct TagIter<'a, T: Tag<'a>> {
546     taglist: &'a TagListRef,
547     idx: u32,
548     size: u32,
549     phantom: PhantomData<T>,
550 }
551 
552 impl<'a, T: Tag<'a>> TagIter<'a, T> {
new(taglist: &'a TagListRef) -> TagIter<'a, T>553     fn new(taglist: &'a TagListRef) -> TagIter<'a, T> {
554         skip_assert_initialized!();
555         TagIter {
556             taglist,
557             idx: 0,
558             size: taglist.get_size::<T>(),
559             phantom: PhantomData,
560         }
561     }
562 }
563 
564 impl<'a, T: Tag<'a>> Iterator for TagIter<'a, T>
565 where
566     <T as Tag<'a>>::TagType: 'a,
567     T: 'a,
568 {
569     type Item = &'a TypedValue<T::TagType>;
570 
next(&mut self) -> Option<Self::Item>571     fn next(&mut self) -> Option<Self::Item> {
572         if self.idx >= self.size {
573             return None;
574         }
575 
576         let item = self.taglist.get_index::<T>(self.idx);
577         self.idx += 1;
578 
579         item
580     }
581 
size_hint(&self) -> (usize, Option<usize>)582     fn size_hint(&self) -> (usize, Option<usize>) {
583         if self.idx == self.size {
584             return (0, Some(0));
585         }
586 
587         let remaining = (self.size - self.idx) as usize;
588 
589         (remaining, Some(remaining))
590     }
591 }
592 
593 impl<'a, T: Tag<'a>> DoubleEndedIterator for TagIter<'a, T>
594 where
595     <T as Tag<'a>>::TagType: 'a,
596     T: 'a,
597 {
next_back(&mut self) -> Option<Self::Item>598     fn next_back(&mut self) -> Option<Self::Item> {
599         if self.idx == self.size {
600             return None;
601         }
602 
603         self.size -= 1;
604         self.taglist.get_index::<T>(self.size)
605     }
606 }
607 
608 impl<'a, T: Tag<'a>> ExactSizeIterator for TagIter<'a, T>
609 where
610     <T as Tag<'a>>::TagType: 'a,
611     T: 'a,
612 {
613 }
614 
615 #[derive(Debug)]
616 pub struct GenericTagIter<'a> {
617     taglist: &'a TagListRef,
618     name: &'a str,
619     idx: u32,
620     size: u32,
621 }
622 
623 impl<'a> GenericTagIter<'a> {
new(taglist: &'a TagListRef, name: &'a str) -> GenericTagIter<'a>624     fn new(taglist: &'a TagListRef, name: &'a str) -> GenericTagIter<'a> {
625         skip_assert_initialized!();
626         GenericTagIter {
627             taglist,
628             name,
629             idx: 0,
630             size: taglist.get_size_by_name(name),
631         }
632     }
633 }
634 
635 impl<'a> Iterator for GenericTagIter<'a> {
636     type Item = &'a SendValue;
637 
next(&mut self) -> Option<Self::Item>638     fn next(&mut self) -> Option<Self::Item> {
639         if self.idx >= self.size {
640             return None;
641         }
642 
643         let item = self.taglist.get_index_generic(self.name, self.idx);
644         self.idx += 1;
645 
646         item
647     }
648 
size_hint(&self) -> (usize, Option<usize>)649     fn size_hint(&self) -> (usize, Option<usize>) {
650         if self.idx == self.size {
651             return (0, Some(0));
652         }
653 
654         let remaining = (self.size - self.idx) as usize;
655 
656         (remaining, Some(remaining))
657     }
658 }
659 
660 impl<'a> DoubleEndedIterator for GenericTagIter<'a> {
next_back(&mut self) -> Option<Self::Item>661     fn next_back(&mut self) -> Option<Self::Item> {
662         if self.idx == self.size {
663             return None;
664         }
665 
666         self.size -= 1;
667         self.taglist.get_index_generic(self.name, self.size)
668     }
669 }
670 
671 impl<'a> ExactSizeIterator for GenericTagIter<'a> {}
672 
673 #[derive(Debug)]
674 pub struct GenericIter<'a> {
675     taglist: &'a TagListRef,
676     idx: u32,
677     size: u32,
678 }
679 
680 impl<'a> GenericIter<'a> {
new(taglist: &'a TagListRef) -> GenericIter<'a>681     fn new(taglist: &'a TagListRef) -> GenericIter<'a> {
682         skip_assert_initialized!();
683         let size = taglist.n_tags();
684         GenericIter {
685             taglist,
686             idx: 0,
687             size: if size > 0 { size as u32 } else { 0 },
688         }
689     }
690 }
691 
692 impl<'a> Iterator for GenericIter<'a> {
693     type Item = (&'a str, GenericTagIter<'a>);
694 
next(&mut self) -> Option<Self::Item>695     fn next(&mut self) -> Option<Self::Item> {
696         if self.idx >= self.size {
697             return None;
698         }
699 
700         let name = self.taglist.nth_tag_name(self.idx);
701         let item = (name, self.taglist.iter_tag_generic(name));
702         self.idx += 1;
703 
704         Some(item)
705     }
706 
size_hint(&self) -> (usize, Option<usize>)707     fn size_hint(&self) -> (usize, Option<usize>) {
708         if self.idx == self.size {
709             return (0, Some(0));
710         }
711 
712         let remaining = (self.size - self.idx) as usize;
713 
714         (remaining, Some(remaining))
715     }
716 }
717 
718 impl<'a> DoubleEndedIterator for GenericIter<'a> {
next_back(&mut self) -> Option<Self::Item>719     fn next_back(&mut self) -> Option<Self::Item> {
720         if self.idx == self.size {
721             return None;
722         }
723 
724         self.size -= 1;
725         let name = self.taglist.nth_tag_name(self.idx);
726         Some((name, self.taglist.iter_tag_generic(name)))
727     }
728 }
729 
730 impl<'a> ExactSizeIterator for GenericIter<'a> {}
731 
732 #[derive(Debug)]
733 pub struct Iter<'a> {
734     taglist: &'a TagListRef,
735     idx: u32,
736     size: u32,
737 }
738 
739 impl<'a> Iter<'a> {
new(taglist: &'a TagListRef) -> Iter<'a>740     fn new(taglist: &'a TagListRef) -> Iter<'a> {
741         skip_assert_initialized!();
742         let size = taglist.n_tags();
743         Iter {
744             taglist,
745             idx: 0,
746             size: if size > 0 { size as u32 } else { 0 },
747         }
748     }
749 }
750 
751 impl<'a> Iterator for Iter<'a> {
752     type Item = (&'a str, glib::SendValue);
753 
next(&mut self) -> Option<Self::Item>754     fn next(&mut self) -> Option<Self::Item> {
755         if self.idx >= self.size {
756             return None;
757         }
758 
759         let name = self.taglist.nth_tag_name(self.idx);
760         let item = (name, self.taglist.get_generic(name).unwrap());
761         self.idx += 1;
762 
763         Some(item)
764     }
765 
size_hint(&self) -> (usize, Option<usize>)766     fn size_hint(&self) -> (usize, Option<usize>) {
767         if self.idx == self.size {
768             return (0, Some(0));
769         }
770 
771         let remaining = (self.size - self.idx) as usize;
772 
773         (remaining, Some(remaining))
774     }
775 }
776 
777 impl<'a> DoubleEndedIterator for Iter<'a> {
next_back(&mut self) -> Option<Self::Item>778     fn next_back(&mut self) -> Option<Self::Item> {
779         if self.idx == self.size {
780             return None;
781         }
782 
783         self.size -= 1;
784         let name = self.taglist.nth_tag_name(self.idx);
785         Some((name, self.taglist.get_generic(name).unwrap()))
786     }
787 }
788 
789 impl<'a> ExactSizeIterator for Iter<'a> {}
790 
tag_exists(name: &str) -> bool791 pub fn tag_exists(name: &str) -> bool {
792     unsafe { from_glib(gst_sys::gst_tag_exists(name.to_glib_none().0)) }
793 }
794 
tag_get_type(name: &str) -> glib::Type795 pub fn tag_get_type(name: &str) -> glib::Type {
796     unsafe { from_glib(gst_sys::gst_tag_get_type(name.to_glib_none().0)) }
797 }
798 
tag_get_nick(name: &str) -> Option<&'static str>799 pub fn tag_get_nick(name: &str) -> Option<&'static str> {
800     unsafe {
801         let ptr = gst_sys::gst_tag_get_nick(name.to_glib_none().0);
802 
803         if ptr.is_null() {
804             None
805         } else {
806             Some(CStr::from_ptr(ptr).to_str().unwrap())
807         }
808     }
809 }
810 
tag_get_description(name: &str) -> Option<&'static str>811 pub fn tag_get_description(name: &str) -> Option<&'static str> {
812     unsafe {
813         let ptr = gst_sys::gst_tag_get_description(name.to_glib_none().0);
814 
815         if ptr.is_null() {
816             None
817         } else {
818             Some(CStr::from_ptr(ptr).to_str().unwrap())
819         }
820     }
821 }
822 
tag_get_flag(name: &str) -> ::TagFlag823 pub fn tag_get_flag(name: &str) -> ::TagFlag {
824     unsafe { from_glib(gst_sys::gst_tag_get_flag(name.to_glib_none().0)) }
825 }
826 
827 pub trait CustomTag<'a>: Tag<'a> {
828     const FLAG: ::TagFlag;
829     const NICK: &'static str;
830     const DESCRIPTION: &'static str;
831 
merge_func(src: &Value) -> Value832     fn merge_func(src: &Value) -> Value {
833         merge_use_first(src)
834     }
835 }
836 
register<T: for<'a> CustomTag<'a>>()837 pub fn register<T: for<'a> CustomTag<'a>>() {
838     assert!(!tag_exists(T::tag_name()));
839 
840     unsafe extern "C" fn merge_func_trampoline<T: for<'a> CustomTag<'a>>(
841         dest: *mut gobject_sys::GValue,
842         src: *const gobject_sys::GValue,
843     ) {
844         *dest = T::merge_func(&*(src as *const Value)).into_raw();
845     }
846 
847     unsafe {
848         gst_sys::gst_tag_register(
849             T::tag_name().to_glib_none().0,
850             T::FLAG.to_glib(),
851             T::TagType::static_type().to_glib(),
852             T::NICK.to_glib_none().0,
853             T::DESCRIPTION.to_glib_none().0,
854             Some(merge_func_trampoline::<T>),
855         )
856     }
857 }
858 
merge_use_first(src: &Value) -> Value859 pub fn merge_use_first(src: &Value) -> Value {
860     assert_eq!(src.type_(), ::List::static_type());
861 
862     unsafe {
863         use glib::translate::Uninitialized;
864 
865         let mut res = Value::uninitialized();
866         gst_sys::gst_tag_merge_use_first(res.to_glib_none_mut().0, src.to_glib_none().0);
867         res
868     }
869 }
870 
merge_strings_with_comma(src: &Value) -> Value871 pub fn merge_strings_with_comma(src: &Value) -> Value {
872     assert_eq!(src.type_(), ::List::static_type());
873 
874     unsafe {
875         use glib::translate::Uninitialized;
876 
877         let mut res = Value::uninitialized();
878         gst_sys::gst_tag_merge_strings_with_comma(res.to_glib_none_mut().0, src.to_glib_none().0);
879         res
880     }
881 }
882 
883 #[cfg(test)]
884 mod tests {
885     use super::*;
886 
887     #[test]
test_add()888     fn test_add() {
889         ::init().unwrap();
890 
891         let mut tags = TagList::new();
892         assert_eq!(tags.to_string(), "taglist;");
893         {
894             let tags = tags.get_mut().unwrap();
895             tags.add::<Title>(&"some title", TagMergeMode::Append);
896             tags.add::<Duration>(&(::SECOND * 120), TagMergeMode::Append);
897         }
898         assert_eq!(
899             tags.to_string(),
900             "taglist, title=(string)\"some\\ title\", duration=(guint64)120000000000;"
901         );
902     }
903 
904     #[test]
test_get()905     fn test_get() {
906         ::init().unwrap();
907 
908         let mut tags = TagList::new();
909         assert_eq!(tags.to_string(), "taglist;");
910         {
911             let tags = tags.get_mut().unwrap();
912             tags.add::<Title>(&"some title", TagMergeMode::Append);
913             tags.add::<Duration>(&(::SECOND * 120), TagMergeMode::Append);
914         }
915 
916         assert_eq!(tags.get::<Title>().unwrap().get(), Some("some title"));
917         assert_eq!(tags.get::<Duration>().unwrap().get_some(), (::SECOND * 120));
918         assert_eq!(
919             tags.get_index::<Title>(0).unwrap().get(),
920             Some("some title")
921         );
922         assert_eq!(
923             tags.get_index::<Duration>(0).unwrap().get_some(),
924             (::SECOND * 120)
925         );
926     }
927 
928     #[test]
test_scope()929     fn test_scope() {
930         ::init().unwrap();
931 
932         let mut tags = TagList::new();
933         assert_eq!(tags.get_scope(), TagScope::Stream);
934         {
935             let tags = tags.get_mut().unwrap();
936             tags.set_scope(TagScope::Global);
937         }
938         assert_eq!(tags.get_scope(), TagScope::Global);
939     }
940 
941     #[test]
942     #[allow(clippy::cognitive_complexity)]
test_generic()943     fn test_generic() {
944         ::init().unwrap();
945 
946         let mut tags = TagList::new();
947         {
948             let tags = tags.get_mut().unwrap();
949             assert!(tags
950                 .add_generic(&TAG_TITLE, &"some title", TagMergeMode::Append)
951                 .is_ok());
952             assert!(tags
953                 .add_generic(&TAG_TITLE, &"second title", TagMergeMode::Append)
954                 .is_ok());
955             assert!(tags
956                 .add_generic(&TAG_DURATION, &(::SECOND * 120), TagMergeMode::Append)
957                 .is_ok());
958             assert!(tags
959                 .add_generic(&TAG_TITLE, &"third title", TagMergeMode::Append)
960                 .is_ok());
961 
962             assert_eq!(
963                 tags.add_generic(
964                     &TAG_IMAGE,
965                     &"`&[str] instead of `Sample`",
966                     TagMergeMode::Append
967                 ),
968                 Err(TagError::TypeMismatch),
969             );
970         }
971 
972         assert_eq!(
973             tags.get_index_generic(&TAG_TITLE, 0).unwrap().get(),
974             Ok(Some("some title"))
975         );
976         assert_eq!(
977             tags.get_index_generic(&TAG_TITLE, 1).unwrap().get(),
978             Ok(Some("second title"))
979         );
980         assert_eq!(
981             tags.get_index_generic(&TAG_DURATION, 0).unwrap().get(),
982             Ok(Some(::SECOND * 120))
983         );
984         assert_eq!(
985             tags.get_index_generic(&TAG_TITLE, 2).unwrap().get(),
986             Ok(Some("third title"))
987         );
988 
989         assert_eq!(
990             tags.get_generic(&TAG_TITLE).unwrap().get(),
991             Ok(Some("some title, second title, third title"))
992         );
993 
994         assert_eq!(tags.n_tags(), 2);
995         assert_eq!(tags.nth_tag_name(0), *TAG_TITLE);
996         assert_eq!(tags.get_size_by_name(&TAG_TITLE), 3);
997         assert_eq!(tags.nth_tag_name(1), *TAG_DURATION);
998         assert_eq!(tags.get_size_by_name(&TAG_DURATION), 1);
999 
1000         // GenericTagIter
1001         let mut title_iter = tags.iter_tag_generic(&TAG_TITLE);
1002         assert_eq!(title_iter.size_hint(), (3, Some(3)));
1003         let first_title = title_iter.next().unwrap();
1004         assert_eq!(first_title.get(), Ok(Some("some title")));
1005         let second_title = title_iter.next().unwrap();
1006         assert_eq!(second_title.get(), Ok(Some("second title")));
1007         let third_title = title_iter.next().unwrap();
1008         assert_eq!(third_title.get(), Ok(Some("third title")));
1009         assert!(title_iter.next().is_none());
1010 
1011         // GenericIter
1012         let mut tag_list_iter = tags.iter_generic();
1013         assert_eq!(tag_list_iter.size_hint(), (2, Some(2)));
1014 
1015         let (tag_name, mut tag_iter) = tag_list_iter.next().unwrap();
1016         assert_eq!(tag_name, *TAG_TITLE);
1017         let first_title = tag_iter.next().unwrap();
1018         assert_eq!(first_title.get(), Ok(Some("some title")));
1019         let second_title = tag_iter.next().unwrap();
1020         assert_eq!(second_title.get(), Ok(Some("second title")));
1021         let third_title = tag_iter.next().unwrap();
1022         assert_eq!(third_title.get(), Ok(Some("third title")));
1023         assert!(tag_iter.next().is_none());
1024 
1025         let (tag_name, mut tag_iter) = tag_list_iter.next().unwrap();
1026         assert_eq!(tag_name, *TAG_DURATION);
1027         let first_duration = tag_iter.next().unwrap();
1028         assert_eq!(first_duration.get_some(), Ok(::SECOND * 120));
1029         assert!(tag_iter.next().is_none());
1030 
1031         // Iter
1032         let mut tag_list_iter = tags.iter();
1033         assert_eq!(tag_list_iter.size_hint(), (2, Some(2)));
1034 
1035         let (tag_name, tag_value) = tag_list_iter.next().unwrap();
1036         assert_eq!(tag_name, *TAG_TITLE);
1037         assert_eq!(
1038             tag_value.get(),
1039             Ok(Some("some title, second title, third title"))
1040         );
1041 
1042         let (tag_name, tag_value) = tag_list_iter.next().unwrap();
1043         assert_eq!(tag_name, *TAG_DURATION);
1044         assert_eq!(tag_value.get_some(), Ok(::SECOND * 120));
1045         assert!(tag_iter.next().is_none());
1046     }
1047 
1048     #[test]
test_custom_tags()1049     fn test_custom_tags() {
1050         ::init().unwrap();
1051 
1052         enum MyCustomTag {};
1053 
1054         impl<'a> Tag<'a> for MyCustomTag {
1055             type TagType = &'a str;
1056             fn tag_name<'b>() -> &'b str {
1057                 "my-custom-tag"
1058             }
1059         }
1060 
1061         impl<'a> CustomTag<'a> for MyCustomTag {
1062             const FLAG: ::TagFlag = ::TagFlag::Meta;
1063             const NICK: &'static str = "my custom tag";
1064             const DESCRIPTION: &'static str = "My own custom tag type for testing";
1065 
1066             fn merge_func(src: &Value) -> Value {
1067                 merge_strings_with_comma(src)
1068             }
1069         }
1070 
1071         register::<MyCustomTag>();
1072 
1073         assert!(tag_exists(MyCustomTag::tag_name()));
1074         assert_eq!(
1075             tag_get_type(MyCustomTag::tag_name()),
1076             <MyCustomTag as Tag>::TagType::static_type()
1077         );
1078         assert_eq!(
1079             tag_get_nick(MyCustomTag::tag_name()),
1080             Some(MyCustomTag::NICK)
1081         );
1082         assert_eq!(
1083             tag_get_description(MyCustomTag::tag_name()),
1084             Some(MyCustomTag::DESCRIPTION)
1085         );
1086         assert_eq!(tag_get_flag(MyCustomTag::tag_name()), MyCustomTag::FLAG);
1087 
1088         let mut tags = TagList::new();
1089         {
1090             let tags = tags.get_mut().unwrap();
1091             tags.add::<MyCustomTag>(&"first one", TagMergeMode::Append);
1092         }
1093 
1094         assert_eq!(tags.get::<MyCustomTag>().unwrap().get(), Some("first one"));
1095 
1096         {
1097             let tags = tags.get_mut().unwrap();
1098             tags.add::<MyCustomTag>(&"second one", TagMergeMode::Append);
1099         }
1100 
1101         assert_eq!(
1102             tags.get::<MyCustomTag>().unwrap().get(),
1103             Some("first one, second one")
1104         );
1105     }
1106 
1107     #[test]
test_display()1108     fn test_display() {
1109         ::init().unwrap();
1110 
1111         format!("{}", TagList::new());
1112     }
1113 }
1114