opentok/
audio_device.rs

1/// The OpenTok SDK handles all audio related work through a singleton instance
2/// of `otc_audio_device` which persists throughout the lifetime of the OpenTok
3/// library.
4///
5/// Unfortunately, the SDK expose no way of having a dedicated audio device
6/// per session, which makes many use cases that differs from the basic video
7/// chat demo pretty hard to implement. In summary, if you want independent
8/// audio devices, you likely need to have a multiprocess application, where
9/// an independent opentok::init is executed per process.
10///
11/// Likewise, there is currently no way to get independent audio samples
12/// per participant in a session. The SDK exposes an audio stream which is
13/// a mix of all participants' audio streams. Check
14/// <https://github.com/opentok/opentok-linux-sdk-samples/issues/25>
15use crate::enums::{IntoResult, OtcBool, OtcResult};
16
17use lazy_static::lazy_static;
18use log::warn;
19use std::os::raw::c_void;
20use std::sync::atomic::{AtomicBool, Ordering};
21use std::sync::{Arc, Mutex};
22use thiserror::Error;
23
24lazy_static! {
25    static ref SINGLETON: Arc<Mutex<AudioDevice>> = Arc::new(Mutex::new(AudioDevice::new()));
26}
27
28#[derive(Error, Debug)]
29pub enum AudioDeviceError {
30    #[error("Audio device singleton not configured")]
31    MissingAudioDevice,
32    #[error("Audio device already initialized")]
33    InitializationFailure,
34}
35
36ffi_callback_with_return_singleton!(start_capturer, *const ffi::otc_audio_device, ffi::otc_bool);
37ffi_callback_with_return_singleton!(stop_capturer, *const ffi::otc_audio_device, ffi::otc_bool);
38ffi_callback_with_return_singleton!(start_renderer, *const ffi::otc_audio_device, ffi::otc_bool);
39ffi_callback_with_return_singleton!(stop_renderer, *const ffi::otc_audio_device, ffi::otc_bool);
40
41unsafe extern "C" fn get_capture_settings(
42    _: *const ffi::otc_audio_device,
43    _: *mut c_void,
44    settings: *mut ffi::otc_audio_device_settings,
45) -> ffi::otc_bool {
46    if settings.is_null() {
47        return false.into();
48    }
49    if let Ok(singleton) = SINGLETON.try_lock() {
50        let capture_settings = singleton.capture_settings();
51        (*settings).sampling_rate = capture_settings.sampling_rate;
52        (*settings).number_of_channels = capture_settings.number_of_channels;
53        true
54    } else {
55        false
56    }
57    .into()
58}
59
60unsafe extern "C" fn get_render_settings(
61    _: *const ffi::otc_audio_device,
62    _: *mut c_void,
63    settings: *mut ffi::otc_audio_device_settings,
64) -> ffi::otc_bool {
65    if settings.is_null() {
66        return false.into();
67    }
68    if let Ok(singleton) = SINGLETON.try_lock() {
69        let render_settings = singleton.render_settings();
70        (*settings).sampling_rate = render_settings.sampling_rate;
71        (*settings).number_of_channels = render_settings.number_of_channels;
72        true
73    } else {
74        false
75    }
76    .into()
77}
78
79/// Raw data holder for audio samples.
80pub struct AudioSampleData(pub Vec<i16>);
81
82/// High level storage for audio samples. The data size should correspond with
83/// the sampling_rate and number_of_channels (size = (sampling_rate / 1000) *
84/// number_of_channels).
85pub struct AudioSample {
86    pub data: AudioSampleData,
87    pub sampling_rate: i32,
88    pub number_of_channels: i32,
89}
90
91/// Settings for a AudioDevice.
92#[derive(Clone, Debug, Copy)]
93pub struct AudioDeviceSettings {
94    pub sampling_rate: i32,
95    pub number_of_channels: i32,
96}
97
98impl Default for AudioDeviceSettings {
99    fn default() -> AudioDeviceSettings {
100        AudioDeviceSettings {
101            sampling_rate: 44100,
102            number_of_channels: 1,
103        }
104    }
105}
106
107type OnAudioSampleCallback = Box<dyn Fn(AudioSample) + Send + Sync + 'static>;
108
109#[derive(Clone)]
110pub struct AudioDevice {
111    ffi_callbacks: Arc<Mutex<ffi::otc_audio_device_callbacks>>,
112    capturer_ready: Arc<AtomicBool>,
113    capture_settings: Arc<Mutex<AudioDeviceSettings>>,
114    render_thread_running: Arc<AtomicBool>,
115    render_settings: Arc<Mutex<AudioDeviceSettings>>,
116    on_audio_sample_callbacks: Arc<Mutex<Vec<OnAudioSampleCallback>>>,
117}
118
119unsafe impl Send for AudioDevice {}
120unsafe impl Sync for AudioDevice {}
121
122impl AudioDevice {
123    fn new() -> Self {
124        let device = Self {
125            capturer_ready: Default::default(),
126            capture_settings: Default::default(),
127            render_thread_running: Default::default(),
128            render_settings: Default::default(),
129            on_audio_sample_callbacks: Default::default(),
130            ffi_callbacks: Arc::new(Mutex::new(ffi::otc_audio_device_callbacks {
131                init: None,
132                destroy: None,
133                init_capturer: None,
134                destroy_capturer: None,
135                start_capturer: Some(start_capturer),
136                stop_capturer: Some(stop_capturer),
137                is_capturer_initialized: None,
138                is_capturer_started: None,
139                get_estimated_capture_delay: None,
140                get_capture_settings: Some(get_capture_settings),
141                init_renderer: None,
142                destroy_renderer: None,
143                start_renderer: Some(start_renderer),
144                stop_renderer: Some(stop_renderer),
145                is_renderer_initialized: None,
146                is_renderer_started: None,
147                get_estimated_render_delay: None,
148                get_render_settings: Some(get_render_settings),
149                user_data: std::ptr::null_mut(),
150                reserved: std::ptr::null_mut(),
151            })),
152        };
153
154        unsafe {
155            if let Err(e) =
156                ffi::otc_set_audio_device(&*device.ffi_callbacks.lock().unwrap()
157                    as *const ffi::otc_audio_device_callbacks)
158                .into_result()
159            {
160                warn!("Could not set audio device callbacks. {}", e);
161            }
162        }
163
164        device
165    }
166
167    pub fn get_instance() -> Arc<Mutex<AudioDevice>> {
168        SINGLETON.clone()
169    }
170
171    pub fn stop() {
172        let _ = SINGLETON.lock().unwrap().stop_renderer();
173    }
174
175    pub fn override_capture_settings(&self, settings: AudioDeviceSettings) {
176        *self.capture_settings.lock().unwrap() = settings;
177    }
178
179    pub fn override_render_settings(&self, settings: AudioDeviceSettings) {
180        *self.render_settings.lock().unwrap() = settings;
181    }
182
183    fn capture_settings(&self) -> AudioDeviceSettings {
184        *self.capture_settings.lock().unwrap()
185    }
186
187    fn render_settings(&self) -> AudioDeviceSettings {
188        *self.render_settings.lock().unwrap()
189    }
190
191    pub fn set_on_audio_sample_callback(&self, callback: OnAudioSampleCallback) {
192        self.on_audio_sample_callbacks
193            .lock()
194            .unwrap()
195            .push(callback);
196    }
197
198    pub fn push_audio_sample(&self, data: AudioSampleData) {
199        if !self.capturer_ready.load(Ordering::Relaxed) {
200            warn!("Audio capturer is not ready yet. Dropping audio sample");
201            return;
202        }
203        let size = data.0.len();
204        unsafe { ffi::otc_audio_device_write_capture_data(data.0.as_ptr(), size as u64) };
205    }
206
207    fn start_capturer(&self) -> OtcResult {
208        self.capturer_ready.store(true, Ordering::Relaxed);
209        Ok(())
210    }
211
212    fn stop_capturer(&self) -> OtcResult {
213        self.capturer_ready.store(false, Ordering::Relaxed);
214        Ok(())
215    }
216
217    fn start_renderer(&self) -> OtcResult {
218        self.render_thread_running.store(true, Ordering::Relaxed);
219        let render_thread_running = self.render_thread_running.clone();
220        let sampling_rate = self.render_settings.lock().unwrap().sampling_rate;
221        let number_of_channels = self.render_settings.lock().unwrap().number_of_channels;
222        let size = (sampling_rate / 100) * number_of_channels;
223        let on_audio_sample_callbacks = self.on_audio_sample_callbacks.clone();
224        std::thread::spawn(move || loop {
225            if !render_thread_running.load(Ordering::Relaxed) {
226                break;
227            }
228            let data = unsafe {
229                let mut data = Vec::with_capacity(size as usize);
230                let size = ffi::otc_audio_device_read_render_data(data.as_mut_ptr(), size as u64);
231                data.set_len(size as usize);
232                data
233            };
234            if data.is_empty() {
235                continue;
236            }
237            if let Ok(callbacks) = on_audio_sample_callbacks.try_lock() {
238                for ref callback in callbacks.iter() {
239                    callback(AudioSample {
240                        data: AudioSampleData(data.clone()),
241                        sampling_rate,
242                        number_of_channels,
243                    });
244                }
245            }
246            std::thread::sleep(std::time::Duration::from_micros(10000));
247        });
248        Ok(())
249    }
250
251    fn stop_renderer(&self) -> OtcResult {
252        self.render_thread_running.store(false, Ordering::Relaxed);
253        Ok(())
254    }
255}