Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cannot stream from inside a docker container without network_mode: "host" #2559

Open
AndreiArdelean1 opened this issue Nov 19, 2024 · 1 comment
Labels

Comments

@AndreiArdelean1
Copy link

Describe the bug

When trying to stream to either an AppleTV or a HomePod mini, it fails with RTSP/1.0 method SETUP failed with code 400: Bad Request

The RTSP stream binds to the docker network IP, not the host IP. Specifying an --address HOST_IP doesn't change the RTSP IP used.

Running it directly on the host works.

Error log

homeassistant:/config# atvremote --id XXXXXXX stream_file=http://XXXXXXX:8123/api/tts_proxy/XXXXXXX_en_-_google_translate.mp3
Traceback (most recent call last):
  File "/usr/local/lib/python3.12/site-packages/pyatv/scripts/atvremote.py", line 997, in _run_application
    return await cli_handler(loop)
           ^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/pyatv/scripts/atvremote.py", line 726, in cli_handler
    return await _handle_commands(args, config, storage, loop)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/pyatv/scripts/atvremote.py", line 876, in _handle_commands
    ret = await _handle_device_command(args, cmd, atv, storage, loop)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/pyatv/scripts/atvremote.py", line 932, in _handle_device_command
    return await _exec_command(atv.stream, cmd, True, *cmd_args)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/pyatv/scripts/atvremote.py", line 964, in _exec_command
    value = await tmp(*args)
            ^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/pyatv/core/facade.py", line 374, in stream_file
    await self.relay("stream_file")(
  File "/usr/local/lib/python3.12/site-packages/pyatv/protocols/raop/__init__.py", line 356, in stream_file
    await client.initialize(self.core.service.properties)
  File "/usr/local/lib/python3.12/site-packages/pyatv/protocols/raop/stream_client.py", line 338, in initialize
    await self._protocol.setup(self.timing_server.port, self.control_client.port)
  File "/usr/local/lib/python3.12/site-packages/pyatv/protocols/raop/protocols/airplayv2.py", line 109, in setup
    await self._setup_base(timing_server_port)
  File "/usr/local/lib/python3.12/site-packages/pyatv/protocols/raop/protocols/airplayv2.py", line 56, in _setup_base
    setup_resp = await self.rtsp.setup(
                     ^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/pyatv/support/rtsp.py", line 177, in setup
    return await self.exchange("SETUP", headers=headers, body=body)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/pyatv/support/rtsp.py", line 294, in exchange
    resp = await self.connection.send_and_receive(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/site-packages/pyatv/support/http.py", line 496, in send_and_receive
    raise exceptions.HttpError(
pyatv.exceptions.HttpError: RTSP/1.0 method SETUP failed with code 400: Bad Request

>>> An error occurred, full stack trace above

How to reproduce the bug?

Run atvremote --id XXXXXXX stream_file=http://XXXXXXX:8123/api/tts_proxy/XXXXXXX_en_-_google_translate.mp3 from a docker container without using network_mode: "host".

What is expected behavior?

A --host-ip and -host-port (I've seen it defaults to 5353) arguments should be introduced to be used for the RTSP stream.

Operating System

Docker

Python

3.12

pyatv

0.15.1

Device

Apple TV 4K (gen 2), tvOS 18.1 and HomePod Mini, tvOS 18.1

Additional context

In the Apple Home app settings, "Speakers & TV" is set to "Everyone".
Running pyatv directly on the host works.
I use an Avahi repeater to get mDNS messages to and from the docker network

@yuxincs
Copy link

yuxincs commented Jan 19, 2025

I have very similar setup for Home Assistant (i.e., Home Assistant within docker network, with an Avahi reflector to reflect mDNS messages on the host), and I have applied some hacking to make it work.

I don't think you can actually bind to host IP within the docker network, but you can map the ports used by RAOP (i.e., timing port and control port) to the container.

So, on the high-level you have to do the following patching:

(1) By default, pyatv uses random ports for RAOP (and the Home Assistant doesn't change them), so you have to specify fixed ports for them here:

pyatv/pyatv/settings.py

Lines 121 to 131 in 49bd01d

timing_port: int = 0
"""Server side (UDP) port used by timing server.
Set to 0 to use random free port.
"""
control_port: int = 0
"""Server side (UDP) port used by control server.
Set to 0 to use random free port.
"""

Let's say we fix them to 12345 and 12346 for timing and control ports.

Then map the UDP ports in the docker configurations (I'm using docker compose, but passing args to plain docker command should also work)

ports:
  - 10.20.0.3:12345:12345/udp
  - 10.20.0.3:12346:12346/udp

(2) The announced RTSP uri bakes in the local address, which would be the docker internal one. We have to patch it to use the host IP here:

@property
def uri(self) -> str:
"""Return URI used for session requests."""
return f"rtsp://{self.connection.local_ip}/{self.session_id}"

Replace {self.connection.local_ip} to your hardcoded host IP.

Then it works :)

To make this more general, for (1) we don't have to change anything in pyatv, we just need to expose these options in the options flow in Home Assistant integration, for (2) we first need to make that configurable in pyatv (perhaps via another field in RaopSettings), and then expose such options in Home Assistant.

The HomeKit integration in Home Assistant already supports such configurations (advertise_ip https://www.home-assistant.io/integrations/homekit/#advertise_ip), so I think Hass would probably be open to having such options.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants