~audio-recorder/audio-recorder/trunk

« back to all changes in this revision

Viewing changes to src/dbus-mpris.c

  • Committer: Osmo Antero
  • Date: 2012-04-18 19:22:01 UTC
  • Revision ID: osmoma@gmail.com-20120418192201-ejjs6ikv7o4aznbi
New media-player interface that's based on the MediaPlayer2 standard. See src/dbus-mpris2.c.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
 * Copyright (c) Linux community.
3
 
 *
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.
8
 
 *
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.
13
 
 *
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.
18
 
 */
19
 
#include <string.h>
20
 
#include <glib.h>
21
 
#include <gdk/gdk.h>
22
 
#include <dbus/dbus.h>
23
 
#include <dbus/dbus-glib.h>
24
 
 
25
 
#include "log.h"
26
 
#include "dbus-player.h"
27
 
#include "dbus-mpris.h"
28
 
#include "dbus-rhythmbox.h"
29
 
#include "dbus-banshee.h"
30
 
 
31
 
// Code for Media Players that strictly follow the MPRIS DBus standard.
32
 
// Ref.: http://xmms2.org/wiki/MPRIS
33
 
//
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/
36
 
 
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"
42
 
 
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))
46
 
 
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);
49
 
 
50
 
void mpris_module_init() {
51
 
    LOG_DEBUG("Init dbus_mpris.c.\n");
52
 
}
53
 
 
54
 
void mpris_module_exit() {
55
 
    LOG_DEBUG("Clean up dbus_mpris.c.\n");
56
 
}
57
 
 
58
 
void mpris_player_track_changed(MediaPlayerRec *player) {
59
 
    // Called when the song/audio track changes
60
 
 
61
 
    // Re-read all player data
62
 
    mpris_get_info(player);
63
 
 
64
 
    // Set track changed signal
65
 
    TrackInfo *tr = &player->track;
66
 
    tr->status = PLAYER_STATUS_TRACK_CHANGE;
67
 
 
68
 
    // Print debug info
69
 
    // dbus_player_debug_print(player);
70
 
 
71
 
    // Process data
72
 
    dbus_player_process_data(player);
73
 
 
74
 
}
75
 
 
76
 
void mpris_player_status_changed(MediaPlayerRec *player) {
77
 
    // Called when player's status changes
78
 
 
79
 
    // Get player information
80
 
    mpris_get_info(player);
81
 
 
82
 
    // Print debug info
83
 
    // dbus_player_debug_print(player);
84
 
 
85
 
    // Process data
86
 
    dbus_player_process_data(player);
87
 
}
88
 
 
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);
93
 
}
94
 
 
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
98
 
 
99
 
    DBusGProxy *proxy = NULL;
100
 
    GError *error = NULL;
101
 
 
102
 
    DBusGConnection *dbus_conn = dbus_player_connect_to_dbus();
103
 
 
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"
108
 
    if (!proxy) {
109
 
        return FALSE;
110
 
    }
111
 
 
112
 
    gboolean running = FALSE;
113
 
 
114
 
    if (dbus_g_proxy_call_with_timeout(proxy, "NameHasOwner", DBUS_MPRIS_TIMEOUT, &error,
115
 
                                       G_TYPE_STRING, service_name,
116
 
                                       G_TYPE_INVALID,
117
 
                                       G_TYPE_BOOLEAN, &running,
118
 
                                       G_TYPE_INVALID)) {
119
 
    }
120
 
 
121
 
    if (error) {
122
 
        LOG_ERROR("mpris_service_is_running_by_name: NameHasOwner call for %s failed. %s.\n", service_name, error->message);
123
 
        g_error_free(error);
124
 
    }
125
 
 
126
 
    // Unref the proxy
127
 
    g_object_unref(proxy);
128
 
 
129
 
    return running;
130
 
}
131
 
 
132
 
void mpris_read_track_info(GHashTable *table, MediaPlayerRec *player) {
133
 
    GValue *value;
134
 
    TrackInfo *tr = &player->track;
135
 
 
136
 
    // Fetch values from the hash table
137
 
    // Artist
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);
141
 
    }
142
 
 
143
 
    // Album
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);
147
 
    }
148
 
 
149
 
    // Title
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);
153
 
    }
154
 
 
155
 
    tr->total_secs = 0;
156
 
 
157
 
    // Time values
158
 
    value = (GValue *)g_hash_table_lookup(table, "time");
159
 
 
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);
166
 
 
167
 
    // Note:
168
 
    // The hash map contains probably other keys, like: "location", "tracknumber", "genre", etc.
169
 
}
170
 
 
171
 
void mpris_get_info(gpointer player_rec) {
172
 
    MediaPlayerRec *player = (MediaPlayerRec*) player_rec;
173
 
    TrackInfo *tr = &player->track;
174
 
 
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;
179
 
        return;
180
 
    }
181
 
 
182
 
    GError *error = NULL;
183
 
    GHashTable *table = NULL;
184
 
 
185
 
    if(dbus_g_proxy_call_with_timeout(player->proxy,
186
 
                                      "GetMetadata",
187
 
                                      DBUS_MPRIS_TIMEOUT, &error,
188
 
                                      G_TYPE_INVALID, DBUS_TYPE_G_STRING_VALUE_HASHTABLE, &table,
189
 
                                      G_TYPE_INVALID)) {
190
 
 
191
 
        // Read values from the hash table
192
 
        mpris_read_track_info(table, player);
193
 
 
194
 
        // Free it
195
 
        g_hash_table_destroy(table);
196
 
    }
197
 
 
198
 
    if (error) {
199
 
        // The player has shutdown
200
 
        tr->status = PLAYER_STATUS_CLOSED;
201
 
        player->active = FALSE;
202
 
 
203
 
        LOG_ERROR("mpris_get_info: GetMetadata call for %s failed. %s\n", player->service_name, error->message);
204
 
        g_error_free(error);
205
 
        return;
206
 
    }
207
 
 
208
 
    GValueArray *s = NULL;
209
 
    error = NULL;
210
 
    if (dbus_g_proxy_call_with_timeout(player->proxy, "GetStatus", DBUS_MPRIS_TIMEOUT, &error,
211
 
                                       G_TYPE_INVALID,
212
 
                                       DBUS_TYPE_MPRIS_STATUS, &s,
213
 
                                       G_TYPE_INVALID)) {
214
 
 
215
 
        // Read player's status
216
 
        gint status = PLAYER_STATUS_CLOSED;
217
 
 
218
 
        if (s) {
219
 
            GValue *v = g_value_array_get_nth(s, 0);
220
 
            status = g_value_get_int(v);
221
 
        }
222
 
 
223
 
        g_value_array_free(s);
224
 
 
225
 
        // Ref: http://xmms2.org/wiki/MPRIS#GetStatus
226
 
        // 0 = Playing, 1 = Paused, 2 = Stopped.
227
 
 
228
 
        // Convert player status to this program's value system
229
 
        switch (status) {
230
 
        case 0:
231
 
            tr->status = PLAYER_STATUS_PLAYING;
232
 
            break;
233
 
 
234
 
        case 1:
235
 
            tr->status = PLAYER_STATUS_PAUSED;
236
 
            break;
237
 
 
238
 
        case 2:
239
 
            tr->status = PLAYER_STATUS_STOPPED;
240
 
            break;
241
 
 
242
 
        default:
243
 
            tr->status = PLAYER_STATUS_CLOSED;
244
 
        }
245
 
    }
246
 
 
247
 
    if (error) {
248
 
        tr->status = PLAYER_STATUS_CLOSED;
249
 
        LOG_ERROR("mpris_get_info: GetStatus call for %s failed. %s\n", player->service_name, error->message);
250
 
        g_error_free(error);
251
 
    }
252
 
 
253
 
 
254
 
    // Current play time
255
 
    tr->current_secs = 0;
256
 
    gint position = 0;
257
 
    error = NULL;
258
 
 
259
 
    if(dbus_g_proxy_call_with_timeout(player->proxy, "PositionGet", DBUS_MPRIS_TIMEOUT, &error,
260
 
                                      G_TYPE_INVALID, G_TYPE_INT, &position,
261
 
                                      G_TYPE_INVALID)) {
262
 
        tr->current_secs = position/1000;
263
 
    }
264
 
 
265
 
    if (error) {
266
 
        g_error_free(error);
267
 
    }
268
 
 
269
 
}
270
 
 
271
 
void debug_hash_table(MediaPlayerRec *player, GHashTable *table) {
272
 
 
273
 
    GHashTableIter iter;
274
 
    g_hash_table_iter_init(&iter, table);
275
 
 
276
 
    gpointer key, value;
277
 
 
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"
282
 
        //
283
 
        // The keys defined by Amarok are:
284
 
        // "comment", "genre", "location", "rating", "arturl", "album", "audio-bitrate", "tracknumber", "year", "artist", "audio-samplerate" and "title"
285
 
 
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);
297
 
        }
298
 
 
299
 
        if (long_val != G_MINLONG) {
300
 
            LOG_PLAYER("%s %s: %s=%ld\n", player->service_name, player->app_name, key, long_val);
301
 
        }
302
 
    }
303
 
    LOG_PLAYER("------------------------\n");
304
 
}
305
 
 
306
 
 
307
 
void mpris_track_signal_cb(DBusGProxy *player_proxy, gpointer data, MediaPlayerRec *player) {
308
 
    GHashTable *hash_table = (GHashTable*)data;
309
 
    if (hash_table) {
310
 
        /*suppress unused variable msg*/
311
 
    }
312
 
 
313
 
    LOG_PLAYER("mpris_track_signal_cb()\n");
314
 
 
315
 
#ifdef ACTIVE_DEBUGGING
316
 
    debug_hash_table(player, hash_table);
317
 
#endif
318
 
 
319
 
    // Re-read all player data and inform GUI
320
 
    mpris_player_track_changed(player);
321
 
}
322
 
 
323
 
void mpris_status_signal_int_cb(DBusGProxy *player_proxy, gpointer data, MediaPlayerRec *player) {
324
 
    gint status = (gint)(glong)data;
325
 
    if (status) {
326
 
        /*suppress unused variable msg*/
327
 
    }
328
 
 
329
 
    LOG_PLAYER("mpris_status_signal_int_cb: Status: %d\n", status);
330
 
 
331
 
    // Re-read all player data and inform the GUI
332
 
    mpris_player_status_changed(player);
333
 
}
334
 
 
335
 
void mpris_status_signal_struct_cb(DBusGProxy *player_proxy, gpointer data, MediaPlayerRec *player) {
336
 
    GValueArray *sigstruct = (GValueArray*)data;
337
 
 
338
 
    gint status = -1;
339
 
    if (sigstruct) {
340
 
        GValue *v = g_value_array_get_nth(sigstruct, 0);
341
 
        status = g_value_get_int(v);
342
 
    }
343
 
 
344
 
    // Suppress "not used" message
345
 
    if (status) {
346
 
        ;
347
 
    }
348
 
 
349
 
    LOG_PLAYER("mpris_status_signal_struct_cb(): status=%d\n", status);
350
 
 
351
 
    // Re-read all player data and inform GUI
352
 
    mpris_player_status_changed(player);
353
 
}
354
 
 
355
 
void mpris_set_signals(gpointer player_rec, gboolean connect) {
356
 
    // Connect/disconnect signals
357
 
 
358
 
    MediaPlayerRec *player = (MediaPlayerRec*)player_rec;
359
 
 
360
 
    DBusGConnection *dbus_conn = dbus_player_connect_to_dbus();
361
 
 
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);
364
 
 
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);
368
 
    }
369
 
 
370
 
    if (!player->proxy) {
371
 
        LOG_ERROR("mpris_set_signals: Cannot create proxy for %s.\n", player->service_name);
372
 
        return;
373
 
    }
374
 
 
375
 
    if (connect) {
376
 
        // Connect signals
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);
379
 
 
380
 
    } else {
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);
384
 
    }
385
 
}
386
 
 
387
 
void mpris_get_app_name(MediaPlayerRec *player) {
388
 
 
389
 
    DBusGConnection *dbus_conn = dbus_player_connect_to_dbus();
390
 
 
391
 
    // Get identity/name of this player (the name + version)
392
 
    // Ref:http://xmms2.org/wiki/MPRIS#Identity
393
 
 
394
 
    LOG_PLAYER("mpris_get_app_name: %s\n", player->app_name);
395
 
 
396
 
    DBusGProxy *proxy = dbus_g_proxy_new_for_name(dbus_conn,
397
 
                        player->service_name,
398
 
                        DBUS_MPRIS_ROOT_PATH,
399
 
                        DBUS_MPRIS_PLAYER);
400
 
 
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);
406
 
    }
407
 
 
408
 
    if (error) {
409
 
        LOG_ERROR("mpris_get_app_name: Cannot get identity/name for %s. %s\n", player->service_name, error->message);
410
 
        g_error_free(error);
411
 
    }
412
 
}
413
 
 
414
 
void mpris_start_app(gpointer player_rec) {
415
 
    MediaPlayerRec *player = (MediaPlayerRec*)player_rec;
416
 
 
417
 
    LOG_PLAYER("mpris_start_app: %s\n", player->app_name);
418
 
 
419
 
    DBusGConnection *dbus_conn = dbus_player_connect_to_dbus();
420
 
 
421
 
    if (!player->proxy) {
422
 
        player->proxy = dbus_g_proxy_new_for_name(dbus_conn, player->service_name,
423
 
                        DBUS_MPRIS_PLAYER_PATH,
424
 
                        DBUS_MPRIS_PLAYER);
425
 
    }
426
 
 
427
 
    // Start it
428
 
    dbus_g_proxy_call_no_reply(player->proxy, "Play", G_TYPE_INVALID);
429
 
}
430
 
 
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;
436
 
    return player;
437
 
}
438