use std::sync::{Arc, Mutex};
use hang::catalog::VideoConfig;
use moq_mux::container::Container as _;
use tokio::sync::broadcast;
use crate::Error;
const CAPACITY: usize = 16;
#[derive(Clone)]
pub(crate) enum Item {
Group(u64),
Frame(Arc<moq_video::decode::Frame>),
End,
Finished,
Lagged,
}
#[derive(Clone)]
pub(crate) struct Feed {
inner: Arc<Inner>,
}
struct Inner {
source: moq_net::track::Consumer,
config: VideoConfig,
decoder: moq_video::decode::Kind,
state: Mutex<State>,
}
#[derive(Default)]
struct State {
listeners: usize,
sender: Option<broadcast::Sender<Item>>,
task: Option<tokio::task::JoinHandle<()>>,
}
impl Feed {
pub(crate) fn new(source: moq_net::track::Consumer, config: VideoConfig, decoder: moq_video::decode::Kind) -> Self {
Self {
inner: Arc::new(Inner {
source,
config,
decoder,
state: Mutex::new(State::default()),
}),
}
}
pub(crate) fn listen(&self) -> Listener {
let mut state = self.inner.state.lock().unwrap();
state.listeners += 1;
if state.task.as_ref().is_some_and(|task| task.is_finished()) {
state.sender = None;
state.task = None;
}
if state.sender.is_none() {
let (sender, _) = broadcast::channel(CAPACITY);
state.task = Some(tokio::spawn(run(self.inner.clone(), sender.clone())));
state.sender = Some(sender);
}
let receiver = state.sender.as_ref().expect("sender ensured above").subscribe();
Listener {
feed: self.clone(),
receiver,
}
}
}
pub(crate) struct Listener {
feed: Feed,
receiver: broadcast::Receiver<Item>,
}
impl Listener {
pub(crate) async fn recv(&mut self) -> Option<Item> {
match self.receiver.recv().await {
Ok(item) => Some(item),
Err(broadcast::error::RecvError::Lagged(_)) => Some(Item::Lagged),
Err(broadcast::error::RecvError::Closed) => None,
}
}
}
impl Drop for Listener {
fn drop(&mut self) {
let mut state = self.feed.inner.state.lock().unwrap();
state.listeners -= 1;
if state.listeners == 0 {
state.sender = None;
if let Some(task) = state.task.take() {
task.abort();
}
}
}
}
async fn run(inner: Arc<Inner>, sender: broadcast::Sender<Item>) {
match decode(&inner, &sender).await {
Ok(()) => {
let _ = sender.send(Item::Finished);
}
Err(err) => tracing::warn!(%err, "shared decode session failed"),
}
let mut state = inner.state.lock().unwrap();
if state.sender.as_ref().is_some_and(|s| s.same_channel(&sender)) {
state.sender = None;
state.task = None;
}
}
async fn decode(inner: &Inner, sender: &broadcast::Sender<Item>) -> Result<(), Error> {
let container = moq_mux::catalog::hang::Container::try_from(&inner.config.container)?;
let mut config = moq_video::decode::Config::new();
config.kind = inner.decoder.clone();
let mut decoder = moq_video::decode::Decoder::new(&inner.config, &config)?;
let mut subscriber = inner.source.subscribe(None).await?;
while let Some(mut group) = subscriber.next_group().await? {
let _ = sender.send(Item::Group(group.sequence));
let mut first = true;
while let Some(frames) = container.read(&mut group).await? {
for frame in frames {
let timestamp = frame.timestamp;
let keyframe = frame.keyframe || first;
first = false;
for decoded in decoder.decode(&frame.payload, timestamp, keyframe)? {
let _ = sender.send(Item::Frame(Arc::new(decoded)));
}
}
}
let _ = sender.send(Item::End);
}
Ok(())
}