Skip to content

Mute audio across all client apps — default muted, settable from every config path #838

Description

@jonathaneoliver

Motivation

When testing streaming we rarely want to hear the audio. Every client app should have a mute option that defaults to muted, lives in Settings → Advanced, and is settable from every way we configure the app (Settings UI, cold-launch flags, Appium capabilities / config-on-connect, harness per-play app-config, and the characterization matrix knobs).

In scope: iOS/tvOS (apple/) + Android (android/), plus the dashboard/proxy/harness config plumbing.
Out of scope: Roku.

Current state (verified 2026-06-23)

  • iOS — mostly built. isMuted + a "Mute audio" toggle in Settings/Advanced, the is.flag.muted launch flag, UserDefaults persistence, and always-muted preview tiles all already exist. Pointers: apple/InfiniteStreamPlayer/InfiniteStreamPlayer/PlayerViewModel.swift:103,485,732,1337,2274,2308,2372,2445; SettingsOverlay.swift:375. Gaps below.
  • Android — not built. android/ exists but has no app-level mute (no FLAG_MUTED / setVolume / muted). Tiles silence via a no-audio renderer factory (LivePreviewTile.kt). Full feature needed.
  • Harness — no muted. tools/harness-cli/cmd/harness/appconfig.go covers segment/protocol/live-offset/peak-bitrate only.

Requirements

  1. Mute toggle in Settings → Advanced on iOS and Android.
  2. Default = muted (true) on both platforms.
  3. Live toggle (no relaunch) updates the player immediately.
  4. Settable from all config paths, all converging on the same state:
    • Settings UI (persisted)
    • Cold-launch flag -is.flag.muted true|false (NSArgumentDomain / intent extra)
    • Appium capability / config-on-connect (isAudioMuted / app.muted)
    • Harness per-play harness app-config <target> --muted true|false
    • Characterization matrix per-arm + sweep knobs

Scope checklist

iOS (apple/InfiniteStreamPlayer/InfiniteStreamPlayer/)

  • Flip default to muted — PlayerViewModel.swift:103 isMuted = falsetrue.
  • Add muted: Bool? to ServerAppConfig (struct ~:1073); parse it in fetchServerAppConfig(); overlay in applyServerAppConfig() so harness/config-on-connect per-play push works.
  • Confirm the isAudioMutedis.flag.muted capability mapping (PlayerViewModel.swift:3912) is complete end to end.

Android (android/InfiniteStreamPlayer/) — build the whole thing, copy the allow4K toggle pattern

  • UiState.muted: Boolean = true (state/PlayerState.kt).
  • FLAG_MUTED = "advanced_muted" + setMuted() setter that writes SharedPreferences and applies player.volume = if (muted) 0f else 1f (state/PlayerViewModel.kt).
  • Load order: LaunchConfig.muted ?: prefs.getBoolean(FLAG_MUTED, true) in loadAdvancedFlags().
  • LaunchConfig.muted holder + intent-extra capture for is.flag.muted (MainActivity.kt).
  • "Mute audio" PickerItem in the Advanced section (ui/screen/SettingsOverlay.kt).
  • Add muted: Boolean? to Android ServerAppConfig; parse from /api/sessions; overlay in applyServerAppConfig().

Harness + characterization plumbing (tools/harness-cli/, tests/characterization/)

  • appconfig.go: add --muted flag → ac["muted"] in buildAppConfigBody (+ usage text + appconfig_test.go case).
  • runner/probe.go: ProbeConfig.Muted + emit -is.flag.muted in ProbeLaunchArgs (+ probe_test.go case).
  • Char matrix: Muted on the Arm spec (internal/charmatrix/spec.go); CHAR_ARM_<i>_MUTED + CHAR_SWEEP_MUTED in cmd/harness/char.go; read them in modes/char_matrix_fleet_test.go.
  • Decide whether characterization should force a baseline (runner/appium.go baselineTestFlags / modes/runconfig.go) — default-muted may make a forced baseline unnecessary; document the choice.
  • Config-on-connect parser (go-proxy/cmd/server/configconnect.go) is generic (app.muted=true routes automatically) — no change expected; verify.

Acceptance criteria

  • iOS and Android both launch muted by default, with a working Settings → Advanced toggle that takes effect live.
  • -is.flag.muted false at launch, harness app-config --muted false, and a char-matrix arm muted: false each produce audible playback on a real run; the inverse mutes.
  • Verify from data, not just the UI: confirm on a real play that audio is/ isn't flowing (per the project's "don't trust a screenshot" rule).

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions