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

New input plugin: TCP stream #511

Open
badaix opened this issue Nov 28, 2019 · 32 comments
Open

New input plugin: TCP stream #511

badaix opened this issue Nov 28, 2019 · 32 comments

Comments

@badaix
Copy link
Owner

@badaix badaix commented Nov 28, 2019

I'm currently using MPD as input for snapcast (connected via pipe) and now I started playing with Mopidy, which seems to be more actively developed and more feature rich.
I've observed some strange audio loops during my Mopidy tests, using this setup GStreamer setup:

[audio]
output = audioresample ! audioconvert ! audio/x-raw,rate=48000,channels=2,format=S16LE ! wavenc ! filesink location=/tmp/snapfifo

I was wondering if there are GStreamer sinks that are more reliable and implemented a TCP stream that can be configured to act as client or as server, allowing the following setups:

Snapcast running in TCP server mode

snapcast.conf:

[stream]
stream = tcp://127.0.0.1?name=mopidy_tcp

the general pattern is: tcp://<IP of the listen interface>:<port>?name=<name>&mode=[client|server]
default for port (if omitted) is 4953, default for mode is server

mopidy.conf (running GStreamer in client mode)

[audio]
output = audioresample ! audioconvert ! audio/x-raw,rate=48000,channels=2,format=S16LE ! wavenc ! tcpclientsink

Snapcast running in TCP client mode

snapcast.conf:

[stream]
stream = tcp://127.0.0.1?name=mopidy_tcp&mode=client

in client mode the IP and port are the server's IP, port to connect to (default for port is again 4953)

mopidy.conf (running GStreamer in server mode)

[audio]
output = audioresample ! audioconvert ! audio/x-raw,rate=48000,channels=2,format=S16LE ! wavenc ! tcpserversink

If you want to give it a try: it's available in the current develop branch, any feedback is appreciated!
This would also solve #486 (well, at least it would work around it)

@uSpike

This comment has been minimized.

Copy link
Contributor

@uSpike uSpike commented Dec 2, 2019

Awesome! I think this would be very useful for streaming pulseaudio from a different machine on the network.

@slowphil

This comment has been minimized.

Copy link

@slowphil slowphil commented Dec 5, 2019

I'd love to test that on my Raspberry 4 running Raspbian Buster. I hope it could solve frequent drop-out of music when I interact with the desktop (for instance browsing internet).

Could you please update the building instructions for Raspbian? (in particular, Boost provided with Raspbian Buster seems too old...)

@badaix

This comment has been minimized.

Copy link
Owner Author

@badaix badaix commented Dec 5, 2019

I will do so tonight. You will just need header only boost libs, i.e. it's enough to download and unzip the latest boost and let make/cmake point to the directory. I think you can pass the boost path to cmake like this cmake -DBOOST_ROOT=<path to your unzipped boost root> (no guarantee, I have to cross check on my dev machine tonight).

@miLORD1337

This comment has been minimized.

Copy link

@miLORD1337 miLORD1337 commented Dec 7, 2019

Interesting. I'm using Mopidy since I first found out about Snapcast. I did not experience audible looping issues... How are you testing for that? Our GStreamer setup is the same.

@gerroon

This comment has been minimized.

Copy link

@gerroon gerroon commented Dec 9, 2019

I had terrible issues with Mopidy and Gstreamer, changing songs or radio channels meant delays, glitches, looping feedbacks etc then I moved to Mpd and it works great with SC. However I am interested in this imporvement as well.

@JayGatsby7

This comment has been minimized.

Copy link

@JayGatsby7 JayGatsby7 commented Dec 12, 2019

I'm currently using MPD as input for snapcast (connected via pipe) and now I started playing with Mopidy, which seems to be more actively developed and more feature rich.
I've observed some strange audio loops during my Mopidy tests, using this setup GStreamer setup:

[audio]
output = audioresample ! audioconvert ! audio/x-raw,rate=48000,channels=2,format=S16LE ! wavenc ! filesink location=/tmp/snapfifo

I was wondering if there are GStreamer sinks that are more reliable and implemented a TCP stream that can be configured to act as client or as server, allowing the following setups:

Snapcast running in TCP server mode

snapcast.conf:

[stream]
stream = tcp://127.0.0.1?name=mopidy_tcp

the general pattern is: tcp://<IP of the listen interface>:<port>?name=<name>&mode=[client|server]
default for port (if omitted) is 4953, default for mode is server

mopidy.conf (running GStreamer in client mode)

[audio]
output = audioresample ! audioconvert ! audio/x-raw,rate=48000,channels=2,format=S16LE ! wavenc ! tcpclientsink

Snapcast running in TCP client mode

snapcast.conf:

[stream]
stream = tcp://127.0.0.1?name=mopidy_tcp&mode=client

in client mode the IP and port are the server's IP, port to connect to (default for port is again 4953)

mopidy.conf (running GStreamer in server mode)

[audio]
output = audioresample ! audioconvert ! audio/x-raw,rate=48000,channels=2,format=S16LE ! wavenc ! tcpserversink

If you want to give it a try: it's available in the current develop branch, any feedback is appreciated!
This would also solve #486 (well, at least it would work around it)

Has anyone tried this? I run several clients and a server on raspberry pi's and have some issues with mopidy that don't seem to exist when using librespot - appreciate your good work on this software.

@uSpike

This comment has been minimized.

Copy link
Contributor

@uSpike uSpike commented Dec 30, 2019

I'm running with the latest dev commit, and the following configs:

stream = tcp://0.0.0.0:4953?name=Spotify_1
stream = tcp://0.0.0.0:4954?name=Spotify_2
stream = tcp://0.0.0.0:4955?name=Mopidy

Librespot is running on two separate docker containers and is forwarding FIFO to network via netcat:

nc ${TARGET_HOST} ${TARGET_PORT} </tmp/snapfifo &
librespot ... --backend pipe --device /tmp/snapfifo

and mopidy:

[audio]
output = audioresample ! audioconvert ! audio/x-raw,rate=48000,channels=2,format=S16LE ! wavenc ! tcpclientsink host=snapcast port=4955

It seems to work well :) I'll do more stress testing this week.

@fatg3erman

This comment has been minimized.

Copy link

@fatg3erman fatg3erman commented Jan 1, 2020

Ooh this looks good, just been discussing th issues Gstreamer has with fifos with the mopidy devs and one of them pointed me at this. I need to give this a try.

One thing is slightly confusing me though, is the nomenclature around client mode/server mode. I'd assume snapserver would always need to be in 'server mode' and snapclient in 'client mode' - but I'm not sure if that makes sense - and then mopidy in... what mode?

@fatg3erman

This comment has been minimized.

Copy link

@fatg3erman fatg3erman commented Jan 1, 2020

OK I managed to get it to build natively on a RaspberryPi 3B+ and the good news is it's working!

For anybody interested in trying, here's what I did. I'm no expert at editing MakeFiles so I just blundered around making edits until it built, so here's the full list of what I changed.

Download the latest libboost and unzip it somewhere. This will give you a directory we'll call <boost dir>

I only rebuilt the Snapcast server. So, in <snapcast dir>/server:

Edit CMakeLists.txt and find the SERVER_INCLUDE section and change it to

set(SERVER_INCLUDE
    ${CMAKE_SOURCE_DIR}/server
    ${CMAKE_SOURCE_DIR}/common)

list(APPEND SERVER_INCLUDE "<boost dir>")

Then edit Makefile:

Find the long line beginning CXXFLAGS += and change it to

CXXFLAGS += $(ADD_CFLAGS) -std=c++14 -Wall -Wextra -Wpedantic -Wno-unused-function -DBOOST_ROOT=<boost dir> -DBOOST_ERROR_CODE_HEADER_ONLY -DHAS_FLAC -DHAS_OGG -DHAS_VORBIS -DHAS_VORBIS_ENC -DHAS_OPUS -DVERSION=\"$(VERSION)\" -I. -I.. -I../common  -I<boost dir> 

and change the LDFLAGS line below it to

LDFLAGS  += $(ADD_LDFLAGS) -lvorbis -lvorbisenc -logg -lFLAC -lopus -latomic

Now do

make
sudo make install

In my /etc/snapserver.conf I have

stream = tcp://127.0.0.1?name=mopidy_tcp&port=4953

And in /etc/mopidy/mopidy.conf I have

output = audioresample ! audioconvert ! audio/x-raw,rate=48000,channels=2,format=S16LE ! wavenc ! tcpclientsink host=127.0.0.1 port=4953

Note that using 'localhost' in the mopidy.conf didn't work, I got errors saying GStreamer could not open the source, but using 127.0.0.1 fixed that. Wierd.

The only minor issue I now have is that the playback position reported by mopidy is about 5 seconds ahead of what's coming out of my speakers. I assume this is some buffer size thing in the tcp sink, it'd be interesting to know how to change that.

@gerroon

This comment has been minimized.

Copy link

@gerroon gerroon commented Jan 1, 2020

Can Snapcast run multiple modes at the same time? I still would like to be able to use fifo together with tcp

@uSpike

This comment has been minimized.

Copy link
Contributor

@uSpike uSpike commented Jan 2, 2020

@gerroon you can define multiple streams that have different sources (fifo, tcp)

@KraigoMpls

This comment has been minimized.

Copy link

@KraigoMpls KraigoMpls commented Jan 2, 2020

FWIW, the Mopidy team just dropped version 3.0.1 eleven days ago. I haven't had a chance to play with it yet. It requires Python 3.7 I've read and it uses a much more recent version of GStreamer, 1.14.0. This might be enough to resolve your issue. I'm planning on playing with it tonight. I got all of my backups in line this morning so I can step back if necessary, but I'm really excited about it.

https://mopidy.com/blog/2019/12/22/mopidy-3.0/

KO

@fatg3erman

This comment has been minimized.

Copy link

@fatg3erman fatg3erman commented Jan 2, 2020

I'm already running it. It doesn't solve the issue :) The Mopidy devs say that GStreamer and FIFOs don't play well together. They did try to make a fifo output within Mopidy some time ago but again, GStreamer makes it hard. This new TCP sink is the way forward.

@KraigoMpls

This comment has been minimized.

Copy link

@KraigoMpls KraigoMpls commented Jan 2, 2020

My biggest concern is that the configuration seems to rely on static IP addresses, which I spent a few days getting rid of in favor of DHCP when I went to OpenWRT a month or two ago . I really want to stay dynamic. My implementation of the static IP addresses was ugly and I'd really prefer to not step back to it.

@fatg3erman

This comment has been minimized.

Copy link

@fatg3erman fatg3erman commented Jan 2, 2020

Well if you're using it in place of a fifo - which you can't use over a network anyway - then the only IP address you need is 127.0.0.1, which is 'local loopback' and requires no configuration.

@KraigoMpls

This comment has been minimized.

Copy link

@KraigoMpls KraigoMpls commented Jan 2, 2020

TYVM!

@uSpike

This comment has been minimized.

Copy link
Contributor

@uSpike uSpike commented Jan 3, 2020

I've been testing this all night, and it's working great with a bunch of streams. I've been able to separate my services (multiple librespot instances, mopidy, etc) and the FIFO mess is gone!

I've also been able to stream the audio from my PC to snapcast from pulseaudio.

Config:

stream = tcp://0.0.0.0:4956?name=PC

On my PC I pipe pulseaudio to netcat:

$ parec --rate=48000 --format=s16le --channels=2 | nc snapserver.home 4956

Does anyone know of a better way to have pulseaudio send raw PCM over a network to a host?

@uSpike

This comment has been minimized.

Copy link
Contributor

@uSpike uSpike commented Jan 3, 2020

@badaix I have one suggestion... instead of tcp://...&mode=[client|server] how about tcp-server://... and tcp-client://...? I think it'd be a lot more immediately clear what's going on there.

@natumbri

This comment has been minimized.

Copy link

@natumbri natumbri commented Jan 11, 2020

Another confirmed working: the develop branch builds just fine on Armbian (ubuntu bionic) after manually installing the ubuntu libboost1.71-dev package (https://launchpad.net/ubuntu/+source/boost1.71).

And the tcp stream works great with mopidy 3.0.

Thanks!

@fatg3erman very early testing suggests something seems to be up with the RompR snapcast volume control when using the TCP stream. Here's the output I get:

Screenshot_20200112-072805

RompR v1.30

Cheers
Nik

@eoware

This comment has been minimized.

Copy link

@eoware eoware commented Jan 25, 2020

If I understand this correctly, I could run a stream in server mode, and on another machine netcat a PCM stream to the IP/PORT set in the stream, is that right? This is great timing as I've been looking for a way to easily stream a PCM capture from a remote device.

@uSpike

This comment has been minimized.

Copy link
Contributor

@uSpike uSpike commented Jan 26, 2020

@eoware yes, correct.

@JayGatsby7

This comment has been minimized.

Copy link

@JayGatsby7 JayGatsby7 commented Jan 28, 2020

I'm running with the latest dev commit, and the following configs:

stream = tcp://0.0.0.0:4953?name=Spotify_1
stream = tcp://0.0.0.0:4954?name=Spotify_2
stream = tcp://0.0.0.0:4955?name=Mopidy

Librespot is running on two separate docker containers and is forwarding FIFO to network via netcat:

nc ${TARGET_HOST} ${TARGET_PORT} </tmp/snapfifo &
librespot ... --backend pipe --device /tmp/snapfifo

and mopidy:

[audio]
output = audioresample ! audioconvert ! audio/x-raw,rate=48000,channels=2,format=S16LE ! wavenc ! tcpclientsink host=snapcast port=4955

It seems to work well :) I'll do more stress testing this week.

can't seem to get this to work with librespot (raspotify) -- could be my lack of understanding about netcat . . . I'm running librespot on the same server as the snapserver.... TCP seems to work ok with mopidy.

@uSpike

This comment has been minimized.

Copy link
Contributor

@uSpike uSpike commented Jan 28, 2020

can't seem to get this to work with librespot (raspotify) -- could be my lack of understanding about netcat . . . I'm running librespot on the same server as the snapserver.... TCP seems to work ok with mopidy.

@JayGatsby7 can you provide more information?

@JayGatsby7

This comment has been minimized.

Copy link

@JayGatsby7 JayGatsby7 commented Jan 28, 2020

can't seem to get this to work with librespot (raspotify) -- could be my lack of understanding about netcat . . . I'm running librespot on the same server as the snapserver.... TCP seems to work ok with mopidy.

@JayGatsby7 can you provide more information?

  1. Do you have a pipe open on snapserver.conf for tmp/snapfifo? Or just the setting for TCP?

  2. Netcat configs in your example. Are you putting those parameters in the librespot / raspotify config?

I’m using mopidy with tcp for the time being but would like to get librespot working with tcp on a separate port as well.

@badaix

This comment has been minimized.

Copy link
Owner Author

@badaix badaix commented Jan 28, 2020

My biggest concern is that the configuration seems to rely on static IP addresses, which I spent a few days getting rid of in favor of DHCP when I went to OpenWRT a month or two ago . I really want to stay dynamic. My implementation of the static IP addresses was ugly and I'd really prefer to not step back to it.

@KraigoMpls: I will keep this in mind for the next version. I already started to implementing host resolution, but removed it for this first version of the tcp plugin. I think for most users the audio source will run on the same host. You can open a feature request for this, so that I will not forget about it.

@badaix

This comment has been minimized.

Copy link
Owner Author

@badaix badaix commented Jan 28, 2020

@badaix I have one suggestion... instead of tcp://...&mode=[client|server] how about tcp-server://... and tcp-client://...? I think it'd be a lot more immediately clear what's going on there.

@uSpike: valid suggestion, could be done together with the client host resolution feature. Can you also open a feature request for this?

@languitar

This comment has been minimized.

Copy link

@languitar languitar commented Jan 28, 2020

I've now also tested this with mopidy and things work fine apart from one fact: If I stop music on mopidy or switch the song, this sometimes takes ~ 30 seconds to propagate to the snapclient instances. Any ideas on that? Sounds a like the buffer is too large.

@badaix

This comment has been minimized.

Copy link
Owner Author

@badaix badaix commented Jan 28, 2020

@languitar GStreamer's tcpserversink has the option buffers-max, maybe this could help.
What happens when you stop/start playing? Will the delay be reset? Does it grow over time?
What is your gstreamer config (your [audio] output)?

Remark: that's the inherent problem with TCP: it doesn't drop. I was initially playing with the UDP client/server sinks, but had lots of dropped packets, this would require a dedicated reader thread and packet queue.

@languitar

This comment has been minimized.

Copy link

@languitar languitar commented Jan 28, 2020

@languitar GStreamer's tcpserversink has the option buffers-max, maybe this could help.

I haven't configured anything special here so far.

What happens when you stop/start playing? Will the delay be reset? Does it grow over time?

It starts playing more or less immediately. Seems to accumulate over some time, though.

What is your gstreamer config (your [audio] output)?

The basic config that I have found in the docs / issues:

output = audioresample ! audioconvert ! audio/x-raw,rate=48000,channels=2,format=S16LE ! wavenc ! tcpclientsink host=127.0.0.1 port=3333

Remark: that's the inherent problem with TCP: it doesn't drop. I was initially playing with the UDP client/server sinks, but had lots of dropped packets, this would require a dedicated reader thread and packet queue.

I don't think dropping packages here is needed as long as the buffer size is controlled somehow.

@Uatschitchun

This comment has been minimized.

Copy link

@Uatschitchun Uatschitchun commented Jan 30, 2020

Does anyone know of a better way to have pulseaudio send raw PCM over a network to a host?

I'd be interested in this, too, as I stream from a local kodi instance (which needs pulseaudio). Or is it possible to create a tcp sink to use this new plugin?

@parameter-pollution

This comment has been minimized.

Copy link

@parameter-pollution parameter-pollution commented Feb 3, 2020

Does anyone have problems with seeking when using tcpclientsink ?

I have mopidy 3.0.1, snapserver 0.18.1 and tried the tcpclientsink, but when I seek using any mpd/webclient the audio immediately stops, does nothing for ~1min (snapclient is not receiving any samples) and then starts playing at the seeked time.

It looks like a problem with gstreamer, but I even upgraded it to gstreamer 1.16.2 and had the same problem ( https://discourse.mopidy.com/t/seeking-takes-a-very-long-time-1min/ )

@fatg3erman

This comment has been minimized.

Copy link

@fatg3erman fatg3erman commented Feb 3, 2020

Does anyone have problems with seeking when using tcpclientsink ?

I have mopidy 3.0.1, snapserver 0.18.1 and tried the tcpclientsink, but when I seek using any mpd/webclient the audio immediately stops, does nothing for ~1min (snapclient is not receiving any samples) and then starts playing at the seeked time.

It looks like a problem with gstreamer, but I even upgraded it to gstreamer 1.16.2 and had the same problem ( https://discourse.mopidy.com/t/seeking-takes-a-very-long-time-1min/ )

Yep same problem here. I was going to look into debugging on the Mopidy end when I get some time.

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

Successfully merging a pull request may close this issue.

None yet
You can’t perform that action at this time.