]> git.ipfire.org Git - thirdparty/shairport-sync.git/commitdiff
Add new metadata item: sps:songdatakind, derived from the asdk metadata token. It...
authorMike Brady <4265913+mikebrady@users.noreply.github.com>
Sat, 12 Nov 2022 16:42:25 +0000 (16:42 +0000)
committerMike Brady <4265913+mikebrady@users.noreply.github.com>
Sat, 12 Nov 2022 16:42:25 +0000 (16:42 +0000)
dacp.c
dbus-service.c
metadata_hub.c
metadata_hub.h
mpris-service.c
mqtt.c
org.gnome.ShairportSync.xml
player.c
shairport.c

diff --git a/dacp.c b/dacp.c
index 73164ae256f8c343cc0c0248e7d9230c1cd570ab..d557de240cd078456780ca746d3c4f732911c508 100644 (file)
--- a/dacp.c
+++ b/dacp.c
@@ -803,8 +803,8 @@ void *dacp_monitor_thread_code(__attribute__((unused)) void *na) {
                 case 'canp': // nowplaying 4 ids: dbid, plid, playlistItem, itemid (from mellowware
                              // see reference above)
                   debug(2, "DACP Composite ID seen");
-                  if (memcmp(metadata_store.item_composite_id, sp - item_size,
-                             sizeof(metadata_store.item_composite_id)) != 0) {
+                  if ((metadata_store.item_composite_id_is_valid == 0) || (memcmp(metadata_store.item_composite_id, sp - item_size,
+                             sizeof(metadata_store.item_composite_id)) != 0)) {
                     memcpy(metadata_store.item_composite_id, sp - item_size,
                            sizeof(metadata_store.item_composite_id));
                     char st[33];
@@ -817,6 +817,7 @@ void *dacp_monitor_thread_code(__attribute__((unused)) void *na) {
                     *pt = 0;
                     debug(2, "Item composite ID changed to 0x%s.", st);
                     metadata_store.item_composite_id_changed = 1;
+                    metadata_store.item_composite_id_is_valid = 1;
                   }
                   break;
                 case 'astm':
@@ -826,6 +827,7 @@ void *dacp_monitor_thread_code(__attribute__((unused)) void *na) {
                   if (ui != metadata_store.songtime_in_milliseconds) {
                     metadata_store.songtime_in_milliseconds = ui;
                     metadata_store.songtime_in_milliseconds_changed = 1;
+                    metadata_store.songtime_in_milliseconds_is_valid = 1;
                     debug(2, "DACP Song Time set to: \"%u\"",
                           metadata_store.songtime_in_milliseconds);
                   }
index b8cfea16ba802de77900387c8610c0ec373cb601..51c1b12829ba7d45e3c08915a4429fc3b1aa0e76 100644 (file)
@@ -96,6 +96,18 @@ void dbus_metadata_watcher(struct metadata_bundle *argc, __attribute__((unused))
     }
   }
 
+  if (argc->stream_type) {
+    // debug(1, "Check stream type");
+    th = shairport_sync_remote_control_get_stream_type(shairportSyncRemoteControlSkeleton);
+    if ((th == NULL) || (strcasecmp(th, argc->stream_type) != 0)) {
+      // debug(1, "Stream type string should be changed");
+      shairport_sync_remote_control_set_stream_type(shairportSyncRemoteControlSkeleton,
+                                                        argc->stream_type);
+    }
+  }
+  
+  
+
   switch (argc->player_state) {
   case PS_NOT_AVAILABLE:
     shairport_sync_remote_control_set_player_state(shairportSyncRemoteControlSkeleton,
@@ -202,8 +214,8 @@ void dbus_metadata_watcher(struct metadata_bundle *argc, __attribute__((unused))
     g_variant_builder_add(dict_builder, "{sv}", "mpris:artUrl", artUrl);
   }
 
-  // Add in the Track ID based on the 'mper' metadata if it is non-zero
-  if (argc->item_id != 0) {
+  // Add in the Track ID based on the 'mper' metadata if it is valid
+  if (argc->item_id_is_valid != 0) {
     char trackidstring[128];
     snprintf(trackidstring, sizeof(trackidstring), "/org/gnome/ShairportSync/%" PRIX64 "",
              argc->item_id);
@@ -211,6 +223,14 @@ void dbus_metadata_watcher(struct metadata_bundle *argc, __attribute__((unused))
     g_variant_builder_add(dict_builder, "{sv}", "mpris:trackid", trackid);
   }
 
+  // Add in the Song Data Kind based on the 'asdk' metadata if it is valid
+  // It seems that this is 0 for a timed play, e.g. a track or an album, but is 1 for an untimed play, such as a stream.
+  
+  if (argc->song_data_kind_is_valid != 0) {
+    GVariant *songdatakind = g_variant_new_uint32(argc->song_data_kind);
+    g_variant_builder_add(dict_builder, "{sv}", "sps:songdatakind", songdatakind);
+  }
+
   // Add the track name if it exists
   if (argc->track_name) {
     GVariant *track_name = g_variant_new("s", argc->track_name);
@@ -241,11 +261,11 @@ void dbus_metadata_watcher(struct metadata_bundle *argc, __attribute__((unused))
     g_variant_builder_add(dict_builder, "{sv}", "xesam:genre", genre);
   }
 
-  if (argc->songtime_in_milliseconds) {
+  if (argc->songtime_in_milliseconds_is_valid != 0) {
     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);
+    // debug(1, "Set tracklength to %" PRId64 ".", track_length_in_microseconds);
     GVariant *tracklength = g_variant_new("x", track_length_in_microseconds);
     g_variant_builder_add(dict_builder, "{sv}", "mpris:length", tracklength);
   }
index 9ad7052de283893a2c0db4a5125435e2cdf94ff8..29190f90b5c16bd5acdd3630d61b2af4147b9dc3 100644 (file)
@@ -110,6 +110,7 @@ void run_metadata_watchers(void) {
   metadata_store.artist_name_changed = 0;
   metadata_store.album_artist_name_changed = 0;
   metadata_store.album_name_changed = 0;
+  metadata_store.song_data_kind_changed = 0;
   metadata_store.track_name_changed = 0;
   metadata_store.genre_changed = 0;
   metadata_store.comment_changed = 0;
@@ -348,6 +349,19 @@ void metadata_hub_process_metadata(uint32_t type, uint32_t code, char *data, uin
   char *cs;
   if (type == 'core') {
     switch (code) {
+    case 'asdk': {
+      // get the one-byte number as an unsigned number
+      int song_data_kind = data[0]; // one byte
+      song_data_kind = song_data_kind & 0xFF; // unsigned
+      debug(2, "MH Song Data Kind seen: \"%d\" of length %u.", song_data_kind, length);
+      if ((song_data_kind != metadata_store.song_data_kind) || (metadata_store.song_data_kind_is_valid == 0)) {
+        metadata_store.song_data_kind = song_data_kind;
+        metadata_store.song_data_kind_changed = 1;
+        metadata_store.song_data_kind_is_valid = 1;
+        debug(2, "MH Song Data Kind set to: \"%d\"", metadata_store.song_data_kind);
+        metadata_packet_item_changed = 1;
+      }
+    } break;
     case 'mper': {
       // get the 64-bit number as a uint64_t by reading two uint32_t s and combining them
       uint64_t vl = ntohl(*(uint32_t *)data); // get the high order 32 bits
@@ -355,10 +369,10 @@ void metadata_hub_process_metadata(uint32_t type, uint32_t code, char *data, uin
       uint64_t ul = ntohl(*(uint32_t *)(data + sizeof(uint32_t))); // and the low order 32 bits
       vl = vl + ul;
       debug(2, "MH Item ID seen: \"%" PRIx64 "\" of length %u.", vl, length);
-      if (vl != metadata_store.item_id) {
+      if ((vl != metadata_store.item_id) || (metadata_store.item_id_is_valid == 0)) {
         metadata_store.item_id = vl;
         metadata_store.item_id_changed = 1;
-        metadata_store.item_id_received = 1;
+        metadata_store.item_id_is_valid = 1;
         debug(2, "MH Item ID set to: \"%" PRIx64 "\"", metadata_store.item_id);
         metadata_packet_item_changed = 1;
       }
@@ -366,9 +380,10 @@ void metadata_hub_process_metadata(uint32_t type, uint32_t code, char *data, uin
     case 'astm': {
       uint32_t ui = ntohl(*(uint32_t *)data);
       debug(2, "MH Song Time seen: \"%u\" of length %u.", ui, length);
-      if (ui != metadata_store.songtime_in_milliseconds) {
+      if ((ui != metadata_store.songtime_in_milliseconds) || (metadata_store.songtime_in_milliseconds_is_valid == 0)) {
         metadata_store.songtime_in_milliseconds = ui;
         metadata_store.songtime_in_milliseconds_changed = 1;
+        metadata_store.songtime_in_milliseconds_is_valid = 1;
         debug(2, "MH Song Time set to: \"%u\"", metadata_store.songtime_in_milliseconds);
         metadata_packet_item_changed = 1;
       }
@@ -554,7 +569,7 @@ void metadata_hub_process_metadata(uint32_t type, uint32_t code, char *data, uin
       cs = strndup(data, length);
       if (string_update(&metadata_store.client_name, &metadata_store.client_name_changed, cs)) {
         changed = 1;
-        debug(1, "MH Client Name set to: \"%s\"", metadata_store.client_name);
+        debug(2, "MH Client Name set to: \"%s\"", metadata_store.client_name);
       }
       free(cs);
       break;
@@ -567,6 +582,15 @@ void metadata_hub_process_metadata(uint32_t type, uint32_t code, char *data, uin
       }
       free(cs);
       break;
+    case 'styp':
+      cs = strndup(data, length);
+      if (string_update(&metadata_store.stream_type, &metadata_store.stream_type_changed,
+                        cs)) {
+        changed = 1;
+        debug(2, "MH Stream Type set to: \"%s\"", metadata_store.stream_type);
+      }
+      free(cs);
+      break;
     case 'svip':
       cs = strndup(data, length);
       if (string_update(&metadata_store.server_ip, &metadata_store.server_ip_changed, cs)) {
index bb57a8d54120e8a985235927a567ef39fd4604c5..730e6e8c691322c9d8e08488eab17d65c3c77e71 100644 (file)
@@ -48,6 +48,9 @@ typedef struct metadata_bundle {
 
   char *server_ip; // IP number used by Shairport Sync
   int server_ip_changed;
+  
+  char *stream_type; // Realtime or Buffered
+  int stream_type_changed;
 
   char *progress_string; // progress string, emitted by the source from time to time
   int progress_string_changed;
@@ -70,15 +73,21 @@ typedef struct metadata_bundle {
 
   char *cover_art_pathname;
   int cover_art_pathname_changed;
-
+  
   uint64_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.
+  int item_id_is_valid;
 
   unsigned char
       item_composite_id[16]; // seems to be nowplaying 4 ids: dbid, plid, playlistItem, itemid
   int item_composite_id_changed;
+  int item_composite_id_is_valid;
+
 
+  int song_data_kind;
+  int song_data_kind_changed;
+  int song_data_kind_is_valid;
+  
   char *track_name;
   int track_name_changed;
 
@@ -123,6 +132,8 @@ typedef struct metadata_bundle {
 
   uint32_t songtime_in_milliseconds;
   int songtime_in_milliseconds_changed;
+  int songtime_in_milliseconds_is_valid;
+  
 
   // end
 
index 8416df9538e49132c59d2f349f573a9110a5c76b..b31d179db803ae2087b52a11c462758b6f9a57ad 100644 (file)
@@ -174,7 +174,7 @@ void mpris_metadata_watcher(struct metadata_bundle *argc, __attribute__((unused)
   }
 
   // Add in the Track ID based on the 'mper' metadata if it is non-zero
-  if (argc->item_id != 0) {
+  if (argc->item_id_is_valid != 0) {
     char trackidstring[128];
     snprintf(trackidstring, sizeof(trackidstring), "/org/gnome/ShairportSync/%" PRIX64 "",
              argc->item_id);
@@ -212,7 +212,7 @@ void mpris_metadata_watcher(struct metadata_bundle *argc, __attribute__((unused)
     g_variant_builder_add(dict_builder, "{sv}", "xesam:genre", genre);
   }
 
-  if (argc->songtime_in_milliseconds) {
+  if (argc->songtime_in_milliseconds_is_valid) {
     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
diff --git a/mqtt.c b/mqtt.c
index b12eb97d5f3f26b574bd7ad0bcab929f1bccb7cb..cb426bdfadf290d36b3b759165f3c0e8f8344640 100644 (file)
--- a/mqtt.c
+++ b/mqtt.c
@@ -178,7 +178,10 @@ void mqtt_process_metadata(uint32_t type, uint32_t code, char *data, uint32_t le
       case 'asal':
         mqtt_publish("songalbum", data, length);
         break;
-      case 'clip':
+      case 'asdk':
+        mqtt_publish("songdatakind", data, length); // 0 seem to be a timed item, 1 an untimed stream
+        break;
+     case 'clip':
         mqtt_publish("client_ip", data, length);
         break;
       case 'cdid':
@@ -193,6 +196,12 @@ void mqtt_process_metadata(uint32_t type, uint32_t code, char *data, uint32_t le
       case 'daid':
         mqtt_publish("dacp_id", data, length);
         break;
+      case 'ofmt':
+        mqtt_publish("output_format", data, length);
+        break;
+      case 'ofps':
+        mqtt_publish("output_frame_rate", data, length);
+        break;
       case 'pbeg':
         mqtt_publish("play_start", data, length);
         break;
@@ -216,6 +225,9 @@ void mqtt_process_metadata(uint32_t type, uint32_t code, char *data, uint32_t le
       case 'snam':
         mqtt_publish("client_name", data, length);
         break;
+      case 'styp':
+        mqtt_publish("stream_type", data, length);
+        break;
       case 'svip':
         mqtt_publish("server_ip", data, length);
         break;
index c9f5868fa8bb4b64a4dc2361c01910823b65ba31..47aa35372598b47f609751bacbced26b676d6070 100644 (file)
@@ -51,6 +51,7 @@
                <property name='ProgressString' type='s' access='read'/>
                <property name='Client' type='s' access='read'/>
                <property name='ClientName' type='s' access='read'/>
+               <property name='StreamType' type='s' access='read'/>
                <property name='AirplayVolume' type='d' access='read'/>
     <method name="SetAirplayVolume">
       <arg name="volume" type="d" direction="in" />
index 5aad915343b889a3d2c3b0ebd0c790e7c27b1b20..c6432eecfd9de2da43a081137522b160d7ed7a43 100644 (file)
--- a/player.c
+++ b/player.c
@@ -363,20 +363,32 @@ static void terminate_decoders(rtsp_conn_info *conn) {
 #endif
 }
 
+uint64_t buffers_allocated = 0;
+uint64_t buffers_released = 0;
 static void init_buffer(rtsp_conn_info *conn) {
   // debug(1,"input_bytes_per_frame: %d.", conn->input_bytes_per_frame);
   // debug(1,"input_bit_depth: %d.", conn->input_bit_depth);
   int i;
-  for (i = 0; i < BUFFER_FRAMES; i++)
+  for (i = 0; i < BUFFER_FRAMES; i++) {
     //    conn->audio_buffer[i].data = malloc(conn->input_bytes_per_frame *
     //    conn->max_frames_per_packet);
-    conn->audio_buffer[i].data = malloc(8 * conn->max_frames_per_packet); // todo
+    void *allocation = malloc(8 * conn->max_frames_per_packet);
+    if (allocation == NULL) {
+      die("could not allocate memory for audio buffers. %" PRId64 " buffers allocated, %" PRId64 " buffers released.", buffers_allocated, buffers_released);
+    } else {
+      conn->audio_buffer[i].data = allocation;
+      buffers_allocated++;
+    }
+  }
+  debug(1, "%" PRId64 " buffers allocated, %" PRId64 " buffers released.", buffers_allocated, buffers_released);
 }
 
 static void free_audio_buffers(rtsp_conn_info *conn) {
   int i;
-  for (i = 0; i < BUFFER_FRAMES; i++)
+  for (i = 0; i < BUFFER_FRAMES; i++) {
     free(conn->audio_buffer[i].data);
+    buffers_released++;
+  }
 }
 
 int first_possibly_missing_frame = -1;
@@ -2725,23 +2737,31 @@ void *player_thread_func(void *arg) {
                 sync_error = 0; // say the error was fixed!
               }
               // since this is the first frame of audio, inform the user if requested...
-              if (config.statistics_requested) {
+
 #ifdef CONFIG_AIRPLAY_2
-                if (conn->airplay_stream_type == realtime_stream) {
-                  if (conn->airplay_type == ap_1)
-                    inform("Connection %d: Playback Started -- AirPlay 1 Compatible.",
-                           conn->connection_number);
-                  else
-                    inform("Connection %d: Playback Started -- AirPlay 2 Realtime.",
-                           conn->connection_number);
+              if (conn->airplay_stream_type == realtime_stream) {
+                if (conn->airplay_type == ap_1) {
+                  send_ssnc_metadata('styp', "Classic", strlen("Classic"), 1);
+                  if (config.statistics_requested)
+                    inform("Connection %d: Playback Started at frame %" PRId64 " -- AirPlay 1 Compatible.",
+                         conn->connection_number, inframe->given_timestamp);
                 } else {
-                  inform("Connection %d: Playback Started -- AirPlay 2 Buffered.",
-                         conn->connection_number);
+                  send_ssnc_metadata('styp', "Realtime", strlen("Realtime"), 1);
+                  if (config.statistics_requested)
+                    inform("Connection %d: Playback Started at frame %" PRId64 " -- AirPlay 2 Realtime.",
+                         conn->connection_number, inframe->given_timestamp);
                 }
+              } else {
+                send_ssnc_metadata('styp', "Buffered", strlen("Buffered"), 1);
+                if (config.statistics_requested)
+                  inform("Connection %d: Playback Started at frame %" PRId64 " -- AirPlay 2 Buffered.",
+                       conn->connection_number, inframe->given_timestamp);
+              }
 #else
-                inform("Connection %d: Playback Started -- AirPlay 1.", conn->connection_number);
+              send_ssnc_metadata('styp', "Classic", strlen("Classic"), 1);
+              if (config.statistics_requested)
+                inform("Connection %d: Playback Started at frame %" PRId64 " -- AirPlay 1.", conn->connection_number, inframe->given_timestamp);
 #endif
-              }
             }
             // not too sure if abs() is implemented for int64_t, so we'll do it manually
             int64_t abs_sync_error = sync_error;
index 86c01e83d25c79fb112f85153053b60849209b33..7083b99f579ae0e105ea2faa17161ce5eac4926a 100644 (file)
@@ -2539,6 +2539,13 @@ int main(int argc, char **argv) {
 
 #ifdef CONFIG_METADATA
   send_ssnc_metadata('svna', config.service_name, strlen(config.service_name), 1);
+  char buffer[256] = "";
+  snprintf(buffer, sizeof(buffer), "%d",
+           config.output_rate);
+  send_ssnc_metadata('ofps', buffer, strlen(buffer), 1);
+  snprintf(buffer, sizeof(buffer), "%s",
+           sps_format_description_string(config.output_format));  
+  send_ssnc_metadata('ofmt', buffer, strlen(buffer), 1);  
 #endif
 
   activity_monitor_start(); // not yet for AP2