Skip to content

Fix HDMI audio detection on Pi4 with dual HDMI ports#2811

Merged
vpetersson merged 10 commits into
masterfrom
alex-hdmi-audio-pi4
May 12, 2026
Merged

Fix HDMI audio detection on Pi4 with dual HDMI ports#2811
vpetersson merged 10 commits into
masterfrom
alex-hdmi-audio-pi4

Conversation

@vpetersson
Copy link
Copy Markdown
Contributor

Summary

Supersedes #2667 by @Alex1981-tech, rebased onto current master with conflicts resolved against the recent media_player.py refactor (top-level get_alsa_audio_device() shared across MPV and VLC) and tests updated.

Original work and the fix itself are by @Alex1981-tech; Alex's commit is preserved as-is.

Changes (from @Alex1981-tech)

  • Pi4 has two HDMI ports (HDMI-A-1 / HDMI-A-2), but VLC audio always targeted default:CARD=vc4hdmi0 regardless of which port had a display connected — no audio when using HDMI-A-2.
  • The default:CARD= ALSA device name is unreliable when PulseAudio or dmix is involved.

The fix:

  • Adds _detect_hdmi_audio_device() that reads /sys/class/drm/cardN-HDMI-A-N/status to find which HDMI port is connected and returns the matching ALSA device.
  • Switches from default:CARD= to sysdefault:CARD= for direct hardware access.
  • Applies auto-detection for Pi4 and Pi5 HDMI output. The detection now naturally benefits MPV too (Pi4-64 / Pi5), since master's refactor moved get_alsa_audio_device() to a top-level helper used by both players.
  • Headphones and Pi1-3 paths are unchanged in behavior; their device strings move from default: to sysdefault: for consistency.

Conflict resolution notes

  • Master refactored get_alsa_audio_device into a top-level function shared by MPVMediaPlayer and VLCMediaPlayer. Alex's PR was based on a version where it lived as a method on VLCMediaPlayer. Alex's intent has been ported onto the current top-level helper.
  • Alex's second commit (style: apply ruff format) was redundant on the resolved structure and was dropped during rebase; an equivalent format pass is already folded into his fix commit.

Tests

  • Existing tests/test_media_player.py cases updated for default:sysdefault:.
  • HDMI Pi4/Pi5 dispatch test now mocks _detect_hdmi_audio_device and asserts it's called.
  • New cases cover _detect_hdmi_audio_device itself: first-port-connected, second-port-only-connected, no status files (fallback), and OSError on read (fallback).

Test plan

  • uv run ruff check viewer/media_player.py tests/test_media_player.py — clean
  • uv run ruff format --check viewer/media_player.py tests/test_media_player.py — clean
  • uv run pytest -m "not integration" — 413 passed
  • Hardware verification on Pi4 with HDMI-A-1 only (already confirmed in Fix HDMI audio detection on Pi4 with dual HDMI ports #2667)
  • Hardware verification on Pi4 with HDMI-A-2 only
  • Hardware verification on Pi5

🤖 Generated with Claude Code

Alex and others added 2 commits May 2, 2026 13:09
Pi4 has two HDMI ports (HDMI-A-1 and HDMI-A-2), but the existing code
always hardcodes `default:CARD=vc4hdmi0` regardless of which port has
a display connected. This causes no audio when using HDMI-A-2.

Changes:
- Add `_detect_hdmi_audio_device()` that reads
  `/sys/class/drm/cardN-HDMI-A-N/status` to find which HDMI port
  is connected and returns the correct ALSA device
- Use `sysdefault:CARD=` instead of `default:CARD=` for more
  reliable audio output (avoids PulseAudio/dmix interference)
- Apply detection for Pi4 and Pi5 HDMI output
- Keep headphones and Pi1-3 paths unchanged

Tested on Pi4 Model B with single HDMI connected to HDMI-A-1.
…ings

Update existing tests for the default: -> sysdefault: switch and add
coverage for _detect_hdmi_audio_device(): both port preferences,
missing status files, and OSError fall-through.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Fixes HDMI audio device selection on Raspberry Pi 4/5 by auto-detecting which HDMI connector is physically connected and selecting the corresponding ALSA card, improving reliability when default: is affected by PulseAudio/dmix.

Changes:

  • Add _detect_hdmi_audio_device() that reads /sys/class/drm/*/status for HDMI connector connection state and returns a matching sysdefault:CARD=... device string.
  • Update get_alsa_audio_device() to use HDMI auto-detection on Pi4/Pi5 and switch several ALSA device strings from default: to sysdefault:.
  • Update and extend unit tests to cover the new detection helper and the updated device selection behavior.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
viewer/media_player.py Introduces HDMI connector detection helper and routes Pi4/Pi5 HDMI output through it; switches ALSA device strings to sysdefault:.
tests/test_media_player.py Updates expectations for sysdefault: and adds direct tests for HDMI detection/fallback behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread viewer/media_player.py Outdated
Comment thread viewer/media_player.py Outdated
vpetersson and others added 2 commits May 2, 2026 13:22
…Error

Address PR feedback:
- Docstring previously conflated DRM card directories
  (/sys/class/drm/cardN-HDMI-A-N/) with ALSA card names
  (vc4hdmiN). Rewrite to describe the two layers separately and
  document the card1 / vc4hdmi0|1 mapping explicitly.
- The except OSError branch silently swallowed read failures.
  Log them at debug level with the path and exception so field
  debugging has something to grep for; behavior (fallback to
  vc4hdmi0) is unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@vpetersson vpetersson requested a review from Copilot May 12, 2026 15:02
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

Comment thread src/anthias_viewer/media_player.py
Comment thread tests/test_media_player.py Outdated
When audio_output=local on Pi5 the helper hardcoded sysdefault:CARD=vc4hdmi0,
which has the same dual-HDMI bug we fix elsewhere — a Pi5 with only HDMI-A-2
connected would still target vc4hdmi0 and produce no audio. Pi5 has no
3.5mm jack, so 'local' is effectively HDMI; route it through the same
_detect_hdmi_audio_device() helper used by the 'hdmi' branch.

Reviewed-by: copilot-pull-request-reviewer
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

Comment thread src/anthias_viewer/media_player.py
Comment thread src/anthias_viewer/media_player.py Outdated
… card1

Two follow-ups from Copilot review:

1. _detect_hdmi_audio_device hardcoded /sys/class/drm/card1-HDMI-A-{1,2}.
   The vc4 DRM card index isn't stable across images/kernels — on a
   layout where it probes as card0 or card2, auto-detection would never
   see a connected port and always fall back to vc4hdmi0, reintroducing
   the very bug this PR fixes. Discover directories by scanning
   /sys/class/drm and matching the HDMI-A-N suffix; the HDMI-A-N to
   vc4hdmiN mapping is what's actually stable.

2. get_alsa_audio_device() called get_device_type() up to three times
   per dispatch (each re-reading /proc/device-tree/model). Cache it in a
   local.

Tests updated to mock os.scandir and add coverage for non-card1 DRM
layouts.

Reviewed-by: copilot-pull-request-reviewer
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

Comment thread src/anthias_viewer/media_player.py Outdated
Comment thread tests/test_media_player.py
Sorting the discovered DRM connector list by the full entry name
(card0-HDMI-A-2 < card1-HDMI-A-1) could prefer HDMI-A-2 over HDMI-A-1
on multi-card layouts. Build a (suffix, entry) list and sort on the
HDMI-A-N suffix instead, so HDMI-A-1 always wins when both connectors
report "connected" — regardless of the DRM card prefix.

Adds two precedence tests covering same-card and mixed-card layouts.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

Comment thread src/anthias_viewer/media_player.py Outdated
get_alsa_audio_device() runs on every MPV play() and every VLC
set_asset(), so the "No connected HDMI detected" fallback warning
spammed the viewer log whenever a Pi4/Pi5 ran with no display
attached (e.g. cold boot before the TV powers on, transient EDID
loss). Keep the first miss at WARNING — that's the useful signal —
and drop subsequent misses to DEBUG via a module-level flag.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@vpetersson vpetersson requested a review from Copilot May 12, 2026 16:19
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

Comments suppressed due to low confidence (1)

tests/test_media_player.py:1

  • This test patches _detect_hdmi_audio_device but doesn’t assert it was called, so it wouldn’t fail if MPV switched back to a hard-coded sysdefault:CARD=vc4hdmi0. Add an assertion (e.g., _mock_detect.assert_called_once()) after mpv.player.play() to explicitly verify that the MPV path is using the HDMI auto-detection helper.
import logging

Comment thread src/anthias_viewer/media_player.py Outdated
Comment thread src/anthias_viewer/media_player.py Outdated
- Replace the WARN-once flag with a single _last_detected_device
  cache. The first call always logs at the higher level; repeated
  identical results drop to DEBUG; transitions (HDMI-A-1 ⇄ HDMI-A-2,
  or detected ⇄ fallback) re-log loudly. Addresses log spam on both
  the success path (INFO ran on every play()) and the fallback path,
  and keeps state changes visible.
- Quote the os.DirEntry[str] annotation so it isn't evaluated at
  import time, sidestepping any PEP-585 runtime concern on older
  interpreters while keeping mypy happy.
- Patch logging via string paths in the new test (fixes mypy
  attr-defined on patch.object(mp.logging, ...)). Extend the test
  to cover the three transition shapes: fallback→fallback (DEBUG),
  fallback→connected (INFO), connected→fallback (WARN re-fires).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@vpetersson vpetersson requested a review from Copilot May 12, 2026 16:27
@sonarqubecloud
Copy link
Copy Markdown

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.

@vpetersson vpetersson merged commit 832aed9 into master May 12, 2026
13 checks passed
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