Skip to content

Merge main into dev#1950

Merged
kixelated merged 36 commits into
devfrom
claude/merge-main-dev-backports-1a1r3e
Jun 30, 2026
Merged

Merge main into dev#1950
kixelated merged 36 commits into
devfrom
claude/merge-main-dev-backports-1a1r3e

Conversation

@kixelated

Copy link
Copy Markdown
Collaborator

Summary

Merges main into dev. The hard part: several dev-only features had been backported to main against main's older moq-net API, so the same code exists on both sides in two API shapes. Throughout this merge, dev's newer API is the source of truth; main's genuine post-backport fixes are ported onto it, and main's old-API shapes are dropped.

Genuine main fixes ported (translated to dev's API)

  • moq-mux moq-mux: pre-existing codec/container correctness findings (from #1918 review) #1923 (codec/container correctness): AAC BitReader parser rewrite; H.264/H.265 IDR first-slice access-unit split; fMP4 MissingSampleDuration guard; container poll_aborted (distinguishes a relay eviction from a real decode error); producer backfills every batched sample's duration; MSF AAC SBR/PS trailing-byte acceptance; catalog Filter/Target end-with-upstream EOF fix.
  • moq-mux Fix fMP4 zero-duration samples #1933: fMP4 zero-duration handling across decode/import/export + infer_missing_durations, with the fallback expressed at the frames' own timescale (dev's Timestamp is multi-scale, unlike main's fixed-microsecond one).
  • moq-mux moq-mux: API cleanup before the semver bump #1941: opus::Config::encode() returns Result instead of panicking on multichannel (+ mkv::Error::Opus ripple).
  • moq-srt [codex] fix moq-srt negative pacing offsets #1922: negative-pacing offsets (a reordered B-frame paces before the anchor); the serve binary derives the HTTP sidecar bind from the real listener.
  • moq-rtmp: reject a publish cleanly when the broadcast path is unavailable (before acking the client); RTMPS flags gated behind noq/quinn.
  • moq-ffi: publish_media gains whole-buffer container support, re-expressed in dev's reserve_track/MediaDecoder shape.
  • moq-relay: main's feat(moq-native): unified client TLS verification + quiche backend support #1902 client-TLS verification (tls.rs) + HTTPS cert arrays ([codex] support relay HTTPS cert arrays #1932) + the web-embedding routes()/serve() test.
  • moq-gst: reject multichannel Opus.
  • moq-json / JS: adopt moq-flate (Rust) / @moq/flate (JS) extraction + main's compressed delta budgeting + drain-to-latest backlog collapse; net Group gains tryReadFrame/tryReadFrameSequence/readable/done; watch/publish gain the hangz (catalog.json.z) compressed catalog.
  • Dependency bumps from main's cargo/bun groups (e.g. tower-http 0.7).

Clean-but-broken auto-merges fixed (a naive merge would miss these)

Decisions deferred as owed follow-ups (documented in the merge commit)

Test plan

  • cargo build + cargo test for every crate touched by the merge: moq-mux (308), moq-json (50), moq-flate (7), hang (16), plus moq-net/moq-srt/moq-relay/moq-rtmp/moq-rtc/moq-native compiling. All green.
  • JS: tsc --noEmit + Biome clean; bun test for net (194), json (43), flate (9), msf (7), watch (78), publish (5), hang (34). All green.
  • cargo fmt clean; no conflict markers anywhere.
  • Full-workspace CI build (the sandbox can't fetch the nvidia-video-codec-sdk git dep behind its egress policy, nor build the GStreamer system dep, so moq-video/moq-boy/moq-cli/libmoq/moq-gst were validated by compile-of-touched-code only).

🤖 Generated with Claude Code

https://claude.ai/code/session_01R3bVtLrzn4Q82tChYu9EJZ


Generated by Claude Code

dependabot Bot and others added 30 commits June 24, 2026 11:59
…#1903)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…pport (#1902)

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…#1905)

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…nt-tls-* flags consistent (#1901)

Co-authored-by: Claude <noreply@anthropic.com>
…ds (#1908)

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…enchmark) (#1912)

Co-authored-by: Claude <noreply@anthropic.com>
…r::request_broadcast (#1913)

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
#1914)

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…xport) (#1915)

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Codex <codex@openai.com>
…ks) (#1918)

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Luke Curley <luke.curley@discordapp.com>
…1858)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…updates (#1832)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
) (#1925)

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: OpenAI Codex <codex@openai.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…1942)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Luke Curley <kixelated@gmail.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Replace the consume-side-only catalog::Filter with one selection type that reads
the same at both ends of the pipeline.

- New moq_mux::select module: Broadcast/Video/Audio fluent builders (default
  selects nothing; opt a role in, narrow by name/codec, empty field = any).
- catalog::Select replaces Filter/FilterVideo/FilterAudio; Stream::filter() ->
  Stream::select(selection). The adapter is immutable, dropping the old reactive
  set-mid-stream machinery.
- fmp4::Import::with_select restricts which roles are published (new() unchanged,
  so existing callers still import everything).
- moq-hls uses it on both ends: per-rendition export selects only its own axis,
  and import publishes master-playlist variants video-only beside a separate
  audio rendition (the track-selection half of #1940).

Breaking moq-mux change, targeting main during the pre-semver-bump window. The
discontinuity-sequence change from #1940 is intentionally left for a separate PR.
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
kixelated and others added 4 commits June 29, 2026 22:42
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Careful merge: several dev-only features had been backported to main against
main's older moq-net API, so the same code exists on both sides in two API
shapes. Throughout, dev's newer API is the source of truth; main's genuine
post-backport fixes are ported onto it, and main's old-API shapes are dropped.

Genuine main fixes ported (translated to dev's API):
- moq-mux #1923: AAC BitReader parser rewrite; H.264/H.265 IDR first-slice AU
  split; fMP4 MissingSampleDuration guard; container poll_aborted (tell a relay
  eviction from a real decode error); producer backfills every batched sample's
  duration; MSF AAC SBR/PS trailing-bytes acceptance.
- moq-mux #1933: fMP4 zero-duration handling (decode/import/export +
  infer_missing_durations), with the fallback expressed at the frames' own
  timescale (dev's Timestamp is multi-scale).
- moq-mux #1941: opus encode() returns Result instead of panicking on
  multichannel (+ mkv::Error::Opus ripple).
- moq-mux #1923 catalog Filter/Target: end-with-upstream EOF fix.
- moq-srt #1922: negative-pacing offsets (reordered B-frames pace before the
  anchor); bin derives the HTTP sidecar bind from the real listener.
- moq-rtmp: reject a publish cleanly when the broadcast path is unavailable
  (before acking the client); RTMPS flags gated behind noq/quinn.
- moq-ffi: publish_media gains container support (whole-buffer path), re-expressed
  in dev's reserve_track/MediaDecoder shape.
- moq-relay: main's #1902 client-TLS verification (tls.rs) + HTTPS cert arrays
  (#1932) + the web-embedding routes()/serve() test.
- moq-gst: reject multichannel Opus.
- Dependency bumps from main's cargo/bun groups (tower-http 0.7, etc.);
  moq-flate crate added as a workspace member; moq-json adopts moq-flate +
  main's compressed delta budgeting.

kio: dev (#1779) and main (#1913) both added an identical Consumer::write; kept
one. dev already had OriginProducer::dynamic and the untyped catalog sections
(#1886 dev port), so main's re-introductions were dropped as duplicates.

Public-API / scope decisions (deferred as owed follow-ups, documented here):
- moq-mux select::Broadcast reshape (68c9ebd/#1944): NOT adopted. main's reshape
  drops dev's Target (resolution/bitrate ABR) capability that moq-cli depends on,
  so dev's Filter+Target is kept. Re-port the import-side with_select ergonomics
  later if wanted.
- moq-rtc #1931 session runner: NOT ported. dev's moq-rtc has diverged well past
  main (whip/whep accept, shared-UDP mux, production hardening); the #1931 API
  reshape should be re-applied against dev's shape in a follow-up.
- moq-hls #1945 (discontinuity sequence through fMP4): deferred. dev's hls import
  predates main's select-coupled shape; re-port the core moq_sequence/discontinuity
  logic against dev's importer later.
- Rust compressed-catalog (#1904, catalog.json.z / HangZ): deferred on the Rust
  side (the JS @moq/flate + hangz path is kept). hang::Catalog::compressed_track
  and the CatalogFormat::HangZ consumer wiring are owed.
- moq-relay #1901 (reuse client TLS for outbound auth HTTP): deferred. tls.rs
  carries #1902, but auth.init keeps dev's 0-arg shape.
- moq-json telemetry example removed (main-only, written against main's net API);
  re-add against dev's API later.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R3bVtLrzn4Q82tChYu9EJZ
The moq-cli `hls import` subcommand (main's #1939) auto-merged into the merge
using main's old API: `moq_net::Broadcast::new()` and a bool-returning
`publish_broadcast` wrapped in `ensure!`. On dev, `Broadcast` is `BroadcastInfo`
and `publish_broadcast` returns `Result<OriginPublish>`, so hold the guard via
`?` (matching client.rs/server.rs). Also use the canonical `with_publisher`/
`with_subscriber` names instead of the deprecated `with_publish`/`with_consume`
aliases. CI caught this; the crate isn't locally buildable here because its
optional moq-video dep forces resolving a network-blocked git dependency.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R3bVtLrzn4Q82tChYu9EJZ

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @kixelated, your pull request is larger than the review limit of 150000 diff characters

The merged `hls export` subcommand (main's #1939) uses `moq_hls::Server`, which
is gated behind moq-hls's `server` feature. The workspace moq-hls dep is
`default-features = false`, so the dev-side `moq-hls = { workspace = true }`
left it off. Enable `features = ["server"]` to match main. dev's moq-hls API
(`export::Config{part_target,window}`, `Server::new(OriginConsumer, Config)`,
`router()`) already matches the call site.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R3bVtLrzn4Q82tChYu9EJZ

Copy link
Copy Markdown
Collaborator Author

CI status

Check is green (full cargo build + clippy + tests across the workspace, including libmoq/moq-relay). Two follow-up commits fixed moq-cli (moq-ffi/moq-cli aren't in the default cargo workspace, so the merge commit's hls import/export API mismatches only surfaced in the Smoke/nix build):

  • 747f242 — adapt main's hls import to dev's moq-net API (BroadcastInfo, Result-returning publish_broadcast).
  • e287eab — enable moq-hls's server feature for hls export.

Smoke is red, but it's pre-existing dev debt, not a merge regression. The cross-language interop matrix is green on the diagonal (rust→rust, rust→gst); every cross-client subscriber fails because the smoke clients under test/smoke/clients/ call APIs that dev's own bindings already dropped:

  • C (subscribe.c): moq_origin_consume (sync lookup) was replaced on dev by the async moq_origin_consume_announced callback.
  • Python (smoke.py): subscribe_catalog() is now an awaitable returning a CatalogConsumer, not an async-iterable.
  • js-native / browser (subscribe.ts): Catalog.fetch was removed from @moq/hang.

These clients track main's API (which is why nightly Smoke on main is green), and origin/dev carries the same stale clients — git diff origin/dev HEAD -- test/smoke/ is empty apart from one package.json line. Pure dev would fail identically; it has just never been exercised because the Smoke job only runs on PRs that touch test/smoke/** (this PR triggered it via a package.json dependency bump). The matrix is all-or-nothing, so it can't go green until all three client surfaces are updated to dev's API — which is a separate Cross-Package-Sync task, not part of resolving the merge.

Recommend tracking the test/smoke/clients/{c,python,js-native} update to dev's API as a follow-up. Happy to do it here instead if preferred.

🤖 Generated with Claude Code


Generated by Claude Code

@kixelated kixelated enabled auto-merge (squash) June 30, 2026 02:44
@kixelated kixelated merged commit e93604f into dev Jun 30, 2026
2 of 3 checks passed
@kixelated kixelated deleted the claude/merge-main-dev-backports-1a1r3e branch June 30, 2026 02:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants