Skip to content

audio: send each frame as its own group#1414

Merged
kixelated merged 3 commits into
mainfrom
claude/revert-audio-frames-SqkSM
May 18, 2026
Merged

audio: send each frame as its own group#1414
kixelated merged 3 commits into
mainfrom
claude/revert-audio-frames-SqkSM

Conversation

@kixelated
Copy link
Copy Markdown
Collaborator

Audio frames were being packed into ~100ms groups for relay efficiency.
For real-time use cases, that bounds end-to-end latency at the group
boundary since the relay cannot forward a group until it is closed.

Go back to one-frame-per-group: each frame is flushed to the relay
immediately, and the codec's packet loss concealment (Opus PLC, AAC PLC)
handles individual frame drops.

Applies to the browser publish encoder and the Rust opus/aac mux
importers, which all had the same 100ms grouping pattern.

Audio frames were being packed into ~100ms groups for relay efficiency.
For real-time use cases, that bounds end-to-end latency at the group
boundary since the relay cannot forward a group until it is closed.

Go back to one-frame-per-group: each frame is flushed to the relay
immediately, and the codec's packet loss concealment (Opus PLC, AAC PLC)
handles individual frame drops.

Applies to the browser publish encoder and the Rust opus/aac mux
importers, which all had the same 100ms grouping pattern.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 16, 2026

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 60b7a07a-258c-445d-ae9e-597421bab84f

📥 Commits

Reviewing files that changed from the base of the PR and between 6ce226e and 636c976.

📒 Files selected for processing (2)
  • js/hang/src/container/legacy.ts
  • js/publish/src/audio/encoder.ts

Walkthrough

This PR refactors audio codec implementations across JavaScript and Rust to use per-frame keyframe grouping instead of time-based or frame-count-based batching. The JavaScript Encoder removes its groupDuration configuration and now marks every audio frame as a keyframe. The AAC and Opus importers both remove their GROUP_FRAMES constants and frames counters, switching from buffering multiple frames to emitting each frame as an independent keyframe group that is finalized immediately.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'audio: send each frame as its own group' directly and clearly summarizes the main change across all modified files: removing ~100ms grouping logic and instead treating each audio frame as its own individual group for immediate relay forwarding.
Description check ✅ Passed The description is directly related to the changeset, explaining the motivation (real-time latency bounds) and the implementation approach (one-frame-per-group with codec PLC fallback) that reflects the changes made to encoder, opus, and aac modules.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/revert-audio-frames-SqkSM
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch claude/revert-audio-frames-SqkSM

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint skipped: no ESLint configuration detected in root package.json. To enable, add eslint to devDependencies.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@js/publish/src/audio/encoder.ts`:
- Around line 169-171: The current call to producer.encode(frame,
frame.timestamp as Time.Micro, true) causes each frame to remain buffered until
the next keyframe because Container.Legacy.Producer.encode() finalizes the
previous group on a new keyframe; to fix the one-frame latency, after writing
the frame (where producer.encode or producer.writeFrame is invoked) explicitly
close the current group by calling the producer's group close method (e.g., call
producer.#group?.close() or producer.group.close() immediately after
writeFrame/encode) so each frame's group is finalized right away; alternatively,
if you prefer to keep buffering, update the comment near the producer.encode
call to state that frames are buffered until the next keyframe rather than being
forwarded immediately.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2acbe249-3f3e-4281-91a1-e07df3e32167

📥 Commits

Reviewing files that changed from the base of the PR and between 303b25c and 4c42cbb.

📒 Files selected for processing (3)
  • js/publish/src/audio/encoder.ts
  • rs/moq-mux/src/import/aac.rs
  • rs/moq-mux/src/import/opus.rs

Comment thread js/publish/src/audio/encoder.ts Outdated
claude added 2 commits May 17, 2026 22:46
The previous commit marked every audio frame as a keyframe but the
Legacy producer only closes the prior group when it sees the next
keyframe. That left each frame's group open until the following frame
arrived, adding one frame interval of latency.

Add a finishGroup() method on Legacy.Producer (mirroring the Rust
container Producer's finish_group) and call it from the audio encoder
after each frame so the relay can forward it without delay.
Producer's keyframe/group bookkeeping is only useful for video GoPs.
For audio, each frame is its own group, so use Moq.Track.writeFrame
directly (which appends a group, writes the frame, and closes it).

Expose Legacy.encodeFrame as a standalone function for callers that
only need the timestamp+payload encoding without the Producer state
machine. Producer keeps using it internally.
@kixelated kixelated merged commit 35134a2 into main May 18, 2026
1 check passed
@kixelated kixelated deleted the claude/revert-audio-frames-SqkSM branch May 18, 2026 01:52
This was referenced May 18, 2026
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