use std::collections::{HashMap, VecDeque};
use std::sync::{Arc, Mutex};
use cef::*;
#[derive(Debug, Default, Clone)]
pub struct AudioState {
pub active: bool,
pub stream_count: u32,
}
pub type AudioStateSink = Arc<Mutex<HashMap<i32, AudioState>>>;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct AudioEvent {
pub browser_id: i32,
pub active: bool,
}
pub type AudioEventQueue = Arc<Mutex<VecDeque<AudioEvent>>>;
pub fn new_audio_state_sink() -> AudioStateSink {
Arc::new(Mutex::new(HashMap::new()))
}
pub fn new_audio_event_queue() -> AudioEventQueue {
Arc::new(Mutex::new(VecDeque::new()))
}
pub fn drain_audio_events(queue: &AudioEventQueue) -> Vec<AudioEvent> {
if let Ok(mut g) = queue.lock() {
return g.drain(..).collect();
}
Vec::new()
}
pub fn any_audio_active(sink: &AudioStateSink) -> bool {
sink.lock()
.map(|g| g.values().any(|s| s.active))
.unwrap_or(false)
}
wrap_audio_handler! {
pub struct BuffrAudioHandler {
sink: AudioStateSink,
queue: AudioEventQueue,
}
impl AudioHandler {
fn on_audio_stream_started(
&self,
browser: Option<&mut Browser>,
_params: Option<&AudioParameters>,
_channels: ::std::os::raw::c_int,
) {
let browser_id = browser.map(|b| b.identifier()).unwrap_or(-1);
let was_active = {
let Ok(mut map) = self.sink.lock() else { return };
let entry = map.entry(browser_id).or_default();
let prev = entry.active;
entry.stream_count = entry.stream_count.saturating_add(1);
entry.active = true;
prev
};
if !was_active
&& let Ok(mut q) = self.queue.lock()
{
q.push_back(AudioEvent {
browser_id,
active: true,
});
}
tracing::debug!(
target: "buffr_core::audio",
browser_id,
"on_audio_stream_started"
);
}
fn on_audio_stream_stopped(&self, browser: Option<&mut Browser>) {
let browser_id = browser.map(|b| b.identifier()).unwrap_or(-1);
let became_inactive = {
let Ok(mut map) = self.sink.lock() else { return };
let entry = map.entry(browser_id).or_default();
entry.stream_count = entry.stream_count.saturating_sub(1);
if entry.stream_count == 0 && entry.active {
entry.active = false;
true
} else {
false
}
};
if became_inactive
&& let Ok(mut q) = self.queue.lock()
{
q.push_back(AudioEvent {
browser_id,
active: false,
});
}
tracing::debug!(
target: "buffr_core::audio",
browser_id,
"on_audio_stream_stopped"
);
}
fn on_audio_stream_error(
&self,
browser: Option<&mut Browser>,
_message: Option<&CefString>,
) {
let browser_id = browser.map(|b| b.identifier()).unwrap_or(-1);
let became_inactive = {
let Ok(mut map) = self.sink.lock() else { return };
let entry = map.entry(browser_id).or_default();
if entry.active {
entry.stream_count = 0;
entry.active = false;
true
} else {
false
}
};
if became_inactive
&& let Ok(mut q) = self.queue.lock()
{
q.push_back(AudioEvent {
browser_id,
active: false,
});
}
tracing::debug!(
target: "buffr_core::audio",
browser_id,
"on_audio_stream_error: treating as stopped"
);
}
}
}
impl BuffrAudioHandler {
pub fn make(sink: AudioStateSink, queue: AudioEventQueue) -> AudioHandler {
Self::new(sink, queue)
}
}