}
return previous_random_number;
}
+
+// This will check the incoming string "s" of length "len" with the existing NUL-terminated string "str" and update "flag" accordingly.
+
+// Note: if the incoming string length is zero, then the a NULL is used; i.e. no zero-length strings are stored.
+
+// If the strings are different, the str is free'd and replaced by a pointer
+// to a newly strdup'd string and the flag is set
+// If they are the same, the flag is cleared
+
+int string_update_with_size(char **str, int *flag, char *s, size_t len) {
+ if (*str) {
+ if ((s) && (len)) {
+ if (strncmp(*str, s, len) != 0) {
+ free(*str);
+ *str = strndup(s, len);
+ *flag = 1;
+ } else {
+ *flag = 0;
+ }
+ } else {
+ // old string is non-NULL, new string is NULL or length 0
+ free(*str);
+ *str = NULL;
+ *flag = 1;
+ }
+ } else { // old string is NULL
+ if ((s) && (len)) {
+ *str = strndup(s, len);
+ *flag = 1;
+ } else {
+ // old string is NULL and new string is NULL or length 0
+ *flag = 0; // so no change
+ }
+ }
+ return *flag;
+}
void malloc_cleanup(void *arg);
+int string_update_with_size(char **str, int *flag, char *s, size_t len);
+
#endif // _COMMON_H
}
static const struct http_funcs responseFuncs = {
- response_realloc, response_body, response_header, response_code,
+ response_realloc,
+ response_body,
+ response_header,
+ response_code,
};
// static pthread_mutex_t dacp_conversation_lock = PTHREAD_MUTEX_INITIALIZER;
// debug(1,"Sent command\"%s\" with a response body of size %d.",command,response.size);
// debug(1,"dacp_conversation_lock released.");
} else {
- debug(3, "dacp_send_command: could not acquire a lock on the dacp transmit/receive section "
- "when attempting to "
- "send the command \"%s\". Possible timeout?",
+ debug(3,
+ "dacp_send_command: could not acquire a lock on the dacp transmit/receive section "
+ "when attempting to "
+ "send the command \"%s\". Possible timeout?",
command);
response.code = 494; // This client is already busy
}
void dacp_monitor_port_update_callback(char *dacp_id, uint16_t port) {
debug_mutex_lock(&dacp_server_information_lock, 500000, 2);
- debug(3, "dacp_monitor_port_update_callback with Remote ID \"%s\", target ID \"%s\" and port "
- "number %d.",
+ debug(3,
+ "dacp_monitor_port_update_callback with Remote ID \"%s\", target ID \"%s\" and port "
+ "number %d.",
dacp_id, dacp_server.dacp_id, port);
if (strcmp(dacp_id, dacp_server.dacp_id) == 0) {
dacp_server.port = port;
(metadata_store.advanced_dacp_server_active != 0);
metadata_store.dacp_server_active = 0;
metadata_store.advanced_dacp_server_active = 0;
- debug(2, "setting dacp_server_active and advanced_dacp_server_active to 0 with an update "
- "flag value of %d",
+ debug(2,
+ "setting dacp_server_active and advanced_dacp_server_active to 0 with an update "
+ "flag value of %d",
ch);
metadata_hub_modify_epilog(ch);
while (dacp_server.scan_enable == 0) {
scan_index++;
result = dacp_get_volume(&the_volume); // just want the http code
- if ((result == 496) || (result == 403) || (result == 501)) {
+ if ((result == 403) || (result == 501)) {
bad_result_count++;
// debug(1,"Bad Scan : %d.",result);
} else
if (dacp_server.scan_enable ==
1) { // if it hasn't been turned off, continue looking for information.
int transient_problem =
- (result == 494) ||
- (result == 495); // this just means that it couldn't send the query because something
+ (result == 494) || (result == 495) ||
+ (result == 496); // this just means that it couldn't send the query because something
// else
// was sending a command or something
if ((!transient_problem) && (bad_result_count == 0) && (idle_scan_count == 0) &&
int inactive = metadata_store.dacp_server_active == 0;
if (inactive) {
metadata_store.dacp_server_active = 1;
- debug(2, "Setting dacp_server_active to active because of a response of %d.", result);
+ debug(1, "Setting dacp_server_active to active because of a response of %d.", result);
}
int same = metadata_store.advanced_dacp_server_active == (result == 200);
if (!same) {
metadata_store.advanced_dacp_server_active = (result == 200);
- debug(2, "Setting dacp_advanced_server_active to %d because of a response of %d.",
+ debug(1, "Setting dacp_advanced_server_active to %d because of a response of %d.",
(result == 200), result);
}
metadata_hub_modify_epilog(inactive + (!same));
// char u;
// char *st;
int32_t r;
+ uint32_t ui;
// uint64_t v;
// int i;
break;
}
break;
- /*
case 'cann': // track name
- t = sp - item_size;
- if ((metadata_store.track_name == NULL) ||
- (strncmp(metadata_store.track_name, t, item_size) != 0)) {
- if (metadata_store.track_name)
- free(metadata_store.track_name);
- metadata_store.track_name = strndup(t, item_size);
- debug(1, "Track name changed to: \"%s\"", metadata_store.track_name);
- metadata_store.track_name_changed = 1;
- metadata_store.changed = 1;
+ debug(2, "DACP Track Name seen");
+ if (string_update_with_size(&metadata_store.track_name, &metadata_store.track_name_changed,
+ sp - item_size, item_size)) {
+ debug(2, "DACP Track Name set to: \"%s\"", metadata_store.track_name);
}
break;
case 'cana': // artist name
- t = sp - item_size;
- if ((metadata_store.artist_name == NULL) ||
- (strncmp(metadata_store.artist_name, t, item_size) != 0)) {
- if (metadata_store.artist_name)
- free(metadata_store.artist_name);
- metadata_store.artist_name = strndup(t, item_size);
- debug(1, "Artist name changed to: \"%s\"", metadata_store.artist_name);
- metadata_store.artist_name_changed = 1;
- metadata_store.changed = 1;
+ debug(2, "DACP Artist Name seen");
+ if (string_update_with_size(&metadata_store.artist_name,
+ &metadata_store.artist_name_changed, sp - item_size,
+ item_size)) {
+ debug(2, "DACP Artist Name set to: \"%s\"", metadata_store.artist_name);
}
break;
case 'canl': // album name
- t = sp - item_size;
- if ((metadata_store.album_name == NULL) ||
- (strncmp(metadata_store.album_name, t, item_size) != 0)) {
- if (metadata_store.album_name)
- free(metadata_store.album_name);
- metadata_store.album_name = strndup(t, item_size);
- debug(1, "Album name changed to: \"%s\"", metadata_store.album_name);
- metadata_store.album_name_changed = 1;
- metadata_store.changed = 1;
+ debug(2, "DACP Album Name seen");
+ if (string_update_with_size(&metadata_store.album_name, &metadata_store.album_name_changed,
+ sp - item_size, item_size)) {
+ debug(2, "DACP Album Name set to: \"%s\"", metadata_store.album_name);
}
break;
case 'cang': // genre
- t = sp - item_size;
- if ((metadata_store.genre == NULL) ||
- (strncmp(metadata_store.genre, t, item_size) != 0)) {
- if (metadata_store.genre)
- free(metadata_store.genre);
- metadata_store.genre = strndup(t, item_size);
- debug(1, "Genre changed to: \"%s\"", metadata_store.genre);
- metadata_store.genre_changed = 1;
- metadata_store.changed = 1;
+ debug(2, "DACP Genre seen");
+ if (string_update_with_size(&metadata_store.genre, &metadata_store.genre_changed,
+ sp - item_size, item_size)) {
+ debug(2, "DACP Genre set to: \"%s\"", metadata_store.genre);
}
break;
case 'canp': // nowplaying 4 ids: dbid, plid, playlistItem, itemid (from mellowware
- --
// see reference above)
- t = sp - item_size;
- if (memcmp(metadata_store.item_composite_id, t,
+ debug(2, "DACP Composite ID seen");
+ if (memcmp(metadata_store.item_composite_id, sp - item_size,
sizeof(metadata_store.item_composite_id)) != 0) {
- memcpy(metadata_store.item_composite_id, t,
+ memcpy(metadata_store.item_composite_id, sp - item_size,
sizeof(metadata_store.item_composite_id));
-
char st[33];
char *pt = st;
int it;
pt += 2;
}
*pt = 0;
- // debug(1, "Item composite ID set to 0x%s.", st);
- metadata_store.item_id_changed = 1;
- metadata_store.changed = 1;
+ debug(2, "Item composite ID changed to 0x%s.", st);
+ metadata_store.item_composite_id_changed = 1;
}
break;
- */
case 'astm':
t = sp - item_size;
- r = ntohl(*(uint32_t *)(t));
- if (metadata_store.track_metadata)
- metadata_store.track_metadata->songtime_in_milliseconds =
- ntohl(*(uint32_t *)(t));
+ ui = ntohl(*(uint32_t *)(t));
+ debug(2, "DACP Song Time seen: \"%u\" of length %u.", ui, item_size);
+ if (ui != metadata_store.songtime_in_milliseconds) {
+ metadata_store.songtime_in_milliseconds = ui;
+ metadata_store.songtime_in_milliseconds_changed = 1;
+ debug(2, "DACP Song Time set to: \"%u\"",
+ metadata_store.songtime_in_milliseconds);
+ }
break;
/*
debug(1, "This should never happen.");
}
- GVariantBuilder *dict_builder, *aa;
+ // Build the metadata array
+ debug(2, "Build metadata");
+ GVariantBuilder *dict_builder = g_variant_builder_new(G_VARIANT_TYPE("a{sv}"));
- /* Build the metadata array */
- // debug(1,"Build metadata");
- dict_builder = g_variant_builder_new(G_VARIANT_TYPE("a{sv}"));
-
- // Make up the artwork URI if we have one
+ // Add in the artwork URI if it exists.
if (argc->cover_art_pathname) {
- char artURIstring[1024];
- snprintf(artURIstring, sizeof(artURIstring), "file://%s", argc->cover_art_pathname);
- // debug(1,"artURI String: \"%s\".",artURIstring);
- GVariant *artUrl = g_variant_new("s", artURIstring);
+ GVariant *artUrl = g_variant_new("s", argc->cover_art_pathname);
g_variant_builder_add(dict_builder, "{sv}", "mpris:artUrl", artUrl);
}
- // Add the TrackID if we have one
- if ((argc->track_metadata) && (argc->track_metadata->item_id)) {
+ // Add in the Track ID based on the 'mper' metadata if it is non-zero
+ if (argc->item_id != 0) {
char trackidstring[128];
- // debug(1, "Set ID using mper ID: \"%u\".",argc->item_id);
snprintf(trackidstring, sizeof(trackidstring), "/org/gnome/ShairportSync/mper_%u",
- argc->track_metadata->item_id);
+ argc->item_id);
GVariant *trackid = g_variant_new("o", trackidstring);
g_variant_builder_add(dict_builder, "{sv}", "mpris:trackid", trackid);
}
- // Add the track name if there is one
- if ((argc->track_metadata) && (argc->track_metadata->track_name)) {
- // debug(1, "Track name set to \"%s\".", argc->track_name);
- GVariant *trackname = g_variant_new("s", argc->track_metadata->track_name);
- g_variant_builder_add(dict_builder, "{sv}", "xesam:title", trackname);
+ // Add the track name if it exists
+ if (argc->track_name) {
+ GVariant *track_name = g_variant_new("s", argc->track_name);
+ g_variant_builder_add(dict_builder, "{sv}", "xesam:title", track_name);
}
- // Add the album name if there is one
- if ((argc->track_metadata) && (argc->track_metadata->album_name)) {
- // debug(1, "Album name set to \"%s\".", argc->album_name);
- GVariant *albumname = g_variant_new("s", argc->track_metadata->album_name);
- g_variant_builder_add(dict_builder, "{sv}", "xesam:album", albumname);
+ // Add the album name if it exists
+ if (argc->album_name) {
+ GVariant *album_name = g_variant_new("s", argc->album_name);
+ g_variant_builder_add(dict_builder, "{sv}", "xesam:album", album_name);
}
- // Add the artists if there are any (actually there will be at most one, but put it in an array)
- if ((argc->track_metadata) && (argc->track_metadata->artist_name)) {
- /* Build the artists array */
- // debug(1,"Build artist array");
- aa = g_variant_builder_new(G_VARIANT_TYPE("as"));
- g_variant_builder_add(aa, "s", argc->track_metadata->artist_name);
- GVariant *artists = g_variant_builder_end(aa);
- g_variant_builder_unref(aa);
+ // Add the artist name if it exists
+ if (argc->artist_name) {
+ GVariantBuilder *artist_as = g_variant_builder_new(G_VARIANT_TYPE("as"));
+ g_variant_builder_add(artist_as, "s", argc->artist_name);
+ GVariant *artists = g_variant_builder_end(artist_as);
+ g_variant_builder_unref(artist_as);
g_variant_builder_add(dict_builder, "{sv}", "xesam:artist", artists);
}
- // Add the genres if there are any (actually there will be at most one, but put it in an array)
- if ((argc->track_metadata) && (argc->track_metadata->genre)) {
- // debug(1,"Build genre");
- aa = g_variant_builder_new(G_VARIANT_TYPE("as"));
- g_variant_builder_add(aa, "s", argc->track_metadata->genre);
- GVariant *genres = g_variant_builder_end(aa);
- g_variant_builder_unref(aa);
- g_variant_builder_add(dict_builder, "{sv}", "xesam:genre", genres);
+ // Add the genre if it exists
+ if (argc->genre) {
+ GVariantBuilder *genre_as = g_variant_builder_new(G_VARIANT_TYPE("as"));
+ g_variant_builder_add(genre_as, "s", argc->genre);
+ GVariant *genre = g_variant_builder_end(genre_as);
+ g_variant_builder_unref(genre_as);
+ g_variant_builder_add(dict_builder, "{sv}", "xesam:genre", genre);
}
+ if (argc->songtime_in_milliseconds) {
+ uint64_t track_length_in_microseconds = argc->songtime_in_milliseconds;
+ track_length_in_microseconds *= 1000; // to microseconds in 64-bit precision
+ // Make up the track name and album name
+ // debug(1, "Set tracklength to %lu.", track_length_in_microseconds);
+ GVariant *tracklength = g_variant_new("x", track_length_in_microseconds);
+ g_variant_builder_add(dict_builder, "{sv}", "mpris:length", tracklength);
+ }
+
GVariant *dict = g_variant_builder_end(dict_builder);
g_variant_builder_unref(dict_builder);
-
- // debug(1,"Set metadata");
shairport_sync_remote_control_set_metadata(shairportSyncRemoteControlSkeleton, dict);
}
static gboolean on_handle_set_volume(ShairportSyncAdvancedRemoteControl *skeleton,
GDBusMethodInvocation *invocation, const gint volume,
__attribute__((unused)) gpointer user_data) {
- debug(1, "Set volume to %d.", volume);
+ debug(2, "Set volume to %d.", volume);
dacp_set_volume(volume);
shairport_sync_advanced_remote_control_complete_set_volume(skeleton, invocation);
return TRUE;
int metadata_hub_initialised = 0;
pthread_rwlock_t metadata_hub_re_lock = PTHREAD_RWLOCK_INITIALIZER;
-struct track_metadata_bundle *track_metadata; // used for a temporary track metadata store
-void release_char_string(char **str) {
- if (*str) {
- free(*str);
- *str = NULL;
- }
-}
-
-void metadata_hub_release_track_metadata(struct track_metadata_bundle *track_metadata) {
- // debug(1,"release track metadata");
- if (track_metadata) {
- release_char_string(&track_metadata->track_name);
- release_char_string(&track_metadata->artist_name);
- release_char_string(&track_metadata->album_artist_name);
- release_char_string(&track_metadata->album_name);
- release_char_string(&track_metadata->genre);
- release_char_string(&track_metadata->comment);
- release_char_string(&track_metadata->composer);
- release_char_string(&track_metadata->file_kind);
- release_char_string(&track_metadata->song_description);
- release_char_string(&track_metadata->song_album_artist);
- release_char_string(&track_metadata->sort_name);
- release_char_string(&track_metadata->sort_artist);
- release_char_string(&track_metadata->sort_album);
- release_char_string(&track_metadata->sort_composer);
- free((char *)track_metadata);
- } else {
- debug(3, "Asked to release non-existent track metadata");
- }
-}
-
-void metadata_hub_release_track_artwork(void) {
- // debug(1,"release track artwork");
- release_char_string(&metadata_store.cover_art_pathname);
+int string_update(char **str, int *flag, char *s) {
+ if (s)
+ return string_update_with_size(str, flag, s, strlen(s));
+ else
+ return string_update_with_size(str, flag, NULL, 0);
}
void metadata_hub_init(void) {
// debug(1, "Metadata bundle initialisation.");
memset(&metadata_store, 0, sizeof(metadata_store));
- track_metadata = NULL;
metadata_hub_initialised = 1;
}
-void metadata_hub_stop(void) {
- if (metadata_hub_initialised) {
- debug(2, "metadata_hub_stop.");
- metadata_hub_release_track_artwork();
- if (metadata_store.track_metadata) {
- metadata_hub_release_track_metadata(metadata_store.track_metadata);
- metadata_store.track_metadata = NULL;
- }
- if (track_metadata) {
- metadata_hub_release_track_metadata(track_metadata);
- track_metadata = NULL;
- }
- }
-}
+void metadata_hub_stop(void) {}
void add_metadata_watcher(metadata_watcher fn, void *userdata) {
int i;
}
}
+/*
void metadata_hub_unlock_hub_mutex_cleanup(__attribute__((unused)) void *arg) {
// debug(1, "metadata_hub_unlock_hub_mutex_cleanup called.");
pthread_rwlock_unlock(&metadata_hub_re_lock);
}
+*/
void run_metadata_watchers(void) {
int i;
- // debug(1, "locking metadata hub for reading");
- pthread_rwlock_rdlock(&metadata_hub_re_lock);
- pthread_cleanup_push(metadata_hub_unlock_hub_mutex_cleanup, NULL);
for (i = 0; i < number_of_watchers; i++) {
if (metadata_store.watchers[i]) {
metadata_store.watchers[i](&metadata_store, metadata_store.watchers_data[i]);
}
}
- // debug(1, "unlocking metadata hub for reading");
- // pthread_rwlock_unlock(&metadata_hub_re_lock);
- pthread_cleanup_pop(1);
+ // turn off changed flags
+ metadata_store.cover_art_pathname_changed = 0;
+ metadata_store.client_ip_changed = 0;
+ metadata_store.server_ip_changed = 0;
+ metadata_store.progress_string_changed = 0;
+ metadata_store.item_id_changed = 0;
+ metadata_store.item_composite_id_changed = 0;
+ metadata_store.artist_name_changed = 0;
+ metadata_store.album_artist_name_changed = 0;
+ metadata_store.album_name_changed = 0;
+ metadata_store.track_name_changed = 0;
+ metadata_store.genre_changed = 0;
+ metadata_store.comment_changed = 0;
+ metadata_store.composer_changed = 0;
+ metadata_store.file_kind_changed = 0;
+ metadata_store.song_description_changed = 0;
+ metadata_store.song_album_artist_changed = 0;
+ metadata_store.sort_artist_changed = 0;
+ metadata_store.sort_album_changed = 0;
+ metadata_store.sort_composer_changed = 0;
+ metadata_store.songtime_in_milliseconds_changed = 0;
}
void metadata_hub_modify_prolog(void) {
}
void metadata_hub_modify_epilog(int modified) {
- // always run this after changing an entry or a sequence of entries in the metadata_hub
- // debug(1, "unlocking metadata hub for writing");
-
- // Here, we check to see if the dacp_server is transitioning between active and inactive
- // If it's going off, we will release track metadata and image stuff
- // If it's already off, we do nothing
- // If it's transitioning to on, we will record it for use later.
-
- int m = 0;
- int tm = modified;
-
- if ((metadata_store.dacp_server_active == 0) &&
- (metadata_store.dacp_server_has_been_active != 0)) {
- debug(2, "dacp_scanner going inactive -- release track metadata and artwork");
- if (metadata_store.track_metadata) {
- m = 1;
- metadata_hub_release_track_metadata(metadata_store.track_metadata);
- metadata_store.track_metadata = NULL;
- }
- if (metadata_store.cover_art_pathname) {
- m = 1;
- metadata_hub_release_track_artwork();
- }
- if (m)
- debug(2, "Release track metadata after dacp server goes inactive.");
- tm += m;
- }
metadata_store.dacp_server_has_been_active =
metadata_store.dacp_server_active; // set the scanner_has_been_active now.
- pthread_rwlock_unlock(&metadata_hub_re_lock);
- if (tm) {
+ if (modified) {
run_metadata_watchers();
}
+ pthread_rwlock_unlock(&metadata_hub_re_lock);
}
void metadata_hub_read_prolog(void) {
// always run this before reading an entry or a sequence of entries in the metadata_hub
// debug(1, "locking metadata hub for reading");
if (pthread_rwlock_tryrdlock(&metadata_hub_re_lock) != 0) {
- debug(1, "Metadata_hub read lock is already taken -- must wait.");
+ debug(2, "Metadata_hub read lock is already taken -- must wait.");
pthread_rwlock_rdlock(&metadata_hub_re_lock);
- debug(1, "Okay -- acquired the metadata_hub read lock.");
+ debug(2, "Okay -- acquired the metadata_hub read lock.");
}
}
char *path = NULL; // this will be what is returned
uint8_t img_md5[16];
-// uint8_t ap_md5[16];
+ // uint8_t ap_md5[16];
#ifdef CONFIG_OPENSSL
MD5_CTX ctx;
// https://code.google.com/p/ytrack/wiki/DMAP
// all the following items of metadata are contained in one metadata packet
- // they are preseded by an 'ssnc' 'mdst' item and followed by an 'ssnc 'mden' item.
+ // they are preceded by an 'ssnc' 'mdst' item and followed by an 'ssnc 'mden' item.
+ uint32_t ui;
+ char *cs;
+ int changed = 0;
if (type == 'core') {
switch (code) {
case 'mper':
- if (track_metadata) {
- track_metadata->item_id = ntohl(*(uint32_t *)data);
- track_metadata->item_id_received = 1;
- debug(2, "MH Item ID set to: \"%u\"", track_metadata->item_id);
- } else {
- debug(1, "No track metadata memory allocated when item id received!");
+ ui = ntohl(*(uint32_t *)data);
+ debug(2, "MH Item ID seen: \"%u\" of length %u.", ui, length);
+ if (ui != metadata_store.item_id) {
+ metadata_store.item_id = ui;
+ metadata_store.item_id_changed = 1;
+ metadata_store.item_id_received = 1;
+ debug(2, "MH Item ID set to: \"%u\"", metadata_store.item_id);
+ }
+ break;
+ case 'astm':
+ ui = ntohl(*(uint32_t *)data);
+ debug(2, "MH Song Time seen: \"%u\" of length %u.", ui, length);
+ if (ui != metadata_store.songtime_in_milliseconds) {
+ metadata_store.songtime_in_milliseconds = ui;
+ metadata_store.songtime_in_milliseconds_changed = 1;
+ debug(2, "MH Song Time set to: \"%u\"", metadata_store.songtime_in_milliseconds);
}
break;
case 'asal':
- if (track_metadata) {
- track_metadata->album_name = strndup(data, length);
- debug(2, "MH Album name set to: \"%s\"", track_metadata->album_name);
- } else {
- debug(1, "No track metadata memory allocated when album name received!");
+ cs = strndup(data, length);
+ if (string_update(&metadata_store.album_name, &metadata_store.album_name_changed, cs)) {
+ debug(2, "MH Album name set to: \"%s\"", metadata_store.album_name);
}
+ free(cs);
break;
case 'asar':
- if (track_metadata) {
- track_metadata->artist_name = strndup(data, length);
- debug(2, "MH Artist name set to: \"%s\"", track_metadata->artist_name);
- } else {
- debug(1, "No track metadata memory allocated when artist name received!");
+ cs = strndup(data, length);
+ if (string_update(&metadata_store.artist_name, &metadata_store.artist_name_changed, cs)) {
+ debug(2, "MH Artist name set to: \"%s\"", metadata_store.artist_name);
}
+ free(cs);
break;
case 'assl':
- if (track_metadata) {
- track_metadata->album_artist_name = strndup(data, length);
- debug(2, "MH Album Artist name set to: \"%s\"", track_metadata->album_artist_name);
- } else {
- debug(1, "No track metadata memory allocated when album artist name received!");
+ cs = strndup(data, length);
+ if (string_update(&metadata_store.album_artist_name,
+ &metadata_store.album_artist_name_changed, cs)) {
+ debug(2, "MH Album Artist name set to: \"%s\"", metadata_store.album_artist_name);
}
+ free(cs);
break;
case 'ascm':
- if (track_metadata) {
- track_metadata->comment = strndup(data, length);
- debug(2, "MH Comment set to: \"%s\"", track_metadata->comment);
- } else {
- debug(1, "No track metadata memory allocated when comment received!");
+ cs = strndup(data, length);
+ if (string_update(&metadata_store.comment, &metadata_store.comment_changed, cs)) {
+ debug(2, "MH Comment set to: \"%s\"", metadata_store.comment);
}
+ free(cs);
break;
case 'asgn':
- if (track_metadata) {
- track_metadata->genre = strndup(data, length);
- debug(2, "MH Genre set to: \"%s\"", track_metadata->genre);
- } else {
- debug(1, "No track metadata memory allocated when genre received!");
+ cs = strndup(data, length);
+ if (string_update(&metadata_store.genre, &metadata_store.genre_changed, cs)) {
+ debug(2, "MH Genre set to: \"%s\"", metadata_store.genre);
}
+ free(cs);
break;
case 'minm':
- if (track_metadata) {
- track_metadata->track_name = strndup(data, length);
- debug(2, "MH Track name set to: \"%s\"", track_metadata->track_name);
- } else {
- debug(1, "No track metadata memory allocated when track name received!");
+ cs = strndup(data, length);
+ if (string_update(&metadata_store.track_name, &metadata_store.track_name_changed, cs)) {
+ debug(2, "MH Track Name set to: \"%s\"", metadata_store.track_name);
}
+ free(cs);
break;
case 'ascp':
- if (track_metadata) {
- track_metadata->composer = strndup(data, length);
- debug(2, "MH Composer set to: \"%s\"", track_metadata->composer);
- } else {
- debug(1, "No track metadata memory allocated when track name received!");
+ cs = strndup(data, length);
+ if (string_update(&metadata_store.composer, &metadata_store.composer_changed, cs)) {
+ debug(2, "MH Composer set to: \"%s\"", metadata_store.composer);
}
+ free(cs);
break;
case 'asdt':
- if (track_metadata) {
- track_metadata->song_description = strndup(data, length);
- debug(2, "MH Song Description set to: \"%s\"", track_metadata->song_description);
- } else {
- debug(1, "No track metadata memory allocated when song description received!");
+ cs = strndup(data, length);
+ if (string_update(&metadata_store.song_description, &metadata_store.song_description_changed,
+ cs)) {
+ debug(2, "MH Song Description set to: \"%s\"", metadata_store.song_description);
}
+ free(cs);
break;
case 'asaa':
- if (track_metadata) {
- track_metadata->song_album_artist = strndup(data, length);
- debug(2, "MH Song Album Artist set to: \"%s\"", track_metadata->song_album_artist);
- } else {
- debug(1, "No track metadata memory allocated when song artist received!");
+ cs = strndup(data, length);
+ if (string_update(&metadata_store.song_album_artist,
+ &metadata_store.song_album_artist_changed, cs)) {
+ debug(2, "MH Song Album Artist set to: \"%s\"", metadata_store.song_album_artist);
}
+ free(cs);
break;
case 'assn':
- if (track_metadata) {
- track_metadata->sort_name = strndup(data, length);
- debug(2, "MH Sort Name set to: \"%s\"", track_metadata->sort_name);
- } else {
- debug(1, "No track metadata memory allocated when sort name description received!");
+ cs = strndup(data, length);
+ if (string_update(&metadata_store.sort_name, &metadata_store.sort_name_changed, cs)) {
+ debug(2, "MH Sort Name set to: \"%s\"", metadata_store.sort_name);
}
+ free(cs);
break;
case 'assa':
- if (track_metadata) {
- track_metadata->sort_artist = strndup(data, length);
- debug(2, "MH Sort Artist set to: \"%s\"", track_metadata->sort_artist);
- } else {
- debug(1, "No track metadata memory allocated when sort artist description received!");
+ cs = strndup(data, length);
+ if (string_update(&metadata_store.sort_artist, &metadata_store.sort_artist_changed, cs)) {
+ debug(2, "MH Sort Artist set to: \"%s\"", metadata_store.sort_artist);
}
+ free(cs);
break;
case 'assu':
- if (track_metadata) {
- track_metadata->sort_album = strndup(data, length);
- debug(2, "MH Sort Album set to: \"%s\"", track_metadata->sort_album);
- } else {
- debug(1, "No track metadata memory allocated when sort album description received!");
+ cs = strndup(data, length);
+ if (string_update(&metadata_store.sort_album, &metadata_store.sort_album_changed, cs)) {
+ debug(2, "MH Sort Album set to: \"%s\"", metadata_store.sort_album);
}
+ free(cs);
break;
case 'assc':
- if (track_metadata) {
- track_metadata->sort_composer = strndup(data, length);
- debug(2, "MH Sort Composer set to: \"%s\"", track_metadata->sort_composer);
- } else {
- debug(1, "No track metadata memory allocated when sort composer description received!");
+ cs = strndup(data, length);
+ if (string_update(&metadata_store.sort_composer, &metadata_store.sort_composer_changed, cs)) {
+ debug(2, "MH Sort Composer set to: \"%s\"", metadata_store.sort_composer);
}
- break;
-
+ free(cs);
default:
/*
{
}
} else if (type == 'ssnc') {
switch (code) {
-
// ignore the following
case 'pcst':
case 'pcen':
break;
-
case 'mdst':
debug(2, "MH Metadata stream processing start.");
- if (track_metadata) {
- debug(1, "This track metadata bundle still seems to exist -- releasing it");
- metadata_hub_release_track_metadata(track_metadata);
- }
- track_metadata = (struct track_metadata_bundle *)malloc(sizeof(struct track_metadata_bundle));
- if (track_metadata == NULL)
- die("Could not allocate memory for track metadata.");
- memset(track_metadata, 0, sizeof(struct track_metadata_bundle));
+ metadata_hub_modify_prolog();
break;
case 'mden':
- if (track_metadata) {
- metadata_hub_modify_prolog();
- metadata_hub_release_track_metadata(metadata_store.track_metadata);
- metadata_store.track_metadata = track_metadata;
- track_metadata = NULL;
- metadata_hub_modify_epilog(1);
- }
debug(2, "MH Metadata stream processing end.");
+ metadata_hub_modify_epilog(1);
+ debug(2, "MH Metadata stream processing epilog complete.");
break;
case 'PICT':
+ metadata_hub_modify_prolog();
+ debug(2, "MH Picture received, length %u bytes.", length);
+ char uri[2048];
if (length > 16) {
- metadata_hub_modify_prolog();
- debug(2, "MH Picture received, length %u bytes.", length);
- release_char_string(&metadata_store.cover_art_pathname);
- metadata_store.cover_art_pathname = metadata_write_image_file(data, length);
- metadata_hub_modify_epilog(1);
+ char *pathname = metadata_write_image_file(data, length);
+ snprintf(uri, sizeof(uri), "file://%s", pathname);
+ free(pathname);
+ } else {
+ uri[0] = '\0';
}
+ if (string_update(&metadata_store.cover_art_pathname,
+ &metadata_store.cover_art_pathname_changed,
+ uri)) // if the picture's file path is different from the stored one...
+ metadata_hub_modify_epilog(1);
+ else
+ metadata_hub_modify_epilog(0);
break;
- /*
case 'clip':
- if ((metadata_store.client_ip == NULL) ||
- (strncmp(metadata_store.client_ip, data, length) != 0)) {
- metadata_hub_modify_prolog();
- if (metadata_store.client_ip)
- free(metadata_store.client_ip);
- metadata_store.client_ip = strndup(data, length);
- debug(1, "MH Client IP set to: \"%s\"", metadata_store.client_ip);
- metadata_store.client_ip_changed = 1;
- metadata_store.changed = 1;
- metadata_hub_modify_epilog(1);
+ metadata_hub_modify_prolog();
+ cs = strndup(data, length);
+ if (string_update(&metadata_store.client_ip, &metadata_store.client_ip_changed, cs)) {
+ changed = 1;
+ debug(2, "MH Client IP set to: \"%s\"", metadata_store.client_ip);
}
+ free(cs);
+ metadata_hub_modify_epilog(changed);
break;
- */
case 'prgr':
- if ((metadata_store.progress_string == NULL) ||
- (strncmp(metadata_store.progress_string, data, length) != 0)) {
- metadata_hub_modify_prolog();
- release_char_string(&metadata_store.progress_string);
- metadata_store.progress_string = strndup(data, length);
+ metadata_hub_modify_prolog();
+ cs = strndup(data, length);
+ if (string_update(&metadata_store.progress_string, &metadata_store.progress_string_changed,
+ cs)) {
+ changed = 1;
debug(2, "MH Progress String set to: \"%s\"", metadata_store.progress_string);
- metadata_hub_modify_epilog(1);
}
+ free(cs);
+ metadata_hub_modify_epilog(changed);
break;
case 'svip':
- if ((metadata_store.server_ip == NULL) ||
- (strncmp(metadata_store.server_ip, data, length) != 0)) {
- metadata_hub_modify_prolog();
- release_char_string(&metadata_store.server_ip);
- metadata_store.server_ip = strndup(data, length);
- // debug(1, "MH Server IP set to: \"%s\"", metadata_store.server_ip);
- metadata_hub_modify_epilog(1);
+ metadata_hub_modify_prolog();
+ cs = strndup(data, length);
+ if (string_update(&metadata_store.server_ip, &metadata_store.server_ip_changed, cs)) {
+ changed = 1;
+ debug(2, "MH Server IP set to: \"%s\"", metadata_store.server_ip);
}
+ free(cs);
+ metadata_hub_modify_epilog(changed);
break;
- // these could tell us about play / pause etc. but will only occur if metadata is enabled, so
- // we'll just ignore them
- case 'abeg': {
+ case 'abeg':
metadata_hub_modify_prolog();
- int changed = (metadata_store.active_state != AM_ACTIVE);
+ changed = (metadata_store.active_state != AM_ACTIVE);
metadata_store.active_state = AM_ACTIVE;
metadata_hub_modify_epilog(changed);
- } break;
- case 'aend': {
+ break;
+ case 'aend':
metadata_hub_modify_prolog();
- int changed = (metadata_store.active_state != AM_INACTIVE);
+ changed = (metadata_store.active_state != AM_INACTIVE);
metadata_store.active_state = AM_INACTIVE;
metadata_hub_modify_epilog(changed);
- } break;
- case 'pbeg': {
+ break;
+ case 'pbeg':
metadata_hub_modify_prolog();
- int changed = (metadata_store.player_state != PS_PLAYING);
+ changed = (metadata_store.player_state != PS_PLAYING);
metadata_store.player_state = PS_PLAYING;
metadata_store.player_thread_active = 1;
metadata_hub_modify_epilog(changed);
- } break;
- case 'pend': {
+ break;
+ case 'pend':
metadata_hub_modify_prolog();
+ changed = (metadata_store.player_state != PS_STOPPED);
metadata_store.player_thread_active = 0;
metadata_store.player_state = PS_STOPPED;
- metadata_hub_modify_epilog(1);
- } break;
- case 'pfls': {
+ metadata_hub_modify_epilog(changed);
+ break;
+ case 'pfls':
metadata_hub_modify_prolog();
- int changed = (metadata_store.player_state != PS_PAUSED);
+ changed = (metadata_store.player_state != PS_PAUSED);
metadata_store.player_state = PS_PAUSED;
metadata_hub_modify_epilog(changed);
- } break;
+ break;
case 'pffr': // this is sent when the first frame has been received
- case 'prsm': {
+ case 'prsm':
metadata_hub_modify_prolog();
int changed = (metadata_store.player_state != PS_PLAYING);
metadata_store.player_state = PS_PLAYING;
metadata_hub_modify_epilog(changed);
- } break;
+ break;
case 'pvol': {
// Note: it's assumed that the config.airplay volume has already been correctly set.
- int modified = 0;
int32_t actual_volume;
int gv = dacp_get_volume(&actual_volume);
metadata_hub_modify_prolog();
if ((gv == 200) && (metadata_store.speaker_volume != actual_volume)) {
metadata_store.speaker_volume = actual_volume;
- modified = 1;
+ changed = 1;
}
if (metadata_store.airplay_volume != config.airplay_volume) {
metadata_store.airplay_volume = config.airplay_volume;
- modified = 1;
+ changed = 1;
}
- metadata_hub_modify_epilog(modified); // change
+ metadata_hub_modify_epilog(changed); // change
} break;
default: {
RS_ALL,
} repeat_status_type;
-typedef struct track_metadata_bundle {
- uint32_t item_id; // seems to be a track ID -- see itemid in DACP.c
- int item_id_received; // important for deciding if the track information should be ignored.
- unsigned char
- item_composite_id[16]; // seems to be nowplaying 4 ids: dbid, plid, playlistItem, itemid
- char *track_name; // a malloced string -- if non-zero, free it before replacing it
- char *artist_name; // a malloced string -- if non-zero, free it before replacing it
- char *album_artist_name; // a malloced string -- if non-zero, free it before replacing it
- char *album_name; // a malloced string -- if non-zero, free it before replacing it
- char *genre; // a malloced string -- if non-zero, free it before replacing it
- char *comment; // a malloced string -- if non-zero, free it before replacing it
- char *composer; // a malloced string -- if non-zero, free it before replacing it
- char *file_kind; // a malloced string -- if non-zero, free it before replacing it
- char *song_description; // a malloced string -- if non-zero, free it before replacing it
- char *song_album_artist; // a malloced string -- if non-zero, free it before replacing it
- char *sort_name; // a malloced string -- if non-zero, free it before replacing it
- char *sort_artist; // a malloced string -- if non-zero, free it before replacing it
- char *sort_album; // a malloced string -- if non-zero, free it before replacing it
- char *sort_composer; // a malloced string -- if non-zero, free it before replacing it
- uint32_t songtime_in_milliseconds;
-} track_metadata_bundle;
+int string_update(char **str, int *changed, char *s);
+int int_update(int *receptacle, int *changed, int value);
struct metadata_bundle;
char *client_ip; // IP number used by the audio source (i.e. the "client"), which is also the DACP
// server
+ int client_ip_changed;
+
char *server_ip; // IP number used by Shairport Sync
- char *progress_string; // progress string, emitted by the source from time to time
+ int server_ip_changed;
+
+ char *progress_string; // progress string, emitted by the source from time to time
+ int progress_string_changed;
+
int player_thread_active; // true if a play thread is running
int dacp_server_active; // true if there's a reachable DACP server (assumed to be the Airplay
// client) ; false otherwise
enum shuffle_status_type shuffle_status;
enum repeat_status_type repeat_status;
- struct track_metadata_bundle *track_metadata;
+ // the following pertain to the track playing
+
+ char *cover_art_pathname;
+ int cover_art_pathname_changed;
+
+ uint32_t item_id; // seems to be a track ID -- see itemid in DACP.c
+ int item_id_changed;
+ int item_id_received; // important for deciding if the track information should be ignored.
+
+ unsigned char
+ item_composite_id[16]; // seems to be nowplaying 4 ids: dbid, plid, playlistItem, itemid
+ int item_composite_id_changed;
+
+ char *track_name;
+ int track_name_changed;
+
+ char *artist_name;
+ int artist_name_changed;
+
+ char *album_artist_name;
+ int album_artist_name_changed;
+
+ char *album_name;
+ int album_name_changed;
+
+ char *genre;
+ int genre_changed;
+
+ char *comment;
+ int comment_changed;
+
+ char *composer;
+ int composer_changed;
+
+ char *file_kind;
+ int file_kind_changed;
+
+ char *song_description;
+ int song_description_changed;
+
+ char *song_album_artist;
+ int song_album_artist_changed;
+
+ char *sort_name;
+ int sort_name_changed;
+
+ char *sort_artist;
+ int sort_artist_changed;
+
+ char *sort_album;
+ int sort_album_changed;
+
+ char *sort_composer;
+ int sort_composer_changed;
+
+ uint32_t songtime_in_milliseconds;
+ int songtime_in_milliseconds_changed;
- char *cover_art_pathname; // if non-zero, it will have been assigned with malloc.
+ // end
enum play_status_type
player_state; // this is the state of the actual player itself, which can be a bit noisy.
debug(1, "This should never happen.");
}
- GVariantBuilder *dict_builder, *aa;
-
- /* Build the metadata array */
- // debug(1,"Build metadata");
- dict_builder = g_variant_builder_new(G_VARIANT_TYPE("a{sv}"));
-
- // Make up the artwork URI if we have one
- if (argc->cover_art_pathname) {
- char artURIstring[1024];
- snprintf(artURIstring, sizeof(artURIstring), "file://%s", argc->cover_art_pathname);
- // debug(1,"artURI String: \"%s\".",artURIstring);
- GVariant *artUrl = g_variant_new("s", artURIstring);
- g_variant_builder_add(dict_builder, "{sv}", "mpris:artUrl", artUrl);
- }
-
- // Add the TrackID if we have one
- // Build the Track ID from the 16-byte item_composite_id in hex prefixed by
- // /org/gnome/ShairportSync
- char st[33];
- char *pt = st;
- int it;
- int non_zero = 0;
- if (argc->track_metadata) {
+ /*
+ // Add the TrackID if we have one
+ // Build the Track ID from the 16-byte item_composite_id in hex prefixed by
+ // /org/gnome/ShairportSync
+ char st[33];
+ char *pt = st;
+ int it;
+ int non_zero = 0;
for (it = 0; it < 16; it++) {
if (argc->track_metadata->item_composite_id[it])
non_zero = 1;
snprintf(pt, 3, "%02X", argc->track_metadata->item_composite_id[it]);
pt += 2;
}
+ *pt = 0;
+
+ if (non_zero) {
+ // debug(1, "Set ID using composite ID: \"0x%s\".", st);
+ char trackidstring[1024];
+ snprintf(trackidstring, sizeof(trackidstring), "/org/gnome/ShairportSync/%s", st);
+ GVariant *trackid = g_variant_new("o", trackidstring);
+ g_variant_builder_add(dict_builder, "{sv}", "mpris:trackid", trackid);
+ } else if ((argc->track_metadata) && (argc->track_metadata->item_id)) {
+ char trackidstring[128];
+ // debug(1, "Set ID using mper ID: \"%u\".",argc->item_id);
+ snprintf(trackidstring, sizeof(trackidstring), "/org/gnome/ShairportSync/mper_%u",
+ argc->track_metadata->item_id);
+ GVariant *trackid = g_variant_new("o", trackidstring);
+ g_variant_builder_add(dict_builder, "{sv}", "mpris:trackid", trackid);
+ }
+
+ */
+
+ // Build the metadata array
+ debug(2, "Build metadata");
+ GVariantBuilder *dict_builder = g_variant_builder_new(G_VARIANT_TYPE("a{sv}"));
+
+ // Add in the artwork URI if it exists.
+ if (argc->cover_art_pathname) {
+ GVariant *artUrl = g_variant_new("s", argc->cover_art_pathname);
+ g_variant_builder_add(dict_builder, "{sv}", "mpris:artUrl", artUrl);
}
- *pt = 0;
- if (non_zero) {
- // debug(1, "Set ID using composite ID: \"0x%s\".", st);
- char trackidstring[1024];
- snprintf(trackidstring, sizeof(trackidstring), "/org/gnome/ShairportSync/%s", st);
- GVariant *trackid = g_variant_new("o", trackidstring);
- g_variant_builder_add(dict_builder, "{sv}", "mpris:trackid", trackid);
- } else if ((argc->track_metadata) && (argc->track_metadata->item_id)) {
+ // Add in the Track ID based on the 'mper' metadata if it is non-zero
+ if (argc->item_id != 0) {
char trackidstring[128];
- // debug(1, "Set ID using mper ID: \"%u\".",argc->item_id);
snprintf(trackidstring, sizeof(trackidstring), "/org/gnome/ShairportSync/mper_%u",
- argc->track_metadata->item_id);
+ argc->item_id);
GVariant *trackid = g_variant_new("o", trackidstring);
g_variant_builder_add(dict_builder, "{sv}", "mpris:trackid", trackid);
}
- // Add the track length if it's non-zero
- if ((argc->track_metadata) && (argc->track_metadata->songtime_in_milliseconds)) {
- uint64_t track_length_in_microseconds = argc->track_metadata->songtime_in_milliseconds;
- track_length_in_microseconds *= 1000; // to microseconds in 64-bit precision
- // Make up the track name and album name
- // debug(1, "Set tracklength to %lu.", track_length_in_microseconds);
- GVariant *tracklength = g_variant_new("x", track_length_in_microseconds);
- g_variant_builder_add(dict_builder, "{sv}", "mpris:length", tracklength);
+ // Add the track name if it exists
+ if (argc->track_name) {
+ GVariant *track_name = g_variant_new("s", argc->track_name);
+ g_variant_builder_add(dict_builder, "{sv}", "xesam:title", track_name);
}
- // Add the track name if there is one
- if ((argc->track_metadata) && (argc->track_metadata->track_name)) {
- // debug(1, "Track name set to \"%s\".", argc->track_name);
- GVariant *trackname = g_variant_new("s", argc->track_metadata->track_name);
- g_variant_builder_add(dict_builder, "{sv}", "xesam:title", trackname);
+ // Add the album name if it exists
+ if (argc->album_name) {
+ GVariant *album_name = g_variant_new("s", argc->album_name);
+ g_variant_builder_add(dict_builder, "{sv}", "xesam:album", album_name);
}
- // Add the album name if there is one
- if ((argc->track_metadata) && (argc->track_metadata->album_name)) {
- // debug(1, "Album name set to \"%s\".", argc->album_name);
- GVariant *albumname = g_variant_new("s", argc->track_metadata->album_name);
- g_variant_builder_add(dict_builder, "{sv}", "xesam:album", albumname);
+ // Add the artist name if it exists
+ if (argc->artist_name) {
+ GVariantBuilder *artist_as = g_variant_builder_new(G_VARIANT_TYPE("as"));
+ g_variant_builder_add(artist_as, "s", argc->artist_name);
+ GVariant *artists = g_variant_builder_end(artist_as);
+ g_variant_builder_unref(artist_as);
+ g_variant_builder_add(dict_builder, "{sv}", "xesam:artist", artists);
}
- // Add the artists if there are any (actually there will be at most one, but put it in an array)
- if ((argc->track_metadata) && (argc->track_metadata->artist_name)) {
- /* Build the artists array */
- // debug(1,"Build artist array");
- aa = g_variant_builder_new(G_VARIANT_TYPE("as"));
- g_variant_builder_add(aa, "s", argc->track_metadata->artist_name);
- GVariant *artists = g_variant_builder_end(aa);
- g_variant_builder_unref(aa);
- g_variant_builder_add(dict_builder, "{sv}", "xesam:artist", artists);
+ // Add the genre if it exists
+ if (argc->genre) {
+ GVariantBuilder *genre_as = g_variant_builder_new(G_VARIANT_TYPE("as"));
+ g_variant_builder_add(genre_as, "s", argc->genre);
+ GVariant *genre = g_variant_builder_end(genre_as);
+ g_variant_builder_unref(genre_as);
+ g_variant_builder_add(dict_builder, "{sv}", "xesam:genre", genre);
}
- // Add the genres if there are any (actually there will be at most one, but put it in an array)
- if ((argc->track_metadata) && (argc->track_metadata->genre)) {
- // debug(1,"Build genre");
- aa = g_variant_builder_new(G_VARIANT_TYPE("as"));
- g_variant_builder_add(aa, "s", argc->track_metadata->genre);
- GVariant *genres = g_variant_builder_end(aa);
- g_variant_builder_unref(aa);
- g_variant_builder_add(dict_builder, "{sv}", "xesam:genre", genres);
+ if (argc->songtime_in_milliseconds) {
+ uint64_t track_length_in_microseconds = argc->songtime_in_milliseconds;
+ track_length_in_microseconds *= 1000; // to microseconds in 64-bit precision
+ // Make up the track name and album name
+ // debug(1, "Set tracklength to %lu.", track_length_in_microseconds);
+ GVariant *tracklength = g_variant_new("x", track_length_in_microseconds);
+ g_variant_builder_add(dict_builder, "{sv}", "mpris:length", tracklength);
}
GVariant *dict = g_variant_builder_end(dict_builder);
g_variant_builder_unref(dict_builder);
-
- // debug(1,"Set metadata");
media_player2_player_set_metadata(mprisPlayerPlayerSkeleton, dict);
-
- // media_player2_player_set_volume(mprisPlayerPlayerSkeleton, metadata_store.speaker_volume);
}
static gboolean on_handle_quit(MediaPlayer2 *skeleton, GDBusMethodInvocation *invocation,