]> git.ipfire.org Git - thirdparty/shairport-sync.git/commitdiff
Send rtsp events to the metadata thteread for handling. Begin to clean up signals.
authorMike Brady <mikebrady@eircom.net>
Sat, 31 Mar 2018 11:39:26 +0000 (12:39 +0100)
committerMike Brady <mikebrady@eircom.net>
Sat, 31 Mar 2018 11:39:26 +0000 (12:39 +0100)
RELEASENOTES.md
audio_alsa.c
common.c
common.h
dacp.c
dbus-service.c
metadata_hub.c
player.c
rtsp.c

index 2e13e9553fc6df66ffbb20be15aa6d3be78fdcfc..e716f4a276b82b8bc9a0ad7b5f59eddceb03508d 100644 (file)
@@ -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**
index dcc3f26fb36e5b29152eed61f4984dcc40dc6872..7a60db22ae0b452a2b49472dd0baa40e93daf4f0 100644 (file)
@@ -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
index 2ae3b1ce2f778eaa8ad193c73d640170f4b2160f..33666e95c201dcd6a5d919e91a49c6060602492b 100644 (file)
--- 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);
   }
index 15f02696fe4d6c4fdba7bac5846ce0fc04c2d4eb..49717c8b8dfde88075a27536c829c763848955f0 100644 (file)
--- 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 542f9890b57193cce29079bd706ca30f55bfc0a5..cb6581ca2deed5fb303465245cc53c1ee7f372da 100644 (file)
--- 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)
index e78703e3db121cac949580db6adb4387ea2d4287..150f5aca02225576f5e4a1faca5e3488edb3883e 100644 (file)
@@ -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");
index 910de5447834b50dc326e09f53be3b4e397af421..3f912cf0c864bda935155201091a84a73b15e690 100644 (file)
@@ -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];
index e8e5a052f69c2dd9ac5fde0936ca5b87ccf99f4f..3e36940d71d94ab4f8d205b8c5832dfacbf57723 100644 (file)
--- 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 51c2b4bb84ee9ced1c31061967ed88027bf3647f..530fdbfa47a90a99ac9ae35f762b02d4f2060b60 100644 (file)
--- 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.