Skip to content

feat: audio in Quad mode for the selected cell (#12) - #51

Merged
compot-dev merged 3 commits into
v2from
feat/quad-audio
Oct 12, 2026
Merged

compot-dev merged 3 commits into
v2from
feat/quad-audio

Conversation

@compot-dev

@compot-dev compot-dev commented Oct 12, 2026 •

Copy link
Copy Markdown
Collaborator

Fixes #12.

Stacked on #48: this PR targets fix/decoder-lifecycle (it needs the per-cell FrameLayout containers from there). After #48 is merged, the base becomes v2.

⚠️ Do not merge before testing on a device (label awaiting device test).

How it works

  • Every Quad cell sets up its audio track at connect time, but only the selected cell passes its audio to AudioManager. Switching is instant and needs no reconnect. Audio traffic is small compared with video.
  • Long press on a cell selects it; a long press on the selected cell mutes. The selected cell shows an AUDIO badge, and the choice is saved (quad_audio).
  • A separate playback thread runs while Quad is on screen; it stops when leaving Quad and on pause.
  • When the audible cell reconnects, its new audio parameters are applied to AudioManager.
  • README in all three languages: Quad audio is described, and Quad mode has no audio #12 is removed from "Known limitations".

Tests

QuadAudioTest:

  • a cell's audio reaches the sink only while the cell is selected, and video packets never do;
  • the audio configuration is reported when the cell connects.

93 JVM tests on this branch (stacked on #48), all pass. assembleDebug and lintDebug pass with no new issues.

Device checklist

  • In Quad, long press on a cell: its sound plays and the badge appears
  • Long press on another cell: the sound switches without a reconnect
  • Long press on the selected cell: silence, no badge
  • The selection survives Home → back
  • Leaving Quad mutes the sound; single view keeps working as before

@compot-dev compot-dev added the awaiting device test PR ready, merge only after testing on a real device label Oct 12, 2026
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

Add selectable audio playback in Quad mode

✨ Enhancement 🧪 Tests 📝 Documentation 🕐 20-40 Minutes

Grey Divider

AI Description

• Play audio from one selected Quad cell without reconnecting the camera streams.
• Long-press to select or mute a cell; show an AUDIO badge and remember the choice.
• Stop Quad playback when leaving or pausing, and refresh audio settings after reconnects.
Diagram

sequenceDiagram
    actor U as Viewer
    participant D as Decoder
    participant C as QuadCell
    participant R as RTSP session
    participant A as AudioManager
    participant P as Playback worker
    D->>C: Start each cell
    C->>R: Enable audio track
    R-->>C: SDP and RTP packets
    C-->>D: Report audio config
    U->>D: Long-press cell
    D->>C: Select or mute sink
    D->>A: Configure selected audio
    opt Cell is selected
        C->>A: Forward audio packets
    end
    D->>P: Start in Quad mode
    P->>A: Play PCM frames
    D->>P: Stop on exit or pause
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Request audio only for the selected RTSP cell
  • ➕ Avoids receiving audio traffic from muted cells.
  • ➖ Changing cells would require reconnecting or renegotiating streams, delaying the switch.

Recommendation: Keep the PR’s per-cell audio setup with a single selected sink: it reuses AudioManager and permits switching without reconnects. Complete the requested device checks before merging, particularly switching, reconnects, and pause/resume.

Files changed (7) +213 / -23

Enhancement (3) +134 / -17
Decoder.javaManage selected Quad audio and playback lifecycle +103/-14

Manage selected Quad audio and playback lifecycle

• Adds long-press selection, persistent preference, AUDIO badges, and routing to the shared AudioManager. Starts a dedicated Quad playback worker, stops it on exit or pause, and applies updated audio parameters when the selected cell reconnects.

android/app/src/main/java/com/openipc/decoder/Decoder.java

QuadCell.javaEnable audio tracks and gate packet forwarding per cell +30/-3

Enable audio tracks and gate packet forwarding per cell

• Requests audio for each Quad RTSP session while forwarding packets only when an audio sink is assigned. Reports SDP audio configuration to the listener and clears the sink when stopped.

android/app/src/main/java/com/openipc/decoder/QuadCell.java

strings.xmlAdd the Quad AUDIO badge label +1/-0

Add the Quad AUDIO badge label

• Defines the string displayed on the selected cell’s audio badge.

android/app/src/main/res/values/strings.xml

Tests (1) +76 / -0
QuadAudioTest.javaTest Quad audio routing and connection configuration +76/-0

Test Quad audio routing and connection configuration

• Checks that only a cell with an assigned sink forwards audio, video never reaches that sink, and SDP audio settings are reported on connection.

android/app/src/test/java/com/openipc/decoder/QuadAudioTest.java

Documentation (3) +3 / -6
README.mdDocument Quad audio in English +1/-2

Document Quad audio in English

• Explains long-press selection, muting, the AUDIO badge, and remembered selection. Removes the resolved Quad-audio limitation.

README.md

README.ru.mdDocument Quad audio in Russian +1/-2

Document Quad audio in Russian

• Describes Quad audio selection and removes the resolved limitation from the Russian README.

README.ru.md

README.zh-CN.mdDocument Quad audio in Chinese +1/-2

Document Quad audio in Chinese

• Describes Quad audio selection and removes the resolved limitation from the Chinese README.

README.zh-CN.md

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Oct 12, 2026 •

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Remediation recommended

1. Camera switches can use the wrong audio format ✓ Resolved
Description
onAudioConfig configures the shared AudioManager based only on the cell index, without checking
whether the callback belongs to the current cell or mode. If a stopped Quad session delivers an
in-flight SDP callback after a replacement cell or the single-camera stream starts, its old codec
parameters can overwrite those of the playing stream.
Code

android/app/src/main/java/com/openipc/decoder/Decoder.java[548]

+                            if (index == quadAudio) audioManager.configure(audio);
Evidence
RtspSession.stop() does not wait for its network thread, and the generation check precedes
callback invocation, leaving an in-flight callback possible. The new callback checks only the index
before changing the shared configuration that AudioManager.onAudioFrame() uses to decode packets.

android/app/src/main/java/com/openipc/decoder/RtspSession.java[107-119]
android/app/src/main/java/com/openipc/decoder/RtspSession.java[253-255]
android/app/src/main/java/com/openipc/decoder/Decoder.java[519-552]
android/app/src/main/java/com/openipc/decoder/AudioManager.java[91-114]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A stopped Quad cell can reconfigure the shared audio manager after its replacement or the single-camera stream starts.
## Fix Focus Areas
- android/app/src/main/java/com/openipc/decoder/Decoder.java[536-555]
- android/app/src/main/java/com/openipc/decoder/Decoder.java[519-533]
## Recommended Fix
Associate audio-configuration callbacks with the current cell and mode, and discard callbacks from stopped or replaced cells. Ensure single-camera callbacks cannot overwrite an active Quad selection during a mode switch.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Audio tracks can linger after leaving Quad ✓ Resolved
Description
stopQuadAudio interrupts the playback executor and closes audio without ensuring that an
in-progress track creation has finished. If the worker creates an AudioTrack while shutdown runs,
closeAudio() can see no track, after which the worker publishes and writes to a track that remains
open.
Code

android/app/src/main/java/com/openipc/decoder/Decoder.java[R595-598]

+        quadAudioGen++;
+        if (quadAudioExecutor != null) {
+            quadAudioExecutor.shutdownNow();
+            quadAudioExecutor = null;
Evidence
The Quad worker checks its generation only between playPcmFrame() calls. AudioManager starts a
new track before publishing it, while closeAudio() releases only the track already published; Quad
shutdown leaves the manager enabled, so the late publication is permitted.

android/app/src/main/java/com/openipc/decoder/Decoder.java[583-606]
android/app/src/main/java/com/openipc/decoder/AudioManager.java[122-140]
android/app/src/main/java/com/openipc/decoder/AudioManager.java[239-295]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Stopping Quad audio can close the current track before its playback worker publishes a newly created one.
## Fix Focus Areas
- android/app/src/main/java/com/openipc/decoder/Decoder.java[583-606]
- android/app/src/main/java/com/openipc/decoder/AudioManager.java[239-295]
## Recommended Fix
Invalidate in-progress track creation as part of Quad shutdown and check that invalidation before publishing a track. Close the existing track after invalidation, without relying on executor interruption alone.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Single view can play leftover Quad audio ✓ Resolved
Description
stopQuadAudio clears the shared PCM queue after detaching cell sinks, but an in-flight packet
callback may already have captured the old sink and enqueue audio afterward. When leaving Quad
immediately starts the single-view listener, its playback thread can consume that leftover frame
before the new stream's audio.
Code

android/app/src/main/java/com/openipc/decoder/Decoder.java[R600-603]

+        QuadCell[] cells = quadCells;
+        if (cells != null) {
+            for (QuadCell cell : cells) {
+                if (cell != null) cell.setAudioSink(null);
Evidence
QuadCell.onPacket() retains a local sink reference before forwarding the frame, and session
shutdown does not wait for callbacks. closeAudio() clears the queue only at that moment; the
single-view playback worker subsequently polls the same queue.

android/app/src/main/java/com/openipc/decoder/QuadCell.java[213-224]
android/app/src/main/java/com/openipc/decoder/RtspSession.java[107-119]
android/app/src/main/java/com/openipc/decoder/AudioManager.java[122-126]
android/app/src/main/java/com/openipc/decoder/Decoder.java[291-302]
android/app/src/main/java/com/openipc/decoder/Decoder.java[817-823]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
An in-flight Quad packet callback can enqueue audio after the shared queue is cleared for a mode switch.
## Fix Focus Areas
- android/app/src/main/java/com/openipc/decoder/Decoder.java[594-606]
- android/app/src/main/java/com/openipc/decoder/QuadCell.java[213-224]
## Recommended Fix
Invalidate Quad audio delivery when stopping it so callbacks that captured an earlier sink cannot enqueue frames after cleanup. Keep frames from the previous mode out of the single-view playback queue.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Tip of the day
💡 Did you know, you can commit Qodo's fix in one click with committable suggestions (GitHub & GitLab)

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread android/app/src/main/java/com/openipc/decoder/Decoder.java Outdated
Comment thread android/app/src/main/java/com/openipc/decoder/Decoder.java
Comment thread android/app/src/main/java/com/openipc/decoder/Decoder.java Outdated
Every Quad cell now sets up its audio track, but only the selected cell
passes its audio to the AudioManager, so switching needs no reconnect
(fixes #12).

- Long press on a cell selects it; a long press on the selected cell
  mutes. The cell shows an AUDIO badge, and the choice is saved
  ("quad_audio").
- A dedicated playback thread runs while Quad is shown; it is stopped
  with Quad and on pause.
- A reconnect of the audible cell reconfigures the AudioManager with the
  new audio parameters.
- README (EN/RU/ZH): Quad audio documented, #12 removed from the
  limitations.

Tests: audio of a cell reaches the sink only while selected, never
video packets; the audio configuration is reported on connect.
Review findings on Quad audio: an in-flight SDP callback of a stopped
cell could reconfigure the audio, a track created during shutdown could
stay open, and a late Quad packet could be played in single view.

AudioManager now has routes: newRoute() bumps the route and clears the
queues under one lock and closes audio. Every producer carries its
route: onAudioFrame(frame, route), configure(config, route), the AAC
decode loop (route of its codec), and track creation (route at start).
Anything from an older route is dropped.

- Single view: startListener() gives RtspClient a new route, and
  RtspClient configures the AudioManager itself on that route.
- Quad: applyQuadAudio() starts a route for the selected cell; the cell
  configures audio on its own route; stopQuadAudio() invalidates it.

Tests: stale config and frames are ignored, a new route clears the queue;
a cell's config reaches the sink with the cell's route.
@compot-dev
compot-dev changed the base branch from fix/decoder-lifecycle to v2 October 12, 2026 14:57
@compot-dev compot-dev removed the awaiting device test PR ready, merge only after testing on a real device label Oct 12, 2026
@compot-dev
compot-dev merged commit 8d9f5c2 into v2 Oct 12, 2026
4 checks passed
@compot-dev
compot-dev deleted the feat/quad-audio branch October 13, 2026 17:15
compot-dev added a commit that referenced this pull request Oct 15, 2026
feat: audio in Quad mode for the selected cell (#12)
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.

Quad mode has no audio

1 participant