Skip to main content

moq_mux/
catalog.rs

1use std::ops::{Deref, DerefMut};
2use std::sync::{Arc, Mutex, MutexGuard};
3
4/// Produces both a hang and MSF catalog track for a broadcast.
5///
6/// The JSON catalog is updated when tracks are added/removed but is *not* automatically published.
7/// You'll have to call [`lock`](Self::lock) to update and publish the catalog.
8/// Both the hang (`catalog.json`) and MSF (`catalog`) tracks are published on drop of the guard.
9#[derive(Clone)]
10pub struct CatalogProducer {
11	/// Access to the underlying hang catalog track producer.
12	pub hang_track: moq_lite::TrackProducer,
13
14	/// Access to the underlying MSF catalog track producer.
15	pub msf_track: moq_lite::TrackProducer,
16
17	current: Arc<Mutex<hang::Catalog>>,
18}
19
20impl CatalogProducer {
21	/// Create a new catalog producer, inserting both catalog tracks into the broadcast.
22	pub fn new(broadcast: &mut moq_lite::BroadcastProducer) -> Result<Self, moq_lite::Error> {
23		Self::with_catalog(broadcast, hang::Catalog::default())
24	}
25
26	/// Create a new catalog producer with the given initial catalog.
27	pub fn with_catalog(
28		broadcast: &mut moq_lite::BroadcastProducer,
29		catalog: hang::Catalog,
30	) -> Result<Self, moq_lite::Error> {
31		let hang_track = broadcast.create_track(hang::Catalog::default_track())?;
32		let msf_track = broadcast.create_track(moq_lite::Track {
33			name: moq_msf::DEFAULT_NAME.to_string(),
34			priority: 100,
35		})?;
36
37		Ok(Self {
38			hang_track,
39			msf_track,
40			current: Arc::new(Mutex::new(catalog)),
41		})
42	}
43
44	/// Get mutable access to the catalog, publishing it after any changes.
45	pub fn lock(&mut self) -> CatalogGuard<'_> {
46		CatalogGuard {
47			catalog: self.current.lock().unwrap(),
48			hang_track: &mut self.hang_track,
49			msf_track: &mut self.msf_track,
50			updated: false,
51		}
52	}
53
54	/// Create a consumer for this catalog, receiving updates as they're published.
55	pub fn consume(&self) -> hang::CatalogConsumer {
56		hang::CatalogConsumer::new(self.hang_track.consume())
57	}
58
59	/// Finish publishing to this catalog.
60	pub fn finish(&mut self) -> Result<(), moq_lite::Error> {
61		self.hang_track.finish()?;
62		self.msf_track.finish()?;
63		Ok(())
64	}
65}
66
67/// RAII guard for modifying a catalog with automatic publishing on drop.
68///
69/// Obtained via [`CatalogProducer::lock`].
70///
71/// On drop, both the hang and MSF catalog tracks are updated if the catalog was mutated.
72pub struct CatalogGuard<'a> {
73	catalog: MutexGuard<'a, hang::Catalog>,
74	hang_track: &'a mut moq_lite::TrackProducer,
75	msf_track: &'a mut moq_lite::TrackProducer,
76	updated: bool,
77}
78
79impl<'a> Deref for CatalogGuard<'a> {
80	type Target = hang::Catalog;
81
82	fn deref(&self) -> &Self::Target {
83		&self.catalog
84	}
85}
86
87impl<'a> DerefMut for CatalogGuard<'a> {
88	fn deref_mut(&mut self) -> &mut Self::Target {
89		self.updated = true;
90		&mut self.catalog
91	}
92}
93
94impl Drop for CatalogGuard<'_> {
95	fn drop(&mut self) {
96		if !self.updated {
97			return;
98		}
99
100		// Publish hang catalog
101		let Ok(mut group) = self.hang_track.append_group() else {
102			return;
103		};
104		let frame = self.catalog.to_string().expect("invalid catalog");
105		let _ = group.write_frame(frame);
106		let _ = group.finish();
107
108		// Publish MSF catalog
109		crate::msf::publish(&self.catalog, self.msf_track);
110	}
111}