Skip to main content

gstreamer_pbutils/
discoverer.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use std::{boxed::Box as Box_, fmt, mem::transmute};
4
5use glib::{
6    prelude::*,
7    signal::{SignalHandlerId, connect_raw},
8    translate::*,
9};
10
11use crate::{Discoverer, DiscovererInfo, ffi};
12
13#[cfg(feature = "v1_30")]
14use crate::{DiscovererResult, DiscovererStreamInfo};
15
16impl Discoverer {
17    pub fn set_timeout(&self, timeout: gst::ClockTime) {
18        self.set_property("timeout", timeout);
19    }
20
21    pub fn timeout(&self) -> gst::ClockTime {
22        self.property("timeout")
23    }
24
25    #[doc(alias = "timeout")]
26    pub fn connect_timeout_notify<F: Fn(&Self) + Send + Sync + 'static>(
27        &self,
28        f: F,
29    ) -> SignalHandlerId {
30        unsafe {
31            let f: Box_<F> = Box_::new(f);
32            connect_raw(
33                self.as_ptr() as *mut _,
34                b"notify::timeout\0".as_ptr() as *const _,
35                Some(transmute::<*const (), unsafe extern "C" fn()>(
36                    notify_timeout_trampoline::<Self, F> as *const (),
37                )),
38                Box_::into_raw(f),
39            )
40        }
41    }
42}
43
44unsafe extern "C" fn notify_timeout_trampoline<P, F: Fn(&P) + Send + Sync + 'static>(
45    this: *mut ffi::GstDiscoverer,
46    _param_spec: glib::ffi::gpointer,
47    f: glib::ffi::gpointer,
48) where
49    P: IsA<Discoverer>,
50{
51    unsafe {
52        let f: &F = &*(f as *const F);
53        f(Discoverer::from_glib_borrow(this).unsafe_cast_ref())
54    }
55}
56
57pub struct DebugInfo<'a>(&'a DiscovererInfo);
58
59impl fmt::Debug for DebugInfo<'_> {
60    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
61        let stream_info = self.0.stream_info();
62        let stream_list = self.0.stream_list();
63        let container_streams = self.0.container_streams();
64        let audio_streams = self.0.audio_streams();
65        let video_streams = self.0.video_streams();
66        let subtitle_streams = self.0.subtitle_streams();
67
68        f.debug_struct("DiscovererInfo")
69            .field("uri", &self.0.uri())
70            .field("result", &self.0.result())
71            .field("duration", &self.0.duration())
72            .field("is-live", &self.0.is_live())
73            .field("is-seekable", &self.0.is_seekable())
74            .field(
75                "stream-info",
76                &stream_info.as_ref().map(|info| info.debug()),
77            )
78            .field(
79                "stream-list",
80                &stream_list
81                    .iter()
82                    .map(|info| info.debug())
83                    .collect::<Vec<_>>(),
84            )
85            .field(
86                "container-streams",
87                &container_streams
88                    .iter()
89                    .map(|info| info.debug())
90                    .collect::<Vec<_>>(),
91            )
92            .field(
93                "audio-streams",
94                &audio_streams
95                    .iter()
96                    .map(|info| info.debug())
97                    .collect::<Vec<_>>(),
98            )
99            .field(
100                "video-streams",
101                &video_streams
102                    .iter()
103                    .map(|info| info.debug())
104                    .collect::<Vec<_>>(),
105            )
106            .field(
107                "subtitle-streams",
108                &subtitle_streams
109                    .iter()
110                    .map(|info| info.debug())
111                    .collect::<Vec<_>>(),
112            )
113            .field("toc", &self.0.toc())
114            .field("misc", &self.0.misc())
115            .field(
116                "missing-elements-installer-details",
117                &self.0.missing_elements_installer_details(),
118            )
119            .finish()
120    }
121}
122
123impl DiscovererInfo {
124    pub fn debug(&self) -> DebugInfo<'_> {
125        DebugInfo(self)
126    }
127
128    #[cfg(feature = "v1_30")]
129    #[cfg_attr(docsrs, doc(cfg(feature = "v1_30")))]
130    #[doc(alias = "gst_discoverer_info_builder_new")]
131    pub fn builder(
132        uri: &str,
133        stream_info: impl IsA<DiscovererStreamInfo>,
134    ) -> DiscovererInfoBuilder {
135        skip_assert_initialized!();
136
137        DiscovererInfoBuilder::new(uri, stream_info)
138    }
139}
140
141#[cfg(feature = "v1_30")]
142#[cfg_attr(docsrs, doc(cfg(feature = "v1_30")))]
143#[derive(Debug)]
144#[doc(alias = "GstDiscovererInfoBuilder")]
145#[repr(transparent)]
146pub struct DiscovererInfoBuilder(std::ptr::NonNull<ffi::GstDiscovererInfoBuilder>);
147
148#[cfg(feature = "v1_30")]
149#[cfg_attr(docsrs, doc(cfg(feature = "v1_30")))]
150impl DiscovererInfoBuilder {
151    #[doc(alias = "gst_discoverer_info_builder_new")]
152    pub fn new(uri: &str, stream_info: impl IsA<DiscovererStreamInfo>) -> Self {
153        skip_assert_initialized!();
154
155        unsafe {
156            let ptr = ffi::gst_discoverer_info_builder_new(
157                uri.to_glib_none().0,
158                stream_info.upcast().into_glib_ptr(),
159            );
160            DiscovererInfoBuilder(std::ptr::NonNull::new_unchecked(ptr))
161        }
162    }
163
164    #[doc(alias = "gst_discoverer_info_builder_set_duration")]
165    pub fn duration(self, duration: gst::ClockTime) -> Self {
166        unsafe {
167            ffi::gst_discoverer_info_builder_set_duration(self.0.as_ptr(), duration.into_glib());
168        }
169        self
170    }
171
172    #[doc(alias = "gst_discoverer_info_builder_set_live")]
173    pub fn live(self, live: bool) -> Self {
174        unsafe {
175            ffi::gst_discoverer_info_builder_set_live(self.0.as_ptr(), live.into_glib());
176        }
177        self
178    }
179
180    #[doc(alias = "gst_discoverer_info_builder_set_result")]
181    pub fn result(self, result: DiscovererResult) -> Self {
182        unsafe {
183            ffi::gst_discoverer_info_builder_set_result(self.0.as_ptr(), result.into_glib());
184        }
185        self
186    }
187
188    #[doc(alias = "gst_discoverer_info_builder_set_seekable")]
189    pub fn seekable(self, seekable: bool) -> Self {
190        unsafe {
191            ffi::gst_discoverer_info_builder_set_seekable(self.0.as_ptr(), seekable.into_glib());
192        }
193        self
194    }
195
196    #[doc(alias = "gst_discoverer_info_builder_set_tags")]
197    pub fn tags(self, tags: &gst::TagList) -> Self {
198        unsafe {
199            ffi::gst_discoverer_info_builder_set_tags(self.0.as_ptr(), tags.to_glib_none().0);
200        }
201        self
202    }
203
204    #[doc(alias = "gst_discoverer_info_builder_build")]
205    pub fn build(self) -> DiscovererInfo {
206        unsafe {
207            // gst_discoverer_info_builder_build frees the builder,
208            // so prevent it from being dropped at the end of this scope/function
209            let s = std::mem::ManuallyDrop::new(self);
210
211            from_glib_full(ffi::gst_discoverer_info_builder_build(s.0.as_ptr()))
212        }
213    }
214}
215
216#[cfg(feature = "v1_30")]
217#[cfg_attr(docsrs, doc(cfg(feature = "v1_30")))]
218impl Drop for DiscovererInfoBuilder {
219    #[inline]
220    #[doc(alias = "gst_discoverer_info_builder_free")]
221    fn drop(&mut self) {
222        unsafe {
223            ffi::gst_discoverer_info_builder_free(self.0.as_ptr());
224        }
225    }
226}
227
228#[cfg(test)]
229mod tests {
230    #[test]
231    #[cfg(feature = "v1_30")]
232    fn discoverer_info_builder_basic() {
233        use super::*;
234
235        gst::init().unwrap();
236
237        let audio_caps = gst::Caps::builder("audio/x-raw")
238            .field("format", "S16LE")
239            .build();
240        let audio_info =
241            crate::DiscovererAudioInfoBuilder::new("audio_stream", &audio_caps).build();
242
243        let mut tags = gst::TagList::new();
244        tags.get_mut()
245            .unwrap()
246            .add::<gst::tags::Title>(&"some title", gst::TagMergeMode::Append);
247
248        let info = DiscovererInfoBuilder::new("file:///tmp/test.wav", audio_info)
249            .duration(gst::ClockTime::from_seconds(5))
250            .live(false)
251            .seekable(true)
252            .result(DiscovererResult::Ok)
253            .tags(&tags)
254            .build();
255
256        assert_eq!(info.duration(), Some(gst::ClockTime::from_seconds(5)));
257        assert!(!info.is_live());
258        assert!(info.is_seekable());
259        assert_eq!(info.result(), DiscovererResult::Ok);
260        assert_eq!(info.uri(), glib::GString::from("file:///tmp/test.wav"));
261    }
262}