1use 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")]
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 #[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 let mut toc_entry = TocEntry::new(TocEntryType::Chapter, "chapter");
304 toc_entry.get_mut().unwrap().set_start_stop_times(1, 10);
305
306 let toc_sub_entry = TocEntry::new(TocEntryType::Angle, "angle");
308 let parent = toc_sub_entry.parent();
309 assert!(parent.is_none());
310
311 toc_entry.get_mut().unwrap().append_sub_entry(toc_sub_entry);
313
314 let mut toc = Toc::new(TocScope::Global);
316 assert_eq!(toc.scope(), TocScope::Global);
317
318 toc.get_mut().unwrap().append_entry(toc_entry);
320 assert_eq!(toc.scope(), TocScope::Global);
321
322 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 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}