* For `ssnc` metadata, the second 4-character code is used to distinguish the messages. Cover art, coming from the source, is not tagged in the same way as other metadata, it seems, so is sent as an `ssnc` type metadata message with the code `PICT`. Progress information, similarly, is not tagged like other source-originated metadata, so it is sent as an `ssnc` type with the code `prgr`.
Here are some of the `core` codes commonly passed from the source:
- * `asar` -- "artist"
- * `asal` -- "album"
- * `minm` -- "title"
- * `asgn` -- "genre"
- * `asfm` -- "format"
- * `asal` -- "songalbum'
- * `pvol` -- "volume"
- * `clip` -- "client_ip"
+ * `asal` -- album
+ * `asar` -- artist
+ * `ascp` -- composer
+ * `asgn` -- genre
+ * `astm` -- song time
+ * `caps` -- play status (stopped, paused, playing)
+ * `minm` -- title
+ * `mper` -- track persistent id
Here are the 'ssnc' codes defined so far:
* `PICT` -- the payload is a picture, either a JPEG or a PNG. Check the first few bytes to see which.
- * `clip` -- the payload is the IP number of the client, i.e. the sender of audio. Can be an IPv4 or an IPv6 number.
+ * `acre` -- Active Remote
+ * `clip` -- the payload is the IP address of the client, i.e. the sender of audio. Can be an IPv4 or an IPv6 address.
+ * `daid` -- DACP ID
+ * `dapo` -- DACP Port
+ * `mden` -- a sequence of metadata has ended. The RTP timestamp associated with the metadata sequence is included as data, if available.
+ * `mdst` -- a sequence of metadata is about to start. The RTP timestamp associated with the metadata sequence is included as data, if available.
* `pbeg` -- play stream begin. No arguments
+ * `pcen` -- a picture has been sent. The RTP timestamp associated with it is included as data, if available.
+ * `pcst` -- a picture is about to be sent. The RTP timestamp associated with it is included as data, if available.
* `pend` -- play stream end. No arguments
* `pfls` -- play stream flush. No arguments
* `prsm` -- play stream resume. No arguments
- * `pvol` -- play volume. The volume is sent as a string -- "airplay_volume,volume,lowest_volume,highest_volume", where "volume", "lowest_volume" and "highest_volume" are given in dB. The "airplay_volume" is what's sent by the source (e.g. iTunes) to the player, and is from 0.00 down to -30.00, with -144.00 meaning "mute". This is linear on the volume control slider of iTunes or iOS AirPlay. If the volume setting is being ignored by Shairport Sync itself, the volume, lowest_volume and highest_volume values are zero.
* `prgr` -- progress -- this is metadata from AirPlay consisting of RTP timestamps for the start of the current play sequence, the current play point and the end of the play sequence.
- * `mdst` -- a sequence of metadata is about to start. The RTP timestamp associated with the metadata sequence is included as data, if available.
- * `mden` -- a sequence of metadata has ended. The RTP timestamp associated with the metadata sequence is included as data, if available.
- * `pcst` -- a picture is about to be sent. The RTP timestamp associated with it is included as data, if available.
- * `pcen` -- a picture has been sent. The RTP timestamp associated with it is included as data, if available.
+ * `pvol` -- play volume. The volume is sent as a string -- "airplay_volume,volume,lowest_volume,highest_volume", where "volume", "lowest_volume" and "highest_volume" are given in dB. The "airplay_volume" is what's sent by the source (e.g. iTunes) to the player, and is from 0.00 down to -30.00, with -144.00 meaning "mute". This is linear on the volume control slider of iTunes or iOS AirPlay. If the volume setting is being ignored by Shairport Sync itself, the volume, lowest_volume and highest_volume values are zero.
* `snam` -- a device e.g. "Joe's iPhone" has started a play session. Specifically, it's the "X-Apple-Client-Name" string.
* `snua` -- a "user agent" e.g. "iTunes/12..." has started a play session. Specifically, it's the "User-Agent" string.
* `stal` -- this is an error message meaning that reception of a large piece of metadata, usually a large picture, has stalled; bad things may happen.
+ * `svip` -- the payload is the IP address of the server, i.e. shairport-sync. Can be an IPv4 or an IPv6 address.
Parsed Messages
The MQTT service can parse the above raw messages into a subset of human-readable topics that include,
+* `active_remote_id` -- Active Remote ID
* `artist` -- text of artist name
* `album` -- text of album name
-* `title` -- text of song title
-* `genre` -- text of genre
+* `client_ip` -- IP address of the connected client
+* `dacp_id` -- DACP ID
* `format` -- ??
+* `genre` -- text of genre
+* `server_ip` -- IP address of shairport-sync that the client is connected to
* `songalbum` --
+* `title` -- text of song title
* `volume` -- The volume is sent as a string -- "airplay_volume,volume,lowest_volume,highest_volume", where "volume", "lowest_volume" and "highest_volume" are given in dB. (see above)
-* `client_ip` -- IP address of the connected client
and empty messages at the following topics are published.
+#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "rtp.h"
#include "dacp.h"
+#include "metadata_hub.h"
#include "mqtt.h"
#include <mosquitto.h>
}
if (config.mqtt_publish_parsed) {
if (type == 'core') {
+ int32_t r;
+ uint64_t trackid;
+ char trackidstring[32];
+
switch (code) {
case 'asar':
mqtt_publish("artist", data, length);
case 'asal':
mqtt_publish("album", data, length);
break;
- case 'minm':
- mqtt_publish("title", data, length);
+ case 'asfm':
+ mqtt_publish("format", data, length);
break;
case 'asgn':
mqtt_publish("genre", data, length);
break;
- case 'asfm':
- mqtt_publish("format", data, length);
+ case 'minm':
+ mqtt_publish("title", data, length);
break;
+ case 'mper':
+ trackid = *(uint64_t *)(data);
+ r = snprintf(trackidstring, sizeof(trackidstring), "%" PRIX64 "", trackid);
+ mqtt_publish("track_id", trackidstring, r);
}
} else if (type == 'ssnc') {
switch (code) {
+ case 'abeg':
+ mqtt_publish("active_start", data, length);
+ break;
+ case 'acre':
+ mqtt_publish("active_remote_id", data, length);
+ break;
+ case 'aend':
+ mqtt_publish("active_end", data, length);
+ break;
case 'asal':
mqtt_publish("songalbum", data, length);
break;
- case 'pvol':
- mqtt_publish("volume", data, length);
- break;
case 'clip':
mqtt_publish("client_ip", data, length);
break;
- case 'abeg':
- mqtt_publish("active_start", data, length);
- break;
- case 'aend':
- mqtt_publish("active_end", data, length);
+ case 'daid':
+ mqtt_publish("dacp_id", data, length);
break;
case 'pbeg':
mqtt_publish("play_start", data, length);
case 'pfls':
mqtt_publish("play_flush", data, length);
break;
- case 'prsm':
- mqtt_publish("play_resume", data, length);
- break;
case 'PICT':
if (config.mqtt_publish_cover) {
mqtt_publish("cover", data, length);
}
break;
+ case 'prsm':
+ mqtt_publish("play_resume", data, length);
+ break;
+ case 'pvol':
+ mqtt_publish("volume", data, length);
+ break;
+ case 'svip':
+ mqtt_publish("server_ip", data, length);
+ break;
+
}
}
}