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}