PDA

View Full Version : shairport-sync pipe to LMS



Noel Hibbard
2015-08-29, 05:42
The latest version of shairport-sync has a new pipe backend. To get this new backend you have to use the --with-pipe config flag before compiling. If you compile with the pipe option you would then use a command line like this to pipe the output:
shairport-sync -o pipe -- /tmp/airpipe

The pipe contains a stream of raw PCM. My question is, can LMS open a pipe?

For those that are not familiar with shairport-sync. It is an AirPlay emulator. My goal is to use stream from my iPhone to shairport-sync which then pipes to LMS and then from LMS I can play the pipe to a sync group and have music in sync to multiple rooms.

https://github.com/mikebrady/shairport-sync

Noel Hibbard
2015-08-29, 21:31
I installed the WaveInput plugin and then modified the custom-convert.conf to simply use this command line:
[cat] $FILE$

So now I open this URL in LMS:
wavin:/tmp/airpipe

It played the AirPlay stream from my phone and I can create a sync group to a few rooms and it stays in sync. The only problem is if I press pause on my phone the audio stops within about 2 secs but then when I unpause it takes LMS about 30 secs to start playing.

At least I am getting somewhere. I think an experienced plugin dev could make a really good plugin with shairport-sync. Shairport-sync can also output metadata to a pipe. So a plugin should be able to read this pipe to get the track info, album art and track position.

bpa
2015-08-30, 01:44
I think you have gone back to the original shairport solution for SB which used pipes and wavin. There were problems with a pipe/wavin solution that the second verison of shairport "fixed" but lost SB player sync.

Pipes have problems - if you don't read from them (I.e. when youpause) they buffer data and the writing process is then blocked when buffer becomes full - some writers can handle this others die.

Also some version of waveinput have an option to stop wavin process when pause is pressed as otherwsaie buffer becomes full and when unpaused there is a long delay between what is played.

Noel Hibbard
2015-08-30, 05:38
Yes I have sort of gone back to the old method only I am not actually capturing the sound card output and the total lag is only 2 secs. I should be able to drop it even more with the buffer settings in shairport-sync. The old wavein/shairport rig had about a 10 sec lag.

Shairport-sync doesn't die when the pipe isn't being read so that is good. Right now the only problem I have is that when I change tracks, seek or pause the AirPlay stream the flow of data to the pipe ends and LMS seems to get lost and I have to pause/play LMS to make it pick back up. I have put in a request with the developer of shairport-sync to pad with PCM silence when ever there isn't an active AirPlay connection or when pausing and seeking. So LMS should keep on playing.

I like the newer shairport2 plugin but not supporting sync groups is a deal breaker for me.

bpa
2015-08-30, 13:25
Did you check for a Waveinput plugin setting ?

You need to figure what is dying in order to fix this. Is the problem on the LMS side or on the pipe writer side ?

I would have thought using something like "dd" would be better than "cat" for the modified wavinpuit as it would provide better control of no of buffers and buffer size being used with the pipe - it also may provide better error handling.

Noel Hibbard
2015-08-30, 17:18
It seems to be the pipe writer that stops writing data when you pause the AirPlay stream or change tracks. I didn't think about using dd. I will give that a shot.

Btw, when I say pause the AirPlay stream I am not talking about pausing from LMS. I am talking about pausing on my iOS device.

bpa
2015-08-31, 04:27
It seems to be the pipe writer that stops writing data when you pause the AirPlay stream or change tracks. I didn't think about using dd. I will give that a shot.

Btw, when I say pause the AirPlay stream I am not talking about pausing from LMS. I am talking about pausing on my iOS device.

If the problem is writer dying on shairport then dd or anything on reader side won't help. The writer code needs to be fixed - does pipe fail/close when pause or does writer process fail ?

Noel Hibbard
2015-08-31, 07:06
The pipe doesn't close. It's just the flow of data stops. For example if you just cat directly to the console you see the stream of data on the screen. As soon as you pause the AirPlay stream the flow of data stops but cat doesn't terminate. Then when you unpause the AirPlay stream the data starts flowing on the screen again. It just seems like LMS doesn't expect the data to stop like that. When I unpause the AirPlay stream LMS does eventually start playing again but it takes about 30secs to do so and by then it's lagged way behind. If shairport-sync could be modified to pad the pipe with silence I think LMS would keep on going. But I suspect this isn't a simple two line change for the shairport-sync dev.

bpa
2015-08-31, 13:13
The pipe doesn't close. It's just the flow of data stops. For example if you just cat directly to the console you see the stream of data on the screen. As soon as you pause the AirPlay stream the flow of data stops but cat doesn't terminate. Then when you unpause the AirPlay stream the data starts flowing on the screen again. It just seems like LMS doesn't expect the data to stop like that. When I unpause the AirPlay stream LMS does eventually start playing again but it takes about 30secs to do so and by then it's lagged way behind. If shairport-sync could be modified to pad the pipe with silence I think LMS would keep on going. But I suspect this isn't a simple two line change for the shairport-sync dev.

You really should look back at the shairport development - I'm pretty sure this is the same issue the original developer encountered. The problem of having to keep an Airport connection going even though there may not be a LMS stream reading it. Using generic Pipe and cat will never give you enough control over the stream - the application receiving the airport stream has to specific for LMS to know when to forward audio stream, when to insert "silence" stream and when to discard audio stream.

I think you'll be better off trying to understand why the current plugin cannot do sync as otherwsie you seem to be treading the same as the original developer and you may end up with a similar solution.

MikeBrady
2015-09-21, 08:47
You really should look back at the shairport development - I'm pretty sure this is the same issue the original developer encountered. The problem of having to keep an Airport connection going even though there may not be a LMS stream reading it. Using generic Pipe and cat will never give you enough control over the stream - the application receiving the airport stream has to specific for LMS to know when to forward audio stream, when to insert "silence" stream and when to discard audio stream.

I think you'll be better off trying to understand why the current plugin cannot do sync as otherwsie you seem to be treading the same as the original developer and you may end up with a similar solution.

Hi there. I'm the developer of Shairport Sync and, thinking about this issue, I wonder if you have the problem the wrong way around. If LMS were to stop reading data from the Shairport Sync pipe, the data would simply be discarded by Shairport Sync; the pipe is not disabled or shut down. The actual problem occurs when the flow of data from Shairport Sync naturally pauses. It seems the the LMS system is unable to deal with a pause in the flow of data, so a hack is necessary whereby the source somehow has to pad out a real gap with packets of silence. Is there any possibility that LMS could deal more gracefully with such pauses?

Best wishes
Mike

Noel Hibbard
2015-09-24, 22:04
Hi there. I'm the developer of Shairport Sync and, thinking about this issue, I wonder if you have the problem the wrong way around. If LMS were to stop reading data from the Shairport Sync pipe, the data would simply be discarded by Shairport Sync; the pipe is not disabled or shut down. The actual problem occurs when the flow of data from Shairport Sync naturally pauses. It seems the the LMS system is unable to deal with a pause in the flow of data, so a hack is necessary whereby the source somehow has to pad out a real gap with packets of silence. Is there any possibility that LMS could deal more gracefully with such pauses?

Best wishes
Mike

This is exactly what I requested on your github page. It is a bit hacky I agree and I understand why you declined the request.

mattclayton
2017-04-17, 12:06
I have been looking at this problem, I can't seem to find any other people struggling with it or any decent solutions to it - the issue still exists that if you give the pipe from shairport-sync to the waveinput plugin you get terrible lag between tracks and skips, volume changes, etc.

By hexdump-ing the pipe I noticed it's padded with lots of null bytes when pressing pause or skipping track - I didn't look into if this was shairport trying to keep sync or just how airplay works but I knocked together some noddy python to test my theory: if i just throw away any big reads of null bytes, will this improve the lag?

The answer is yes: It's not perfect but it reduces the lag between skip track and it actually playing the new track on the squeezebox client to a few seconds (2-3 ish).

The interesting thing about these null bytes is that they also come in much quicker than the actual audio data, so, even if it only read the null bytes for 1 second, there seems to be around 30 seconds of 'null' if you attempt to play it (i'm guessing the 0 bytes are valid PCM data - effectively just silence).. hence the huge lags between track skips in LMS.

Here is my proof of concept python - this file is called pipe.py in my WaveInput plugin folder (/var/lib/squeezeboxserver/cache/InstalledPlugins/Plugins/WaveInput/)


import sys
import os

pipein = open("/tmp/shairport.raw", 'r') # this is the name of my pipe

while True:
r=pipein.read(64*1024) # 64 KB - not played with this value just picked something..
r=bytearray(r)
out = False
# For every byte in the buffer, if any bytes are not zero then send them out, if not, skip them..
for bt in r:
if bt != 0:
out = True
if out:
os.write(sys.stdout.fileno(),r)


My WaveInput conf /var/lib/squeezeboxserver/cache/InstalledPlugins/Plugins/WaveInput/custom-convert.conf becomes;



#
# wavin
#
wavin pcm * *
# R
[python] /var/lib/squeezeboxserver/cache/InstalledPlugins/Plugins/WaveInput/pipe.py


It's not a perfect solution but it narrows in on what seems to be the issue.

with this knowledge writing a bit of C would be more efficient, but perhaps if it is shairport-sync introducing the null bytes a solution could be an option to remove any padding null bytes (if there isn't already a way to do it)..

I'm still investigating..

Just background to why I want this.. my goal is to allow anyone with an iDevice to play to all rooms current 'enabled' in squeezebox - basically making multiroom easy for guests (and my partner). I set up the room configuration but then they can just use it by connecting to the 'Multiroom' airplay device.. The good thing is that because shairport-sync alters the volume and this is encoded in the PCM output, I can set the max and relative volumes in the squeezebox app for each room and then anyone messing will the volume on their iDevice will have a limit how loud they can go and also the volume will increase/decrease relatively in each room.

philippe_44
2017-04-29, 13:06
I'm still investigating..

Just background to why I want this.. my goal is to allow anyone with an iDevice to play to all rooms current 'enabled' in squeezebox - basically making multiroom easy for guests (and my partner). I set up the room configuration but then they can just use it by connecting to the 'Multiroom' airplay device.. The good thing is that because shairport-sync alters the volume and this is encoded in the PCM output, I can set the max and relative volumes in the squeezebox app for each room and then anyone messing will the volume on their iDevice will have a limit how loud they can go and also the volume will increase/decrease relatively in each room.

If I understand well what you want to do, the ShairTunes2 plugin should do the work