owntone / owntone-server

Linux/FreeBSD DAAP (iTunes) and MPD media server with support for AirPlay 1 and 2 speakers (multiroom), Apple Remote (and compatibles), Chromecast, Spotify and internet radio.
https://owntone.github.io/owntone-server
GNU General Public License v2.0
2.01k stars 231 forks source link

Handle Airplay stereo group as one speaker instead of two individual speakers #1413

Open ma-ku opened 2 years ago

ma-ku commented 2 years ago

When two Airplay speakers have been joined into a stereo pair, they still show up as two individual speakers. While playback of stereo streams seem to be working starting with audioOS 15.4 (see #1291), the UI still does not reflect the pairing and does not show the name of the stereo pair.

Expected behavior would be that instead of two speakers the UI would only show the stereo group with its name and eventually a visual clue that this is a stereo pair. When this pair is selected as output, then internally the server should stream content to the speakers involved in that group.

Initial work has been done on the backend in a forked repo. There, the parsing of the Airplay outputs is reflecting the grouping information found in mDNS and adds the information to the extended airplay record. It needs to be discussed, how the information is handled internally so that the UI can render the correct speaker information while the backend would need to know, which speakers are involved. Some guidance from @ejurgensen would be required to complete the work.

ejurgensen commented 2 years ago

Thanks for opening this issue. I'm thinking if we should expand the scope to include grouped speakers that are not in stereo mode? When changing this stuff it would be nice to do it in a way that can support this too.

Could you also share screenshots of iOS speaker selection, both with a stereo group and a non-stereo group? In the former case I understand the individual speakers are not listed, but it's not clear to me if the latter case shows individual speakers? And if so, how (is there individual volume control?). I'm also curious how the Remote app shows grouped speakers - if it can, then we have to check how grouping is communicated via dacp.

With regard to implementation, my initial thought is that struct output_device needs to be expanded with group_name and group_id. So not unlike what you did in your fork, just not in the "extra" struct. It needs to be in struct output_device because otherwise it cannot be exposed to the player, which in turn exposes it to the client modules (json api, daap, mpd etc.) via struct player_speaker_info. I'm not sure how the latter should be expanded, I think the screenshots would help clarify that.

ma-ku commented 2 years ago

Thanks for opening this issue. I'm thinking if we should expand the scope to include grouped speakers that are not in stereo mode? When changing this stuff it would be nice to do it in a way that can support this too.

From what I have seen so far, this is basically the same. Internally, then stream must be sent to any of the peers in a group, no matter if it is a stereo pair or any other group. The main difference I have seen so far between a custom built group and a stereo pair is the ability to assign a name to the stereo pair ("gpn" attribute).

Could you also share screenshots of iOS speaker selection, both with a stereo group and a non-stereo group? In the former case I understand the individual speakers are not listed, but it's not clear to me if the latter case shows individual speakers? And if so, how (is there individual volume control?). I'm also curious how the Remote app shows grouped speakers - if it can, then we have to check how grouping is communicated via dacp.

Will attach screenshots for as many scenarios as possible.

With regard to implementation, my initial thought is that struct output_device needs to be expanded with group_name and group_id. So not unlike what you did in your fork, just not in the "extra" struct. It needs to be in struct output_device because otherwise it cannot be exposed to the player, which in turn exposes it to the client modules (json api, daap, mpd etc.) via struct player_speaker_info. I'm not sure how the latter should be expanded, I think the screenshots would help clarify that.

I had the same thought. I was actually moving back and forth between the two. The true challenge that I see Is that we have two or more group members and on the UI side, we only want to show one speaker. So enabling the speaker and thus the associated (single) output_device is not sufficient. We will need to translate between the UI portion and the backend logic. My idea was also to move the info into the struct output_device but in the call that builds the list of all speakers, this information is used to remove peer speakers from the list of returned speakers (like a distinct gidquery).

Once the speaker is selected, the UI would know that the speaker is a group and trigger playback on any output_device with the same group_id.

ma-ku commented 2 years ago

Screenshots:

Below you can see how individual speakers can be selected in the Airplay control panel. Once the connection is established, a checkmark is shown:

GroupSpeakers

If the Airplay control panel was closed and is opened again, the selected speakers will be displayed as a group:

GroupedSpeakers

The group entry can be unfold by tapping on it, showing the first screen again.

The even two groups can be grouped together into one (basically this dissolves the other group, no nesting occurs):

GroupingGroups

The entry changes its icon depending on the content of the group. This time including an Apple TV:

BigGroupEntry

Unfolding the entry shows all members individually (like above with two speakers) and now folding is possible except for closing the Airplay control panel and reopening it:

BigGroup

Legend: The above example is using the following devices:

ma-ku commented 2 years ago

I have spent some more time analyzing the metadata for the scenario below:

Scenario

We have a Sonos/Symfonisk speaker, a stereo pair of (legacy) HomePods, and a single HomePod Mini. The speakers are the following:

Below is the mDNS dump for this setup:

=  wlan0 IPv4 Links                                         _airplay._tcp        local
   hostname = [Links.local]
   address = [192.168.178.46]
   port = [7000]
   txt = [
        "vv=2" 
        "osvers=15.4" 
        "srcvers=610.14.1" 
        "pk=40e4f71d4e7153f65b5b906e5c2e5a0011b89dd1b5bf598e939fae406e7d34f0" 
        "psi=3CF62C5C-9249-47F0-98A7-6FA451CA53B1" 
        "pi=3af67dea-4913-441e-898f-0ae9e675ab4a" 
        "protovers=1.1" 
        "model=AudioAccessory1,1" 
        "tsm=0" 
        "tsid=EAFA36AA-9785-54B2-A537-D9EE2A55CF1C" 
        "pgcgl=0" 
        "pgid=A918B6A2-BB3F-4A50-A422-0BB043C9F3BF" 
        "gpn=Büro 2" 
        "gcgl=0" 
        "igl=0" 
        "gid=A918B6A2-BB3F-4A50-A422-0BB043C9F3BF" 
        "flags=0xbac04" 
        "features=0x4A7FCA00,0xBC354BD0" 
        "fex=AMp/StBLNbw" 
        "deviceid=D4:A3:3D:7A:28:D8" 
        "btaddr=5D:74:09:28:97:F2" 
        "acl=0"]

=  wlan0 IPv4 B__rok__che                                   _airplay._tcp        local
   hostname = [Burokuche.local]
   address = [192.168.178.49]
   port = [7000]
   txt = [
        "vv=2" 
        "osvers=15.4" 
        "srcvers=610.14.1" 
        "pk=ec12c623fbcc82ba197fd77714bbfff63045b13482487e8a5ffda60bef6148ef" 
        "psi=A683442A-A5B4-44FB-ADBF-136625DF40AC" 
        "pi=7ee13959-a6df-48e3-a38b-7fff4fdca20c" 
        "protovers=1.1" 
        "model=AudioAccessory5,1" 
        "pgcgl=0" 
        "pgid=A918B6A2-BB3F-4A50-A422-0BB043C9F3BF" 
        "gcgl=0" 
        "igl=0" 
        "gid=A918B6A2-BB3F-4A50-A422-0BB043C9F3BF" 
        "flags=0xb8c04" 
        "features=0x4A7FCA00,0xBC356BD0" 
        "fex=AMp/StBrNbw" 
        "deviceid=E0:2B:96:96:FB:77" 
        "btaddr=42:B9:EB:EE:5C:F3" 
        "acl=0"]

=  wlan0 IPv4 Rechts                                        _airplay._tcp        local
   hostname = [Rechts.local]
   address = [192.168.178.56]
   port = [7000]
   txt = [
        "vv=2" 
        "osvers=15.4" 
        "srcvers=610.14.1" 
        "pk=44398512772475964d6156e8a68485325ae773ee23a4698239412b3b407bedef" 
        "psi=85195D3E-AA83-4C5C-A20E-CD309C8C33AD" 
        "pi=0d39b4ce-fd53-4203-b272-efa6fc68d825" 
        "protovers=1.1" 
        "model=AudioAccessory1,1" 
        "tsm=0" 
        "tsid=EAFA36AA-9785-54B2-A537-D9EE2A55CF1C" 
        "pgcgl=0" 
        "pgid=A918B6A2-BB3F-4A50-A422-0BB043C9F3BF" 
        "gpn=Büro 2" 
        "gcgl=0" 
        "igl=0" 
        "gid=A918B6A2-BB3F-4A50-A422-0BB043C9F3BF" 
        "flags=0xb8c04" 
        "features=0x4A7FCA00,0xBC354BD0" 
        "fex=AMp/StBLNbw" 
        "deviceid=50:BC:96:07:E8:6D" 
        "btaddr=7F:F1:BE:47:D7:28" 
        "acl=0"]
=  wlan0 IPv4 Basement                                      _airplay._tcp        local
   hostname = [Sonos-347E5C31D996.local]
   address = [192.168.178.91]
   port = [7000]
   txt = [
        "pk=ffdf084c9ed44061f76cd50917208fe388d83eaa46962bf72205e0a493e9b166" 
        "isGroupLeader=0" 
        "gcgl=0" 
        "gid=A918B6A2-BB3F-4A50-A422-0BB043C9F3BF" 
        "pi=34:7E:5C:31:D9:96" 
        "srcvers=366.0" 
        "protovers=1.1" 
        "serialNumber=34-7E-5C-31-D9-96:F" 
        "manufacturer=Sonos" 
        "model=Bookshelf" 
        "flags=0x804" 
        "fv=p20.67.1-25031" 
        "rsf=0x0" 
        "features=0x445F8A00,0x1C340" 
        "deviceid=34:7E:5C:31:D9:96" 
        "acl=0"]

Observations:

ejurgensen commented 2 years ago

Not sure I quite understand this. Like how are the speakers negotiating group id?

The mdns output is also very different from what you shared here. Even the length of the group id seems to have changed? In the previous post the stereo Homepods Links and Rechts even had different gid's, which is confusing.

ma-ku commented 2 years ago

Hmm. The different gid values for Links and Rechts are weird. Maybe I took the snapshot during a reconfiguration of the setup?

Regarding the negotiation. This is nothing that happens magically. The user initiates the setup of the groups and thus its either the device in the users hands that does all the configuration including the creation if identifiers, or the first speaker in a group takes the lead. However, it does not matter for our scenario because owntone is just taking the resulting metadata. I would not try to build groups from the owntone UI.

I guess the rule is:

Makes sense?

ma-ku commented 2 years ago

Links and Rechts even had different gid's, which is confusing.

Good catch. However, the gid still is the same if you strip off the data after the first '+' character. I would suggest to apply the above rules but to strip the gid down the first of the three entries. Maybe one day we can figure out what the rest of the data means.

=  wlan0 IPv4 Links                                         _airplay._tcp        local
   hostname = [Links.local]
   address = [192.168.178.46]
   port = [7000]
   txt = [
        "vv=2" 
        "osvers=15.4" 
        "srcvers=610.14.1" 
        "pk=40e4f71d4e7153f65b5b906e5c2e5a0011b89dd1b5bf598e939fae406e7d34f0" 
        "psi=3CF62C5C-9249-47F0-98A7-6FA451CA53B1" 
        "pi=3af67dea-4913-441e-898f-0ae9e675ab4a" 
        "protovers=1.1" 
        "model=AudioAccessory1,1" 
        "tsm=0" 
        "tsid=EAFA36AA-9785-54B2-A537-D9EE2A55CF1C" 
        "gpn=Büro 2" 
        "gcgl=1" 
        "igl=1" 
        "gid=EAFA36AA-9785-54B2-A537-D9EE2A55CF1C+0+6276FFFA-04E1-439E-8139-2C906B34E587" 
        "flags=0x9a404" 
        "features=0x4A7FCA00,0xBC354BD0" 
        "fex=AMp/StBLNbw" 
        "deviceid=D4:A3:3D:7A:28:D8" 
        "btaddr=6C:27:9A:26:46:1A" 
        "acl=0"]

=  wlan0 IPv4 Rechts                                        _airplay._tcp        local
   hostname = [Rechts.local]
   address = [192.168.178.56]
   port = [7000]
   txt = [
        "vv=2" 
        "osvers=15.4" 
        "srcvers=610.14.1" 
        "pk=44398512772475964d6156e8a68485325ae773ee23a4698239412b3b407bedef" 
        "psi=85195D3E-AA83-4C5C-A20E-CD309C8C33AD" 
        "pi=0d39b4ce-fd53-4203-b272-efa6fc68d825" 
        "protovers=1.1" 
        "model=AudioAccessory1,1" 
        "tsm=0" 
        "tsid=EAFA36AA-9785-54B2-A537-D9EE2A55CF1C" 
        "gpn=Büro 2" 
        "gcgl=1" 
        "igl=1" 
        "gid=EAFA36AA-9785-54B2-A537-D9EE2A55CF1C+1+4671EEC3-7E13-4DC7-BEC3-C9805D3AB964" 
        "flags=0x98404" 
        "features=0x4A7FCA00,0xBC354BD0" 
        "fex=AMp/StBLNbw" 
        "deviceid=50:BC:96:07:E8:6D" 
        "btaddr=40:9E:6F:4A:8A:77" 
        "acl=0"]

I found similar data for the other stereo pair:

=  wlan0 IPv4 Links (2)                                     _airplay._tcp        local
   hostname = [Links-2.local]
   address = [192.168.178.41]
   port = [7000]
   txt = [
        "vv=2" 
        "osvers=15.4" 
        "srcvers=610.14.1" 
        "pk=5a14d439edf3ad0926f0c96c3059be33861df97c6f72380ab54ea18478e425c0" 
        "psi=7E5AF368-C056-43BF-953B-7C7EA3B9D27F" 
        "pi=01c6816f-3347-4ddd-8cab-e6a735c9b5a9" 
        "protovers=1.1" 
        "model=AudioAccessory5,1" 
        "gpn=Büroküche" 
        "gcgl=1" 
        "igl=1" 
        "gid=A7D5EDDB-26B0-4D2E-A921-06949E8E8675+0+1D921AC6-B591-4141-87E9-D4CF73033038" 
        "flags=0x98404" 
        "features=0x4A7FCA00,0xBC354BD0" 
        "fex=AMp/StBLNbw" 
        "deviceid=E0:2B:96:89:74:9C" 
        "btaddr=54:7C:5E:6E:AD:2F" 
        "acl=0"]

=  wlan0 IPv4 B__rok__che (2)                               _airplay._tcp        local
   hostname = [Keller.local]
   address = [192.168.178.49]
   port = [7000]
   txt = [
        "vv=2" 
        "osvers=15.4" 
        "srcvers=610.14.1" 
        "pk=ec12c623fbcc82ba197fd77714bbfff63045b13482487e8a5ffda60bef6148ef" 
        "psi=A683442A-A5B4-44FB-ADBF-136625DF40AC" 
        "pi=7ee13959-a6df-48e3-a38b-7fff4fdca20c" 
        "protovers=1.1" 
        "model=AudioAccessory5,1" 
        "tsm=1" 
        "tsid=9270BA34-547F-5E97-AB55-B39F6BC9579A" 
        "gpn=Büroküche" 
        "gcgl=1" 
        "igl=1" 
        "gid=9270BA34-547F-5E97-AB55-B39F6BC9579A+0+DB47A04B-0AC2-42A7-8AC7-1A7C3CA85C85" 
        "flags=0x9e404" 
        "features=0x4A7FCA00,0xBC356BD0" 
        "fex=AMp/StBrNbw" 
        "deviceid=E0:2B:96:96:FB:77" 
        "btaddr=56:57:ED:A0:55:B8" 
        "acl=0"]

The data after the first entry of the gid cannot be found anywhere else in the mDNS dump. So no clue what this could be. The second entry is zero for both entries probably is does not indicate left/right.

ejurgensen commented 2 years ago

Could it mean that a speaker can be member of multiple groups? Where each group is separated by '+'? I suppose we should then take that into consideration when implementing it in owntone. Maybe struct output_device needs to have an array of groups.

ma-ku commented 2 years ago

I don't think that this is the case. First of all, I did not find any reference of the third parameter anywhere. To me, the grouping is mainly a UI thing. The only case where I see speakers being in two groups is stereo pairs where the initial group is the stereo pair and the pgid forms the group. I would say I have a fairly broad set of devices here in my house and I have not been able to build other groups like the ones we have seen.

The key difference between groups is that the ones we can build manually in the Airplay control panel have a transient character. And I must admit, I do not know when they get dissolved. The stereo pairs are persistent (and cannot be built from the Airplay panel) and have a name.

Same is true for Sonos stereo pairs. These even completely eliminate the two speakers from Airplay and replace them with a single speaker. The stereo playback is basically handled by the Sonos logic itself.

aleszczynskig commented 2 years ago

Just wanted to update that I intend to contribute to this however I am not able to get my stereo pair working correctly. I have realised that my stereo pair is not actually forming despite the Home app showing the group has been created. @ma-ku - are you having any issues with your HomePods? I have tried everything including resetting both HomePods and I am still having no luck.

In retrospect I realise I've had this bug since I deployed 15.4. Initially I thought this was a transient issue where the HomePods were configuring themselves but actually my stereo pair has never formed correctly.

UPDATE - it has sprung to life.

ma-ku commented 2 years ago

Since we do not know the protocol required to setup speaker groups, we will just digest what is provided by mDNS. I was thinking about expanding the output_device struct with the two group ids (pgid, gid) and the group name (gpn) if available.

On the UI side, the behavior of speaker_enumerate would need to be changed to reflect the addtl. data.

I am not sure if it suffices to only provide the information about the groups and nesting on the UI side. There it would be possible to introduce a new speaker type that represents a group (and has children). The resulting JSON would be comprised of speakers and groups whereas a group can only contain speakers (max height two for the tree). This would keep the UI simple. The group would get the name built from the names of the speakers. Stereo pairs would again be treated as a speaker but could reference the underlying devices represented by player_speaker_info.

Selection of speakers would then internally be dispatched to the associated players and vice versa.

@ejurgensen: Does that make sense or do you see any issues with that approach with regards to other areas of the code?

ma-ku commented 2 years ago

UPDATE - it has sprung to life.

Yep. Things need some time. As I already alluded to in the other issue. Sometimes I was too fast and this messed up the observations. Good to hear that you are back in the game.

Stereo is working?

aleszczynskig commented 2 years ago

Yes - it is. Just tested with stereo!

It took several hours but it got there in the end - I've never seen that before 15.4. No doubt there are a few bugs Apple need to iron out.

aleszczynskig commented 2 years ago

I've done some testing of the way the Remote app works with the Music app on MacOS 12.2. Here is a screenshot. I have noticed that this is actually a little buggy. For example, the Apple TV output only shows if the output window is selected in the Music app as you see in the second screen shot otherwise it disappears from the list.

IMG_01EF579F4B0B-1

Screenshot 2022-02-03 at 11 00 36

For reference, my two HomePods are configured in a stereo pair and set as the default output for my Apple TV. You can see they show as an Apple TV in the Remote app and the app has no awareness of the underlying speaker configuration. I have checked and it is working correctly as you would expect. Should this be the MVP for OwnTone functionality?

ejurgensen commented 2 years ago

Does that make sense or do you see any issues with that approach with regards to other areas of the code?

I'm not sure I completely understood the idea, so pardon me if I got it wrong. I'm thinking that we have to take into account that the player (via speaker_enumerate) serves speaker lists to multiple interfaces, and that some of them don't have the concept of groups. So while the json API/web UI can be modified to understand groups, there are limitations with regard to mpd and also dacp, as it would seem from @aleszczynskig screenshots.

So it would be easiest if speaker_enumerate is modified, so that it doesn't necessarily return individual speakers, but rather groups plus non-grouped speakers. That would be in line with the MVP you suggest @aleszczynskig, and should mean that Remote (and mpd) without any further changes displays a list similar to the above.

The suggestion you have @ma-ku about using a group type sounds sensible to me, since in the future a group could perhaps even contain a mix of Airplay and other devices. Then later we could add expansion of groups, individual volume control and maybe even creating groups.

This would require that either in player.c or in outputs.c there has to be group <> individual speaker translation. The most appropriate place is outputs.c, but I'm not sure if that is possible.

ma-ku commented 2 years ago

@ejurgensen´: Thank you for your insights. I will take a first step and implement the mDNS parsing and the JSON generation. And then we can see how things come together.

ma-ku commented 2 years ago

@ejurgensen: I am in the process of reading the metadata and pushing it to the frontend. Unfortunately the JSON is empty like the player_speaker_info. The device_info is populated with additional attributes in airplay.c but the data is somehow deleted at some point. Is there anything that I am missing? Is there a duplication or a copy operation that needs to respect the additional fields?

Never mind. Airplay 1 vs. Airplay 2 selection killed my precious data.

ma-ku commented 2 years ago

Update: The general handling of the mDNS data is in place. However, the mapping between device and speaker_info is problematic as the device.id is used also as speaker.id. Since we now can have a 1:1..n mapping, this is getting problematic. Any advice before I start digging deeper?

ma-ku commented 2 years ago

I have pushed a first version that shows stereo speakers as one device and forwards start/stop events to the underlying individual devices. With that the first step is taken to support groups. I did not have the chance to test various scenarios such as authentication and other use cases.

aleszczynskig commented 2 years ago

@ma-ku - thanks for your efforts on this? Where have you pushed this too? Is it a branch or your forked-repo?

ma-ku commented 2 years ago

@ma-ku - thanks for your efforts on this? Where have you pushed this too? Is it a branch or your forked-repo?

In a branch feature/grouping on my fork.

shauder commented 2 years ago

@ma-ku Is there anything special you need to do in yours to get the group to show instead of the individual speakers? I tried to run your fork and it doesn't seem to show the group but I may have done it wrong.

ma-ku commented 2 years ago

Well, check that you have built from the branch.

And also be aware that this (intermediate) version only shows stereo pairs as ones Groups are the next iteration. At first I had to handle these devices that are statically grouped. Next step would be handling of speaker groups.

shauder commented 2 years ago

I did build from the feature/grouping branch.

git clone https://github.com/ma-ku/owntone-server.git /tmp/source/owntone && \ export PATH="/tmp/source:$PATH" && \ cd /tmp/source/owntone && \ git checkout feature/grouping && \ autoreconf -i -v && \ ./configure \ --build=$CBUILD \ --enable-chromecast \ --enable-itunes \ --enable-lastfm \ --enable-mpd \ --enable-spotify \ --host=$CHOST \ --infodir=/usr/share/info \ --localstatedir=/var \ --mandir=/usr/share/man \ --prefix=/usr \ --sysconfdir=/etc && \ make

I have 4 stereo pairs and they all show up as individual speakers and when I hover over show they are only using AirPlay2. I tried to modify the linuxserver DockerFile to build yours. Maybe I did something wrong there but I don't see it yet.

https://github.com/shauder/docker-daapd/blob/master/Dockerfile

ejurgensen commented 2 years ago

It looks like you haven't pushed the changes @ma-ku? The diff doesn't seem to have the changes you describe: https://github.com/owntone/owntone-server/compare/master...ma-ku:feature/grouping

ma-ku commented 2 years ago

Forgive me. You are right. Somehow the initial push never made it and I did not verify. Code is up on GitHub now.

ma-ku commented 2 years ago

Never mind. Changed user in owntone.conf also to root. Not the best way but OK for development for now.

As we are talking. On my development machine (Debian VM), I am all over sudden receiving the following error during startup and I cannot run owntone anymore.

No protocol specified
xcb_connection_has_error() returned true
Failed to create secure directory (/root/.config/pulse): Permission denied

I am running owntone from the build directory using sudo – technically I would think that this should work, right?

shauder commented 2 years ago

I rebuilt the docker image with the new commits you pushed and am having some success. I noticed when it first starts the speakers are still on their own. After a while I came back and most of them seem grouped. Right now I only have one pair as 15.4 and when i select their group I get audio from both speakers. Will upgrade my other homepods and do more testing. Here is the image for anyone else that may want to test.

  daapd:
    image: shauder/daapd:latest
    container_name: daapd
    network_mode: host
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=Europe/London
    volumes:
      - /data/docker/daapd:/config
      - /data/media/music:/music
    restart: unless-stopped
shauder commented 2 years ago

My setup right now is:

Living Room: 2 Stereo Homepods and AppleTV with HomePods as default audio Office: Stereo Homepods and no AppleTV Bedroom: 2 Stereo Homepods and AppleTV with HomePods as default audio

Everything is 15.4 developer beta. When OwnTone first starts everything shows up as an AirPlay1 device. Both all the HomePods and the AppleTVs. Then eventually the Bedroom pair/tv shows up as an AirPlay2 grouped device that I can select. Sometimes the Living Room pair/tv also show up and I have successfully selected both pairs as two objects in the UI and had audio coming from all 4 HomePods. The office pair has shown up as an AirPlay2 pair but every time I have tried to select it, it disappears and doesn't play.

I am not seeing anything useful in the logs to share. I may need to enable a debug setting but I am not sure. If there's anything I can provide to help let me know and I'd be happy to test and share.

image

The first Living Room entry is the AppleTV and same with the first Bedroom entry. The Office entries are each of the Office HomePod stereo pairs showing up individually. The second Living Room and Bedroom entries are the grouped Stereo pairs that are also default audio on the AppleTV's.

I tried resetting the Office HomePods and regrouping them but it does not seem to have fixed anything.

ma-ku commented 2 years ago

@shauder In general, the devices can be detected as Airplay 1 devices and as Airplay 2 devices, typically as both. In earlier versions the Airplay 1 version took precedence over Airplay 2. I changed to logic in that way that a device that has either a device grouping (stereo pair) or a playback grouping (ad-hoc grouping done via the Airplay control panel of iTunes), then this device (Airplay 2) takes precedence over the Airplay 1 device.

Now the challenge; I was working on the latter type of grouping today and was already able to render groups in the UI but every time I changed the config in the Airplay panel, the mDNS information somehow got messed up. It sometimes takes a while until these changes have been properly sorted out in the metadata. It even felt that these updates were not properly propagated from the underlying mDNS client implementation to owntone and so the internal metadata was left garbled.

I will need to further understand why things start getting messy at some point.

What is helpful for the analysis is a dump of the mDNS data in your network using avahi-browse -r -k _airplay._tcp.

You can look for the metadata yourself:

gid sometimes has a '+' separator, only the first entry denotes the group, the other values are unclear at least for now.

So if you run into an incorrect scenario, then grab the mDNS dump and post it here. We can sort it out.

shauder commented 2 years ago

@ma-ku thanks for all the work you've done already to get this started it's really awesome to see! I am posting this all only to provide more info in case it helps.

the devices can be detected as Airplay 1 devices and as Airplay 2 devices, typically as both. In earlier versions the Airplay 1 version took precedence over Airplay 2. I changed to logic in that way that a device that has either a device grouping (stereo pair) or a playback grouping (ad-hoc grouping done via the Airplay control panel of iTunes), then this device (Airplay 2) takes precedence over the Airplay 1 device.

This generally describes what I am seeing. When OwnTone first launches all of the devices show up as non-grouped AirPlay1 devices. If I let everything settle down for a while then the AirPlay2 stereo pairs show up within the UI.

Here is when it first starts: image

Here is after I left it settle for a few minutes: image

Here is my iPhone: Screenshot 2022-02-08 at 9 29 31 PM

The top two entries here are AirPlay1 entries for the Living Room Apple TV and the Bedroom Apple TV. The next 3 entries are AirPlay2 entries for each of my 3 stereo HomePod pairs I have connected. The two stereo pairs that are selected as default audio output for the Bedroom and Living Room Apple TV's show up as the Apple TV name instead of the stereo pair name (Their actual names are 'Bedroom HomePods' and 'Living Room HomePods'). I also find that I am only able to select and play music to the stereo pairs that have Apple TV's. The Office, which does not, will deselect when I try and select it and switch from being two AirPlay1 devices to AirPlay2 devices.

This is my setup and also my avahi-browse output.

calvin% sudo avahi-browse -r -k _airplay._tcp
+   eno1 IPv4 Living Room Apple TV                          _airplay._tcp        local
+   eno1 IPv6 Living Room Apple TV                          _airplay._tcp        local
=   eno1 IPv4 Living Room Apple TV                          _airplay._tcp        local
   hostname = [Living-Room-Apple-TV.local]
   address = [xxx.xxx.xxx.157]
   port = [7000]
   txt = [
      "vv=2"
"osvers=15.4"
"srcvers=610.14.1" 
      "pk=7726f39c9bbfce682eb201158b6dc0a37290ff695c231192d5d0a34249f8cab0" 
      "psi=69F74E37-9440-46FA-A909-EA91BD9B31BD" 
      "pi=4fc10a37-13e0-4225-a934-6b8c7935ec18" 
      "protovers=1.1" 
      "model=AppleTV11,1" 
      "pgm=0" 
      "psgid=3D207050-3E41-5BA2-A970-82854250ABFD" 
      "psgtp=1" 
      "psgsz=3" 
      "gpn=Living Room Apple TV" 
      "gcgl=1" 
      "igl=1" 
      "gid=3D207050-3E41-5BA2-A970-82854250ABFD" 
      "flags=0x18644" 
      "features=0x4A7FDFD5,0xBC157FDE" 
      "fex=1d9/St5/FbwI" 
      "deviceid=58:D3:49:E7:EB:CE" 
      "btaddr=58:D3:49:EB:A7:65"
"acl=0"]
=   eno1 IPv6 Living Room Apple TV                          _airplay._tcp        local
   hostname = [Living-Room-Apple-TV.local]
   address = [xxx.xxx.xxx.157]
   port = [7000]
   txt = [
      "vv=2"
      "osvers=15.4"
      "srcvers=610.14.1"
      "pk=7726f39c9bbfce682eb201158b6dc0a37290ff695c231192d5d0a34249f8cab0"
      "psi=69F74E37-9440-46FA-A909-EA91BD9B31BD"
      "pi=4fc10a37-13e0-4225-a934-6b8c7935ec18"
      "protovers=1.1"
      "model=AppleTV11,1"
      "pgm=0"
      "psgid=3D207050-3E41-5BA2-A970-82854250ABFD"
      "psgtp=1"
      "psgsz=3"
      "gpn=Living Room Apple TV"
      "gcgl=1"
      "igl=1"
      "gid=3D207050-3E41-5BA2-A970-82854250ABFD"
      "flags=0x18644"
      "features=0x4A7FDFD5,0xBC157FDE"
      "fex=1d9/St5/FbwI"
      "deviceid=58:D3:49:E7:EB:CE"
      "btaddr=58:D3:49:EB:A7:65"
      "acl=0"]
+   eno1 IPv4 Office Right HomePod                          _airplay._tcp        local
=   eno1 IPv4 Office Right HomePod                          _airplay._tcp        local
   hostname = [Office.local]
   address = [xxx.xxx.xxx.156]
   port = [7000]
   txt = [
      "vv=2"
      "osvers=15.4"
      "srcvers=610.14.1"
      "pk=9c5c1d46641c8cf1a1bfb56b56251318532c6355df9337a24a3a09e33f919b43"
      "psi=1E003E0D-CE6E-4038-BF2F-0118CF559650"
      "pi=45fe65a0-7d5b-4b35-91de-60b5e191586e"
      "protovers=1.1"
      "model=AudioAccessory1,1"
      "tsm=0"
      "tsid=61D7305A-2881-56F2-B807-63F9AFEBE126"
      "gpn=Office HomePods"
      "gcgl=1"
      "igl=1"
      "gid=61D7305A-2881-56F2-B807-63F9AFEBE126+1+B46E3171-B4F6-4312-A7A5-0ED2FA4DF849"
      "flags=0x98404"
      "features=0x4A7FCA00,0xBC354BD0"
      "fex=AMp/StBLNbw"
      "deviceid=D4:90:9C:CB:1B:BF"
      "btaddr=63:07:F4:5C:C3:32"
      "acl=0"]
+   eno1 IPv6 Office Right HomePod                          _airplay._tcp        local
=   eno1 IPv6 Office Right HomePod                          _airplay._tcp        local
   hostname = [Office.local]
   address = [xxx.xxx.xxx.156]
   port = [7000]
   txt = [
      "vv=2"
      "osvers=15.4"
      "srcvers=610.14.1"
      "pk=9c5c1d46641c8cf1a1bfb56b56251318532c6355df9337a24a3a09e33f919b43"
      "psi=1E003E0D-CE6E-4038-BF2F-0118CF559650"
      "pi=45fe65a0-7d5b-4b35-91de-60b5e191586e"
      "protovers=1.1"
      "model=AudioAccessory1,1"
      "tsm=0"
      "tsid=61D7305A-2881-56F2-B807-63F9AFEBE126"
      "gpn=Office HomePods"
      "gcgl=1"
      "igl=1"
      "gid=61D7305A-2881-56F2-B807-63F9AFEBE126+1+B46E3171-B4F6-4312-A7A5-0ED2FA4DF849"
      "flags=0x98404"
      "features=0x4A7FCA00,0xBC354BD0"
      "fex=AMp/StBLNbw"
      "deviceid=D4:90:9C:CB:1B:BF"
      "btaddr=63:07:F4:5C:C3:32"
      "acl=0"]
+   eno1 IPv4 Office Left HomePod                           _airplay._tcp        local
=   eno1 IPv4 Office Left HomePod                           _airplay._tcp        local
   hostname = [Office-2.local]
   address = [xxx.xxx.xxx.101]
   port = [7000]
   txt = [
      "vv=2"
      "osvers=15.4"
      "srcvers=610.14.1"
      "pk=b758d16aa1fa16913934531fd2568e859b5ce0f5fc33f8b7de7ced203a323bdc"
      "psi=9971AD3B-1129-42E2-965B-C97D42A2E283"
      "pi=dcd26c99-d6fd-406f-89a3-809d1252fe10"
      "protovers=1.1"
      "model=AudioAccessory1,1"
      "tsm=0"
      "tsid=61D7305A-2881-56F2-B807-63F9AFEBE126"
      "gpn=Office HomePods"
      "gcgl=1"
      "igl=1"
      "gid=61D7305A-2881-56F2-B807-63F9AFEBE126+0+957D47E0-7FDF-4094-A86B-94C28C393A3B"
      "flags=0x9a404"
      "features=0x4A7FCA00,0xBC356BD0"
      "fex=AMp/StBrNbw"
      "deviceid=D4:A3:3D:6A:E2:AC"
      "btaddr=7C:23:65:0B:56:9F"
      "acl=0"]
+   eno1 IPv6 Office Left HomePod                           _airplay._tcp        local
=   eno1 IPv6 Office Left HomePod                           _airplay._tcp        local
   hostname = [Office-2.local]
   address = [xxx.xxx.xxx.101]
   port = [7000]
   txt = [
      "vv=2"
      "osvers=15.4"
      "srcvers=610.14.1"
      "pk=b758d16aa1fa16913934531fd2568e859b5ce0f5fc33f8b7de7ced203a323bdc"
      "psi=9971AD3B-1129-42E2-965B-C97D42A2E283"
      "pi=dcd26c99-d6fd-406f-89a3-809d1252fe10"
      "protovers=1.1"
      "model=AudioAccessory1,1"
      "tsm=0"
      "tsid=61D7305A-2881-56F2-B807-63F9AFEBE126"
      "gpn=Office HomePods"
      "gcgl=1"
      "igl=1"
      "gid=61D7305A-2881-56F2-B807-63F9AFEBE126+0+957D47E0-7FDF-4094-A86B-94C28C393A3B"
      "flags=0x9a404"
      "features=0x4A7FCA00,0xBC356BD0"
      "fex=AMp/StBrNbw"
      "deviceid=D4:A3:3D:6A:E2:AC"
      "btaddr=7C:23:65:0B:56:9F"
      "acl=0"]
+   eno1 IPv4 Bedroom Apple TV                              _airplay._tcp        local
+   eno1 IPv6 Bedroom Apple TV                              _airplay._tcp        local
=   eno1 IPv4 Bedroom Apple TV                              _airplay._tcp        local
   hostname = [Bedroom-Apple-TV.local]
   address = [xxx.xxx.xxx.170]
   port = [7000]
   txt = [
      "vv=2"
      "osvers=15.4"
      "srcvers=610.14.1"
      "pk=96646dbf5fd2692154baccb77751bdafd2b81e29f93918077c18ae1431efc6cf"
      "psi=BC6A6199-792D-4027-AB8B-C4962224E7B1"
      "pi=ee759ded-10cb-4c82-90c6-efc7b7bb56f2"
      "protovers=1.1"
      "model=AppleTV11,1"
      "pgm=0"
      "psgid=5758DCB6-6E1A-5E69-99CD-74807BE84F23"
      "psgtp=1"
      "psgsz=3"
      "gpn=Bedroom Apple TV"
      "gcgl=1"
      "igl=1"
      "gid=5758DCB6-6E1A-5E69-99CD-74807BE84F23"
      "flags=0x18644"
      "features=0x4A7FDFD5,0xBC157FDE"
      "fex=1d9/St5/FbwI"
      "deviceid=4C:AB:4F:8A:03:81"
      "btaddr=4C:AB:4F:8D:A6:AC"
      "acl=0"]
=   eno1 IPv6 Bedroom Apple TV                              _airplay._tcp        local
   hostname = [Bedroom-Apple-TV.local]
   address = [xxx.xxx.xxx.170]
   port = [7000]
   txt = [
      "vv=2"
      "osvers=15.4"
      "srcvers=610.14.1"
      "pk=96646dbf5fd2692154baccb77751bdafd2b81e29f93918077c18ae1431efc6cf"
      "psi=BC6A6199-792D-4027-AB8B-C4962224E7B1"
      "pi=ee759ded-10cb-4c82-90c6-efc7b7bb56f2"
      "protovers=1.1"
      "model=AppleTV11,1"
      "pgm=0"
      "psgid=5758DCB6-6E1A-5E69-99CD-74807BE84F23"
      "psgtp=1"
      "psgsz=3"
      "gpn=Bedroom Apple TV"
      "gcgl=1"
      "igl=1"
      "gid=5758DCB6-6E1A-5E69-99CD-74807BE84F23"
      "flags=0x18644"
      "features=0x4A7FDFD5,0xBC157FDE"
      "fex=1d9/St5/FbwI"
      "deviceid=4C:AB:4F:8A:03:81"
      "btaddr=4C:AB:4F:8D:A6:AC"
      "acl=0"]
+   eno1 IPv4 Bedroom Right HomePod                         _airplay._tcp        local
+   eno1 IPv6 Bedroom Right HomePod                         _airplay._tcp        local
=   eno1 IPv4 Bedroom Right HomePod                         _airplay._tcp        local
   hostname = [Bedroom-3.local]
   address = [xxx.xxx.xxx.116]
   port = [7000]
   txt = [
      "vv=2"
      "osvers=15.4"
      "srcvers=610.17.41"
      "pk=cb26dae52922ace4a271e7267a5a3217d4ea258df78fd7d54a6dd5f428553b58"
      "psi=BEBD8D78-4CD4-4A68-BAAB-C144C0DDB839"
      "pi=602820f1-f843-48c2-b19d-e2e872d2ccdb"
      "protovers=1.1"
      "model=AudioAccessory1,1"
      "tsm=0"
      "tsid=F67AC6B1-4284-59D4-B270-A570D37FC2A1"
      "pgcgl=1"
      "pgid=5758DCB6-6E1A-5E69-99CD-74807BE84F23"
      "pgm=0"
      "psgid=5758DCB6-6E1A-5E69-99CD-74807BE84F23"
      "psgtp=1"
      "psgsz=3"
      "gpn=Bedroom Apple TV"
      "gcgl=1"
      "igl=0"
      "gid=5758DCB6-6E1A-5E69-99CD-74807BE84F23"
      "flags=0xb8404"
      "features=0x4A7FCA00,0xBC356BD0"
      "fex=AMp/StBrNbw"
      "deviceid=D4:A3:3D:7E:8F:44"
      "btaddr=7E:21:7C:39:B7:07"
      "acl=0"]
=   eno1 IPv6 Bedroom Right HomePod                         _airplay._tcp        local
   hostname = [Bedroom-3.local]
   address = [xxx.xxx.xxx.116]
   port = [7000]
   txt = [
      "vv=2"
      "osvers=15.4"
      "srcvers=610.17.41"
      "pk=cb26dae52922ace4a271e7267a5a3217d4ea258df78fd7d54a6dd5f428553b58"
      "psi=BEBD8D78-4CD4-4A68-BAAB-C144C0DDB839"
      "pi=602820f1-f843-48c2-b19d-e2e872d2ccdb"
      "protovers=1.1"
      "model=AudioAccessory1,1"
      "tsm=0"
      "tsid=F67AC6B1-4284-59D4-B270-A570D37FC2A1"
      "pgcgl=1"
      "pgid=5758DCB6-6E1A-5E69-99CD-74807BE84F23"
      "pgm=0"
      "psgid=5758DCB6-6E1A-5E69-99CD-74807BE84F23"
      "psgtp=1"
      "psgsz=3"
      "gpn=Bedroom Apple TV"
      "gcgl=1"
      "igl=0"
      "gid=5758DCB6-6E1A-5E69-99CD-74807BE84F23"
      "flags=0xb8404"
      "features=0x4A7FCA00,0xBC356BD0"
      "fex=AMp/StBrNbw"
      "deviceid=D4:A3:3D:7E:8F:44"
      "btaddr=7E:21:7C:39:B7:07"
      "acl=0"]
+   eno1 IPv4 Living Room Left HomePod                      _airplay._tcp        local
+   eno1 IPv6 Living Room Left HomePod                      _airplay._tcp        local
=   eno1 IPv4 Living Room Left HomePod                      _airplay._tcp        local
   hostname = [Living-Room-3.local]
   address = [xxx.xxx.xxx.24]
   port = [7000]
   txt = [
      "vv=2"
      "osvers=15.4"
      "srcvers=610.17.41"
      "pk=50551f9829d006546d1f8cf284105920ae25c713b6e6ba5cc5f193460fd4897c"
      "psi=D93FDBFB-F95F-4A82-A2A4-A86903EB3FD8"
      "pi=fef0056d-1260-4ae9-9221-813b3c44fb63"
      "protovers=1.1"
      "model=AudioAccessory1,1"
      "tsm=0"
      "tsid=8E0FC80F-5850-5287-B80A-C6BCD12DD3B7"
      "pgcgl=1"
      "pgid=3D207050-3E41-5BA2-A970-82854250ABFD"
      "pgm=0"
      "psgid=3D207050-3E41-5BA2-A970-82854250ABFD"
      "psgtp=1"
      "psgsz=3"
      "gpn=Living Room Apple TV"
      "gcgl=1"
      "igl=0"
      "gid=3D207050-3E41-5BA2-A970-82854250ABFD"
      "flags=0xba404"
      "features=0x4A7FCA00,0xBC354BD0"
      "fex=AMp/StBLNbw"
      "deviceid=D4:A3:3D:7B:DA:9C"
      "btaddr=6E:D8:0D:76:20:2B"
      "acl=0"]
=   eno1 IPv6 Living Room Left HomePod                      _airplay._tcp        local
   hostname = [Living-Room-3.local]
   address = [xxx.xxx.xxx.24]
   port = [7000]
   txt = [
      "vv=2"
      "osvers=15.4"
      "srcvers=610.17.41"
      "pk=50551f9829d006546d1f8cf284105920ae25c713b6e6ba5cc5f193460fd4897c"
      "psi=D93FDBFB-F95F-4A82-A2A4-A86903EB3FD8"
      "pi=fef0056d-1260-4ae9-9221-813b3c44fb63"
      "protovers=1.1"
      "model=AudioAccessory1,1"
      "tsm=0"
      "tsid=8E0FC80F-5850-5287-B80A-C6BCD12DD3B7"
      "pgcgl=1"
      "pgid=3D207050-3E41-5BA2-A970-82854250ABFD"
      "pgm=0"
      "psgid=3D207050-3E41-5BA2-A970-82854250ABFD"
      "psgtp=1"
      "psgsz=3"
      "gpn=Living Room Apple TV"
      "gcgl=1"
      "igl=0"
      "gid=3D207050-3E41-5BA2-A970-82854250ABFD"
      "flags=0xba404"
      "features=0x4A7FCA00,0xBC354BD0"
      "fex=AMp/StBLNbw"
      "deviceid=D4:A3:3D:7B:DA:9C"
      "btaddr=6E:D8:0D:76:20:2B"
      "acl=0"]
+   eno1 IPv4 Bedroom Left HomePod                          _airplay._tcp        local
+   eno1 IPv6 Bedroom Left HomePod                          _airplay._tcp        local
=   eno1 IPv4 Bedroom Left HomePod                          _airplay._tcp        local
   hostname = [Bedroom.local]
   address = [xxx.xxx.xxx.28]
   port = [7000]
   txt = [
      "vv=2"
      "osvers=15.4"
      "srcvers=610.17.41"
      "pk=37e02248bd5dfbf5f30b9bbcb22002fe9988e1f6039e893909d5a0f49c909a4d"
      "psi=8C9D323E-E102-4945-9333-E0BF454081C1"
      "pi=45259a98-24fa-4e57-8c59-90e7cc63ce27"
      "protovers=1.1"
      "model=AudioAccessory1,1"
      "tsm=0"
      "tsid=F67AC6B1-4284-59D4-B270-A570D37FC2A1"
      "pgcgl=1"
      "pgid=5758DCB6-6E1A-5E69-99CD-74807BE84F23"
      "pgm=0"
      "psgid=5758DCB6-6E1A-5E69-99CD-74807BE84F23"
      "psgtp=1"
      "psgsz=3"
      "gpn=Bedroom Apple TV"
      "gcgl=1"
      "igl=0"
      "gid=5758DCB6-6E1A-5E69-99CD-74807BE84F23"
      "flags=0xba404"
      "features=0x4A7FCA00,0xBC356BD0"
      "fex=AMp/StBrNbw"
      "deviceid=D4:A3:3D:5E:77:8F"
      "btaddr=69:E8:6A:63:C2:26"
      "acl=0"]
=   eno1 IPv6 Bedroom Left HomePod                          _airplay._tcp        local
   hostname = [Bedroom.local]
   address = [xxx.xxx.xxx.28]
   port = [7000]
   txt = [
      "vv=2"
      "osvers=15.4"
      "srcvers=610.17.41"
      "pk=37e02248bd5dfbf5f30b9bbcb22002fe9988e1f6039e893909d5a0f49c909a4d"
      "psi=8C9D323E-E102-4945-9333-E0BF454081C1"
      "pi=45259a98-24fa-4e57-8c59-90e7cc63ce27"
      "protovers=1.1"
      "model=AudioAccessory1,1"
      "tsm=0"
      "tsid=F67AC6B1-4284-59D4-B270-A570D37FC2A1"
      "pgcgl=1"
      "pgid=5758DCB6-6E1A-5E69-99CD-74807BE84F23"
      "pgm=0"
      "psgid=5758DCB6-6E1A-5E69-99CD-74807BE84F23"
      "psgtp=1"
      "psgsz=3"
      "gpn=Bedroom Apple TV"
      "gcgl=1"
      "igl=0"
      "gid=5758DCB6-6E1A-5E69-99CD-74807BE84F23"
      "flags=0xba404"
      "features=0x4A7FCA00,0xBC356BD0"
      "fex=AMp/StBrNbw"
      "deviceid=D4:A3:3D:5E:77:8F"
      "btaddr=69:E8:6A:63:C2:26"
      "acl=0"]
+   eno1 IPv4 Living Room Right HomePod                     _airplay._tcp        local
+   eno1 IPv6 Living Room Right HomePod                     _airplay._tcp        local
=   eno1 IPv4 Living Room Right HomePod                     _airplay._tcp        local
   hostname = [Living-Room-2.local]
   address = [xxx.xxx.xxx.20]
   port = [7000]
   txt = [
      "vv=2"
      "osvers=15.4"
      "srcvers=610.17.41"
      "pk=48659c03b70af2eebaddd352c9c6cc8223f3c37fd679ba75ce96901885cec96c"
      "psi=48283227-2418-4780-8FFB-9F73E3E334BC"
      "pi=4c6fa473-38d8-4acd-b689-b0262b98def7"
      "protovers=1.1"
      "model=AudioAccessory1,1"
      "tsm=0"
      "tsid=8E0FC80F-5850-5287-B80A-C6BCD12DD3B7"
      "pgcgl=1"
      "pgid=3D207050-3E41-5BA2-A970-82854250ABFD"
      "pgm=0"
      "psgid=3D207050-3E41-5BA2-A970-82854250ABFD"
      "psgtp=1"
      "psgsz=3"
      "gpn=Living Room Apple TV"
      "gcgl=1"
      "igl=0"
      "gid=3D207050-3E41-5BA2-A970-82854250ABFD"
      "flags=0xb8404"
      "features=0x4A7FCA00,0xBC354BD0"
      "fex=AMp/StBLNbw"
      "deviceid=D4:90:9C:E7:44:10"
      "btaddr=6D:DB:B2:90:03:0E"
      "acl=0"]
=   eno1 IPv6 Living Room Right HomePod                     _airplay._tcp        local
   hostname = [Living-Room-2.local]
   address = [xxx.xxx.xxx.20]
   port = [7000]
   txt = [
      "vv=2"
      "osvers=15.4"
      "srcvers=610.17.41"
      "pk=48659c03b70af2eebaddd352c9c6cc8223f3c37fd679ba75ce96901885cec96c"
      "psi=48283227-2418-4780-8FFB-9F73E3E334BC"
      "pi=4c6fa473-38d8-4acd-b689-b0262b98def7"
      "protovers=1.1"
      "model=AudioAccessory1,1"
      "tsm=0"
      "tsid=8E0FC80F-5850-5287-B80A-C6BCD12DD3B7"
      "pgcgl=1"
      "pgid=3D207050-3E41-5BA2-A970-82854250ABFD"
      "pgm=0"
      "psgid=3D207050-3E41-5BA2-A970-82854250ABFD"
      "psgtp=1"
      "psgsz=3"
      "gpn=Living Room Apple TV"
      "gcgl=1"
      "igl=0"
      "gid=3D207050-3E41-5BA2-A970-82854250ABFD"
      "flags=0xb8404"
      "features=0x4A7FCA00,0xBC354BD0"
      "fex=AMp/StBLNbw"
      "deviceid=D4:90:9C:E7:44:10"
      "btaddr=6D:DB:B2:90:03:0E"
      "acl=0"]
ma-ku commented 2 years ago

Hi all,

i have just pushed a new version of the code to my repo. It contains initial support for playback groups in the sense that playback groups get detected and displayed as an entry with the name of the speakers concatenated with a plus character. However, selecting these devices for playback is not yet possible as the internal mapping between speaker and devices needs to be implemented. This new capability is already embedded in the backend but the UI portion of it must be activated by defining the symbol SPEAKER_GROUPING when compiling the code.

@shauder: I also came across a potential solution for your problem. For reasons not yet clear to me, sometimes the Airplay 2 entries of a device gets overridden by its Airplay 1 representation and as a consequence, the additional information for grouping and stereo pairing is getting lost. You can prioritize Airplay 2 over Airplay 1 by defining the symbol PREFER_AIRPLAY2 when building the code.

ma-ku commented 2 years ago

Different topic: UI improvement:

@ejurgensen: I am thinking about also providing the corresponding icons in the UI similar to the Airplay panel in iOS. I cam across enum airplay_devtype and enum raop_devtype and the corresponding string tables. Would it make sense to elevate this into a general enumeration in outputs.h/.c that would include the Airplay types but also other types and also to move the corresponding fields from the extension object to the device object so that we could leverage the information in the UI? The new enumeration would of course cover the Airplay/Apple devices but also other like SONOS.

ejurgensen commented 2 years ago

It's a good idea to support more exact icons. In theory, outputs.c/h should only work as a general abstraction, so it goes against the design if device type lists for all output types need to be maintained there. Could the objective be achieved by making a function that lets the player request output details? Or if there is some other generic way the output abstraction could collect the device types from the output modules.

shauder commented 2 years ago

@ma-ku thanks for that suggestion and the additional update. Here is some of my findings in case they help.

I rebuilt the image with the latest commits and also enabled PREFER_AIRPLAY2 and what shows up matches what shows up in iOS. test

I am also now able to start and stop playback to the Office HomePods stereo pair unlike before. However there is some new odd behavior in that when I select either of the stereo HomePods connected to an Apple TV, the audio will play but the pair is not selected in the UI. The prompt for a pin for the Apple TV also shows up in the OwnTone UI. From here putting in the PIN does not seem to work and you are not able to de-select the HomePods from playback.

I then built the image without the PREFER_AIRPLAY2 setting and when it loaded up you could see the individual HomePods selected in the UI once OwnTone started up. test2

After I let that settle the AirPlay2 devices came back and I was now able to select the stereo pairs that are associated with the Apple TVs again. image

But then the error with the standalone stereo HomePods is back with the Office HomePods where it seems the AirPlay1 device is overwriting the AirPlay2 device.

ma-ku commented 2 years ago

@shauder: I am sure there will be more of these quirks. The challenge now is to find all these API methods that somehow require mapping between one speaker and multiple devices. Pairing with a PIN is definitely one of these cases that needs special handling. For now only the toggling of the checkbox is supported.

I need to setup my Apple TV for audio output to see if I can test that scenario.

shauder commented 2 years ago

@ma-ku let me know if there is anything I can test or help with. Another behavior I will share that I found yesterday is this. Since the stand alone HomePod stereo pair was not working yesterday I did some further testing. Within the iOS Home app I moved the 'Office HomePods' stereo pair from my Office room to my bedroom room. I then set the 'Office HomePods' to the default audio output of the Apple TV in the bedroom. From there what I noticed is I was now able to select and control the 'Office HomePods' stereo pair from OwnTone but they now took on the name of the 'Bedroom Apple TV' instead of 'Office HomePods'. Then the 'Bedroom HomePods' that were no longer part of the 'Bedroom Apple TV' faced the same issue I was having with the Office HomePods when they were stand alone. Where the AirPlay1 device seems to overwrite/replace the grouped AirPlay2 device and cannot select it and initiate playback.

ma-ku commented 2 years ago

Interesting. We should be careful with these observations as 15.4 might still be in flux. I have seen also quite a few quirks with speakers after the stereo group was dissolved. One speaker had to be reset and reinstalled before it was usable again. I will look into your findings. The best is always to provide the mDNS dump in these cases. Is the one above including the described behavior?

Another question; did you update the code from GitHub or just recompile the version you had on your computer? Might be that the latest is behaving a bit better.

shauder commented 2 years ago

The mDNS above is when I set everything back to how it normally is with the Bedroom HomePods being default audio for the Bedroom Apple TV and the Office HomePods standalone in it's own room. If it's helpful, later I can move the Office HomePods back to the bedroom, set them as the default audio for the Bedroom Apple TV and then share what mDNS reports.

shauder commented 2 years ago

I forgot to reply to your other question. All the recent testing today was with the latest code from your branch pulled and recompiled.

ma-ku commented 2 years ago

I played with this a little today and if you select speakers for audio playback in Apple TV it comes down to be just another Airplay grouping (I refer to these groups as playback groups in the code). Technically it could dissolve into individual speakers at any time.

One thing that I also saw is that for some reason the information about authentication requirements seem to get lost. I will need to check where and why.

@ejurgensen: Have you checked the SETPEERS command? I am wondering if this is the magic key to tell speakers that they are in a group. Currently owntone just sets itself as the peer but in more complex setups you can see that the SETPEERS command sends all other speakers to one speaker. I have that on my list to check but its has a fairly low priority for now.

ma-ku commented 2 years ago

It's a good idea to support more exact icons. In theory, outputs.c/h should only work as a general abstraction, so it goes against the design if device type lists for all output types need to be maintained there. Could the objective be achieved by making a function that lets the player request output details? Or if there is some other generic way the output abstraction could collect the device types from the output modules.

Agree. I must admit that I do not want to make things overly complicated. It works nicely as-is so we should put it on a mental backlog for now.

ma-ku commented 2 years ago

I have added PIN authentication and volume control for speaker groups to the branch. Feel free to test this version.

To use the functionality, compile with the following symbols:

e.g. make CFLAGS="-DPREFER_AIRPLAY2 -DSPEAKER_GROUPING"

Technically it would also display speaker groups built in the Airplay panel. However, when playback to such a group is started from the web UI, the group 'magically' dissolves. Apparently there is more to group playback than just starting to stream to all members.

aleszczynskig commented 2 years ago

@ma-ku - Thanks for the update. I have tested this and can confirm that it works as expected for my test scenario which is a stereo pair of original HomePods set as the default output for my Apple TV 4K. I will monitor the stability and performance of the system and report back any issues if they present.

aleszczynskig commented 2 years ago

One issue I have found is that when using the api to select or deselect the speakers, the web UI does not reflect the changes. I have also seen a situation where after selecting the speakers from the web UI, the speaker is selected and works as expected however the UI updates a few moments later to show the speaker deselected.

Would you want logs for these issues?

ma-ku commented 2 years ago

Would you want logs for these issues?

Yes. Send those over. As I have already mentioned; I do not know all the different paths a function may get executed. So the API is not on my radar. I will see what I can do.

ma-ku commented 2 years ago

Would you want logs for these issues?

Any news? Or should we commence to create PR for this?

aleszczynskig commented 2 years ago

Sorry for the delay. Here are the logs. In this instance, only the owntone UI is used, when selecting the speaker (living room) the UI reports a 500 response code and does not show the speaker as selected however the speaker does play sound.

It is not then possible to deselect the speaker as the UI always reports a 500 response code owntone.log .

shauder commented 2 years ago

I updated my docker image (shauder/daapd:latest) and tested this too. I have 3 stereo pairs with two being connected to Apple TV's and then a Sonos roam. When I loaded up Owntone I could see them all (for some reason Office Stereo was grouped with the Sonos). I first tried the Living Room Stereo connected to the Apple TV and got the same 500 error but music played and the speaker showed disconnected. Then I did the Bedroom Stereo connected to the Apple TV and same thing. Then I did the Office Stereo grouped with the Sonos and they both started played and then showed ungrouped. At this point I had music played on all of them. I then deselected the Sonos and it stopped and then same for the Office Stereo. From there I just have music playing on the two connected to Apple TV's since I cannot deselect them.

Withdrawing address record for fe80::d836:f9ff:fea6:64ff on veth0551f05.
[2022-03-24 15:18:42] [  LOG]  airplay: Pairing step 5 response from 'Living Room Apple TV' error: Device returned an authentication failure
[2022-03-24 15:18:42] [  LOG]   player: The AirPlay 2 device 'Living Room Apple TV' requires a valid PIN or password
[2022-03-24 15:18:42] [  LOG]      web: JSON api request failed with error code 500 (/api/outputs/16857613540288788273)
[2022-03-24 15:18:44] [  LOG]     dacp: 'device-volume' request from '192.168.0.24' has unknown Active-Remote: '839676306'
[2022-03-24 15:18:44] [  LOG]     dacp: 'device-volume' request from '192.168.0.20' has unknown Active-Remote: '2620695595'

Withdrawing address record for fe80::1017:7aff:febd:96a on veth3aeebb2.
[2022-03-24 15:19:17] [  LOG]  airplay: Pairing step 5 response from 'Bedroom Apple TV' error: Device returned an authentication failure
[2022-03-24 15:19:17] [  LOG]   player: The AirPlay 2 device 'Bedroom Apple TV' requires a valid PIN or password
[2022-03-24 15:19:17] [  LOG]      web: JSON api request failed with error code 500 (/api/outputs/12837454162567186744/toggle)
[2022-03-24 15:19:19] [  LOG]     dacp: 'device-volume' request from '192.168.0.28' has unknown Active-Remote: '1447466968'
[2022-03-24 15:19:19] [  LOG]     dacp: 'device-volume' request from '192.168.0.116' has unknown Active-Remote: '358758761'
Joining mDNS multicast group on interface veth6a533cb.IPv6 with address fe80::3c30:6bff:feeb:df98.

The 500 error seems to be something with it wanting a PIN for the AppleTV but then it still manages to connect to the HomePods and play the music.

owntone_shauder.log

ma-ku commented 2 years ago

I agree with @shauder, the 500 seems to be related to the pin request. So eventually the API still requires some work in that area.

Just as an explanation what is going on behind the scenes; the mDNS scanning process finds potential groups and adds that information to the internal metadata about devices. Before the change, a device was also a speaker. The new change mangles that information on its way to the web in that way that instead of two speakers it now just returns one speaker with the group id. In the API, each call for a speaker is then dissolved into its corresponding devices and then executed. So far I have identified three calls where this is relevant; enable/disable a speaker, volume control, and pin pairing.

Currently the code also tries to find grouped speakers (not stereo pairs) but for now this seems to be more complex than expected as these groups dissolve once owntone starts a playback to any of these speakers. So eventually I will turn that part off to make sure the stereo pairs are working correctly.