]> git.ipfire.org Git - thirdparty/shairport-sync.git/commitdiff
Add metadata to MPRIS output -- not complete.
authorMike Brady <mikebrady@eircom.net>
Mon, 1 Jan 2018 23:04:18 +0000 (23:04 +0000)
committerMike Brady <mikebrady@eircom.net>
Mon, 1 Jan 2018 23:04:18 +0000 (23:04 +0000)
dacp.c
metadata_hub.c
metadata_hub.h
mpris-service.c

diff --git a/dacp.c b/dacp.c
index 8865bd49bf76714c9dd1a1c09e31104a1941a347..bd170b4dd1ba889bc72f9a1004d5578490c5946d 100644 (file)
--- a/dacp.c
+++ b/dacp.c
@@ -457,7 +457,8 @@ void *dacp_monitor_thread_code(void *na) {
               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 == NULL) ||
+                  (strncmp(metadata_store.genre, t, item_size) != 0)) {
                 if (metadata_store.genre)
                   free(metadata_store.genre);
                 metadata_store.genre = strndup(t, item_size);
@@ -469,8 +470,10 @@ void *dacp_monitor_thread_code(void *na) {
             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, sizeof(metadata_store.item_composite_id)) != 0) {
-                memcpy(metadata_store.item_composite_id, t, sizeof(metadata_store.item_composite_id));
+              if (memcmp(metadata_store.item_composite_id, t,
+                         sizeof(metadata_store.item_composite_id)) != 0) {
+                memcpy(metadata_store.item_composite_id, t,
+                       sizeof(metadata_store.item_composite_id));
 
                 char st[33];
                 char *pt = st;
@@ -485,6 +488,12 @@ void *dacp_monitor_thread_code(void *na) {
                 metadata_store.changed = 1;
               }
               break;
+            case 'astm':
+              t = sp - item_size;
+              r = ntohl(*(int32_t *)(t));
+              metadata_store.songtime_in_milliseconds = ntohl(*(uint32_t *)(t));
+              break;
+
             /*
                         case 'mstt':
                         case 'cant':
@@ -492,7 +501,6 @@ void *dacp_monitor_thread_code(void *na) {
                         case 'cmmk':
                         case 'caas':
                         case 'caar':
-                        case 'astm':
                           t = sp - item_size;
                           r = ntohl(*(int32_t *)(t));
                           printf("    %d", r);
@@ -534,7 +542,9 @@ void *dacp_monitor_thread_code(void *na) {
             }
             // printf("\n");
           }
+
           // now, if the metadata is changed, send a signal
+          run_metadata_watchers();
 
         } else {
           printf("Status Update not found.\n");
@@ -571,7 +581,7 @@ void *dacp_monitor_thread_code(void *na) {
       response = NULL;
     }
     */
-     sleep(2);
+    sleep(2);
   }
   debug(1, "DACP monitor thread exiting.");
   pthread_exit(NULL);
index 3dd1ed81cd925e8562eee073060d05e659574ccf..3f09fc5d08ce6f3908398c4984431ede2f9bf24d 100644 (file)
@@ -3,7 +3,7 @@
  * Basically, if you need to store metadata
  * (e.g. for use with the dbus interfaces),
  * then you need a metadata hub,
- * where everything is stored 
+ * where everything is stored
  * This file is part of Shairport Sync.
  * Copyright (c) Mike Brady 2017
  * All rights reserved.
@@ -38,3 +38,24 @@ void metadata_hub_init(void) {
   debug(1, "Metadata bundle initialisation.");
   memset(&metadata_store, 0, sizeof(metadata_store));
 }
+
+void add_metadata_watcher(metadata_watcher fn, void *userdata) {
+  int i;
+  for (i = 0; i < number_of_watchers; i++) {
+    if (metadata_store.watchers[i] == NULL) {
+      metadata_store.watchers[i] = fn;
+      metadata_store.watchers_data[i] = userdata;
+      debug(1, "Added a metadata watcher into slot %d", i);
+      break;
+    }
+  }
+}
+
+void run_metadata_watchers(void) {
+  int i;
+  for (i = 0; i < number_of_watchers; i++) {
+    if (metadata_store.watchers[i]) {
+      metadata_store.watchers[i](&metadata_store, metadata_store.watchers_data[i]);
+    }
+  }
+}
index 0d4e49985e5d7899fb1392e38dcf1476acef68a2..cb116f05479ef3852faef19e77386b347b623ff4 100644 (file)
@@ -3,6 +3,8 @@
 #include "config.h"
 #include <pthread.h>
 
+#define number_of_watchers 2
+
 enum play_status_type {
   PS_PLAYING = 0,
   PS_PAUSED,
@@ -20,6 +22,10 @@ enum repeat_status_type {
   RS_ALL,
 } repeat_status_type;
 
+struct metadata_bundle;
+
+typedef void (*metadata_watcher)(struct metadata_bundle *argc, void *userdata);
+
 typedef struct metadata_bundle {
   int changed;                          // normally 0, nonzero if a field has been changed
   int playerstatusupdates_are_received; // false if it's "traditional" metadata
@@ -48,11 +54,19 @@ typedef struct metadata_bundle {
   uint32_t item_id; // seems to be a track ID -- see itemid in DACP.c
   int item_id_changed;
 
+  uint32_t songtime_in_milliseconds;
+
   unsigned char
       item_composite_id[16]; // seems to be nowplaying 4 ids: dbid, plid, playlistItem, itemid
 
+  metadata_watcher watchers[number_of_watchers]; // functions to call if the metadata is changed.
+  void *watchers_data[number_of_watchers];       // their individual data
+
 } metadata_bundle;
 
 struct metadata_bundle metadata_store;
 
+void add_metadata_watcher(metadata_watcher fn, void *userdata);
+void run_metadata_watchers(void);
+
 void metadata_hub_init(void);
index 4b1a0cffd633f218e7a3599fdb01a42920b024fa..393de0d3b0ed037619beee371ef81ccc77c002b2 100644 (file)
 
 #include "dacp.h"
 
+#include "metadata_hub.h"
 #include "mpris-service.h"
 
+void mpris_metadata_watcher(struct metadata_bundle *argc, void *userdata) {
+  debug(1, "MPRIS metadata watcher called");
+  char response[100];
+  switch (argc->repeat_status) {
+  case RS_NONE:
+    strcpy(response, "None");
+    break;
+  case RS_SINGLE:
+    strcpy(response, "Track");
+    break;
+  case RS_ALL:
+    strcpy(response, "Playlist");
+    break;
+  }
+
+  // debug(1,"Set loop status to \"%s\"",response);
+  media_player2_player_set_loop_status(mprisPlayerPlayerSkeleton, response);
+
+  GVariantBuilder *dict_builder, *aa;
+  GVariant *trackname, *albumname, *trackid, *tracklength;
+
+  // 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;
+  for (it = 0; it < 16; it++) {
+    sprintf(pt, "%02X", argc->item_composite_id[it]);
+    pt += 2;
+  }
+  *pt = 0;
+  debug(1, "Item composite ID set to 0x%s.", st);
+
+  char trackidstring[1024];
+  sprintf(trackidstring, "/org/gnome/ShairportSync/%s", st);
+
+  trackid = g_variant_new("o", trackidstring);
+
+  // Make up the track name and album name
+  trackname = g_variant_new("s", argc->track_name);
+  albumname = g_variant_new("s", argc->album_name);
+
+  // Make up the track length in microseconds as an int64
+
+  uint64_t track_length_in_microseconds = argc->songtime_in_milliseconds;
+
+  track_length_in_microseconds *= 1000; // to micorseconds in 64-bit precision
+
+  // Make up the track name and album name
+  tracklength = g_variant_new("x", track_length_in_microseconds);
+
+  /* Build the artists array */
+  // debug(1,"Build artists");
+  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);
+
+  /* Build the genre array */
+  // 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);
+
+  /* Build the metadata array */
+  // debug(1,"Build metadata");
+  dict_builder = g_variant_builder_new(G_VARIANT_TYPE("a{sv}"));
+  g_variant_builder_add(dict_builder, "{sv}", "xesam:title", trackname);
+  g_variant_builder_add(dict_builder, "{sv}", "xesam:album", albumname);
+  g_variant_builder_add(dict_builder, "{sv}", "xesam:artist", artists);
+  g_variant_builder_add(dict_builder, "{sv}", "xesam:genre", genres);
+  g_variant_builder_add(dict_builder, "{sv}", "xesam:trackid", trackid);
+  g_variant_builder_add(dict_builder, "{sv}", "xesam:length", tracklength);
+  GVariant *dict = g_variant_builder_end(dict_builder);
+  g_variant_builder_unref(dict_builder);
+
+  media_player2_player_set_metadata(mprisPlayerPlayerSkeleton, dict);
+}
+
 static gboolean on_handle_next(MediaPlayer2Player *skeleton, GDBusMethodInvocation *invocation,
                                gpointer user_data) {
   send_simple_dacp_command("nextitem");
@@ -77,8 +159,8 @@ static void on_mpris_name_acquired(GDBusConnection *connection, const gchar *nam
   media_player2_set_supported_uri_schemes(mprisPlayerSkeleton, empty_string_array);
   media_player2_set_supported_mime_types(mprisPlayerSkeleton, empty_string_array);
 
-  media_player2_player_set_playback_status(mprisPlayerPlayerSkeleton, "stop");
-  media_player2_player_set_loop_status(mprisPlayerPlayerSkeleton, "off");
+  media_player2_player_set_playback_status(mprisPlayerPlayerSkeleton, "Stopped");
+  media_player2_player_set_loop_status(mprisPlayerPlayerSkeleton, "None");
   media_player2_player_set_volume(mprisPlayerPlayerSkeleton, 0.5);
   media_player2_player_set_minimum_rate(mprisPlayerPlayerSkeleton, 1.0);
   media_player2_player_set_maximum_rate(mprisPlayerPlayerSkeleton, 1.0);
@@ -98,6 +180,8 @@ static void on_mpris_name_acquired(GDBusConnection *connection, const gchar *nam
   g_signal_connect(mprisPlayerPlayerSkeleton, "handle-previous", G_CALLBACK(on_handle_previous),
                    NULL);
 
+  add_metadata_watcher(mpris_metadata_watcher, NULL);
+
   debug(1, "Shairport Sync D-BUS service started on interface \"%s\".", name);
 
   debug(1, "MPRIS service started on interface \"%s\".", name);