freeswitch_rs/
session.rs

1use freeswitch_sys::*;
2use std::ffi::c_void;
3use std::ffi::CStr;
4use std::ffi::CString;
5use std::ops::Deref;
6use std::ptr;
7
8use crate::prelude::*;
9
10use crate::channel::Channel;
11use crate::Frame;
12
13/// RAII guard that unlocks a session on drop.
14pub struct LocateGuard(Session);
15
16impl Drop for LocateGuard {
17    fn drop(&mut self) {
18        // SAFETY: In theory this is safe because ptr is valid since we located Session
19        // to create Session struct
20        unsafe { switch_core_session_rwunlock(self.0.as_ptr()) }
21    }
22}
23
24impl Deref for LocateGuard {
25    type Target = Session;
26    fn deref(&self) -> &Self::Target {
27        &self.0
28    }
29}
30
31fs_new_type!(Session, *mut switch_core_session_t);
32fs_session_owned_type!(SessionHandle, *mut switch_core_session_t);
33
34pub trait SessionExt: FSNewType<Inner = *mut switch_core_session_t> {
35    /// Retrieve the unique identifier from a session. See: [`switch_core_session_get_uuid`](../../freeswitch_sys/fn.switch_core_session_get_uuid.html).
36    fn get_uuid(&self) -> &CStr {
37        unsafe { CStr::from_ptr(switch_core_session_get_uuid(self.as_ptr())) }
38    }
39
40    /// Retrieve a reference to the channel object associated with a given session. See: [`switch_core_session_get_channel`](../../freeswitch_sys/fn.switch_core_session_get_channel.html).
41    fn get_channel(&self) -> Option<Channel<'_>> {
42        unsafe {
43            let ptr = switch_core_session_get_channel(self.as_ptr());
44            if ptr.is_null() {
45                return None;
46            };
47            Some(Channel::from_ptr(ptr))
48        }
49    }
50}
51
52impl SessionExt for Session {}
53impl<'a> SessionExt for SessionHandle<'a> {}
54
55impl Session {
56    /// Locate a session by UUID. See: [`switch_core_session_perform_locate`](../../freeswitch_sys/fn.switch_core_session_perform_locate.html).
57    #[track_caller]
58    pub fn locate(id: &CStr) -> Option<LocateGuard> {
59        // SAFETY
60        // Locating will take a read lock of any found session
61        // so the reference to session can live as long as you own the guard
62        unsafe {
63            let ptr = call_with_meta_suffix!(switch_core_session_perform_locate, id.as_ptr());
64            if ptr.is_null() {
65                None
66            } else {
67                Some(LocateGuard(Session::from_ptr(ptr)))
68            }
69        }
70    }
71
72    /// Remove a media bug from the session. See: [`switch_core_media_bug_remove`](../../freeswitch_sys/fn.switch_core_media_bug_remove.html).
73    pub fn remove_media_bug(&self, mut bug: MediaBugHandle) -> Result<()> {
74        // SAFETY:
75        // FS will nullify the media bug ptr, so all me
76        unsafe {
77            if bug.0.is_null() {
78                // Bug has already been removed
79                return Ok(());
80            }
81            match switch_core_media_bug_remove(self.0, &mut bug.0) {
82                switch_status_t::SWITCH_STATUS_SUCCESS => Ok(()),
83                other => Err(other.into()),
84            }
85        }
86    }
87
88    /// Add a media bug to the session. See: [`switch_core_media_bug_add`](../../freeswitch_sys/fn.switch_core_media_bug_add.html).
89    ///
90    /// # Examples
91    ///
92    /// ```
93    /// use freeswitch_rs::{Session, MediaBugFlags};
94    /// use freeswitch_sys::{switch_abc_type_t, switch_media_bug_flag_t};
95    /// use std::ffi::CString;
96    ///
97    /// let session = Session::locate("uuid").unwrap();
98    /// let handle = session.add_media_bug(
99    ///     Some(CString::new("my_function").unwrap()),
100    ///     None,
101    ///     switch_media_bug_flag_t(switch_media_bug_flag_t::SWITCH_MEDIA_BUG_FLAG_READ),
102    ///     |bug, abc_type| {
103    ///         match abc_type {
104    ///             switch_abc_type_t::SWITCH_ABC_TYPE_INIT => {
105    ///                 // Initialize
106    ///             }
107    ///             switch_abc_type_t::SWITCH_ABC_TYPE_READ => {
108    ///                 // Read media
109    ///             }
110    ///             switch_abc_type_t::SWITCH_ABC_TYPE_CLOSE => {
111    ///                 // Cleanup
112    ///             }
113    ///             _ => {}
114    ///         }
115    ///         true
116    ///     }
117    /// ).unwrap();
118    /// ```
119    pub fn add_media_bug<F>(
120        &self,
121        function: Option<CString>,
122        target: Option<CString>,
123        flags: MediaBugFlags,
124        callback: F,
125    ) -> Result<MediaBugHandle>
126    where
127        F: FnMut(&mut MediaBug, switch_abc_type_t) -> bool + 'static + Send,
128    {
129        let data = Box::into_raw(Box::new(callback));
130        let mut bug: *mut switch_media_bug_t = ptr::null_mut();
131
132        // SAFETY:
133        unsafe {
134            let res = switch_core_media_bug_add(
135                self.as_ptr(),
136                function.map(|f| f.as_ptr()).unwrap_or(ptr::null()),
137                target.map(|t| t.as_ptr()).unwrap_or(ptr::null()),
138                Some(bug_extern_callback::<F>),
139                data as *mut c_void,
140                0,
141                flags.0,
142                &mut bug as *mut *mut switch_media_bug_t,
143            );
144
145            match res {
146                switch_status_t::SWITCH_STATUS_SUCCESS => {
147                    if bug.is_null() {
148                        return Err(switch_status_t::SWITCH_STATUS_MEMERR.into());
149                    }
150                    Ok(MediaBugHandle(bug))
151                }
152                other => Err(other.into()),
153            }
154        }
155    }
156}
157// =====
158
159// =====
160
161pub type MediaBugFlags = freeswitch_sys::switch_media_bug_flag_enum_t;
162
163fs_session_owned_type!(MediaBug, *mut switch_media_bug_t);
164
165// Its *probably* safe to make this cloneable
166// FS won't dealloc the bug on removal, so double remove is a non op
167// and it allows for easy safe storage into Channel
168fs_new_type!(MediaBugHandle, *mut switch_media_bug_t, derive(Clone));
169unsafe impl Send for MediaBugHandle {}
170
171unsafe extern "C" fn bug_extern_callback<F>(
172    arg1: *mut switch_media_bug_t,
173    arg2: *mut ::std::os::raw::c_void,
174    arg3: switch_abc_type_t,
175) -> switch_bool_t
176where
177    F: FnMut(&mut MediaBug, switch_abc_type_t) -> bool,
178{
179    let callback_ptr = arg2 as *mut F;
180    let callback = &mut *callback_ptr;
181    let mut bug = MediaBug::from_ptr(arg1);
182
183    let res = if callback(&mut bug, arg3) {
184        switch_bool_t_SWITCH_TRUE
185    } else {
186        switch_bool_t_SWITCH_FALSE
187    };
188
189    // take back ownership of box so we can clean up
190    if arg3 == switch_abc_type_t::SWITCH_ABC_TYPE_CLOSE {
191        let _ = Box::from_raw(arg2);
192    }
193    res
194}
195
196impl<'a> MediaBug<'a> {
197    /// Obtain the session from a media bug. See: [`switch_core_media_bug_get_session`](../../freeswitch_sys/fn.switch_core_media_bug_get_session.html).
198    pub fn get_session(&self) -> SessionHandle<'a> {
199        // SAFETY
200        // Media bug lifetime is tied to session, so should be safe
201        // assuming media bug ptr is ok which in the context of bug callback, should be
202        unsafe {
203            let ptr = switch_core_media_bug_get_session(self.0);
204            debug_assert!(!ptr.is_null(), "parent session ptr is not null");
205            SessionHandle::from_ptr(ptr)
206        }
207    }
208
209    /// Read a frame from the bug. See: [`switch_core_media_bug_read`](../../freeswitch_sys/fn.switch_core_media_bug_read.html).
210    pub fn read_frame(&mut self, frame: &mut Frame) -> Result<usize> {
211        let res = unsafe { switch_core_media_bug_read(self.0, &mut frame.0, true.into()) };
212        if res != switch_status_t::SWITCH_STATUS_SUCCESS {
213            Err(res.into())
214        } else {
215            Ok(frame.0.datalen as usize)
216        }
217    }
218
219    //pub fn as_handle(&self) -> MediaBugHandle {
220    //    MediaBugHandle::from_ptr(self.as_ptr())
221    //}
222}