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

Command to pause/stop Shairport-sync playback? #5

Closed
ericwongcm opened this issue Aug 31, 2014 · 51 comments
Closed

Command to pause/stop Shairport-sync playback? #5

ericwongcm opened this issue Aug 31, 2014 · 51 comments

Comments

@ericwongcm
Copy link

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, https://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.

@mikebrady
Copy link
Owner

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?

@ericwongcm
Copy link
Author

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?

@mikebrady
Copy link
Owner

This might be significant: https://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.)

@ericwongcm
Copy link
Author

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.

@mikebrady
Copy link
Owner

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.

@mikebrady mikebrady self-assigned this Sep 3, 2014
@ericwongcm
Copy link
Author

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.

@mikebrady
Copy link
Owner

Just committed d2ec715. 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.

@ericwongcm
Copy link
Author

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..

@mikebrady
Copy link
Owner

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

@ericwongcm
Copy link
Author

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

@mikebrady
Copy link
Owner

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?

@ericwongcm
Copy link
Author

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.

@mikebrady
Copy link
Owner

Excellent, thanks.

@mikebrady
Copy link
Owner

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...

@mikebrady
Copy link
Owner

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!

@ericwongcm
Copy link
Author

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 ;)

@mikebrady
Copy link
Owner

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.

@ericwongcm
Copy link
Author

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.

@mikebrady
Copy link
Owner

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.

@ericwongcm
Copy link
Author

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?

@mikebrady
Copy link
Owner

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.)

@ericwongcm
Copy link
Author

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.

@mikebrady
Copy link
Owner

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.

@ericwongcm
Copy link
Author

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

@mikebrady
Copy link
Owner

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.

@ericwongcm
Copy link
Author

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 :)

@mikebrady
Copy link
Owner

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

@ericwongcm
Copy link
Author

No worries, take your time.

@mikebrady
Copy link
Owner

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...

@ericwongcm
Copy link
Author

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

@mikebrady
Copy link
Owner

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...

@ericwongcm
Copy link
Author

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...

@ericwongcm
Copy link
Author

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.

@mikebrady
Copy link
Owner

See #5 (comment). It doesn't look like there is a good way to do this.

@ericwongcm
Copy link
Author

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.

@mikebrady
Copy link
Owner

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

@ericwongcm
Copy link
Author

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?

@mikebrady
Copy link
Owner

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.

@ericwongcm
Copy link
Author

Do you mean the pause/stop command you tried to implement previously?
#5 (comment)

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.

@mikebrady
Copy link
Owner

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

@EliaCereda
Copy link

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.

@mikebrady
Copy link
Owner

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

@ericwongcm
Copy link
Author

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

@NickSutton
Copy link

@EliaCereda Would you mind sharing your working example?

@mikebrady
Copy link
Owner

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

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

No branches or pull requests

4 participants