Skip to content

Conversation

@kixelated
Copy link
Collaborator

@kixelated kixelated commented Jan 22, 2026

Replaces #822

Overview:

  1. Added support for --passthrough (Rust only) when using HLS and fMP4 import to write the raw MP4 bytes.
  2. MSE is a separate Backend, so it will be tree-shaken when not used.
    • The same but reverse for WebCodecs. No need for a fMP4 encoder.
  3. The <hang-watch> element takes either a <video> or <canvas> tag to switch between backends.
    • Won't tree-shake, use the JS API if you want to reduce the bundle size.
  4. Watch.Broadcast no longer owns the video/audio component; they have to be constructed separately.
    • Allows tree-shaking Watch.Audio.Source and Watch.Video.Source.
    • Opens the door for more tree-shakable modules that take a Broadcast.
  5. Removed support for the unused raw container.
    • In hind-sight, a new container format should use delta encoded timestamps anyway.
    • Additionally, timestamps are being added to the moq-lite layer that we could reuse.
    • Sorry @gdavila, I didn't think enough before filing that issue.
  6. The "mse" container contains an init_track, used to initialize the decoder.
    • This is inlined in the CMSF draft, but I plan to use it via a separate track for HLS fallback.
    • It's more efficient this way when renditions share an init segment.
  7. A bunch of API changes to support the generic Backend interface.
    • ex. watch.audio.muted instead of watch.muted.

TODO:

  • WebCodecs support for CMAF (decode MP4)
  • MSE support for hang (encode MP4)
  • Fix fMP4 import --passthrough not working with bbb
  • Fix the buffering signal
  • Fix the stats panel
  • Actually review the HLS import changes
  • Send jitter in the catalog so we can increase the latency on demand?
  • Remove init_track and build it in memory instead?

@kixelated kixelated requested a review from jdreetz January 22, 2026 22:23
@kixelated
Copy link
Collaborator Author

Some of this has been merged as #867

Copy link
Collaborator

@jdreetz jdreetz left a comment

Choose a reason for hiding this comment

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

Been over this a few times (didn't look to close at the Rust code though). Seems fine to me. Just some nits about naming and comments, but overall seems fine. Haven't tested locally.

Comment on lines +310 to +333
function decodeVarInt(buf: Uint8Array): [number, Uint8Array] {
const size = 1 << ((buf[0] & 0xc0) >> 6);

const view = new DataView(buf.buffer, buf.byteOffset, size);
const remain = new Uint8Array(buf.buffer, buf.byteOffset + size, buf.byteLength - size);
let v: number;

if (size === 1) {
v = buf[0] & 0x3f;
} else if (size === 2) {
v = view.getUint16(0) & 0x3fff;
} else if (size === 4) {
v = view.getUint32(0) & 0x3fffffff;
} else if (size === 8) {
// NOTE: Precision loss above 2^52
v = Number(view.getBigUint64(0) & 0x3fffffffffffffffn);
} else {
throw new Error("impossible");
}

return [v, remain];
}

function encodeVarInt(v: number): Uint8Array {
Copy link
Collaborator

Choose a reason for hiding this comment

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

should we have tests for any of this stuff?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

IMO We should export this from @moq/lite and yeah add some tests.

NOTE: `reload` will detect when the broadcast goes offline/online and automatically reconnect.
TODO: Cloudflare doesn't support it yet (SUBSCRIBE_NAMESPACE), so make sure you remove it if you're using Cloudflare.

NOTE: You can use a <video> element instead of a <canvas> element to use the MSE backend.
Copy link
Collaborator

Choose a reason for hiding this comment

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

I would put this comment above the video tag.

throw new Error("Missing required fields to create video init segment");
}

const timescale = 1_000_000; // microseconds
Copy link
Collaborator

Choose a reason for hiding this comment

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

should we use the Time.Micro type here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Also if you have any opinions on the timer helpers. It's a little annoying how much casting is required because the math operations convert them back to numbers.

Copy link
Collaborator

Choose a reason for hiding this comment

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

It's a little annoying how much casting is required because the math operations convert them back to numbers

I've used alias types for stuff like this too, e.g. type Microseconds = numbers. You don't have to cast everywhere (I think) but you get the benefit of additional readability.

};

// Build codec-specific sample entry
const sampleEntry = createAudioSampleEntry(codec, sampleRate, numberOfChannels, description);
Copy link
Collaborator

Choose a reason for hiding this comment

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

can you explain the point of a sample entry maybe?

} else {
this.syncStatus.set({ state: "ready" });

if (sleep <= BUFFERING_MS) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

could we add a comment about why this is relevant?

Comment on lines +36 to +38
if (isBuffering) return "#f87171"; // red
if (index > 0) return "#facc15"; // yellow
return "#4ade80"; // green
Copy link
Collaborator

Choose a reason for hiding this comment

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

it'd be good to use css variables for this eventually, keep all that stuff in one place, make it easy to theme.

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.

4 participants