Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions gridplayer/vlc_player/instance.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ def init_options(self):
"--no-keyboard-events",
"--no-mouse-events",
*self.vlc_options,
*_audio_output_options(),
# before the options typed by hand, which are last so that
# they still win over anything a page here has set
*_subtitle_style_options(),
Expand Down Expand Up @@ -244,6 +245,15 @@ def _subtitle_style_options() -> list[str]:
return subtitle_style_options(style)


def _audio_output_options() -> list[str]:
"""Audio output to ask VLC for, ahead of any typed by hand."""

if env.IS_LINUX:
return ["--aout=pulse,any"]

return []


def _iter_settings_options() -> Iterator[str]:
options = shlex.split(Settings().sync_get("misc/vlc_options"))

Expand Down
75 changes: 75 additions & 0 deletions tests/test_vlc_audio_output.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
"""Which audio output VLC is asked for on Linux.

VLC's PipeWire output (vlc-plugin-pipewire 3, as shipped by Fedora) never
sets its stream pointer when it opens, only when the first audio is played.
A mute or a volume change before then follows whatever the allocator left
there and the VLC process dies. Every player is muted the moment it is made,
and a video without sound never starts the output at all, so there is no
safe moment to wait for. PulseAudio is asked for first instead, which on a
PipeWire desktop is served by pipewire-pulse.
"""

import logging

import pytest
from PyQt5.QtCore import QSettings

from gridplayer.params import env
from gridplayer.settings import Settings
from gridplayer.vlc_player.instance import InstanceVLC


@pytest.fixture(autouse=True)
def _isolated_settings(tmp_path):
settings = Settings()
real_settings = settings.settings

settings.settings = QSettings(str(tmp_path / "test.ini"), QSettings.IniFormat)

yield

settings.settings = real_settings


@pytest.fixture
def _on_linux(monkeypatch):
monkeypatch.setattr(env, "IS_LINUX", True)
monkeypatch.setattr(env, "IS_WINDOWS", False)
monkeypatch.setattr(env, "IS_APPIMAGE", False)


@pytest.fixture
def _on_macos(monkeypatch):
monkeypatch.setattr(env, "IS_LINUX", False)
monkeypatch.setattr(env, "IS_WINDOWS", False)
monkeypatch.setattr(env, "IS_APPIMAGE", False)


def _init_options():
instance = InstanceVLC(0, [])
instance._logger = logging.getLogger("test")

return instance.init_options


def _aout_options(options):
return [opt for opt in options if opt.startswith("--aout")]


@pytest.mark.usefixtures("_on_linux")
class TestOnLinux:
def test_pulse_is_asked_for_first(self):
assert _aout_options(_init_options()) == ["--aout=pulse,any"]

def test_what_was_typed_by_hand_still_wins(self):
"""Last on the command line is what VLC goes by."""

Settings().set("misc/vlc_options", "--aout=alsa")

assert _aout_options(_init_options()) == ["--aout=pulse,any", "--aout=alsa"]


@pytest.mark.usefixtures("_on_macos")
class TestElsewhere:
def test_the_platform_default_is_left_alone(self):
assert _aout_options(_init_options()) == []