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: ObjectImpl + ObjectSubclass<Type: IsA<RTSPMedia>> + 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
156pub trait RTSPMediaImplExt: RTSPMediaImpl {
157    fn parent_handle_message(&self, message: &gst::MessageRef) -> bool {
158        unsafe {
159            let data = Self::type_data();
160            let parent_class = data.as_ref().parent_class() as *mut ffi::GstRTSPMediaClass;
161            if let Some(f) = (*parent_class).handle_message {
162                from_glib(f(
163                    self.obj().unsafe_cast_ref::<RTSPMedia>().to_glib_none().0,
164                    message.as_ptr() as *mut _,
165                ))
166            } else {
167                false
168            }
169        }
170    }
171
172    fn parent_prepare(&self, thread: &RTSPThread) -> Result<(), gst::LoggableError> {
173        unsafe {
174            let data = Self::type_data();
175            let parent_class = data.as_ref().parent_class() as *mut ffi::GstRTSPMediaClass;
176            if let Some(f) = (*parent_class).prepare {
177                gst::result_from_gboolean!(
178                    f(
179                        self.obj().unsafe_cast_ref::<RTSPMedia>().to_glib_none().0,
180                        thread.to_glib_none().0
181                    ),
182                    gst::CAT_RUST,
183                    "Parent function `prepare` failed"
184                )
185            } else {
186                Ok(())
187            }
188        }
189    }
190
191    fn parent_unprepare(&self) -> Result<(), gst::LoggableError> {
192        unsafe {
193            let data = Self::type_data();
194            let parent_class = data.as_ref().parent_class() as *mut ffi::GstRTSPMediaClass;
195            if let Some(f) = (*parent_class).unprepare {
196                gst::result_from_gboolean!(
197                    f(self.obj().unsafe_cast_ref::<RTSPMedia>().to_glib_none().0),
198                    gst::CAT_RUST,
199                    "Parent function `unprepare` failed"
200                )
201            } else {
202                Ok(())
203            }
204        }
205    }
206
207    fn parent_suspend(&self) -> Result<(), gst::LoggableError> {
208        unsafe {
209            let data = Self::type_data();
210            let parent_class = data.as_ref().parent_class() as *mut ffi::GstRTSPMediaClass;
211            if let Some(f) = (*parent_class).suspend {
212                gst::result_from_gboolean!(
213                    f(self.obj().unsafe_cast_ref::<RTSPMedia>().to_glib_none().0),
214                    gst::CAT_RUST,
215                    "Parent function `suspend` failed"
216                )
217            } else {
218                Ok(())
219            }
220        }
221    }
222
223    fn parent_unsuspend(&self) -> Result<(), gst::LoggableError> {
224        unsafe {
225            let data = Self::type_data();
226            let parent_class = data.as_ref().parent_class() as *mut ffi::GstRTSPMediaClass;
227            if let Some(f) = (*parent_class).unsuspend {
228                gst::result_from_gboolean!(
229                    f(self.obj().unsafe_cast_ref::<RTSPMedia>().to_glib_none().0),
230                    gst::CAT_RUST,
231                    "Parent function `unsuspend` failed"
232                )
233            } else {
234                Ok(())
235            }
236        }
237    }
238
239    // TODO missing: convert_range
240    fn parent_query_position(&self) -> Option<gst::ClockTime> {
241        unsafe {
242            use std::mem;
243
244            let data = Self::type_data();
245            let parent_class = data.as_ref().parent_class() as *mut ffi::GstRTSPMediaClass;
246            if let Some(f) = (*parent_class).query_position {
247                let mut position = mem::MaybeUninit::uninit();
248                if f(
249                    self.obj().unsafe_cast_ref::<RTSPMedia>().to_glib_none().0,
250                    position.as_mut_ptr(),
251                ) == glib::ffi::GFALSE
252                {
253                    None
254                } else {
255                    from_glib(position.assume_init() as u64)
256                }
257            } else {
258                None
259            }
260        }
261    }
262
263    fn parent_query_stop(&self) -> Option<gst::ClockTime> {
264        unsafe {
265            use std::mem;
266
267            let data = Self::type_data();
268            let parent_class = data.as_ref().parent_class() as *mut ffi::GstRTSPMediaClass;
269            if let Some(f) = (*parent_class).query_stop {
270                let mut stop = mem::MaybeUninit::uninit();
271                if f(
272                    self.obj().unsafe_cast_ref::<RTSPMedia>().to_glib_none().0,
273                    stop.as_mut_ptr(),
274                ) == glib::ffi::GFALSE
275                {
276                    None
277                } else {
278                    from_glib(stop.assume_init() as u64)
279                }
280            } else {
281                None
282            }
283        }
284    }
285
286    fn parent_create_rtpbin(&self) -> Option<gst::Element> {
287        unsafe {
288            let data = Self::type_data();
289            let parent_class = data.as_ref().parent_class() as *mut ffi::GstRTSPMediaClass;
290            let f = (*parent_class)
291                .create_rtpbin
292                .expect("No `create_rtpbin` virtual method implementation in parent class");
293
294            from_glib_none(f(self
295                .obj()
296                .unsafe_cast_ref::<RTSPMedia>()
297                .to_glib_none()
298                .0))
299        }
300    }
301
302    fn parent_setup_rtpbin(&self, rtpbin: &gst::Element) -> Result<(), gst::LoggableError> {
303        unsafe {
304            let data = Self::type_data();
305            let parent_class = data.as_ref().parent_class() as *mut ffi::GstRTSPMediaClass;
306            if let Some(f) = (*parent_class).setup_rtpbin {
307                let ptr = rtpbin.to_glib_none().0;
308
309                // The C code assumes to pass a floating reference around so let's make sure we do
310                glib::gobject_ffi::g_object_force_floating(ptr as *mut _);
311
312                let res = gst::result_from_gboolean!(
313                    f(
314                        self.obj().unsafe_cast_ref::<RTSPMedia>().to_glib_none().0,
315                        ptr
316                    ),
317                    gst::CAT_RUST,
318                    "Parent function `setup_sdp` failed"
319                );
320
321                // If the code didn't accidentally sink it then we have to do that
322                // here now so that we don't have any floating reference on our side
323                // anymore
324                if glib::gobject_ffi::g_object_is_floating(ptr as *mut _) != glib::ffi::GFALSE {
325                    glib::gobject_ffi::g_object_ref_sink(ptr as *mut _);
326                }
327
328                res
329            } else {
330                Ok(())
331            }
332        }
333    }
334
335    fn parent_setup_sdp(
336        &self,
337        sdp: &mut gst_sdp::SDPMessageRef,
338        info: &SDPInfo,
339    ) -> Result<(), gst::LoggableError> {
340        unsafe {
341            let data = Self::type_data();
342            let parent_class = data.as_ref().parent_class() as *mut ffi::GstRTSPMediaClass;
343            let f = (*parent_class)
344                .setup_sdp
345                .expect("No `setup_sdp` virtual method implementation in parent class");
346
347            gst::result_from_gboolean!(
348                f(
349                    self.obj().unsafe_cast_ref::<RTSPMedia>().to_glib_none().0,
350                    sdp as *mut _ as *mut gst_sdp::ffi::GstSDPMessage,
351                    info.0.as_ptr()
352                ),
353                gst::CAT_RUST,
354                "Parent function `setup_sdp` failed"
355            )
356        }
357    }
358
359    fn parent_new_stream(&self, stream: &crate::RTSPStream) {
360        unsafe {
361            let data = Self::type_data();
362            let parent_class = data.as_ref().parent_class() as *mut ffi::GstRTSPMediaClass;
363            if let Some(f) = (*parent_class).new_stream {
364                f(
365                    self.obj().unsafe_cast_ref::<RTSPMedia>().to_glib_none().0,
366                    stream.to_glib_none().0,
367                );
368            }
369        }
370    }
371
372    fn parent_removed_stream(&self, stream: &crate::RTSPStream) {
373        unsafe {
374            let data = Self::type_data();
375            let parent_class = data.as_ref().parent_class() as *mut ffi::GstRTSPMediaClass;
376            if let Some(f) = (*parent_class).removed_stream {
377                f(
378                    self.obj().unsafe_cast_ref::<RTSPMedia>().to_glib_none().0,
379                    stream.to_glib_none().0,
380                );
381            }
382        }
383    }
384
385    fn parent_prepared(&self) {
386        unsafe {
387            let data = Self::type_data();
388            let parent_class = data.as_ref().parent_class() as *mut ffi::GstRTSPMediaClass;
389            if let Some(f) = (*parent_class).prepared {
390                f(self.obj().unsafe_cast_ref::<RTSPMedia>().to_glib_none().0);
391            }
392        }
393    }
394
395    fn parent_unprepared(&self) {
396        unsafe {
397            let data = Self::type_data();
398            let parent_class = data.as_ref().parent_class() as *mut ffi::GstRTSPMediaClass;
399            if let Some(f) = (*parent_class).unprepared {
400                f(self.obj().unsafe_cast_ref::<RTSPMedia>().to_glib_none().0);
401            }
402        }
403    }
404
405    fn parent_target_state(&self, state: gst::State) {
406        unsafe {
407            let data = Self::type_data();
408            let parent_class = data.as_ref().parent_class() as *mut ffi::GstRTSPMediaClass;
409            if let Some(f) = (*parent_class).target_state {
410                f(
411                    self.obj().unsafe_cast_ref::<RTSPMedia>().to_glib_none().0,
412                    state.into_glib(),
413                );
414            }
415        }
416    }
417
418    fn parent_new_state(&self, state: gst::State) {
419        unsafe {
420            let data = Self::type_data();
421            let parent_class = data.as_ref().parent_class() as *mut ffi::GstRTSPMediaClass;
422            if let Some(f) = (*parent_class).new_state {
423                f(
424                    self.obj().unsafe_cast_ref::<RTSPMedia>().to_glib_none().0,
425                    state.into_glib(),
426                );
427            }
428        }
429    }
430
431    fn parent_handle_sdp(&self, sdp: &gst_sdp::SDPMessageRef) -> Result<(), gst::LoggableError> {
432        unsafe {
433            let data = Self::type_data();
434            let parent_class = data.as_ref().parent_class() as *mut ffi::GstRTSPMediaClass;
435            let f = (*parent_class)
436                .handle_sdp
437                .expect("No `handle_sdp` virtual method implementation in parent class");
438
439            gst::result_from_gboolean!(
440                f(
441                    self.obj().unsafe_cast_ref::<RTSPMedia>().to_glib_none().0,
442                    sdp as *const _ as *mut gst_sdp::ffi::GstSDPMessage
443                ),
444                gst::CAT_RUST,
445                "Parent function `handle_sdp` failed"
446            )
447        }
448    }
449}
450
451impl<T: RTSPMediaImpl> RTSPMediaImplExt for T {}
452
453unsafe impl<T: RTSPMediaImpl> IsSubclassable<T> for RTSPMedia {
454    fn class_init(klass: &mut glib::Class<Self>) {
455        Self::parent_class_init::<T>(klass);
456        let klass = klass.as_mut();
457        klass.handle_message = Some(media_handle_message::<T>);
458        klass.prepare = Some(media_prepare::<T>);
459        klass.unprepare = Some(media_unprepare::<T>);
460        klass.suspend = Some(media_suspend::<T>);
461        klass.unsuspend = Some(media_unsuspend::<T>);
462        klass.query_position = Some(media_query_position::<T>);
463        klass.query_stop = Some(media_query_stop::<T>);
464        klass.create_rtpbin = Some(media_create_rtpbin::<T>);
465        klass.setup_rtpbin = Some(media_setup_rtpbin::<T>);
466        klass.setup_sdp = Some(media_setup_sdp::<T>);
467        klass.new_stream = Some(media_new_stream::<T>);
468        klass.removed_stream = Some(media_removed_stream::<T>);
469        klass.prepared = Some(media_prepared::<T>);
470        klass.unprepared = Some(media_unprepared::<T>);
471        klass.target_state = Some(media_target_state::<T>);
472        klass.new_state = Some(media_new_state::<T>);
473        klass.handle_sdp = Some(media_handle_sdp::<T>);
474    }
475}
476
477unsafe extern "C" fn media_handle_message<T: RTSPMediaImpl>(
478    ptr: *mut ffi::GstRTSPMedia,
479    message: *mut gst::ffi::GstMessage,
480) -> glib::ffi::gboolean {
481    let instance = &*(ptr as *mut T::Instance);
482    let imp = instance.imp();
483
484    imp.handle_message(gst::MessageRef::from_ptr(message))
485        .into_glib()
486}
487
488unsafe extern "C" fn media_prepare<T: RTSPMediaImpl>(
489    ptr: *mut ffi::GstRTSPMedia,
490    thread: *mut ffi::GstRTSPThread,
491) -> glib::ffi::gboolean {
492    let instance = &*(ptr as *mut T::Instance);
493    let imp = instance.imp();
494
495    match imp.prepare(&from_glib_borrow(thread)) {
496        Ok(()) => glib::ffi::GTRUE,
497        Err(err) => {
498            err.log_with_imp(imp);
499            glib::ffi::GFALSE
500        }
501    }
502}
503
504unsafe extern "C" fn media_unprepare<T: RTSPMediaImpl>(
505    ptr: *mut ffi::GstRTSPMedia,
506) -> glib::ffi::gboolean {
507    let instance = &*(ptr as *mut T::Instance);
508    let imp = instance.imp();
509
510    match imp.unprepare() {
511        Ok(()) => glib::ffi::GTRUE,
512        Err(err) => {
513            err.log_with_imp(imp);
514            glib::ffi::GFALSE
515        }
516    }
517}
518
519unsafe extern "C" fn media_suspend<T: RTSPMediaImpl>(
520    ptr: *mut ffi::GstRTSPMedia,
521) -> glib::ffi::gboolean {
522    let instance = &*(ptr as *mut T::Instance);
523    let imp = instance.imp();
524
525    match imp.suspend() {
526        Ok(()) => glib::ffi::GTRUE,
527        Err(err) => {
528            err.log_with_imp(imp);
529            glib::ffi::GFALSE
530        }
531    }
532}
533
534unsafe extern "C" fn media_unsuspend<T: RTSPMediaImpl>(
535    ptr: *mut ffi::GstRTSPMedia,
536) -> glib::ffi::gboolean {
537    let instance = &*(ptr as *mut T::Instance);
538    let imp = instance.imp();
539
540    match imp.unsuspend() {
541        Ok(()) => glib::ffi::GTRUE,
542        Err(err) => {
543            err.log_with_imp(imp);
544            glib::ffi::GFALSE
545        }
546    }
547}
548
549unsafe extern "C" fn media_query_position<T: RTSPMediaImpl>(
550    ptr: *mut ffi::GstRTSPMedia,
551    position: *mut i64,
552) -> glib::ffi::gboolean {
553    let instance = &*(ptr as *mut T::Instance);
554    let imp = instance.imp();
555
556    match imp.query_position() {
557        Some(pos) => {
558            *position = pos.into_glib() as i64;
559            glib::ffi::GTRUE
560        }
561        None => glib::ffi::GFALSE,
562    }
563}
564
565unsafe extern "C" fn media_query_stop<T: RTSPMediaImpl>(
566    ptr: *mut ffi::GstRTSPMedia,
567    stop: *mut i64,
568) -> glib::ffi::gboolean {
569    let instance = &*(ptr as *mut T::Instance);
570    let imp = instance.imp();
571
572    match imp.query_stop() {
573        Some(s) => {
574            *stop = s.into_glib() as i64;
575            glib::ffi::GTRUE
576        }
577        None => glib::ffi::GFALSE,
578    }
579}
580
581unsafe extern "C" fn media_create_rtpbin<T: RTSPMediaImpl>(
582    ptr: *mut ffi::GstRTSPMedia,
583) -> *mut gst::ffi::GstElement {
584    let instance = &*(ptr as *mut T::Instance);
585    let imp = instance.imp();
586
587    let res: *mut gst::ffi::GstElement = imp.create_rtpbin().into_glib_ptr();
588
589    if !res.is_null() {
590        glib::gobject_ffi::g_object_force_floating(res as *mut _);
591    }
592
593    res
594}
595
596unsafe extern "C" fn media_setup_rtpbin<T: RTSPMediaImpl>(
597    ptr: *mut ffi::GstRTSPMedia,
598    rtpbin: *mut gst::ffi::GstElement,
599) -> glib::ffi::gboolean {
600    let instance = &*(ptr as *mut T::Instance);
601    let imp = instance.imp();
602
603    // If the rtpbin was floating before make sure it is not anymore for now so
604    // we don't accidentally take ownership of it somewhere along the line
605    if glib::gobject_ffi::g_object_is_floating(rtpbin as *mut _) != glib::ffi::GFALSE {
606        glib::gobject_ffi::g_object_ref_sink(rtpbin as *mut _);
607    }
608
609    let res = match imp.setup_rtpbin(&from_glib_borrow(rtpbin)) {
610        Ok(()) => glib::ffi::GTRUE,
611        Err(err) => {
612            err.log_with_imp(imp);
613            glib::ffi::GFALSE
614        }
615    };
616
617    // Ensure that the rtpbin is still floating afterwards here
618    glib::gobject_ffi::g_object_force_floating(rtpbin as *mut _);
619
620    res
621}
622
623unsafe extern "C" fn media_setup_sdp<T: RTSPMediaImpl>(
624    ptr: *mut ffi::GstRTSPMedia,
625    sdp: *mut gst_sdp::ffi::GstSDPMessage,
626    info: *mut ffi::GstSDPInfo,
627) -> glib::ffi::gboolean {
628    let instance = &*(ptr as *mut T::Instance);
629    let imp = instance.imp();
630
631    match imp.setup_sdp(
632        &mut *(sdp as *mut gst_sdp::SDPMessageRef),
633        &SDPInfo(ptr::NonNull::new(info).expect("NULL SDPInfo")),
634    ) {
635        Ok(()) => glib::ffi::GTRUE,
636        Err(err) => {
637            err.log_with_imp(imp);
638            glib::ffi::GFALSE
639        }
640    }
641}
642
643unsafe extern "C" fn media_new_stream<T: RTSPMediaImpl>(
644    ptr: *mut ffi::GstRTSPMedia,
645    stream: *mut ffi::GstRTSPStream,
646) {
647    let instance = &*(ptr as *mut T::Instance);
648    let imp = instance.imp();
649
650    imp.new_stream(&from_glib_borrow(stream));
651}
652
653unsafe extern "C" fn media_removed_stream<T: RTSPMediaImpl>(
654    ptr: *mut ffi::GstRTSPMedia,
655    stream: *mut ffi::GstRTSPStream,
656) {
657    let instance = &*(ptr as *mut T::Instance);
658    let imp = instance.imp();
659
660    imp.removed_stream(&from_glib_borrow(stream));
661}
662
663unsafe extern "C" fn media_prepared<T: RTSPMediaImpl>(ptr: *mut ffi::GstRTSPMedia) {
664    let instance = &*(ptr as *mut T::Instance);
665    let imp = instance.imp();
666
667    imp.prepared();
668}
669
670unsafe extern "C" fn media_unprepared<T: RTSPMediaImpl>(ptr: *mut ffi::GstRTSPMedia) {
671    let instance = &*(ptr as *mut T::Instance);
672    let imp = instance.imp();
673
674    imp.unprepared();
675}
676
677unsafe extern "C" fn media_target_state<T: RTSPMediaImpl>(
678    ptr: *mut ffi::GstRTSPMedia,
679    state: gst::ffi::GstState,
680) {
681    let instance = &*(ptr as *mut T::Instance);
682    let imp = instance.imp();
683
684    imp.target_state(from_glib(state));
685}
686
687unsafe extern "C" fn media_new_state<T: RTSPMediaImpl>(
688    ptr: *mut ffi::GstRTSPMedia,
689    state: gst::ffi::GstState,
690) {
691    let instance = &*(ptr as *mut T::Instance);
692    let imp = instance.imp();
693
694    imp.new_state(from_glib(state));
695}
696
697unsafe extern "C" fn media_handle_sdp<T: RTSPMediaImpl>(
698    ptr: *mut ffi::GstRTSPMedia,
699    sdp: *mut gst_sdp::ffi::GstSDPMessage,
700) -> glib::ffi::gboolean {
701    let instance = &*(ptr as *mut T::Instance);
702    let imp = instance.imp();
703
704    match imp.handle_sdp(&*(sdp as *const gst_sdp::SDPMessageRef)) {
705        Ok(()) => glib::ffi::GTRUE,
706        Err(err) => {
707            err.log_with_imp(imp);
708            glib::ffi::GFALSE
709        }
710    }
711}