player.c 46 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /*
  3. *
  4. * BlueZ - Bluetooth protocol stack for Linux
  5. *
  6. * Copyright (C) 2006-2007 Nokia Corporation
  7. * Copyright (C) 2004-2009 Marcel Holtmann <marcel@holtmann.org>
  8. * Copyright (C) 2012-2012 Intel Corporation
  9. *
  10. */
  11. #ifdef HAVE_CONFIG_H
  12. #include <config.h>
  13. #endif
  14. #define _GNU_SOURCE
  15. #include <stdlib.h>
  16. #include <stdint.h>
  17. #include <inttypes.h>
  18. #include <stdbool.h>
  19. #include <errno.h>
  20. #include <unistd.h>
  21. #include <string.h>
  22. #include <glib.h>
  23. #include <dbus/dbus.h>
  24. #include "gdbus/gdbus.h"
  25. #include "src/log.h"
  26. #include "src/dbus-common.h"
  27. #include "src/error.h"
  28. #include "player.h"
  29. #define MEDIA_PLAYER_INTERFACE "org.bluez.MediaPlayer1"
  30. #define MEDIA_FOLDER_INTERFACE "org.bluez.MediaFolder1"
  31. #define MEDIA_ITEM_INTERFACE "org.bluez.MediaItem1"
  32. struct player_callback {
  33. const struct media_player_callback *cbs;
  34. void *user_data;
  35. };
  36. struct pending_req {
  37. GDBusPendingPropertySet id;
  38. const char *key;
  39. const char *value;
  40. };
  41. struct media_item {
  42. struct media_player *player;
  43. char *path; /* Item object path */
  44. char *name; /* Item name */
  45. player_item_type_t type; /* Item type */
  46. player_folder_type_t folder_type; /* Folder type */
  47. bool playable; /* Item playable flag */
  48. uint64_t uid; /* Item uid */
  49. GHashTable *metadata; /* Item metadata */
  50. };
  51. struct media_folder {
  52. struct media_folder *parent;
  53. struct media_item *item; /* Folder item */
  54. uint32_t number_of_items;/* Number of items */
  55. GSList *subfolders;
  56. GSList *items;
  57. DBusMessage *msg;
  58. };
  59. struct media_player {
  60. char *device; /* Device path */
  61. char *name; /* Player name */
  62. char *type; /* Player type */
  63. char *subtype; /* Player subtype */
  64. bool browsable; /* Player browsing feature */
  65. bool searchable; /* Player searching feature */
  66. struct media_folder *scope; /* Player current scope */
  67. struct media_folder *folder; /* Player current folder */
  68. struct media_folder *search; /* Player search folder */
  69. struct media_folder *playlist; /* Player current playlist */
  70. char *path; /* Player object path */
  71. GHashTable *settings; /* Player settings */
  72. GHashTable *track; /* Player current track */
  73. char *status;
  74. uint32_t position;
  75. GTimer *progress;
  76. guint process_id;
  77. struct player_callback *cb;
  78. GSList *pending;
  79. GSList *folders;
  80. };
  81. static void append_track(void *key, void *value, void *user_data)
  82. {
  83. DBusMessageIter *dict = user_data;
  84. const char *strkey = key;
  85. if (strcasecmp(strkey, "Duration") == 0 ||
  86. strcasecmp(strkey, "TrackNumber") == 0 ||
  87. strcasecmp(strkey, "NumberOfTracks") == 0) {
  88. uint32_t num = atoi(value);
  89. dict_append_entry(dict, key, DBUS_TYPE_UINT32, &num);
  90. } else if (strcasecmp(strkey, "Item") == 0) {
  91. dict_append_entry(dict, key, DBUS_TYPE_OBJECT_PATH, &value);
  92. } else {
  93. dict_append_entry(dict, key, DBUS_TYPE_STRING, &value);
  94. }
  95. }
  96. static struct pending_req *find_pending(struct media_player *mp,
  97. const char *key)
  98. {
  99. GSList *l;
  100. for (l = mp->pending; l; l = l->next) {
  101. struct pending_req *p = l->data;
  102. if (strcasecmp(key, p->key) == 0)
  103. return p;
  104. }
  105. return NULL;
  106. }
  107. static struct pending_req *pending_new(GDBusPendingPropertySet id,
  108. const char *key, const char *value)
  109. {
  110. struct pending_req *p;
  111. p = g_new0(struct pending_req, 1);
  112. p->id = id;
  113. p->key = key;
  114. p->value = value;
  115. return p;
  116. }
  117. static uint32_t media_player_get_position(struct media_player *mp)
  118. {
  119. double timedelta;
  120. uint32_t sec, msec;
  121. if (g_strcmp0(mp->status, "playing") != 0 ||
  122. mp->position == UINT32_MAX)
  123. return mp->position;
  124. timedelta = g_timer_elapsed(mp->progress, NULL);
  125. sec = (uint32_t) timedelta;
  126. msec = (uint32_t) ((timedelta - sec) * 1000);
  127. return mp->position + sec * 1000 + msec;
  128. }
  129. static gboolean get_position(const GDBusPropertyTable *property,
  130. DBusMessageIter *iter, void *data)
  131. {
  132. struct media_player *mp = data;
  133. uint32_t position;
  134. position = media_player_get_position(mp);
  135. dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32, &position);
  136. return TRUE;
  137. }
  138. static gboolean status_exists(const GDBusPropertyTable *property, void *data)
  139. {
  140. struct media_player *mp = data;
  141. return mp->status != NULL;
  142. }
  143. static gboolean get_status(const GDBusPropertyTable *property,
  144. DBusMessageIter *iter, void *data)
  145. {
  146. struct media_player *mp = data;
  147. if (mp->status == NULL)
  148. return FALSE;
  149. dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &mp->status);
  150. return TRUE;
  151. }
  152. static gboolean setting_exists(const GDBusPropertyTable *property, void *data)
  153. {
  154. struct media_player *mp = data;
  155. const char *value;
  156. value = g_hash_table_lookup(mp->settings, property->name);
  157. return value ? TRUE : FALSE;
  158. }
  159. static gboolean get_setting(const GDBusPropertyTable *property,
  160. DBusMessageIter *iter, void *data)
  161. {
  162. struct media_player *mp = data;
  163. const char *value;
  164. value = g_hash_table_lookup(mp->settings, property->name);
  165. if (value == NULL)
  166. return FALSE;
  167. dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &value);
  168. return TRUE;
  169. }
  170. static void player_set_setting(struct media_player *mp,
  171. GDBusPendingPropertySet id,
  172. const char *key, const char *value)
  173. {
  174. struct player_callback *cb = mp->cb;
  175. struct pending_req *p;
  176. if (cb == NULL || cb->cbs->set_setting == NULL) {
  177. g_dbus_pending_property_error(id,
  178. ERROR_INTERFACE ".NotSupported",
  179. "Operation is not supported");
  180. return;
  181. }
  182. p = find_pending(mp, key);
  183. if (p != NULL) {
  184. g_dbus_pending_property_error(id,
  185. ERROR_INTERFACE ".InProgress",
  186. "Operation already in progress");
  187. return;
  188. }
  189. if (!cb->cbs->set_setting(mp, key, value, cb->user_data)) {
  190. g_dbus_pending_property_error(id,
  191. ERROR_INTERFACE ".InvalidArguments",
  192. "Invalid arguments in method call");
  193. return;
  194. }
  195. p = pending_new(id, key, value);
  196. mp->pending = g_slist_append(mp->pending, p);
  197. }
  198. static void set_setting(const GDBusPropertyTable *property,
  199. DBusMessageIter *iter, GDBusPendingPropertySet id,
  200. void *data)
  201. {
  202. struct media_player *mp = data;
  203. const char *value, *current;
  204. if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING) {
  205. g_dbus_pending_property_error(id,
  206. ERROR_INTERFACE ".InvalidArguments",
  207. "Invalid arguments in method call");
  208. return;
  209. }
  210. dbus_message_iter_get_basic(iter, &value);
  211. current = g_hash_table_lookup(mp->settings, property->name);
  212. if (g_strcmp0(current, value) == 0) {
  213. g_dbus_pending_property_success(id);
  214. return;
  215. }
  216. player_set_setting(mp, id, property->name, value);
  217. }
  218. static gboolean track_exists(const GDBusPropertyTable *property, void *data)
  219. {
  220. struct media_player *mp = data;
  221. return g_hash_table_size(mp->track) != 0;
  222. }
  223. static gboolean get_track(const GDBusPropertyTable *property,
  224. DBusMessageIter *iter, void *data)
  225. {
  226. struct media_player *mp = data;
  227. DBusMessageIter dict;
  228. dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
  229. DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
  230. DBUS_TYPE_STRING_AS_STRING
  231. DBUS_TYPE_VARIANT_AS_STRING
  232. DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
  233. &dict);
  234. g_hash_table_foreach(mp->track, append_track, &dict);
  235. dbus_message_iter_close_container(iter, &dict);
  236. return TRUE;
  237. }
  238. static gboolean get_device(const GDBusPropertyTable *property,
  239. DBusMessageIter *iter, void *data)
  240. {
  241. struct media_player *mp = data;
  242. dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH,
  243. &mp->device);
  244. return TRUE;
  245. }
  246. static gboolean name_exists(const GDBusPropertyTable *property, void *data)
  247. {
  248. struct media_player *mp = data;
  249. return mp->name != NULL;
  250. }
  251. static gboolean get_name(const GDBusPropertyTable *property,
  252. DBusMessageIter *iter, void *data)
  253. {
  254. struct media_player *mp = data;
  255. if (mp->name == NULL)
  256. return FALSE;
  257. dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &mp->name);
  258. return TRUE;
  259. }
  260. static gboolean type_exists(const GDBusPropertyTable *property, void *data)
  261. {
  262. struct media_player *mp = data;
  263. return mp->type != NULL;
  264. }
  265. static gboolean get_type(const GDBusPropertyTable *property,
  266. DBusMessageIter *iter, void *data)
  267. {
  268. struct media_player *mp = data;
  269. if (mp->type == NULL)
  270. return FALSE;
  271. dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &mp->type);
  272. return TRUE;
  273. }
  274. static gboolean subtype_exists(const GDBusPropertyTable *property, void *data)
  275. {
  276. struct media_player *mp = data;
  277. return mp->subtype != NULL;
  278. }
  279. static gboolean get_subtype(const GDBusPropertyTable *property,
  280. DBusMessageIter *iter, void *data)
  281. {
  282. struct media_player *mp = data;
  283. if (mp->subtype == NULL)
  284. return FALSE;
  285. dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &mp->subtype);
  286. return TRUE;
  287. }
  288. static gboolean browsable_exists(const GDBusPropertyTable *property, void *data)
  289. {
  290. struct media_player *mp = data;
  291. return mp->folder != NULL;
  292. }
  293. static gboolean get_browsable(const GDBusPropertyTable *property,
  294. DBusMessageIter *iter, void *data)
  295. {
  296. struct media_player *mp = data;
  297. dbus_bool_t value;
  298. if (mp->folder == NULL)
  299. return FALSE;
  300. value = mp->browsable;
  301. dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &value);
  302. return TRUE;
  303. }
  304. static gboolean searchable_exists(const GDBusPropertyTable *property,
  305. void *data)
  306. {
  307. struct media_player *mp = data;
  308. return mp->folder != NULL;
  309. }
  310. static gboolean get_searchable(const GDBusPropertyTable *property,
  311. DBusMessageIter *iter, void *data)
  312. {
  313. struct media_player *mp = data;
  314. dbus_bool_t value;
  315. if (mp->folder == NULL)
  316. return FALSE;
  317. value = mp->searchable;
  318. dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &value);
  319. return TRUE;
  320. }
  321. static gboolean playlist_exists(const GDBusPropertyTable *property,
  322. void *data)
  323. {
  324. struct media_player *mp = data;
  325. return mp->playlist != NULL;
  326. }
  327. static gboolean get_playlist(const GDBusPropertyTable *property,
  328. DBusMessageIter *iter, void *data)
  329. {
  330. struct media_player *mp = data;
  331. struct media_folder *playlist = mp->playlist;
  332. if (playlist == NULL)
  333. return FALSE;
  334. dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH,
  335. &playlist->item->path);
  336. return TRUE;
  337. }
  338. static DBusMessage *media_player_play(DBusConnection *conn, DBusMessage *msg,
  339. void *data)
  340. {
  341. struct media_player *mp = data;
  342. struct player_callback *cb = mp->cb;
  343. int err;
  344. if (cb->cbs->play == NULL)
  345. return btd_error_not_supported(msg);
  346. err = cb->cbs->play(mp, cb->user_data);
  347. if (err < 0)
  348. return btd_error_failed(msg, strerror(-err));
  349. return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
  350. }
  351. static DBusMessage *media_player_pause(DBusConnection *conn, DBusMessage *msg,
  352. void *data)
  353. {
  354. struct media_player *mp = data;
  355. struct player_callback *cb = mp->cb;
  356. int err;
  357. if (cb->cbs->pause == NULL)
  358. return btd_error_not_supported(msg);
  359. err = cb->cbs->pause(mp, cb->user_data);
  360. if (err < 0)
  361. return btd_error_failed(msg, strerror(-err));
  362. return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
  363. }
  364. static DBusMessage *media_player_stop(DBusConnection *conn, DBusMessage *msg,
  365. void *data)
  366. {
  367. struct media_player *mp = data;
  368. struct player_callback *cb = mp->cb;
  369. int err;
  370. if (cb->cbs->stop == NULL)
  371. return btd_error_not_supported(msg);
  372. err = cb->cbs->stop(mp, cb->user_data);
  373. if (err < 0)
  374. return btd_error_failed(msg, strerror(-err));
  375. return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
  376. }
  377. static DBusMessage *media_player_next(DBusConnection *conn, DBusMessage *msg,
  378. void *data)
  379. {
  380. struct media_player *mp = data;
  381. struct player_callback *cb = mp->cb;
  382. int err;
  383. if (cb->cbs->next == NULL)
  384. return btd_error_not_supported(msg);
  385. err = cb->cbs->next(mp, cb->user_data);
  386. if (err < 0)
  387. return btd_error_failed(msg, strerror(-err));
  388. return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
  389. }
  390. static DBusMessage *media_player_previous(DBusConnection *conn,
  391. DBusMessage *msg, void *data)
  392. {
  393. struct media_player *mp = data;
  394. struct player_callback *cb = mp->cb;
  395. int err;
  396. if (cb->cbs->previous == NULL)
  397. return btd_error_not_supported(msg);
  398. err = cb->cbs->previous(mp, cb->user_data);
  399. if (err < 0)
  400. return btd_error_failed(msg, strerror(-err));
  401. return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
  402. }
  403. static DBusMessage *media_player_fast_forward(DBusConnection *conn,
  404. DBusMessage *msg, void *data)
  405. {
  406. struct media_player *mp = data;
  407. struct player_callback *cb = mp->cb;
  408. int err;
  409. if (cb->cbs->fast_forward == NULL)
  410. return btd_error_not_supported(msg);
  411. err = cb->cbs->fast_forward(mp, cb->user_data);
  412. if (err < 0)
  413. return btd_error_failed(msg, strerror(-err));
  414. return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
  415. }
  416. static DBusMessage *media_player_rewind(DBusConnection *conn, DBusMessage *msg,
  417. void *data)
  418. {
  419. struct media_player *mp = data;
  420. struct player_callback *cb = mp->cb;
  421. int err;
  422. if (cb->cbs->rewind == NULL)
  423. return btd_error_not_supported(msg);
  424. err = cb->cbs->rewind(mp, cb->user_data);
  425. if (err < 0)
  426. return btd_error_failed(msg, strerror(-err));
  427. return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
  428. }
  429. static DBusMessage *media_player_press(DBusConnection *conn, DBusMessage *msg,
  430. void *data)
  431. {
  432. struct media_player *mp = data;
  433. struct player_callback *cb = mp->cb;
  434. int err;
  435. uint8_t avc_key;
  436. if (cb->cbs->press == NULL)
  437. return btd_error_not_supported(msg);
  438. if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_BYTE, &avc_key,
  439. DBUS_TYPE_INVALID))
  440. return btd_error_invalid_args(msg);
  441. err = cb->cbs->press(mp, avc_key, cb->user_data);
  442. if (err < 0)
  443. return btd_error_failed(msg, strerror(-err));
  444. return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
  445. }
  446. static DBusMessage *media_player_hold(DBusConnection *conn, DBusMessage *msg,
  447. void *data)
  448. {
  449. struct media_player *mp = data;
  450. struct player_callback *cb = mp->cb;
  451. int err;
  452. uint8_t avc_key;
  453. if (cb->cbs->hold == NULL)
  454. return btd_error_not_supported(msg);
  455. if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_BYTE, &avc_key,
  456. DBUS_TYPE_INVALID))
  457. return btd_error_invalid_args(msg);
  458. err = cb->cbs->hold(mp, avc_key, cb->user_data);
  459. if (err < 0)
  460. return btd_error_failed(msg, strerror(-err));
  461. return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
  462. }
  463. static DBusMessage *media_player_release(DBusConnection *conn, DBusMessage *msg,
  464. void *data)
  465. {
  466. struct media_player *mp = data;
  467. struct player_callback *cb = mp->cb;
  468. int err;
  469. if (cb->cbs->release == NULL)
  470. return btd_error_not_supported(msg);
  471. err = cb->cbs->release(mp, cb->user_data);
  472. if (err < 0)
  473. return btd_error_failed(msg, strerror(-err));
  474. return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
  475. }
  476. static void parse_folder_list(gpointer data, gpointer user_data)
  477. {
  478. struct media_item *item = data;
  479. DBusMessageIter *array = user_data;
  480. DBusMessageIter entry;
  481. dbus_message_iter_open_container(array, DBUS_TYPE_DICT_ENTRY, NULL,
  482. &entry);
  483. dbus_message_iter_append_basic(&entry, DBUS_TYPE_OBJECT_PATH,
  484. &item->path);
  485. g_dbus_get_properties(btd_get_dbus_connection(), item->path,
  486. MEDIA_ITEM_INTERFACE, &entry);
  487. dbus_message_iter_close_container(array, &entry);
  488. }
  489. void media_player_list_complete(struct media_player *mp, GSList *items,
  490. int err)
  491. {
  492. struct media_folder *folder = mp->scope;
  493. DBusMessage *reply;
  494. DBusMessageIter iter, array;
  495. if (folder == NULL || folder->msg == NULL)
  496. return;
  497. if (err < 0) {
  498. reply = btd_error_failed(folder->msg, strerror(-err));
  499. goto done;
  500. }
  501. reply = dbus_message_new_method_return(folder->msg);
  502. dbus_message_iter_init_append(reply, &iter);
  503. dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
  504. DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
  505. DBUS_TYPE_OBJECT_PATH_AS_STRING
  506. DBUS_TYPE_ARRAY_AS_STRING
  507. DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
  508. DBUS_TYPE_STRING_AS_STRING
  509. DBUS_TYPE_VARIANT_AS_STRING
  510. DBUS_DICT_ENTRY_END_CHAR_AS_STRING
  511. DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
  512. &array);
  513. g_slist_foreach(items, parse_folder_list, &array);
  514. dbus_message_iter_close_container(&iter, &array);
  515. done:
  516. g_dbus_send_message(btd_get_dbus_connection(), reply);
  517. dbus_message_unref(folder->msg);
  518. folder->msg = NULL;
  519. }
  520. static struct media_item *
  521. media_player_create_subfolder(struct media_player *mp, const char *name,
  522. uint64_t uid)
  523. {
  524. struct media_folder *folder = mp->scope;
  525. struct media_item *item;
  526. char *path;
  527. path = g_strdup_printf("%s/%s", folder->item->name, name);
  528. DBG("%s", path);
  529. item = media_player_create_item(mp, path, PLAYER_ITEM_TYPE_FOLDER,
  530. uid);
  531. g_free(path);
  532. return item;
  533. }
  534. void media_player_search_complete(struct media_player *mp, int ret)
  535. {
  536. struct media_folder *folder = mp->scope;
  537. struct media_folder *search = mp->search;
  538. DBusMessage *reply;
  539. if (folder == NULL || folder->msg == NULL)
  540. return;
  541. if (ret < 0) {
  542. reply = btd_error_failed(folder->msg, strerror(-ret));
  543. goto done;
  544. }
  545. if (search == NULL) {
  546. search = g_new0(struct media_folder, 1);
  547. search->item = media_player_create_subfolder(mp, "search", 0);
  548. mp->search = search;
  549. mp->folders = g_slist_prepend(mp->folders, search);
  550. }
  551. search->number_of_items = ret;
  552. reply = g_dbus_create_reply(folder->msg,
  553. DBUS_TYPE_OBJECT_PATH, &search->item->path,
  554. DBUS_TYPE_INVALID);
  555. done:
  556. g_dbus_send_message(btd_get_dbus_connection(), reply);
  557. dbus_message_unref(folder->msg);
  558. folder->msg = NULL;
  559. }
  560. void media_player_total_items_complete(struct media_player *mp,
  561. uint32_t num_of_items)
  562. {
  563. struct media_folder *folder = mp->scope;
  564. if (folder == NULL || folder->msg == NULL)
  565. return;
  566. if (folder->number_of_items != num_of_items) {
  567. folder->number_of_items = num_of_items;
  568. g_dbus_emit_property_changed(btd_get_dbus_connection(),
  569. mp->path, MEDIA_FOLDER_INTERFACE,
  570. "NumberOfItems");
  571. }
  572. }
  573. static const GDBusMethodTable media_player_methods[] = {
  574. { GDBUS_METHOD("Play", NULL, NULL, media_player_play) },
  575. { GDBUS_METHOD("Pause", NULL, NULL, media_player_pause) },
  576. { GDBUS_METHOD("Stop", NULL, NULL, media_player_stop) },
  577. { GDBUS_METHOD("Next", NULL, NULL, media_player_next) },
  578. { GDBUS_METHOD("Previous", NULL, NULL, media_player_previous) },
  579. { GDBUS_METHOD("FastForward", NULL, NULL, media_player_fast_forward) },
  580. { GDBUS_METHOD("Rewind", NULL, NULL, media_player_rewind) },
  581. { GDBUS_METHOD("Press", GDBUS_ARGS({"avc_key", "y"}), NULL,
  582. media_player_press) },
  583. { GDBUS_METHOD("Hold", GDBUS_ARGS({"avc_key", "y"}), NULL,
  584. media_player_hold) },
  585. { GDBUS_METHOD("Release", NULL, NULL, media_player_release) },
  586. { }
  587. };
  588. static const GDBusSignalTable media_player_signals[] = {
  589. { }
  590. };
  591. static const GDBusPropertyTable media_player_properties[] = {
  592. { "Name", "s", get_name, NULL, name_exists },
  593. { "Type", "s", get_type, NULL, type_exists },
  594. { "Subtype", "s", get_subtype, NULL, subtype_exists },
  595. { "Position", "u", get_position, NULL, NULL },
  596. { "Status", "s", get_status, NULL, status_exists },
  597. { "Equalizer", "s", get_setting, set_setting, setting_exists },
  598. { "Repeat", "s", get_setting, set_setting, setting_exists },
  599. { "Shuffle", "s", get_setting, set_setting, setting_exists },
  600. { "Scan", "s", get_setting, set_setting, setting_exists },
  601. { "Track", "a{sv}", get_track, NULL, track_exists },
  602. { "Device", "o", get_device, NULL, NULL },
  603. { "Browsable", "b", get_browsable, NULL, browsable_exists },
  604. { "Searchable", "b", get_searchable, NULL, searchable_exists },
  605. { "Playlist", "o", get_playlist, NULL, playlist_exists },
  606. { }
  607. };
  608. static DBusMessage *media_folder_search(DBusConnection *conn, DBusMessage *msg,
  609. void *data)
  610. {
  611. struct media_player *mp = data;
  612. struct media_folder *folder = mp->scope;
  613. struct player_callback *cb = mp->cb;
  614. DBusMessageIter iter;
  615. const char *string;
  616. int err;
  617. dbus_message_iter_init(msg, &iter);
  618. if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
  619. return btd_error_invalid_args(msg);
  620. dbus_message_iter_get_basic(&iter, &string);
  621. if (!mp->searchable || folder != mp->folder || !cb->cbs->search)
  622. return btd_error_not_supported(msg);
  623. if (folder->msg != NULL)
  624. return btd_error_failed(msg, strerror(EINVAL));
  625. err = cb->cbs->search(mp, string, cb->user_data);
  626. if (err < 0)
  627. return btd_error_failed(msg, strerror(-err));
  628. folder->msg = dbus_message_ref(msg);
  629. return NULL;
  630. }
  631. static int parse_filters(struct media_player *player, DBusMessageIter *iter,
  632. uint32_t *start, uint32_t *end)
  633. {
  634. struct media_folder *folder = player->scope;
  635. DBusMessageIter dict;
  636. int ctype;
  637. *start = 0;
  638. *end = folder->number_of_items ? folder->number_of_items - 1 :
  639. UINT32_MAX;
  640. ctype = dbus_message_iter_get_arg_type(iter);
  641. if (ctype != DBUS_TYPE_ARRAY)
  642. return FALSE;
  643. dbus_message_iter_recurse(iter, &dict);
  644. while ((ctype = dbus_message_iter_get_arg_type(&dict)) !=
  645. DBUS_TYPE_INVALID) {
  646. DBusMessageIter entry, var;
  647. const char *key;
  648. if (ctype != DBUS_TYPE_DICT_ENTRY)
  649. return -EINVAL;
  650. dbus_message_iter_recurse(&dict, &entry);
  651. if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
  652. return -EINVAL;
  653. dbus_message_iter_get_basic(&entry, &key);
  654. dbus_message_iter_next(&entry);
  655. if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_VARIANT)
  656. return -EINVAL;
  657. dbus_message_iter_recurse(&entry, &var);
  658. if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_UINT32)
  659. return -EINVAL;
  660. if (strcasecmp(key, "Start") == 0)
  661. dbus_message_iter_get_basic(&var, start);
  662. else if (strcasecmp(key, "End") == 0)
  663. dbus_message_iter_get_basic(&var, end);
  664. dbus_message_iter_next(&dict);
  665. }
  666. if (folder->number_of_items > 0 && *end > folder->number_of_items)
  667. *end = folder->number_of_items;
  668. return 0;
  669. }
  670. static DBusMessage *media_folder_list_items(DBusConnection *conn,
  671. DBusMessage *msg, void *data)
  672. {
  673. struct media_player *mp = data;
  674. struct media_folder *folder = mp->scope;
  675. struct player_callback *cb = mp->cb;
  676. DBusMessageIter iter;
  677. uint32_t start, end;
  678. int err;
  679. dbus_message_iter_init(msg, &iter);
  680. if (parse_filters(mp, &iter, &start, &end) < 0)
  681. return btd_error_invalid_args(msg);
  682. if (cb->cbs->list_items == NULL)
  683. return btd_error_not_supported(msg);
  684. if (folder->msg != NULL)
  685. return btd_error_failed(msg, strerror(EBUSY));
  686. err = cb->cbs->list_items(mp, folder->item->name, start, end,
  687. cb->user_data);
  688. if (err < 0)
  689. return btd_error_failed(msg, strerror(-err));
  690. folder->msg = dbus_message_ref(msg);
  691. return NULL;
  692. }
  693. static void media_item_free(struct media_item *item)
  694. {
  695. if (item->metadata != NULL)
  696. g_hash_table_unref(item->metadata);
  697. g_free(item->path);
  698. g_free(item->name);
  699. g_free(item);
  700. }
  701. static void media_item_destroy(void *data)
  702. {
  703. struct media_item *item = data;
  704. DBG("%s", item->path);
  705. g_dbus_unregister_interface(btd_get_dbus_connection(), item->path,
  706. MEDIA_ITEM_INTERFACE);
  707. media_item_free(item);
  708. }
  709. static void media_folder_destroy(void *data)
  710. {
  711. struct media_folder *folder = data;
  712. g_slist_free_full(folder->subfolders, media_folder_destroy);
  713. g_slist_free_full(folder->items, media_item_destroy);
  714. if (folder->msg != NULL)
  715. dbus_message_unref(folder->msg);
  716. media_item_destroy(folder->item);
  717. g_free(folder);
  718. }
  719. static void media_player_change_scope(struct media_player *mp,
  720. struct media_folder *folder)
  721. {
  722. struct player_callback *cb = mp->cb;
  723. int err;
  724. if (mp->scope == folder)
  725. return;
  726. DBG("%s", folder->item->name);
  727. /* Skip setting current folder if folder is current playlist/search */
  728. if (folder == mp->playlist || folder == mp->search)
  729. goto cleanup;
  730. mp->folder = folder;
  731. /* Skip item cleanup if scope is the current playlist */
  732. if (mp->scope == mp->playlist)
  733. goto done;
  734. cleanup:
  735. g_slist_free_full(mp->scope->items, media_item_destroy);
  736. mp->scope->items = NULL;
  737. /* Destroy search folder if it exists and is not being set as scope */
  738. if (mp->search != NULL && folder != mp->search) {
  739. mp->folders = g_slist_remove(mp->folders, mp->search);
  740. media_folder_destroy(mp->search);
  741. mp->search = NULL;
  742. }
  743. done:
  744. mp->scope = folder;
  745. if (cb->cbs->total_items) {
  746. err = cb->cbs->total_items(mp, folder->item->name,
  747. cb->user_data);
  748. if (err < 0)
  749. DBG("Failed to get total num of items");
  750. } else {
  751. g_dbus_emit_property_changed(btd_get_dbus_connection(),
  752. mp->path, MEDIA_FOLDER_INTERFACE,
  753. "NumberOfItems");
  754. }
  755. g_dbus_emit_property_changed(btd_get_dbus_connection(), mp->path,
  756. MEDIA_FOLDER_INTERFACE, "Name");
  757. }
  758. static struct media_folder *find_folder(GSList *folders, const char *pattern)
  759. {
  760. GSList *l;
  761. for (l = folders; l; l = l->next) {
  762. struct media_folder *folder = l->data;
  763. if (g_str_equal(folder->item->name, pattern))
  764. return folder;
  765. if (g_str_equal(folder->item->path, pattern))
  766. return folder;
  767. folder = find_folder(folder->subfolders, pattern);
  768. if (folder != NULL)
  769. return folder;
  770. }
  771. return NULL;
  772. }
  773. static struct media_folder *media_player_find_folder(struct media_player *mp,
  774. const char *pattern)
  775. {
  776. return find_folder(mp->folders, pattern);
  777. }
  778. static DBusMessage *media_folder_change_folder(DBusConnection *conn,
  779. DBusMessage *msg, void *data)
  780. {
  781. struct media_player *mp = data;
  782. struct media_folder *folder = mp->scope;
  783. struct player_callback *cb = mp->cb;
  784. const char *path;
  785. int err;
  786. if (!dbus_message_get_args(msg, NULL,
  787. DBUS_TYPE_OBJECT_PATH, &path,
  788. DBUS_TYPE_INVALID))
  789. return btd_error_invalid_args(msg);
  790. if (folder->msg != NULL)
  791. return btd_error_failed(msg, strerror(EBUSY));
  792. folder = media_player_find_folder(mp, path);
  793. if (folder == NULL)
  794. return btd_error_invalid_args(msg);
  795. if (mp->scope == folder)
  796. return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
  797. if (folder == mp->playlist || folder == mp->folder ||
  798. folder == mp->search) {
  799. media_player_change_scope(mp, folder);
  800. return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
  801. }
  802. /*
  803. * ChangePath can only navigate one level up/down so check if folder
  804. * is direct child or parent of the current folder otherwise fail.
  805. */
  806. if (!g_slist_find(mp->folder->subfolders, folder) &&
  807. !g_slist_find(folder->subfolders, mp->folder))
  808. return btd_error_invalid_args(msg);
  809. if (cb->cbs->change_folder == NULL)
  810. return btd_error_not_supported(msg);
  811. err = cb->cbs->change_folder(mp, folder->item->name, folder->item->uid,
  812. cb->user_data);
  813. if (err < 0)
  814. return btd_error_failed(msg, strerror(-err));
  815. mp->scope->msg = dbus_message_ref(msg);
  816. return NULL;
  817. }
  818. static gboolean folder_name_exists(const GDBusPropertyTable *property,
  819. void *data)
  820. {
  821. struct media_player *mp = data;
  822. struct media_folder *folder = mp->scope;
  823. if (folder == NULL || folder->item == NULL)
  824. return FALSE;
  825. return folder->item->name != NULL;
  826. }
  827. static gboolean get_folder_name(const GDBusPropertyTable *property,
  828. DBusMessageIter *iter, void *data)
  829. {
  830. struct media_player *mp = data;
  831. struct media_folder *folder = mp->scope;
  832. if (folder == NULL || folder->item == NULL)
  833. return FALSE;
  834. DBG("%s", folder->item->name);
  835. dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
  836. &folder->item->name);
  837. return TRUE;
  838. }
  839. static gboolean items_exists(const GDBusPropertyTable *property, void *data)
  840. {
  841. struct media_player *mp = data;
  842. return mp->scope != NULL;
  843. }
  844. static gboolean get_items(const GDBusPropertyTable *property,
  845. DBusMessageIter *iter, void *data)
  846. {
  847. struct media_player *mp = data;
  848. struct media_folder *folder = mp->scope;
  849. if (folder == NULL)
  850. return FALSE;
  851. DBG("%u", folder->number_of_items);
  852. dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32,
  853. &folder->number_of_items);
  854. return TRUE;
  855. }
  856. static const GDBusMethodTable media_folder_methods[] = {
  857. { GDBUS_ASYNC_METHOD("Search",
  858. GDBUS_ARGS({ "string", "s" }, { "filter", "a{sv}" }),
  859. GDBUS_ARGS({ "folder", "o" }),
  860. media_folder_search) },
  861. { GDBUS_ASYNC_METHOD("ListItems",
  862. GDBUS_ARGS({ "filter", "a{sv}" }),
  863. GDBUS_ARGS({ "items", "a{oa{sv}}" }),
  864. media_folder_list_items) },
  865. { GDBUS_ASYNC_METHOD("ChangeFolder",
  866. GDBUS_ARGS({ "folder", "o" }), NULL,
  867. media_folder_change_folder) },
  868. { }
  869. };
  870. static const GDBusPropertyTable media_folder_properties[] = {
  871. { "Name", "s", get_folder_name, NULL, folder_name_exists },
  872. { "NumberOfItems", "u", get_items, NULL, items_exists },
  873. { }
  874. };
  875. static void media_player_set_scope(struct media_player *mp,
  876. struct media_folder *folder)
  877. {
  878. if (mp->scope == NULL) {
  879. if (!g_dbus_register_interface(btd_get_dbus_connection(),
  880. mp->path, MEDIA_FOLDER_INTERFACE,
  881. media_folder_methods,
  882. NULL,
  883. media_folder_properties, mp, NULL)) {
  884. error("D-Bus failed to register %s on %s path",
  885. MEDIA_FOLDER_INTERFACE, mp->path);
  886. return;
  887. }
  888. mp->scope = folder;
  889. return;
  890. }
  891. return media_player_change_scope(mp, folder);
  892. }
  893. static struct media_folder *
  894. media_player_find_folder_by_uid(struct media_player *mp, uint64_t uid)
  895. {
  896. struct media_folder *folder = mp->scope;
  897. struct media_folder *parent = folder->parent;
  898. GSList *l;
  899. if (parent && parent->item->uid == uid)
  900. return parent;
  901. for (l = folder->subfolders; l; l = l->next) {
  902. struct media_folder *folder = l->data;
  903. if (folder->item->uid == uid)
  904. return folder;
  905. }
  906. return NULL;
  907. }
  908. static void media_player_set_folder_by_uid(struct media_player *mp,
  909. uint64_t uid, uint32_t number_of_items)
  910. {
  911. struct media_folder *folder;
  912. DBG("uid %" PRIu64 " number of items %u", uid, number_of_items);
  913. folder = media_player_find_folder_by_uid(mp, uid);
  914. if (folder == NULL) {
  915. error("Unknown folder: %" PRIu64, uid);
  916. return;
  917. }
  918. folder->number_of_items = number_of_items;
  919. media_player_set_scope(mp, folder);
  920. }
  921. void media_player_change_folder_complete(struct media_player *mp,
  922. const char *path, uint64_t uid,
  923. int ret)
  924. {
  925. struct media_folder *folder = mp->scope;
  926. DBusMessage *reply;
  927. if (folder == NULL || folder->msg == NULL)
  928. return;
  929. if (ret < 0) {
  930. reply = btd_error_failed(folder->msg, strerror(-ret));
  931. goto done;
  932. }
  933. media_player_set_folder_by_uid(mp, uid, ret);
  934. reply = g_dbus_create_reply(folder->msg, DBUS_TYPE_INVALID);
  935. done:
  936. g_dbus_send_message(btd_get_dbus_connection(), reply);
  937. dbus_message_unref(folder->msg);
  938. folder->msg = NULL;
  939. }
  940. void media_player_destroy(struct media_player *mp)
  941. {
  942. DBG("%s", mp->path);
  943. g_dbus_unregister_interface(btd_get_dbus_connection(), mp->path,
  944. MEDIA_PLAYER_INTERFACE);
  945. if (mp->track)
  946. g_hash_table_unref(mp->track);
  947. if (mp->settings)
  948. g_hash_table_unref(mp->settings);
  949. if (mp->process_id > 0)
  950. g_source_remove(mp->process_id);
  951. if (mp->scope)
  952. g_dbus_unregister_interface(btd_get_dbus_connection(),
  953. mp->path,
  954. MEDIA_FOLDER_INTERFACE);
  955. g_slist_free_full(mp->pending, g_free);
  956. g_slist_free_full(mp->folders, media_folder_destroy);
  957. g_timer_destroy(mp->progress);
  958. g_free(mp->cb);
  959. g_free(mp->status);
  960. g_free(mp->path);
  961. g_free(mp->device);
  962. g_free(mp->subtype);
  963. g_free(mp->type);
  964. g_free(mp->name);
  965. g_free(mp);
  966. }
  967. struct media_player *media_player_controller_create(const char *path,
  968. uint16_t id)
  969. {
  970. struct media_player *mp;
  971. mp = g_new0(struct media_player, 1);
  972. mp->device = g_strdup(path);
  973. mp->path = g_strdup_printf("%s/player%u", path, id);
  974. mp->settings = g_hash_table_new_full(g_str_hash, g_str_equal,
  975. g_free, g_free);
  976. mp->track = g_hash_table_new_full(g_str_hash, g_str_equal,
  977. g_free, g_free);
  978. mp->progress = g_timer_new();
  979. if (!g_dbus_register_interface(btd_get_dbus_connection(),
  980. mp->path, MEDIA_PLAYER_INTERFACE,
  981. media_player_methods,
  982. media_player_signals,
  983. media_player_properties, mp, NULL)) {
  984. error("D-Bus failed to register %s path", mp->path);
  985. media_player_destroy(mp);
  986. return NULL;
  987. }
  988. DBG("%s", mp->path);
  989. return mp;
  990. }
  991. const char *media_player_get_path(struct media_player *mp)
  992. {
  993. return mp->path;
  994. }
  995. void media_player_set_duration(struct media_player *mp, uint32_t duration)
  996. {
  997. char *value, *curval;
  998. DBG("%u", duration);
  999. /* Only update duration if track exists */
  1000. if (g_hash_table_size(mp->track) == 0)
  1001. return;
  1002. /* Ignore if duration is already set */
  1003. curval = g_hash_table_lookup(mp->track, "Duration");
  1004. if (curval != NULL)
  1005. return;
  1006. value = g_strdup_printf("%u", duration);
  1007. g_hash_table_replace(mp->track, g_strdup("Duration"), value);
  1008. g_dbus_emit_property_changed(btd_get_dbus_connection(),
  1009. mp->path, MEDIA_PLAYER_INTERFACE,
  1010. "Track");
  1011. }
  1012. void media_player_set_position(struct media_player *mp, uint32_t position)
  1013. {
  1014. DBG("%u", position);
  1015. /* Only update duration if track exists */
  1016. if (g_hash_table_size(mp->track) == 0)
  1017. return;
  1018. mp->position = position;
  1019. g_timer_start(mp->progress);
  1020. g_dbus_emit_property_changed(btd_get_dbus_connection(), mp->path,
  1021. MEDIA_PLAYER_INTERFACE, "Position");
  1022. }
  1023. void media_player_set_setting(struct media_player *mp, const char *key,
  1024. const char *value)
  1025. {
  1026. char *curval;
  1027. struct pending_req *p;
  1028. DBG("%s: %s", key, value);
  1029. if (strcasecmp(key, "Error") == 0) {
  1030. p = g_slist_nth_data(mp->pending, 0);
  1031. if (p == NULL)
  1032. return;
  1033. g_dbus_pending_property_error(p->id, ERROR_INTERFACE ".Failed",
  1034. value);
  1035. goto send;
  1036. }
  1037. curval = g_hash_table_lookup(mp->settings, key);
  1038. if (g_strcmp0(curval, value) == 0)
  1039. goto done;
  1040. g_hash_table_replace(mp->settings, g_strdup(key), g_strdup(value));
  1041. g_dbus_emit_property_changed(btd_get_dbus_connection(), mp->path,
  1042. MEDIA_PLAYER_INTERFACE, key);
  1043. done:
  1044. p = find_pending(mp, key);
  1045. if (p == NULL)
  1046. return;
  1047. if (strcasecmp(value, p->value) == 0)
  1048. g_dbus_pending_property_success(p->id);
  1049. else
  1050. g_dbus_pending_property_error(p->id,
  1051. ERROR_INTERFACE ".NotSupported",
  1052. "Operation is not supported");
  1053. send:
  1054. mp->pending = g_slist_remove(mp->pending, p);
  1055. g_free(p);
  1056. return;
  1057. }
  1058. const char *media_player_get_status(struct media_player *mp)
  1059. {
  1060. return mp->status;
  1061. }
  1062. void media_player_set_status(struct media_player *mp, const char *status)
  1063. {
  1064. DBG("%s", status);
  1065. if (g_strcmp0(mp->status, status) == 0)
  1066. return;
  1067. g_free(mp->status);
  1068. mp->status = g_strdup(status);
  1069. g_dbus_emit_property_changed(btd_get_dbus_connection(), mp->path,
  1070. MEDIA_PLAYER_INTERFACE, "Status");
  1071. mp->position = media_player_get_position(mp);
  1072. g_timer_start(mp->progress);
  1073. }
  1074. static gboolean process_metadata_changed(void *user_data)
  1075. {
  1076. struct media_player *mp = user_data;
  1077. const char *item;
  1078. mp->process_id = 0;
  1079. g_dbus_emit_property_changed(btd_get_dbus_connection(),
  1080. mp->path, MEDIA_PLAYER_INTERFACE,
  1081. "Track");
  1082. item = g_hash_table_lookup(mp->track, "Item");
  1083. if (item == NULL)
  1084. return FALSE;
  1085. g_dbus_emit_property_changed(btd_get_dbus_connection(),
  1086. item, MEDIA_ITEM_INTERFACE,
  1087. "Metadata");
  1088. return FALSE;
  1089. }
  1090. void media_player_set_metadata(struct media_player *mp,
  1091. struct media_item *item, const char *key,
  1092. void *data, size_t len)
  1093. {
  1094. char *value, *curval;
  1095. value = g_strndup(data, len);
  1096. DBG("%s: %s", key, value);
  1097. curval = g_hash_table_lookup(mp->track, key);
  1098. if (g_strcmp0(curval, value) == 0) {
  1099. g_free(value);
  1100. return;
  1101. }
  1102. if (mp->process_id == 0) {
  1103. g_hash_table_remove_all(mp->track);
  1104. mp->process_id = g_idle_add(process_metadata_changed, mp);
  1105. }
  1106. g_hash_table_replace(mp->track, g_strdup(key), value);
  1107. }
  1108. void media_player_set_type(struct media_player *mp, const char *type)
  1109. {
  1110. if (g_strcmp0(mp->type, type) == 0)
  1111. return;
  1112. DBG("%s", type);
  1113. mp->type = g_strdup(type);
  1114. g_dbus_emit_property_changed(btd_get_dbus_connection(),
  1115. mp->path, MEDIA_PLAYER_INTERFACE,
  1116. "Type");
  1117. }
  1118. void media_player_set_subtype(struct media_player *mp, const char *subtype)
  1119. {
  1120. if (g_strcmp0(mp->subtype, subtype) == 0)
  1121. return;
  1122. DBG("%s", subtype);
  1123. mp->subtype = g_strdup(subtype);
  1124. g_dbus_emit_property_changed(btd_get_dbus_connection(),
  1125. mp->path, MEDIA_PLAYER_INTERFACE,
  1126. "Subtype");
  1127. }
  1128. void media_player_set_name(struct media_player *mp, const char *name)
  1129. {
  1130. if (g_strcmp0(mp->name, name) == 0)
  1131. return;
  1132. DBG("%s", name);
  1133. mp->name = g_strdup(name);
  1134. g_dbus_emit_property_changed(btd_get_dbus_connection(),
  1135. mp->path, MEDIA_PLAYER_INTERFACE,
  1136. "Name");
  1137. }
  1138. void media_player_set_browsable(struct media_player *mp, bool enabled)
  1139. {
  1140. if (mp->browsable == enabled)
  1141. return;
  1142. DBG("%s", enabled ? "true" : "false");
  1143. mp->browsable = enabled;
  1144. g_dbus_emit_property_changed(btd_get_dbus_connection(),
  1145. mp->path, MEDIA_PLAYER_INTERFACE,
  1146. "Browsable");
  1147. }
  1148. bool media_player_get_browsable(struct media_player *mp)
  1149. {
  1150. return mp->browsable;
  1151. }
  1152. void media_player_set_searchable(struct media_player *mp, bool enabled)
  1153. {
  1154. if (mp->searchable == enabled)
  1155. return;
  1156. DBG("%s", enabled ? "true" : "false");
  1157. mp->searchable = enabled;
  1158. g_dbus_emit_property_changed(btd_get_dbus_connection(),
  1159. mp->path, MEDIA_PLAYER_INTERFACE,
  1160. "Searchable");
  1161. }
  1162. void media_player_set_folder(struct media_player *mp, const char *name,
  1163. uint32_t number_of_items)
  1164. {
  1165. struct media_folder *folder;
  1166. DBG("%s number of items %u", name, number_of_items);
  1167. folder = media_player_find_folder(mp, name);
  1168. if (folder == NULL) {
  1169. error("Unknown folder: %s", name);
  1170. return;
  1171. }
  1172. folder->number_of_items = number_of_items;
  1173. media_player_set_scope(mp, folder);
  1174. }
  1175. void media_player_set_playlist(struct media_player *mp, const char *name)
  1176. {
  1177. struct media_folder *folder;
  1178. DBG("%s", name);
  1179. folder = media_player_find_folder(mp, name);
  1180. if (folder == NULL) {
  1181. error("Unknown folder: %s", name);
  1182. return;
  1183. }
  1184. mp->playlist = folder;
  1185. g_dbus_emit_property_changed(btd_get_dbus_connection(), mp->path,
  1186. MEDIA_PLAYER_INTERFACE, "Playlist");
  1187. }
  1188. static struct media_item *media_folder_find_item(struct media_folder *folder,
  1189. uint64_t uid)
  1190. {
  1191. GSList *l;
  1192. if (uid == 0)
  1193. return NULL;
  1194. for (l = folder->items; l; l = l->next) {
  1195. struct media_item *item = l->data;
  1196. if (item->uid == uid)
  1197. return item;
  1198. }
  1199. return NULL;
  1200. }
  1201. static DBusMessage *media_item_play(DBusConnection *conn, DBusMessage *msg,
  1202. void *data)
  1203. {
  1204. struct media_item *item = data;
  1205. struct media_player *mp = item->player;
  1206. struct media_folder *folder = mp->scope;
  1207. struct player_callback *cb = mp->cb;
  1208. const char *path;
  1209. int err;
  1210. if (!item->playable || !cb->cbs->play_item)
  1211. return btd_error_not_supported(msg);
  1212. if (folder->msg)
  1213. return btd_error_failed(msg, strerror(EBUSY));
  1214. path = mp->search && folder == mp->search ? "/Search" : item->path;
  1215. err = cb->cbs->play_item(mp, path, item->uid, cb->user_data);
  1216. if (err < 0)
  1217. return btd_error_failed(msg, strerror(-err));
  1218. folder->msg = dbus_message_ref(msg);
  1219. return NULL;
  1220. }
  1221. static DBusMessage *media_item_add_to_nowplaying(DBusConnection *conn,
  1222. DBusMessage *msg, void *data)
  1223. {
  1224. struct media_item *item = data;
  1225. struct media_player *mp = item->player;
  1226. struct player_callback *cb = mp->cb;
  1227. int err;
  1228. if (!item->playable || !cb->cbs->play_item)
  1229. return btd_error_not_supported(msg);
  1230. err = cb->cbs->add_to_nowplaying(mp, item->path, item->uid,
  1231. cb->user_data);
  1232. if (err < 0)
  1233. return btd_error_failed(msg, strerror(-err));
  1234. return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
  1235. }
  1236. static gboolean get_player(const GDBusPropertyTable *property,
  1237. DBusMessageIter *iter, void *data)
  1238. {
  1239. struct media_item *item = data;
  1240. dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH,
  1241. &item->player->path);
  1242. return TRUE;
  1243. }
  1244. static gboolean item_name_exists(const GDBusPropertyTable *property,
  1245. void *data)
  1246. {
  1247. struct media_item *item = data;
  1248. return item->name != NULL;
  1249. }
  1250. static gboolean get_item_name(const GDBusPropertyTable *property,
  1251. DBusMessageIter *iter, void *data)
  1252. {
  1253. struct media_item *item = data;
  1254. if (item->name == NULL)
  1255. return FALSE;
  1256. dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &item->name);
  1257. return TRUE;
  1258. }
  1259. static const char *type_to_string(uint8_t type)
  1260. {
  1261. switch (type) {
  1262. case PLAYER_ITEM_TYPE_AUDIO:
  1263. return "audio";
  1264. case PLAYER_ITEM_TYPE_VIDEO:
  1265. return "video";
  1266. case PLAYER_ITEM_TYPE_FOLDER:
  1267. return "folder";
  1268. }
  1269. return NULL;
  1270. }
  1271. static gboolean get_item_type(const GDBusPropertyTable *property,
  1272. DBusMessageIter *iter, void *data)
  1273. {
  1274. struct media_item *item = data;
  1275. const char *string;
  1276. string = type_to_string(item->type);
  1277. dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &string);
  1278. return TRUE;
  1279. }
  1280. static gboolean get_playable(const GDBusPropertyTable *property,
  1281. DBusMessageIter *iter, void *data)
  1282. {
  1283. struct media_item *item = data;
  1284. dbus_bool_t value;
  1285. value = item->playable;
  1286. dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &value);
  1287. return TRUE;
  1288. }
  1289. static const char *folder_type_to_string(uint8_t type)
  1290. {
  1291. switch (type) {
  1292. case PLAYER_FOLDER_TYPE_MIXED:
  1293. return "mixed";
  1294. case PLAYER_FOLDER_TYPE_TITLES:
  1295. return "titles";
  1296. case PLAYER_FOLDER_TYPE_ALBUMS:
  1297. return "albums";
  1298. case PLAYER_FOLDER_TYPE_ARTISTS:
  1299. return "artists";
  1300. case PLAYER_FOLDER_TYPE_GENRES:
  1301. return "genres";
  1302. case PLAYER_FOLDER_TYPE_PLAYLISTS:
  1303. return "playlists";
  1304. case PLAYER_FOLDER_TYPE_YEARS:
  1305. return "years";
  1306. }
  1307. return NULL;
  1308. }
  1309. static gboolean folder_type_exists(const GDBusPropertyTable *property,
  1310. void *data)
  1311. {
  1312. struct media_item *item = data;
  1313. return folder_type_to_string(item->folder_type) != NULL;
  1314. }
  1315. static gboolean get_folder_type(const GDBusPropertyTable *property,
  1316. DBusMessageIter *iter, void *data)
  1317. {
  1318. struct media_item *item = data;
  1319. const char *string;
  1320. string = folder_type_to_string(item->folder_type);
  1321. if (string == NULL)
  1322. return FALSE;
  1323. dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &string);
  1324. return TRUE;
  1325. }
  1326. static gboolean metadata_exists(const GDBusPropertyTable *property, void *data)
  1327. {
  1328. struct media_item *item = data;
  1329. return item->metadata != NULL;
  1330. }
  1331. static void append_metadata(void *key, void *value, void *user_data)
  1332. {
  1333. DBusMessageIter *dict = user_data;
  1334. const char *strkey = key;
  1335. if (strcasecmp(strkey, "Item") == 0)
  1336. return;
  1337. if (strcasecmp(strkey, "Duration") == 0 ||
  1338. strcasecmp(strkey, "TrackNumber") == 0 ||
  1339. strcasecmp(strkey, "NumberOfTracks") == 0) {
  1340. uint32_t num = atoi(value);
  1341. dict_append_entry(dict, key, DBUS_TYPE_UINT32, &num);
  1342. } else {
  1343. dict_append_entry(dict, key, DBUS_TYPE_STRING, &value);
  1344. }
  1345. }
  1346. static gboolean get_metadata(const GDBusPropertyTable *property,
  1347. DBusMessageIter *iter, void *data)
  1348. {
  1349. struct media_item *item = data;
  1350. DBusMessageIter dict;
  1351. dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
  1352. DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
  1353. DBUS_TYPE_STRING_AS_STRING
  1354. DBUS_TYPE_VARIANT_AS_STRING
  1355. DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
  1356. &dict);
  1357. if (g_hash_table_size(item->metadata) > 0)
  1358. g_hash_table_foreach(item->metadata, append_metadata, &dict);
  1359. else if (item->name != NULL)
  1360. dict_append_entry(&dict, "Title", DBUS_TYPE_STRING,
  1361. &item->name);
  1362. dbus_message_iter_close_container(iter, &dict);
  1363. return TRUE;
  1364. }
  1365. static const GDBusMethodTable media_item_methods[] = {
  1366. { GDBUS_ASYNC_METHOD("Play", NULL, NULL, media_item_play) },
  1367. { GDBUS_METHOD("AddtoNowPlaying", NULL, NULL,
  1368. media_item_add_to_nowplaying) },
  1369. { }
  1370. };
  1371. static const GDBusPropertyTable media_item_properties[] = {
  1372. { "Player", "o", get_player, NULL, NULL },
  1373. { "Name", "s", get_item_name, NULL, item_name_exists },
  1374. { "Type", "s", get_item_type, NULL, NULL },
  1375. { "FolderType", "s", get_folder_type, NULL, folder_type_exists },
  1376. { "Playable", "b", get_playable, NULL, NULL },
  1377. { "Metadata", "a{sv}", get_metadata, NULL, metadata_exists },
  1378. { }
  1379. };
  1380. void media_player_play_item_complete(struct media_player *mp, int err)
  1381. {
  1382. struct media_folder *folder = mp->scope;
  1383. DBusMessage *reply;
  1384. if (folder == NULL || folder->msg == NULL)
  1385. return;
  1386. if (err < 0) {
  1387. reply = btd_error_failed(folder->msg, strerror(-err));
  1388. goto done;
  1389. }
  1390. reply = g_dbus_create_reply(folder->msg, DBUS_TYPE_INVALID);
  1391. done:
  1392. g_dbus_send_message(btd_get_dbus_connection(), reply);
  1393. dbus_message_unref(folder->msg);
  1394. folder->msg = NULL;
  1395. }
  1396. void media_item_set_playable(struct media_item *item, bool value)
  1397. {
  1398. if (item->playable == value)
  1399. return;
  1400. item->playable = value;
  1401. g_dbus_emit_property_changed(btd_get_dbus_connection(), item->path,
  1402. MEDIA_ITEM_INTERFACE, "Playable");
  1403. }
  1404. static struct media_item *media_folder_create_item(struct media_player *mp,
  1405. struct media_folder *folder,
  1406. const char *name,
  1407. player_item_type_t type,
  1408. uint64_t uid)
  1409. {
  1410. struct media_item *item;
  1411. const char *strtype;
  1412. item = media_folder_find_item(folder, uid);
  1413. if (item != NULL)
  1414. return item;
  1415. strtype = type_to_string(type);
  1416. if (strtype == NULL)
  1417. return NULL;
  1418. DBG("%s type %s uid %" PRIu64 "", name, strtype, uid);
  1419. item = g_new0(struct media_item, 1);
  1420. item->player = mp;
  1421. item->uid = uid;
  1422. if (!uid && name[0] == '/')
  1423. item->path = g_strdup_printf("%s%s", mp->path, name);
  1424. else
  1425. item->path = g_strdup_printf("%s/item%" PRIu64 "",
  1426. folder->item->path, uid);
  1427. item->name = g_strdup(name);
  1428. item->type = type;
  1429. item->folder_type = PLAYER_FOLDER_TYPE_INVALID;
  1430. if (!g_dbus_register_interface(btd_get_dbus_connection(),
  1431. item->path, MEDIA_ITEM_INTERFACE,
  1432. media_item_methods,
  1433. NULL,
  1434. media_item_properties, item, NULL)) {
  1435. error("D-Bus failed to register %s on %s path",
  1436. MEDIA_ITEM_INTERFACE, item->path);
  1437. media_item_free(item);
  1438. return NULL;
  1439. }
  1440. if (type != PLAYER_ITEM_TYPE_FOLDER) {
  1441. folder->items = g_slist_prepend(folder->items, item);
  1442. item->metadata = g_hash_table_new_full(g_str_hash, g_str_equal,
  1443. g_free, g_free);
  1444. }
  1445. DBG("%s", item->path);
  1446. return item;
  1447. }
  1448. struct media_item *media_player_create_item(struct media_player *mp,
  1449. const char *name,
  1450. player_item_type_t type,
  1451. uint64_t uid)
  1452. {
  1453. return media_folder_create_item(mp, mp->scope, name, type, uid);
  1454. }
  1455. struct media_item *media_player_create_folder(struct media_player *mp,
  1456. const char *name,
  1457. player_folder_type_t type,
  1458. uint64_t uid)
  1459. {
  1460. struct media_folder *folder;
  1461. struct media_item *item;
  1462. if (uid > 0)
  1463. folder = media_player_find_folder_by_uid(mp, uid);
  1464. else
  1465. folder = media_player_find_folder(mp, name);
  1466. if (folder != NULL)
  1467. return folder->item;
  1468. if (uid > 0)
  1469. item = media_player_create_subfolder(mp, name, uid);
  1470. else
  1471. item = media_player_create_item(mp, name,
  1472. PLAYER_ITEM_TYPE_FOLDER, uid);
  1473. if (item == NULL)
  1474. return NULL;
  1475. folder = g_new0(struct media_folder, 1);
  1476. folder->item = item;
  1477. item->folder_type = type;
  1478. if (mp->folder != NULL)
  1479. goto done;
  1480. mp->folder = folder;
  1481. done:
  1482. if (uid > 0) {
  1483. folder->parent = mp->folder;
  1484. mp->folder->subfolders = g_slist_prepend(
  1485. mp->folder->subfolders,
  1486. folder);
  1487. } else
  1488. mp->folders = g_slist_prepend(mp->folders, folder);
  1489. return item;
  1490. }
  1491. void media_player_set_callbacks(struct media_player *mp,
  1492. const struct media_player_callback *cbs,
  1493. void *user_data)
  1494. {
  1495. struct player_callback *cb;
  1496. if (mp->cb)
  1497. g_free(mp->cb);
  1498. cb = g_new0(struct player_callback, 1);
  1499. cb->cbs = cbs;
  1500. cb->user_data = user_data;
  1501. mp->cb = cb;
  1502. }
  1503. struct media_item *media_player_set_playlist_item(struct media_player *mp,
  1504. uint64_t uid)
  1505. {
  1506. struct media_folder *folder = mp->playlist;
  1507. struct media_item *item;
  1508. DBG("%" PRIu64 "", uid);
  1509. if (folder == NULL || uid == 0)
  1510. return NULL;
  1511. item = media_folder_create_item(mp, folder, NULL,
  1512. PLAYER_ITEM_TYPE_AUDIO, uid);
  1513. if (item == NULL)
  1514. return NULL;
  1515. media_item_set_playable(item, true);
  1516. if (mp->track != item->metadata) {
  1517. g_hash_table_unref(mp->track);
  1518. mp->track = g_hash_table_ref(item->metadata);
  1519. }
  1520. if (item == g_hash_table_lookup(mp->track, "Item"))
  1521. return item;
  1522. if (mp->process_id == 0) {
  1523. g_hash_table_remove_all(mp->track);
  1524. mp->process_id = g_idle_add(process_metadata_changed, mp);
  1525. }
  1526. g_hash_table_replace(mp->track, g_strdup("Item"),
  1527. g_strdup(item->path));
  1528. return item;
  1529. }