Stream multiple docker container audio - docker

I want to run multiple docker containers on a machine and stream the audio via RTP. I can run apps with sound with this command:
sudo docker run -it \
--device /dev/snd \
-e PULSE_SERVER=unix:${XDG_RUNTIME_DIR}/pulse/native \
-v ${XDG_RUNTIME_DIR}/pulse/native:${XDG_RUNTIME_DIR}/pulse/native \
-v ~/.config/pulse/cookie:/root/.config/pulse/cookie \
--group-add $(getent group audio | cut -d: -f3) \
ubuntu:16.04 /bin/bash
It works fine and when i play any sound in the container it plays and i can hear it and i can stream it with FFmpeg. The problem is when i run multiple containers and play any sound from any container, it seems they are using the same sink in pulseaudio server so i can't separate them and each stream client hear all the sounds from all the running containers. How can i achieve my goal?
My FFmpeg Stream code:
ffmpeg -f alsa -i pulse -acodec libmp3lame -ab 128k -ac 2 -ar 48000 -c:a libopus -f rtp rtp://$IP:4001

Untested, but it looks as if you are using alsa instead of pulse, and also using two codecs (-acodec and -a:c are the same)
Can you try:
ffmpeg -f pulse -i default -ab 128k -ac 2 -c:a libopus -f rtp rtp://$IP:400
Also, if you're trying to run pulse audio inside a docker container, you might need to start the pulseaudio daemon:
# your docker apt-get:
apt-get install -y pulseaudio
# your entrypoint/command script:
pulseaudio &
# or, if using root (dont)
pulseaudio --system &

Related

How to run ffmpeg to record a virtual screen from a different docker container

I using 2 docker containers to manage my application, one of them is for the application itself and it's rendered on a virtual screen using XVFB and the another one have ffmpeg which supposed to record this screen, so I managed to get them work together if both the application and ffmpeg on the same docker container but don't know how to do it if they're on a different docker containers.
so in main container I run
Xvfb :99 -ac -screen +extension RANDR -1920x1080x24 -nolisten tcp &
and on the ffmpeg container I run
ffmpeg -video_size 1024x768 -framerate 25 -f x11grab -i :99+100,200 output.mp4
I tried to change the host to application_container_name:99+100,200 and tried to use the ip address of the maintain application like 172.17.0.2:99+100,200 but both didn't work.
Check if you are able to access the x11 port of container_1 from the FFmpeg container
if it is accessible
then in the FFmpeg container, you can start
ffmpeg -y -f x11grab -video_size 1360x1020 -r 15 -i ${host}:${DISPLAY_NUM}.0 -codec:v libx264 -preset ultrafast -pix_fmt yuv420p record1.mp4
Here ${host} is your container_1 name
${DISPLAY_NUM} is the display of container_1
To validate it, in container_1 run echo $DISPLAY

Is it possible to host multiple aler9/rtsp-simple-server on the same machine

I am using this command to start a server on my linux machine:
docker run -d --rm -it --network=host aler9/rtsp-simple-server
And this command to connect an rtsp stream
docker run -v $(pwd):$(pwd) --network=host
linuxserver/ffmpeg:arm64v8-latest -re -stream_loop -1 -i
$(pwd)/sample.mp4 -c copy -f rtsp rtsp://localhost:8554/mystream
Is it possible to start a second rtsp server and connect rtsp streams to this second server.
What I am trying to do is to simulate multiple cameras with one sub stream for each camera
Try running multiple rtsp servers like so:
docker run --rm -it -e RTSP_PROTOCOLS=tcp -p 8554:8554 -p 1935:1935 aler9/rtsp-simple-server
docker run --rm -it -e RTSP_PROTOCOLS=tcp -p 8555:8554 -p 1936:1935 aler9/rtsp-simple-server
docker run --rm -it -e RTSP_PROTOCOLS=tcp -p 8556:8554 -p 1937:1935 aler9/rtsp-simple-server
and connect like so:
# Connecting to first server
docker run -v $(pwd):$(pwd) --network=host linuxserver/ffmpeg:arm64v8-latest -re -stream_loop -1 -i
$(pwd)/sample.mp4 -c copy -f rtsp rtsp://localhost:8554/mystream
# Connecting to second server
docker run -v $(pwd):$(pwd) --network=host linuxserver/ffmpeg:arm64v8-latest -re -stream_loop -1 -i $(pwd)/sample.mp4 -c copy -f rtsp rtsp://localhost:8555/mystream
# Connecting to third server
docker run -v $(pwd):$(pwd) --network=host linuxserver/ffmpeg:arm64v8-latest -re -stream_loop -1 -i $(pwd)/sample.mp4 -c copy -f rtsp rtsp://localhost:8556/mystream
This solution basically uses docker port mapping and map each server to diffrent ports so they won't colide. According to aler9/rtsp-simple-server port mapping is working for tcp and might not work for udp.
Solution for udp will require more investigation.

Why UDP port fails only in Docker?

I've got the following docker that streams out some sample sine wav via UDP port 1234:
from ubuntu
RUN apt update
RUN apt install -y ffmpeg
EXPOSE 1234/udp
CMD ffmpeg -re -f lavfi -i aevalsrc="sin(400*2*PI*t)" -ar 8000 -f mulaw -f rtp rtp://localhost:1234
I run the container using:
docker run -p 127.0.0.1:1234:1234/udp xxxx
Now I try to open VLC and play that stream from my host machine using the stream URL rtp://#:1234 as instructed by VLC. It plays nothing. Silence. However, if I run the same ffmpeg cmd from the host machine, it works and I can hear the sample.
Any ideas what's going on?
Found the problem. I guess I misunderstood RTP. ffmpeg is expected to send the RTP packets to a "server" (peer, actually) outside the container (i.e. my host machine). It was not "serving" the sample audio like an RTSP server...
So I don't need to expose any ports. The problem was the rtp url on the container side - it should target the host machine's IP:
ffmpeg -re -f lavfi -i aevalsrc="sin(400*2*PI*t)" -ar 8000 -f mulaw -f rtp rtp://$HOST_MACHINE_IP:1234
And its the VLC app that is actually the peer. Once I do it, VLC can receive the stream - no problem.

Docker : sharing /dev/snd on multiple containers leads to "device or resource busy"

When adding host device (--device /dev/snd) to a Docker container, I sometimes encounter Device or resource busy errors.
Example
I have reproduced the issue with a minimal example involving audio (alsa). Here's my Dockerfile (producing an image 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/*
I am running the following command (speaker-test is a tool to generate a tone that can be used to test the speakers), with /dev/snd shared :
docker run --rm \
-i -t \
--device /dev/snd \
docker-device-example \
speaker-test
Issue
When running the previous command, a pink noise is played, but only under some conditions :
if I am not playing any sound on my host : for example, if I'm playing a video, and that even if the video is paused, the command fails
if I am not running another container accessing the /dev/snd device
It looks like the /dev/snd is "locked" when used, and if that is the case, I got the following output (the error is represented by the last 2 lines) :
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
And, vice versa, if the pink noise is played (on the container), then I cannot play any sound on my host (Ubuntu). But commands on my host does not fail with the same message. Instead, the command on the host (like aplay test.wav to play a simple sound) is blocked indefinitely (even when the container is shutdown afterwards).
I tried to debug by running strace aplay test.way, and the command seems to be blocked on the poll system call :
poll([{fd=3, events=POLLIN|POLLERR|POLLNVAL}], 1, 4294967295
Question
How can I play sounds from 2 (or more) different containers, or from my host and a container, at the same time?
Additional info
I've reproduced the issue with /dev/snd, but I don't know if similar things happen when using other devices, or if it's just related to sound devices or to alsa.
Note also that when running multiple speaker-test or aplay commands simultaneously and all on my host (no containers involved), then all sounds are played.
I can't tell how to solve this with ALSA, but can provide 2 possible ways with pulseaudio. If these setups fail, install pulseaudio in image to make sure dependencies are fullfilled.
ALSA directly accesses sound hardware and blocks access to it for other clients. But it is possible to set up ALSA to serve multiple clients. That has to be answered by someone else. Probably some ALSA dmix plugin setup is the way to go.
Pulseaudio with shared socket:
Create pulseaudio socket:
pactl load-module module-native-protocol-unix socket=/tmp/pulseaudio.socket
Create /tmp/pulseaudio.client.conf for pulseaudio clients:
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
Share socket and config file with docker and set environment variables PULSE_SERVER and PULSE_COOKIE. Container user must be same as on host:
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
The cookie will be created by pulseaudio itself.
Pulseaudio over TCP:
Get IP address from host:
# 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)"
Run docker image. You need a free TCP port, here 34567 is used.
(TCP port number must be in range of cat /proc/sys/net/ipv4/ip_local_port_range and must not be in use. Check with ss -nlp | grep 34567.)
docker run --rm \
--name pulsecontainer \
--env PULSE_SERVER=tcp:$Hostip:34567 \
imagename
After docker run get IP of container with:
Containerip="$(docker inspect --format '{{ .NetworkSettings.IPAddress }}' pulsecontainer)"
Load pulseaudio TCP module authenticated with container IP:
pactl load-module module-native-protocol-tcp port=34567 auth-ip-acl=$Containerip
Be aware that the TCP module is loaded after container is up and running. It takes a moment until pulseaudio server is available for container applications.
If TCP connection fails, check iptables and ufw settings.
A How-To summarizing these setups: https://github.com/mviereck/x11docker/wiki/Container-sound:-ALSA-or-Pulseaudio

Stdout to memory python

I wanted to stream the raspivid using VLC. So I followed the below link
raspivid -o - -t 9999999 -w 1280 -h 1024 -b 500000 -fps 20|cvlc -vvv stream:///dev/stdin --sout '#rtp{sdp=rtsp://:8080/}' :demux=h264
Now I wanted to record the streamed result in vlc with
cvlc -vvv stream:///rtsp://0.0.0.0:8554/}' --sout=file/ps:record.mpg
Am I doing it right? Alternative approach/methods appreciated.

Resources