mikebrady / shairport-sync

AirPlay and AirPlay 2 audio player
Other
7.28k stars 574 forks source link

Pause remote streaming device - possible? #907

Closed JulianWAS closed 5 years ago

JulianWAS commented 5 years ago

Hi,

I'd like to know if it is possible to pause the streaming device? Are there any hooks or commands within shairport-sync that I can use to fire back to the streaming device ( iPad ) to pause it? I have an ARM platform that also uses Bluetooth audio so as one starts to play ( user presses play ) I'd like to pause the other ( from the ARM ). Bluetooth start -> pause Shairport, resume play on Shairport ( user presses play on iPad ) -> pause Bluetooth. The Bluetooth side is all done over DBus, now I just need to send a command to the iPad over whatever protocol/interface Shairport uses to pause the it.

If this can be done, what would be the best method?

Thanks.

mikebrady commented 5 years ago

Thanks for the post. Yes, Shairport Sync has an optional D-Bus interface (and an MQTT interface too).

If the streaming source permits a basic level of remote control (and the iPad does) you can send a D-Bus command to Shairport Sync which will be relayed to the source. You can, for example, pause/resume, raise/lower volume.

The first step is to include D-Bus support with --with-dbus-interface at the configuration step. Then you can explore the D-Bus interface with, e.g. dfeet.

[Update] You need to fully install Shairport Sync for the D-Bus interface to be enabled on the D-Bus system bus.

You may wish to take a look at the sample commands here. One of them – "# Play using regular Remote Control" is close to what you are suggesting; just use the commands Pause to stop and Play to start again. Alternatively, the command PlayPause can be use to toggle play on and off. BTW, not all commands are available on all remote devices; for instance, Resume may not be available.

idcrook commented 5 years ago

I have two apps that send remote commands if you wanted additional examples. They use MQTT, and require the MQTT remote interface to be enabled in shairport-sync.conf.

One is a webapp that has buttons on webpage that, e.g., can play/pause iTunes, Airfoil, etc. The other uses physical buttons to activate the remote controls on a Character LCD on a Raspberry Pi hat.

They can be found in https://github.com/idcrook/shairport-sync-mqtt-display

mikebrady commented 5 years ago

@idcrook – they look impressive!

JulianWAS commented 5 years ago

@mikebrady - The DBus pause is exactly what I need, thanks! I recompiled with the appropriate flag and on starting up I get the following error:
Error org.freedesktop.DBus.Error.ServiceUnknown: The name org.gnome.ShairportSync was not provided by any .service files

The application is cross-compiled for an iMX ARM processor so everything's a little hand-tweaked so this might be a install issue. What do you suggest I need to fix this; add something to one of my dbus confs?

Thanks.

JulianWAS commented 5 years ago

Added shairport-sync-dbus-policy.conf to /etc/dbus-1/system.d/ - which fixed the above issue but resulted in: Could not acquire a Shairport Sync native D-Bus interface "org.gnome.ShairportSync.i606" on the system bus.

Bear in mind this is a headless ARM system where most things are root if it's a permissions issue. What's with the i606 on the end of org.gnome.ShairportSync.i606?

Thanks

mikebrady commented 5 years ago

That's good news! For an application to provide a service on the D-Bus system bus, there some security involved. On the standard # make install, a user and group called shairport-sync are created, if necessary, and the application shairport-sync is set up to launch under user shairport-sync. Then, a D-Bus policy file called shairport-sync-dbus-policy.conf is also installed, usually into /etc/dbus-1/system.d, to give the appropriate permission to the user shairport-sync to provide the service org.gnome.ShairportSync. It might be worth looking at the last part (line 162 onwards) of the Makefile.am for more info.

mikebrady commented 5 years ago

The policy doc requires the provider to be the user shairport-sync, so maybe change that to root.

The i606 is made up from the process number in case there is the possibility of a name clash. It's in the guidelines somewhere.

JulianWAS commented 5 years ago

Hi, Changing the conf to root cleared up all the starting DBus problems. The next issue is purely one of no response to the DBus pause command.

Attached is a file containing the response of the pause command and a dump from DBUS Introspect. Pause, play etc all come up as methods but issuing any command has no effect on the iPad whatsoever.

ShairportSync-DBus-Pause-Dump.txt

Shairport-sync-3.2.2 Kernel 4.9.88 DBus 1.10.20

Thanks again

mikebrady commented 5 years ago

Thanks. Just looking at this now on my own system and it seems as if something has changed at the client end. I'll take a look at it now.

JulianWAS commented 5 years ago

I just updated the iPad to 13.1.3 and it's all working! It was quite a few updates behind as I'm always a bit reluctant to update if everything works on it.... play, pause, volume now are good. Stop doesn't, work but I'm not going to need it at the moment.

Interesting that you've found an inconsistency though..

Thanks again,

mikebrady commented 5 years ago

Yeah, I'm worried about something there. Glad it's working for you though.

JulianWAS commented 5 years ago

I'll keep an eye on this ticket if any updates are available. Cheers.

mikebrady commented 5 years ago

Thanks. I'm afraid I have to leave it now until tomorrow...

mikebrady commented 5 years ago

It turns out that the new Music app on the Mac – the one replacing iTunes – doesn't offer any the iTunes remote control facilities. I guess this is hardly a surprise.

However, if you direct the System Output to a Shairport Sync device, you still have limited commands, including Pause.

Functionality from iOS is unchanged.

JulianWAS commented 5 years ago

Quick question before closing this ticket, can I "catch" incoming Shairport-Sync DBus signals like a volume change "from" the iPad? The iPad can of course adjust the volume on my headless Shairport-Sync platform so are these volume changes passed onto to DBus internally?

Below is a section of how I monitor Bluez volume changes in Python, if Shairport-sync volume changes are passed onto to DBus I imagine it's just a few path changes - in principle..? Bluez is fairly layered and complex, if I can pick up Shairport-Sync volume signals do you have the DBus paths/properties handy?

def device_property_changed_cb(property_name, value, path, interface, device_path): global bus if property_name != BLUEZ_DEV: return

device = dbus.Interface(bus.get_object("org.bluez", device_path), "org.freedesktop.DBus.Properties")
properties = device.GetAll(BLUEZ_DEV)

print("Getting dbus interface for device: %s interface: %s property_name: %s" % (device_path, interface, property_name))

vol = properties["Volume"]
volume_percentage = format(vol / 1.27, '.2f')

Cheers.

mikebrady commented 5 years ago

Thanks for the suggestion. I can't believe that I didn't think of providing this volume control information! I'll try to incorporate it over the next few days.

mikebrady commented 5 years ago

Actually, AirPlay volume control information is provided (whew!) in the RemoteControl interface. This is from the shairport-sync-dbus-test program:

...
*** Properties Changed:
      ShairportSync.RemoteControl.AirplayVolume -> -16.0
 *** Properties Changed:
      ShairportSync.RemoteControl.AirplayVolume -> -14.0
...
JulianWAS commented 5 years ago

Great! Interestingly though I can send to the iPad via DBus as you've instructed but monitoring DBus I see nothing from Shairport-Sync on DBus locally ( incoming ). If I use dbus-monitor --system there's nothing from Shairport whatsoever i.e. pause iPad -> Shairport-Sync pauses -> local DBus = nothing. Is this expected?

mikebrady commented 5 years ago

Hmm, not sure if that isn't a problem with dbus-monitor, which I never really figured out.

The shairport-sync-dbus-test program monitors the bus (the system bus by default) to get information about Shairport Sync.

JulianWAS commented 5 years ago

OK I'll give it a burst. I think that wraps this question up really. Appreciate your advice. Thanks.

JulianWAS commented 5 years ago

Instead of org.gnome.ShairportSync.RemoteControl.VolumeDown, can I use the AdvancedRemoteControl interface ( below ) to pass a volume value directly to my iPad? If so what would the end of the DBus string look like ( ie. org.gnome.ShairportSync.AdvancedRemoteControl ...........?

org.gnome.ShairportSync.AdvancedRemoteControl.SetVolume int32:50 According to https://github.com/mikebrady/shairport- sync/blob/master/documents/sample%20dbus%20commands doesn't appear to do anything

interface name="org.gnome.ShairportSync.AdvancedRemoteControl" method name="SetVolume" arg type="i" name="volume" direction="in" method property type="b" name="Available" access="read" property type="s" name="PlaybackStatus" access="read" property type="b" name="Shuffle" access="readwrite" property type="s" name="LoopStatus" access="readwrite" property type="i" name="Volume" access="read" interface

Thanks.

mikebrady commented 5 years ago

Unfortunately, the answers is “no”. iOS devices don’t offer the features that the advanced remote control interface depends on, so it never goes active. The only client that it works with is iTunes, and that is now gone/deprecated, replaced by the Music app, which offers no remote control whatever.

JulianWAS commented 5 years ago

Understood, thank you.

JulianWAS commented 5 years ago

Hi, regarding all the conversation above, I OK with VolumeUp/Down to the iPad :)

My attention now is what's outputted to local DBus from ShairportSync i.e. to "capture" play/pause/volume changes. I not sure if this information is truly available. Perhaps I'm missing a flag or something ( have included ----with-dbus-interface whist building ) ?

I tried "shairport-sync-dbus-test" and a number of other bus monitoring applications ( python etc ) and I get absolutely no local DBus information form ShairportSync with exception of the initial connect. I understand your comment above about a possible bug in dbus-monitor hence trying lots of other apps/code. Looking at the DBus introspection for ShairportSync is seems a little lean on "signals" but I'm certainly no DBus expert. My DBus is 1.10.20, have you any python examples that simply output Shairport signals like volume/play/pause?

I'd like to use this information to drive a local LCD display etc.

Apologies about dragging this mater on.... appreciate your patience!

mikebrady commented 5 years ago

No apologies needed – this is very interesting and may help to clarify the issue for others. So, I have just run an experiment on a Raspberry Pi Zero and an iPhone Xs Max running iOS 13.1.3 and using its Music app. Here is some output from shairport-sync-dbus-test-client running on the Pi Zero. The metadata section of the /etc/shairport-sync.conf file is all defaults.

pi@zeropi:~/shairport-sync $ shairport-sync -V
3.3.3d9-OpenSSL-Avahi-ALSA-soxr-metadata-dbus-sysconfdir:/etc
pi@zeropi:~/shairport-sync $ ./shairport-sync-dbus-test-client 
Listening on the D-Bus system bus.
Starting test...
 *** Properties Changed:
      ShairportSync.RemoteControl.Metadata -> @a{sv} {}
      ShairportSync.RemoteControl.Server -> 'fde6:4029:319e:e180:1d46:7fc9:bf39:a33e'
 *** Properties Changed:
      ShairportSync.Active -> true
 *** Properties Changed:
      ShairportSync.RemoteControl.PlayerState -> 'Playing'
 *** Properties Changed:
      ShairportSync.RemoteControl.AirplayVolume -> -18.0
 *** Properties Changed:
      ShairportSync.RemoteControl.Metadata -> {'mpris:artUrl': <'file:///tmp/shairport-sync/.cache/coverart/cover-e672a9bf4d3c83cb506d5d415cb3e0d8.jpg'>}
 *** Properties Changed:
      ShairportSync.RemoteControl.ProgressString -> '1081171389/1084836999/1101803965'
 *** Properties Changed:
      ShairportSync.RemoteControl.Metadata -> {'mpris:artUrl': <'file:///tmp/shairport-sync/.cache/coverart/cover-e672a9bf4d3c83cb506d5d415cb3e0d8.jpg'>, 'mpris:trackid': <objectpath '/org/gnome/ShairportSync/mper_685169194'>, 'xesam:title': <'Mozart: Apollo et Hyacinthus, K38 - No 05. Aria: Saepe terrent Numina (Hyacinthus) '>, 'xesam:album': <'Mozart: Apollo et Hyacinthus'>, 'xesam:artist': <['Sophie Bevan; Ian Page: Classical Opera']>, 'xesam:genre': <['Classical']>, 'mpris:length': <int64 467858000>}
 *** Properties Changed:
      ShairportSync.RemoteControl.Available -> true
 *** Properties Changed:
      ShairportSync.RemoteControl.ProgressString -> '1081171368/1084846151/1101803944'
 *** Properties Changed:
      ShairportSync.RemoteControl.ProgressString -> '1082909460/1084885223/1103542036'
 *** Properties Changed:
      ShairportSync.RemoteControl.AirplayVolume -> -16.0
 *** Properties Changed:
      ShairportSync.RemoteControl.AirplayVolume -> -18.0
Volume up for five seconds...
 *** Properties Changed:
      ShairportSync.RemoteControl.AirplayVolume -> -16.0
Volume down
Finished test. Listening for property changes...
 *** Properties Changed:
      ShairportSync.RemoteControl.AirplayVolume -> -18.0
 *** Properties Changed:
      ShairportSync.RemoteControl.PlayerState -> 'Paused'
 *** Properties Changed:
      ShairportSync.RemoteControl.PlayerState -> 'Playing'
 *** Properties Changed:
      ShairportSync.RemoteControl.PlayerState -> 'Stopped'
 *** Properties Changed:
      ShairportSync.Active -> false
 *** Properties Changed:
      ShairportSync.Active -> true
 *** Properties Changed:
      ShairportSync.RemoteControl.PlayerState -> 'Playing'
 *** Properties Changed:
      ShairportSync.RemoteControl.PlayerState -> 'Paused'
 *** Properties Changed:
      ShairportSync.RemoteControl.ProgressString -> '1812296259/1812299779/1812296259'
 *** Properties Changed:
      ShairportSync.RemoteControl.Metadata -> {'mpris:artUrl': <'file:///tmp/shairport-sync/.cache/coverart/cover-e672a9bf4d3c83cb506d5d415cb3e0d8.jpg'>, 'mpris:trackid': <objectpath '/org/gnome/ShairportSync/mper_1313128366'>, 'xesam:title': <'Radio Svizzera Classica'>, 'xesam:album': <'Radio Svizzera Classica'>, 'xesam:artist': <['La radio classica per rilassarsi']>}
 *** Properties Changed:
      ShairportSync.RemoteControl.ProgressString -> '1812296807/1812301891/1812296807'
 *** Properties Changed:
      ShairportSync.RemoteControl.PlayerState -> 'Playing'
 *** Properties Changed:
      ShairportSync.RemoteControl.Metadata -> {'mpris:artUrl': <'file:///tmp/shairport-sync/.cache/coverart/cover-8f0cb63fb3a32e029bdba6b888b2ae91.jpg'>, 'mpris:trackid': <objectpath '/org/gnome/ShairportSync/mper_1313128366'>, 'xesam:title': <'Radio Svizzera Classica'>, 'xesam:album': <'Radio Svizzera Classica'>, 'xesam:artist': <['La radio classica per rilassarsi']>}
 *** Properties Changed:
      ShairportSync.RemoteControl.ProgressString -> '1812296337/1812313155/1812296337'
...
JulianWAS commented 5 years ago

Hmm.... clearly something different. I enabled --with-metadata, --with-dbus-interface, --with-mpris-interface and build it, ran it and it resulted in below, which I think I got anyway.

I'm running as root in shairport-sync-dbus-policy.conf => policy user="root" and system not session. I can send play, pause and volume over DBus but nothing when monitored so I assume the confs OK. Could this be a system vs session issue? I thought system would give everything..

As there's obvious differences between our Linux builds, DBus versions, compilers etc perhaps something like a bit of Python shared between us could help? I've tried hacking some simple Python of the net to dump out some signals but didn't show anything, but if you were to do it and it worked then I tried it, any exception(s) thrown on my build ( Yocto / iMx7 ) could then provide some useful information.

== with flags enabled ===== This came out on connection only. Play, stop, volume after this yielded nothing.

signal time=1571750128.949492 sender=:1.178 -> destination=(null destination) serial=8 path=/org/gnome/ShairportSync; interface=org.freedesktop.DBus.Properties; member=PropertiesChanged string "org.gnome.ShairportSync.RemoteControl" array [ dict entry( string "Server" variant string "fdaa:bbcc:ddee:0:10a3:c1be:397e:a1e2" ) ] array [ ]

signal time=1571750129.282383 sender=:1.178 -> destination=(null destination) serial=9 path=/org/gnome/ShairportSync; interface=org.freedesktop.DBus.Properties; member=PropertiesChanged string "org.gnome.ShairportSync.RemoteControl" array [ dict entry( string "Metadata" variant array [ ] ) ] array [ ]

Thanks.

mikebrady commented 5 years ago

I'm not a Python guy, so if you could post some of your Python examples, I might get the hang of it sufficiently to check this out...

JulianWAS commented 5 years ago

Ha, you and me both! I'll look around for the best and easiest example.

JulianWAS commented 5 years ago

Here are two files I use to monitor Bluetooth transport status and volume. If you run them and connect some music via Bluetooth on your RPi you'll see DBus info. Python has it's good points at least one can change a file and save it without recompilation on board. Depending on how much you've used it, don't forget it's all about the indentation ( tabs or spaces ) separating functions and conditional statements etc. I much prefer C but Python has it's good points especially for hacking stuff like this..

bluetooth-python.zip

JulianWAS commented 5 years ago

Hi,

After discovering

run_this_when_volume_is_set = "/full/path/to/application/and/args"; // Run the specified application whenever the volume control is set or changed. // The desired AirPlay volume is appended to the end of the command line – leave a space if you want it treated as an extra argument. // AirPlay volume goes from 0 to -30 and -144 means "mute".

and run_this_before_play_begins = run_this_after_play_ends =

I think this will suffice for the DBus stuff. Thanks again.

mikebrady commented 5 years ago

Okay. Good stuff.