Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions dart/moq_ffi/lib/src/moq.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7022,7 +7022,7 @@ class LocMoqContainer extends MoqContainer {
}
}

enum MoqContainerFormat { fmp4, mkv, ts, flv, cmaf }
enum MoqContainerFormat { fmp4, mkv, ts, flv }
Comment thread
kixelated marked this conversation as resolved.

class FfiConverterMoqContainerFormat {
static LiftRetVal<MoqContainerFormat> read(Uint8List buf) {
Expand All @@ -7036,8 +7036,6 @@ class FfiConverterMoqContainerFormat {
return LiftRetVal(MoqContainerFormat.ts, 4);
case 4:
return LiftRetVal(MoqContainerFormat.flv, 4);
case 5:
return LiftRetVal(MoqContainerFormat.cmaf, 4);
default:
throw UniffiInternalError(
UniffiInternalError.unexpectedEnumCase,
Expand Down
2 changes: 1 addition & 1 deletion doc/lib/c/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ inside. The header is the reference: each function carries a doc comment.
- **Threading.** Call any function from any thread. Raw publish calls block until the codec takes the frame, which paces a publisher.
- **Zeroed config is the default.** A zeroed `moq_client_config` means the default for every field, so new fields don't break callers. A field whose default isn't zero has a `has_*` flag: `backoff_timeout_us = 0` means "retry forever" only with `has_backoff_timeout = true`.
- **Compressed tracks.** `moq_track_info.compress` on a raw track sends each group DEFLATE-compressed to opt-in `moq-lite-07-wip` peers; other peers, and every reader here, get the original frames. `@moq/net` cannot read a compressed track yet.
- **fMP4 import.** `MOQ_CONTAINER_FORMAT_FMP4` splits each fragment into one frame per sample; `MOQ_CONTAINER_FORMAT_CMAF` republishes it whole.
- **fMP4 import.** `MOQ_CONTAINER_FORMAT_FMP4` splits each fragment into one frame per sample.
- **Audio needs cuts.** Video groups at its keyframes, but audio forms a group only where you call `moq_publish_media_cut`: after every frame, or at a segment cadence to align with video.
- **Ending cleanly.** When the input ends, finish every track, then call `moq_publish_finish()`. It consumes the broadcast and returns a finish handle that keeps it announced and serving the finished tracks, writing the linger, their longest max age, to `linger_us`. Keep the handle for that long on your own clock so a subscriber still catching up can drain them, then `moq_publish_finish_close()` it; closing early is the interrupt. `moq_publish_close()` stays the abrupt end.
- **Timeline breaks.** On a seek or pause, call `moq_publish_media_discontinuity`, then keep timestamps moving forward and resume video on a keyframe.
Expand Down
2 changes: 1 addition & 1 deletion doc/lib/cpp/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ Share `moq::MediaTimebase::init(broadcast)` through `ContainerInit` when several
use one source timeline; omitting it keeps an independent offset. A timebase belongs to
its broadcast and does not keep it open. `MediaContainerProducer` takes chunks split
anywhere, so a raw byte stream needs no other importer.
`moq::ContainerFormat::kFmp4` splits each fragment into one frame per sample; `kCmaf` republishes it whole.
`moq::ContainerFormat::kFmp4` splits each fragment into one frame per sample.

```cpp
// Publish encoded frames, or raw pixels with the codec inside the binding.
Expand Down
2 changes: 1 addition & 1 deletion doc/lib/dart/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ Share `media.Timebase(broadcast: ...)` through `ContainerInit` when several impo
one source timeline; omitting it keeps an independent offset. A timebase belongs to its
broadcast and does not keep it open. `ContainerProducer` takes chunks split anywhere,
so a raw byte stream needs no other importer.
`ContainerFormat.fmp4` splits each fragment into one frame per sample; `ContainerFormat.cmaf` republishes it whole.
`ContainerFormat.fmp4` splits each fragment into one frame per sample.
`media.CatalogConsumer.subscribe`, `media.TrackConsumer.subscribe`, and `media.GroupConsumer.fetch`
construct the read side from a broadcast; the last two take a catalog `media.Rendition` and
follow its broadcast and track themselves. For arrival-order reads, decode each group from
Expand Down
2 changes: 1 addition & 1 deletion doc/lib/go/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ Share `media.NewTimebase(broadcast)` through `ContainerInit` when several import
source timeline; omitting it keeps an independent offset. Timebases belong to one broadcast
and do not keep it open; release the handle with `Close` on the calling goroutine.
`NewContainerProducer` takes chunks split anywhere, so a raw byte stream needs no other importer.
`ContainerFormatFmp4` splits each fragment into one frame per sample; `ContainerFormatCmaf` republishes it whole.
`ContainerFormatFmp4` splits each fragment into one frame per sample.
For arrival-order reads, decode each group from `moq.TrackConsumer.RecvGroup` with
`media.NewGroupConsumer`; frames carry container media time as they land. `media.FetchGroup`
fetches and decodes one past group.
Expand Down
2 changes: 1 addition & 1 deletion doc/lib/kt/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ Share `Timebase(broadcast)` through `ContainerInit` when several imports use one
timeline; omitting it keeps an independent offset. A timebase belongs to its broadcast
and does not keep it open. `ContainerProducer` takes chunks split anywhere, so a raw
byte stream needs no other importer.
`ContainerFormat.FMP4` splits each fragment into one frame per sample; `ContainerFormat.CMAF` republishes it whole.
`ContainerFormat.FMP4` splits each fragment into one frame per sample.
Construct readers with `CatalogConsumer.subscribe`, `TrackConsumer.subscribe`, or
`GroupConsumer.fetch`; the last two take a catalog `Rendition` and follow its broadcast and
track themselves. For arrival-order reads, decode each group from
Expand Down
2 changes: 1 addition & 1 deletion doc/lib/py/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Share `media.Timebase(broadcast)` through `ContainerInit` when several imports u
source timeline; omitting it keeps an independent offset. A timebase belongs to its
broadcast and does not keep it open. `ContainerProducer` takes chunks split anywhere,
so a raw byte stream needs no other importer.
`ContainerFormat.FMP4` splits each fragment into one frame per sample; `ContainerFormat.CMAF` republishes it whole.
`ContainerFormat.FMP4` splits each fragment into one frame per sample.
`MediaFrame.timestamp`, media writes, and `flush` use `datetime.timedelta`.
A missing data timestamp stays untimed; the library never substitutes zero or now.
JSON consumers yield `moq.json.Timed(value, timestamp)`; a snapshot consumer yields each state in order,
Expand Down
2 changes: 1 addition & 1 deletion doc/lib/swift/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ Share `Media.Timebase(broadcast:)` through `ContainerInit` when several imports
source timeline; omitting it keeps an independent offset. A timebase belongs to its
broadcast and does not keep it open. `ContainerProducer` takes chunks split anywhere,
so a raw byte stream needs no other importer.
`.fmp4` splits each fragment into one frame per sample; `.cmaf` republishes it whole.
`.fmp4` splits each fragment into one frame per sample.
For arrival-order reads, decode each group from `TrackConsumer.recvGroup()` with
`Media.GroupConsumer(group:container:)`; frames carry container media time as they land.

Expand Down
2 changes: 0 additions & 2 deletions go/wrapper/media/media.go
Original file line number Diff line number Diff line change
Expand Up @@ -330,8 +330,6 @@ const (
const (
// ContainerFormatFmp4 is fragmented MP4 / CMAF, split into one frame per sample.
ContainerFormatFmp4 = ffi.MoqContainerFormatFmp4
// ContainerFormatCmaf is fragmented MP4 / CMAF, republished whole as one CMAF frame per fragment.
ContainerFormatCmaf = ffi.MoqContainerFormatCmaf
// ContainerFormatMkv is Matroska / WebM.
ContainerFormatMkv = ffi.MoqContainerFormatMkv
// ContainerFormatTs is an MPEG-2 transport stream.
Expand Down
1 change: 0 additions & 1 deletion quest/m1/api-audit/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,6 @@ Rejected in planning:

- [Two broadcast resolutions](/quest/m1/api-audit/resolve.md) - Rust, JS, and the bindings resolve a broadcast with `request(path, epoch)` or `follow(path, epoch)`, and every racy two-step caller is gone
- [The origin announces](/quest/m1/api-audit/announce-owner.md) - a broadcast producer cannot announce; the origin hands out the announcement, so `Prepared` and standalone broadcasts lose methods that always fail
- [Container formats name inputs](/quest/m1/api-audit/container-format.md) - `ContainerFormat` names what is parsed, never how it is packaged; `ContainerStream` and TS passthrough internals are gone
- [moq-net exports with a consumer](/quest/m1/api-audit/net-exports.md) - the dead and test-only moq-net, `@moq/net`, and moq-ffi items the follow-up audit found go private or away
- [Bindings read gaps and ends](/quest/m1/api-audit/decode-read-bindings.md) - moq-ffi and every wrapper surface `Gap` and `End` instead of discarding them
- [Media surface trim](/quest/m1/api-audit/media-trim.md) - media crate items with no consumer go private, and Rust/JS media names agree
Expand Down
42 changes: 0 additions & 42 deletions quest/m1/api-audit/container-format.md

This file was deleted.

3 changes: 0 additions & 3 deletions rs/moq-c/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,8 +155,6 @@ pub enum moq_container_format {
MOQ_CONTAINER_FORMAT_TS = 2,
/// Flash Video, as used by RTMP.
MOQ_CONTAINER_FORMAT_FLV = 3,
/// Fragmented MP4 / CMAF, republished whole as one CMAF frame per fragment.
MOQ_CONTAINER_FORMAT_CMAF = 4,
}

/// Configuration for [moq_publish_audio].
Expand Down Expand Up @@ -309,7 +307,6 @@ fn container_format_from_u32(value: u32) -> Result<moq_mux::import::ContainerFor
v if v == moq_container_format::MOQ_CONTAINER_FORMAT_MKV as u32 => ContainerFormat::Mkv,
v if v == moq_container_format::MOQ_CONTAINER_FORMAT_TS as u32 => ContainerFormat::Ts,
v if v == moq_container_format::MOQ_CONTAINER_FORMAT_FLV as u32 => ContainerFormat::Flv,
v if v == moq_container_format::MOQ_CONTAINER_FORMAT_CMAF as u32 => ContainerFormat::Cmaf,
_ => return Err(Error::InvalidCode),
})
}
Expand Down
5 changes: 3 additions & 2 deletions rs/moq-c/src/publish.rs
Original file line number Diff line number Diff line change
Expand Up @@ -452,8 +452,9 @@ impl Publish {
pub fn container_cut(&mut self, container: Id) -> Result<(), Error> {
let container = self.containers.get_mut(container).ok_or(Error::MediaNotFound)?;
container.driver.check()?;
container.inner.cut();
container.driver.check()
let result = container.inner.cut();
container.driver.check()?;
Ok(result?)
}

/// Start a new segment and number its groups `sequence`.
Expand Down
7 changes: 3 additions & 4 deletions rs/moq-ffi/src/media.rs
Original file line number Diff line number Diff line change
Expand Up @@ -296,8 +296,6 @@ pub enum MoqContainerFormat {
Ts,
/// Flash Video, as used by RTMP.
Flv,
/// Fragmented MP4 / CMAF, republished whole as one CMAF frame per fragment.
Cmaf,
}
Comment thread
kixelated marked this conversation as resolved.

/// What an audio publish needs: a format, its codec init bytes, and an optional label.
Expand Down Expand Up @@ -477,7 +475,6 @@ impl From<MoqContainerFormat> for moq_mux::import::ContainerFormat {
fn from(format: MoqContainerFormat) -> Self {
match format {
MoqContainerFormat::Fmp4 => Self::Fmp4,
MoqContainerFormat::Cmaf => Self::Cmaf,
MoqContainerFormat::Mkv => Self::Mkv,
MoqContainerFormat::Ts => Self::Ts,
MoqContainerFormat::Flv => Self::Flv,
Expand Down Expand Up @@ -1076,7 +1073,9 @@ impl MoqMediaContainerProducer {
let _guard = crate::ffi::enter();
let mut guard = self.inner.lock().unwrap();
let container = guard.as_mut().ok_or(MoqError::Closed)?;
container.import.cut();
container.import.cut().map_err(|err| MoqError::Codec {
reason: format!("cut failed: {err}"),
})?;
Ok(())
})();
self.driver.check()?;
Expand Down
4 changes: 2 additions & 2 deletions rs/moq-hls/src/import.rs
Original file line number Diff line number Diff line change
Expand Up @@ -640,15 +640,15 @@ impl TrackState {
.next_discontinuity
.is_some_and(|previous| previous != discontinuity_sequence)
{
importer.discontinuity();
importer.discontinuity()?;
}
if reanchored {
importer.seek(group_sequence)?;
}
// Every playlist entry is a source segment boundary, so declare it even when the media
// carries no styp. Every rendition declares the same ones; the timeline drops a cut
// that would land inside its minimum segment duration, so the duplicates cost nothing.
importer.cut();
importer.cut()?;
importer.decode(&bytes).map_err(|err| match err {
// Timestamp resets are unsupported: the published tracks never rewind, and no later
// pass can import this segment, so it is refused rather than retried.
Expand Down
6 changes: 4 additions & 2 deletions rs/moq-mux/src/container/fmp4/import.rs
Original file line number Diff line number Diff line change
Expand Up @@ -262,8 +262,9 @@ impl<E: crate::catalog::hang::CatalogExt> Import<E> {
/// This is where audio groups are drawn too: audio rolls its group at a segment boundary, so
/// it follows the same segmentation as video. A passthrough video track rolls there as well,
/// while an unwrapped one opens a group at each sync sample.
pub fn cut(&mut self) {
pub fn cut(&mut self) -> Result<()> {
self.pending_cut = true;
Ok(())
}

/// Restrict which track roles are published.
Expand Down Expand Up @@ -1389,13 +1390,14 @@ impl<E: crate::catalog::hang::CatalogExt> Import<E> {
/// restarts below where it was still fails with
/// [`TimestampRewind`](crate::container::TimestampRewind): a track refuses a group that
/// rewinds, before publishing any of it.
pub fn discontinuity(&mut self) {
pub fn discontinuity(&mut self) -> Result<()> {
for track in self.text.values_mut() {
track.end_decode_time = None;
track.discontinuity = true;
}
for track in self.tracks.values_mut() {
track.last_decode_time = None;
}
Ok(())
}
}
2 changes: 1 addition & 1 deletion rs/moq-mux/src/container/fmp4/import_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2212,7 +2212,7 @@ async fn seek_keeps_the_decode_time_until_a_discontinuity_in(passthrough: bool)
"{err:?}"
);

fmp4.discontinuity();
fmp4.discontinuity().unwrap();
fmp4.seek(200).unwrap();
let err = fmp4.decode(&live_session(0, 5)).unwrap_err();
// Passthrough writes its groups by hand, so it refuses the rewound group itself, the way the
Expand Down
2 changes: 1 addition & 1 deletion rs/moq-mux/src/container/ts/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ mod follower;
mod health;
mod import;
mod mux_rate;
mod passthrough;
mod programs;
mod psi;
mod release;
Expand All @@ -50,7 +51,6 @@ pub use passthrough::Passthrough;
pub use programs::Programs;
pub use release::Release;

pub mod passthrough;
pub mod stats;

#[cfg(test)]
Expand Down
Loading
Loading