gstreamer/
toc.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use std::{ffi::CStr, fmt, mem, ptr};
4
5use glib::translate::{
6    from_glib, from_glib_full, from_glib_none, FromGlibPtrContainer, IntoGlib, IntoGlibPtr,
7    ToGlibPtr,
8};
9
10use crate::{ffi, TagList, TagMergeMode, TocEntryType, TocLoopType, TocScope};
11
12mini_object_wrapper!(Toc, TocRef, ffi::GstToc, || { ffi::gst_toc_get_type() });
13
14impl Toc {
15    #[doc(alias = "gst_toc_new")]
16    pub fn new(scope: TocScope) -> Self {
17        assert_initialized_main_thread!();
18        unsafe { from_glib_full(ffi::gst_toc_new(scope.into_glib())) }
19    }
20}
21
22impl TocRef {
23    #[doc(alias = "get_scope")]
24    #[doc(alias = "gst_toc_get_scope")]
25    pub fn scope(&self) -> TocScope {
26        unsafe { from_glib(ffi::gst_toc_get_scope(self.as_ptr())) }
27    }
28
29    #[doc(alias = "gst_toc_find_entry")]
30    pub fn find_entry(&self, uid: &str) -> Option<TocEntry> {
31        unsafe { from_glib_none(ffi::gst_toc_find_entry(self.as_ptr(), uid.to_glib_none().0)) }
32    }
33
34    #[doc(alias = "get_entries")]
35    #[doc(alias = "gst_toc_get_entries")]
36    pub fn entries(&self) -> Vec<TocEntry> {
37        unsafe { FromGlibPtrContainer::from_glib_none(ffi::gst_toc_get_entries(self.as_ptr())) }
38    }
39
40    #[doc(alias = "gst_toc_append_entry")]
41    pub fn append_entry(&mut self, entry: TocEntry) {
42        unsafe {
43            ffi::gst_toc_append_entry(self.as_mut_ptr(), entry.into_glib_ptr());
44        }
45    }
46
47    #[doc(alias = "get_tags")]
48    #[doc(alias = "gst_toc_get_tags")]
49    pub fn tags(&self) -> Option<TagList> {
50        unsafe { from_glib_none(ffi::gst_toc_get_tags(self.as_ptr())) }
51    }
52
53    #[doc(alias = "gst_toc_set_tags")]
54    pub fn set_tags(&mut self, tag_list: impl Into<Option<TagList>>) {
55        unsafe {
56            ffi::gst_toc_set_tags(
57                self.as_mut_ptr(),
58                tag_list
59                    .into()
60                    .map(|t| t.into_glib_ptr())
61                    .unwrap_or(ptr::null_mut()),
62            );
63        }
64    }
65
66    #[doc(alias = "gst_toc_merge_tags")]
67    pub fn merge_tags<'a>(&mut self, tag_list: impl Into<Option<&'a TagList>>, mode: TagMergeMode) {
68        unsafe {
69            ffi::gst_toc_merge_tags(
70                self.as_mut_ptr(),
71                tag_list
72                    .into()
73                    .map(|l| l.as_mut_ptr())
74                    .unwrap_or(ptr::null_mut()),
75                mode.into_glib(),
76            );
77        }
78    }
79
80    #[doc(alias = "gst_toc_dump")]
81    pub fn dump(&self) {
82        unsafe {
83            ffi::gst_toc_dump(self.as_mut_ptr());
84        }
85    }
86}
87
88impl fmt::Debug for Toc {
89    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
90        TocRef::fmt(self, f)
91    }
92}
93
94impl fmt::Debug for TocRef {
95    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
96        f.debug_struct("Toc")
97            .field("scope", &self.scope())
98            .field("tags", &self.tags())
99            .field("entries", &self.entries())
100            .finish()
101    }
102}
103
104mini_object_wrapper!(TocEntry, TocEntryRef, ffi::GstTocEntry, || {
105    ffi::gst_toc_entry_get_type()
106});
107
108impl TocEntry {
109    #[doc(alias = "gst_toc_entry_new")]
110    pub fn new(type_: TocEntryType, uid: &str) -> Self {
111        assert_initialized_main_thread!();
112        unsafe {
113            from_glib_full(ffi::gst_toc_entry_new(
114                type_.into_glib(),
115                uid.to_glib_none().0,
116            ))
117        }
118    }
119}
120
121impl TocEntryRef {
122    #[doc(alias = "get_entry_type")]
123    #[doc(alias = "gst_toc_entry_get_entry_type")]
124    pub fn entry_type(&self) -> TocEntryType {
125        unsafe { from_glib(ffi::gst_toc_entry_get_entry_type(self.as_ptr())) }
126    }
127
128    #[doc(alias = "get_uid")]
129    #[doc(alias = "gst_toc_entry_get_uid")]
130    pub fn uid(&self) -> &str {
131        unsafe {
132            CStr::from_ptr(ffi::gst_toc_entry_get_uid(self.as_ptr()))
133                .to_str()
134                .unwrap()
135        }
136    }
137
138    #[doc(alias = "gst_toc_entry_append_sub_entry")]
139    pub fn append_sub_entry(&mut self, subentry: TocEntry) {
140        unsafe {
141            ffi::gst_toc_entry_append_sub_entry(self.as_mut_ptr(), subentry.into_glib_ptr());
142        }
143    }
144
145    #[doc(alias = "get_sub_entries")]
146    #[doc(alias = "gst_toc_entry_get_sub_entries")]
147    pub fn sub_entries(&self) -> Vec<TocEntry> {
148        unsafe {
149            FromGlibPtrContainer::from_glib_none(ffi::gst_toc_entry_get_sub_entries(self.as_ptr()))
150        }
151    }
152
153    #[doc(alias = "get_parent")]
154    #[doc(alias = "gst_toc_entry_get_parent")]
155    pub fn parent(&self) -> Option<TocEntry> {
156        unsafe { from_glib_none(ffi::gst_toc_entry_get_parent(self.as_mut_ptr())) }
157    }
158
159    #[doc(alias = "get_start_stop_times")]
160    #[doc(alias = "gst_toc_entry_get_start_stop_times")]
161    pub fn start_stop_times(&self) -> Option<(i64, i64)> {
162        unsafe {
163            let mut start = mem::MaybeUninit::uninit();
164            let mut stop = mem::MaybeUninit::uninit();
165
166            if from_glib(ffi::gst_toc_entry_get_start_stop_times(
167                self.as_ptr(),
168                start.as_mut_ptr(),
169                stop.as_mut_ptr(),
170            )) {
171                Some((start.assume_init(), stop.assume_init()))
172            } else {
173                None
174            }
175        }
176    }
177
178    #[doc(alias = "gst_toc_entry_set_start_stop_times")]
179    pub fn set_start_stop_times(&mut self, start: i64, stop: i64) {
180        unsafe {
181            ffi::gst_toc_entry_set_start_stop_times(self.as_mut_ptr(), start, stop);
182        }
183    }
184
185    #[doc(alias = "get_tags")]
186    #[doc(alias = "gst_toc_entry_get_tags")]
187    pub fn tags(&self) -> Option<TagList> {
188        unsafe { from_glib_none(ffi::gst_toc_entry_get_tags(self.as_ptr())) }
189    }
190
191    #[doc(alias = "gst_toc_entry_set_tags")]
192    pub fn set_tags(&mut self, tag_list: impl Into<Option<TagList>>) {
193        unsafe {
194            ffi::gst_toc_entry_set_tags(
195                self.as_mut_ptr(),
196                tag_list
197                    .into()
198                    .map(|t| t.into_glib_ptr())
199                    .unwrap_or(ptr::null_mut()),
200            );
201        }
202    }
203
204    #[doc(alias = "gst_toc_entry_merge_tags")]
205    pub fn merge_tags<'a>(&mut self, tag_list: impl Into<Option<&'a TagList>>, mode: TagMergeMode) {
206        unsafe {
207            ffi::gst_toc_entry_merge_tags(
208                self.as_mut_ptr(),
209                tag_list
210                    .into()
211                    .map(|l| l.as_mut_ptr())
212                    .unwrap_or(ptr::null_mut()),
213                mode.into_glib(),
214            );
215        }
216    }
217
218    #[doc(alias = "gst_toc_entry_is_alternative")]
219    pub fn is_alternative(&self) -> bool {
220        unsafe { from_glib(ffi::gst_toc_entry_is_alternative(self.as_ptr())) }
221    }
222
223    #[doc(alias = "gst_toc_entry_is_sequence")]
224    pub fn is_sequence(&self) -> bool {
225        unsafe { from_glib(ffi::gst_toc_entry_is_sequence(self.as_ptr())) }
226    }
227
228    #[doc(alias = "get_loop")]
229    pub fn loop_(&self) -> Option<(TocLoopType, i32)> {
230        unsafe {
231            let mut loop_type = mem::MaybeUninit::uninit();
232            let mut repeat_count = mem::MaybeUninit::uninit();
233            if from_glib(ffi::gst_toc_entry_get_loop(
234                self.as_ptr(),
235                loop_type.as_mut_ptr(),
236                repeat_count.as_mut_ptr(),
237            )) {
238                Some((
239                    from_glib(loop_type.assume_init()),
240                    repeat_count.assume_init(),
241                ))
242            } else {
243                None
244            }
245        }
246    }
247
248    #[doc(alias = "gst_toc_entry_set_loop")]
249    pub fn set_loop(&mut self, loop_type: TocLoopType, repeat_count: i32) {
250        unsafe {
251            ffi::gst_toc_entry_set_loop(self.as_mut_ptr(), loop_type.into_glib(), repeat_count);
252        }
253    }
254}
255
256impl fmt::Debug for TocEntry {
257    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
258        TocEntryRef::fmt(self, f)
259    }
260}
261
262impl fmt::Debug for TocEntryRef {
263    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
264        f.debug_struct("TocEntry")
265            .field("entry_type", &self.entry_type())
266            .field("uid", &self.uid())
267            .field("start_stop", &self.start_stop_times())
268            .field("tags", &self.tags())
269            .field("is_alternative", &self.is_alternative())
270            .field("is_sequence", &self.is_sequence())
271            .field("loop", &self.loop_())
272            .field("sub_entries", &self.sub_entries())
273            .finish()
274    }
275}
276
277#[cfg(test)]
278mod tests {
279    use super::*;
280
281    #[test]
282    fn test_simple() {
283        crate::init().unwrap();
284
285        // Top level toc entry
286        let mut toc_entry = TocEntry::new(TocEntryType::Chapter, "chapter");
287        toc_entry.get_mut().unwrap().set_start_stop_times(1, 10);
288
289        // Toc sub entry
290        let toc_sub_entry = TocEntry::new(TocEntryType::Angle, "angle");
291        let parent = toc_sub_entry.parent();
292        assert!(parent.is_none());
293
294        // Append sub entry
295        toc_entry.get_mut().unwrap().append_sub_entry(toc_sub_entry);
296
297        // Toc
298        let mut toc = Toc::new(TocScope::Global);
299        assert_eq!(toc.scope(), TocScope::Global);
300
301        // Append toc entry
302        toc.get_mut().unwrap().append_entry(toc_entry);
303        assert_eq!(toc.scope(), TocScope::Global);
304
305        // Check toc entries
306        let toc_entries = toc.entries();
307        assert_eq!(toc_entries.len(), 1);
308
309        let toc_parent_entry = &toc_entries[0];
310        assert_eq!(toc_parent_entry.entry_type(), TocEntryType::Chapter);
311        assert_eq!(toc_parent_entry.uid(), "chapter");
312        let start_stop_times = toc_parent_entry.start_stop_times();
313        assert!(start_stop_times.is_some());
314        assert_eq!(start_stop_times.unwrap(), (1, 10));
315
316        // Check sub entry
317        let toc_sub_entries = toc_parent_entry.sub_entries();
318        assert_eq!(toc_sub_entries.len(), 1);
319        let toc_sub_entry = &toc_sub_entries[0];
320        assert_eq!(toc_sub_entry.entry_type(), TocEntryType::Angle);
321        let parent = toc_sub_entry.parent();
322        assert!(parent.is_some());
323        assert_eq!(parent.unwrap().entry_type(), TocEntryType::Chapter);
324    }
325}