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