From d680ba0a15c27fde458d7c9a4649673c00e236db Mon Sep 17 00:00:00 2001 From: Brad Greenway Date: Fri, 25 Sep 2026 07:41:26 -0500 Subject: [PATCH 1/3] feat(ffi): advertise JSON tracks in the catalog, add binary data tracks The moq-ffi mirror of #4073. publish_json_snapshot / publish_json_stream now go through the broadcast's moq-mux catalog, so the catalog carries json.tracks. (mode, and compression when set) for as long as the track lives and retires it on finish. New publish_binary_snapshot / publish_binary_stream expose moq-mux's binary data tracks with an optional mime. A name the catalog already carries is refused. moq-mux's json Snapshot/Stream gain demand() so the FFI producers keep their existing demand() getter. --- rs/moq-ffi/src/binary.rs | 125 +++++++++++++++++++++++++++++++++++++++ rs/moq-ffi/src/json.rs | 35 ++++++----- rs/moq-ffi/src/lib.rs | 1 + rs/moq-ffi/src/test.rs | 123 ++++++++++++++++++++++++++++++++++++++ rs/moq-mux/src/json.rs | 10 ++++ 5 files changed, 280 insertions(+), 14 deletions(-) create mode 100644 rs/moq-ffi/src/binary.rs diff --git a/rs/moq-ffi/src/binary.rs b/rs/moq-ffi/src/binary.rs new file mode 100644 index 0000000000..8e343bd2ad --- /dev/null +++ b/rs/moq-ffi/src/binary.rs @@ -0,0 +1,125 @@ +//! Binary data tracks over the FFI boundary, advertised in the catalog. +//! +//! The binary counterpart of [`crate::json`]: opaque payloads (for example a camera's latest JPEG +//! thumbnail) on a named track, in either mode — `snapshot` (each payload supersedes the last) or +//! `stream` (every payload preserved in order). The broadcast's catalog carries +//! `binary.tracks.` (mode, plus `mime` and `compression` when set) for as long as the track +//! lives, so a consumer discovers it without knowing the application. + +use std::sync::Arc; + +use moq_mux::catalog::hang::Extra; + +use crate::error::MoqError; +use crate::producer::MoqBroadcastProducer; + +/// Options for a binary data track, in either mode (the mode is fixed by the constructor). +#[derive(Clone, uniffi::Record)] +pub struct MoqBinaryConfig { + /// DEFLATE-compress each payload, advertised in the catalog entry. + #[uniffi(default = false)] + pub compression: bool, + + /// The payloads' media type (e.g. `image/jpeg`), or `None` to leave it unstated. + #[uniffi(default = None)] + pub mime: Option, +} + +impl From for moq_mux::binary::Config { + fn from(config: MoqBinaryConfig) -> Self { + let mut out = moq_mux::binary::Config::default().with_compression(config.compression); + if let Some(mime) = config.mime { + out = out.with_mime(mime); + } + out + } +} + +#[uniffi::export] +impl MoqBroadcastProducer { + /// Publish a binary snapshot track (lossy latest-value) by name, advertised in the catalog. + /// + /// Errors if the catalog already carries an entry under `name`. + pub fn publish_binary_snapshot( + &self, + name: String, + config: MoqBinaryConfig, + ) -> Result, MoqError> { + let _guard = crate::ffi::enter(); + self.with_state(|state| { + let track = state.broadcast.create_track(name, None)?; + let producer = state.catalog.binary_snapshot(track, config.into())?; + Ok(Arc::new(MoqBinarySnapshotProducer { + inner: std::sync::Mutex::new(Some(producer)), + })) + }) + } + + /// Publish a binary stream track (lossless append-log) by name, advertised in the catalog. + /// + /// Errors if the catalog already carries an entry under `name`. + pub fn publish_binary_stream( + &self, + name: String, + config: MoqBinaryConfig, + ) -> Result, MoqError> { + let _guard = crate::ffi::enter(); + self.with_state(|state| { + let track = state.broadcast.create_track(name, None)?; + let producer = state.catalog.binary_stream(track, config.into())?; + Ok(Arc::new(MoqBinaryStreamProducer { + inner: std::sync::Mutex::new(Some(producer)), + })) + }) + } +} + +/// Publishes opaque payloads that consumers see as a single latest value. +#[derive(uniffi::Object)] +pub struct MoqBinarySnapshotProducer { + inner: std::sync::Mutex>>, +} + +#[uniffi::export] +impl MoqBinarySnapshotProducer { + /// Publish a new payload, superseding the last. + pub fn update(&self, payload: Vec) -> Result<(), MoqError> { + let _guard = crate::ffi::enter(); + let mut guard = self.inner.lock().unwrap(); + guard.as_mut().ok_or(MoqError::Closed)?.update(payload)?; + Ok(()) + } + + /// Finish the track and retire its catalog entry. + pub fn finish(&self) -> Result<(), MoqError> { + let _guard = crate::ffi::enter(); + let producer = self.inner.lock().unwrap().take().ok_or(MoqError::Closed)?; + producer.finish()?; + Ok(()) + } +} + +/// Publishes an ordered log of opaque payloads, one per append. +#[derive(uniffi::Object)] +pub struct MoqBinaryStreamProducer { + inner: std::sync::Mutex>>, +} + +#[uniffi::export] +impl MoqBinaryStreamProducer { + /// Append one payload to the log. + pub fn append(&self, payload: Vec) -> Result<(), MoqError> { + let _guard = crate::ffi::enter(); + let mut guard = self.inner.lock().unwrap(); + guard.as_mut().ok_or(MoqError::Closed)?.append(payload)?; + Ok(()) + } + + /// Finish the track and retire its catalog entry. + pub fn finish(&self) -> Result<(), MoqError> { + let _guard = crate::ffi::enter(); + let producer = self.inner.lock().unwrap().take().ok_or(MoqError::Closed)?; + producer.finish()?; + Ok(()) + } +} diff --git a/rs/moq-ffi/src/json.rs b/rs/moq-ffi/src/json.rs index 243067d655..6b10c40e10 100644 --- a/rs/moq-ffi/src/json.rs +++ b/rs/moq-ffi/src/json.rs @@ -14,6 +14,7 @@ use crate::demand::MoqTrackDemand; use crate::error::MoqError; use crate::ffi::Task; use crate::producer::MoqBroadcastProducer; +use moq_mux::catalog::hang::Extra; /// Options for a JSON snapshot track (lossy latest-value mode). /// @@ -101,10 +102,11 @@ mod tests { #[uniffi::export] impl MoqBroadcastProducer { - /// Publish a JSON snapshot track (lossy latest-value) by name. + /// Publish a JSON snapshot track (lossy latest-value) by name, advertised in the catalog. /// - /// Advertise it in the catalog yourself with - /// [`set_catalog_section`](Self::set_catalog_section) if consumers should discover it. + /// The broadcast's catalog carries `json.tracks.` (`mode: snapshot`, and + /// `compression: deflate` when set) for as long as the track lives; finishing or dropping the + /// producer retires it. Errors if the catalog already carries an entry under `name`. pub fn publish_json_snapshot( &self, name: String, @@ -112,16 +114,21 @@ impl MoqBroadcastProducer { ) -> Result, MoqError> { let _guard = crate::ffi::enter(); self.with_state(|state| { - let broadcast = state.broadcast.clone(); - let track = broadcast.create_track(name, None)?; - let producer = moq_json::snapshot::Producer::::new(track, config.into()); + let track = state.broadcast.create_track(name, None)?; + let config = moq_mux::json::Config::default() + .with_compression(config.compression) + .with_delta_ratio(config.delta_ratio); + let producer = state.catalog.json_snapshot::(track, config)?; Ok(Arc::new(MoqJsonSnapshotProducer { inner: std::sync::Mutex::new(Some(producer)), })) }) } - /// Publish a JSON stream track (lossless append-log) by name. + /// Publish a JSON stream track (lossless append-log) by name, advertised in the catalog. + /// + /// The broadcast's catalog carries `json.tracks.` (`mode: stream`) for as long as the + /// track lives. Errors if the catalog already carries an entry under `name`. pub fn publish_json_stream( &self, name: String, @@ -129,9 +136,9 @@ impl MoqBroadcastProducer { ) -> Result, MoqError> { let _guard = crate::ffi::enter(); self.with_state(|state| { - let broadcast = state.broadcast.clone(); - let track = broadcast.create_track(name, None)?; - let producer = moq_json::stream::Producer::::new(track, config.into()); + let track = state.broadcast.create_track(name, None)?; + let config = moq_mux::json::Config::default().with_compression(config.compression); + let producer = state.catalog.json_stream::(track, config)?; Ok(Arc::new(MoqJsonStreamProducer { inner: std::sync::Mutex::new(Some(producer)), })) @@ -175,7 +182,7 @@ impl MoqBroadcastConsumer { /// Publishes a JSON value that consumers see as a single latest state. #[derive(uniffi::Object)] pub struct MoqJsonSnapshotProducer { - inner: std::sync::Mutex>>, + inner: std::sync::Mutex>>, } #[uniffi::export] @@ -200,7 +207,7 @@ impl MoqJsonSnapshotProducer { /// Finish the track, closing any open group. pub fn finish(&self) -> Result<(), MoqError> { let _guard = crate::ffi::enter(); - let mut producer = self.inner.lock().unwrap().take().ok_or(MoqError::Closed)?; + let producer = self.inner.lock().unwrap().take().ok_or(MoqError::Closed)?; producer.finish()?; Ok(()) } @@ -247,7 +254,7 @@ impl MoqJsonSnapshotConsumer { /// Publishes an ordered log of JSON records, one record per append. #[derive(uniffi::Object)] pub struct MoqJsonStreamProducer { - inner: std::sync::Mutex>>, + inner: std::sync::Mutex>>, } #[uniffi::export] @@ -271,7 +278,7 @@ impl MoqJsonStreamProducer { /// Finish the track, closing the group. pub fn finish(&self) -> Result<(), MoqError> { let _guard = crate::ffi::enter(); - let mut producer = self.inner.lock().unwrap().take().ok_or(MoqError::Closed)?; + let producer = self.inner.lock().unwrap().take().ok_or(MoqError::Closed)?; producer.finish()?; Ok(()) } diff --git a/rs/moq-ffi/src/lib.rs b/rs/moq-ffi/src/lib.rs index 1916f1282b..8b2743fdef 100644 --- a/rs/moq-ffi/src/lib.rs +++ b/rs/moq-ffi/src/lib.rs @@ -17,6 +17,7 @@ mod android; #[cfg(all(feature = "audio", not(target_arch = "wasm32")))] pub mod audio; pub mod bandwidth; +pub mod binary; pub mod consumer; pub mod demand; pub mod error; diff --git a/rs/moq-ffi/src/test.rs b/rs/moq-ffi/src/test.rs index 4963dff98f..265dee6162 100644 --- a/rs/moq-ffi/src/test.rs +++ b/rs/moq-ffi/src/test.rs @@ -2,6 +2,7 @@ use super::origin::*; use super::producer::*; use super::server::MoqServer; use super::session::{MoqClient, MoqSession}; +use crate::binary::MoqBinaryConfig; use crate::consumer::MoqBroadcastConsumer; use crate::consumer::MoqFetchGroupOptions; use crate::consumer::MoqSubscription; @@ -4495,3 +4496,125 @@ async fn shutdown_cancels_and_drops_cleanly() { drop(client_origin); drop(server_origin); } + +/// The broadcast's current catalog, read on the publish side. +fn published_catalog( + broadcast: &MoqBroadcastProducer, +) -> moq_mux::catalog::hang::Catalog { + broadcast.with_state(|state| Ok(state.catalog.snapshot())).unwrap() +} + +/// JSON tracks are advertised in the catalog (no `set_catalog_section` needed) and retired on finish. +#[tokio::test] +async fn json_tracks_are_advertised_in_the_catalog() { + let broadcast = MoqBroadcastProducer::new().unwrap(); + let snapshot = broadcast + .publish_json_snapshot( + "status".into(), + MoqJsonSnapshotConfig { + delta_ratio: 4, + compression: true, + }, + ) + .unwrap(); + let stream = broadcast + .publish_json_stream("events".into(), MoqJsonStreamConfig { compression: false }) + .unwrap(); + + let catalog = published_catalog(&broadcast); + let entry = catalog.json.tracks.get("status").expect("snapshot track advertised"); + assert_eq!(entry.mode, hang::catalog::Mode::Snapshot); + assert_eq!(entry.compression, Some(hang::catalog::Compression::Deflate)); + let entry = catalog.json.tracks.get("events").expect("stream track advertised"); + assert_eq!(entry.mode, hang::catalog::Mode::Stream); + assert_eq!(entry.compression, None); + + snapshot.finish().unwrap(); + let catalog = published_catalog(&broadcast); + assert!( + !catalog.json.tracks.contains_key("status"), + "finished track still advertised" + ); + assert!(catalog.json.tracks.contains_key("events")); + stream.finish().unwrap(); + assert!(published_catalog(&broadcast).json.tracks.is_empty()); +} + +/// Binary tracks carry their mode and (optional) media type in the catalog. +#[tokio::test] +async fn binary_tracks_are_advertised_in_the_catalog() { + let broadcast = MoqBroadcastProducer::new().unwrap(); + let thumb = broadcast + .publish_binary_snapshot( + "thumbnail".into(), + MoqBinaryConfig { + compression: false, + mime: Some("image/jpeg".into()), + }, + ) + .unwrap(); + let log = broadcast + .publish_binary_stream( + "log".into(), + MoqBinaryConfig { + compression: false, + mime: None, + }, + ) + .unwrap(); + thumb.update(vec![0xff, 0xd8, 0xff]).unwrap(); + log.append(vec![1, 2, 3]).unwrap(); + + let catalog = published_catalog(&broadcast); + let entry = catalog + .binary + .tracks + .get("thumbnail") + .expect("binary snapshot advertised"); + assert_eq!(entry.mode, hang::catalog::Mode::Snapshot); + assert_eq!(entry.mime.as_deref(), Some("image/jpeg")); + let entry = catalog.binary.tracks.get("log").expect("binary stream advertised"); + assert_eq!(entry.mode, hang::catalog::Mode::Stream); + assert_eq!(entry.mime, None); + + thumb.finish().unwrap(); + assert!(matches!(thumb.update(vec![0]), Err(MoqError::Closed))); + log.finish().unwrap(); + assert!(published_catalog(&broadcast).binary.tracks.is_empty()); +} + +/// A second data track under a name the catalog already carries is refused, leaving the first. +#[tokio::test] +async fn data_track_names_cannot_collide() { + let broadcast = MoqBroadcastProducer::new().unwrap(); + let first = broadcast + .publish_json_snapshot( + "state".into(), + MoqJsonSnapshotConfig { + delta_ratio: 0, + compression: false, + }, + ) + .unwrap(); + assert!( + broadcast + .publish_binary_stream( + "state".into(), + MoqBinaryConfig { + compression: false, + mime: None, + }, + ) + .is_err(), + "a duplicate data track name should fail" + ); + assert_eq!( + published_catalog(&broadcast) + .json + .tracks + .get("state") + .map(|e| e.mode.clone()), + Some(hang::catalog::Mode::Snapshot) + ); + first.finish().unwrap(); +} diff --git a/rs/moq-mux/src/json.rs b/rs/moq-mux/src/json.rs index 26692fa745..f12e80b49c 100644 --- a/rs/moq-mux/src/json.rs +++ b/rs/moq-mux/src/json.rs @@ -148,6 +148,11 @@ impl Snapshot { self.inner.consume() } + /// A watch-only handle to whether this track has subscribers. + pub fn demand(&self) -> moq_net::track::Demand { + self.inner.demand() + } + /// Publish a new value, superseding the previous one. pub fn update(&mut self, value: &T) -> crate::Result<()> { Ok(self.inner.update(value)?) @@ -209,6 +214,11 @@ impl Stream { self.inner.consume() } + /// A watch-only handle to whether this track has subscribers. + pub fn demand(&self) -> moq_net::track::Demand { + self.inner.demand() + } + /// Append one record to the log. /// /// Any failure ends the track (see [`moq_json::stream::Producer::append`]) and retires the From 563faadfd14d2f75fe2f040bcad57c3f2f421235 Mon Sep 17 00:00:00 2001 From: Brad Greenway Date: Fri, 25 Sep 2026 07:41:59 -0500 Subject: [PATCH 2/3] docs(py): JSON tracks are advertised in the catalog; drop the set_catalog_section hint --- py/moq-rs/moq/publish.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/py/moq-rs/moq/publish.py b/py/moq-rs/moq/publish.py index 2a5929954a..cbaf29ba13 100644 --- a/py/moq-rs/moq/publish.py +++ b/py/moq-rs/moq/publish.py @@ -792,8 +792,8 @@ def publish_json_snapshot( ``delta_ratio`` controls how aggressively deltas are emitted instead of full snapshots (0 disables deltas); ``None`` uses the binding's default. Set ``compression`` to DEFLATE-compress each group; the consumer must pass the same - flag. Advertise the track with :meth:`set_catalog_section` if consumers should - discover it. + flag. The track is advertised in the broadcast's catalog (``json.tracks.``) + until it finishes; a name the catalog already carries is refused. """ # Let the record supply delta_ratio's default rather than restating it here. config = ( @@ -807,7 +807,9 @@ def publish_json_stream(self, name: str, *, compression: bool = False) -> JsonSt """Publish a JSON stream track (lossless append-log). Every appended record is preserved and delivered in order. Set ``compression`` to - DEFLATE-compress the group; the consumer must pass the same flag. + DEFLATE-compress the group; the consumer must pass the same flag. The track is + advertised in the broadcast's catalog (``json.tracks.``) until it finishes; a + name the catalog already carries is refused. """ config = MoqJsonStreamConfig(compression=compression) return JsonStreamProducer(self._inner.publish_json_stream(name, config)) From be8dcafb4ab96c216718c764cdfd6e8e9b58d2da Mon Sep 17 00:00:00 2001 From: Brad Greenway Date: Fri, 25 Sep 2026 08:14:18 -0500 Subject: [PATCH 3/3] chore(dart): regenerate bindings for the binary data track producers --- dart/moq_ffi/lib/src/moq.dart | 404 +++++++++++++++++++++++++++++++++- 1 file changed, 402 insertions(+), 2 deletions(-) diff --git a/dart/moq_ffi/lib/src/moq.dart b/dart/moq_ffi/lib/src/moq.dart index d4635f31c0..4ada2c2a1c 100644 --- a/dart/moq_ffi/lib/src/moq.dart +++ b/dart/moq_ffi/lib/src/moq.dart @@ -14,6 +14,65 @@ import "package:ffi/ffi.dart"; import "uniffi_runtime.dart"; export "uniffi_runtime.dart"; +class MoqBinaryConfig { + final bool compression; + final String? mime; + MoqBinaryConfig({this.compression = false, this.mime = null}); +} + +class FfiConverterMoqBinaryConfig { + static MoqBinaryConfig lift(RustBuffer buf) { + return FfiConverterMoqBinaryConfig.read(buf.asUint8List()).value; + } + + static LiftRetVal read(Uint8List buf) { + int new_offset = buf.offsetInBytes; + final compression_lifted = FfiConverterBool.read( + Uint8List.view(buf.buffer, new_offset), + ); + final compression = compression_lifted.value; + new_offset += compression_lifted.bytesRead; + final mime_lifted = FfiConverterOptionalString.read( + Uint8List.view(buf.buffer, new_offset), + ); + final mime = mime_lifted.value; + new_offset += mime_lifted.bytesRead; + return LiftRetVal( + MoqBinaryConfig(compression: compression, mime: mime), + new_offset - buf.offsetInBytes, + ); + } + + static RustBuffer lower(MoqBinaryConfig value) { + final total_length = + FfiConverterBool.allocationSize(value.compression) + + FfiConverterOptionalString.allocationSize(value.mime) + + 0; + final buf = Uint8List(total_length); + write(value, buf); + return toRustBuffer(buf); + } + + static int write(MoqBinaryConfig value, Uint8List buf) { + int new_offset = buf.offsetInBytes; + new_offset += FfiConverterBool.write( + value.compression, + Uint8List.view(buf.buffer, new_offset), + ); + new_offset += FfiConverterOptionalString.write( + value.mime, + Uint8List.view(buf.buffer, new_offset), + ); + return new_offset - buf.offsetInBytes; + } + + static int allocationSize(MoqBinaryConfig value) { + return FfiConverterBool.allocationSize(value.compression) + + FfiConverterOptionalString.allocationSize(value.mime) + + 0; + } +} + class MoqFetchGroupOptions { final int priority; MoqFetchGroupOptions({this.priority = 0}); @@ -4002,6 +4061,164 @@ class FfiConverterMoqReservation { } } +abstract class MoqBinarySnapshotProducerInterface { + void finish(); + void update({required Uint8List payload}); +} + +final _MoqBinarySnapshotProducerFinalizer = Finalizer>((ptr) { + rustCall( + (status) => uniffi_moq_ffi_fn_free_moqbinarysnapshotproducer(ptr, status), + ); +}); + +class MoqBinarySnapshotProducer implements MoqBinarySnapshotProducerInterface { + late final Pointer _ptr; + MoqBinarySnapshotProducer._(this._ptr) { + _MoqBinarySnapshotProducerFinalizer.attach(this, _ptr, detach: this); + } + factory MoqBinarySnapshotProducer.lift(Pointer ptr) { + return MoqBinarySnapshotProducer._(ptr); + } + Pointer uniffiClonePointer() { + return rustCall( + (status) => + uniffi_moq_ffi_fn_clone_moqbinarysnapshotproducer(_ptr, status), + ); + } + + void dispose() { + _MoqBinarySnapshotProducerFinalizer.detach(this); + rustCall( + (status) => + uniffi_moq_ffi_fn_free_moqbinarysnapshotproducer(_ptr, status), + ); + } + + void finish() { + return rustCall((status) { + uniffi_moq_ffi_fn_method_moqbinarysnapshotproducer_finish( + uniffiClonePointer(), + status, + ); + }, moqExceptionErrorHandler); + } + + void update({required Uint8List payload}) { + return rustCall((status) { + uniffi_moq_ffi_fn_method_moqbinarysnapshotproducer_update( + uniffiClonePointer(), + FfiConverterUint8List.lower(payload), + status, + ); + }, moqExceptionErrorHandler); + } +} + +class FfiConverterMoqBinarySnapshotProducer { + static MoqBinarySnapshotProducer lift(Pointer ptr) { + return MoqBinarySnapshotProducer.lift(ptr); + } + + static Pointer lower(MoqBinarySnapshotProducer value) { + return value.uniffiClonePointer(); + } + + static int allocationSize(MoqBinarySnapshotProducer value) { + return 8; + } + + static LiftRetVal read(Uint8List buf) { + final handle = buf.buffer.asByteData(buf.offsetInBytes).getInt64(0); + final pointer = Pointer.fromAddress(handle); + return LiftRetVal(MoqBinarySnapshotProducer.lift(pointer), 8); + } + + static int write(MoqBinarySnapshotProducer value, Uint8List buf) { + final handle = lower(value); + buf.buffer.asByteData(buf.offsetInBytes).setInt64(0, handle.address); + return 8; + } +} + +abstract class MoqBinaryStreamProducerInterface { + void append({required Uint8List payload}); + void finish(); +} + +final _MoqBinaryStreamProducerFinalizer = Finalizer>((ptr) { + rustCall( + (status) => uniffi_moq_ffi_fn_free_moqbinarystreamproducer(ptr, status), + ); +}); + +class MoqBinaryStreamProducer implements MoqBinaryStreamProducerInterface { + late final Pointer _ptr; + MoqBinaryStreamProducer._(this._ptr) { + _MoqBinaryStreamProducerFinalizer.attach(this, _ptr, detach: this); + } + factory MoqBinaryStreamProducer.lift(Pointer ptr) { + return MoqBinaryStreamProducer._(ptr); + } + Pointer uniffiClonePointer() { + return rustCall( + (status) => uniffi_moq_ffi_fn_clone_moqbinarystreamproducer(_ptr, status), + ); + } + + void dispose() { + _MoqBinaryStreamProducerFinalizer.detach(this); + rustCall( + (status) => uniffi_moq_ffi_fn_free_moqbinarystreamproducer(_ptr, status), + ); + } + + void append({required Uint8List payload}) { + return rustCall((status) { + uniffi_moq_ffi_fn_method_moqbinarystreamproducer_append( + uniffiClonePointer(), + FfiConverterUint8List.lower(payload), + status, + ); + }, moqExceptionErrorHandler); + } + + void finish() { + return rustCall((status) { + uniffi_moq_ffi_fn_method_moqbinarystreamproducer_finish( + uniffiClonePointer(), + status, + ); + }, moqExceptionErrorHandler); + } +} + +class FfiConverterMoqBinaryStreamProducer { + static MoqBinaryStreamProducer lift(Pointer ptr) { + return MoqBinaryStreamProducer.lift(ptr); + } + + static Pointer lower(MoqBinaryStreamProducer value) { + return value.uniffiClonePointer(); + } + + static int allocationSize(MoqBinaryStreamProducer value) { + return 8; + } + + static LiftRetVal read(Uint8List buf) { + final handle = buf.buffer.asByteData(buf.offsetInBytes).getInt64(0); + final pointer = Pointer.fromAddress(handle); + return LiftRetVal(MoqBinaryStreamProducer.lift(pointer), 8); + } + + static int write(MoqBinaryStreamProducer value, Uint8List buf) { + final handle = lower(value); + buf.buffer.asByteData(buf.offsetInBytes).setInt64(0, handle.address); + return 8; + } +} + abstract class MoqBroadcastConsumerInterface { Future fetchGroup({ required String name, @@ -5891,6 +6108,14 @@ class FfiConverterMoqBroadcastDynamic { } abstract class MoqBroadcastProducerInterface { + MoqBinarySnapshotProducer publishBinarySnapshot({ + required String name, + required MoqBinaryConfig config, + }); + MoqBinaryStreamProducer publishBinaryStream({ + required String name, + required MoqBinaryConfig config, + }); MoqJsonSnapshotProducer publishJsonSnapshot({ required String name, required MoqJsonSnapshotConfig config, @@ -5963,6 +6188,40 @@ class MoqBroadcastProducer implements MoqBroadcastProducerInterface { ); } + MoqBinarySnapshotProducer publishBinarySnapshot({ + required String name, + required MoqBinaryConfig config, + }) { + return rustCallWithLifter( + (status) => + uniffi_moq_ffi_fn_method_moqbroadcastproducer_publish_binary_snapshot( + uniffiClonePointer(), + FfiConverterString.lower(name), + FfiConverterMoqBinaryConfig.lower(config), + status, + ), + FfiConverterMoqBinarySnapshotProducer.lift, + moqExceptionErrorHandler, + ); + } + + MoqBinaryStreamProducer publishBinaryStream({ + required String name, + required MoqBinaryConfig config, + }) { + return rustCallWithLifter( + (status) => + uniffi_moq_ffi_fn_method_moqbroadcastproducer_publish_binary_stream( + uniffiClonePointer(), + FfiConverterString.lower(name), + FfiConverterMoqBinaryConfig.lower(config), + status, + ), + FfiConverterMoqBinaryStreamProducer.lift, + moqExceptionErrorHandler, + ); + } + MoqJsonSnapshotProducer publishJsonSnapshot({ required String name, required MoqJsonSnapshotConfig config, @@ -9318,6 +9577,72 @@ external void uniffi_moq_ffi_fn_method_moqreservation_update( Pointer uniffiStatus, ); +@Native Function(Pointer, Pointer)>( + assetId: _uniffiAssetId, +) +external Pointer uniffi_moq_ffi_fn_clone_moqbinarysnapshotproducer( + Pointer handle, + Pointer uniffiStatus, +); + +@Native, Pointer)>( + assetId: _uniffiAssetId, +) +external void uniffi_moq_ffi_fn_free_moqbinarysnapshotproducer( + Pointer handle, + Pointer uniffiStatus, +); + +@Native, Pointer)>( + assetId: _uniffiAssetId, +) +external void uniffi_moq_ffi_fn_method_moqbinarysnapshotproducer_finish( + Pointer ptr, + Pointer uniffiStatus, +); + +@Native, RustBuffer, Pointer)>( + assetId: _uniffiAssetId, +) +external void uniffi_moq_ffi_fn_method_moqbinarysnapshotproducer_update( + Pointer ptr, + RustBuffer payload, + Pointer uniffiStatus, +); + +@Native Function(Pointer, Pointer)>( + assetId: _uniffiAssetId, +) +external Pointer uniffi_moq_ffi_fn_clone_moqbinarystreamproducer( + Pointer handle, + Pointer uniffiStatus, +); + +@Native, Pointer)>( + assetId: _uniffiAssetId, +) +external void uniffi_moq_ffi_fn_free_moqbinarystreamproducer( + Pointer handle, + Pointer uniffiStatus, +); + +@Native, RustBuffer, Pointer)>( + assetId: _uniffiAssetId, +) +external void uniffi_moq_ffi_fn_method_moqbinarystreamproducer_append( + Pointer ptr, + RustBuffer payload, + Pointer uniffiStatus, +); + +@Native, Pointer)>( + assetId: _uniffiAssetId, +) +external void uniffi_moq_ffi_fn_method_moqbinarystreamproducer_finish( + Pointer ptr, + Pointer uniffiStatus, +); + @Native Function(Pointer, Pointer)>( assetId: _uniffiAssetId, ) @@ -10132,6 +10457,38 @@ external Pointer uniffi_moq_ffi_fn_constructor_moqbroadcastproducer_new( Pointer uniffiStatus, ); +@Native< + Pointer Function( + Pointer, + RustBuffer, + RustBuffer, + Pointer, + ) +>(assetId: _uniffiAssetId) +external Pointer +uniffi_moq_ffi_fn_method_moqbroadcastproducer_publish_binary_snapshot( + Pointer ptr, + RustBuffer name, + RustBuffer config, + Pointer uniffiStatus, +); + +@Native< + Pointer Function( + Pointer, + RustBuffer, + RustBuffer, + Pointer, + ) +>(assetId: _uniffiAssetId) +external Pointer +uniffi_moq_ffi_fn_method_moqbroadcastproducer_publish_binary_stream( + Pointer ptr, + RustBuffer name, + RustBuffer config, + Pointer uniffiStatus, +); + @Native< Pointer Function( Pointer, @@ -11620,6 +11977,18 @@ external int uniffi_moq_ffi_checksum_method_moqreservation_grant(); @Native(assetId: _uniffiAssetId) external int uniffi_moq_ffi_checksum_method_moqreservation_update(); +@Native(assetId: _uniffiAssetId) +external int uniffi_moq_ffi_checksum_method_moqbinarysnapshotproducer_finish(); + +@Native(assetId: _uniffiAssetId) +external int uniffi_moq_ffi_checksum_method_moqbinarysnapshotproducer_update(); + +@Native(assetId: _uniffiAssetId) +external int uniffi_moq_ffi_checksum_method_moqbinarystreamproducer_append(); + +@Native(assetId: _uniffiAssetId) +external int uniffi_moq_ffi_checksum_method_moqbinarystreamproducer_finish(); + @Native(assetId: _uniffiAssetId) external int uniffi_moq_ffi_checksum_method_moqbroadcastconsumer_fetch_group(); @@ -11814,6 +12183,14 @@ external int uniffi_moq_ffi_checksum_method_moqbroadcastdynamic_cancel(); external int uniffi_moq_ffi_checksum_method_moqbroadcastdynamic_requested_track(); +@Native(assetId: _uniffiAssetId) +external int +uniffi_moq_ffi_checksum_method_moqbroadcastproducer_publish_binary_snapshot(); + +@Native(assetId: _uniffiAssetId) +external int +uniffi_moq_ffi_checksum_method_moqbroadcastproducer_publish_binary_stream(); + @Native(assetId: _uniffiAssetId) external int uniffi_moq_ffi_checksum_method_moqbroadcastproducer_publish_json_snapshot(); @@ -12186,6 +12563,21 @@ void _checkApiChecksums() { if (uniffi_moq_ffi_checksum_method_moqreservation_update() != 9626) { throw UniffiInternalError.panicked("UniFFI API checksum mismatch"); } + if (uniffi_moq_ffi_checksum_method_moqbinarysnapshotproducer_finish() != + 10338) { + throw UniffiInternalError.panicked("UniFFI API checksum mismatch"); + } + if (uniffi_moq_ffi_checksum_method_moqbinarysnapshotproducer_update() != + 56077) { + throw UniffiInternalError.panicked("UniFFI API checksum mismatch"); + } + if (uniffi_moq_ffi_checksum_method_moqbinarystreamproducer_append() != 1645) { + throw UniffiInternalError.panicked("UniFFI API checksum mismatch"); + } + if (uniffi_moq_ffi_checksum_method_moqbinarystreamproducer_finish() != + 60630) { + throw UniffiInternalError.panicked("UniFFI API checksum mismatch"); + } if (uniffi_moq_ffi_checksum_method_moqbroadcastconsumer_fetch_group() != 18633) { throw UniffiInternalError.panicked("UniFFI API checksum mismatch"); @@ -12388,12 +12780,20 @@ void _checkApiChecksums() { 24118) { throw UniffiInternalError.panicked("UniFFI API checksum mismatch"); } + if (uniffi_moq_ffi_checksum_method_moqbroadcastproducer_publish_binary_snapshot() != + 6748) { + throw UniffiInternalError.panicked("UniFFI API checksum mismatch"); + } + if (uniffi_moq_ffi_checksum_method_moqbroadcastproducer_publish_binary_stream() != + 58418) { + throw UniffiInternalError.panicked("UniFFI API checksum mismatch"); + } if (uniffi_moq_ffi_checksum_method_moqbroadcastproducer_publish_json_snapshot() != - 51036) { + 64276) { throw UniffiInternalError.panicked("UniFFI API checksum mismatch"); } if (uniffi_moq_ffi_checksum_method_moqbroadcastproducer_publish_json_stream() != - 47317) { + 54975) { throw UniffiInternalError.panicked("UniFFI API checksum mismatch"); } if (uniffi_moq_ffi_checksum_method_moqbroadcastproducer_announce() != 13700) {