Skip to content

Commit c38abd5

Browse files
kixelatedclaude
andauthored
moq-net: reshape Track into TrackInfo + an async TrackConsumer handle (#1631)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent d82486c commit c38abd5

65 files changed

Lines changed: 1331 additions & 1217 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

rs/hang/examples/subscribe.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,8 @@ async fn run_subscribe(consumer: moq_net::OriginConsumer) -> anyhow::Result<()>
5252

5353
// Read the catalog to discover available tracks.
5454
let catalog_track = broadcast
55-
.consume_track(hang::Catalog::DEFAULT_NAME)
56-
.subscribe(hang::Catalog::default_subscription())
55+
.track(hang::Catalog::DEFAULT_NAME)?
56+
.subscribe(hang::Catalog::default_subscription())?
5757
.await?;
5858
let mut catalog = moq_mux::catalog::hang::Consumer::new(catalog_track);
5959

@@ -77,11 +77,11 @@ async fn run_subscribe(consumer: moq_net::OriginConsumer) -> anyhow::Result<()>
7777

7878
// Subscribe to the video track.
7979
let track_consumer = broadcast
80-
.consume_track(name)
80+
.track(name)?
8181
.subscribe(moq_net::Subscription {
8282
priority: 1,
8383
..Default::default()
84-
})
84+
})?
8585
.await?;
8686
let mut ordered = moq_mux::container::Consumer::new(track_consumer, moq_mux::catalog::hang::Container::Legacy)
8787
.with_latency(Duration::from_millis(500));

rs/hang/examples/video.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ async fn run_session(origin: moq_net::OriginProducer) -> anyhow::Result<()> {
4242
// The catalog can contain multiple tracks, used by the viewer to choose the best track.
4343
fn create_track(broadcast: &mut moq_net::BroadcastProducer) -> anyhow::Result<moq_net::TrackProducer> {
4444
// Basic information about the video track.
45-
let video_track = moq_net::Track::new("video");
45+
let video_track = "video";
4646

4747
// Example video configuration
4848
// In a real application, you would get this from the encoder
@@ -61,7 +61,7 @@ fn create_track(broadcast: &mut moq_net::BroadcastProducer) -> anyhow::Result<mo
6161
// Create a map of video renditions
6262
// Multiple renditions allow the viewer to choose based on their capabilities
6363
let mut renditions = std::collections::BTreeMap::new();
64-
renditions.insert(video_track.name.clone(), video_config);
64+
renditions.insert(video_track.to_string(), video_config);
6565

6666
// Create the catalog describing our video track.
6767
let catalog = hang::catalog::Catalog {
@@ -75,21 +75,21 @@ fn create_track(broadcast: &mut moq_net::BroadcastProducer) -> anyhow::Result<mo
7575
};
7676

7777
// Publish the catalog as a "catalog.json" track in the broadcast.
78-
let mut catalog_track = broadcast.create_track(hang::Catalog::default_track())?;
78+
let mut catalog_track = broadcast.create_track(hang::Catalog::DEFAULT_NAME, hang::Catalog::default_track_info())?;
7979
let mut group = catalog_track.append_group()?;
8080
group.write_frame(catalog.to_string()?)?;
8181
group.finish()?;
8282

8383
// Actually create the media track now.
84-
let track = broadcast.create_track(video_track)?;
84+
let track = broadcast.create_track(video_track, None)?;
8585

8686
Ok(track)
8787
}
8888

8989
// Produce a broadcast and publish it to the origin.
9090
async fn run_broadcast(origin: moq_net::OriginProducer) -> anyhow::Result<()> {
9191
// Create and publish a broadcast to the origin.
92-
let mut broadcast = moq_net::Broadcast::new().produce();
92+
let mut broadcast = moq_net::BroadcastInfo::new().produce();
9393
let track = create_track(&mut broadcast)?;
9494

9595
// NOTE: The path is empty because we're using the URL to scope the broadcast.

rs/hang/src/catalog/root.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,12 @@ impl Catalog {
6464
Ok(serde_json::to_writer(writer, self)?)
6565
}
6666

67-
pub fn default_track() -> moq_net::Track {
68-
// The catalog is JSON and re-sent on every change, so it pays to compress.
69-
moq_net::Track::new(Catalog::DEFAULT_NAME).with_compress(true)
67+
/// Track properties for creating the catalog track via
68+
/// [`create_track`](moq_net::BroadcastProducer::create_track) at
69+
/// [`DEFAULT_NAME`](Self::DEFAULT_NAME). The catalog is JSON and re-sent on
70+
/// every change, so it pays to compress.
71+
pub fn default_track_info() -> moq_net::TrackInfo {
72+
moq_net::TrackInfo::default().with_compress(true)
7073
}
7174

7275
/// The subscription preferences used for the catalog track (high priority so

rs/kio/src/consumer.rs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use std::{
44
};
55

66
use crate::{
7-
Counts, State, Weak,
7+
Counts, Mut, State, Weak,
88
lock::*,
99
producer::{Producer, Ref},
1010
waiter::*,
@@ -106,6 +106,24 @@ impl<T> Consumer<T> {
106106
}
107107
}
108108

109+
/// Acquire mutable access to the shared state.
110+
///
111+
/// Returns `Ok(Mut)` if the channel is open, or `Err(Ref)` with
112+
/// read-only access if closed. Only locks once.
113+
pub fn write(&self) -> Result<Mut<'_, T>, Ref<'_, T>> {
114+
let state = self.state.lock();
115+
if state.closed {
116+
Err(Ref { state })
117+
} else {
118+
Ok(Mut::new(state))
119+
}
120+
}
121+
122+
/// Returns `true` if the channel has been closed by the producer.
123+
pub fn is_closed(&self) -> bool {
124+
self.state.lock().closed
125+
}
126+
109127
/// Returns `true` if both consumers share the same underlying state.
110128
pub fn same_channel(&self, other: &Self) -> bool {
111129
self.state.is_clone(&other.state)

rs/libmoq/src/consume.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,8 @@ impl Consume {
6969
tokio::spawn(async move {
7070
let res = async move {
7171
let catalog = broadcast
72-
.consume_track(hang::catalog::Catalog::DEFAULT_NAME)
73-
.subscribe(hang::catalog::Catalog::default_subscription())
72+
.track(hang::catalog::Catalog::DEFAULT_NAME)?
73+
.subscribe(hang::catalog::Catalog::default_subscription())?
7474
.await?;
7575
Self::run_catalog(on_catalog, broadcast.clone(), catalog.into(), channel.1).await
7676
}
@@ -250,11 +250,11 @@ impl Consume {
250250
tokio::spawn(async move {
251251
let res = async move {
252252
let track = broadcast
253-
.consume_track(&name)
253+
.track(&name)?
254254
.subscribe(moq_net::Subscription {
255255
priority: 1,
256256
..Default::default()
257-
})
257+
})?
258258
.await?;
259259
let track = moq_mux::container::Consumer::new(track, moq_mux::catalog::hang::Container::Legacy)
260260
.with_latency(latency);
@@ -302,11 +302,11 @@ impl Consume {
302302
tokio::spawn(async move {
303303
let res = async move {
304304
let track = broadcast
305-
.consume_track(&name)
305+
.track(&name)?
306306
.subscribe(moq_net::Subscription {
307307
priority: 2,
308308
..Default::default()
309-
})
309+
})?
310310
.await?;
311311
let track = moq_mux::container::Consumer::new(track, moq_mux::catalog::hang::Container::Legacy)
312312
.with_latency(latency);
@@ -407,7 +407,7 @@ impl Consume {
407407
// `subscribe` blocks on SUBSCRIBE_OK, so run it inside the task.
408408
tokio::spawn(async move {
409409
let res = async move {
410-
let track = broadcast.consume_track(&name).subscribe(None).await?;
410+
let track = broadcast.track(&name)?.subscribe(None)?.await?;
411411
Self::run_raw(on_frame, track, channel.1).await
412412
}
413413
.await;

rs/libmoq/src/publish.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ pub struct Publish {
2222

2323
impl Publish {
2424
pub fn create(&mut self) -> Result<Id, Error> {
25-
let mut broadcast = moq_net::Broadcast::new().produce();
25+
let mut broadcast = moq_net::BroadcastInfo::new().produce();
2626
let catalog = moq_mux::catalog::hang::Producer::new(&mut broadcast)?;
2727

2828
let id = self.broadcasts.insert((broadcast, catalog))?;
@@ -128,7 +128,7 @@ impl Publish {
128128
/// if you want to describe the track in the catalog as well.
129129
pub fn track(&mut self, broadcast: Id, name: &str) -> Result<Id, Error> {
130130
let (broadcast, _) = self.broadcasts.get_mut(broadcast).ok_or(Error::BroadcastNotFound)?;
131-
let track = broadcast.create_track(moq_net::Track::new(name))?;
131+
let track = broadcast.create_track(name, None)?;
132132
self.tracks.insert(track)
133133
}
134134

rs/moq-audio/src/consumer.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ impl AudioConsumer {
5353
};
5454

5555
let name = name.into();
56-
let track = broadcast.consume_track(&name).subscribe(None).await?;
56+
let track = broadcast.track(&name)?.subscribe(None)?.await?;
5757
let mut track = moq_mux::container::Consumer::new(track, moq_mux::container::legacy::Wire);
5858
if let Some(latency) = output.latency_max {
5959
track = track.with_latency(latency);

rs/moq-audio/src/producer.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,10 @@ impl AudioProducer {
6161
// Audio hang frames carry microsecond timestamps; advertise that on the
6262
// track so Lite05 subscribers know what scale to expect and the model
6363
// layer accepts Frame::timestamp on append.
64-
let track =
65-
broadcast.create_track(moq_net::Track::new(name.clone()).with_timescale(hang::container::TIMESCALE))?;
64+
let track = broadcast.create_track(
65+
name.clone(),
66+
moq_net::TrackInfo::default().with_timescale(hang::container::TIMESCALE),
67+
)?;
6668
let track = moq_mux::container::Producer::new(track, moq_mux::container::legacy::Wire);
6769

6870
let mut catalog_mut = catalog.clone();

rs/moq-audio/tests/roundtrip.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ fn f32_bytes(samples: &[f32]) -> Bytes {
3030

3131
#[tokio::test]
3232
async fn opus_round_trip_48k_stereo() {
33-
let mut broadcast = moq_net::Broadcast::new().produce();
33+
let mut broadcast = moq_net::BroadcastInfo::new().produce();
3434
let catalog = moq_mux::catalog::hang::Producer::new(&mut broadcast).unwrap();
3535
let mut catalog_consumer = catalog.consume().unwrap();
3636
let broadcast_consumer = broadcast.consume();
@@ -110,7 +110,7 @@ async fn opus_round_trip_48k_stereo() {
110110

111111
#[tokio::test]
112112
async fn opus_round_trip_44100_s16_resampled() {
113-
let mut broadcast = moq_net::Broadcast::new().produce();
113+
let mut broadcast = moq_net::BroadcastInfo::new().produce();
114114
let catalog = moq_mux::catalog::hang::Producer::new(&mut broadcast).unwrap();
115115
let mut catalog_consumer = catalog.consume().unwrap();
116116
let broadcast_consumer = broadcast.consume();

rs/moq-boy/src/input.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ async fn handle_viewer_commands(
9999
broadcast: moq_net::BroadcastConsumer,
100100
cmd_tx: &tokio::sync::mpsc::Sender<Command>,
101101
) -> anyhow::Result<()> {
102-
let mut track = broadcast.consume_track("command").subscribe(None).await?;
102+
let mut track = broadcast.track("command")?.subscribe(None)?.await?;
103103

104104
while let Some(mut group) = track.recv_group().await? {
105105
while let Some(frame) = group.read_frame().await? {

0 commit comments

Comments
 (0)