adding host device(--device /dev/snd
)到Docker容器时,有时会遇到Device or resource busy
错误。
我通过一个涉及音频(alsa
)的最小示例重现了该问题。这是我的Dockerfile
(产生图像docker-device-example
):
FROM debian:buster
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
alsa-utils \
&& rm -rf /var/lib/apt/lists/*
我正在运行以下命令(speaker-test
是用于生成可用来测试扬声器的音调的工具),并且共享了/dev/snd
:
docker run --rm \
-i -t \
--device /dev/snd \
docker-device-example \
speaker-test
运行上一个命令时,会播放粉红声音,但仅在某些情况下:
/dev/snd
设备使用/dev/snd
时似乎被“锁定”了,如果是这样,我得到以下输出(错误由后两行表示):
speaker-test 1.1.6
Playback device is default
Stream parameters are 48000Hz, S16_LE, 1 channels
Using 16 octaves of pink noise
ALSA lib pcm_dmix.c:1099:(snd_pcm_dmix_open) unable to open slave
Playback open error: -16,Device or resource busy
反之亦然,如果在容器上播放了粉红色的声音,那么我将无法在主机上播放任何声音(Ubuntu)。但是主机上的命令不会因相同的消息而失败。取而代之的是,主机上的命令(例如aplay test.wav
播放简单的声音)会无限期地被阻止(即使随后关闭容器)。
我尝试通过运行strace aplay test.way
进行调试,并且该命令似乎在poll
系统调用中被阻止:
poll([{fd=3, events=POLLIN|POLLERR|POLLNVAL}], 1, 4294967295
如何从两个(或更多)不同的容器中,或从主机和容器中同时播放声音?
我已经用/dev/snd
重现了该问题,但我不知道在使用其他设备时是否发生了类似的事情,或者是否与声音设备或alsa
有关。
还请注意,当同时在主机上全部同时运行多个speaker-test
或aplay
命令(不涉及容器)时,将播放所有声音。
答案 0 :(得分:4)
我不知道如何使用ALSA解决此问题,但是可以提供2种可能的方法来使用Pulseaudio。如果这些设置失败,请在映像中安装pulseaudio
,以确保完全满足依赖关系。
ALSA直接访问声音硬件,并阻止其他客户端对其进行访问。但是可以设置ALSA服务于多个客户端。那必须由其他人来回答。可能需要进行一些ALSA dmix plugin设置。
创建pulseaudio套接字:
pactl load-module module-native-protocol-unix socket=/tmp/pulseaudio.socket
为Pulseaudio客户端创建/tmp/pulseaudio.client.conf
:
default-server = unix:/tmp/pulseaudio.socket
# Prevent a server running in the container
autospawn = no
daemon-binary = /bin/true
# Prevent the use of shared memory
enable-shm = false
与docker共享套接字和配置文件,并设置环境变量PULSE_SERVER
和PULSE_COOKIE
。容器用户必须与主机上的用户相同:
docker run --rm \
--env PULSE_SERVER=unix:/tmp/pulseaudio.socket \
--env PULSE_COOKIE=/tmp/pulseaudio.cookie \
--volume /tmp/pulseaudio.socket:/tmp/pulseaudio.socket \
--volume /tmp/pulseaudio.client.conf:/etc/pulse/client.conf \
--user $(id -u):$(id -g) \
imagename
cookie将由Pulseaudio本身创建。
从主机获取IP地址:
# either an arbitrary IPv4 address
Hostip="$(ip -4 -o a | awk '{print $4}' | cut -d/ -f1 | grep -v 127.0.0.1 | head -n1)"
# or especially IP from docker daemon
Hostip="$(ip -4 -o a| grep docker0 | awk '{print $4}' | cut -d/ -f1)"
运行docker映像。您需要一个免费的TCP端口,此处使用34567
。
(TCP端口号必须在cat /proc/sys/net/ipv4/ip_local_port_range
范围内,并且不能使用。请检查ss -nlp | grep 34567
。)
docker run --rm \
--name pulsecontainer \
--env PULSE_SERVER=tcp:$Hostip:34567 \
imagename
docker run
获得以下容器的IP:
Containerip="$(docker inspect --format '{{ .NetworkSettings.IPAddress }}' pulsecontainer)"
加载已通过容器IP验证的pulseaudio TCP模块:
pactl load-module module-native-protocol-tcp port=34567 auth-ip-acl=$Containerip
请注意,在容器启动并运行后 会加载TCP模块。直到Pulseaudio服务器可用于容器应用程序为止,需要一些时间。
如果TCP连接失败,请检查iptables
和ufw
设置。
总结这些设置的方法:https://github.com/mviereck/x11docker/wiki/Container-sound:-ALSA-or-Pulseaudio