Index: configure.in
===================================================================
--- configure.in (revision 2245)
+++ configure.in (working copy)
@@ -598,6 +598,21 @@
fi
fi
+dnl mpris
+AC_ARG_WITH(mpris, AS_HELP_STRING(--without-mpris, Compile without MPRIS/D-Bus support.))
+if test "x$with_mpris" != "xno"
+then
+ PKG_CHECK_MODULES(DBus, [dbus-1 >= 1.2],
+ [COMPILE_MPRIS="yes"
+ AC_DEFINE([HAVE_MPRIS], 1, [Define if you want to have MPRIS/D-Bus support.])
+ EXTRA_LIBS="$EXTRA_LIBS -ldbus-1"
+ EXTRA_OBJS="$EXTRA_OBJS mpris.o"
+ CFLAGS="$CFLAGS `pkg-config dbus-1 --cflags`"],
+ [COMPILE_MPRIS="no"])
+else
+ COMPILE_MPRIS="no"
+fi
+
EXTRA_LIBS="$EXTRA_LIBS $CURSES_LIB"
AC_SUBST(EXTRA_LIBS)
@@ -631,6 +646,7 @@
echo "Network streams: "$COMPILE_CURL
echo "Resampling: "$COMPILE_SAMPLERATE
echo "MIME magic: "$COMPILE_MAGIC
+echo "MPRIS/D-Bus: "$COMPILE_MPRIS
echo "----------------------------------------------------------------"
echo
Index: mpris_introspection.h
===================================================================
--- mpris_introspection.h (revision 0)
+++ mpris_introspection.h (revision 0)
@@ -0,0 +1,111 @@
+#ifndef MPRIS_INTROSPECTION_H
+#define MPRIS_INTROSPECTION_H
+
+static char *root_introspection =
+"\n"
+"\n"
+" \n"
+" \n"
+" \n"
+" \n"
+" \n"
+" \n"
+" \n"
+" \n"
+" \n"
+" \n"
+" \n"
+" \n"
+""
+;
+
+static char *tracklist_introspection =
+"\n"
+"\n"
+" \n"
+" \n"
+" \n"
+" \n"
+" \n"
+" \n"
+" \n"
+" \n"
+" \n"
+" \n"
+" \n"
+" \n"
+" \n"
+" \n"
+" \n"
+" \n"
+" \n"
+" \n"
+" \n"
+" \n"
+" \n"
+" \n"
+" \n"
+" \n"
+" \n"
+" \n"
+" \n"
+" \n"
+" \n"
+""
+;
+
+static char *player_introspection =
+"\n"
+"\n"
+" \n"
+" \n"
+" \n"
+" \n"
+" \n"
+" \n"
+" \n"
+" \n"
+" \n"
+" \n"
+" \n"
+" \n"
+" \n"
+" \n"
+" \n"
+" \n"
+" \n"
+" \n"
+" \n"
+" \n"
+" \n"
+" \n"
+" \n"
+" \n"
+" \n"
+" \n"
+" \n"
+" \n"
+" \n"
+" \n"
+" \n"
+" \n"
+" \n"
+" \n"
+" \n"
+" \n"
+" \n"
+" \n"
+" \n"
+" \n"
+" \n"
+" \n"
+" \n"
+" \n"
+" \n"
+""
+;
+
+#endif
Index: mpris.c
===================================================================
--- mpris.c (revision 0)
+++ mpris.c (revision 0)
@@ -0,0 +1,556 @@
+/*
+ * MOC - music on console
+ * Copyright (C) 2004-2010 Damian Pietras
+ *
+ * MPRIS (Media Player Remote Interfacing Specification) 1.0 implementation.
+ * This is a D-Bus interface to MOC.
+ * All processing is done in a separate server thread.
+ *
+ * TODO:
+ * Almost no error-checking during message processing will be coded until it is
+ * declared necessary. Errors are said to only emerge when there is
+ * not enough memory. In that case the thread will stop looping anyway (I hope).
+ *
+ * Author: Ondřej Svoboda
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include
+#include
+
+#include "audio.h"
+#include "common.h"
+#include "files.h"
+#include "log.h"
+#include "mpris.h"
+#include "mpris_introspection.h"
+#include "options.h"
+#include "player.h"
+#include "playlist.h"
+#include "protocol.h"
+#include "server.h"
+#include "tags_cache.h"
+
+static DBusConnection *dbus_conn; // Connection handle.
+static DBusError dbus_err; // Error flag.
+static DBusMessage *msg; // An incoming message.
+static DBusMessageIter args_in, args_out; // Iterators for in- or outcoming messages' content.
+static DBusMessageIter array, dict, variant; // Iterators for containers only used in outgoing messages.
+
+/* Shared with audio.c */
+/* Track number in the current playlist.
+ * It is not what we want when the playlist is shuffled.
+ * TODO: Get the right track number. */
+extern int curr_playing;
+extern struct plist *curr_plist; // Self-explanatory.
+extern pthread_mutex_t curr_playing_mut;
+extern pthread_mutex_t plist_mut;
+
+/* Shared with server.c */
+/* TODO: Do we need mutex for these? */
+extern int server_quit;
+extern struct tags_cache tags_cache;
+
+/* Flags to be changed by the server + their mutex. */
+static int mpris_track_changed = 0;
+static int mpris_status_changed = 0;
+static int mpris_caps_changed = 0;
+static int mpris_tracklist_changed = 0;
+static pthread_mutex_t mpris_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+/* Connection to D-Bus happens here. If it fails, for any reason, we just move
+ * on as the MPRIS/D-Bus feature is not crucial for the server.
+ * Later, it could be possible to rerun the initialization and the thread
+ * on-demand or automatically after a period of time. */
+void mpris_init()
+{
+ dbus_error_init(&dbus_err);
+
+ dbus_conn = dbus_bus_get(DBUS_BUS_SESSION, &dbus_err);
+ if (dbus_error_is_set(&dbus_err)) {
+ logit("Error while connecting to D-Bus: %s", dbus_err.message);
+ dbus_error_free(&dbus_err);
+ return;
+ }
+
+ if (dbus_conn == NULL) {
+ logit("Connection to D-Bus not established.");
+ return;
+ }
+
+ int ret = dbus_bus_request_name(dbus_conn, MPRIS_BUS_NAME,
+ DBUS_NAME_FLAG_DO_NOT_QUEUE, &dbus_err);
+ if (dbus_error_is_set(&dbus_err)) {
+ logit("Error while requesting a bus name: %s", dbus_err.message);
+ dbus_error_free(&dbus_err);
+ return;
+ }
+
+ if (ret != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
+ logit("Could not become the primary owner of the bus name.");
+ return;
+ }
+
+ logit("Everything went fine when connecting to D-Bus.");
+}
+
+/* Low-level wrappers for sending message arguments with possible error checking.
+ * I know, this stuff would not be needed if I used a proper D-Bus binding. */
+
+static void msg_add_string(char **value)
+{
+ if (!dbus_message_iter_append_basic(&args_out, DBUS_TYPE_STRING, value)) {
+ logit("Out of memory!");
+ /* Error handling would go here. */
+ }
+}
+
+static void msg_add_int32(int *value)
+{
+ dbus_message_iter_append_basic(&args_out, DBUS_TYPE_INT32, value);
+}
+
+static void msg_add_uint16(unsigned short int *value)
+{
+ dbus_message_iter_append_basic(&args_out, DBUS_TYPE_UINT16, value);
+}
+
+/* Helper functions for sending bigger chunks of data or with some added logic. */
+
+static void mpris_send_tracklist_length()
+{
+ LOCK(plist_mut);
+ int length = curr_plist != NULL ? plist_count(curr_plist) : 0;
+ UNLOCK(plist_mut);
+ msg_add_int32(&length);
+}
+
+static void mpris_send_metadata_string_field(char **key, char **value)
+{
+ dbus_message_iter_open_container(&array, DBUS_TYPE_DICT_ENTRY, 0, &dict);
+ dbus_message_iter_append_basic(&dict, DBUS_TYPE_STRING, key);
+ dbus_message_iter_open_container(&dict, DBUS_TYPE_VARIANT, "s", &variant);
+ dbus_message_iter_append_basic(&variant, DBUS_TYPE_STRING, value);
+ dbus_message_iter_close_container(&dict, &variant);
+ dbus_message_iter_close_container(&array, &dict);
+}
+
+// If tags are missing, at least a title made from file name should be returned!
+static void mpris_send_metadata(int item)
+{
+ char *location_key = "location";
+ char *title_key = "title";
+ char *artist_key = "artist";
+ char *location_val = NULL;
+ char *file = NULL;
+ struct file_tags *tags;
+
+ if (item >= 0) {
+ LOCK(plist_mut);
+ file = plist_get_file(curr_plist, item);
+ UNLOCK(plist_mut);
+ }
+ if (file == NULL) {
+ file = xstrdup("");
+ tags = tags_new();
+ } else {
+ tags = tags_cache_get_immediate(&tags_cache, file, TAGS_COMMENTS | TAGS_TIME);
+ }
+
+ if (!is_url(file)) {
+ location_val = (char *)xmalloc(sizeof(char) * (7 + strlen(file) + 1));
+ strcpy(location_val, "file://");
+ strcat(location_val, file);
+ } else {
+ location_val = xstrdup(file);
+ }
+ free(file);
+
+ dbus_message_iter_open_container(&args_out, DBUS_TYPE_ARRAY, "{sv}", &array);
+ mpris_send_metadata_string_field(&location_key, &location_val);
+ if (tags->title != NULL)
+ mpris_send_metadata_string_field(&title_key, &tags->title);
+ if (tags->artist != NULL)
+ mpris_send_metadata_string_field(&artist_key, &tags->artist);
+
+ dbus_message_iter_close_container(&args_out, &array);
+
+ free(location_val);
+ tags_free(tags);
+}
+
+static void mpris_send_status()
+{
+ DBusMessageIter structure;
+ int state, shuffle, repeat_current, next, repeat;
+
+ switch (audio_get_state()) {
+ case STATE_PLAY:
+ state = 0;
+ break;
+ case STATE_PAUSE:
+ state = 1;
+ break;
+ case STATE_STOP:
+ state = 2;
+ }
+
+ shuffle = options_get_int("Shuffle");
+ repeat = options_get_int("Repeat");
+ next = options_get_int("AutoNext");
+ repeat_current = !next && repeat;
+
+ /* I saw a nice usage of a C++ << operator for appending values to the message. */
+ dbus_message_iter_open_container(&args_out, DBUS_TYPE_STRUCT, 0, &structure);
+ dbus_message_iter_append_basic(&structure, DBUS_TYPE_INT32, &state);
+ dbus_message_iter_append_basic(&structure, DBUS_TYPE_INT32, &shuffle);
+ dbus_message_iter_append_basic(&structure, DBUS_TYPE_INT32, &repeat_current);
+ dbus_message_iter_append_basic(&structure, DBUS_TYPE_INT32, &repeat);
+ dbus_message_iter_close_container(&args_out, &structure);
+}
+
+// Not really implemented yet!
+static void mpris_send_caps()
+{
+ int caps = 0
+ | CAN_HAS_TRACKLIST // Yes, MOC always has a playlist, be it an actual playlist or a directory.
+ ;
+
+ msg_add_int32(&caps);
+}
+
+/* Functions for sending signals. */
+
+static void mpris_tracklist_change_signal()
+{
+ msg = dbus_message_new_signal("/TrackList", MPRIS_IFACE, "TrackListChange");
+ dbus_message_iter_init_append(msg, &args_out);
+
+ mpris_send_tracklist_length();
+
+ dbus_connection_send(dbus_conn, msg, NULL);
+ dbus_connection_flush(dbus_conn);
+
+ dbus_message_unref(msg);
+}
+
+static void mpris_track_change_signal()
+{
+ msg = dbus_message_new_signal("/Player", MPRIS_IFACE, "TrackChange");
+ dbus_message_iter_init_append(msg, &args_out);
+
+ LOCK(curr_playing_mut);
+ int curr = curr_playing;
+ UNLOCK(curr_playing_mut);
+ mpris_send_metadata(curr);
+
+ dbus_connection_send(dbus_conn, msg, NULL);
+ dbus_connection_flush(dbus_conn);
+
+ dbus_message_unref(msg);
+}
+
+static void mpris_status_change_signal()
+{
+ msg = dbus_message_new_signal("/Player", MPRIS_IFACE, "StatusChange");
+ dbus_message_iter_init_append(msg, &args_out);
+
+ mpris_send_status();
+
+ dbus_connection_send(dbus_conn, msg, NULL);
+ dbus_connection_flush(dbus_conn);
+
+ dbus_message_unref(msg);
+}
+
+static void mpris_caps_change_signal()
+{
+ msg = dbus_message_new_signal("/Player", MPRIS_IFACE, "CapsChange");
+ dbus_message_iter_init_append(msg, &args_out);
+
+ mpris_send_caps();
+
+ dbus_connection_send(dbus_conn, msg, NULL);
+ dbus_connection_flush(dbus_conn);
+
+ dbus_message_unref(msg);
+}
+
+/* Argument checking for incoming messages. */
+
+static int mpris_arg_bool()
+{
+ return dbus_message_iter_init(msg, &args_in) &&
+ dbus_message_iter_get_arg_type(&args_in) == DBUS_TYPE_BOOLEAN;
+}
+
+static int mpris_arg_int32()
+{
+ return dbus_message_iter_init(msg, &args_in) &&
+ dbus_message_iter_get_arg_type(&args_in) == DBUS_TYPE_INT32;
+}
+
+/* Implementation of D-Bus methods (incomplete, see comments). */
+
+static void mpris_root_methods()
+{
+ if (dbus_message_is_method_call(msg, INTROSPECTION_IFACE, "Introspect")) {
+ msg_add_string(&root_introspection);
+ } else if (dbus_message_is_method_call(msg, MPRIS_IFACE, "Quit")) {
+ server_quit = 1; // Why care about using mutex?
+ } else if (dbus_message_is_method_call(msg, MPRIS_IFACE, "Identity")) {
+ char *identity = xstrdup(PACKAGE_STRING);
+ msg_add_string(&identity);
+ free(identity);
+ } else if (dbus_message_is_method_call(msg, MPRIS_IFACE, "MprisVersion")) {
+ short unsigned int version_major = 1, version_minor = 0;
+ msg_add_uint16(&version_major);
+ msg_add_uint16(&version_minor);
+ }
+}
+
+static void mpris_tracklist_methods()
+{
+ if (dbus_message_is_method_call(msg, INTROSPECTION_IFACE, "Introspect")) {
+ msg_add_string(&tracklist_introspection);
+ } else if (dbus_message_is_method_call(msg, MPRIS_IFACE, "GetMetadata")) {
+ if (mpris_arg_int32()) {
+ int item;
+ dbus_message_iter_get_basic(&args_in, &item);
+ mpris_send_metadata(item);
+ }
+ } else if (dbus_message_is_method_call(msg, MPRIS_IFACE, "GetCurrentTrack")) {
+ /* In curr_playing there's not a number we always want. */
+ LOCK(curr_playing_mut);
+ int curr = curr_playing != -1 ? curr_playing : 0;
+ UNLOCK(curr_playing_mut);
+ msg_add_int32(&curr);
+ } else if (dbus_message_is_method_call(msg, MPRIS_IFACE, "GetLength")) {
+ mpris_send_tracklist_length();
+ } else if (dbus_message_is_method_call(msg, MPRIS_IFACE, "AddTrack")) {
+ /*
+ * Not implemented yet!
+ * There are some issues to handle:
+ * - every client can have its own playlist, which one to add a file to then?
+ * - to add a file we seem to need to duplicate a few functions, such as
+ * add_file_plist(), play_from_url(), add_url_to_plist()
+ * from interface.c and code chunks processing the CMD_CLI_PLIST_ADD command
+ * in server.c
+ */
+ } else if (dbus_message_is_method_call(msg, MPRIS_IFACE, "DelTrack")) {
+ // Not implemented yet for the same reason as for AddTrack.
+ } else if (dbus_message_is_method_call(msg, MPRIS_IFACE, "SetLoop")) {
+ if (mpris_arg_bool()) {
+ int loop_plist;
+ dbus_message_iter_get_basic(&args_in, &loop_plist);
+ option_set_int("Repeat", loop_plist);
+ // We are asked to loop the playlist, so make it play more than one file.
+ if (loop_plist) option_set_int("AutoNext", 1);
+ add_event_all(EV_OPTIONS, NULL);
+ }
+ } else if (dbus_message_is_method_call(msg, MPRIS_IFACE, "SetRandom")) {
+ if (mpris_arg_bool()) {
+ int random;
+ dbus_message_iter_get_basic(&args_in, &random);
+ option_set_int("Shuffle", random);
+ add_event_all(EV_OPTIONS, NULL);
+ }
+ }
+}
+
+static void mpris_player_methods()
+{
+ if (dbus_message_is_method_call(msg, INTROSPECTION_IFACE, "Introspect")) {
+ msg_add_string(&player_introspection);
+ } else if (dbus_message_is_method_call(msg, MPRIS_IFACE, "Next")) {
+ audio_next();
+ } else if (dbus_message_is_method_call(msg, MPRIS_IFACE, "Prev")) {
+ audio_prev();
+ } else if (dbus_message_is_method_call(msg, MPRIS_IFACE, "Pause")) {
+ switch (audio_get_state()) {
+ case STATE_PAUSE:
+ audio_unpause();
+ break;
+ case STATE_PLAY:
+ audio_pause();
+ }
+ } else if (dbus_message_is_method_call(msg, MPRIS_IFACE, "Stop")) {
+ audio_stop();
+ } else if (dbus_message_is_method_call(msg, MPRIS_IFACE, "Play")) {
+ // This does not make MOC play a song if it hasn't played one yet!
+ char *file;
+ switch (audio_get_state()) {
+ case STATE_PAUSE:
+ audio_unpause();
+ break;
+ case STATE_PLAY:
+ LOCK(plist_mut);
+ LOCK(curr_playing_mut);
+ if (curr_plist != NULL && curr_playing != -1) {
+ file = plist_get_file (curr_plist, curr_playing);
+ UNLOCK(plist_mut);
+ UNLOCK(curr_playing_mut);
+ audio_play(file);
+ free(file);
+ } else {
+ UNLOCK(plist_mut);
+ UNLOCK(curr_playing_mut);
+ }
+ break;
+ case STATE_STOP:
+ audio_play("");
+ }
+ } else if (dbus_message_is_method_call(msg, MPRIS_IFACE, "Repeat")) {
+ if (mpris_arg_bool()) {
+ int repeat_current;
+ dbus_message_iter_get_basic(&args_in, &repeat_current);
+ option_set_int("Repeat", repeat_current);
+ option_set_int("AutoNext", !repeat_current);
+ add_event_all(EV_OPTIONS, NULL);
+ }
+ } else if (dbus_message_is_method_call(msg, MPRIS_IFACE, "GetStatus")) {
+ mpris_send_status();
+ } else if (dbus_message_is_method_call(msg, MPRIS_IFACE, "GetMetadata")) {
+ LOCK(curr_playing_mut);
+ int curr = curr_playing;
+ UNLOCK(curr_playing_mut);
+ mpris_send_metadata(curr);
+ } else if (dbus_message_is_method_call(msg, MPRIS_IFACE, "GetCaps")) {
+ mpris_send_caps();
+ } else if (dbus_message_is_method_call(msg, MPRIS_IFACE, "VolumeSet")) {
+ if (mpris_arg_int32()) {
+ int volume;
+ dbus_message_iter_get_basic(&args_in, &volume);
+ // TODO: Check the parameter first!
+ audio_set_mixer(volume);
+ }
+ } else if (dbus_message_is_method_call(msg, MPRIS_IFACE, "VolumeGet")) {
+ int volume = audio_get_mixer();
+ msg_add_int32(&volume);
+ } else if (dbus_message_is_method_call(msg, MPRIS_IFACE, "PositionSet")) {
+ // Should support milisecond precision too
+ if (mpris_arg_int32()) {
+ int pos;
+ dbus_message_iter_get_basic(&args_in, &pos);
+ // TODO: Do we need to check the value for correctness?
+ audio_jump_to(pos / 1000);
+ }
+ } else if (dbus_message_is_method_call(msg, MPRIS_IFACE, "PositionGet")) {
+ int pos = audio_get_milisec();
+ msg_add_int32(&pos);
+ }
+}
+
+/* A server thread where all D-Bus messages are received and signals are sent. */
+void *mpris_thread(void *unused ATTR_UNUSED)
+{
+ DBusMessage *reply;
+ const char *path; // The path an incoming message has been sent to.
+
+ /* If no D-Bus connection has been established we have nothing to do. */
+ if (dbus_conn == NULL) return NULL;
+
+ logit("Starting the MPRIS thread.");
+
+ /* Wait for an incoming message for a max. of MPRIS_TIMEOUT (50 ms). */
+ while (dbus_connection_read_write(dbus_conn, MPRIS_TIMEOUT)) {
+ if (server_quit) {
+ logit("Stopping the MPRIS thread due to server exit.");
+ return NULL;
+ }
+
+ /* Signals are sent if needed. */
+ LOCK(mpris_mutex);
+ if (mpris_tracklist_changed) {
+ mpris_tracklist_change_signal();
+ mpris_tracklist_changed = 0;
+ }
+ if (mpris_track_changed) {
+ mpris_track_change_signal();
+ mpris_track_changed = 0;
+ }
+ if (mpris_caps_changed) {
+ mpris_caps_change_signal();
+ mpris_caps_changed = 0;
+ }
+ if (mpris_status_changed) {
+ mpris_status_change_signal();
+ mpris_status_changed = 0;
+ }
+ UNLOCK(mpris_mutex);
+
+ /* Fetch an incoming message. */
+ msg = dbus_connection_pop_message(dbus_conn);
+ if (msg == NULL) continue;
+
+ /* Respond to all messages. They say in the specification some messages
+ * are sometimes no-ops, so this is the place to start moving code. */
+
+ reply = dbus_message_new_method_return(msg);
+ dbus_message_iter_init_append(reply, &args_out);
+
+ /* Process the incoming message and prepare a response. */
+ if ((path = dbus_message_get_path(msg)) != NULL) {
+ if (!strcmp("/", path))
+ mpris_root_methods();
+ else if (!strcmp("/Player", path))
+ mpris_player_methods();
+ else if (!strcmp("/TrackList", path))
+ mpris_tracklist_methods();
+ }
+
+ /* Send the response and clean up. */
+ dbus_connection_send(dbus_conn, reply, NULL);
+ dbus_message_unref(msg);
+ dbus_connection_flush(dbus_conn);
+ }
+
+ logit("Stopping MPRIS thread due to a loss of communication with D-Bus.");
+ return NULL;
+}
+
+/* Hooks in the core/server. */
+
+// Not used yet.
+void mpris_tracklist_change()
+{
+ LOCK(mpris_mutex);
+ mpris_tracklist_changed = 1;
+ UNLOCK(mpris_mutex);
+}
+
+void mpris_track_change()
+{
+ LOCK(mpris_mutex);
+ mpris_track_changed = 1;
+ UNLOCK(mpris_mutex);
+}
+
+void mpris_status_change()
+{
+ LOCK(mpris_mutex);
+ mpris_status_changed = 1;
+ UNLOCK(mpris_mutex);
+}
+
+// Not used yet.
+void mpris_caps_change()
+{
+ LOCK(mpris_mutex);
+ mpris_caps_changed = 1;
+ UNLOCK(mpris_mutex);
+}
+
+inline void mpris_exit()
+{
+ pthread_mutex_destroy(&mpris_mutex);
+}
Index: mpris.h
===================================================================
--- mpris.h (revision 0)
+++ mpris.h (revision 0)
@@ -0,0 +1,27 @@
+#ifndef MPRIS_H
+#define MPRIS_H
+
+#define MPRIS_TIMEOUT 50
+#define MPRIS_BUS_NAME "org.mpris.moc"
+#define MPRIS_IFACE "org.freedesktop.MediaPlayer"
+#define INTROSPECTION_IFACE "org.freedesktop.DBus.Introspectable"
+
+#define CAN_GO_NEXT 1 << 0
+#define CAN_GO_PREV 1 << 1
+#define CAN_PAUSE 1 << 2
+#define CAN_PLAY 1 << 3
+#define CAN_SEEK 1 << 4
+#define CAN_PROVIDE_METADATA 1 << 5
+#define CAN_HAS_TRACKLIST 1 << 6
+
+#include
+
+void mpris_init();
+void *mpris_thread(void *unused ATTR_UNUSED);
+inline void mpris_exit();
+void mpris_track_change();
+void mpris_status_change();
+void mpris_caps_change();
+void mpris_tracklist_change();
+
+#endif
Index: audio.c
===================================================================
--- audio.c (revision 2245)
+++ audio.c (working copy)
@@ -61,7 +61,7 @@
static int play_thread_running = 0;
/* currentlu played file */
-static int curr_playing = -1;
+int curr_playing = -1;
/* file we played before playing songs from queue */
static char *before_queue_fname = NULL;
static char *curr_playing_fname = NULL;
@@ -69,7 +69,7 @@
* so we know that when the queue is empty, we should play the regular
* playlist from the beginning */
static int started_playing_in_queue = 0;
-static pthread_mutex_t curr_playing_mut = PTHREAD_MUTEX_INITIALIZER;
+pthread_mutex_t curr_playing_mut = PTHREAD_MUTEX_INITIALIZER;
static struct out_buf out_buf;
static struct hw_funcs hw;
@@ -89,8 +89,8 @@
static struct plist playlist;
static struct plist shuffled_plist;
static struct plist queue;
-static struct plist *curr_plist; /* currently used playlist */
-static pthread_mutex_t plist_mut = PTHREAD_MUTEX_INITIALIZER;
+struct plist *curr_plist; /* currently used playlist */
+pthread_mutex_t plist_mut = PTHREAD_MUTEX_INITIALIZER;
/* Is the audio deice opened? */
static int audio_opened = 0;
@@ -438,7 +438,7 @@
free (curr_playing_fname);
curr_playing_fname = xstrdup (file);
- out_buf_time_set (&out_buf, 0.0);
+ out_buf_time_set (&out_buf, 0);
next = plist_next (curr_plist, curr_playing);
next_file = next != -1
@@ -453,7 +453,7 @@
set_info_rate (0);
set_info_bitrate (0);
set_info_channels (1);
- out_buf_time_set (&out_buf, 0.0);
+ out_buf_time_set (&out_buf, 0);
free (file);
}
@@ -847,10 +847,16 @@
return played;
}
+/* Get current time of the song in miliseconds. */
+int audio_get_milisec ()
+{
+ return state != STATE_STOP ? out_buf_time_get (&out_buf) : 0;
+}
+
/* Get current time of the song in seconds. */
int audio_get_time ()
{
- return state != STATE_STOP ? out_buf_time_get (&out_buf) : 0;
+ return state != STATE_STOP ? out_buf_time_get (&out_buf) / 1000 : 0;
}
void audio_close ()
Index: out_buf.c
===================================================================
--- out_buf.c (revision 2245)
+++ out_buf.c (working copy)
@@ -35,7 +35,7 @@
/* Don't play more than that value (in seconds) in one audio_play().
* This prevent locking. */
-#define AUDIO_MAX_PLAY 0.1
+#define AUDIO_MAX_PLAY 0.05
#define AUDIO_MAX_PLAY_BYTES 32768
/*static int fd;*/
@@ -165,7 +165,7 @@
/* Update time */
if (played && audio_get_bps())
- buf->time += played / (float)audio_get_bps();
+ buf->time += played * 1000 / audio_get_bps();
buf->hardware_buf_fill = audio_get_buf_fill();
}
}
@@ -187,7 +187,7 @@
buf->exit = 0;
buf->pause = 0;
buf->stop = 0;
- buf->time = 0.0;
+ buf->time = 0;
buf->reset_dev = 0;
buf->hardware_buf_fill = 0;
buf->read_thread_waiting = 0;
@@ -328,10 +328,10 @@
UNLOCK (buf->mutex);
}
-void out_buf_time_set (struct out_buf *buf, const float time)
+void out_buf_time_set (struct out_buf *buf, const int time)
{
LOCK (buf->mutex);
- buf->time = time;
+ buf->time = time * 1000;
UNLOCK (buf->mutex);
}
@@ -341,10 +341,11 @@
int bps = audio_get_bps ();
LOCK (buf->mutex);
- time = buf->time - (bps ? buf->hardware_buf_fill / (float)bps : 0);
+ time = buf->time - (bps ? buf->hardware_buf_fill * 1000 / bps : 0);
UNLOCK (buf->mutex);
- assert (time >= 0);
+ if (time < 0)
+ time = 0;
return time;
}
Index: audio.h
===================================================================
--- audio.h (revision 2245)
+++ audio.h (working copy)
@@ -238,6 +238,7 @@
int audio_get_bps ();
int audio_get_buf_fill ();
void audio_close ();
+int audio_get_milisec ();
int audio_get_time ();
int audio_get_state ();
void audio_plist_add (const char *file);
Index: server.c
===================================================================
--- server.c (revision 2245)
+++ server.c (working copy)
@@ -48,6 +48,9 @@
#include "files.h"
#include "softmixer.h"
#include "equalizer.h"
+#ifdef HAVE_MPRIS
+# include "mpris.h"
+#endif
#define SERVER_LOG "mocp_server_log"
#define PID_FILE "pid"
@@ -69,11 +72,16 @@
/* Thread ID of the server thread. */
static pthread_t server_tid;
+/* Thread ID of the MPRIS thread. */
+#ifdef HAVE_MPRIS
+static pthread_t mpris_tid;
+#endif
+
/* Pipe used to wake up the server from select() from another thread. */
static int wake_up_pipe[2];
/* Set to 1 when a signal arrived causing the program to exit. */
-static volatile int server_quit = 0;
+volatile int server_quit = 0;
static char err_msg[265] = "";
@@ -90,7 +98,7 @@
-1
};
-static struct tags_cache tags_cache;
+struct tags_cache tags_cache;
extern char **environ;
@@ -335,6 +343,10 @@
tags_cache_init (&tags_cache, options_get_int("TagsCacheSize"));
tags_cache_load (&tags_cache, create_file_name("cache"));
clients_init ();
+ #ifdef HAVE_MPRIS
+ mpris_init ();
+ pthread_create (&mpris_tid, NULL, mpris_thread, NULL);
+ #endif
server_tid = pthread_self ();
thread_signal (SIGTERM, sig_exit);
@@ -400,8 +412,11 @@
curr_file = audio_get_sname();
command = xstrdup(options_get_str("OnSongChange"));
- if((command)&&(curr_file) &&
- ((!last_file) || (strcmp(last_file,curr_file)))) {
+ if (command && curr_file && (!last_file || strcmp(last_file, curr_file))) {
+ #ifdef HAVE_MPRIS
+ mpris_track_change ();
+ #endif
+
while(command[i] != 0) {
if((command[i] == '/')&&(argc == 2))
bin = &command[i] + 1;
@@ -513,7 +528,7 @@
}
}
-static void add_event_all (const int event, const void *data)
+void add_event_all (const int event, const void *data)
{
int i;
int added = 0;
@@ -599,9 +614,11 @@
/* End playing and cleanup. */
static void server_shutdown ()
{
-
logit ("Server exiting...");
audio_exit ();
+ #ifdef HAVE_MPRIS
+ mpris_exit ();
+ #endif
tags_cache_save (&tags_cache, create_file_name("tags_cache"));
tags_cache_destroy (&tags_cache);
unlink (socket_name());
@@ -787,6 +804,9 @@
}
option_set_int (name, val);
+ #ifdef HAVE_MPRIS
+ mpris_status_change ();
+ #endif
free (name);
add_event_all (EV_OPTIONS, NULL);
@@ -1685,6 +1705,9 @@
close_clients ();
clients_cleanup ();
+ #ifdef HAVE_MPRIS
+ pthread_join (mpris_tid, NULL);
+ #endif
close (list_sock);
server_shutdown ();
}
@@ -1716,6 +1739,9 @@
/* Notify the client about change of the player state. */
void state_change ()
{
+ #ifdef HAVE_MPRIS
+ mpris_status_change ();
+ #endif
add_event_all (EV_STATE, NULL);
}
Index: out_buf.h
===================================================================
--- out_buf.h (revision 2245)
+++ out_buf.h (working copy)
@@ -32,7 +32,7 @@
int reset_dev; /* request to the reading thread to reset the audio
device */
- float time; /* Time of played sound .*/
+ int time; /* Time of played sound. */
int hardware_buf_fill; /* How the sound card buffer is filled */
int read_thread_waiting; /* is the read thread waiting for data? */
@@ -45,7 +45,7 @@
void out_buf_unpause (struct out_buf *buf);
void out_buf_stop (struct out_buf *buf);
void out_buf_reset (struct out_buf *buf);
-void out_buf_time_set (struct out_buf *buf, const float time);
+void out_buf_time_set (struct out_buf *buf, const int time);
int out_buf_time_get (struct out_buf *buf);
void out_buf_set_free_callback (struct out_buf *buf,
out_buf_free_callback callback);
Index: server.h
===================================================================
--- server.h (revision 2245)
+++ server.h (working copy)
@@ -25,6 +25,7 @@
void ev_audio_start ();
void ev_audio_stop ();
void server_queue_pop (const char *filename);
+void add_event_all (const int event, const void *data);
#ifdef __cplusplus
}
Index: main.c
===================================================================
--- main.c (revision 2245)
+++ main.c (working copy)
@@ -245,6 +245,9 @@
#ifdef HAVE_SAMPLERATE
printf (" resample");
#endif
+#ifdef HAVE_MPRIS
+ printf (" MPRIS/D-Bus");
+#endif
putchar ('\n');