| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995 |
- // SPDX-License-Identifier: GPL-2.0-or-later
- /*
- *
- * BlueZ - Bluetooth protocol stack for Linux
- *
- * Copyright (C) 2006-2007 Nokia Corporation
- * Copyright (C) 2004-2009 Marcel Holtmann <marcel@holtmann.org>
- * Copyright (C) 2012-2012 Intel Corporation
- *
- */
- #ifdef HAVE_CONFIG_H
- #include <config.h>
- #endif
- #define _GNU_SOURCE
- #include <stdlib.h>
- #include <stdint.h>
- #include <inttypes.h>
- #include <stdbool.h>
- #include <errno.h>
- #include <unistd.h>
- #include <string.h>
- #include <glib.h>
- #include <dbus/dbus.h>
- #include "gdbus/gdbus.h"
- #include "src/log.h"
- #include "src/dbus-common.h"
- #include "src/error.h"
- #include "player.h"
- #define MEDIA_PLAYER_INTERFACE "org.bluez.MediaPlayer1"
- #define MEDIA_FOLDER_INTERFACE "org.bluez.MediaFolder1"
- #define MEDIA_ITEM_INTERFACE "org.bluez.MediaItem1"
- struct player_callback {
- const struct media_player_callback *cbs;
- void *user_data;
- };
- struct pending_req {
- GDBusPendingPropertySet id;
- const char *key;
- const char *value;
- };
- struct media_item {
- struct media_player *player;
- char *path; /* Item object path */
- char *name; /* Item name */
- player_item_type_t type; /* Item type */
- player_folder_type_t folder_type; /* Folder type */
- bool playable; /* Item playable flag */
- uint64_t uid; /* Item uid */
- GHashTable *metadata; /* Item metadata */
- };
- struct media_folder {
- struct media_folder *parent;
- struct media_item *item; /* Folder item */
- uint32_t number_of_items;/* Number of items */
- GSList *subfolders;
- GSList *items;
- DBusMessage *msg;
- };
- struct media_player {
- char *device; /* Device path */
- char *name; /* Player name */
- char *type; /* Player type */
- char *subtype; /* Player subtype */
- bool browsable; /* Player browsing feature */
- bool searchable; /* Player searching feature */
- struct media_folder *scope; /* Player current scope */
- struct media_folder *folder; /* Player current folder */
- struct media_folder *search; /* Player search folder */
- struct media_folder *playlist; /* Player current playlist */
- char *path; /* Player object path */
- GHashTable *settings; /* Player settings */
- GHashTable *track; /* Player current track */
- char *status;
- uint32_t position;
- GTimer *progress;
- guint process_id;
- struct player_callback *cb;
- GSList *pending;
- GSList *folders;
- };
- static void append_track(void *key, void *value, void *user_data)
- {
- DBusMessageIter *dict = user_data;
- const char *strkey = key;
- if (strcasecmp(strkey, "Duration") == 0 ||
- strcasecmp(strkey, "TrackNumber") == 0 ||
- strcasecmp(strkey, "NumberOfTracks") == 0) {
- uint32_t num = atoi(value);
- dict_append_entry(dict, key, DBUS_TYPE_UINT32, &num);
- } else if (strcasecmp(strkey, "Item") == 0) {
- dict_append_entry(dict, key, DBUS_TYPE_OBJECT_PATH, &value);
- } else {
- dict_append_entry(dict, key, DBUS_TYPE_STRING, &value);
- }
- }
- static struct pending_req *find_pending(struct media_player *mp,
- const char *key)
- {
- GSList *l;
- for (l = mp->pending; l; l = l->next) {
- struct pending_req *p = l->data;
- if (strcasecmp(key, p->key) == 0)
- return p;
- }
- return NULL;
- }
- static struct pending_req *pending_new(GDBusPendingPropertySet id,
- const char *key, const char *value)
- {
- struct pending_req *p;
- p = g_new0(struct pending_req, 1);
- p->id = id;
- p->key = key;
- p->value = value;
- return p;
- }
- static uint32_t media_player_get_position(struct media_player *mp)
- {
- double timedelta;
- uint32_t sec, msec;
- if (g_strcmp0(mp->status, "playing") != 0 ||
- mp->position == UINT32_MAX)
- return mp->position;
- timedelta = g_timer_elapsed(mp->progress, NULL);
- sec = (uint32_t) timedelta;
- msec = (uint32_t) ((timedelta - sec) * 1000);
- return mp->position + sec * 1000 + msec;
- }
- static gboolean get_position(const GDBusPropertyTable *property,
- DBusMessageIter *iter, void *data)
- {
- struct media_player *mp = data;
- uint32_t position;
- position = media_player_get_position(mp);
- dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32, &position);
- return TRUE;
- }
- static gboolean status_exists(const GDBusPropertyTable *property, void *data)
- {
- struct media_player *mp = data;
- return mp->status != NULL;
- }
- static gboolean get_status(const GDBusPropertyTable *property,
- DBusMessageIter *iter, void *data)
- {
- struct media_player *mp = data;
- if (mp->status == NULL)
- return FALSE;
- dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &mp->status);
- return TRUE;
- }
- static gboolean setting_exists(const GDBusPropertyTable *property, void *data)
- {
- struct media_player *mp = data;
- const char *value;
- value = g_hash_table_lookup(mp->settings, property->name);
- return value ? TRUE : FALSE;
- }
- static gboolean get_setting(const GDBusPropertyTable *property,
- DBusMessageIter *iter, void *data)
- {
- struct media_player *mp = data;
- const char *value;
- value = g_hash_table_lookup(mp->settings, property->name);
- if (value == NULL)
- return FALSE;
- dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &value);
- return TRUE;
- }
- static void player_set_setting(struct media_player *mp,
- GDBusPendingPropertySet id,
- const char *key, const char *value)
- {
- struct player_callback *cb = mp->cb;
- struct pending_req *p;
- if (cb == NULL || cb->cbs->set_setting == NULL) {
- g_dbus_pending_property_error(id,
- ERROR_INTERFACE ".NotSupported",
- "Operation is not supported");
- return;
- }
- p = find_pending(mp, key);
- if (p != NULL) {
- g_dbus_pending_property_error(id,
- ERROR_INTERFACE ".InProgress",
- "Operation already in progress");
- return;
- }
- if (!cb->cbs->set_setting(mp, key, value, cb->user_data)) {
- g_dbus_pending_property_error(id,
- ERROR_INTERFACE ".InvalidArguments",
- "Invalid arguments in method call");
- return;
- }
- p = pending_new(id, key, value);
- mp->pending = g_slist_append(mp->pending, p);
- }
- static void set_setting(const GDBusPropertyTable *property,
- DBusMessageIter *iter, GDBusPendingPropertySet id,
- void *data)
- {
- struct media_player *mp = data;
- const char *value, *current;
- if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING) {
- g_dbus_pending_property_error(id,
- ERROR_INTERFACE ".InvalidArguments",
- "Invalid arguments in method call");
- return;
- }
- dbus_message_iter_get_basic(iter, &value);
- current = g_hash_table_lookup(mp->settings, property->name);
- if (g_strcmp0(current, value) == 0) {
- g_dbus_pending_property_success(id);
- return;
- }
- player_set_setting(mp, id, property->name, value);
- }
- static gboolean track_exists(const GDBusPropertyTable *property, void *data)
- {
- struct media_player *mp = data;
- return g_hash_table_size(mp->track) != 0;
- }
- static gboolean get_track(const GDBusPropertyTable *property,
- DBusMessageIter *iter, void *data)
- {
- struct media_player *mp = data;
- DBusMessageIter dict;
- dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
- DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
- DBUS_TYPE_STRING_AS_STRING
- DBUS_TYPE_VARIANT_AS_STRING
- DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
- &dict);
- g_hash_table_foreach(mp->track, append_track, &dict);
- dbus_message_iter_close_container(iter, &dict);
- return TRUE;
- }
- static gboolean get_device(const GDBusPropertyTable *property,
- DBusMessageIter *iter, void *data)
- {
- struct media_player *mp = data;
- dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH,
- &mp->device);
- return TRUE;
- }
- static gboolean name_exists(const GDBusPropertyTable *property, void *data)
- {
- struct media_player *mp = data;
- return mp->name != NULL;
- }
- static gboolean get_name(const GDBusPropertyTable *property,
- DBusMessageIter *iter, void *data)
- {
- struct media_player *mp = data;
- if (mp->name == NULL)
- return FALSE;
- dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &mp->name);
- return TRUE;
- }
- static gboolean type_exists(const GDBusPropertyTable *property, void *data)
- {
- struct media_player *mp = data;
- return mp->type != NULL;
- }
- static gboolean get_type(const GDBusPropertyTable *property,
- DBusMessageIter *iter, void *data)
- {
- struct media_player *mp = data;
- if (mp->type == NULL)
- return FALSE;
- dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &mp->type);
- return TRUE;
- }
- static gboolean subtype_exists(const GDBusPropertyTable *property, void *data)
- {
- struct media_player *mp = data;
- return mp->subtype != NULL;
- }
- static gboolean get_subtype(const GDBusPropertyTable *property,
- DBusMessageIter *iter, void *data)
- {
- struct media_player *mp = data;
- if (mp->subtype == NULL)
- return FALSE;
- dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &mp->subtype);
- return TRUE;
- }
- static gboolean browsable_exists(const GDBusPropertyTable *property, void *data)
- {
- struct media_player *mp = data;
- return mp->folder != NULL;
- }
- static gboolean get_browsable(const GDBusPropertyTable *property,
- DBusMessageIter *iter, void *data)
- {
- struct media_player *mp = data;
- dbus_bool_t value;
- if (mp->folder == NULL)
- return FALSE;
- value = mp->browsable;
- dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &value);
- return TRUE;
- }
- static gboolean searchable_exists(const GDBusPropertyTable *property,
- void *data)
- {
- struct media_player *mp = data;
- return mp->folder != NULL;
- }
- static gboolean get_searchable(const GDBusPropertyTable *property,
- DBusMessageIter *iter, void *data)
- {
- struct media_player *mp = data;
- dbus_bool_t value;
- if (mp->folder == NULL)
- return FALSE;
- value = mp->searchable;
- dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &value);
- return TRUE;
- }
- static gboolean playlist_exists(const GDBusPropertyTable *property,
- void *data)
- {
- struct media_player *mp = data;
- return mp->playlist != NULL;
- }
- static gboolean get_playlist(const GDBusPropertyTable *property,
- DBusMessageIter *iter, void *data)
- {
- struct media_player *mp = data;
- struct media_folder *playlist = mp->playlist;
- if (playlist == NULL)
- return FALSE;
- dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH,
- &playlist->item->path);
- return TRUE;
- }
- static DBusMessage *media_player_play(DBusConnection *conn, DBusMessage *msg,
- void *data)
- {
- struct media_player *mp = data;
- struct player_callback *cb = mp->cb;
- int err;
- if (cb->cbs->play == NULL)
- return btd_error_not_supported(msg);
- err = cb->cbs->play(mp, cb->user_data);
- if (err < 0)
- return btd_error_failed(msg, strerror(-err));
- return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
- }
- static DBusMessage *media_player_pause(DBusConnection *conn, DBusMessage *msg,
- void *data)
- {
- struct media_player *mp = data;
- struct player_callback *cb = mp->cb;
- int err;
- if (cb->cbs->pause == NULL)
- return btd_error_not_supported(msg);
- err = cb->cbs->pause(mp, cb->user_data);
- if (err < 0)
- return btd_error_failed(msg, strerror(-err));
- return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
- }
- static DBusMessage *media_player_stop(DBusConnection *conn, DBusMessage *msg,
- void *data)
- {
- struct media_player *mp = data;
- struct player_callback *cb = mp->cb;
- int err;
- if (cb->cbs->stop == NULL)
- return btd_error_not_supported(msg);
- err = cb->cbs->stop(mp, cb->user_data);
- if (err < 0)
- return btd_error_failed(msg, strerror(-err));
- return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
- }
- static DBusMessage *media_player_next(DBusConnection *conn, DBusMessage *msg,
- void *data)
- {
- struct media_player *mp = data;
- struct player_callback *cb = mp->cb;
- int err;
- if (cb->cbs->next == NULL)
- return btd_error_not_supported(msg);
- err = cb->cbs->next(mp, cb->user_data);
- if (err < 0)
- return btd_error_failed(msg, strerror(-err));
- return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
- }
- static DBusMessage *media_player_previous(DBusConnection *conn,
- DBusMessage *msg, void *data)
- {
- struct media_player *mp = data;
- struct player_callback *cb = mp->cb;
- int err;
- if (cb->cbs->previous == NULL)
- return btd_error_not_supported(msg);
- err = cb->cbs->previous(mp, cb->user_data);
- if (err < 0)
- return btd_error_failed(msg, strerror(-err));
- return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
- }
- static DBusMessage *media_player_fast_forward(DBusConnection *conn,
- DBusMessage *msg, void *data)
- {
- struct media_player *mp = data;
- struct player_callback *cb = mp->cb;
- int err;
- if (cb->cbs->fast_forward == NULL)
- return btd_error_not_supported(msg);
- err = cb->cbs->fast_forward(mp, cb->user_data);
- if (err < 0)
- return btd_error_failed(msg, strerror(-err));
- return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
- }
- static DBusMessage *media_player_rewind(DBusConnection *conn, DBusMessage *msg,
- void *data)
- {
- struct media_player *mp = data;
- struct player_callback *cb = mp->cb;
- int err;
- if (cb->cbs->rewind == NULL)
- return btd_error_not_supported(msg);
- err = cb->cbs->rewind(mp, cb->user_data);
- if (err < 0)
- return btd_error_failed(msg, strerror(-err));
- return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
- }
- static DBusMessage *media_player_press(DBusConnection *conn, DBusMessage *msg,
- void *data)
- {
- struct media_player *mp = data;
- struct player_callback *cb = mp->cb;
- int err;
- uint8_t avc_key;
- if (cb->cbs->press == NULL)
- return btd_error_not_supported(msg);
- if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_BYTE, &avc_key,
- DBUS_TYPE_INVALID))
- return btd_error_invalid_args(msg);
- err = cb->cbs->press(mp, avc_key, cb->user_data);
- if (err < 0)
- return btd_error_failed(msg, strerror(-err));
- return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
- }
- static DBusMessage *media_player_hold(DBusConnection *conn, DBusMessage *msg,
- void *data)
- {
- struct media_player *mp = data;
- struct player_callback *cb = mp->cb;
- int err;
- uint8_t avc_key;
- if (cb->cbs->hold == NULL)
- return btd_error_not_supported(msg);
- if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_BYTE, &avc_key,
- DBUS_TYPE_INVALID))
- return btd_error_invalid_args(msg);
- err = cb->cbs->hold(mp, avc_key, cb->user_data);
- if (err < 0)
- return btd_error_failed(msg, strerror(-err));
- return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
- }
- static DBusMessage *media_player_release(DBusConnection *conn, DBusMessage *msg,
- void *data)
- {
- struct media_player *mp = data;
- struct player_callback *cb = mp->cb;
- int err;
- if (cb->cbs->release == NULL)
- return btd_error_not_supported(msg);
- err = cb->cbs->release(mp, cb->user_data);
- if (err < 0)
- return btd_error_failed(msg, strerror(-err));
- return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
- }
- static void parse_folder_list(gpointer data, gpointer user_data)
- {
- struct media_item *item = data;
- DBusMessageIter *array = user_data;
- DBusMessageIter entry;
- dbus_message_iter_open_container(array, DBUS_TYPE_DICT_ENTRY, NULL,
- &entry);
- dbus_message_iter_append_basic(&entry, DBUS_TYPE_OBJECT_PATH,
- &item->path);
- g_dbus_get_properties(btd_get_dbus_connection(), item->path,
- MEDIA_ITEM_INTERFACE, &entry);
- dbus_message_iter_close_container(array, &entry);
- }
- void media_player_list_complete(struct media_player *mp, GSList *items,
- int err)
- {
- struct media_folder *folder = mp->scope;
- DBusMessage *reply;
- DBusMessageIter iter, array;
- if (folder == NULL || folder->msg == NULL)
- return;
- if (err < 0) {
- reply = btd_error_failed(folder->msg, strerror(-err));
- goto done;
- }
- reply = dbus_message_new_method_return(folder->msg);
- dbus_message_iter_init_append(reply, &iter);
- dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
- DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
- DBUS_TYPE_OBJECT_PATH_AS_STRING
- DBUS_TYPE_ARRAY_AS_STRING
- DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
- DBUS_TYPE_STRING_AS_STRING
- DBUS_TYPE_VARIANT_AS_STRING
- DBUS_DICT_ENTRY_END_CHAR_AS_STRING
- DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
- &array);
- g_slist_foreach(items, parse_folder_list, &array);
- dbus_message_iter_close_container(&iter, &array);
- done:
- g_dbus_send_message(btd_get_dbus_connection(), reply);
- dbus_message_unref(folder->msg);
- folder->msg = NULL;
- }
- static struct media_item *
- media_player_create_subfolder(struct media_player *mp, const char *name,
- uint64_t uid)
- {
- struct media_folder *folder = mp->scope;
- struct media_item *item;
- char *path;
- path = g_strdup_printf("%s/%s", folder->item->name, name);
- DBG("%s", path);
- item = media_player_create_item(mp, path, PLAYER_ITEM_TYPE_FOLDER,
- uid);
- g_free(path);
- return item;
- }
- void media_player_search_complete(struct media_player *mp, int ret)
- {
- struct media_folder *folder = mp->scope;
- struct media_folder *search = mp->search;
- DBusMessage *reply;
- if (folder == NULL || folder->msg == NULL)
- return;
- if (ret < 0) {
- reply = btd_error_failed(folder->msg, strerror(-ret));
- goto done;
- }
- if (search == NULL) {
- search = g_new0(struct media_folder, 1);
- search->item = media_player_create_subfolder(mp, "search", 0);
- mp->search = search;
- mp->folders = g_slist_prepend(mp->folders, search);
- }
- search->number_of_items = ret;
- reply = g_dbus_create_reply(folder->msg,
- DBUS_TYPE_OBJECT_PATH, &search->item->path,
- DBUS_TYPE_INVALID);
- done:
- g_dbus_send_message(btd_get_dbus_connection(), reply);
- dbus_message_unref(folder->msg);
- folder->msg = NULL;
- }
- void media_player_total_items_complete(struct media_player *mp,
- uint32_t num_of_items)
- {
- struct media_folder *folder = mp->scope;
- if (folder == NULL || folder->msg == NULL)
- return;
- if (folder->number_of_items != num_of_items) {
- folder->number_of_items = num_of_items;
- g_dbus_emit_property_changed(btd_get_dbus_connection(),
- mp->path, MEDIA_FOLDER_INTERFACE,
- "NumberOfItems");
- }
- }
- static const GDBusMethodTable media_player_methods[] = {
- { GDBUS_METHOD("Play", NULL, NULL, media_player_play) },
- { GDBUS_METHOD("Pause", NULL, NULL, media_player_pause) },
- { GDBUS_METHOD("Stop", NULL, NULL, media_player_stop) },
- { GDBUS_METHOD("Next", NULL, NULL, media_player_next) },
- { GDBUS_METHOD("Previous", NULL, NULL, media_player_previous) },
- { GDBUS_METHOD("FastForward", NULL, NULL, media_player_fast_forward) },
- { GDBUS_METHOD("Rewind", NULL, NULL, media_player_rewind) },
- { GDBUS_METHOD("Press", GDBUS_ARGS({"avc_key", "y"}), NULL,
- media_player_press) },
- { GDBUS_METHOD("Hold", GDBUS_ARGS({"avc_key", "y"}), NULL,
- media_player_hold) },
- { GDBUS_METHOD("Release", NULL, NULL, media_player_release) },
- { }
- };
- static const GDBusSignalTable media_player_signals[] = {
- { }
- };
- static const GDBusPropertyTable media_player_properties[] = {
- { "Name", "s", get_name, NULL, name_exists },
- { "Type", "s", get_type, NULL, type_exists },
- { "Subtype", "s", get_subtype, NULL, subtype_exists },
- { "Position", "u", get_position, NULL, NULL },
- { "Status", "s", get_status, NULL, status_exists },
- { "Equalizer", "s", get_setting, set_setting, setting_exists },
- { "Repeat", "s", get_setting, set_setting, setting_exists },
- { "Shuffle", "s", get_setting, set_setting, setting_exists },
- { "Scan", "s", get_setting, set_setting, setting_exists },
- { "Track", "a{sv}", get_track, NULL, track_exists },
- { "Device", "o", get_device, NULL, NULL },
- { "Browsable", "b", get_browsable, NULL, browsable_exists },
- { "Searchable", "b", get_searchable, NULL, searchable_exists },
- { "Playlist", "o", get_playlist, NULL, playlist_exists },
- { }
- };
- static DBusMessage *media_folder_search(DBusConnection *conn, DBusMessage *msg,
- void *data)
- {
- struct media_player *mp = data;
- struct media_folder *folder = mp->scope;
- struct player_callback *cb = mp->cb;
- DBusMessageIter iter;
- const char *string;
- int err;
- dbus_message_iter_init(msg, &iter);
- if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
- return btd_error_invalid_args(msg);
- dbus_message_iter_get_basic(&iter, &string);
- if (!mp->searchable || folder != mp->folder || !cb->cbs->search)
- return btd_error_not_supported(msg);
- if (folder->msg != NULL)
- return btd_error_failed(msg, strerror(EINVAL));
- err = cb->cbs->search(mp, string, cb->user_data);
- if (err < 0)
- return btd_error_failed(msg, strerror(-err));
- folder->msg = dbus_message_ref(msg);
- return NULL;
- }
- static int parse_filters(struct media_player *player, DBusMessageIter *iter,
- uint32_t *start, uint32_t *end)
- {
- struct media_folder *folder = player->scope;
- DBusMessageIter dict;
- int ctype;
- *start = 0;
- *end = folder->number_of_items ? folder->number_of_items - 1 :
- UINT32_MAX;
- ctype = dbus_message_iter_get_arg_type(iter);
- if (ctype != DBUS_TYPE_ARRAY)
- return FALSE;
- dbus_message_iter_recurse(iter, &dict);
- while ((ctype = dbus_message_iter_get_arg_type(&dict)) !=
- DBUS_TYPE_INVALID) {
- DBusMessageIter entry, var;
- const char *key;
- if (ctype != DBUS_TYPE_DICT_ENTRY)
- return -EINVAL;
- dbus_message_iter_recurse(&dict, &entry);
- if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
- return -EINVAL;
- dbus_message_iter_get_basic(&entry, &key);
- dbus_message_iter_next(&entry);
- if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_VARIANT)
- return -EINVAL;
- dbus_message_iter_recurse(&entry, &var);
- if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_UINT32)
- return -EINVAL;
- if (strcasecmp(key, "Start") == 0)
- dbus_message_iter_get_basic(&var, start);
- else if (strcasecmp(key, "End") == 0)
- dbus_message_iter_get_basic(&var, end);
- dbus_message_iter_next(&dict);
- }
- if (folder->number_of_items > 0 && *end > folder->number_of_items)
- *end = folder->number_of_items;
- return 0;
- }
- static DBusMessage *media_folder_list_items(DBusConnection *conn,
- DBusMessage *msg, void *data)
- {
- struct media_player *mp = data;
- struct media_folder *folder = mp->scope;
- struct player_callback *cb = mp->cb;
- DBusMessageIter iter;
- uint32_t start, end;
- int err;
- dbus_message_iter_init(msg, &iter);
- if (parse_filters(mp, &iter, &start, &end) < 0)
- return btd_error_invalid_args(msg);
- if (cb->cbs->list_items == NULL)
- return btd_error_not_supported(msg);
- if (folder->msg != NULL)
- return btd_error_failed(msg, strerror(EBUSY));
- err = cb->cbs->list_items(mp, folder->item->name, start, end,
- cb->user_data);
- if (err < 0)
- return btd_error_failed(msg, strerror(-err));
- folder->msg = dbus_message_ref(msg);
- return NULL;
- }
- static void media_item_free(struct media_item *item)
- {
- if (item->metadata != NULL)
- g_hash_table_unref(item->metadata);
- g_free(item->path);
- g_free(item->name);
- g_free(item);
- }
- static void media_item_destroy(void *data)
- {
- struct media_item *item = data;
- DBG("%s", item->path);
- g_dbus_unregister_interface(btd_get_dbus_connection(), item->path,
- MEDIA_ITEM_INTERFACE);
- media_item_free(item);
- }
- static void media_folder_destroy(void *data)
- {
- struct media_folder *folder = data;
- g_slist_free_full(folder->subfolders, media_folder_destroy);
- g_slist_free_full(folder->items, media_item_destroy);
- if (folder->msg != NULL)
- dbus_message_unref(folder->msg);
- media_item_destroy(folder->item);
- g_free(folder);
- }
- static void media_player_change_scope(struct media_player *mp,
- struct media_folder *folder)
- {
- struct player_callback *cb = mp->cb;
- int err;
- if (mp->scope == folder)
- return;
- DBG("%s", folder->item->name);
- /* Skip setting current folder if folder is current playlist/search */
- if (folder == mp->playlist || folder == mp->search)
- goto cleanup;
- mp->folder = folder;
- /* Skip item cleanup if scope is the current playlist */
- if (mp->scope == mp->playlist)
- goto done;
- cleanup:
- g_slist_free_full(mp->scope->items, media_item_destroy);
- mp->scope->items = NULL;
- /* Destroy search folder if it exists and is not being set as scope */
- if (mp->search != NULL && folder != mp->search) {
- mp->folders = g_slist_remove(mp->folders, mp->search);
- media_folder_destroy(mp->search);
- mp->search = NULL;
- }
- done:
- mp->scope = folder;
- if (cb->cbs->total_items) {
- err = cb->cbs->total_items(mp, folder->item->name,
- cb->user_data);
- if (err < 0)
- DBG("Failed to get total num of items");
- } else {
- g_dbus_emit_property_changed(btd_get_dbus_connection(),
- mp->path, MEDIA_FOLDER_INTERFACE,
- "NumberOfItems");
- }
- g_dbus_emit_property_changed(btd_get_dbus_connection(), mp->path,
- MEDIA_FOLDER_INTERFACE, "Name");
- }
- static struct media_folder *find_folder(GSList *folders, const char *pattern)
- {
- GSList *l;
- for (l = folders; l; l = l->next) {
- struct media_folder *folder = l->data;
- if (g_str_equal(folder->item->name, pattern))
- return folder;
- if (g_str_equal(folder->item->path, pattern))
- return folder;
- folder = find_folder(folder->subfolders, pattern);
- if (folder != NULL)
- return folder;
- }
- return NULL;
- }
- static struct media_folder *media_player_find_folder(struct media_player *mp,
- const char *pattern)
- {
- return find_folder(mp->folders, pattern);
- }
- static DBusMessage *media_folder_change_folder(DBusConnection *conn,
- DBusMessage *msg, void *data)
- {
- struct media_player *mp = data;
- struct media_folder *folder = mp->scope;
- struct player_callback *cb = mp->cb;
- const char *path;
- int err;
- if (!dbus_message_get_args(msg, NULL,
- DBUS_TYPE_OBJECT_PATH, &path,
- DBUS_TYPE_INVALID))
- return btd_error_invalid_args(msg);
- if (folder->msg != NULL)
- return btd_error_failed(msg, strerror(EBUSY));
- folder = media_player_find_folder(mp, path);
- if (folder == NULL)
- return btd_error_invalid_args(msg);
- if (mp->scope == folder)
- return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
- if (folder == mp->playlist || folder == mp->folder ||
- folder == mp->search) {
- media_player_change_scope(mp, folder);
- return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
- }
- /*
- * ChangePath can only navigate one level up/down so check if folder
- * is direct child or parent of the current folder otherwise fail.
- */
- if (!g_slist_find(mp->folder->subfolders, folder) &&
- !g_slist_find(folder->subfolders, mp->folder))
- return btd_error_invalid_args(msg);
- if (cb->cbs->change_folder == NULL)
- return btd_error_not_supported(msg);
- err = cb->cbs->change_folder(mp, folder->item->name, folder->item->uid,
- cb->user_data);
- if (err < 0)
- return btd_error_failed(msg, strerror(-err));
- mp->scope->msg = dbus_message_ref(msg);
- return NULL;
- }
- static gboolean folder_name_exists(const GDBusPropertyTable *property,
- void *data)
- {
- struct media_player *mp = data;
- struct media_folder *folder = mp->scope;
- if (folder == NULL || folder->item == NULL)
- return FALSE;
- return folder->item->name != NULL;
- }
- static gboolean get_folder_name(const GDBusPropertyTable *property,
- DBusMessageIter *iter, void *data)
- {
- struct media_player *mp = data;
- struct media_folder *folder = mp->scope;
- if (folder == NULL || folder->item == NULL)
- return FALSE;
- DBG("%s", folder->item->name);
- dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
- &folder->item->name);
- return TRUE;
- }
- static gboolean items_exists(const GDBusPropertyTable *property, void *data)
- {
- struct media_player *mp = data;
- return mp->scope != NULL;
- }
- static gboolean get_items(const GDBusPropertyTable *property,
- DBusMessageIter *iter, void *data)
- {
- struct media_player *mp = data;
- struct media_folder *folder = mp->scope;
- if (folder == NULL)
- return FALSE;
- DBG("%u", folder->number_of_items);
- dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32,
- &folder->number_of_items);
- return TRUE;
- }
- static const GDBusMethodTable media_folder_methods[] = {
- { GDBUS_ASYNC_METHOD("Search",
- GDBUS_ARGS({ "string", "s" }, { "filter", "a{sv}" }),
- GDBUS_ARGS({ "folder", "o" }),
- media_folder_search) },
- { GDBUS_ASYNC_METHOD("ListItems",
- GDBUS_ARGS({ "filter", "a{sv}" }),
- GDBUS_ARGS({ "items", "a{oa{sv}}" }),
- media_folder_list_items) },
- { GDBUS_ASYNC_METHOD("ChangeFolder",
- GDBUS_ARGS({ "folder", "o" }), NULL,
- media_folder_change_folder) },
- { }
- };
- static const GDBusPropertyTable media_folder_properties[] = {
- { "Name", "s", get_folder_name, NULL, folder_name_exists },
- { "NumberOfItems", "u", get_items, NULL, items_exists },
- { }
- };
- static void media_player_set_scope(struct media_player *mp,
- struct media_folder *folder)
- {
- if (mp->scope == NULL) {
- if (!g_dbus_register_interface(btd_get_dbus_connection(),
- mp->path, MEDIA_FOLDER_INTERFACE,
- media_folder_methods,
- NULL,
- media_folder_properties, mp, NULL)) {
- error("D-Bus failed to register %s on %s path",
- MEDIA_FOLDER_INTERFACE, mp->path);
- return;
- }
- mp->scope = folder;
- return;
- }
- return media_player_change_scope(mp, folder);
- }
- static struct media_folder *
- media_player_find_folder_by_uid(struct media_player *mp, uint64_t uid)
- {
- struct media_folder *folder = mp->scope;
- struct media_folder *parent = folder->parent;
- GSList *l;
- if (parent && parent->item->uid == uid)
- return parent;
- for (l = folder->subfolders; l; l = l->next) {
- struct media_folder *folder = l->data;
- if (folder->item->uid == uid)
- return folder;
- }
- return NULL;
- }
- static void media_player_set_folder_by_uid(struct media_player *mp,
- uint64_t uid, uint32_t number_of_items)
- {
- struct media_folder *folder;
- DBG("uid %" PRIu64 " number of items %u", uid, number_of_items);
- folder = media_player_find_folder_by_uid(mp, uid);
- if (folder == NULL) {
- error("Unknown folder: %" PRIu64, uid);
- return;
- }
- folder->number_of_items = number_of_items;
- media_player_set_scope(mp, folder);
- }
- void media_player_change_folder_complete(struct media_player *mp,
- const char *path, uint64_t uid,
- int ret)
- {
- struct media_folder *folder = mp->scope;
- DBusMessage *reply;
- if (folder == NULL || folder->msg == NULL)
- return;
- if (ret < 0) {
- reply = btd_error_failed(folder->msg, strerror(-ret));
- goto done;
- }
- media_player_set_folder_by_uid(mp, uid, ret);
- reply = g_dbus_create_reply(folder->msg, DBUS_TYPE_INVALID);
- done:
- g_dbus_send_message(btd_get_dbus_connection(), reply);
- dbus_message_unref(folder->msg);
- folder->msg = NULL;
- }
- void media_player_destroy(struct media_player *mp)
- {
- DBG("%s", mp->path);
- g_dbus_unregister_interface(btd_get_dbus_connection(), mp->path,
- MEDIA_PLAYER_INTERFACE);
- if (mp->track)
- g_hash_table_unref(mp->track);
- if (mp->settings)
- g_hash_table_unref(mp->settings);
- if (mp->process_id > 0)
- g_source_remove(mp->process_id);
- if (mp->scope)
- g_dbus_unregister_interface(btd_get_dbus_connection(),
- mp->path,
- MEDIA_FOLDER_INTERFACE);
- g_slist_free_full(mp->pending, g_free);
- g_slist_free_full(mp->folders, media_folder_destroy);
- g_timer_destroy(mp->progress);
- g_free(mp->cb);
- g_free(mp->status);
- g_free(mp->path);
- g_free(mp->device);
- g_free(mp->subtype);
- g_free(mp->type);
- g_free(mp->name);
- g_free(mp);
- }
- struct media_player *media_player_controller_create(const char *path,
- uint16_t id)
- {
- struct media_player *mp;
- mp = g_new0(struct media_player, 1);
- mp->device = g_strdup(path);
- mp->path = g_strdup_printf("%s/player%u", path, id);
- mp->settings = g_hash_table_new_full(g_str_hash, g_str_equal,
- g_free, g_free);
- mp->track = g_hash_table_new_full(g_str_hash, g_str_equal,
- g_free, g_free);
- mp->progress = g_timer_new();
- if (!g_dbus_register_interface(btd_get_dbus_connection(),
- mp->path, MEDIA_PLAYER_INTERFACE,
- media_player_methods,
- media_player_signals,
- media_player_properties, mp, NULL)) {
- error("D-Bus failed to register %s path", mp->path);
- media_player_destroy(mp);
- return NULL;
- }
- DBG("%s", mp->path);
- return mp;
- }
- const char *media_player_get_path(struct media_player *mp)
- {
- return mp->path;
- }
- void media_player_set_duration(struct media_player *mp, uint32_t duration)
- {
- char *value, *curval;
- DBG("%u", duration);
- /* Only update duration if track exists */
- if (g_hash_table_size(mp->track) == 0)
- return;
- /* Ignore if duration is already set */
- curval = g_hash_table_lookup(mp->track, "Duration");
- if (curval != NULL)
- return;
- value = g_strdup_printf("%u", duration);
- g_hash_table_replace(mp->track, g_strdup("Duration"), value);
- g_dbus_emit_property_changed(btd_get_dbus_connection(),
- mp->path, MEDIA_PLAYER_INTERFACE,
- "Track");
- }
- void media_player_set_position(struct media_player *mp, uint32_t position)
- {
- DBG("%u", position);
- /* Only update duration if track exists */
- if (g_hash_table_size(mp->track) == 0)
- return;
- mp->position = position;
- g_timer_start(mp->progress);
- g_dbus_emit_property_changed(btd_get_dbus_connection(), mp->path,
- MEDIA_PLAYER_INTERFACE, "Position");
- }
- void media_player_set_setting(struct media_player *mp, const char *key,
- const char *value)
- {
- char *curval;
- struct pending_req *p;
- DBG("%s: %s", key, value);
- if (strcasecmp(key, "Error") == 0) {
- p = g_slist_nth_data(mp->pending, 0);
- if (p == NULL)
- return;
- g_dbus_pending_property_error(p->id, ERROR_INTERFACE ".Failed",
- value);
- goto send;
- }
- curval = g_hash_table_lookup(mp->settings, key);
- if (g_strcmp0(curval, value) == 0)
- goto done;
- g_hash_table_replace(mp->settings, g_strdup(key), g_strdup(value));
- g_dbus_emit_property_changed(btd_get_dbus_connection(), mp->path,
- MEDIA_PLAYER_INTERFACE, key);
- done:
- p = find_pending(mp, key);
- if (p == NULL)
- return;
- if (strcasecmp(value, p->value) == 0)
- g_dbus_pending_property_success(p->id);
- else
- g_dbus_pending_property_error(p->id,
- ERROR_INTERFACE ".NotSupported",
- "Operation is not supported");
- send:
- mp->pending = g_slist_remove(mp->pending, p);
- g_free(p);
- return;
- }
- const char *media_player_get_status(struct media_player *mp)
- {
- return mp->status;
- }
- void media_player_set_status(struct media_player *mp, const char *status)
- {
- DBG("%s", status);
- if (g_strcmp0(mp->status, status) == 0)
- return;
- g_free(mp->status);
- mp->status = g_strdup(status);
- g_dbus_emit_property_changed(btd_get_dbus_connection(), mp->path,
- MEDIA_PLAYER_INTERFACE, "Status");
- mp->position = media_player_get_position(mp);
- g_timer_start(mp->progress);
- }
- static gboolean process_metadata_changed(void *user_data)
- {
- struct media_player *mp = user_data;
- const char *item;
- mp->process_id = 0;
- g_dbus_emit_property_changed(btd_get_dbus_connection(),
- mp->path, MEDIA_PLAYER_INTERFACE,
- "Track");
- item = g_hash_table_lookup(mp->track, "Item");
- if (item == NULL)
- return FALSE;
- g_dbus_emit_property_changed(btd_get_dbus_connection(),
- item, MEDIA_ITEM_INTERFACE,
- "Metadata");
- return FALSE;
- }
- void media_player_set_metadata(struct media_player *mp,
- struct media_item *item, const char *key,
- void *data, size_t len)
- {
- char *value, *curval;
- value = g_strndup(data, len);
- DBG("%s: %s", key, value);
- curval = g_hash_table_lookup(mp->track, key);
- if (g_strcmp0(curval, value) == 0) {
- g_free(value);
- return;
- }
- if (mp->process_id == 0) {
- g_hash_table_remove_all(mp->track);
- mp->process_id = g_idle_add(process_metadata_changed, mp);
- }
- g_hash_table_replace(mp->track, g_strdup(key), value);
- }
- void media_player_set_type(struct media_player *mp, const char *type)
- {
- if (g_strcmp0(mp->type, type) == 0)
- return;
- DBG("%s", type);
- mp->type = g_strdup(type);
- g_dbus_emit_property_changed(btd_get_dbus_connection(),
- mp->path, MEDIA_PLAYER_INTERFACE,
- "Type");
- }
- void media_player_set_subtype(struct media_player *mp, const char *subtype)
- {
- if (g_strcmp0(mp->subtype, subtype) == 0)
- return;
- DBG("%s", subtype);
- mp->subtype = g_strdup(subtype);
- g_dbus_emit_property_changed(btd_get_dbus_connection(),
- mp->path, MEDIA_PLAYER_INTERFACE,
- "Subtype");
- }
- void media_player_set_name(struct media_player *mp, const char *name)
- {
- if (g_strcmp0(mp->name, name) == 0)
- return;
- DBG("%s", name);
- mp->name = g_strdup(name);
- g_dbus_emit_property_changed(btd_get_dbus_connection(),
- mp->path, MEDIA_PLAYER_INTERFACE,
- "Name");
- }
- void media_player_set_browsable(struct media_player *mp, bool enabled)
- {
- if (mp->browsable == enabled)
- return;
- DBG("%s", enabled ? "true" : "false");
- mp->browsable = enabled;
- g_dbus_emit_property_changed(btd_get_dbus_connection(),
- mp->path, MEDIA_PLAYER_INTERFACE,
- "Browsable");
- }
- bool media_player_get_browsable(struct media_player *mp)
- {
- return mp->browsable;
- }
- void media_player_set_searchable(struct media_player *mp, bool enabled)
- {
- if (mp->searchable == enabled)
- return;
- DBG("%s", enabled ? "true" : "false");
- mp->searchable = enabled;
- g_dbus_emit_property_changed(btd_get_dbus_connection(),
- mp->path, MEDIA_PLAYER_INTERFACE,
- "Searchable");
- }
- void media_player_set_folder(struct media_player *mp, const char *name,
- uint32_t number_of_items)
- {
- struct media_folder *folder;
- DBG("%s number of items %u", name, number_of_items);
- folder = media_player_find_folder(mp, name);
- if (folder == NULL) {
- error("Unknown folder: %s", name);
- return;
- }
- folder->number_of_items = number_of_items;
- media_player_set_scope(mp, folder);
- }
- void media_player_set_playlist(struct media_player *mp, const char *name)
- {
- struct media_folder *folder;
- DBG("%s", name);
- folder = media_player_find_folder(mp, name);
- if (folder == NULL) {
- error("Unknown folder: %s", name);
- return;
- }
- mp->playlist = folder;
- g_dbus_emit_property_changed(btd_get_dbus_connection(), mp->path,
- MEDIA_PLAYER_INTERFACE, "Playlist");
- }
- static struct media_item *media_folder_find_item(struct media_folder *folder,
- uint64_t uid)
- {
- GSList *l;
- if (uid == 0)
- return NULL;
- for (l = folder->items; l; l = l->next) {
- struct media_item *item = l->data;
- if (item->uid == uid)
- return item;
- }
- return NULL;
- }
- static DBusMessage *media_item_play(DBusConnection *conn, DBusMessage *msg,
- void *data)
- {
- struct media_item *item = data;
- struct media_player *mp = item->player;
- struct media_folder *folder = mp->scope;
- struct player_callback *cb = mp->cb;
- const char *path;
- int err;
- if (!item->playable || !cb->cbs->play_item)
- return btd_error_not_supported(msg);
- if (folder->msg)
- return btd_error_failed(msg, strerror(EBUSY));
- path = mp->search && folder == mp->search ? "/Search" : item->path;
- err = cb->cbs->play_item(mp, path, item->uid, cb->user_data);
- if (err < 0)
- return btd_error_failed(msg, strerror(-err));
- folder->msg = dbus_message_ref(msg);
- return NULL;
- }
- static DBusMessage *media_item_add_to_nowplaying(DBusConnection *conn,
- DBusMessage *msg, void *data)
- {
- struct media_item *item = data;
- struct media_player *mp = item->player;
- struct player_callback *cb = mp->cb;
- int err;
- if (!item->playable || !cb->cbs->play_item)
- return btd_error_not_supported(msg);
- err = cb->cbs->add_to_nowplaying(mp, item->path, item->uid,
- cb->user_data);
- if (err < 0)
- return btd_error_failed(msg, strerror(-err));
- return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
- }
- static gboolean get_player(const GDBusPropertyTable *property,
- DBusMessageIter *iter, void *data)
- {
- struct media_item *item = data;
- dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH,
- &item->player->path);
- return TRUE;
- }
- static gboolean item_name_exists(const GDBusPropertyTable *property,
- void *data)
- {
- struct media_item *item = data;
- return item->name != NULL;
- }
- static gboolean get_item_name(const GDBusPropertyTable *property,
- DBusMessageIter *iter, void *data)
- {
- struct media_item *item = data;
- if (item->name == NULL)
- return FALSE;
- dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &item->name);
- return TRUE;
- }
- static const char *type_to_string(uint8_t type)
- {
- switch (type) {
- case PLAYER_ITEM_TYPE_AUDIO:
- return "audio";
- case PLAYER_ITEM_TYPE_VIDEO:
- return "video";
- case PLAYER_ITEM_TYPE_FOLDER:
- return "folder";
- }
- return NULL;
- }
- static gboolean get_item_type(const GDBusPropertyTable *property,
- DBusMessageIter *iter, void *data)
- {
- struct media_item *item = data;
- const char *string;
- string = type_to_string(item->type);
- dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &string);
- return TRUE;
- }
- static gboolean get_playable(const GDBusPropertyTable *property,
- DBusMessageIter *iter, void *data)
- {
- struct media_item *item = data;
- dbus_bool_t value;
- value = item->playable;
- dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &value);
- return TRUE;
- }
- static const char *folder_type_to_string(uint8_t type)
- {
- switch (type) {
- case PLAYER_FOLDER_TYPE_MIXED:
- return "mixed";
- case PLAYER_FOLDER_TYPE_TITLES:
- return "titles";
- case PLAYER_FOLDER_TYPE_ALBUMS:
- return "albums";
- case PLAYER_FOLDER_TYPE_ARTISTS:
- return "artists";
- case PLAYER_FOLDER_TYPE_GENRES:
- return "genres";
- case PLAYER_FOLDER_TYPE_PLAYLISTS:
- return "playlists";
- case PLAYER_FOLDER_TYPE_YEARS:
- return "years";
- }
- return NULL;
- }
- static gboolean folder_type_exists(const GDBusPropertyTable *property,
- void *data)
- {
- struct media_item *item = data;
- return folder_type_to_string(item->folder_type) != NULL;
- }
- static gboolean get_folder_type(const GDBusPropertyTable *property,
- DBusMessageIter *iter, void *data)
- {
- struct media_item *item = data;
- const char *string;
- string = folder_type_to_string(item->folder_type);
- if (string == NULL)
- return FALSE;
- dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &string);
- return TRUE;
- }
- static gboolean metadata_exists(const GDBusPropertyTable *property, void *data)
- {
- struct media_item *item = data;
- return item->metadata != NULL;
- }
- static void append_metadata(void *key, void *value, void *user_data)
- {
- DBusMessageIter *dict = user_data;
- const char *strkey = key;
- if (strcasecmp(strkey, "Item") == 0)
- return;
- if (strcasecmp(strkey, "Duration") == 0 ||
- strcasecmp(strkey, "TrackNumber") == 0 ||
- strcasecmp(strkey, "NumberOfTracks") == 0) {
- uint32_t num = atoi(value);
- dict_append_entry(dict, key, DBUS_TYPE_UINT32, &num);
- } else {
- dict_append_entry(dict, key, DBUS_TYPE_STRING, &value);
- }
- }
- static gboolean get_metadata(const GDBusPropertyTable *property,
- DBusMessageIter *iter, void *data)
- {
- struct media_item *item = data;
- DBusMessageIter dict;
- dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
- DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
- DBUS_TYPE_STRING_AS_STRING
- DBUS_TYPE_VARIANT_AS_STRING
- DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
- &dict);
- if (g_hash_table_size(item->metadata) > 0)
- g_hash_table_foreach(item->metadata, append_metadata, &dict);
- else if (item->name != NULL)
- dict_append_entry(&dict, "Title", DBUS_TYPE_STRING,
- &item->name);
- dbus_message_iter_close_container(iter, &dict);
- return TRUE;
- }
- static const GDBusMethodTable media_item_methods[] = {
- { GDBUS_ASYNC_METHOD("Play", NULL, NULL, media_item_play) },
- { GDBUS_METHOD("AddtoNowPlaying", NULL, NULL,
- media_item_add_to_nowplaying) },
- { }
- };
- static const GDBusPropertyTable media_item_properties[] = {
- { "Player", "o", get_player, NULL, NULL },
- { "Name", "s", get_item_name, NULL, item_name_exists },
- { "Type", "s", get_item_type, NULL, NULL },
- { "FolderType", "s", get_folder_type, NULL, folder_type_exists },
- { "Playable", "b", get_playable, NULL, NULL },
- { "Metadata", "a{sv}", get_metadata, NULL, metadata_exists },
- { }
- };
- void media_player_play_item_complete(struct media_player *mp, int err)
- {
- struct media_folder *folder = mp->scope;
- DBusMessage *reply;
- if (folder == NULL || folder->msg == NULL)
- return;
- if (err < 0) {
- reply = btd_error_failed(folder->msg, strerror(-err));
- goto done;
- }
- reply = g_dbus_create_reply(folder->msg, DBUS_TYPE_INVALID);
- done:
- g_dbus_send_message(btd_get_dbus_connection(), reply);
- dbus_message_unref(folder->msg);
- folder->msg = NULL;
- }
- void media_item_set_playable(struct media_item *item, bool value)
- {
- if (item->playable == value)
- return;
- item->playable = value;
- g_dbus_emit_property_changed(btd_get_dbus_connection(), item->path,
- MEDIA_ITEM_INTERFACE, "Playable");
- }
- static struct media_item *media_folder_create_item(struct media_player *mp,
- struct media_folder *folder,
- const char *name,
- player_item_type_t type,
- uint64_t uid)
- {
- struct media_item *item;
- const char *strtype;
- item = media_folder_find_item(folder, uid);
- if (item != NULL)
- return item;
- strtype = type_to_string(type);
- if (strtype == NULL)
- return NULL;
- DBG("%s type %s uid %" PRIu64 "", name, strtype, uid);
- item = g_new0(struct media_item, 1);
- item->player = mp;
- item->uid = uid;
- if (!uid && name[0] == '/')
- item->path = g_strdup_printf("%s%s", mp->path, name);
- else
- item->path = g_strdup_printf("%s/item%" PRIu64 "",
- folder->item->path, uid);
- item->name = g_strdup(name);
- item->type = type;
- item->folder_type = PLAYER_FOLDER_TYPE_INVALID;
- if (!g_dbus_register_interface(btd_get_dbus_connection(),
- item->path, MEDIA_ITEM_INTERFACE,
- media_item_methods,
- NULL,
- media_item_properties, item, NULL)) {
- error("D-Bus failed to register %s on %s path",
- MEDIA_ITEM_INTERFACE, item->path);
- media_item_free(item);
- return NULL;
- }
- if (type != PLAYER_ITEM_TYPE_FOLDER) {
- folder->items = g_slist_prepend(folder->items, item);
- item->metadata = g_hash_table_new_full(g_str_hash, g_str_equal,
- g_free, g_free);
- }
- DBG("%s", item->path);
- return item;
- }
- struct media_item *media_player_create_item(struct media_player *mp,
- const char *name,
- player_item_type_t type,
- uint64_t uid)
- {
- return media_folder_create_item(mp, mp->scope, name, type, uid);
- }
- struct media_item *media_player_create_folder(struct media_player *mp,
- const char *name,
- player_folder_type_t type,
- uint64_t uid)
- {
- struct media_folder *folder;
- struct media_item *item;
- if (uid > 0)
- folder = media_player_find_folder_by_uid(mp, uid);
- else
- folder = media_player_find_folder(mp, name);
- if (folder != NULL)
- return folder->item;
- if (uid > 0)
- item = media_player_create_subfolder(mp, name, uid);
- else
- item = media_player_create_item(mp, name,
- PLAYER_ITEM_TYPE_FOLDER, uid);
- if (item == NULL)
- return NULL;
- folder = g_new0(struct media_folder, 1);
- folder->item = item;
- item->folder_type = type;
- if (mp->folder != NULL)
- goto done;
- mp->folder = folder;
- done:
- if (uid > 0) {
- folder->parent = mp->folder;
- mp->folder->subfolders = g_slist_prepend(
- mp->folder->subfolders,
- folder);
- } else
- mp->folders = g_slist_prepend(mp->folders, folder);
- return item;
- }
- void media_player_set_callbacks(struct media_player *mp,
- const struct media_player_callback *cbs,
- void *user_data)
- {
- struct player_callback *cb;
- if (mp->cb)
- g_free(mp->cb);
- cb = g_new0(struct player_callback, 1);
- cb->cbs = cbs;
- cb->user_data = user_data;
- mp->cb = cb;
- }
- struct media_item *media_player_set_playlist_item(struct media_player *mp,
- uint64_t uid)
- {
- struct media_folder *folder = mp->playlist;
- struct media_item *item;
- DBG("%" PRIu64 "", uid);
- if (folder == NULL || uid == 0)
- return NULL;
- item = media_folder_create_item(mp, folder, NULL,
- PLAYER_ITEM_TYPE_AUDIO, uid);
- if (item == NULL)
- return NULL;
- media_item_set_playable(item, true);
- if (mp->track != item->metadata) {
- g_hash_table_unref(mp->track);
- mp->track = g_hash_table_ref(item->metadata);
- }
- if (item == g_hash_table_lookup(mp->track, "Item"))
- return item;
- if (mp->process_id == 0) {
- g_hash_table_remove_all(mp->track);
- mp->process_id = g_idle_add(process_metadata_changed, mp);
- }
- g_hash_table_replace(mp->track, g_strdup("Item"),
- g_strdup(item->path));
- return item;
- }
|