Shairport-sync: Command to pause/stop Shairport-sync playback?

Created on 31 Aug 2014  Â·  51Comments  Â·  Source: mikebrady/shairport-sync

I have been looking for a shell command I can use to pause/stop Shairport-sync playback similar to mpc's pause/stop command for mpd, http://volumio.org/forum/shairport-and-mpd-won-get-along-t332.html

Do you know if there is such command available?
I have searched around but found nothing that can be used on Shairport and/or Shairport-sync.

I know I can kill or restart Shairport-sync by command but that is not a good idea if I am going to run this command everytime another app, e.g. mpd plays sound.

enhancement question

Most helpful comment

I'm very interested in this feature too.

I've found on Google this blog post that describes how DACP works (mostly it's the same information on the Unofficial Specification) and provides a proof-of-concept DACP client. I've done some testing and I've been able to reliably use it to control my devices so I think that's the way to go.

From what I saw in my testing I think I found out why your code sometimes didn't work: the port used by iTunes on Mac for DACP seems to be fixed to 3689 (which is the one you hardcoded in your code), while iOS uses a different ephemeral port for every AirPlay session (you can verify this with avahi-browse _dacp._tcp -r). It looks like a mdns lookup is definitely required to implement this feature.

Also, while the Unofficial Specification says that the DACP-ID header is used to advertise DACP support, AirPlay with system-wide audio on Mac includes it even if it doesn't support remote controls. The only way to reliably detect if DACP is supported or not is to try and resolve the Bonjour service.

All 51 comments

Hmm. It's not something I've thought about, TBH. One could add such a command, but what would executing it look like to, say, an iTunes client? It seems to me it would look like the Shairport Sync server had crashed or suddenly disappeared. Would that be acceptable?

On some wireless speaker product that have such functionality, our testing shows that the airplay emulator application (e.g. shairport, we don't know what they are using and console access is obviously not possible on their hardware,), it will behave exactly the same as if the user have pressed the pause/stop button, i.e. Itune client will show paused.

The test scenario is that when airplay is playing, the device receives input from another audio application for playback. Airplay pauses/stop automatically. I have no idea how they did it but I assume the other audio application sent a pause/stop command to airplay server, similar to the mpc command for mpd.

I don't see such command available for shairport but I am inclined to believe they are using Shairport.

"it would look like the Shairport Sync server had crashed or suddenly disappeared."
Not ideal unless there is no better alternative. Would this way be easy/quick to code though?

This might be significant: http://nto.github.io/AirPlay.html#audio-remotecontrol. Just having a quick look at it, it should be possible to send a "pause" command back to the audio source, and this would cause the shairport player to exit after a couple of seconds of inactivity, triggering the -E action. Would that be useful? (It would take a bit of work, BTW.)

Yes, this looks like the way to go, i.e. the server (Shairport-sync) sent pause command to the client (Iphone).

Shouldn't the Shairport-sync pause playback and release sound card almost immediately?

If the sound card release is not immediate, the other sound playing application may not be able to play if the sound card used does not allow multi-input, e.g. USB sound card normally only allow single app sound output. It maybe necessary to channel the shairport-sync sound output to nowhere when this command is triggered until it is actually paused.

For your reference, someone have posted a patch to make Shairport sent the sound to nowhere.
https://github.com/abrasive/shairport/issues/244

Do you think this feature will make it to your to-do-list for future Shairport-sync version?

For this pause action, triggering the -E action is probably not necessary or not preferred. e.g. I have configured the -E action to sent a play command to MPD, this is not required when MPD is already playing... as this was the source of the Shairport-sync pause command. May need an option to enable/disable the -E action after pause command.

Well, we can take this in stages – first, let's try sending a pause back to the player. When iTunes pauses or stops, then, after about two seconds it sends signals that Shairport Sync uses to exit the play mode and release the ALSA device. Implementing this could take a little while – busy at work, etc.

The patch to send sound elsewhere is a hack, IMHO, and I'd rather not do it.

No problem. I am not in any hurry. Take your time.
Just post here or e-mail me directly when you have something I should test/try, my gmail account uses the same username as the one you see here.

Just committed d2ec715463. If you send a SIGUSR2 to it (e.g. kill -USR2 <pid of shairport-sync>) while it's playing a stream, it will send a pause request back to the source. The source pauses immediately, and then after about two seconds drops the play session, releasing the ALSA back end.

It's a bit rough, but worth trying.

I tried the latest makefile, i.e. 2.1.2 but the resultant binary can't start.

root@Openwrt:~# shairport-sync
Successful startup.
setsockopt: Bad file descriptor
could not bind any listen sockets!

Note: I removed libsoxr from makefile because I can't get it to build yet..

Quick question: if you restart the computer, do you still get this error?

My ROM is configured to automatically restart at first boot. So, my error is definitely not due to 1st boot.

The error message comes from when shairport-sync tries to get a socket for port 5000 (or whichever port you specify with the -p option). If shairport-sync was running beforehand and was aborted, or if another program (an iTunes -type music server?) is using the port, then this just might be causing the problem. Just a thought... I'll continue to try to provoke the problem. Could you post the command you are using to start shairport-sync, with arguments?

The command to start shairport-sync doesn't matter to reproduce the error. The log I copy/pasted earlier has no argument.

I get same error using the same start-up and configuration file that works fine on v2.1 shairport-sync as well as shairport. I also tried using the start-up script and configuration file you provided for v2.1.1. Result is the same.

I am pretty sure I don't have anther application taking up that port. Actually, I remove shairport and add shairport-sync when building the ROM to test. Using exactly the same start-up script and configuration file, shairport-sync v2.1 and shairport works fine, but not shairport-sync v2.1.1.

Excellent, thanks.

Aha – success: Using an old Ubuntu: Version 12.04.5 32 bit (uname -a: ubuntu 3.13.0-32-generic #57~precise1-Ubuntu SMP Tue Jul 15 03:50:54 UTC 2014...) and an old OpenWrt: (ATTITUDE ADJUSTMENT Attitude Adjustment, r42647) on a Raspberry Pi, I get this:

root@OpenWrt:/# shairport-sync 
Successful startup.
setsockopt: Bad file descriptor
could not bind any listen sockets!

Now I can see if it means anything...

Good news and bad news.
Good news – the bug you reported has been fixed, thanks, in 2.1.3.
Bad news – it seems that iTunes no longer responds to the pause request. Maybe the most recent iTunes update has changed something...

[Update] Curious, the pause request is working again after restarting my iTunes server, a Mac. Previously, it was (a) rejecting pause requests made to port 3689 but (b) accepting them on port 49153, and (c) the Remote app on my iPhone know which port to use. Upon restarting, it's accepting requests again on port 3689. Clearly there's more to learn about the Remote Control protocol!

I have tested shairport-sync v2.1.3 just now. The socket binding problem is fixed as you said but I was unable to get shairport-sync to sent pause back to Iphone 3Gs, IOS 6.1.3 or Windows Itune 11.0.4.4.
Note: I didn't restart my computer and I could update my Itune to latest if needed.

I tried both of these command but nothing happens, i.e. airplay is still playing...
kill -USR2 1075
kill -SIGUSR2 1075
(yes, PID for shairport-sync during my test was 1075.)

Log doesn't say anything useful but I guess you already know the reason why sending pause didn't work ;)

Thanks for that. TBH, I think the "pause" idea is a dead-end at present. It works sometimes, and other times is doesn't. Also, only iTunes obeys it. VLC ignores it, for example. We need to know more about it – maybe someone out there is reverse-engineering it. But even if it worked perfectly, it would not guarantee that shairport sync would be paused.

On the brighter side, shairport-sync now releases the ALSA device almost instantly when it pauses, and reacquires it when it needs to continue. It does not hold on to the device for a few seconds, or whatever, as it used to. As far as I can tell, the release/reacquire operations are inaudible.

Over the next while, I will try to figure out how best to send a command to shairport-sync to release the ALSA device on request so that other applications can acquire it while shairport-sync continues to operate in every way except sending the audio to the ALSA device.

I see... that's alright. There is always another way or workaround like you have considered ;)

If shairport-sync can releases alsa by command, I think it also need to be programmed not to quit if alsa is not available. Otherwise, if another application is playing audio and someone send request to shairport-sync, shairport-sync will quit, making it unavailable to use for airplay after the other audio application releases alsa. In other words, maybe need to add command to start-up or configuration to disable shairport-sync quit when alsa is occupied?

I know the very old version of shairport doesn't quit if alsa is occupied, not sure why it is programmed to quit in the newer versions.

I appreciate your work to try to implement this.

So, the latest version – 2.1.6 – uses SIGUSR2 (and thus the -P option) to drop and reacquire the ALSA device. It generates benign debug messages, but you might like to try it out and see whether it's on the right track. I have not updated the OpenWrt script to point to it, as it's still quite experimental – you'll have to do that yourself.

Sorry but I don't quite understand how to try this... do I enter some command into shell when shairport-sync is playing? if so, what is the command?

Basically I'm re-using the SIGUSR2 signal. It was used to send a pause request to the audio source, but it no longer does that, as it doesn't seems reliable.

So, to ask shairport-sync to drop the connection to the ALSA device, send a SIGUSR2 signal to it. If you know its PID, you could do kill -SIGUSR2 <PID>.

There is another way to do it. When shairport-sync is started as a daemon with the -d option, it puts its PID in the file /var/run/shairport-sync.pid. If you then start another copy of shairport-sync with the -P option (say from the command line or in a script file) then it will look for the daemon's PID file, extract the PID from it, send a SIGUSR2 signal to the process with that PID and then exit. (This is the same mechanism that is used to to kill the daemon by executing shairport-sync -k, except that a different signal is sent.)

Were you able to play at least one complete song using Shairport-sync 2.1.6?
When I try to play music on Shairport-sync 2.1.6, it stops/disconnect itself every 5-10 seconds.

On Iphone, the airplay icon indicate that it is a disconnection as the Shairport-sync selected is unselected. Not a pause. After this, I can re-select Shairport-Sync again and it would play and then stop again after 5-10 seconds.

Does this sounds like a bug resulting from the recent change to send a pause request to the audio source?
I know Shairport-sync 2.1.3 doesn't have such problem on my test hardware.

Yep, sorry. I inadvertently exposed the universal timer to a race condition, and this might have caused the problem – I had one dropout similar to what you describe, though not as severe, yesterday. 2.1.7 has a fix for that.

ic... I have rebuild it using 2.1.7.

Method 1 works, i.e. it stopped shairport-sync playback.... obviously, I think there is no command to make shairport-sync re-acquire alsa again yet?.. So, I just restart shairport-sync to make it work again for testing.

 kill -SIGUSR2 <PID>

But method 2 doesn't
root@Openwrt:~# shairport-sync -P
Can't send a pause request -- Failed to find daemon: No such file or directory

Good with method 1! You send a SIGUSR2 to it to disconnect (as you discovered), and then you can send a SIGUSR2 to it to reconnect. It will take a few seconds to resync.

Method 2 only works if you started the original shairport-sync with the -d option.

Got it. I retested it just now, I can reconnect using the same command used to disconnect alsa.

Method 2 works as well by starting the shairport-sync with the -d option as you said :)

That's good. I'll be interested to know what your thinking is on it.

For now, I think it works well during my test but I still need to figure out a way to get the other audio applications to sent a shell command to get shairport-sync to release alsa. Unlike shairport, audio applications like mpd doesn't have an option to execute a shell command/script.

Actually, I found someone posted this to run a mpd client on the device to sent shell script/command.
https://gist.github.com/sahib/6718139
Comes from this post
https://bbs.archlinux.org/viewtopic.php?pid=1329315

I have not figured out how to use this yet because Openwrt makefile is not included.
Do you know how?

In any case, I think a different shairport-sync command to release and re-acquire alsa would be better than using the same command. If one were to use this mpd script, every play/pause command will result in the same shell command sent to shairport-sync, using the same command to release/re-acquire alsa won't work in this use case.

I'll leave the mpd part of it to you, Eric :)

Regarding the idea of different commands – I think you're right, but there is a slight problem: the number of signals like SIGUSR2 is very limited. SIGUSR1 is used internally, and can't be used, I'm afraid. The only other possible signal is SIGHUP – it was used to close and reopen the log file, but I think that's no longer necessary having moved to libdaemon.

Well, if it is just a different command that is recognized by shairport-sync, i.e. method 2, does it solve the problem of limited SIGUSR2 signals? In other words, something similar to the two different options for start/stop command to call external script from Shairport.

Could just leave method 1 as is, method 1 is less likely to be used due to the need to determine the PID first.

Version 2.1.8 gives you two separate commands, as you suggest. They are implemented using SIGUSR2 and SIGHUP. (To implement them reliably, a separate signal for each command.) So, the commands for method 2 have changed: shairport-sync –disconnectFromOutput for disconnecting and shairport-sync –reconnectToOutput for reconnecting. Let me know how it goes.

I see this error building shairport-sync 2.1.8

make[4]: Entering directory /home/openwrt/openwrt/attitude_adjustment/build_dir/target-mipsel_r2_uClibc-0.9.33.2/shairport-sync-2.1.8' make all-am make[5]: Entering directory/home/openwrt/openwrt/attitude_adjustment/build_dir/target-mipsel_r2_uClibc-0.9.33.2/shairport-sync-2.1.8'
mipsel-openwrt-linux-uclibc-gcc -Os -pipe -mips32r2 -mtune=mips32r2 -fno-caller-saves -fhonour-copts -Wno-error=unused-but-set-variable -msoft-float -L/home/openwrt/openwrt/attitude_adjustment/staging_dir/target-mipsel_r2_uClibc-0.9.33.2/usr/lib -L/home/openwrt/openwrt/attitude_adjustment/staging_dir/target-mipsel_r2_uClibc-0.9.33.2/lib -L/home/openwrt/openwrt/attitude_adjustment/staging_dir/toolchain-mipsel_r2_gcc-4.6-linaro_uClibc-0.9.33.2/usr/lib -L/home/openwrt/openwrt/attitude_adjustment/staging_dir/toolchain-mipsel_r2_gcc-4.6-linaro_uClibc-0.9.33.2/lib -o shairport-sync shairport.o rtsp.o mdns.o mdns_external.o common.o rtp.o player.o alac.o audio.o audio_dummy.o audio_pipe.o mdns_avahi.o audio_alsa.o -lasound -lavahi-common -lavahi-client -lsoxr -lpopt -lm -lpthread -ldaemon -lrt
rtsp.o: In function handle_announce': rtsp.c:(.text+0x3d8): undefined reference tobase64_dec'
rtsp.c:(.text+0x424): undefined reference to base64_dec' rtsp.c:(.text+0x43c): undefined reference torsa_apply'
rtsp.o: In function rtsp_conversation_thread_func': rtsp.c:(.text+0x870): undefined reference tobase64_dec'
rtsp.c:(.text+0x94c): undefined reference to rsa_apply' rtsp.c:(.text+0x95c): undefined reference tobase64_enc'
rtsp.c:(.text+0xa30): undefined reference to base64_enc' collect2: ld returned 1 exit status make[5]: *** [shairport-sync] Error 1 make[5]: Leaving directory/home/openwrt/openwrt/attitude_adjustment/build_dir/target-mipsel_r2_uClibc-0.9.33.2/shairport-sync-2.1.8'
make[4]: ** [all] Error 2
make[4]: Leaving directory /home/openwrt/openwrt/attitude_adjustment/build_dir/target-mipsel_r2_uClibc-0.9.33.2/shairport-sync-2.1.8' make[3]: *** [/home/openwrt/openwrt/attitude_adjustment/build_dir/target-mipsel_r2_uClibc-0.9.33.2/shairport-sync-2.1.8/.built] Error 2 make[3]: Leaving directory/home/openwrt/openwrt/attitude_adjustment/package/shairport-sync'
make[2]: *
* [package/shairport-sync/compile] Error 2
make[2]: Leaving directory /home/openwrt/openwrt/attitude_adjustment' make[1]: *** [/home/openwrt/openwrt/attitude_adjustment/staging_dir/target-mipsel_r2_uClibc-0.9.33.2/stamp/.package_compile] Error 2 make[1]: Leaving directory/home/openwrt/openwrt/attitude_adjustment'
make: *** [world] Error 2

I'm away from my dev system for a couple more days, so it'll be a few days before I can check this out.

No worries, take your time.

Hi Eric. Works for me alright. Is it possible that you might have just edited the package's Makefile by changing the version to 2.1.8? Just a guess, but this time, the CONFIGURE_ARGS setting has changed as well... Now it's:

CONFIGURE_ARGS+= \
--with-alsa \
--with-avahi \
--with-soxr \
--with-ssl=openssl

Just a thought...

Yup, you are right. I only changed the version number without realizing there is another change needed.

Method 1
I can disconnect alsa but unable to reconnect back.

Method 2
Unable to disconnect alsa.
root@Openwrt:~# shairport-sync -disconnectFromOutput
Daemon already running on PID file 2893

I have already added the -d option.
Otherwise, the error will be like
-disconnectFromOutput: invalid numeric value

Hmm. Note that the disconnectFromOutput and reconnectToOutput require two hyphens before them.

Here is a log from my Raspberry Pi running 2.1.8:

root@FrontRoom:~# /etc/init.d/shairport-sync start
root@FrontRoom:~# htop
root@FrontRoom:~# ps w | grep sha
 3109 root      7828 S    shairport-sync -v -d -a Front Room -- -d hw:1 -t hardware -c PCM
 3114 root      1192 S    grep sha
root@FrontRoom:~# shairport-sync -V
2.1.8-openssl-Avahi-ALSA-soxr
root@FrontRoom:~# shairport-sync -h
Usage: shairport-sync [options...]
  or:  shairport-sync [options...] -- [audio output-specific options]

Mandatory arguments to long options are mandatory for short options too.

Options:
    -h, --help              show this help
    -V, --version           show version information
    -v, --verbose           -v print debug information; -vv more; -vvv lots
    -p, --port=PORT         set RTSP listening port
    -a, --name=NAME         set advertised name
    -A, --AirPlayLatency=FRAMES set how many frames between a just-received frame and audio output
                            if the source's User Agent is "AirPlay". The default value is 88200 frames.
    -i, --iTunesLatency=FRAMES set how many frames between a just-received frame and audio output
                            if the source's User Agent is "iTunes". The default value is 99400 frames.
    -L, --latency=FRAMES set how many frames between a just-received frame and audio output
                            starts. This value is in frames; if non-zero overrides all other latencies.
                            Default latency without "AirPlay" or "iTunes" User Agent is 88200 frames.
    -S, --stuffing=MODE set how to adjust current latency to match desired latency 
                            "basic" (default) inserts or deletes audio frames from packet frames with low processor overhead.
                            "soxr" uses libsoxr to minimally resample packet frames -- moderate processor overhead.
                            "soxr" option only available if built with soxr support.
    -d, --daemon            daemonise.
    -k, --kill              kill the existing shairport daemon.
    -D, --disconnectFromOutput  disconnect immediately from the output device.
    -R, --reconnectToOutput  reconnect to the output device.
    -B, --on-start=PROGRAM  run PROGRAM when playback is about to begin.
    -E, --on-stop=PROGRAM   run PROGRAM when playback has ended.
                            For -B and -E options, specify the full path to the program, e.g. /usr/bin/logger.
                            Executable scripts work, but must have #!/bin/sh (or whatever) in the headline.
    -w, --wait-cmd          wait until the -B or -E programs finish before continuing
    -o, --output=BACKEND    select audio output method
    -m, --mdns=BACKEND      force the use of BACKEND to advertize the service
                            if no mdns provider is specified,
                            shairport tries them all until one works.
    -r, --resync=THRESHOLD  resync if error exceeds this number of frames. Set to 0 to stop resyncing.
    -t, --timeout=SECONDS   go back to idle mode from play mode after a break in communications of this many seconds (default 120). Set to 0 never to exit play mode.

Available mDNS backends: 
    avahi
    external-avahi

Available audio outputs:
    alsa (default)
    dummy
    pipe

Options for output alsa:
    -d output-device    set the output device [default*|...]
    -t mixer-type       set the mixer type [software*|hardware]
    -m mixer-device     set the mixer device ['output-device'*|...]
    -c mixer-control    set the mixer control [Master*|...]
    -i mixer-index      set the mixer index [0*|...]
    *) default option

Options for output dummy:
    There are no options for dummy audio.

Options for output pipe:
    pipe takes 1 argument: the name of the FIFO to write to.
root@FrontRoom:~# shairport-sync -D
root@FrontRoom:~# shairport-sync -R
root@FrontRoom:~# shairport-sync -disconnectFromOutput
Daemon already running on PID file 3109
root@FrontRoom:~# shairport-sync -reconnectToOutput
Daemon already running on PID file 3109
root@FrontRoom:~# shairport-sync -D
root@FrontRoom:~# shairport-sync -R
root@FrontRoom:~# shairport-sync -D
root@FrontRoom:~# shairport-sync -D
root@FrontRoom:~# shairport-sync -D
root@FrontRoom:~# shairport-sync -D
root@FrontRoom:~# shairport-sync -R
root@FrontRoom:~# shairport-sync -disconnectFromOutput
Daemon already running on PID file 3109
root@FrontRoom:~# shairport-sync --disconnectFromOutput
root@FrontRoom:~# shairport-sync --reconnectToOutput
root@FrontRoom:~# 

I'm getting no messages from ALSA, and everything is working as expected...

Got it.. it is indeed working..
I was missing the double hyphens (--)

Method 1 also works but alsa reconnect need to use the new command instead.
kill -SIGHUP

I guess we can close this ticket now then...

I noticed that after shairport-sync releases alsa, Iphone will continue to play music and it actually won't stop until you press stop at the Iphone.

If you come across a way/method, sending a stop signal to the Iphone is still necessary.

See https://github.com/mikebrady/shairport-sync/issues/5#issuecomment-57939147. It doesn't look like there is a good way to do this.

I have read your comment before and I know you said the "pause" idea was not feasible.
Is it actually possible to simply break the airplay connection?
An analogy is when shairport-sync is restarted while it is playing, the Iphone end will automatically "pause/stop".

However, it is not feasible to issue a command to restart shairport-sync repeatedly as other sound application is started/stopped because this will likely cause other problems.

It is true that if you break the connection, the iPhone stops, but iTunes shows an error. It's a hack, IMHO.

I tried it just now on Itune and it actually think the device still exists after I have terminated shairport-sync and keep sending the airplay music through... I can see the WIFI traffic via led indicator despite shairport-sync is not running.

I know there are devices out there that can sent out the airplay stop command. Do you have any suggestion/comment on how I can capture them in a way that is useful for you to look at?

Well, I could easily reinstate the pause/stop command (should I?). It doesn't seem to be a general solution, but at least it's not a hack. If there is a general solution, that's what I need to find out about. Very likely it's some kind of HTML transaction. If you could capture it, it would be great.

Do you mean the pause/stop command you tried to implement previously?
https://github.com/mikebrady/shairport-sync/issues/5#issuecomment-57905079

If so, can you tell me which commit you previously commit that implement this? I can try use that and try to see if it works. I don't recall testing this pause/stop command implementation at all..

Will let you know if I manage to capture it.

Yeah, I implemented it in 2.1.2 and took it out subsequently.
I’d be interested to hear of it was any use.

I'm very interested in this feature too.

I've found on Google this blog post that describes how DACP works (mostly it's the same information on the Unofficial Specification) and provides a proof-of-concept DACP client. I've done some testing and I've been able to reliably use it to control my devices so I think that's the way to go.

From what I saw in my testing I think I found out why your code sometimes didn't work: the port used by iTunes on Mac for DACP seems to be fixed to 3689 (which is the one you hardcoded in your code), while iOS uses a different ephemeral port for every AirPlay session (you can verify this with avahi-browse _dacp._tcp -r). It looks like a mdns lookup is definitely required to implement this feature.

Also, while the Unofficial Specification says that the DACP-ID header is used to advertise DACP support, AirPlay with system-wide audio on Mac includes it even if it doesn't support remote controls. The only way to reliably detect if DACP is supported or not is to try and resolve the Bonjour service.

Thank you so much! I'll certainly look into this over the next few weeks.

Thanks EliaCereda, I guess I don't need to test it when this is probably the reason pause didn't work before..

@EliaCereda Would you mind sharing your working example?

Not gonna reopen this, but it's been done -- see #223.

Was this page helpful?
0 / 5 - 0 ratings