From 86b6d7bfffbed00432a596b83e96507f308a78e8 Mon Sep 17 00:00:00 2001 From: Mike Brady Date: Thu, 29 Aug 2019 11:47:49 +0100 Subject: [PATCH] Squashed commit of the following: Fix Issue #890 -- https://github.com/mikebrady/shairport-sync/issues/890 Recognise situation where a track has no artwork properly. Less likely to drop the 'availabile' property. Always output the full metadata whenever any part of it has changed. Metadata coming from dacp is now incorporated, --- common.c | 36 +++++ common.h | 2 + dacp.c | 113 ++++++-------- dbus-service.c | 82 +++++----- metadata_hub.c | 399 ++++++++++++++++++++---------------------------- metadata_hub.h | 91 ++++++++--- mpris-service.c | 139 ++++++++--------- 7 files changed, 429 insertions(+), 433 deletions(-) diff --git a/common.c b/common.c index 396481612..5221e0eec 100644 --- a/common.c +++ b/common.c @@ -1519,3 +1519,39 @@ int64_t generate_zero_frames(char *outp, size_t number_of_frames, enum sps_forma } 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; +} diff --git a/common.h b/common.h index e1b18d88d..cc8c93c02 100644 --- a/common.h +++ b/common.h @@ -393,4 +393,6 @@ int64_t generate_zero_frames(char *outp, size_t number_of_frames, enum sps_forma void malloc_cleanup(void *arg); +int string_update_with_size(char **str, int *flag, char *s, size_t len); + #endif // _COMMON_H diff --git a/dacp.c b/dacp.c index 0b6db70ed..1c5271e19 100644 --- a/dacp.c +++ b/dacp.c @@ -112,7 +112,10 @@ static void response_code(void *opaque, int code) { } 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; @@ -320,9 +323,10 @@ int dacp_send_command(const char *command, char **body, ssize_t *bodysize) { // 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 } @@ -419,8 +423,9 @@ void set_dacp_server_information(rtsp_conn_info *conn) { 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; @@ -468,8 +473,9 @@ void *dacp_monitor_thread_code(__attribute__((unused)) void *na) { (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) { @@ -483,7 +489,7 @@ void *dacp_monitor_thread_code(__attribute__((unused)) void *na) { 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 @@ -511,8 +517,8 @@ void *dacp_monitor_thread_code(__attribute__((unused)) void *na) { 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) && @@ -525,12 +531,12 @@ void *dacp_monitor_thread_code(__attribute__((unused)) void *na) { 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)); @@ -574,6 +580,7 @@ void *dacp_monitor_thread_code(__attribute__((unused)) void *na) { // char u; // char *st; int32_t r; + uint32_t ui; // uint64_t v; // int i; @@ -658,64 +665,42 @@ void *dacp_monitor_thread_code(__attribute__((unused)) void *na) { 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; @@ -724,18 +709,20 @@ void *dacp_monitor_thread_code(__attribute__((unused)) void *na) { 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; /* diff --git a/dbus-service.c b/dbus-service.c index 8ab4cd2c0..2294a706d 100644 --- a/dbus-service.c +++ b/dbus-service.c @@ -146,77 +146,73 @@ void dbus_metadata_watcher(struct metadata_bundle *argc, __attribute__((unused)) 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; diff --git a/metadata_hub.c b/metadata_hub.c index 400c1e55a..dc2f34336 100644 --- a/metadata_hub.c +++ b/metadata_hub.c @@ -61,64 +61,21 @@ 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; @@ -132,24 +89,41 @@ void add_metadata_watcher(metadata_watcher fn, void *userdata) { } } +/* 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) { @@ -163,48 +137,21 @@ 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."); } } @@ -223,7 +170,7 @@ char *metadata_write_image_file(const char *buf, int len) { 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; @@ -345,124 +292,125 @@ void metadata_hub_process_metadata(uint32_t type, uint32_t code, char *data, uin // 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: /* { @@ -487,132 +435,121 @@ void metadata_hub_process_metadata(uint32_t type, uint32_t code, char *data, uin } } 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: { diff --git a/metadata_hub.h b/metadata_hub.h index 47965d5d8..5b565bbad 100644 --- a/metadata_hub.h +++ b/metadata_hub.h @@ -30,27 +30,8 @@ enum repeat_status_type { 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; @@ -60,8 +41,14 @@ typedef 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 @@ -76,9 +63,65 @@ typedef struct metadata_bundle { 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. diff --git a/mpris-service.c b/mpris-service.c index 668d776e2..37e7bf8a5 100644 --- a/mpris-service.c +++ b/mpris-service.c @@ -82,105 +82,100 @@ void mpris_metadata_watcher(struct metadata_bundle *argc, __attribute__((unused) 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,