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/// Opaque structure for building [`DiscovererInfo`][crate::DiscovererInfo] objects programmatically.
142#[cfg(feature = "v1_30")]
143#[cfg_attr(docsrs, doc(cfg(feature = "v1_30")))]
144#[derive(Debug)]
145#[doc(alias = "GstDiscovererInfoBuilder")]
146#[repr(transparent)]
147pub struct DiscovererInfoBuilder(std::ptr::NonNull<ffi::GstDiscovererInfoBuilder>);
148
149#[cfg(feature = "v1_30")]
150#[cfg_attr(docsrs, doc(cfg(feature = "v1_30")))]
151impl DiscovererInfoBuilder {
152    /// Creates a new [`DiscovererInfoBuilder`][crate::DiscovererInfoBuilder] for building a [`DiscovererInfo`][crate::DiscovererInfo].
153    ///
154    /// The stream info can be either:
155    /// - A single stream (audio, video, or subtitle) for files without a container
156    /// - A [`DiscovererContainerInfo`][crate::DiscovererContainerInfo] for files with a container and multiple streams
157    /// ## `uri`
158    /// the URI for this discoverer info
159    /// ## `stream_info`
160    /// the stream info (single stream or container)
161    ///
162    /// # Returns
163    ///
164    /// a new [`DiscovererInfoBuilder`][crate::DiscovererInfoBuilder]
165    #[doc(alias = "gst_discoverer_info_builder_new")]
166    pub fn new(uri: &str, stream_info: impl IsA<DiscovererStreamInfo>) -> Self {
167        skip_assert_initialized!();
168
169        unsafe {
170            let ptr = ffi::gst_discoverer_info_builder_new(
171                uri.to_glib_none().0,
172                stream_info.upcast().into_glib_ptr(),
173            );
174            DiscovererInfoBuilder(std::ptr::NonNull::new_unchecked(ptr))
175        }
176    }
177
178    #[doc(alias = "gst_discoverer_info_builder_set_duration")]
179    pub fn duration(self, duration: gst::ClockTime) -> Self {
180        unsafe {
181            ffi::gst_discoverer_info_builder_set_duration(self.0.as_ptr(), duration.into_glib());
182        }
183        self
184    }
185
186    #[doc(alias = "gst_discoverer_info_builder_set_live")]
187    pub fn live(self, live: bool) -> Self {
188        unsafe {
189            ffi::gst_discoverer_info_builder_set_live(self.0.as_ptr(), live.into_glib());
190        }
191        self
192    }
193
194    #[doc(alias = "gst_discoverer_info_builder_set_result")]
195    pub fn result(self, result: DiscovererResult) -> Self {
196        unsafe {
197            ffi::gst_discoverer_info_builder_set_result(self.0.as_ptr(), result.into_glib());
198        }
199        self
200    }
201
202    #[doc(alias = "gst_discoverer_info_builder_set_seekable")]
203    pub fn seekable(self, seekable: bool) -> Self {
204        unsafe {
205            ffi::gst_discoverer_info_builder_set_seekable(self.0.as_ptr(), seekable.into_glib());
206        }
207        self
208    }
209
210    #[doc(alias = "gst_discoverer_info_builder_set_tags")]
211    pub fn tags(self, tags: &gst::TagList) -> Self {
212        unsafe {
213            ffi::gst_discoverer_info_builder_set_tags(self.0.as_ptr(), tags.to_glib_none().0);
214        }
215        self
216    }
217
218    /// Builds and returns a new [`DiscovererInfo`][crate::DiscovererInfo]. This frees the builder.
219    ///
220    /// # Returns
221    ///
222    /// a new [`DiscovererInfo`][crate::DiscovererInfo]
223    #[doc(alias = "gst_discoverer_info_builder_build")]
224    pub fn build(self) -> DiscovererInfo {
225        unsafe {
226            // gst_discoverer_info_builder_build frees the builder,
227            // so prevent it from being dropped at the end of this scope/function
228            let s = std::mem::ManuallyDrop::new(self);
229
230            from_glib_full(ffi::gst_discoverer_info_builder_build(s.0.as_ptr()))
231        }
232    }
233}
234
235#[cfg(feature = "v1_30")]
236#[cfg_attr(docsrs, doc(cfg(feature = "v1_30")))]
237impl Drop for DiscovererInfoBuilder {
238    #[inline]
239    #[doc(alias = "gst_discoverer_info_builder_free")]
240    fn drop(&mut self) {
241        unsafe {
242            ffi::gst_discoverer_info_builder_free(self.0.as_ptr());
243        }
244    }
245}
246
247#[cfg(test)]
248mod tests {
249    #[test]
250    #[cfg(feature = "v1_30")]
251    fn discoverer_info_builder_basic() {
252        use super::*;
253
254        gst::init().unwrap();
255
256        let audio_caps = gst::Caps::builder("audio/x-raw")
257            .field("format", "S16LE")
258            .build();
259        let audio_info =
260            crate::DiscovererAudioInfoBuilder::new("audio_stream", &audio_caps).build();
261
262        let mut tags = gst::TagList::new();
263        tags.get_mut()
264            .unwrap()
265            .add::<gst::tags::Title>(&"some title", gst::TagMergeMode::Append);
266
267        let info = DiscovererInfoBuilder::new("file:///tmp/test.wav", audio_info)
268            .duration(gst::ClockTime::from_seconds(5))
269            .live(false)
270            .seekable(true)
271            .result(DiscovererResult::Ok)
272            .tags(&tags)
273            .build();
274
275        assert_eq!(info.duration(), Some(gst::ClockTime::from_seconds(5)));
276        assert!(!info.is_live());
277        assert!(info.is_seekable());
278        assert_eq!(info.result(), DiscovererResult::Ok);
279        assert_eq!(info.uri(), glib::GString::from("file:///tmp/test.wav"));
280    }
281}