1use std::{
2 ffi::{CStr, c_void},
3 marker::PhantomData,
4 panic::{AssertUnwindSafe, catch_unwind},
5 ptr::NonNull,
6 slice,
7};
8
9use crate::{Result, Session, StaticCStr, SwitchError, log_error, status_to_result, sys};
10
11macro_rules! call_ffi {
12 ($call:expr) => {{
13 unsafe { $call }
15 }};
16}
17
18#[derive(Debug, Copy, Clone, PartialEq, Eq)]
19pub enum MediaBugAction {
20 Continue,
21 Stop,
22}
23
24impl MediaBugAction {
25 fn as_switch_bool(self) -> sys::switch_bool_t {
26 match self {
27 Self::Continue => sys::switch_bool_t_SWITCH_TRUE,
28 Self::Stop => sys::switch_bool_t_SWITCH_FALSE,
29 }
30 }
31}
32
33#[derive(Debug, Copy, Clone, PartialEq, Eq)]
34pub struct MediaBugFlags(pub sys::switch_media_bug_flag_t);
35
36impl MediaBugFlags {
37 pub const BOTH: Self = Self(sys::switch_media_bug_flag_enum_t_SMBF_BOTH);
38 pub const READ_STREAM: Self = Self(sys::switch_media_bug_flag_enum_t_SMBF_READ_STREAM);
39 pub const WRITE_STREAM: Self = Self(sys::switch_media_bug_flag_enum_t_SMBF_WRITE_STREAM);
40 pub const WRITE_REPLACE: Self = Self(sys::switch_media_bug_flag_enum_t_SMBF_WRITE_REPLACE);
41 pub const READ_REPLACE: Self = Self(sys::switch_media_bug_flag_enum_t_SMBF_READ_REPLACE);
42 pub const READ_PING: Self = Self(sys::switch_media_bug_flag_enum_t_SMBF_READ_PING);
43 pub const STEREO: Self = Self(sys::switch_media_bug_flag_enum_t_SMBF_STEREO);
44 pub const ANSWER_REQUIRED: Self = Self(sys::switch_media_bug_flag_enum_t_SMBF_ANSWER_REQ);
45 pub const BRIDGE_REQUIRED: Self = Self(sys::switch_media_bug_flag_enum_t_SMBF_BRIDGE_REQ);
46 pub const THREAD_LOCK: Self = Self(sys::switch_media_bug_flag_enum_t_SMBF_THREAD_LOCK);
47 pub const PRUNE: Self = Self(sys::switch_media_bug_flag_enum_t_SMBF_PRUNE);
48 pub const NO_PAUSE: Self = Self(sys::switch_media_bug_flag_enum_t_SMBF_NO_PAUSE);
49 pub const STEREO_SWAP: Self = Self(sys::switch_media_bug_flag_enum_t_SMBF_STEREO_SWAP);
50 pub const LOCK: Self = Self(sys::switch_media_bug_flag_enum_t_SMBF_LOCK);
51 pub const TAP_NATIVE_READ: Self = Self(sys::switch_media_bug_flag_enum_t_SMBF_TAP_NATIVE_READ);
52 pub const TAP_NATIVE_WRITE: Self =
53 Self(sys::switch_media_bug_flag_enum_t_SMBF_TAP_NATIVE_WRITE);
54 pub const ONE_ONLY: Self = Self(sys::switch_media_bug_flag_enum_t_SMBF_ONE_ONLY);
55 pub const READ_TEXT_STREAM: Self =
56 Self(sys::switch_media_bug_flag_enum_t_SMBF_READ_TEXT_STREAM);
57
58 pub const fn bits(self) -> sys::switch_media_bug_flag_t {
59 self.0
60 }
61}
62
63impl std::ops::BitOr for MediaBugFlags {
64 type Output = Self;
65
66 fn bitor(self, rhs: Self) -> Self::Output {
67 Self(self.0 | rhs.0)
68 }
69}
70
71impl std::ops::BitOrAssign for MediaBugFlags {
72 fn bitor_assign(&mut self, rhs: Self) {
73 self.0 |= rhs.0;
74 }
75}
76
77#[derive(Debug, Copy, Clone)]
78pub struct MediaBugConfig {
79 pub function: &'static CStr,
80 pub target: &'static CStr,
81 pub flags: MediaBugFlags,
82 pub stop_time: sys::time_t,
83}
84
85impl MediaBugConfig {
86 pub fn new(
87 function: impl StaticCStr,
88 target: impl StaticCStr,
89 flags: MediaBugFlags,
90 ) -> Result<Self> {
91 Ok(Self {
92 function: function.into_static_cstr()?,
93 target: target.into_static_cstr()?,
94 flags,
95 stop_time: 0,
96 })
97 }
98
99 pub const fn stop_time(mut self, stop_time: sys::time_t) -> Self {
100 self.stop_time = stop_time;
101 self
102 }
103}
104
105#[derive(Debug, Copy, Clone)]
106pub struct MediaBug {
107 raw: NonNull<sys::switch_media_bug_t>,
108}
109
110impl MediaBug {
111 pub fn as_ptr(self) -> *mut sys::switch_media_bug_t {
112 self.raw.as_ptr()
113 }
114}
115
116pub trait MediaBugHandler: 'static {
117 fn on_init(&mut self, _ctx: &mut MediaBugContext<'_>) -> MediaBugAction {
118 MediaBugAction::Continue
119 }
120
121 fn on_read(
122 &mut self,
123 _ctx: &mut MediaBugContext<'_>,
124 _frame: MediaFrame<'_>,
125 ) -> MediaBugAction {
126 MediaBugAction::Continue
127 }
128
129 fn on_write(
130 &mut self,
131 _ctx: &mut MediaBugContext<'_>,
132 _frame: MediaFrame<'_>,
133 ) -> MediaBugAction {
134 MediaBugAction::Continue
135 }
136
137 fn on_read_replace(
138 &mut self,
139 _ctx: &mut MediaBugContext<'_>,
140 _frame: MediaFrameMut<'_>,
141 ) -> MediaBugAction {
142 MediaBugAction::Continue
143 }
144
145 fn on_write_replace(
146 &mut self,
147 _ctx: &mut MediaBugContext<'_>,
148 _frame: MediaFrameMut<'_>,
149 ) -> MediaBugAction {
150 MediaBugAction::Continue
151 }
152
153 fn on_close(&mut self, _ctx: &mut MediaBugContext<'_>) {}
154}
155
156pub fn attach_media_bug<H>(session: Session, config: MediaBugConfig, handler: H) -> Result<MediaBug>
159where
160 H: MediaBugHandler,
161{
162 let state = Box::into_raw(Box::new(MediaBugState { handler }));
163 let mut bug = std::ptr::null_mut();
164
165 let status = call_ffi!(add_media_bug::<H>(session, config, state.cast(), &mut bug));
167
168 if status != crate::SUCCESS {
169 call_ffi!(drop(Box::from_raw(state)));
171 return Err(SwitchError(status));
172 }
173
174 let Some(raw) = NonNull::new(bug) else {
175 call_ffi!(drop(Box::from_raw(state)));
178 return Err(SwitchError(crate::GENERR));
179 };
180 Ok(MediaBug { raw })
181}
182
183unsafe fn add_media_bug<H>(
189 session: Session,
190 config: MediaBugConfig,
191 user_data: *mut c_void,
192 bug: &mut *mut sys::switch_media_bug_t,
193) -> sys::switch_status_t
194where
195 H: MediaBugHandler,
196{
197 let add = sys::switch_core_media_bug_add;
198 call_ffi!(add(
199 session.as_ptr(),
200 config.function.as_ptr(),
201 config.target.as_ptr(),
202 Some(media_bug_trampoline::<H>),
203 user_data,
204 config.stop_time,
205 config.flags.bits(),
206 bug,
207 ))
208}
209
210pub struct MediaBugContext<'a> {
211 raw: NonNull<sys::switch_media_bug_t>,
212 _lifetime: PhantomData<&'a mut sys::switch_media_bug_t>,
213}
214
215impl<'a> MediaBugContext<'a> {
216 pub unsafe fn from_raw(raw: *mut sys::switch_media_bug_t) -> Option<Self> {
222 NonNull::new(raw).map(|raw| Self {
223 raw,
224 _lifetime: PhantomData,
225 })
226 }
227
228 pub fn as_ptr(&self) -> *mut sys::switch_media_bug_t {
229 self.raw.as_ptr()
230 }
231
232 pub fn session(&self) -> Option<NonNull<sys::switch_core_session_t>> {
233 NonNull::new(call_ffi!(sys::switch_core_media_bug_get_session(
235 self.raw.as_ptr()
236 )))
237 }
238
239 pub fn native_read_frame(&self) -> Option<MediaFrame<'_>> {
240 call_ffi!(MediaFrame::from_raw(
242 sys::switch_core_media_bug_get_native_read_frame(self.raw.as_ptr())
243 ))
244 }
245
246 pub fn native_write_frame(&self) -> Option<MediaFrame<'_>> {
247 call_ffi!(MediaFrame::from_raw(
249 sys::switch_core_media_bug_get_native_write_frame(self.raw.as_ptr())
250 ))
251 }
252
253 pub fn read_replace_frame(&mut self) -> Option<MediaFrameMut<'_>> {
254 call_ffi!(MediaFrameMut::from_raw(
256 sys::switch_core_media_bug_get_read_replace_frame(self.raw.as_ptr())
257 ))
258 }
259
260 pub fn write_replace_frame(&mut self) -> Option<MediaFrameMut<'_>> {
261 call_ffi!(MediaFrameMut::from_raw(
263 sys::switch_core_media_bug_get_write_replace_frame(self.raw.as_ptr())
264 ))
265 }
266
267 pub fn set_read_replace_frame(&mut self, frame: &mut MediaFrameMut<'_>) {
268 call_ffi!(sys::switch_core_media_bug_set_read_replace_frame(
270 self.raw.as_ptr(),
271 frame.as_ptr()
272 ));
273 }
274
275 pub fn set_write_replace_frame(&mut self, frame: &mut MediaFrameMut<'_>) {
276 call_ffi!(sys::switch_core_media_bug_set_write_replace_frame(
278 self.raw.as_ptr(),
279 frame.as_ptr()
280 ));
281 }
282
283 pub fn flush(&mut self) {
284 call_ffi!(sys::switch_core_media_bug_flush(self.raw.as_ptr()));
286 }
287
288 pub fn read_into(&mut self, frame: &mut sys::switch_frame_t, fill: bool) -> Result<()> {
289 let fill = if fill {
290 sys::switch_bool_t_SWITCH_TRUE
291 } else {
292 sys::switch_bool_t_SWITCH_FALSE
293 };
294 let status = call_ffi!(sys::switch_core_media_bug_read(
296 self.raw.as_ptr(),
297 frame,
298 fill
299 ));
300 status_to_result(status)
301 }
302}
303
304#[derive(Copy, Clone)]
305pub struct MediaFrame<'a> {
306 raw: NonNull<sys::switch_frame_t>,
307 _lifetime: PhantomData<&'a sys::switch_frame_t>,
308}
309
310impl<'a> MediaFrame<'a> {
311 pub unsafe fn from_raw(raw: *mut sys::switch_frame_t) -> Option<Self> {
317 NonNull::new(raw).map(|raw| Self {
318 raw,
319 _lifetime: PhantomData,
320 })
321 }
322
323 pub fn as_ptr(self) -> *mut sys::switch_frame_t {
324 self.raw.as_ptr()
325 }
326
327 pub fn data_len(self) -> usize {
328 call_ffi!(self.raw.as_ref().datalen as usize)
330 }
331
332 pub fn samples(self) -> u32 {
333 call_ffi!(self.raw.as_ref().samples)
335 }
336
337 pub fn rate(self) -> u32 {
338 call_ffi!(self.raw.as_ref().rate)
340 }
341
342 pub fn channels(self) -> u32 {
343 call_ffi!(self.raw.as_ref().channels)
345 }
346
347 pub fn bytes(self) -> &'a [u8] {
348 call_ffi!({
351 let frame = self.raw.as_ref();
352 if frame.data.is_null() || frame.datalen == 0 {
353 &[]
354 } else {
355 slice::from_raw_parts(frame.data.cast::<u8>(), frame.datalen as usize)
356 }
357 })
358 }
359
360 pub fn pcm_i16(self) -> Option<&'a [i16]> {
361 let bytes = self.bytes();
362 if !bytes.len().is_multiple_of(std::mem::size_of::<i16>())
363 || !(bytes.as_ptr() as usize).is_multiple_of(std::mem::align_of::<i16>())
364 {
365 return None;
366 }
367
368 Some(call_ffi!(slice::from_raw_parts(
370 bytes.as_ptr().cast::<i16>(),
371 bytes.len() / size_of::<i16>()
372 )))
373 }
374}
375
376pub struct MediaFrameMut<'a> {
377 raw: NonNull<sys::switch_frame_t>,
378 _lifetime: PhantomData<&'a mut sys::switch_frame_t>,
379}
380
381impl<'a> MediaFrameMut<'a> {
382 pub unsafe fn from_raw(raw: *mut sys::switch_frame_t) -> Option<Self> {
388 NonNull::new(raw).map(|raw| Self {
389 raw,
390 _lifetime: PhantomData,
391 })
392 }
393
394 pub fn as_ptr(&mut self) -> *mut sys::switch_frame_t {
395 self.raw.as_ptr()
396 }
397
398 pub fn as_frame(&self) -> MediaFrame<'_> {
399 MediaFrame {
400 raw: self.raw,
401 _lifetime: PhantomData,
402 }
403 }
404
405 pub fn bytes_mut(&mut self) -> &mut [u8] {
406 call_ffi!({
408 let frame = self.raw.as_ref();
409 if frame.data.is_null() || frame.datalen == 0 {
410 &mut []
411 } else {
412 slice::from_raw_parts_mut(frame.data.cast::<u8>(), frame.datalen as usize)
413 }
414 })
415 }
416
417 pub fn pcm_i16_mut(&mut self) -> Option<&mut [i16]> {
418 let bytes = self.bytes_mut();
419 if !bytes.len().is_multiple_of(std::mem::size_of::<i16>())
420 || !(bytes.as_ptr() as usize).is_multiple_of(std::mem::align_of::<i16>())
421 {
422 return None;
423 }
424
425 Some(call_ffi!(slice::from_raw_parts_mut(
427 bytes.as_mut_ptr().cast::<i16>(),
428 bytes.len() / size_of::<i16>(),
429 )))
430 }
431}
432
433struct MediaBugState<H> {
434 handler: H,
435}
436
437unsafe extern "C" fn media_bug_trampoline<H>(
444 bug: *mut sys::switch_media_bug_t,
445 user_data: *mut c_void,
446 callback_type: sys::switch_abc_type_t,
447) -> sys::switch_bool_t
448where
449 H: MediaBugHandler,
450{
451 if user_data.is_null() {
452 return sys::switch_bool_t_SWITCH_TRUE;
453 }
454
455 let Some(mut ctx) = (call_ffi!(MediaBugContext::from_raw(bug))) else {
457 return sys::switch_bool_t_SWITCH_TRUE;
458 };
459
460 if callback_type == sys::switch_abc_type_t_SWITCH_ABC_TYPE_CLOSE {
461 return close_media_bug::<H>(user_data, &mut ctx);
462 }
463
464 let state = call_ffi!(&mut *user_data.cast::<MediaBugState<H>>());
466 let dispatch = MediaBugDispatch {
467 bug,
468 state,
469 ctx: &mut ctx,
470 };
471 let result = catch_unwind(AssertUnwindSafe(|| dispatch.run(callback_type)));
472
473 callback_result(result)
474}
475
476fn close_media_bug<H>(user_data: *mut c_void, ctx: &mut MediaBugContext<'_>) -> sys::switch_bool_t
477where
478 H: MediaBugHandler,
479{
480 let mut state = call_ffi!(Box::from_raw(user_data.cast::<MediaBugState<H>>()));
482 let result = catch_unwind(AssertUnwindSafe(|| {
483 state.handler.on_close(ctx);
484 MediaBugAction::Continue
485 }));
486 callback_result(result)
487}
488
489struct MediaBugDispatch<'a, H> {
490 bug: *mut sys::switch_media_bug_t,
491 state: &'a mut MediaBugState<H>,
492 ctx: &'a mut MediaBugContext<'a>,
493}
494
495impl<H> MediaBugDispatch<'_, H>
496where
497 H: MediaBugHandler,
498{
499 fn run(self, callback_type: sys::switch_abc_type_t) -> MediaBugAction {
500 match callback_type {
501 sys::switch_abc_type_t_SWITCH_ABC_TYPE_INIT => self.state.handler.on_init(self.ctx),
502 sys::switch_abc_type_t_SWITCH_ABC_TYPE_READ => self.read(),
503 sys::switch_abc_type_t_SWITCH_ABC_TYPE_WRITE => self.write(),
504 sys::switch_abc_type_t_SWITCH_ABC_TYPE_READ_REPLACE => self.read_replace(),
505 sys::switch_abc_type_t_SWITCH_ABC_TYPE_WRITE_REPLACE => self.write_replace(),
506 _ => MediaBugAction::Continue,
507 }
508 }
509
510 fn read(self) -> MediaBugAction {
511 let frame = call_ffi!(MediaFrame::from_raw(
513 sys::switch_core_media_bug_get_native_read_frame(self.bug)
514 ));
515 frame.map_or(MediaBugAction::Continue, |frame| {
516 self.state.handler.on_read(self.ctx, frame)
517 })
518 }
519
520 fn write(self) -> MediaBugAction {
521 let frame = call_ffi!(MediaFrame::from_raw(
523 sys::switch_core_media_bug_get_native_write_frame(self.bug)
524 ));
525 frame.map_or(MediaBugAction::Continue, |frame| {
526 self.state.handler.on_write(self.ctx, frame)
527 })
528 }
529
530 fn read_replace(self) -> MediaBugAction {
531 let frame = call_ffi!(MediaFrameMut::from_raw(
533 sys::switch_core_media_bug_get_read_replace_frame(self.bug)
534 ));
535 frame.map_or(MediaBugAction::Continue, |frame| {
536 self.state.handler.on_read_replace(self.ctx, frame)
537 })
538 }
539
540 fn write_replace(self) -> MediaBugAction {
541 let frame = call_ffi!(MediaFrameMut::from_raw(
543 sys::switch_core_media_bug_get_write_replace_frame(self.bug)
544 ));
545 frame.map_or(MediaBugAction::Continue, |frame| {
546 self.state.handler.on_write_replace(self.ctx, frame)
547 })
548 }
549}
550
551fn callback_result(result: std::thread::Result<MediaBugAction>) -> sys::switch_bool_t {
552 match result {
553 Ok(action) => action.as_switch_bool(),
554 Err(_) => {
555 log_error("media_bug", "media bug callback panicked");
556 sys::switch_bool_t_SWITCH_FALSE
557 }
558 }
559}