From: Mike Brady Date: Sat, 31 Mar 2018 11:39:26 +0000 (+0100) Subject: Send rtsp events to the metadata thteread for handling. Begin to clean up signals. X-Git-Tag: 3.2RC1~7^2~22 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=69b3b5d7764097c01d45deb725d7ac5ac3d02c4d;p=thirdparty%2Fshairport-sync.git Send rtsp events to the metadata thteread for handling. Begin to clean up signals. --- diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 2e13e955..e716f4a2 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -1,3 +1,7 @@ +Version 3.2d33 +==== +A new metadata token -- 'pffr' The First Frame of a play session has been Received. Not sure we'll keep it... + Version 3.2d30 ==== **Enhancements** diff --git a/audio_alsa.c b/audio_alsa.c index dcc3f26f..7a60db22 100644 --- a/audio_alsa.c +++ b/audio_alsa.c @@ -947,7 +947,7 @@ void do_volume(double vol) { // caller is assumed to have the alsa_mutex when us // debug(1,"Set alsa volume."); do_snd_mixer_selem_set_playback_dB_all(alsa_mix_elem, vol); } else { - debug(1, "Not setting volume because volume-based mute is active"); + debug(2, "Not setting volume because volume-based mute is active"); } } volume_set_request = 0; // any external request that has been made is now satisfied diff --git a/common.c b/common.c index 2ae3b1ce..33666e95 100644 --- a/common.c +++ b/common.c @@ -984,16 +984,29 @@ void memory_barrier() { pthread_mutex_unlock(&barrier_mutex); } -int ss_pthread_mutex_timedlock(pthread_mutex_t *mutex, useconds_t dally_time, - const char *debugmessage, int debuglevel) { +void sps_nanosleep(const time_t sec, const long nanosec) { + struct timespec req, rem; + int result; + req.tv_sec = sec; + req.tv_nsec = nanosec; + do { + result = nanosleep(&req, &rem); + rem = req; + } while ((result == -1) && (errno == EINTR)); + if (result == -1) + debug(1, "Error in sps_nanosleep of %d sec and %ld nanoseconds: %d.", sec, nanosec, errno); +} + +int sps_pthread_mutex_timedlock(pthread_mutex_t *mutex, useconds_t dally_time, + const char *debugmessage, int debuglevel) { - int time_to_wait = dally_time; + useconds_t time_to_wait = dally_time; int r = pthread_mutex_trylock(mutex); while ((r) && (time_to_wait > 0)) { - int st = time_to_wait; + useconds_t st = time_to_wait; if (st > 20000) st = 20000; - usleep(st); + sps_nanosleep(0, st * 1000); time_to_wait -= st; r = pthread_mutex_trylock(mutex); } diff --git a/common.h b/common.h index 15f02696..49717c8b 100644 --- a/common.h +++ b/common.h @@ -251,9 +251,12 @@ void shairport_shutdown(); extern sigset_t pselect_sigset; // wait for the specified time in microseconds -- it checks every 20 milliseconds -int ss_pthread_mutex_timedlock(pthread_mutex_t *mutex, useconds_t dally_time, - const char *debugmessage, int debuglevel); +int sps_pthread_mutex_timedlock(pthread_mutex_t *mutex, useconds_t dally_time, + const char *debugmessage, int debuglevel); char *get_version_string(); // mallocs a string space -- remember to free it afterwards +void sps_nanosleep(const time_t sec, + const long nanosec); // waits for this time, even through interruptions + #endif // _COMMON_H diff --git a/dacp.c b/dacp.c index 542f9890..cb6581ca 100644 --- a/dacp.c +++ b/dacp.c @@ -67,11 +67,14 @@ struct HttpResponse { int code; }; -static void *response_realloc(__attribute__((unused)) void *opaque, void *ptr, int size) { - return realloc(ptr, size); +void *response_realloc(__attribute__((unused)) void *opaque, void *ptr, int size) { + void *t = realloc(ptr, size); + if ((t == NULL) && (size != 0)) + debug(1, "Response realloc of size %d failed!", size); + return t; } -static void response_body(void *opaque, const char *data, int size) { +void response_body(void *opaque, const char *data, int size) { struct HttpResponse *response = (struct HttpResponse *)opaque; ssize_t space_available = response->malloced_size - response->size; @@ -159,7 +162,8 @@ int dacp_send_command(const char *command, char **body, ssize_t *bodysize) { // only do this one at a time -- not sure it is necessary, but better safe than sorry - int mutex_reply = ss_pthread_mutex_timedlock(&dacp_conversation_lock, 1000000, command, 1); + int mutex_reply = sps_pthread_mutex_timedlock(&dacp_conversation_lock, 2000000, command, 1); + // int mutex_reply = pthread_mutex_lock(&dacp_conversation_lock); if (mutex_reply == 0) { // debug(1,"dacp_conversation_lock acquired for command \"%s\".",command); @@ -170,6 +174,13 @@ int dacp_send_command(const char *command, char **body, ssize_t *bodysize) { // debug(1, "DACP socket could not be created -- error %d: \"%s\".",errno,strerror(errno)); response.code = 497; // Can't establish a socket to the DACP server } else { + struct timeval tv; + tv.tv_sec = 0; + tv.tv_usec = 250000; + if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char *)&tv, sizeof tv) == -1) + debug(1, "Error %d setting receive timeout for DACP service.", errno); + if (setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, (const char *)&tv, sizeof tv) == -1) + debug(1, "Error %d setting send timeout for DACP service.", errno); // connect! // debug(1, "DACP socket created."); @@ -185,24 +196,28 @@ int dacp_send_command(const char *command, char **body, ssize_t *bodysize) { // Send command // debug(1,"DACP connect message: \"%s\".",message); + if (setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, (const char *)&tv, sizeof tv) == -1) + debug(1, "Error %d setting send timeout for DACP service.", errno); if (send(sockfd, message, strlen(message), 0) != (ssize_t)strlen(message)) { // debug(1, "Send failed"); response.code = 493; // Client failed to send a message } else { - response.body = malloc(2048); // it can resize this if necessary - response.malloced_size = 2048; + response.body = malloc(32768); // it can resize this if necessary + response.malloced_size = 32768; struct http_roundtripper rt; http_init(&rt, responseFuncs, &response); int needmore = 1; int looperror = 0; - char buffer[1024]; + char buffer[8192]; memset(buffer, 0, sizeof(buffer)); while (needmore && !looperror) { const char *data = buffer; + if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char *)&tv, sizeof tv) == -1) + debug(1, "Error %d setting receive timeout for DACP service.", errno); int ndata = recv(sockfd, buffer, sizeof(buffer), 0); // debug(1,"Received %d bytes: \"%s\".",ndata,buffer); if (ndata <= 0) { @@ -238,10 +253,12 @@ int dacp_send_command(const char *command, char **body, ssize_t *bodysize) { // debug(1,"DACP socket closed."); } pthread_mutex_unlock(&dacp_conversation_lock); + // debug(1,"Sent command\"%s\" with a response body of size %d.",command,response.size); // debug(1,"dacp_conversation_lock released."); } else { - // debug(1, "Could not acquire a lock on the dacp transmit/receive section. Possible - // timeout?"); + debug(1, "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 } } @@ -268,7 +285,7 @@ void relinquish_dacp_server_information(rtsp_conn_info *conn) { // as the conn's connection number // this is to signify that the player has stopped, but only if another thread (with a different // index) hasn't already taken over the dacp service - ss_pthread_mutex_timedlock( + sps_pthread_mutex_timedlock( &dacp_server_information_lock, 500000, "set_dacp_server_information couldn't get DACP server information lock in 0.5 second!.", 1); if (dacp_server.players_connection_thread_index == conn->connection_number) @@ -279,7 +296,7 @@ void relinquish_dacp_server_information(rtsp_conn_info *conn) { // this will be running on the thread of its caller, not of the conversation thread... void set_dacp_server_information(rtsp_conn_info *conn) { // tell the DACP conversation thread that // the port has been set or changed - ss_pthread_mutex_timedlock( + sps_pthread_mutex_timedlock( &dacp_server_information_lock, 500000, "set_dacp_server_information couldn't get DACP server information lock in 0.5 second!.", 1); dacp_server.players_connection_thread_index = conn->connection_number; @@ -290,8 +307,10 @@ void set_dacp_server_information(rtsp_conn_info *conn) { // tell the DACP conver dacp_server.active_remote_id = conn->dacp_active_remote; if (dacp_server.port) dacp_server.scan_enable = 1; - else + else { + debug(1, "DACP server port has been set to zero."); dacp_server.scan_enable = 0; + } metadata_hub_modify_prolog(); int ch = metadata_store.dacp_server_active != dacp_server.scan_enable; metadata_store.dacp_server_active = dacp_server.scan_enable; @@ -320,7 +339,7 @@ void *dacp_monitor_thread_code(__attribute__((unused)) void *na) { int32_t revision_number = 1; while (1) { int result; - ss_pthread_mutex_timedlock( + sps_pthread_mutex_timedlock( &dacp_server_information_lock, 500000, "dacp_monitor_thread_code couldn't get DACP server information lock in 0.5 second!.", 1); while (dacp_server.scan_enable == 0) { @@ -331,7 +350,7 @@ void *dacp_monitor_thread_code(__attribute__((unused)) void *na) { int32_t the_volume; result = dacp_get_volume(&the_volume); // just want the http code if ((result == 496) || (result == 403) || (result == 501)) { - // debug(1,"Stopping scan because the response to \"dacp_get_volume(NULL)\" is %d.",result); + debug(1, "Stopping scan because the response to \"dacp_get_volume(NULL)\" is %d.", result); dacp_server.scan_enable = 0; metadata_hub_modify_prolog(); int ch = metadata_store.dacp_server_active != 0; @@ -342,17 +361,21 @@ void *dacp_monitor_thread_code(__attribute__((unused)) void *na) { // debug(1, "DACP Server ID \"%u\" at \"%s:%u\", scan %d.", dacp_server.active_remote_id, // dacp_server.ip_string, dacp_server.port, scan_index); - int adv = (result == 200); - // a result of 200 means the advanced features of, e.g., iTunes, are available - // so, turn the advanced_dacp_server flag on or off and flag if it's changed. - metadata_hub_modify_prolog(); - int diff = metadata_store.advanced_dacp_server_active != adv; - if (diff) - metadata_store.advanced_dacp_server_active = adv; - metadata_hub_modify_epilog(diff); + if (result != 494) { // this just means that it couldn't send the query because something else + // was sending a command + int adv = (result == 200); + // a result of 200 means the advanced features of, e.g., iTunes, are available + // so, turn the advanced_dacp_server flag on or off and flag if it's changed. + metadata_hub_modify_prolog(); + int diff = metadata_store.advanced_dacp_server_active != adv; + if (diff) { + metadata_store.advanced_dacp_server_active = adv; + debug(1, "Setting dacp_server_active to %d because of a response of %d.", adv, result); + } + metadata_hub_modify_epilog(diff); + } if (result == 200) { - metadata_hub_modify_prolog(); int diff = metadata_store.speaker_volume != the_volume; if (diff) diff --git a/dbus-service.c b/dbus-service.c index e78703e3..150f5aca 100644 --- a/dbus-service.c +++ b/dbus-service.c @@ -20,174 +20,175 @@ ShairportSyncRemoteControl *shairportSyncRemoteControlSkeleton = NULL; ShairportSyncAdvancedRemoteControl *shairportSyncAdvancedRemoteControlSkeleton = NULL; void dbus_metadata_watcher(struct metadata_bundle *argc, __attribute__((unused)) void *userdata) { - char response[100]; - - shairport_sync_advanced_remote_control_set_volume(shairportSyncAdvancedRemoteControlSkeleton, - argc->speaker_volume); - - shairport_sync_remote_control_set_airplay_volume(shairportSyncRemoteControlSkeleton, - argc->airplay_volume); - - shairport_sync_remote_control_set_server(shairportSyncRemoteControlSkeleton, argc->client_ip); - - if (argc->dacp_server_active) { - shairport_sync_remote_control_set_available(shairportSyncRemoteControlSkeleton, TRUE); - } else { - shairport_sync_remote_control_set_available(shairportSyncRemoteControlSkeleton, FALSE); - } - - if (argc->advanced_dacp_server_active) { - shairport_sync_advanced_remote_control_set_available(shairportSyncAdvancedRemoteControlSkeleton, - TRUE); - } else { - shairport_sync_advanced_remote_control_set_available(shairportSyncAdvancedRemoteControlSkeleton, - FALSE); - } - - switch (argc->player_state) { - case PS_NOT_AVAILABLE: - shairport_sync_remote_control_set_player_state(shairportSyncRemoteControlSkeleton, - "Not Available"); - case PS_STOPPED: - shairport_sync_remote_control_set_player_state(shairportSyncRemoteControlSkeleton, "Stopped"); - break; - case PS_PAUSED: - shairport_sync_remote_control_set_player_state(shairportSyncRemoteControlSkeleton, "Paused"); - break; - case PS_PLAYING: - shairport_sync_remote_control_set_player_state(shairportSyncRemoteControlSkeleton, "Playing"); - break; - default: - debug(1, "This should never happen."); - } - - switch (argc->play_status) { - case PS_NOT_AVAILABLE: - shairport_sync_advanced_remote_control_set_playback_status( - shairportSyncAdvancedRemoteControlSkeleton, "Not Available"); - case PS_STOPPED: - shairport_sync_advanced_remote_control_set_playback_status( - shairportSyncAdvancedRemoteControlSkeleton, "Stopped"); - break; - case PS_PAUSED: - shairport_sync_advanced_remote_control_set_playback_status( - shairportSyncAdvancedRemoteControlSkeleton, "Paused"); - break; - case PS_PLAYING: - shairport_sync_advanced_remote_control_set_playback_status( - shairportSyncAdvancedRemoteControlSkeleton, "Playing"); - break; - default: - debug(1, "This should never happen."); - } - - switch (argc->repeat_status) { - case RS_NOT_AVAILABLE: - strcpy(response,"Not Available"); - break; - case RS_OFF: - strcpy(response,"Off"); - break; - case RS_ONE: - strcpy(response,"One"); - break; - case RS_ALL: - strcpy(response,"All"); - break; - default: - debug(1, "This should never happen."); - } - const char *th = shairport_sync_advanced_remote_control_get_loop_status(shairportSyncAdvancedRemoteControlSkeleton); - - // only set this if it's different - if ((th==NULL) || (strcasecmp(th,response)!=0)) { - debug(1,"Loop Status should be changed"); - shairport_sync_advanced_remote_control_set_loop_status(shairportSyncAdvancedRemoteControlSkeleton, response); - } - - switch (argc->shuffle_status) { - case SS_NOT_AVAILABLE: - shairport_sync_advanced_remote_control_set_shuffle(shairportSyncAdvancedRemoteControlSkeleton, - FALSE); - break; - case SS_OFF: - shairport_sync_advanced_remote_control_set_shuffle(shairportSyncAdvancedRemoteControlSkeleton, - FALSE); - break; - case SS_ON: - shairport_sync_advanced_remote_control_set_shuffle(shairportSyncAdvancedRemoteControlSkeleton, - TRUE); - break; - default: - 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]; - sprintf(artURIstring, "file://%s", argc->cover_art_pathname); - // sprintf(artURIstring,""); - // 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 - if (argc->item_id) { - char trackidstring[128]; - // debug(1, "Set ID using mper ID: \"%u\".",argc->item_id); - sprintf(trackidstring, "/org/gnome/ShairportSync/mper_%u", 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_name) { - // debug(1, "Track name set to \"%s\".", argc->track_name); - GVariant *trackname = g_variant_new("s", argc->track_name); - g_variant_builder_add(dict_builder, "{sv}", "xesam:title", trackname); - } - - // Add the album name if there is one - if (argc->album_name) { - // debug(1, "Album name set to \"%s\".", argc->album_name); - GVariant *albumname = g_variant_new("s", argc->album_name); - g_variant_builder_add(dict_builder, "{sv}", "xesam:album", albumname); - } - - // Add the artists if there are any (actually there will be at most one, but put it in an array) - if (argc->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->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 genres if there are any (actually there will be at most one, but put it in an array) - if (argc->genre) { - // debug(1,"Build genre"); - aa = g_variant_builder_new(G_VARIANT_TYPE("as")); - g_variant_builder_add(aa, "s", argc->genre); - GVariant *genres = g_variant_builder_end(aa); - g_variant_builder_unref(aa); - g_variant_builder_add(dict_builder, "{sv}", "xesam:genre", genres); - } - - 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); - + char response[100]; + + shairport_sync_advanced_remote_control_set_volume(shairportSyncAdvancedRemoteControlSkeleton, + argc->speaker_volume); + + shairport_sync_remote_control_set_airplay_volume(shairportSyncRemoteControlSkeleton, + argc->airplay_volume); + + shairport_sync_remote_control_set_server(shairportSyncRemoteControlSkeleton, argc->client_ip); + + if (argc->dacp_server_active) { + shairport_sync_remote_control_set_available(shairportSyncRemoteControlSkeleton, TRUE); + } else { + shairport_sync_remote_control_set_available(shairportSyncRemoteControlSkeleton, FALSE); + } + + if (argc->advanced_dacp_server_active) { + shairport_sync_advanced_remote_control_set_available(shairportSyncAdvancedRemoteControlSkeleton, + TRUE); + } else { + shairport_sync_advanced_remote_control_set_available(shairportSyncAdvancedRemoteControlSkeleton, + FALSE); + } + + switch (argc->player_state) { + case PS_NOT_AVAILABLE: + shairport_sync_remote_control_set_player_state(shairportSyncRemoteControlSkeleton, + "Not Available"); + case PS_STOPPED: + shairport_sync_remote_control_set_player_state(shairportSyncRemoteControlSkeleton, "Stopped"); + break; + case PS_PAUSED: + shairport_sync_remote_control_set_player_state(shairportSyncRemoteControlSkeleton, "Paused"); + break; + case PS_PLAYING: + shairport_sync_remote_control_set_player_state(shairportSyncRemoteControlSkeleton, "Playing"); + break; + default: + debug(1, "This should never happen."); + } + + switch (argc->play_status) { + case PS_NOT_AVAILABLE: + shairport_sync_advanced_remote_control_set_playback_status( + shairportSyncAdvancedRemoteControlSkeleton, "Not Available"); + case PS_STOPPED: + shairport_sync_advanced_remote_control_set_playback_status( + shairportSyncAdvancedRemoteControlSkeleton, "Stopped"); + break; + case PS_PAUSED: + shairport_sync_advanced_remote_control_set_playback_status( + shairportSyncAdvancedRemoteControlSkeleton, "Paused"); + break; + case PS_PLAYING: + shairport_sync_advanced_remote_control_set_playback_status( + shairportSyncAdvancedRemoteControlSkeleton, "Playing"); + break; + default: + debug(1, "This should never happen."); + } + + switch (argc->repeat_status) { + case RS_NOT_AVAILABLE: + strcpy(response, "Not Available"); + break; + case RS_OFF: + strcpy(response, "Off"); + break; + case RS_ONE: + strcpy(response, "One"); + break; + case RS_ALL: + strcpy(response, "All"); + break; + default: + debug(1, "This should never happen."); + } + const char *th = shairport_sync_advanced_remote_control_get_loop_status( + shairportSyncAdvancedRemoteControlSkeleton); + + // only set this if it's different + if ((th == NULL) || (strcasecmp(th, response) != 0)) { + debug(1, "Loop Status should be changed"); + shairport_sync_advanced_remote_control_set_loop_status( + shairportSyncAdvancedRemoteControlSkeleton, response); + } + + switch (argc->shuffle_status) { + case SS_NOT_AVAILABLE: + shairport_sync_advanced_remote_control_set_shuffle(shairportSyncAdvancedRemoteControlSkeleton, + FALSE); + break; + case SS_OFF: + shairport_sync_advanced_remote_control_set_shuffle(shairportSyncAdvancedRemoteControlSkeleton, + FALSE); + break; + case SS_ON: + shairport_sync_advanced_remote_control_set_shuffle(shairportSyncAdvancedRemoteControlSkeleton, + TRUE); + break; + default: + 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]; + sprintf(artURIstring, "file://%s", argc->cover_art_pathname); + // sprintf(artURIstring,""); + // 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 + if (argc->item_id) { + char trackidstring[128]; + // debug(1, "Set ID using mper ID: \"%u\".",argc->item_id); + sprintf(trackidstring, "/org/gnome/ShairportSync/mper_%u", 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_name) { + // debug(1, "Track name set to \"%s\".", argc->track_name); + GVariant *trackname = g_variant_new("s", argc->track_name); + g_variant_builder_add(dict_builder, "{sv}", "xesam:title", trackname); + } + + // Add the album name if there is one + if (argc->album_name) { + // debug(1, "Album name set to \"%s\".", argc->album_name); + GVariant *albumname = g_variant_new("s", argc->album_name); + g_variant_builder_add(dict_builder, "{sv}", "xesam:album", albumname); + } + + // Add the artists if there are any (actually there will be at most one, but put it in an array) + if (argc->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->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 genres if there are any (actually there will be at most one, but put it in an array) + if (argc->genre) { + // debug(1,"Build genre"); + aa = g_variant_builder_new(G_VARIANT_TYPE("as")); + g_variant_builder_add(aa, "s", argc->genre); + GVariant *genres = g_variant_builder_end(aa); + g_variant_builder_unref(aa); + g_variant_builder_add(dict_builder, "{sv}", "xesam:genre", genres); + } + + 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, @@ -485,7 +486,6 @@ gboolean notify_shuffle_callback(ShairportSyncAdvancedRemoteControl *skeleton, send_simple_dacp_command("setproperty?dacp.shufflestate=1"); else send_simple_dacp_command("setproperty?dacp.shufflestate=0"); - ; return TRUE; } @@ -495,7 +495,7 @@ gboolean notify_loop_status_callback(ShairportSyncAdvancedRemoteControl *skeleto char *th = (char *)shairport_sync_advanced_remote_control_get_loop_status(skeleton); // enum volume_control_profile_type previous_volume_control_profile = // config.volume_control_profile; - debug(1,"notify_loop_status_callback called with loop status of \"%s\".",th); + debug(1, "notify_loop_status_callback called with loop status of \"%s\".", th); if (strcasecmp(th, "off") == 0) send_simple_dacp_command("setproperty?dacp.repeatstate=0"); else if (strcasecmp(th, "one") == 0) @@ -555,7 +555,7 @@ static void on_dbus_name_acquired(GDBusConnection *connection, const gchar *name shairportSyncAdvancedRemoteControlSkeleton = shairport_sync_advanced_remote_control_skeleton_new(); - + g_dbus_interface_skeleton_export( G_DBUS_INTERFACE_SKELETON(shairportSyncAdvancedRemoteControlSkeleton), connection, "/org/gnome/ShairportSync", NULL); @@ -623,7 +623,7 @@ static void on_dbus_name_acquired(GDBusConnection *connection, const gchar *name G_CALLBACK(notify_loop_status_callback), NULL); add_metadata_watcher(dbus_metadata_watcher, NULL); - + shairport_sync_set_loudness_threshold(SHAIRPORT_SYNC(shairportSyncSkeleton), config.loudness_reference_volume_db); @@ -701,9 +701,9 @@ static void on_dbus_name_acquired(GDBusConnection *connection, const gchar *name "Not Available"); shairport_sync_advanced_remote_control_set_playback_status( shairportSyncAdvancedRemoteControlSkeleton, "Not Available"); - - shairport_sync_advanced_remote_control_set_loop_status( - shairportSyncAdvancedRemoteControlSkeleton, "Not Available"); + + shairport_sync_advanced_remote_control_set_loop_status(shairportSyncAdvancedRemoteControlSkeleton, + "Not Available"); debug(1, "Shairport Sync native D-Bus service started at \"%s\" on the %s bus.", name, (config.dbus_service_bus_type == DBT_session) ? "session" : "system"); diff --git a/metadata_hub.c b/metadata_hub.c index 910de544..3f912cf0 100644 --- a/metadata_hub.c +++ b/metadata_hub.c @@ -457,11 +457,49 @@ void metadata_hub_process_metadata(uint32_t type, uint32_t code, char *data, uin break; // these could tell us about play / pause etc. but will only occur if metadata is enabled, so // we'll just ignore them - case 'pbeg': - case 'pend': - case 'pfls': - case 'prsm': - break; + case 'pbeg': { + metadata_hub_modify_prolog(); + int changed = (metadata_store.player_state != PS_PLAYING); + metadata_store.player_state = PS_PLAYING; + metadata_hub_modify_epilog(changed); + } break; + case 'pend': { + metadata_hub_modify_prolog(); + metadata_store.player_state = PS_STOPPED; + // debug(1,"player_stop release track metadata and artwork"); + metadata_hub_reset_track_metadata(); + metadata_hub_release_track_artwork(); + metadata_hub_modify_epilog(1); + } break; + case 'pfls': { + metadata_hub_modify_prolog(); + int changed = (metadata_store.player_state != PS_PAUSED); + metadata_store.player_state = PS_PAUSED; + metadata_hub_modify_epilog(changed); + } break; + case 'pffr': // this is sent when the first frame has been received + 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; + 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; + } + if (metadata_store.airplay_volume != config.airplay_volume) { + metadata_store.airplay_volume = config.airplay_volume; + modified = 1; + } + metadata_hub_modify_epilog(modified); // change + } break; default: { char typestring[5]; diff --git a/player.c b/player.c index e8e5a052..3e36940d 100644 --- a/player.c +++ b/player.c @@ -827,12 +827,11 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) { // frames...",curframe->timestamp,seq_diff(ab_read, ab_write)); // say we have started playing here -#ifdef HAVE_METADATA_HUB - metadata_hub_modify_prolog(); - int changed = (metadata_store.player_state != PS_PLAYING); - metadata_store.player_state = PS_PLAYING; - metadata_hub_modify_epilog(changed); +#ifdef CONFIG_METADATA + send_ssnc_metadata('pffr', NULL, 0, + 0); // "first frame received", but don't wait if the queue is locked #endif + if (reference_timestamp) { // if we have a reference time // debug(1,"First frame seen with timestamp..."); conn->first_packet_timestamp = @@ -1062,12 +1061,6 @@ static abuf_t *buffer_get_frame(rtsp_conn_info *conn) { #ifdef CONFIG_METADATA send_ssnc_metadata('prsm', NULL, 0, 0); // "resume", but don't wait if the queue is locked -#endif -#ifdef HAVE_METADATA_HUB - metadata_hub_modify_prolog(); - int changed = (metadata_store.player_state != PS_PLAYING); - metadata_store.player_state = PS_PLAYING; - metadata_hub_modify_epilog(changed); #endif } } @@ -1604,10 +1597,9 @@ static void *player_thread_func(void *arg) { int sync_error_out_of_bounds = 0; // number of times in a row that there's been a serious sync error - // stop looking elsewhere for DACP stuff - conn->dacp_port = 0; +// stop looking elsewhere for DACP stuff #ifdef HAVE_DACP_CLIENT - set_dacp_server_information(conn); // this will stop scanning until a port is registered by the + set_dacp_server_information(conn); // this will start scanning when a port is registered by the // code initiated by the mdns_dacp_monitor #endif // start an mdns/zeroconf thread to look for DACP messages containing our DACP_ID and getting the @@ -2520,21 +2512,6 @@ void player_volume_without_notification(double airplay_volume, rtsp_conn_info *c void player_volume(double airplay_volume, rtsp_conn_info *conn) { command_set_volume(airplay_volume); -#ifdef HAVE_METADATA_HUB - 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; - } - if (metadata_store.airplay_volume != airplay_volume) { - metadata_store.airplay_volume = airplay_volume; - modified = 1; - } - metadata_hub_modify_epilog(modified); // change -#endif player_volume_without_notification(airplay_volume, conn); } @@ -2550,13 +2527,6 @@ void player_flush(int64_t timestamp, rtsp_conn_info *conn) { #ifdef CONFIG_METADATA send_ssnc_metadata('pfls', NULL, 0, 1); #endif - -#ifdef HAVE_METADATA_HUB - metadata_hub_modify_prolog(); - int changed = (metadata_store.player_state != PS_PAUSED); - metadata_store.player_state = PS_PAUSED; - metadata_hub_modify_epilog(changed); -#endif } int player_play(rtsp_conn_info *conn) { @@ -2582,12 +2552,6 @@ int player_play(rtsp_conn_info *conn) { debug(1, "Error setting stack size for player_thread: %s", strerror(errno)); pthread_create(pt, &tattr, player_thread_func, (void *)conn); pthread_attr_destroy(&tattr); -#ifdef HAVE_METADATA_HUB - metadata_hub_modify_prolog(); - int changed = (metadata_store.player_state != PS_PLAYING); - metadata_store.player_state = PS_PLAYING; - metadata_hub_modify_epilog(changed); -#endif return 0; } @@ -2603,14 +2567,6 @@ void player_stop(rtsp_conn_info *conn) { command_stop(); free(conn->player_thread); conn->player_thread = NULL; -#ifdef HAVE_METADATA_HUB - metadata_hub_modify_prolog(); - metadata_store.player_state = PS_STOPPED; - // debug(1,"player_stop release track metadata and artwork"); - metadata_hub_reset_track_metadata(); - metadata_hub_release_track_artwork(); - metadata_hub_modify_epilog(1); -#endif } else { debug(3, "player thread of RTSP conversation %d is already deleted.", conn->connection_number); } diff --git a/rtsp.c b/rtsp.c index 51c2b4bb..530fdbfa 100644 --- a/rtsp.c +++ b/rtsp.c @@ -188,7 +188,7 @@ int pc_queue_add_item(pc_queue *the_queue, const void *the_stuff, int block) { the_queue->eoq = i; the_queue->count++; if (the_queue->count == the_queue->capacity) - debug(1, "pc_queue is full!"); + debug(1, "pc_queue is full with %d items in it!", the_queue->count); rc = pthread_cond_signal(&the_queue->pc_queue_item_added_signal); if (rc) debug(1, "Error signalling after pc_queue_add_item"); @@ -914,6 +914,8 @@ static void handle_set_parameter_parameter(rtsp_conn_info *conn, rtsp_message *r // 'pend' -- play stream end. No arguments // 'pfls' -- play stream flush. No arguments // 'prsm' -- play stream resume. No arguments +// `pffr` -- the first frame of a play session has been received and has been validly +//timed. // 'pvol' -- play volume. The volume is sent as a string -- // "airplay_volume,volume,lowest_volume,highest_volume" // volume, lowest_volume and highest_volume are given in dB. @@ -952,7 +954,6 @@ static void handle_set_parameter_parameter(rtsp_conn_info *conn, rtsp_message *r // Can be an IPv4 or an IPv6 number. // `dapo` -- the payload is the port number (as text) on the server to which remote // control commands should be sent. It is 3689 for iTunes but varies for iOS devices. - // A special sub-protocol is used for sending large data items over UDP // If the payload exceeded 4 MB, it is chunked using the following format: // "ssnc", "chnk", packet_ix, packet_counts, packet_tag, packet_type, chunked_data.