Skip to content

fix(watch): cache audio support across unmute - #7248

Merged
kixelated merged 11 commits into
mainfrom
quest/m1/push-to-talk/audio-support-cache
Nov 2, 2026
Merged

kixelated merged 11 commits into
mainfrom
quest/m1/push-to-talk/audio-support-cache

Conversation

@kixelated

@kixelated kixelated commented Oct 29, 2026 •

Copy link
Copy Markdown
Collaborator

Completes quest/m1/push-to-talk/audio-support-cache.md (deleted here).

Builds on moq-dev/moq#7359, which has landed. The diff against main is now only the audio-specific changes.

Problem

On every catalog change, including a push-to-talk mute or unmute, audio rendition selection asked the decoder again whether the codec is supported. So on unmute the AudioContext could resume only after a support check of a few milliseconds, even though frames were already arriving. Video already caches its verdicts.

Approach

moq-dev/moq#7359 moved the per-rendition support cache into a shared SupportProbe. That probe keys each verdict by the probe's supportCacheKey, or by the full config when the probe sets none. This PR adds only what is specific to audio:

  • audio/config.ts adds decoderConfigKey, the JSON of decoderConfig. The built-in Decoder.supported is tagged with it, the same way video's decoder is. As a result, enabled, jitter, bitrate, label and other metadata updates reuse the cached verdict. A change to a field the decoder uses still probes again.
  • Tests:
    • audio/source.test.ts: mute and unmute make one probe, publish the new metadata, and keep the rendition held. A sampleRate change probes again.
    • audio/decoder.test.ts: the same AudioContext suspends once drained and resumes on unmute, with one support probe in total.
    • audio/supported.test.ts: the real libav/WebCodecs probe makes one isConfigSupported query across mute and unmute.
  • js/watch/bench/support.ts (just js audio-support-bench, in the nightly JS series) measures catalog updates against rendition count.

Impact

  • Public API: none.
  • Wire: none.
  • quest/m1/push-to-talk/README.md has no children left, so it is now sized [M] and is the epic's remaining work.

Validation

  • Full local gate: quest gate nix develop --command just ci origin/main passes on c727535 (check/test js, markdown, gh, quest, shell, toml, nix, justfile, workers, large_files).
  • bun watch/bench/support.ts: after warmup, no new probes for 1, 8 or 32 renditions over 20 and 200 updates.
  • Earlier Chromium 156 run of this bench, from a previous revision with the same keying: the baseline made renditions × updates native queries (6,400 for 32 × 200), while the cached build made zero. For 32 × 200, the median update took 82.5 µs before and 25 µs after.

Decisions

Follow-ups

None.

Review

quest review --base origin/main: Codex, gpt-6.1-sol, medium effort, round 1 on head c727535 (after merging main, so the diff shows only the audio changes). No findings.

Automated reviews on the previous head 3689fc1 (OpenAI review, Pullfrog, CodeRabbit) reported no actionable findings.

🤖 Generated with Claude Code

(Written by Claude Opus 5.5)

kixelated and others added 4 commits October 29, 2026 09:00
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
@kixelated kixelated changed the title Watch caches audio codec support fix(watch): cache audio support across unmute Oct 29, 2026
kixelated and others added 3 commits October 30, 2026 05:15
Audio and video Sources now probe rendition support through a shared
SupportProbe that owns the probe race, the per-rendition config-keyed cache,
pending, and the NotSupportedError. Audio gains video's cache and an
out.error, and the unsupported indicator reports an audio-only broadcast it
cannot play.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kixelated

Copy link
Copy Markdown
Collaborator Author

Maintainer decision (2026-10-31, /quest-plan of the 10-31 follow-ups): keep this PR and rescope it after moq-dev/moq#7359 lands. Merge main, drop support-cache.ts in favour of #7359's shared supportCacheKey, and keep only the audio decoderConfigKey tag, the mute/unmute test, and the bench.

(Written by Claude Opus 5.5)

kixelated and others added 3 commits October 31, 2026 12:24
…d29f664aa66

# Conflicts:
#	js/watch/src/audio/decoder.test.ts
#	js/watch/src/audio/source.test.ts
Drop support-cache.ts in favour of #7359's shared supportCacheKey. Keep the
audio decoderConfigKey tag, the mute/unmute tests, and the bench. Finish the
audio-support-cache quest and size its now childless push-to-talk epic.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kixelated
kixelated marked this pull request as ready for review October 31, 2026 19:32

@kixelated kixelated left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Automated review by review (OpenAI)

Reviewed commit: 3689fc1

No actionable bugs found. The audio key covers the decoder/probe inputs while allowing mute, jitter and bitrate metadata to reuse the verdict; cached selection still publishes the current metadata. Reusing the shared SupportProbe is simpler than maintaining a separate audio cache. The included #7359 extraction also preserves cancellation, cache pruning and unsupported-error handling on inspection. Keep the documented #7359-first landing order.

Verification: static inspection of code, tests and benchmark wiring, including an independent shared-probe/source/UI pass. Builds, tests, browser playback and benchmarks were not run; the reported performance measurements were not independently reproduced.

(Written by OpenAI)

@coderabbitai

coderabbitai Bot commented Oct 31, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review.

Recent review info
Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: bb0202bd-b028-46e2-ab19-9795dd8b01b8
Commits

Reviewing files that changed from the base of the PR and between c04f777 and 3689fc1.

Files selected for processing: 21
  • js/justfile
  • js/watch/api/index.api.md
  • js/watch/bench/support.ts
  • js/watch/src/audio/config.ts
  • js/watch/src/audio/decoder.test.ts
  • js/watch/src/audio/decoder.ts
  • js/watch/src/audio/source.test.ts
  • js/watch/src/audio/source.ts
  • js/watch/src/audio/supported.test.ts
  • js/watch/src/support-probe.ts
  • js/watch/src/ui/components/unsupported-indicator.test.ts
  • js/watch/src/ui/components/unsupported-indicator.ts
  • js/watch/src/video/config.ts
  • js/watch/src/video/decoder.ts
  • js/watch/src/video/source.test.ts
  • js/watch/src/video/source.ts
  • quest/m1/push-to-talk/README.md
  • quest/m1/push-to-talk/audio-support-cache.md
  • quest/m2/README.md
  • quest/m2/watch-support-probe.md
  • sh/gh/nightly.sh
Files with no reviewable changes: 4
  • quest/m2/README.md
  • js/watch/src/video/config.ts
  • quest/m1/push-to-talk/audio-support-cache.md
  • quest/m2/watch-support-probe.md

Included review availability: This review used your included allowance. Your plan provides up to 4 included reviews per hour; 0 remain after this review.

Walkthrough

The changes add a shared support probe for audio and video rendition checks, with cached results, pending state, and error reporting. Audio source output now exposes probe errors, and the unsupported-media indicator can report unsupported audio when no video catalog exists. Tests cover caching, catalog updates, probe failures, and indicator behavior. The changes also add an audio support benchmark and remove related planning documents.

Merge Risk: Minimal · up to 3689f

No confirmed issue blocks this change; merge it after the prerequisite shared-probe change.

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documenta… Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Title check Passed The title clearly and concisely identifies the main change: caching audio support checks across mute and unmute operations.
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.
Description check Passed The description clearly explains the audio support-cache changes, their purpose, implementation, tests, benchmark, and impact. It directly matches the changeset.
Full details: Docstring Coverage

Explanation

Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentation-only edits and deletions within surviving functions are included; unchanged diff context and fully deleted functions are excluded. Analyzed 3 functions across 14 files. (3 skipped: 3 unsupported.)

Resolution

Write docstrings for the functions missing them to satisfy the coverage threshold.

Finishing Touches
Simplify code
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

✅ No new issues found.

Reviewed changes

This is stacked on #7359 and merges its head, so the diff against main also carries #7359's shared SupportProbe refactor; the audio-specific parts are the decoderConfigKey tag, the mute/unmute tests, and the bench.

  • Shared SupportProbe (from #7359) — js/watch/src/support-probe.ts owns the race against an unsettled probe, the per-supported-function, per-rendition config-keyed cache, pending, and a NotSupportedError error; video and audio Source both delegate to it and expose its error.
  • Audio support cache — audio/config.ts adds decoderConfigKey and audio/decoder.ts tags the module Decoder.supported with it, so metadata-only catalog changes (mute/unmute, enabled, jitter, bitrate) reuse the cached verdict while a decoder-relevant field (codec, container, description, sampleRate, numberOfChannels) re-probes.
  • Audio out.error — Audio.Source.Out gains a public, additive error (reflected in api/index.api.md), and unsupported-indicator now reports an audio-only broadcast it cannot decode, deferring to video whenever a video catalog exists.
  • Tests and bench — mute/unmute source and decoder tests assert a single probe across the cycle and the AudioContext suspend/resume; audio/supported.test.ts counts isConfigSupported; js/watch/bench/support.ts (just js audio-support-bench, added to sh/gh/nightly.sh) sweeps rendition count in the nightly series.

I verified the load-bearing behavior end to end: SupportProbe.run's early if (!supported) return; cannot strand pending=true (the effect.set cleanup drains on every rerun/close before the next run body), a torn-down probe's late result cannot reach publish/error (the race resolves undefined on abort, guarded by effect.abort.aborted), and decoderConfigKey covers every field the audio supported probe reads, so no false cache hit is possible. The watch tests for the changed files pass locally.

Pullfrog  | View workflow run | Using deepseek-v4.1-flash (free via Pullfrog for OSS) | 𝕏

# Conflicts:
#	js/watch/src/audio/source.test.ts
#	js/watch/src/ui/components/unsupported-indicator.test.ts
#	js/watch/src/ui/components/unsupported-indicator.ts

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

✅ No new issues found.

Reviewed changes

This run covers the delta since the prior pullfrog review at 3689fc104: a merge of main after #7359 landed, which moves the shared SupportProbe refactor and the audio out.error/unsupported-indicator work into the base and leaves the diff against main as only the audio-specific parts.

  • Merged main after #7359 landed — the branch's support-probe.ts, video Source/decoder/config, and audio Source/unsupported-indicator changes are now inherited from main, so the PR's diff against main shrank to the audio decoderConfigKey tag, the mute/unmute tests, the bench, and the quest cleanup.
  • Adopted main's yieldNow() in audio/source.test.ts — the local tick helper is replaced by the injected-clock helper; the mute/unmute test itself is unchanged.
  • Picked up main's AAC/warmup decoder.test.ts additions — they coexist with the PR's supportProbes mock and the AudioContext suspend/resume assertions.
  • Finished the quest bookkeeping — quest/m1/push-to-talk/README.md is now [M] with its Required section removed, and no references to the deleted audio-support-cache.md remain anywhere.

The audio-specific code is byte-identical to the approved revision, and the merge resolution matches main's landed #7359 code. decoderConfigKey remains a superset of every field the probe reads, so no false cache hit is possible. Ran bun test src/audio/source.test.ts (15 pass) and bun test src/audio/decoder.test.ts (56 pass) locally; audio/supported.test.ts cannot run here because its import graph reaches @moq/libav.

Pullfrog  | View workflow run | Using deepseek-v4.1-flash (free via Pullfrog for OSS) | 𝕏

@kixelated
kixelated added this pull request to the merge queue Nov 2, 2026
Merged via the queue into main with commit 5e327b5 Nov 2, 2026
9 of 10 checks passed
@kixelated
kixelated deleted the quest/m1/push-to-talk/audio-support-cache branch November 2, 2026 19:08
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.

1 participant