2
* Copyright (c) Linux community.
4
* This library is free software; you can redistribute it and/or
5
* modify it under the terms of the GNU Library General Public
6
* License as published by the Free Software Foundation; either
7
* version 2 of the License, or (at your option) any later version.
9
* This library is distributed in the hope that it will be useful,
10
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
* Library General Public License for more details.
14
* You should have received a copy of the GNU Library General Public
15
* License along with this library; if not, write to the
16
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17
* Boston, MA 02111-1307, USA.
22
#include <dbus/dbus.h>
23
#include <dbus/dbus-glib.h>
26
#include "dbus-player.h"
27
#include "dbus-mpris.h"
28
#include "dbus-rhythmbox.h"
29
#include "dbus-banshee.h"
31
// Code for Media Players that strictly follow the MPRIS DBus standard.
32
// Ref.: http://xmms2.org/wiki/MPRIS
34
// This code is largely based on Pidgin-musictracker's code, mpris.c module.
35
// Copyrights and thanks to http://code.google.com/p/pidgin-musictracker/
37
#define DBUS_MPRIS_PLAYER "org.freedesktop.MediaPlayer"
38
#define DBUS_MPRIS_ROOT_PATH "/"
39
#define DBUS_MPRIS_PLAYER_PATH "/Player"
40
#define DBUS_MPRIS_TRACK_SIGNAL "TrackChange"
41
#define DBUS_MPRIS_STATUS_SIGNAL "StatusChange"
43
// Value types for DBus messages
44
#define DBUS_TYPE_G_STRING_VALUE_HASHTABLE (dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, G_TYPE_VALUE))
45
#define DBUS_TYPE_MPRIS_STATUS (dbus_g_type_get_struct ("GValueArray", G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, G_TYPE_INVALID))
47
void mpris_status_signal_int_cb(DBusGProxy *player_proxy, gpointer data, MediaPlayerRec *player);
48
void mpris_status_signal_struct_cb(DBusGProxy *player_proxy, gpointer data, MediaPlayerRec *player);
50
void mpris_module_init() {
51
LOG_DEBUG("Init dbus_mpris.c.\n");
54
void mpris_module_exit() {
55
LOG_DEBUG("Clean up dbus_mpris.c.\n");
58
void mpris_player_track_changed(MediaPlayerRec *player) {
59
// Called when the song/audio track changes
61
// Re-read all player data
62
mpris_get_info(player);
64
// Set track changed signal
65
TrackInfo *tr = &player->track;
66
tr->status = PLAYER_STATUS_TRACK_CHANGE;
69
// dbus_player_debug_print(player);
72
dbus_player_process_data(player);
76
void mpris_player_status_changed(MediaPlayerRec *player) {
77
// Called when player's status changes
79
// Get player information
80
mpris_get_info(player);
83
// dbus_player_debug_print(player);
86
dbus_player_process_data(player);
89
gboolean mpris_service_is_running(gpointer player_rec) {
90
// Check if media player app is is running/is active
91
MediaPlayerRec *player = (MediaPlayerRec*)player_rec;
92
return mpris_service_is_running_by_name(player->service_name);
95
gboolean mpris_service_is_running_by_name(const char *service_name) {
96
// Check if given service (media player application) is running/is active
97
// ref.: http://www.koders.com/c/fid8FD8DF8B9ED4F45A30476317D5A3261237576BAA.aspx?s=%22Christian+Mueller%22
99
DBusGProxy *proxy = NULL;
100
GError *error = NULL;
102
DBusGConnection *dbus_conn = dbus_player_connect_to_dbus();
104
proxy = dbus_g_proxy_new_for_name(dbus_conn,
105
DBUS_SERVICE_DBUS, // "org.freedesktop.DBus"
106
DBUS_PATH_DBUS, // "org/freedesktop/DBus"
107
DBUS_INTERFACE_DBUS);// "org.freedesktop.DBus"
112
gboolean running = FALSE;
114
if (dbus_g_proxy_call_with_timeout(proxy, "NameHasOwner", DBUS_MPRIS_TIMEOUT, &error,
115
G_TYPE_STRING, service_name,
117
G_TYPE_BOOLEAN, &running,
122
LOG_ERROR("mpris_service_is_running_by_name: NameHasOwner call for %s failed. %s.\n", service_name, error->message);
127
g_object_unref(proxy);
132
void mpris_read_track_info(GHashTable *table, MediaPlayerRec *player) {
134
TrackInfo *tr = &player->track;
136
// Fetch values from the hash table
138
value = (GValue *) g_hash_table_lookup(table, "artist");
139
if (G_VALUE_HOLDS_STRING(value)) {
140
g_strlcpy(tr->artist, g_value_get_string(value), MPRIS_STRLEN-1);
144
value = (GValue *) g_hash_table_lookup(table, "album");
145
if (G_VALUE_HOLDS_STRING(value)) {
146
g_strlcpy(tr->album, g_value_get_string(value), MPRIS_STRLEN-1);
150
value = (GValue *) g_hash_table_lookup(table, "title");
151
if (G_VALUE_HOLDS_STRING(value)) {
152
g_strlcpy(tr->track, g_value_get_string(value), MPRIS_STRLEN-1);
158
value = (GValue *)g_hash_table_lookup(table, "time");
160
if (G_VALUE_HOLDS_UINT(value))
161
tr->total_secs = g_value_get_uint(value);
162
else if (G_VALUE_HOLDS_UINT64(value))
163
tr->total_secs = g_value_get_uint64(value);
164
else if (G_VALUE_HOLDS_INT64(value))
165
tr->total_secs = g_value_get_int64(value);
168
// The hash map contains probably other keys, like: "location", "tracknumber", "genre", etc.
171
void mpris_get_info(gpointer player_rec) {
172
MediaPlayerRec *player = (MediaPlayerRec*) player_rec;
173
TrackInfo *tr = &player->track;
175
// Is this player running/is active?
176
player->active = mpris_service_is_running_by_name(player->service_name);
177
if (!player->active) {
178
tr->status = PLAYER_STATUS_CLOSED;
182
GError *error = NULL;
183
GHashTable *table = NULL;
185
if(dbus_g_proxy_call_with_timeout(player->proxy,
187
DBUS_MPRIS_TIMEOUT, &error,
188
G_TYPE_INVALID, DBUS_TYPE_G_STRING_VALUE_HASHTABLE, &table,
191
// Read values from the hash table
192
mpris_read_track_info(table, player);
195
g_hash_table_destroy(table);
199
// The player has shutdown
200
tr->status = PLAYER_STATUS_CLOSED;
201
player->active = FALSE;
203
LOG_ERROR("mpris_get_info: GetMetadata call for %s failed. %s\n", player->service_name, error->message);
208
GValueArray *s = NULL;
210
if (dbus_g_proxy_call_with_timeout(player->proxy, "GetStatus", DBUS_MPRIS_TIMEOUT, &error,
212
DBUS_TYPE_MPRIS_STATUS, &s,
215
// Read player's status
216
gint status = PLAYER_STATUS_CLOSED;
219
GValue *v = g_value_array_get_nth(s, 0);
220
status = g_value_get_int(v);
223
g_value_array_free(s);
225
// Ref: http://xmms2.org/wiki/MPRIS#GetStatus
226
// 0 = Playing, 1 = Paused, 2 = Stopped.
228
// Convert player status to this program's value system
231
tr->status = PLAYER_STATUS_PLAYING;
235
tr->status = PLAYER_STATUS_PAUSED;
239
tr->status = PLAYER_STATUS_STOPPED;
243
tr->status = PLAYER_STATUS_CLOSED;
248
tr->status = PLAYER_STATUS_CLOSED;
249
LOG_ERROR("mpris_get_info: GetStatus call for %s failed. %s\n", player->service_name, error->message);
255
tr->current_secs = 0;
259
if(dbus_g_proxy_call_with_timeout(player->proxy, "PositionGet", DBUS_MPRIS_TIMEOUT, &error,
260
G_TYPE_INVALID, G_TYPE_INT, &position,
262
tr->current_secs = position/1000;
271
void debug_hash_table(MediaPlayerRec *player, GHashTable *table) {
274
g_hash_table_iter_init(&iter, table);
278
g_hash_table_iter_init(&iter, table);
279
while (g_hash_table_iter_next(&iter, &key, &value)) {
280
// The keys defined by Totem are:
281
// "title", "location", "time", "genre", "album", "tracknumber", "year" and "artist"
283
// The keys defined by Amarok are:
284
// "comment", "genre", "location", "rating", "arturl", "album", "audio-bitrate", "tracknumber", "year", "artist", "audio-samplerate" and "title"
286
glong long_val = G_MINLONG;
287
if (G_VALUE_HOLDS_STRING(value)) {
288
LOG_PLAYER("%s %s: %s=%s\n", player->service_name, player->app_name, key, g_value_get_string(value));
289
} else if G_VALUE_HOLDS_UINT(value) {
290
long_val = (glong)g_value_get_uint(value);
291
} else if G_VALUE_HOLDS_INT(value) {
292
long_val = (glong)g_value_get_int(value);
293
} else if G_VALUE_HOLDS_LONG(value) {
294
long_val = (glong)g_value_get_long(value);
295
} else if G_VALUE_HOLDS_ULONG(value) {
296
long_val = (glong)g_value_get_ulong(value);
299
if (long_val != G_MINLONG) {
300
LOG_PLAYER("%s %s: %s=%ld\n", player->service_name, player->app_name, key, long_val);
303
LOG_PLAYER("------------------------\n");
307
void mpris_track_signal_cb(DBusGProxy *player_proxy, gpointer data, MediaPlayerRec *player) {
308
GHashTable *hash_table = (GHashTable*)data;
310
/*suppress unused variable msg*/
313
LOG_PLAYER("mpris_track_signal_cb()\n");
315
#ifdef ACTIVE_DEBUGGING
316
debug_hash_table(player, hash_table);
319
// Re-read all player data and inform GUI
320
mpris_player_track_changed(player);
323
void mpris_status_signal_int_cb(DBusGProxy *player_proxy, gpointer data, MediaPlayerRec *player) {
324
gint status = (gint)(glong)data;
326
/*suppress unused variable msg*/
329
LOG_PLAYER("mpris_status_signal_int_cb: Status: %d\n", status);
331
// Re-read all player data and inform the GUI
332
mpris_player_status_changed(player);
335
void mpris_status_signal_struct_cb(DBusGProxy *player_proxy, gpointer data, MediaPlayerRec *player) {
336
GValueArray *sigstruct = (GValueArray*)data;
340
GValue *v = g_value_array_get_nth(sigstruct, 0);
341
status = g_value_get_int(v);
344
// Suppress "not used" message
349
LOG_PLAYER("mpris_status_signal_struct_cb(): status=%d\n", status);
351
// Re-read all player data and inform GUI
352
mpris_player_status_changed(player);
355
void mpris_set_signals(gpointer player_rec, gboolean connect) {
356
// Connect/disconnect signals
358
MediaPlayerRec *player = (MediaPlayerRec*)player_rec;
360
DBusGConnection *dbus_conn = dbus_player_connect_to_dbus();
362
if (!player->proxy) {
363
player->proxy = dbus_g_proxy_new_for_name(dbus_conn, player->service_name, DBUS_MPRIS_PLAYER_PATH, DBUS_MPRIS_PLAYER);
365
// Register arguments.
366
dbus_g_proxy_add_signal(player->proxy, DBUS_MPRIS_TRACK_SIGNAL, DBUS_TYPE_G_STRING_VALUE_HASHTABLE, G_TYPE_INVALID);
367
dbus_g_proxy_add_signal(player->proxy, DBUS_MPRIS_STATUS_SIGNAL, DBUS_TYPE_MPRIS_STATUS, G_TYPE_INVALID);
370
if (!player->proxy) {
371
LOG_ERROR("mpris_set_signals: Cannot create proxy for %s.\n", player->service_name);
377
dbus_g_proxy_connect_signal(player->proxy, DBUS_MPRIS_TRACK_SIGNAL, G_CALLBACK(mpris_track_signal_cb), (gpointer)player, NULL);
378
dbus_g_proxy_connect_signal(player->proxy, DBUS_MPRIS_STATUS_SIGNAL, G_CALLBACK(mpris_status_signal_struct_cb), (gpointer)player, NULL);
381
// Disconnect signals
382
dbus_g_proxy_disconnect_signal(player->proxy, DBUS_MPRIS_TRACK_SIGNAL, G_CALLBACK(mpris_track_signal_cb), (gpointer)player);
383
dbus_g_proxy_disconnect_signal(player->proxy, DBUS_MPRIS_STATUS_SIGNAL, G_CALLBACK(mpris_status_signal_struct_cb), (gpointer)player);
387
void mpris_get_app_name(MediaPlayerRec *player) {
389
DBusGConnection *dbus_conn = dbus_player_connect_to_dbus();
391
// Get identity/name of this player (the name + version)
392
// Ref:http://xmms2.org/wiki/MPRIS#Identity
394
LOG_PLAYER("mpris_get_app_name: %s\n", player->app_name);
396
DBusGProxy *proxy = dbus_g_proxy_new_for_name(dbus_conn,
397
player->service_name,
398
DBUS_MPRIS_ROOT_PATH,
401
gchar *player_name = NULL;
402
GError *error = NULL;
403
if (dbus_g_proxy_call_with_timeout(proxy, "Identity", DBUS_MPRIS_TIMEOUT, &error, G_TYPE_INVALID, G_TYPE_STRING, &player_name, G_TYPE_INVALID)) {
404
player->app_name = g_strdup(player_name);
405
// g_free(player_name);
409
LOG_ERROR("mpris_get_app_name: Cannot get identity/name for %s. %s\n", player->service_name, error->message);
414
void mpris_start_app(gpointer player_rec) {
415
MediaPlayerRec *player = (MediaPlayerRec*)player_rec;
417
LOG_PLAYER("mpris_start_app: %s\n", player->app_name);
419
DBusGConnection *dbus_conn = dbus_player_connect_to_dbus();
421
if (!player->proxy) {
422
player->proxy = dbus_g_proxy_new_for_name(dbus_conn, player->service_name,
423
DBUS_MPRIS_PLAYER_PATH,
428
dbus_g_proxy_call_no_reply(player->proxy, "Play", G_TYPE_INVALID);
431
MediaPlayerRec *mpris_player_new(const gchar *service_name) {
432
// New MediaPlayerRec record
433
MediaPlayerRec *player = g_malloc0(sizeof(MediaPlayerRec));
434
player->service_name = g_strdup(service_name);
435
player->app_name = NULL;