gstreamer_rtsp_server/subclass/
rtsp_media.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use std::ptr;
4
5use glib::{prelude::*, subclass::prelude::*, translate::*};
6
7use crate::{ffi, RTSPMedia, RTSPThread};
8
9#[derive(Debug)]
10pub struct SDPInfo(ptr::NonNull<ffi::GstSDPInfo>);
11
12impl SDPInfo {
13    pub fn is_ipv6(&self) -> bool {
14        unsafe { from_glib(self.0.as_ref().is_ipv6) }
15    }
16
17    pub fn server_ip(&self) -> &str {
18        unsafe {
19            use std::ffi::CStr;
20            CStr::from_ptr(self.0.as_ref().server_ip).to_str().unwrap()
21        }
22    }
23}
24
25pub trait RTSPMediaImpl: RTSPMediaImplExt + ObjectImpl + Send + Sync {
26    /// handle a message
27    fn handle_message(&self, message: &gst::MessageRef) -> bool {
28        self.parent_handle_message(message)
29    }
30
31    /// Prepare `self` for streaming. This function will create the objects
32    /// to manage the streaming. A pipeline must have been set on `self` with
33    /// [`RTSPMediaExtManual::take_pipeline()`][crate::prelude::RTSPMediaExtManual::take_pipeline()].
34    ///
35    /// It will preroll the pipeline and collect vital information about the streams
36    /// such as the duration.
37    /// ## `thread`
38    /// a [`RTSPThread`][crate::RTSPThread] to run the
39    ///  bus handler or [`None`]
40    ///
41    /// # Returns
42    ///
43    /// [`true`] on success.
44    fn prepare(&self, thread: &RTSPThread) -> Result<(), gst::LoggableError> {
45        self.parent_prepare(thread)
46    }
47
48    /// Unprepare `self`. After this call, the media should be prepared again before
49    /// it can be used again. If the media is set to be non-reusable, a new instance
50    /// must be created.
51    ///
52    /// # Returns
53    ///
54    /// [`true`] on success.
55    fn unprepare(&self) -> Result<(), gst::LoggableError> {
56        self.parent_unprepare()
57    }
58
59    /// Suspend `self`. The state of the pipeline managed by `self` is set to
60    /// GST_STATE_NULL but all streams are kept. `self` can be prepared again
61    /// with [`RTSPMediaExt::unsuspend()`][crate::prelude::RTSPMediaExt::unsuspend()]
62    ///
63    /// `self` must be prepared with [`RTSPMediaExt::prepare()`][crate::prelude::RTSPMediaExt::prepare()];
64    ///
65    /// # Returns
66    ///
67    /// [`true`] on success.
68    fn suspend(&self) -> Result<(), gst::LoggableError> {
69        self.parent_suspend()
70    }
71
72    /// Unsuspend `self` if it was in a suspended state. This method does nothing
73    /// when the media was not in the suspended state.
74    ///
75    /// # Returns
76    ///
77    /// [`true`] on success.
78    fn unsuspend(&self) -> Result<(), gst::LoggableError> {
79        self.parent_unsuspend()
80    }
81
82    // TODO missing: convert_range
83
84    /// query the current position in the pipeline
85    fn query_position(&self) -> Option<gst::ClockTime> {
86        self.parent_query_position()
87    }
88
89    /// query when playback will stop
90    fn query_stop(&self) -> Option<gst::ClockTime> {
91        self.parent_query_stop()
92    }
93
94    fn create_rtpbin(&self) -> Option<gst::Element> {
95        self.parent_create_rtpbin()
96    }
97
98    fn setup_rtpbin(&self, rtpbin: &gst::Element) -> Result<(), gst::LoggableError> {
99        self.parent_setup_rtpbin(rtpbin)
100    }
101
102    /// Add `self` specific info to `sdp`. `info` is used to configure the connection
103    /// information in the SDP.
104    /// ## `sdp`
105    /// a `GstSDPMessage`
106    /// ## `info`
107    /// a `GstSDPInfo`
108    ///
109    /// # Returns
110    ///
111    /// TRUE on success.
112    fn setup_sdp(
113        &self,
114        sdp: &mut gst_sdp::SDPMessageRef,
115        info: &SDPInfo,
116    ) -> Result<(), gst::LoggableError> {
117        self.parent_setup_sdp(sdp, info)
118    }
119
120    fn new_stream(&self, stream: &crate::RTSPStream) {
121        self.parent_new_stream(stream);
122    }
123
124    fn removed_stream(&self, stream: &crate::RTSPStream) {
125        self.parent_removed_stream(stream);
126    }
127
128    fn prepared(&self) {
129        self.parent_prepared();
130    }
131
132    fn unprepared(&self) {
133        self.parent_unprepared();
134    }
135
136    fn target_state(&self, state: gst::State) {
137        self.parent_target_state(state);
138    }
139
140    fn new_state(&self, state: gst::State) {
141        self.parent_new_state(state);
142    }
143
144    /// Configure an SDP on `self` for receiving streams
145    /// ## `sdp`
146    /// a `GstSDPMessage`
147    ///
148    /// # Returns
149    ///
150    /// TRUE on success.
151    fn handle_sdp(&self, sdp: &gst_sdp::SDPMessageRef) -> Result<(), gst::LoggableError> {
152        self.parent_handle_sdp(sdp)
153    }
154}
155
156mod sealed {
157    pub trait Sealed {}
158    impl<T: super::RTSPMediaImplExt> Sealed for T {}
159}
160
161pub trait RTSPMediaImplExt: sealed::Sealed + ObjectSubclass {
162    fn parent_handle_message(&self, message: &gst::MessageRef) -> bool {
163        unsafe {
164            let data = Self::type_data();
165            let parent_class = data.as_ref().parent_class() as *mut ffi::GstRTSPMediaClass;
166            if let Some(f) = (*parent_class).handle_message {
167                from_glib(f(
168                    self.obj().unsafe_cast_ref::<RTSPMedia>().to_glib_none().0,
169                    message.as_ptr() as *mut _,
170                ))
171            } else {
172                false
173            }
174        }
175    }
176
177    fn parent_prepare(&self, thread: &RTSPThread) -> Result<(), gst::LoggableError> {
178        unsafe {
179            let data = Self::type_data();
180            let parent_class = data.as_ref().parent_class() as *mut ffi::GstRTSPMediaClass;
181            if let Some(f) = (*parent_class).prepare {
182                gst::result_from_gboolean!(
183                    f(
184                        self.obj().unsafe_cast_ref::<RTSPMedia>().to_glib_none().0,
185                        thread.to_glib_none().0
186                    ),
187                    gst::CAT_RUST,
188                    "Parent function `prepare` failed"
189                )
190            } else {
191                Ok(())
192            }
193        }
194    }
195
196    fn parent_unprepare(&self) -> Result<(), gst::LoggableError> {
197        unsafe {
198            let data = Self::type_data();
199            let parent_class = data.as_ref().parent_class() as *mut ffi::GstRTSPMediaClass;
200            if let Some(f) = (*parent_class).unprepare {
201                gst::result_from_gboolean!(
202                    f(self.obj().unsafe_cast_ref::<RTSPMedia>().to_glib_none().0),
203                    gst::CAT_RUST,
204                    "Parent function `unprepare` failed"
205                )
206            } else {
207                Ok(())
208            }
209        }
210    }
211
212    fn parent_suspend(&self) -> Result<(), gst::LoggableError> {
213        unsafe {
214            let data = Self::type_data();
215            let parent_class = data.as_ref().parent_class() as *mut ffi::GstRTSPMediaClass;
216            if let Some(f) = (*parent_class).suspend {
217                gst::result_from_gboolean!(
218                    f(self.obj().unsafe_cast_ref::<RTSPMedia>().to_glib_none().0),
219                    gst::CAT_RUST,
220                    "Parent function `suspend` failed"
221                )
222            } else {
223                Ok(())
224            }
225        }
226    }
227
228    fn parent_unsuspend(&self) -> Result<(), gst::LoggableError> {
229        unsafe {
230            let data = Self::type_data();
231            let parent_class = data.as_ref().parent_class() as *mut ffi::GstRTSPMediaClass;
232            if let Some(f) = (*parent_class).unsuspend {
233                gst::result_from_gboolean!(
234                    f(self.obj().unsafe_cast_ref::<RTSPMedia>().to_glib_none().0),
235                    gst::CAT_RUST,
236                    "Parent function `unsuspend` failed"
237                )
238            } else {
239                Ok(())
240            }
241        }
242    }
243
244    // TODO missing: convert_range
245    fn parent_query_position(&self) -> Option<gst::ClockTime> {
246        unsafe {
247            use std::mem;
248
249            let data = Self::type_data();
250            let parent_class = data.as_ref().parent_class() as *mut ffi::GstRTSPMediaClass;
251            if let Some(f) = (*parent_class).query_position {
252                let mut position = mem::MaybeUninit::uninit();
253                if f(
254                    self.obj().unsafe_cast_ref::<RTSPMedia>().to_glib_none().0,
255                    position.as_mut_ptr(),
256                ) == glib::ffi::GFALSE
257                {
258                    None
259                } else {
260                    from_glib(position.assume_init() as u64)
261                }
262            } else {
263                None
264            }
265        }
266    }
267
268    fn parent_query_stop(&self) -> Option<gst::ClockTime> {
269        unsafe {
270            use std::mem;
271
272            let data = Self::type_data();
273            let parent_class = data.as_ref().parent_class() as *mut ffi::GstRTSPMediaClass;
274            if let Some(f) = (*parent_class).query_stop {
275                let mut stop = mem::MaybeUninit::uninit();
276                if f(
277                    self.obj().unsafe_cast_ref::<RTSPMedia>().to_glib_none().0,
278                    stop.as_mut_ptr(),
279                ) == glib::ffi::GFALSE
280                {
281                    None
282                } else {
283                    from_glib(stop.assume_init() as u64)
284                }
285            } else {
286                None
287            }
288        }
289    }
290
291    fn parent_create_rtpbin(&self) -> Option<gst::Element> {
292        unsafe {
293            let data = Self::type_data();
294            let parent_class = data.as_ref().parent_class() as *mut ffi::GstRTSPMediaClass;
295            let f = (*parent_class)
296                .create_rtpbin
297                .expect("No `create_rtpbin` virtual method implementation in parent class");
298
299            from_glib_none(f(self
300                .obj()
301                .unsafe_cast_ref::<RTSPMedia>()
302                .to_glib_none()
303                .0))
304        }
305    }
306
307    fn parent_setup_rtpbin(&self, rtpbin: &gst::Element) -> Result<(), gst::LoggableError> {
308        unsafe {
309            let data = Self::type_data();
310            let parent_class = data.as_ref().parent_class() as *mut ffi::GstRTSPMediaClass;
311            if let Some(f) = (*parent_class).setup_rtpbin {
312                let ptr = rtpbin.to_glib_none().0;
313
314                // The C code assumes to pass a floating reference around so let's make sure we do
315                glib::gobject_ffi::g_object_force_floating(ptr as *mut _);
316
317                let res = gst::result_from_gboolean!(
318                    f(
319                        self.obj().unsafe_cast_ref::<RTSPMedia>().to_glib_none().0,
320                        ptr
321                    ),
322                    gst::CAT_RUST,
323                    "Parent function `setup_sdp` failed"
324                );
325
326                // If the code didn't accidentally sink it then we have to do that
327                // here now so that we don't have any floating reference on our side
328                // anymore
329                if glib::gobject_ffi::g_object_is_floating(ptr as *mut _) != glib::ffi::GFALSE {
330                    glib::gobject_ffi::g_object_ref_sink(ptr as *mut _);
331                }
332
333                res
334            } else {
335                Ok(())
336            }
337        }
338    }
339
340    fn parent_setup_sdp(
341        &self,
342        sdp: &mut gst_sdp::SDPMessageRef,
343        info: &SDPInfo,
344    ) -> Result<(), gst::LoggableError> {
345        unsafe {
346            let data = Self::type_data();
347            let parent_class = data.as_ref().parent_class() as *mut ffi::GstRTSPMediaClass;
348            let f = (*parent_class)
349                .setup_sdp
350                .expect("No `setup_sdp` virtual method implementation in parent class");
351
352            gst::result_from_gboolean!(
353                f(
354                    self.obj().unsafe_cast_ref::<RTSPMedia>().to_glib_none().0,
355                    sdp as *mut _ as *mut gst_sdp::ffi::GstSDPMessage,
356                    info.0.as_ptr()
357                ),
358                gst::CAT_RUST,
359                "Parent function `setup_sdp` failed"
360            )
361        }
362    }
363
364    fn parent_new_stream(&self, stream: &crate::RTSPStream) {
365        unsafe {
366            let data = Self::type_data();
367            let parent_class = data.as_ref().parent_class() as *mut ffi::GstRTSPMediaClass;
368            if let Some(f) = (*parent_class).new_stream {
369                f(
370                    self.obj().unsafe_cast_ref::<RTSPMedia>().to_glib_none().0,
371                    stream.to_glib_none().0,
372                );
373            }
374        }
375    }
376
377    fn parent_removed_stream(&self, stream: &crate::RTSPStream) {
378        unsafe {
379            let data = Self::type_data();
380            let parent_class = data.as_ref().parent_class() as *mut ffi::GstRTSPMediaClass;
381            if let Some(f) = (*parent_class).removed_stream {
382                f(
383                    self.obj().unsafe_cast_ref::<RTSPMedia>().to_glib_none().0,
384                    stream.to_glib_none().0,
385                );
386            }
387        }
388    }
389
390    fn parent_prepared(&self) {
391        unsafe {
392            let data = Self::type_data();
393            let parent_class = data.as_ref().parent_class() as *mut ffi::GstRTSPMediaClass;
394            if let Some(f) = (*parent_class).prepared {
395                f(self.obj().unsafe_cast_ref::<RTSPMedia>().to_glib_none().0);
396            }
397        }
398    }
399
400    fn parent_unprepared(&self) {
401        unsafe {
402            let data = Self::type_data();
403            let parent_class = data.as_ref().parent_class() as *mut ffi::GstRTSPMediaClass;
404            if let Some(f) = (*parent_class).unprepared {
405                f(self.obj().unsafe_cast_ref::<RTSPMedia>().to_glib_none().0);
406            }
407        }
408    }
409
410    fn parent_target_state(&self, state: gst::State) {
411        unsafe {
412            let data = Self::type_data();
413            let parent_class = data.as_ref().parent_class() as *mut ffi::GstRTSPMediaClass;
414            if let Some(f) = (*parent_class).target_state {
415                f(
416                    self.obj().unsafe_cast_ref::<RTSPMedia>().to_glib_none().0,
417                    state.into_glib(),
418                );
419            }
420        }
421    }
422
423    fn parent_new_state(&self, state: gst::State) {
424        unsafe {
425            let data = Self::type_data();
426            let parent_class = data.as_ref().parent_class() as *mut ffi::GstRTSPMediaClass;
427            if let Some(f) = (*parent_class).new_state {
428                f(
429                    self.obj().unsafe_cast_ref::<RTSPMedia>().to_glib_none().0,
430                    state.into_glib(),
431                );
432            }
433        }
434    }
435
436    fn parent_handle_sdp(&self, sdp: &gst_sdp::SDPMessageRef) -> Result<(), gst::LoggableError> {
437        unsafe {
438            let data = Self::type_data();
439            let parent_class = data.as_ref().parent_class() as *mut ffi::GstRTSPMediaClass;
440            let f = (*parent_class)
441                .handle_sdp
442                .expect("No `handle_sdp` virtual method implementation in parent class");
443
444            gst::result_from_gboolean!(
445                f(
446                    self.obj().unsafe_cast_ref::<RTSPMedia>().to_glib_none().0,
447                    sdp as *const _ as *mut gst_sdp::ffi::GstSDPMessage
448                ),
449                gst::CAT_RUST,
450                "Parent function `handle_sdp` failed"
451            )
452        }
453    }
454}
455
456impl<T: RTSPMediaImpl> RTSPMediaImplExt for T {}
457
458unsafe impl<T: RTSPMediaImpl> IsSubclassable<T> for RTSPMedia {
459    fn class_init(klass: &mut glib::Class<Self>) {
460        Self::parent_class_init::<T>(klass);
461        let klass = klass.as_mut();
462        klass.handle_message = Some(media_handle_message::<T>);
463        klass.prepare = Some(media_prepare::<T>);
464        klass.unprepare = Some(media_unprepare::<T>);
465        klass.suspend = Some(media_suspend::<T>);
466        klass.unsuspend = Some(media_unsuspend::<T>);
467        klass.query_position = Some(media_query_position::<T>);
468        klass.query_stop = Some(media_query_stop::<T>);
469        klass.create_rtpbin = Some(media_create_rtpbin::<T>);
470        klass.setup_rtpbin = Some(media_setup_rtpbin::<T>);
471        klass.setup_sdp = Some(media_setup_sdp::<T>);
472        klass.new_stream = Some(media_new_stream::<T>);
473        klass.removed_stream = Some(media_removed_stream::<T>);
474        klass.prepared = Some(media_prepared::<T>);
475        klass.unprepared = Some(media_unprepared::<T>);
476        klass.target_state = Some(media_target_state::<T>);
477        klass.new_state = Some(media_new_state::<T>);
478        klass.handle_sdp = Some(media_handle_sdp::<T>);
479    }
480}
481
482unsafe extern "C" fn media_handle_message<T: RTSPMediaImpl>(
483    ptr: *mut ffi::GstRTSPMedia,
484    message: *mut gst::ffi::GstMessage,
485) -> glib::ffi::gboolean {
486    let instance = &*(ptr as *mut T::Instance);
487    let imp = instance.imp();
488
489    imp.handle_message(gst::MessageRef::from_ptr(message))
490        .into_glib()
491}
492
493unsafe extern "C" fn media_prepare<T: RTSPMediaImpl>(
494    ptr: *mut ffi::GstRTSPMedia,
495    thread: *mut ffi::GstRTSPThread,
496) -> glib::ffi::gboolean {
497    let instance = &*(ptr as *mut T::Instance);
498    let imp = instance.imp();
499
500    match imp.prepare(&from_glib_borrow(thread)) {
501        Ok(()) => glib::ffi::GTRUE,
502        Err(err) => {
503            err.log_with_imp(imp);
504            glib::ffi::GFALSE
505        }
506    }
507}
508
509unsafe extern "C" fn media_unprepare<T: RTSPMediaImpl>(
510    ptr: *mut ffi::GstRTSPMedia,
511) -> glib::ffi::gboolean {
512    let instance = &*(ptr as *mut T::Instance);
513    let imp = instance.imp();
514
515    match imp.unprepare() {
516        Ok(()) => glib::ffi::GTRUE,
517        Err(err) => {
518            err.log_with_imp(imp);
519            glib::ffi::GFALSE
520        }
521    }
522}
523
524unsafe extern "C" fn media_suspend<T: RTSPMediaImpl>(
525    ptr: *mut ffi::GstRTSPMedia,
526) -> glib::ffi::gboolean {
527    let instance = &*(ptr as *mut T::Instance);
528    let imp = instance.imp();
529
530    match imp.suspend() {
531        Ok(()) => glib::ffi::GTRUE,
532        Err(err) => {
533            err.log_with_imp(imp);
534            glib::ffi::GFALSE
535        }
536    }
537}
538
539unsafe extern "C" fn media_unsuspend<T: RTSPMediaImpl>(
540    ptr: *mut ffi::GstRTSPMedia,
541) -> glib::ffi::gboolean {
542    let instance = &*(ptr as *mut T::Instance);
543    let imp = instance.imp();
544
545    match imp.unsuspend() {
546        Ok(()) => glib::ffi::GTRUE,
547        Err(err) => {
548            err.log_with_imp(imp);
549            glib::ffi::GFALSE
550        }
551    }
552}
553
554unsafe extern "C" fn media_query_position<T: RTSPMediaImpl>(
555    ptr: *mut ffi::GstRTSPMedia,
556    position: *mut i64,
557) -> glib::ffi::gboolean {
558    let instance = &*(ptr as *mut T::Instance);
559    let imp = instance.imp();
560
561    match imp.query_position() {
562        Some(pos) => {
563            *position = pos.into_glib() as i64;
564            glib::ffi::GTRUE
565        }
566        None => glib::ffi::GFALSE,
567    }
568}
569
570unsafe extern "C" fn media_query_stop<T: RTSPMediaImpl>(
571    ptr: *mut ffi::GstRTSPMedia,
572    stop: *mut i64,
573) -> glib::ffi::gboolean {
574    let instance = &*(ptr as *mut T::Instance);
575    let imp = instance.imp();
576
577    match imp.query_stop() {
578        Some(s) => {
579            *stop = s.into_glib() as i64;
580            glib::ffi::GTRUE
581        }
582        None => glib::ffi::GFALSE,
583    }
584}
585
586unsafe extern "C" fn media_create_rtpbin<T: RTSPMediaImpl>(
587    ptr: *mut ffi::GstRTSPMedia,
588) -> *mut gst::ffi::GstElement {
589    let instance = &*(ptr as *mut T::Instance);
590    let imp = instance.imp();
591
592    let res: *mut gst::ffi::GstElement = imp.create_rtpbin().into_glib_ptr();
593
594    if !res.is_null() {
595        glib::gobject_ffi::g_object_force_floating(res as *mut _);
596    }
597
598    res
599}
600
601unsafe extern "C" fn media_setup_rtpbin<T: RTSPMediaImpl>(
602    ptr: *mut ffi::GstRTSPMedia,
603    rtpbin: *mut gst::ffi::GstElement,
604) -> glib::ffi::gboolean {
605    let instance = &*(ptr as *mut T::Instance);
606    let imp = instance.imp();
607
608    // If the rtpbin was floating before make sure it is not anymore for now so
609    // we don't accidentally take ownership of it somewhere along the line
610    if glib::gobject_ffi::g_object_is_floating(rtpbin as *mut _) != glib::ffi::GFALSE {
611        glib::gobject_ffi::g_object_ref_sink(rtpbin as *mut _);
612    }
613
614    let res = match imp.setup_rtpbin(&from_glib_borrow(rtpbin)) {
615        Ok(()) => glib::ffi::GTRUE,
616        Err(err) => {
617            err.log_with_imp(imp);
618            glib::ffi::GFALSE
619        }
620    };
621
622    // Ensure that the rtpbin is still floating afterwards here
623    glib::gobject_ffi::g_object_force_floating(rtpbin as *mut _);
624
625    res
626}
627
628unsafe extern "C" fn media_setup_sdp<T: RTSPMediaImpl>(
629    ptr: *mut ffi::GstRTSPMedia,
630    sdp: *mut gst_sdp::ffi::GstSDPMessage,
631    info: *mut ffi::GstSDPInfo,
632) -> glib::ffi::gboolean {
633    let instance = &*(ptr as *mut T::Instance);
634    let imp = instance.imp();
635
636    match imp.setup_sdp(
637        &mut *(sdp as *mut gst_sdp::SDPMessageRef),
638        &SDPInfo(ptr::NonNull::new(info).expect("NULL SDPInfo")),
639    ) {
640        Ok(()) => glib::ffi::GTRUE,
641        Err(err) => {
642            err.log_with_imp(imp);
643            glib::ffi::GFALSE
644        }
645    }
646}
647
648unsafe extern "C" fn media_new_stream<T: RTSPMediaImpl>(
649    ptr: *mut ffi::GstRTSPMedia,
650    stream: *mut ffi::GstRTSPStream,
651) {
652    let instance = &*(ptr as *mut T::Instance);
653    let imp = instance.imp();
654
655    imp.new_stream(&from_glib_borrow(stream));
656}
657
658unsafe extern "C" fn media_removed_stream<T: RTSPMediaImpl>(
659    ptr: *mut ffi::GstRTSPMedia,
660    stream: *mut ffi::GstRTSPStream,
661) {
662    let instance = &*(ptr as *mut T::Instance);
663    let imp = instance.imp();
664
665    imp.removed_stream(&from_glib_borrow(stream));
666}
667
668unsafe extern "C" fn media_prepared<T: RTSPMediaImpl>(ptr: *mut ffi::GstRTSPMedia) {
669    let instance = &*(ptr as *mut T::Instance);
670    let imp = instance.imp();
671
672    imp.prepared();
673}
674
675unsafe extern "C" fn media_unprepared<T: RTSPMediaImpl>(ptr: *mut ffi::GstRTSPMedia) {
676    let instance = &*(ptr as *mut T::Instance);
677    let imp = instance.imp();
678
679    imp.unprepared();
680}
681
682unsafe extern "C" fn media_target_state<T: RTSPMediaImpl>(
683    ptr: *mut ffi::GstRTSPMedia,
684    state: gst::ffi::GstState,
685) {
686    let instance = &*(ptr as *mut T::Instance);
687    let imp = instance.imp();
688
689    imp.target_state(from_glib(state));
690}
691
692unsafe extern "C" fn media_new_state<T: RTSPMediaImpl>(
693    ptr: *mut ffi::GstRTSPMedia,
694    state: gst::ffi::GstState,
695) {
696    let instance = &*(ptr as *mut T::Instance);
697    let imp = instance.imp();
698
699    imp.new_state(from_glib(state));
700}
701
702unsafe extern "C" fn media_handle_sdp<T: RTSPMediaImpl>(
703    ptr: *mut ffi::GstRTSPMedia,
704    sdp: *mut gst_sdp::ffi::GstSDPMessage,
705) -> glib::ffi::gboolean {
706    let instance = &*(ptr as *mut T::Instance);
707    let imp = instance.imp();
708
709    match imp.handle_sdp(&*(sdp as *const gst_sdp::SDPMessageRef)) {
710        Ok(()) => glib::ffi::GTRUE,
711        Err(err) => {
712            err.log_with_imp(imp);
713            glib::ffi::GFALSE
714        }
715    }
716}