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