1 /* Media player skeleton implementation */
2
3 /*
4 * Copyright (c) 2019 - 2023 Nordic Semiconductor ASA
5 *
6 * SPDX-License-Identifier: Apache-2.0
7 */
8
9 #include <errno.h>
10 #include <stdbool.h>
11 #include <stddef.h>
12 #include <stdint.h>
13 #include <string.h>
14 #include <sys/types.h>
15
16 #include <zephyr/autoconf.h>
17 #include <zephyr/bluetooth/audio/ccid.h>
18 #include <zephyr/bluetooth/audio/mcs.h>
19 #include <zephyr/bluetooth/audio/media_proxy.h>
20 #include <zephyr/bluetooth/bluetooth.h>
21 #include <zephyr/bluetooth/services/ots.h>
22 #include <zephyr/bluetooth/conn.h>
23 #include <zephyr/bluetooth/uuid.h>
24 #include <zephyr/kernel.h>
25 #include <zephyr/logging/log.h>
26 #include <zephyr/net_buf.h>
27 #include <zephyr/sys/__assert.h>
28 #include <zephyr/sys/atomic.h>
29 #include <zephyr/sys/util.h>
30 #include <zephyr/sys/time_units.h>
31 #include <zephyr/sys/util_macro.h>
32
33 #include "media_proxy_internal.h"
34 #include "mcs_internal.h"
35 #include "mpl_internal.h"
36
37 LOG_MODULE_REGISTER(bt_mpl, CONFIG_BT_MPL_LOG_LEVEL);
38
39 #define TRACK_STATUS_INVALID 0x00
40 #define TRACK_STATUS_VALID 0x01
41
42 #define TRACK_POS_WORK_DELAY_MS 1000
43 #define TRACK_POS_WORK_DELAY K_MSEC(TRACK_POS_WORK_DELAY_MS)
44
45 #define PLAYBACK_SPEED_PARAM_DEFAULT MEDIA_PROXY_PLAYBACK_SPEED_UNITY
46
47 /* Temporary hardcoded setup for groups, tracks and segments */
48 /* There is one parent group, which is the parent of a number of groups. */
49 /* The groups have a number of tracks. */
50 /* (There is only one level of groups, there are no groups of groups.) */
51 /* The first track of the first group has track segments, other tracks not. */
52
53 /* Track segments */
54 static struct mpl_tseg seg_2;
55 static struct mpl_tseg seg_3;
56
57 static struct mpl_tseg seg_1 = {
58 .name_len = 5,
59 .name = "Start",
60 .pos = 0,
61 .prev = NULL,
62 .next = &seg_2,
63 };
64
65 static struct mpl_tseg seg_2 = {
66 .name_len = 6,
67 .name = "Middle",
68 .pos = 2000,
69 .prev = &seg_1,
70 .next = &seg_3,
71 };
72
73 static struct mpl_tseg seg_3 = {
74 .name_len = 3,
75 .name = "End",
76 .pos = 5000,
77 .prev = &seg_2,
78 .next = NULL,
79 };
80
81 static struct mpl_track track_1_2;
82 static struct mpl_track track_1_3;
83 static struct mpl_track track_1_4;
84 static struct mpl_track track_1_5;
85
86 /* Tracks */
87 static struct mpl_track track_1_1 = {
88 .title = "Interlude #1 (Song for Alison)",
89 .duration = 6300,
90 .segment = &seg_1,
91 .prev = NULL,
92 .next = &track_1_2,
93 };
94
95
96 static struct mpl_track track_1_2 = {
97 .title = "Interlude #2 (For Bobbye)",
98 .duration = 7500,
99 .segment = NULL,
100 .prev = &track_1_1,
101 .next = &track_1_3,
102 };
103
104 static struct mpl_track track_1_3 = {
105 .title = "Interlude #3 (Levanto Seventy)",
106 .duration = 7800,
107 .segment = NULL,
108 .prev = &track_1_2,
109 .next = &track_1_4,
110 };
111
112 static struct mpl_track track_1_4 = {
113 .title = "Interlude #4 (Vesper Dreams)",
114 .duration = 13500,
115 .segment = NULL,
116 .prev = &track_1_3,
117 .next = &track_1_5,
118 };
119
120 static struct mpl_track track_1_5 = {
121 .title = "Interlude #5 (Shasti)",
122 .duration = 7500,
123 .segment = NULL,
124 .prev = &track_1_4,
125 .next = NULL,
126 };
127
128 static struct mpl_track track_2_2;
129 static struct mpl_track track_2_3;
130
131 static struct mpl_track track_2_1 = {
132 .title = "Track 2.1",
133 .duration = 30000,
134 .segment = NULL,
135 .prev = NULL,
136 .next = &track_2_2,
137 };
138
139 static struct mpl_track track_2_2 = {
140 .title = "Track 2.2",
141 .duration = 30000,
142 .segment = NULL,
143 .prev = &track_2_1,
144 .next = &track_2_3,
145 };
146
147 static struct mpl_track track_2_3 = {
148 .title = "Track 2.3",
149 .duration = 30000,
150 .segment = NULL,
151 .prev = &track_2_2,
152 .next = NULL,
153 };
154
155 static struct mpl_track track_3_2;
156 static struct mpl_track track_3_3;
157
158 static struct mpl_track track_3_1 = {
159 .title = "Track 3.1",
160 .duration = 30000,
161 .segment = NULL,
162 .prev = NULL,
163 .next = &track_3_2,
164 };
165
166 static struct mpl_track track_3_2 = {
167 .title = "Track 3.2",
168 .duration = 30000,
169 .segment = NULL,
170 .prev = &track_3_1,
171 .next = &track_3_3,
172 };
173
174 static struct mpl_track track_3_3 = {
175 .title = "Track 3.3",
176 .duration = 30000,
177 .segment = NULL,
178 .prev = &track_3_2,
179 .next = NULL,
180 };
181
182 static struct mpl_track track_4_2;
183
184 static struct mpl_track track_4_1 = {
185 .title = "Track 4.1",
186 .duration = 30000,
187 .segment = NULL,
188 .prev = NULL,
189 .next = &track_4_2,
190 };
191
192 static struct mpl_track track_4_2 = {
193 .title = "Track 4.2",
194 .duration = 30000,
195 .segment = NULL,
196 .prev = &track_4_1,
197 .next = NULL,
198 };
199
200 /* Groups */
201 static struct mpl_group group_2;
202 static struct mpl_group group_3;
203 static struct mpl_group group_4;
204 static struct mpl_group group_p;
205
206 static struct mpl_group group_1 = {
207 .title = "Joe Pass - Guitar Interludes",
208 .track = &track_1_1,
209 .parent = &group_p,
210 .prev = NULL,
211 .next = &group_2,
212 };
213
214 static struct mpl_group group_2 = {
215 .title = "Group 2",
216 .track = &track_2_2,
217 .parent = &group_p,
218 .prev = &group_1,
219 .next = &group_3,
220 };
221
222 static struct mpl_group group_3 = {
223 .title = "Group 3",
224 .track = &track_3_3,
225 .parent = &group_p,
226 .prev = &group_2,
227 .next = &group_4,
228 };
229
230 static struct mpl_group group_4 = {
231 .title = "Group 4",
232 .track = &track_4_2,
233 .parent = &group_p,
234 .prev = &group_3,
235 .next = NULL,
236 };
237
238 static struct mpl_group group_p = {
239 .title = "Parent group",
240 .track = &track_4_1,
241 .parent = &group_p,
242 .prev = NULL,
243 .next = NULL,
244 };
245
246 static struct mpl_mediaplayer media_player = {
247 .name = CONFIG_BT_MPL_MEDIA_PLAYER_NAME,
248 .icon_url = CONFIG_BT_MPL_ICON_URL,
249 .group = &group_1,
250 .track_pos = 0,
251 .state = MEDIA_PROXY_STATE_PAUSED,
252 .playback_speed_param = PLAYBACK_SPEED_PARAM_DEFAULT,
253 .seeking_speed_factor = MEDIA_PROXY_SEEKING_SPEED_FACTOR_ZERO,
254 .playing_order = MEDIA_PROXY_PLAYING_ORDER_INORDER_REPEAT,
255 .playing_orders_supported = MEDIA_PROXY_PLAYING_ORDERS_SUPPORTED_INORDER_ONCE |
256 MEDIA_PROXY_PLAYING_ORDERS_SUPPORTED_INORDER_REPEAT,
257 .opcodes_supported = 0x001fffff, /* All opcodes */
258 #ifdef CONFIG_BT_MPL_OBJECTS
259 .search_results_id = 0,
260 .calls = { 0 },
261 #endif /* CONFIG_BT_MPL_OBJECTS */
262 .next_track_set = false
263 };
264
265 static void set_track_position(int32_t position);
266 static void set_relative_track_position(int32_t rel_pos);
267 static void do_track_change_notifications(struct mpl_mediaplayer *pl);
268 static void do_group_change_notifications(struct mpl_mediaplayer *pl);
269
270 #ifdef CONFIG_BT_MPL_OBJECTS
271
272 /* The types of objects we keep in the Object Transfer Service */
273 enum mpl_objects {
274 MPL_OBJ_NONE = 0,
275 MPL_OBJ_ICON,
276 MPL_OBJ_TRACK_SEGMENTS,
277 MPL_OBJ_TRACK,
278 MPL_OBJ_PARENT_GROUP,
279 MPL_OBJ_GROUP,
280 MPL_OBJ_SEARCH_RESULTS,
281 };
282
283 enum mpl_obj_flag {
284 MPL_OBJ_FLAG_BUSY,
285
286 MPL_OBJ_FLAG_NUM_FLAGS, /* keep as last */
287 };
288
289 /* The active object */
290 /* Only a single object is selected or being added (active) at a time. */
291 /* And, except for the icon object, all objects can be created dynamically. */
292 /* So a single buffer to hold object content is sufficient. */
293 struct obj_t {
294 /* ID of the currently selected object*/
295 uint64_t selected_id;
296
297 /* Type of object being added, e.g. MPL_OBJ_ICON */
298 uint8_t add_type;
299
300 /* Descriptor of object being added */
301 struct bt_ots_obj_created_desc *desc;
302 union {
303 /* Pointer to track being added */
304 struct mpl_track *add_track;
305
306 /* Pointer to group being added */
307 struct mpl_group *add_group;
308 };
309 struct net_buf_simple *content;
310
311 ATOMIC_DEFINE(flags, MPL_OBJ_FLAG_NUM_FLAGS);
312 };
313
314 static struct obj_t obj = {
315 .selected_id = 0,
316 .add_type = MPL_OBJ_NONE,
317 .add_track = NULL,
318 .add_group = NULL,
319 .content = NET_BUF_SIMPLE(CONFIG_BT_MPL_MAX_OBJ_SIZE),
320 };
321
322 /* Set up content buffer for the icon object */
setup_icon_object(void)323 static int setup_icon_object(void)
324 {
325 uint16_t index;
326 uint8_t k;
327
328 /* The icon object is supposed to be a bitmap. */
329 /* For now, fill it with dummy data. */
330
331 net_buf_simple_reset(obj.content);
332
333 /* Size may be larger than what fits in 8 bits, use 16-bit for index */
334 for (index = 0, k = 0;
335 index < MIN(CONFIG_BT_MPL_MAX_OBJ_SIZE,
336 CONFIG_BT_MPL_ICON_BITMAP_SIZE);
337 index++, k++) {
338 net_buf_simple_add_u8(obj.content, k);
339 }
340
341 return obj.content->len;
342 }
343
344 /* Set up content buffer for a track segments object */
setup_segments_object(struct mpl_track * track)345 static uint32_t setup_segments_object(struct mpl_track *track)
346 {
347 struct mpl_tseg *seg = track->segment;
348
349 net_buf_simple_reset(obj.content);
350
351 if (seg) {
352 uint32_t tot_size = 0;
353
354 while (seg->prev) {
355 seg = seg->prev;
356 }
357 while (seg) {
358 uint32_t seg_size = sizeof(seg->name_len);
359
360 seg_size += seg->name_len;
361 seg_size += sizeof(seg->pos);
362 if (tot_size + seg_size > obj.content->size) {
363 LOG_DBG("Segments object out of space");
364 break;
365 }
366 net_buf_simple_add_u8(obj.content, seg->name_len);
367 net_buf_simple_add_mem(obj.content, seg->name,
368 seg->name_len);
369 net_buf_simple_add_le32(obj.content, seg->pos);
370
371 tot_size += seg_size;
372 seg = seg->next;
373 }
374
375 LOG_HEXDUMP_DBG(obj.content->data, obj.content->len, "Segments Object");
376 LOG_DBG("Segments object length: %d", obj.content->len);
377 } else {
378 LOG_ERR("No seg!");
379 }
380
381 return obj.content->len;
382 }
383
384 /* Set up content buffer for a track object */
setup_track_object(struct mpl_track * track)385 static uint32_t setup_track_object(struct mpl_track *track)
386 {
387 uint16_t index;
388 uint8_t k;
389
390 /* The track object is supposed to be in Id3v2 format */
391 /* For now, fill it with dummy data */
392
393 net_buf_simple_reset(obj.content);
394
395 /* Size may be larger than what fits in 8 bits, use 16-bit for index */
396 for (index = 0, k = 0;
397 index < MIN(CONFIG_BT_MPL_MAX_OBJ_SIZE,
398 CONFIG_BT_MPL_TRACK_MAX_SIZE);
399 index++, k++) {
400 net_buf_simple_add_u8(obj.content, k);
401 }
402
403 return obj.content->len;
404 }
405
406 /* Set up content buffer for the parent group object */
setup_parent_group_object(struct mpl_group * group)407 static uint32_t setup_parent_group_object(struct mpl_group *group)
408 {
409 /* This function actually does not use the parent. */
410 /* It just follows the list of groups. */
411 /* The implementation has a fixed structure, with one parent group, */
412 /* and one level of groups containing tracks only. */
413 /* The track groups have a pointer to the parent, but there is no */
414 /* pointer in the other direction, so it is not possible to go from */
415 /* the parent group to a group of tracks. */
416
417 uint8_t type = MEDIA_PROXY_GROUP_OBJECT_GROUP_TYPE;
418 uint8_t record_size = sizeof(type) + BT_OTS_OBJ_ID_SIZE;
419 int next_size = record_size;
420
421 net_buf_simple_reset(obj.content);
422
423 if (group) {
424 while (group->prev) {
425 group = group->prev;
426 }
427 /* While there is a group, and the record fits in the object */
428 while (group && (next_size <= obj.content->size)) {
429 net_buf_simple_add_u8(obj.content, type);
430 net_buf_simple_add_le48(obj.content, group->id);
431 group = group->next;
432 next_size += record_size;
433 }
434 if (next_size > obj.content->size) {
435 LOG_WRN("Not room for full group in object");
436 }
437 LOG_HEXDUMP_DBG(obj.content->data, obj.content->len, "Parent Group Object");
438 LOG_DBG("Group object length: %d", obj.content->len);
439 }
440 return obj.content->len;
441 }
442
443 /* Set up contents for a group object */
444 /* The group object contains a concatenated list of records, where each */
445 /* record consists of a type byte and a UUID */
setup_group_object(struct mpl_group * group)446 static uint32_t setup_group_object(struct mpl_group *group)
447 {
448 struct mpl_track *track = group->track;
449 uint8_t type = MEDIA_PROXY_GROUP_OBJECT_TRACK_TYPE;
450 uint8_t record_size = sizeof(type) + BT_OTS_OBJ_ID_SIZE;
451 int next_size = record_size;
452
453 net_buf_simple_reset(obj.content);
454
455 if (track) {
456 while (track->prev) {
457 track = track->prev;
458 }
459 /* While there is a track, and the record fits in the object */
460 while (track && (next_size <= obj.content->size)) {
461 net_buf_simple_add_u8(obj.content, type);
462 net_buf_simple_add_le48(obj.content, track->id);
463 track = track->next;
464 next_size += record_size;
465 }
466 if (next_size > obj.content->size) {
467 LOG_WRN("Not room for full group in object");
468 }
469 LOG_HEXDUMP_DBG(obj.content->data, obj.content->len, "Group Object");
470 LOG_DBG("Group object length: %d", obj.content->len);
471 }
472 return obj.content->len;
473 }
474
475 /* Add the icon object to the OTS */
add_icon_object(struct mpl_mediaplayer * pl)476 static int add_icon_object(struct mpl_mediaplayer *pl)
477 {
478 int ret;
479 struct bt_ots_obj_add_param add_param = {};
480 struct bt_ots_obj_created_desc created_desc = {};
481 const struct bt_uuid *icon_type = BT_UUID_OTS_TYPE_MPL_ICON;
482 static char *icon_name = "Icon";
483
484 obj.add_type = MPL_OBJ_ICON;
485 obj.desc = &created_desc;
486
487 obj.desc->size.alloc = obj.desc->size.cur = setup_icon_object();
488 obj.desc->name = icon_name;
489 BT_OTS_OBJ_SET_PROP_READ(obj.desc->props);
490
491 add_param.size = obj.desc->size.alloc;
492 add_param.type.uuid.type = BT_UUID_TYPE_16;
493 add_param.type.uuid_16.val = BT_UUID_16(icon_type)->val;
494
495 ret = bt_ots_obj_add(bt_mcs_get_ots(), &add_param);
496 if (ret < 0) {
497 LOG_WRN("Unable to add icon object, error %d", ret);
498
499 return ret;
500 }
501
502 return 0;
503 }
504
505 /* Add a track segments object to the OTS */
add_current_track_segments_object(struct mpl_mediaplayer * pl)506 static int add_current_track_segments_object(struct mpl_mediaplayer *pl)
507 {
508 int ret;
509 struct bt_ots_obj_add_param add_param = {};
510 struct bt_ots_obj_created_desc created_desc = {};
511 const struct bt_uuid *segs_type = BT_UUID_OTS_TYPE_TRACK_SEGMENT;
512
513 obj.add_type = MPL_OBJ_TRACK_SEGMENTS;
514 obj.desc = &created_desc;
515
516 obj.desc->size.alloc = obj.desc->size.cur = setup_segments_object(pl->group->track);
517 obj.desc->name = pl->group->track->title;
518 BT_OTS_OBJ_SET_PROP_READ(obj.desc->props);
519
520 add_param.size = obj.desc->size.alloc;
521 add_param.type.uuid.type = BT_UUID_TYPE_16;
522 add_param.type.uuid_16.val = BT_UUID_16(segs_type)->val;
523
524 ret = bt_ots_obj_add(bt_mcs_get_ots(), &add_param);
525 if (ret < 0) {
526 LOG_WRN("Unable to add track segments object: %d", ret);
527
528 return ret;
529 }
530
531 return 0;
532 }
533
534 /* Add a single track to the OTS */
add_track_object(struct mpl_track * track)535 static int add_track_object(struct mpl_track *track)
536 {
537 struct bt_ots_obj_add_param add_param = {};
538 struct bt_ots_obj_created_desc created_desc = {};
539 const struct bt_uuid *track_type = BT_UUID_OTS_TYPE_TRACK;
540 int ret;
541
542 if (!track) {
543 LOG_ERR("No track");
544 return -EINVAL;
545 }
546
547 obj.add_type = MPL_OBJ_TRACK;
548 obj.add_track = track;
549 obj.desc = &created_desc;
550
551 obj.desc->size.alloc = obj.desc->size.cur = setup_track_object(track);
552 obj.desc->name = track->title;
553 BT_OTS_OBJ_SET_PROP_READ(obj.desc->props);
554
555 add_param.size = obj.desc->size.alloc;
556 add_param.type.uuid.type = BT_UUID_TYPE_16;
557 add_param.type.uuid_16.val = BT_UUID_16(track_type)->val;
558
559 ret = bt_ots_obj_add(bt_mcs_get_ots(), &add_param);
560 if (ret < 0) {
561 LOG_WRN("Unable to add track object: %d", ret);
562
563 return ret;
564 }
565
566 return 0;
567 }
568
569 /* Add the parent group to the OTS */
add_parent_group_object(struct mpl_mediaplayer * pl)570 static int add_parent_group_object(struct mpl_mediaplayer *pl)
571 {
572 int ret;
573 struct bt_ots_obj_add_param add_param = {};
574 struct bt_ots_obj_created_desc created_desc = {};
575 const struct bt_uuid *group_type = BT_UUID_OTS_TYPE_GROUP;
576
577 obj.add_type = MPL_OBJ_PARENT_GROUP;
578 obj.desc = &created_desc;
579
580 obj.desc->size.alloc = obj.desc->size.cur = setup_parent_group_object(pl->group);
581 obj.desc->name = pl->group->parent->title;
582 BT_OTS_OBJ_SET_PROP_READ(obj.desc->props);
583
584 add_param.size = obj.desc->size.alloc;
585 add_param.type.uuid.type = BT_UUID_TYPE_16;
586 add_param.type.uuid_16.val = BT_UUID_16(group_type)->val;
587
588 ret = bt_ots_obj_add(bt_mcs_get_ots(), &add_param);
589 if (ret < 0) {
590 LOG_WRN("Unable to add parent group object");
591
592 return ret;
593 }
594
595 return 0;
596 }
597
598 /* Add a single group to the OTS */
add_group_object(struct mpl_group * group)599 static int add_group_object(struct mpl_group *group)
600 {
601 struct bt_ots_obj_add_param add_param = {};
602 struct bt_ots_obj_created_desc created_desc = {};
603 const struct bt_uuid *group_type = BT_UUID_OTS_TYPE_GROUP;
604 int ret;
605
606 if (!group) {
607 LOG_ERR("No group");
608 return -EINVAL;
609 }
610
611 obj.add_type = MPL_OBJ_GROUP;
612 obj.add_group = group;
613 obj.desc = &created_desc;
614
615 obj.desc->size.alloc = obj.desc->size.cur = setup_group_object(group);
616 obj.desc->name = group->title;
617 BT_OTS_OBJ_SET_PROP_READ(obj.desc->props);
618
619 add_param.size = obj.desc->size.alloc;
620 add_param.type.uuid.type = BT_UUID_TYPE_16;
621 add_param.type.uuid_16.val = BT_UUID_16(group_type)->val;
622
623 ret = bt_ots_obj_add(bt_mcs_get_ots(), &add_param);
624 if (ret < 0) {
625 LOG_WRN("Unable to add group object: %d", ret);
626
627 return ret;
628 }
629
630 return 0;
631 }
632
633 /* Add all tracks of a group to the OTS */
add_group_tracks(struct mpl_group * group)634 static int add_group_tracks(struct mpl_group *group)
635 {
636 int ret_overall = 0;
637 struct mpl_track *track = group->track;
638
639 if (track) {
640 while (track->prev) {
641 track = track->prev;
642 }
643
644 while (track) {
645 int ret = add_track_object(track);
646
647 if (ret && !ret_overall) {
648 ret_overall = ret;
649 }
650 track = track->next;
651 }
652 }
653 return ret_overall;
654 }
655
656 /* Add all groups (except the parent group) and their tracks to the OTS */
add_group_and_track_objects(struct mpl_mediaplayer * pl)657 static int add_group_and_track_objects(struct mpl_mediaplayer *pl)
658 {
659 int ret_overall = 0;
660 int ret;
661 struct mpl_group *group = pl->group;
662
663 if (group) {
664 while (group->prev) {
665 group = group->prev;
666 }
667
668 while (group) {
669 ret = add_group_tracks(group);
670 if (ret && !ret_overall) {
671 ret_overall = ret;
672 }
673
674 ret = add_group_object(group);
675 if (ret && !ret_overall) {
676 ret_overall = ret;
677 }
678 group = group->next;
679 }
680 }
681
682 ret = add_parent_group_object(pl);
683 if (ret && !ret_overall) {
684 ret_overall = ret;
685 }
686
687 return ret_overall;
688 }
689
690 /**** Callbacks from the object transfer service ******************************/
691
on_obj_deleted(struct bt_ots * ots,struct bt_conn * conn,uint64_t id)692 static int on_obj_deleted(struct bt_ots *ots, struct bt_conn *conn,
693 uint64_t id)
694 {
695 LOG_DBG_OBJ_ID("Object Id deleted: ", id);
696
697 return 0;
698 }
699
on_obj_selected(struct bt_ots * ots,struct bt_conn * conn,uint64_t id)700 static void on_obj_selected(struct bt_ots *ots, struct bt_conn *conn,
701 uint64_t id)
702 {
703 if (atomic_test_and_set_bit(obj.flags, MPL_OBJ_FLAG_BUSY)) {
704 /* TODO: Can there be a collision between select and internal */
705 /* activities, like adding new objects? */
706 LOG_ERR("Object busy - select not performed");
707 return;
708 }
709
710 LOG_DBG_OBJ_ID("Object Id selected: ", id);
711
712 if (id == media_player.icon_id) {
713 LOG_DBG("Icon Object ID");
714 (void)setup_icon_object();
715 } else if (id == media_player.group->track->segments_id) {
716 LOG_DBG("Current Track Segments Object ID");
717 (void)setup_segments_object(media_player.group->track);
718 } else if (id == media_player.group->track->id) {
719 LOG_DBG("Current Track Object ID");
720 (void)setup_track_object(media_player.group->track);
721 } else if (media_player.next_track_set && id == media_player.next.track->id) {
722 /* Next track, if the next track has been explicitly set */
723 LOG_DBG("Next Track Object ID");
724 (void)setup_track_object(media_player.next.track);
725 } else if (id == media_player.group->track->next->id) {
726 /* Next track, if next track has not been explicitly set */
727 LOG_DBG("Next Track Object ID");
728 (void)setup_track_object(media_player.group->track->next);
729 } else if (id == media_player.group->parent->id) {
730 LOG_DBG("Parent Group Object ID");
731 (void)setup_parent_group_object(media_player.group);
732 } else if (id == media_player.group->id) {
733 LOG_DBG("Current Group Object ID");
734 (void)setup_group_object(media_player.group);
735 } else {
736 LOG_ERR("Unknown Object ID");
737 atomic_clear_bit(obj.flags, MPL_OBJ_FLAG_BUSY);
738 return;
739 }
740
741 obj.selected_id = id;
742 atomic_clear_bit(obj.flags, MPL_OBJ_FLAG_BUSY);
743 }
744
on_obj_created(struct bt_ots * ots,struct bt_conn * conn,uint64_t id,const struct bt_ots_obj_add_param * add_param,struct bt_ots_obj_created_desc * created_desc)745 static int on_obj_created(struct bt_ots *ots, struct bt_conn *conn, uint64_t id,
746 const struct bt_ots_obj_add_param *add_param,
747 struct bt_ots_obj_created_desc *created_desc)
748 {
749 /* Objects are always created locally so we do not need to check for MPL_OBJ_FLAG_BUSY */
750
751 LOG_DBG_OBJ_ID("Object Id created: ", id);
752
753 *created_desc = *obj.desc;
754
755 if (!bt_uuid_cmp(&add_param->type.uuid, BT_UUID_OTS_TYPE_MPL_ICON)) {
756 LOG_DBG("Icon Obj Type");
757 if (obj.add_type == MPL_OBJ_ICON) {
758 obj.add_type = MPL_OBJ_NONE;
759 media_player.icon_id = id;
760 } else {
761 LOG_DBG("Unexpected object creation");
762 }
763
764 } else if (!bt_uuid_cmp(&add_param->type.uuid,
765 BT_UUID_OTS_TYPE_TRACK_SEGMENT)) {
766 LOG_DBG("Track Segments Obj Type");
767 if (obj.add_type == MPL_OBJ_TRACK_SEGMENTS) {
768 obj.add_type = MPL_OBJ_NONE;
769 media_player.group->track->segments_id = id;
770 } else {
771 LOG_DBG("Unexpected object creation");
772 }
773
774 } else if (!bt_uuid_cmp(&add_param->type.uuid,
775 BT_UUID_OTS_TYPE_TRACK)) {
776 LOG_DBG("Track Obj Type");
777 if (obj.add_type == MPL_OBJ_TRACK) {
778 obj.add_type = MPL_OBJ_NONE;
779 obj.add_track->id = id;
780 obj.add_track = NULL;
781 } else {
782 LOG_DBG("Unexpected object creation");
783 }
784
785 } else if (!bt_uuid_cmp(&add_param->type.uuid,
786 BT_UUID_OTS_TYPE_GROUP)) {
787 LOG_DBG("Group Obj Type");
788 if (obj.add_type == MPL_OBJ_PARENT_GROUP) {
789 LOG_DBG("Parent group");
790 obj.add_type = MPL_OBJ_NONE;
791 media_player.group->parent->id = id;
792 } else if (obj.add_type == MPL_OBJ_GROUP) {
793 LOG_DBG("Other group");
794 obj.add_type = MPL_OBJ_NONE;
795 obj.add_group->id = id;
796 obj.add_group = NULL;
797 } else {
798 LOG_DBG("Unexpected object creation");
799 }
800
801 } else {
802 LOG_DBG("Unknown Object ID");
803 }
804
805 return 0;
806 }
807
on_object_send(struct bt_ots * ots,struct bt_conn * conn,uint64_t id,void ** data,size_t len,off_t offset)808 static ssize_t on_object_send(struct bt_ots *ots, struct bt_conn *conn,
809 uint64_t id, void **data, size_t len,
810 off_t offset)
811 {
812 if (atomic_test_and_set_bit(obj.flags, MPL_OBJ_FLAG_BUSY)) {
813 /* TODO: Can there be a collision between select and internal */
814 /* activities, like adding new objects? */
815 LOG_ERR("Object busy");
816 return -EBUSY;
817 }
818
819 if (IS_ENABLED(CONFIG_BT_MPL_LOG_LEVEL_DBG)) {
820 char t[BT_OTS_OBJ_ID_STR_LEN];
821 (void)bt_ots_obj_id_to_str(id, t, sizeof(t));
822 LOG_DBG("Object Id %s, offset %lu, length %zu", t, (long)offset, len);
823 }
824
825 if (id != obj.selected_id) {
826 LOG_ERR("Read from unselected object");
827 atomic_clear_bit(obj.flags, MPL_OBJ_FLAG_BUSY);
828 return -EINVAL;
829 }
830
831 if (!data) {
832 LOG_DBG("Read complete");
833 atomic_clear_bit(obj.flags, MPL_OBJ_FLAG_BUSY);
834 return 0;
835 }
836
837 if (offset >= obj.content->len) {
838 LOG_DBG("Offset too large");
839 atomic_clear_bit(obj.flags, MPL_OBJ_FLAG_BUSY);
840 return -EINVAL;
841 }
842
843 if (IS_ENABLED(CONFIG_BT_MPL_LOG_LEVEL_DBG)) {
844 if (len > obj.content->len - offset) {
845 LOG_DBG("Requested len too large");
846 }
847 }
848
849 *data = &obj.content->data[offset];
850 atomic_clear_bit(obj.flags, MPL_OBJ_FLAG_BUSY);
851
852 return MIN(len, obj.content->len - offset);
853 }
854
855 static struct bt_ots_cb ots_cbs = {
856 .obj_created = on_obj_created,
857 .obj_read = on_object_send,
858 .obj_selected = on_obj_selected,
859 .obj_deleted = on_obj_deleted,
860 };
861
862 #endif /* CONFIG_BT_MPL_OBJECTS */
863
864
865 /* TODO: It must be possible to replace the do_prev_segment(), do_prev_track */
866 /* and do_prev_group() with a generic do_prev() command that can be used at */
867 /* all levels. Similarly for do_next, do_prev, and so on. */
868
do_prev_segment(struct mpl_mediaplayer * pl)869 static void do_prev_segment(struct mpl_mediaplayer *pl)
870 {
871 LOG_DBG("Segment name before: %s", pl->group->track->segment->name);
872
873 if (pl->group->track->segment->prev != NULL) {
874 pl->group->track->segment = pl->group->track->segment->prev;
875 }
876
877 LOG_DBG("Segment name after: %s", pl->group->track->segment->name);
878 }
879
do_next_segment(struct mpl_mediaplayer * pl)880 static void do_next_segment(struct mpl_mediaplayer *pl)
881 {
882 LOG_DBG("Segment name before: %s", pl->group->track->segment->name);
883
884 if (pl->group->track->segment->next != NULL) {
885 pl->group->track->segment = pl->group->track->segment->next;
886 }
887
888 LOG_DBG("Segment name after: %s", pl->group->track->segment->name);
889 }
890
do_first_segment(struct mpl_mediaplayer * pl)891 static void do_first_segment(struct mpl_mediaplayer *pl)
892 {
893 LOG_DBG("Segment name before: %s", pl->group->track->segment->name);
894
895 while (pl->group->track->segment->prev != NULL) {
896 pl->group->track->segment = pl->group->track->segment->prev;
897 }
898
899 LOG_DBG("Segment name after: %s", pl->group->track->segment->name);
900 }
901
do_last_segment(struct mpl_mediaplayer * pl)902 static void do_last_segment(struct mpl_mediaplayer *pl)
903 {
904 LOG_DBG("Segment name before: %s", pl->group->track->segment->name);
905
906 while (pl->group->track->segment->next != NULL) {
907 pl->group->track->segment = pl->group->track->segment->next;
908 }
909
910 LOG_DBG("Segment name after: %s", pl->group->track->segment->name);
911 }
912
do_goto_segment(struct mpl_mediaplayer * pl,int32_t segnum)913 static void do_goto_segment(struct mpl_mediaplayer *pl, int32_t segnum)
914 {
915 int32_t k;
916
917 LOG_DBG("Segment name before: %s", pl->group->track->segment->name);
918
919 if (segnum > 0) {
920 /* Goto first segment */
921 while (pl->group->track->segment->prev != NULL) {
922 pl->group->track->segment =
923 pl->group->track->segment->prev;
924 }
925
926 /* Then go segnum - 1 tracks forward */
927 for (k = 0; k < (segnum - 1); k++) {
928 if (pl->group->track->segment->next != NULL) {
929 pl->group->track->segment =
930 pl->group->track->segment->next;
931 }
932 }
933 } else if (segnum < 0) {
934 /* Goto last track */
935 while (pl->group->track->segment->next != NULL) {
936 pl->group->track->segment =
937 pl->group->track->segment->next;
938 }
939
940 /* Then go |segnum - 1| tracks back */
941 for (k = 0; k < (-segnum - 1); k++) {
942 if (pl->group->track->segment->prev != NULL) {
943 pl->group->track->segment =
944 pl->group->track->segment->prev;
945 }
946 }
947 }
948
949 LOG_DBG("Segment name after: %s", pl->group->track->segment->name);
950
951 set_track_position(pl->group->track->segment->pos);
952 }
953
do_prev_track(struct mpl_mediaplayer * pl)954 static void do_prev_track(struct mpl_mediaplayer *pl)
955 {
956 #ifdef CONFIG_BT_MPL_OBJECTS
957 LOG_DBG_OBJ_ID("Track ID before: ", pl->group->track->id);
958 #endif /* CONFIG_BT_MPL_OBJECTS */
959
960 if (pl->group->track->prev != NULL) {
961 pl->group->track = pl->group->track->prev;
962 pl->track_pos = 0;
963 do_track_change_notifications(pl);
964 } else {
965 /* For previous track, the position is reset to 0 */
966 /* even if we stay at the same track (goto start of */
967 /* track) */
968 set_track_position(0);
969 }
970
971 #ifdef CONFIG_BT_MPL_OBJECTS
972 LOG_DBG_OBJ_ID("Track ID after: ", pl->group->track->id);
973 #endif /* CONFIG_BT_MPL_OBJECTS */
974 }
975
976 /* Change to next track according to the current track's next track */
do_next_track_normal_order(struct mpl_mediaplayer * pl)977 static void do_next_track_normal_order(struct mpl_mediaplayer *pl)
978 {
979 #ifdef CONFIG_BT_MPL_OBJECTS
980 LOG_DBG_OBJ_ID("Track ID before: ", pl->group->track->id);
981 #endif /* CONFIG_BT_MPL_OBJECTS */
982
983 if (pl->group->track->next != NULL) {
984 pl->group->track = pl->group->track->next;
985 pl->track_pos = 0;
986 do_track_change_notifications(pl);
987 }
988
989 #ifdef CONFIG_BT_MPL_OBJECTS
990 LOG_DBG_OBJ_ID("Track ID after: ", pl->group->track->id);
991 #endif /* CONFIG_BT_MPL_OBJECTS */
992 }
993
994 /* Change to next track when the next track has been explicitly set
995 *
996 * ALWAYS changes the track, changes the group if required
997 * Resets the next_track_set and the "next" pointers
998 *
999 * Returns true if the _group_ has been changed, otherwise false
1000 */
do_next_track_next_track_set(struct mpl_mediaplayer * pl)1001 static void do_next_track_next_track_set(struct mpl_mediaplayer *pl)
1002 {
1003 if (pl->next.group != pl->group) {
1004 pl->group = pl->next.group;
1005 do_group_change_notifications(pl);
1006 }
1007
1008 pl->group->track = pl->next.track;
1009
1010 pl->next.track = NULL;
1011 pl->next.group = NULL;
1012 pl->next_track_set = false;
1013 pl->track_pos = 0;
1014 do_track_change_notifications(pl);
1015 }
1016
do_next_track(struct mpl_mediaplayer * pl)1017 static void do_next_track(struct mpl_mediaplayer *pl)
1018 {
1019 if (pl->next_track_set) {
1020 LOG_DBG("Next track set");
1021 do_next_track_next_track_set(pl);
1022 } else {
1023 do_next_track_normal_order(pl);
1024 }
1025 }
1026
do_first_track(struct mpl_mediaplayer * pl,bool group_change)1027 static void do_first_track(struct mpl_mediaplayer *pl, bool group_change)
1028 {
1029 bool track_changed = false;
1030
1031 #ifdef CONFIG_BT_MPL_OBJECTS
1032 LOG_DBG_OBJ_ID("Track ID before: ", pl->group->track->id);
1033 #endif /* CONFIG_BT_MPL_OBJECTS */
1034
1035 /* Set first track */
1036 while (pl->group->track->prev != NULL) {
1037 pl->group->track = pl->group->track->prev;
1038 track_changed = true;
1039 }
1040
1041 /* Notify about new track */
1042 if (group_change || track_changed) {
1043 media_player.track_pos = 0;
1044 do_track_change_notifications(&media_player);
1045 } else {
1046 /* For first track, the position is reset to 0 even */
1047 /* if we stay at the same track (goto start of track) */
1048 set_track_position(0);
1049 }
1050
1051 #ifdef CONFIG_BT_MPL_OBJECTS
1052 LOG_DBG_OBJ_ID("Track ID after: ", pl->group->track->id);
1053 #endif /* CONFIG_BT_MPL_OBJECTS */
1054 }
1055
do_last_track(struct mpl_mediaplayer * pl)1056 static void do_last_track(struct mpl_mediaplayer *pl)
1057 {
1058 #ifdef CONFIG_BT_MPL_OBJECTS
1059 LOG_DBG_OBJ_ID("Track ID before: ", pl->group->track->id);
1060 #endif /* CONFIG_BT_MPL_OBJECTS */
1061
1062 if (pl->group->track->next != NULL) {
1063 pl->group->track = pl->group->track->next;
1064 media_player.track_pos = 0;
1065 do_track_change_notifications(&media_player);
1066 } else {
1067
1068 /* For last track, the position is reset to 0 even */
1069 /* if we stay at the same track (goto start of track) */
1070 set_track_position(0);
1071 }
1072
1073 while (pl->group->track->next != NULL) {
1074 pl->group->track = pl->group->track->next;
1075 }
1076
1077 #ifdef CONFIG_BT_MPL_OBJECTS
1078 LOG_DBG_OBJ_ID("Track ID after: ", pl->group->track->id);
1079 #endif /* CONFIG_BT_MPL_OBJECTS */
1080 }
1081
do_goto_track(struct mpl_mediaplayer * pl,int32_t tracknum)1082 static void do_goto_track(struct mpl_mediaplayer *pl, int32_t tracknum)
1083 {
1084 int32_t count = 0;
1085 int32_t k;
1086
1087 #ifdef CONFIG_BT_MPL_OBJECTS
1088 LOG_DBG_OBJ_ID("Track ID before: ", pl->group->track->id);
1089 #endif /* CONFIG_BT_MPL_OBJECTS */
1090
1091 if (tracknum > 0) {
1092 /* Goto first track */
1093 while (pl->group->track->prev != NULL) {
1094 pl->group->track = pl->group->track->prev;
1095 count--;
1096 }
1097
1098 /* Then go tracknum - 1 tracks forward */
1099 for (k = 0; k < (tracknum - 1); k++) {
1100 if (pl->group->track->next != NULL) {
1101 pl->group->track = pl->group->track->next;
1102 count++;
1103 }
1104 }
1105 } else if (tracknum < 0) {
1106 /* Goto last track */
1107 while (pl->group->track->next != NULL) {
1108 pl->group->track = pl->group->track->next;
1109 count++;
1110 }
1111
1112 /* Then go |tracknum - 1| tracks back */
1113 for (k = 0; k < (-tracknum - 1); k++) {
1114 if (pl->group->track->prev != NULL) {
1115 pl->group->track = pl->group->track->prev;
1116 count--;
1117 }
1118 }
1119 }
1120
1121 #ifdef CONFIG_BT_MPL_OBJECTS
1122 LOG_DBG_OBJ_ID("Track ID after: ", pl->group->track->id);
1123 #endif /* CONFIG_BT_MPL_OBJECTS */
1124
1125 /* The track has changed if we have moved more in one direction */
1126 /* than in the other */
1127 if (count != 0) {
1128 media_player.track_pos = 0;
1129 do_track_change_notifications(&media_player);
1130 } else {
1131 /* For goto track, the position is reset to 0 */
1132 /* even if we stay at the same track (goto */
1133 /* start of track) */
1134 set_track_position(0);
1135 }
1136 }
1137
do_prev_group(struct mpl_mediaplayer * pl)1138 static void do_prev_group(struct mpl_mediaplayer *pl)
1139 {
1140 #ifdef CONFIG_BT_MPL_OBJECTS
1141 LOG_DBG_OBJ_ID("Group ID before: ", pl->group->id);
1142 #endif /* CONFIG_BT_MPL_OBJECTS */
1143
1144 if (pl->group->prev != NULL) {
1145 pl->group = pl->group->prev;
1146 do_group_change_notifications(pl);
1147 }
1148
1149 #ifdef CONFIG_BT_MPL_OBJECTS
1150 LOG_DBG_OBJ_ID("Group ID after: ", pl->group->id);
1151 #endif /* CONFIG_BT_MPL_OBJECTS */
1152 }
1153
do_next_group(struct mpl_mediaplayer * pl)1154 static void do_next_group(struct mpl_mediaplayer *pl)
1155 {
1156
1157 #ifdef CONFIG_BT_MPL_OBJECTS
1158 LOG_DBG_OBJ_ID("Group ID before: ", pl->group->id);
1159 #endif /* CONFIG_BT_MPL_OBJECTS */
1160
1161 if (pl->group->next != NULL) {
1162 pl->group = pl->group->next;
1163 do_group_change_notifications(pl);
1164 }
1165
1166 #ifdef CONFIG_BT_MPL_OBJECTS
1167 LOG_DBG_OBJ_ID("Group ID after: ", pl->group->id);
1168 #endif /* CONFIG_BT_MPL_OBJECTS */
1169 }
1170
do_first_group(struct mpl_mediaplayer * pl)1171 static void do_first_group(struct mpl_mediaplayer *pl)
1172 {
1173 #ifdef CONFIG_BT_MPL_OBJECTS
1174 LOG_DBG_OBJ_ID("Group ID before: ", pl->group->id);
1175 #endif /* CONFIG_BT_MPL_OBJECTS */
1176
1177 if (pl->group->prev != NULL) {
1178 pl->group = pl->group->prev;
1179 do_group_change_notifications(pl);
1180 }
1181
1182 while (pl->group->prev != NULL) {
1183 pl->group = pl->group->prev;
1184 }
1185
1186 #ifdef CONFIG_BT_MPL_OBJECTS
1187 LOG_DBG_OBJ_ID("Group ID after: ", pl->group->id);
1188 #endif /* CONFIG_BT_MPL_OBJECTS */
1189 }
1190
do_last_group(struct mpl_mediaplayer * pl)1191 static void do_last_group(struct mpl_mediaplayer *pl)
1192 {
1193 #ifdef CONFIG_BT_MPL_OBJECTS
1194 LOG_DBG_OBJ_ID("Group ID before: ", pl->group->id);
1195 #endif /* CONFIG_BT_MPL_OBJECTS */
1196
1197 if (pl->group->next != NULL) {
1198 pl->group = pl->group->next;
1199 do_group_change_notifications(pl);
1200 }
1201
1202 while (pl->group->next != NULL) {
1203 pl->group = pl->group->next;
1204 }
1205
1206 #ifdef CONFIG_BT_MPL_OBJECTS
1207 LOG_DBG_OBJ_ID("Group ID after: ", pl->group->id);
1208 #endif /* CONFIG_BT_MPL_OBJECTS */
1209 }
1210
do_goto_group(struct mpl_mediaplayer * pl,int32_t groupnum)1211 static void do_goto_group(struct mpl_mediaplayer *pl, int32_t groupnum)
1212 {
1213 int32_t count = 0;
1214 int32_t k;
1215
1216 #ifdef CONFIG_BT_MPL_OBJECTS
1217 LOG_DBG_OBJ_ID("Group ID before: ", pl->group->id);
1218 #endif /* CONFIG_BT_MPL_OBJECTS */
1219
1220 if (groupnum > 0) {
1221 /* Goto first group */
1222 while (pl->group->prev != NULL) {
1223 pl->group = pl->group->prev;
1224 count--;
1225 }
1226
1227 /* Then go groupnum - 1 groups forward */
1228 for (k = 0; k < (groupnum - 1); k++) {
1229 if (pl->group->next != NULL) {
1230 pl->group = pl->group->next;
1231 count++;
1232 }
1233 }
1234 } else if (groupnum < 0) {
1235 /* Goto last group */
1236 while (pl->group->next != NULL) {
1237 pl->group = pl->group->next;
1238 count++;
1239 }
1240
1241 /* Then go |groupnum - 1| groups back */
1242 for (k = 0; k < (-groupnum - 1); k++) {
1243 if (pl->group->prev != NULL) {
1244 pl->group = pl->group->prev;
1245 count--;
1246 }
1247 }
1248 }
1249
1250 #ifdef CONFIG_BT_MPL_OBJECTS
1251 LOG_DBG_OBJ_ID("Group ID after: ", pl->group->id);
1252 #endif /* CONFIG_BT_MPL_OBJECTS */
1253
1254 /* The group has changed if we have moved more in one direction */
1255 /* than in the other */
1256 if (count != 0) {
1257 do_group_change_notifications(pl);
1258 }
1259 }
1260
do_track_change_notifications(struct mpl_mediaplayer * pl)1261 static void do_track_change_notifications(struct mpl_mediaplayer *pl)
1262 {
1263 media_proxy_pl_track_changed_cb();
1264 media_proxy_pl_track_title_cb(pl->group->track->title);
1265 media_proxy_pl_track_duration_cb(pl->group->track->duration);
1266 media_proxy_pl_track_position_cb(pl->track_pos);
1267 #ifdef CONFIG_BT_MPL_OBJECTS
1268 media_proxy_pl_current_track_id_cb(pl->group->track->id);
1269 if (pl->group->track->next) {
1270 media_proxy_pl_next_track_id_cb(pl->group->track->next->id);
1271 } else {
1272 /* Send a zero value to indicate that there is no next track */
1273 media_proxy_pl_next_track_id_cb(MPL_NO_TRACK_ID);
1274 }
1275 #endif /* CONFIG_BT_MPL_OBJECTS */
1276 }
1277
do_group_change_notifications(struct mpl_mediaplayer * pl)1278 static void do_group_change_notifications(struct mpl_mediaplayer *pl)
1279 {
1280 #ifdef CONFIG_BT_MPL_OBJECTS
1281 media_proxy_pl_current_group_id_cb(pl->group->id);
1282 #endif /* CONFIG_BT_MPL_OBJECTS */
1283 }
1284
do_full_prev_group(struct mpl_mediaplayer * pl)1285 static void do_full_prev_group(struct mpl_mediaplayer *pl)
1286 {
1287 /* Change the group (if not already on first group) */
1288 do_prev_group(pl);
1289
1290 /* Whether there is a group change or not, we always go to the first track */
1291 do_first_track(pl, true);
1292 }
1293
do_full_next_group(struct mpl_mediaplayer * pl)1294 static void do_full_next_group(struct mpl_mediaplayer *pl)
1295 {
1296 /* Change the group (if not already on last group) */
1297 do_next_group(pl);
1298
1299 /* Whether there is a group change or not, we always go to the first track */
1300 do_first_track(pl, true);
1301 }
1302
do_full_first_group(struct mpl_mediaplayer * pl)1303 static void do_full_first_group(struct mpl_mediaplayer *pl)
1304 {
1305 /* Change the group (if not already on first group) */
1306 do_first_group(pl);
1307
1308 /* Whether there is a group change or not, we always go to the first track */
1309 do_first_track(pl, true);
1310 }
1311
do_full_last_group(struct mpl_mediaplayer * pl)1312 static void do_full_last_group(struct mpl_mediaplayer *pl)
1313 {
1314 /* Change the group (if not already on last group) */
1315 do_last_group(pl);
1316
1317 /* Whether there is a group change or not, we always go to the first track */
1318 do_first_track(pl, true);
1319 }
1320
do_full_goto_group(struct mpl_mediaplayer * pl,int32_t groupnum)1321 static void do_full_goto_group(struct mpl_mediaplayer *pl, int32_t groupnum)
1322 {
1323 /* Change the group (if not already on given group) */
1324 do_goto_group(pl, groupnum);
1325
1326 /* Whether there is a group change or not, we always go to the first track */
1327 do_first_track(pl, true);
1328 }
1329
mpl_set_state(uint8_t state)1330 static void mpl_set_state(uint8_t state)
1331 {
1332 switch (state) {
1333 case MEDIA_PROXY_STATE_INACTIVE:
1334 case MEDIA_PROXY_STATE_PLAYING:
1335 case MEDIA_PROXY_STATE_PAUSED:
1336 (void)k_work_cancel_delayable(&media_player.pos_work);
1337 break;
1338 case MEDIA_PROXY_STATE_SEEKING:
1339 (void)k_work_schedule(&media_player.pos_work, TRACK_POS_WORK_DELAY);
1340 break;
1341 default:
1342 __ASSERT(false, "Invalid state: %u", state);
1343 }
1344
1345 media_player.state = state;
1346 media_proxy_pl_media_state_cb(media_player.state);
1347 }
1348
1349 /* Command handlers (state machines) */
inactive_state_command_handler(const struct mpl_cmd * command)1350 static uint8_t inactive_state_command_handler(const struct mpl_cmd *command)
1351 {
1352 uint8_t result_code = MEDIA_PROXY_CMD_SUCCESS;
1353
1354 LOG_DBG("Command opcode: %d", command->opcode);
1355 if (IS_ENABLED(CONFIG_BT_MPL_LOG_LEVEL_DBG)) {
1356 if (command->use_param) {
1357 LOG_DBG("Command parameter: %d", command->param);
1358 }
1359 }
1360 switch (command->opcode) {
1361 case MEDIA_PROXY_OP_PLAY: /* Fall-through - handle several cases identically */
1362 case MEDIA_PROXY_OP_PAUSE:
1363 case MEDIA_PROXY_OP_FAST_REWIND:
1364 case MEDIA_PROXY_OP_FAST_FORWARD:
1365 case MEDIA_PROXY_OP_STOP:
1366 case MEDIA_PROXY_OP_MOVE_RELATIVE:
1367 case MEDIA_PROXY_OP_PREV_SEGMENT:
1368 case MEDIA_PROXY_OP_NEXT_SEGMENT:
1369 case MEDIA_PROXY_OP_FIRST_SEGMENT:
1370 case MEDIA_PROXY_OP_LAST_SEGMENT:
1371 case MEDIA_PROXY_OP_GOTO_SEGMENT:
1372 result_code = MEDIA_PROXY_CMD_PLAYER_INACTIVE;
1373 break;
1374 case MEDIA_PROXY_OP_PREV_TRACK:
1375 do_prev_track(&media_player);
1376 mpl_set_state(MEDIA_PROXY_STATE_PAUSED);
1377 break;
1378 case MEDIA_PROXY_OP_NEXT_TRACK:
1379 /* TODO:
1380 * The case where the next track has been set explicitly breaks somewhat
1381 * with the "next" order hardcoded into the group and track structure
1382 */
1383 do_next_track(&media_player);
1384
1385 /* For next track, the position is kept if the track */
1386 /* does not change */
1387 mpl_set_state(MEDIA_PROXY_STATE_PAUSED);
1388 break;
1389 case MEDIA_PROXY_OP_FIRST_TRACK:
1390 do_first_track(&media_player, false);
1391 mpl_set_state(MEDIA_PROXY_STATE_PAUSED);
1392 break;
1393 case MEDIA_PROXY_OP_LAST_TRACK:
1394 do_last_track(&media_player);
1395 mpl_set_state(MEDIA_PROXY_STATE_PAUSED);
1396 break;
1397 case MEDIA_PROXY_OP_GOTO_TRACK:
1398 if (command->use_param) {
1399 do_goto_track(&media_player, command->param);
1400 mpl_set_state(MEDIA_PROXY_STATE_PAUSED);
1401 } else {
1402 result_code = MEDIA_PROXY_CMD_CANNOT_BE_COMPLETED;
1403 }
1404 break;
1405 case MEDIA_PROXY_OP_PREV_GROUP:
1406 do_full_prev_group(&media_player);
1407 mpl_set_state(MEDIA_PROXY_STATE_PAUSED);
1408 break;
1409 case MEDIA_PROXY_OP_NEXT_GROUP:
1410 do_full_next_group(&media_player);
1411 mpl_set_state(MEDIA_PROXY_STATE_PAUSED);
1412 break;
1413 case MEDIA_PROXY_OP_FIRST_GROUP:
1414 do_full_first_group(&media_player);
1415 mpl_set_state(MEDIA_PROXY_STATE_PAUSED);
1416 break;
1417 case MEDIA_PROXY_OP_LAST_GROUP:
1418 do_full_last_group(&media_player);
1419 mpl_set_state(MEDIA_PROXY_STATE_PAUSED);
1420 break;
1421 case MEDIA_PROXY_OP_GOTO_GROUP:
1422 if (command->use_param) {
1423 do_full_goto_group(&media_player, command->param);
1424 mpl_set_state(MEDIA_PROXY_STATE_PAUSED);
1425 } else {
1426 result_code = MEDIA_PROXY_CMD_CANNOT_BE_COMPLETED;
1427 }
1428
1429 break;
1430 default:
1431 LOG_DBG("Invalid command: %d", command->opcode);
1432 result_code = MEDIA_PROXY_CMD_NOT_SUPPORTED;
1433 break;
1434 }
1435
1436 return result_code;
1437 }
1438
playing_state_command_handler(const struct mpl_cmd * command)1439 static uint8_t playing_state_command_handler(const struct mpl_cmd *command)
1440 {
1441 uint8_t result_code = MEDIA_PROXY_CMD_SUCCESS;
1442
1443 LOG_DBG("Command opcode: %d", command->opcode);
1444 if (IS_ENABLED(CONFIG_BT_MPL_LOG_LEVEL_DBG)) {
1445 if (command->use_param) {
1446 LOG_DBG("Command parameter: %d", command->param);
1447 }
1448 }
1449
1450 switch (command->opcode) {
1451 case MEDIA_PROXY_OP_PLAY:
1452 /* Continue playing - i.e. do nothing */
1453 break;
1454 case MEDIA_PROXY_OP_PAUSE:
1455 mpl_set_state(MEDIA_PROXY_STATE_PAUSED);
1456 break;
1457 case MEDIA_PROXY_OP_FAST_REWIND:
1458 /* We're in playing state, seeking speed must have been zero */
1459 media_player.seeking_speed_factor = -MPL_SEEKING_SPEED_FACTOR_STEP;
1460 mpl_set_state(MEDIA_PROXY_STATE_SEEKING);
1461 media_proxy_pl_seeking_speed_cb(media_player.seeking_speed_factor);
1462 break;
1463 case MEDIA_PROXY_OP_FAST_FORWARD:
1464 /* We're in playing state, seeking speed must have been zero */
1465 media_player.seeking_speed_factor = MPL_SEEKING_SPEED_FACTOR_STEP;
1466 mpl_set_state(MEDIA_PROXY_STATE_SEEKING);
1467 media_proxy_pl_seeking_speed_cb(media_player.seeking_speed_factor);
1468 break;
1469 case MEDIA_PROXY_OP_STOP:
1470 set_track_position(0);
1471 mpl_set_state(MEDIA_PROXY_STATE_PAUSED);
1472 break;
1473 case MEDIA_PROXY_OP_MOVE_RELATIVE:
1474 if (command->use_param) {
1475 set_relative_track_position(command->param);
1476 } else {
1477 result_code = MEDIA_PROXY_CMD_CANNOT_BE_COMPLETED;
1478 }
1479
1480 break;
1481 case MEDIA_PROXY_OP_PREV_SEGMENT:
1482 /* Switch to previous segment if we are less than <margin> */
1483 /* into the segment, otherwise go to start of segment */
1484 if (media_player.track_pos - PREV_MARGIN <
1485 media_player.group->track->segment->pos) {
1486 do_prev_segment(&media_player);
1487 }
1488 set_track_position(media_player.group->track->segment->pos);
1489 break;
1490 case MEDIA_PROXY_OP_NEXT_SEGMENT:
1491 do_next_segment(&media_player);
1492 set_track_position(media_player.group->track->segment->pos);
1493 break;
1494 case MEDIA_PROXY_OP_FIRST_SEGMENT:
1495 do_first_segment(&media_player);
1496 set_track_position(media_player.group->track->segment->pos);
1497 break;
1498 case MEDIA_PROXY_OP_LAST_SEGMENT:
1499 do_last_segment(&media_player);
1500 set_track_position(media_player.group->track->segment->pos);
1501 break;
1502 case MEDIA_PROXY_OP_GOTO_SEGMENT:
1503 if (command->use_param) {
1504 if (command->param != 0) {
1505 do_goto_segment(&media_player, command->param);
1506 }
1507 /* If the argument to "goto segment" is zero, */
1508 /* the segment shall stay the same, and the */
1509 /* track position shall not change. */
1510 } else {
1511 result_code = MEDIA_PROXY_CMD_CANNOT_BE_COMPLETED;
1512 }
1513
1514 break;
1515 case MEDIA_PROXY_OP_PREV_TRACK:
1516 do_prev_track(&media_player);
1517 break;
1518 case MEDIA_PROXY_OP_NEXT_TRACK:
1519 do_next_track(&media_player);
1520 break;
1521 case MEDIA_PROXY_OP_FIRST_TRACK:
1522 do_first_track(&media_player, false);
1523 break;
1524 case MEDIA_PROXY_OP_LAST_TRACK:
1525 do_last_track(&media_player);
1526 break;
1527 case MEDIA_PROXY_OP_GOTO_TRACK:
1528 if (command->use_param) {
1529 do_goto_track(&media_player, command->param);
1530 } else {
1531 result_code = MEDIA_PROXY_CMD_CANNOT_BE_COMPLETED;
1532 }
1533
1534 break;
1535 case MEDIA_PROXY_OP_PREV_GROUP:
1536 do_full_prev_group(&media_player);
1537 break;
1538 case MEDIA_PROXY_OP_NEXT_GROUP:
1539 do_full_next_group(&media_player);
1540 break;
1541 case MEDIA_PROXY_OP_FIRST_GROUP:
1542 do_full_first_group(&media_player);
1543 break;
1544 case MEDIA_PROXY_OP_LAST_GROUP:
1545 do_full_last_group(&media_player);
1546 break;
1547 case MEDIA_PROXY_OP_GOTO_GROUP:
1548 if (command->use_param) {
1549 do_full_goto_group(&media_player, command->param);
1550 } else {
1551 result_code = MEDIA_PROXY_CMD_CANNOT_BE_COMPLETED;
1552 }
1553 break;
1554 default:
1555 LOG_DBG("Invalid command: %d", command->opcode);
1556 result_code = MEDIA_PROXY_CMD_NOT_SUPPORTED;
1557 break;
1558 }
1559
1560 return result_code;
1561 }
1562
paused_state_command_handler(const struct mpl_cmd * command)1563 static uint8_t paused_state_command_handler(const struct mpl_cmd *command)
1564 {
1565 uint8_t result_code = MEDIA_PROXY_CMD_SUCCESS;
1566
1567 LOG_DBG("Command opcode: %d", command->opcode);
1568 if (IS_ENABLED(CONFIG_BT_MPL_LOG_LEVEL_DBG)) {
1569 if (command->use_param) {
1570 LOG_DBG("Command parameter: %d", command->param);
1571 }
1572 }
1573
1574 switch (command->opcode) {
1575 case MEDIA_PROXY_OP_PLAY:
1576 mpl_set_state(MEDIA_PROXY_STATE_PLAYING);
1577 break;
1578 case MEDIA_PROXY_OP_PAUSE:
1579 /* No change */
1580 break;
1581 case MEDIA_PROXY_OP_FAST_REWIND:
1582 /* We're in paused state, seeking speed must have been zero */
1583 media_player.seeking_speed_factor = -MPL_SEEKING_SPEED_FACTOR_STEP;
1584 mpl_set_state(MEDIA_PROXY_STATE_SEEKING);
1585 media_proxy_pl_seeking_speed_cb(media_player.seeking_speed_factor);
1586 break;
1587 case MEDIA_PROXY_OP_FAST_FORWARD:
1588 /* We're in paused state, seeking speed must have been zero */
1589 media_player.seeking_speed_factor = MPL_SEEKING_SPEED_FACTOR_STEP;
1590 mpl_set_state(MEDIA_PROXY_STATE_SEEKING);
1591 media_proxy_pl_seeking_speed_cb(media_player.seeking_speed_factor);
1592 break;
1593 case MEDIA_PROXY_OP_STOP:
1594 set_track_position(0);
1595 mpl_set_state(MEDIA_PROXY_STATE_PAUSED);
1596 break;
1597 case MEDIA_PROXY_OP_MOVE_RELATIVE:
1598 if (command->use_param) {
1599 set_relative_track_position(command->param);
1600 } else {
1601 result_code = MEDIA_PROXY_CMD_CANNOT_BE_COMPLETED;
1602 }
1603
1604 break;
1605 case MEDIA_PROXY_OP_PREV_SEGMENT:
1606 /* Switch to previous segment if we are less than 5 seconds */
1607 /* into the segment, otherwise go to start of segment */
1608 if (media_player.group->track->segment != NULL) {
1609 if (media_player.track_pos - PREV_MARGIN <
1610 media_player.group->track->segment->pos) {
1611 do_prev_segment(&media_player);
1612 }
1613
1614 set_track_position(media_player.group->track->segment->pos);
1615 } else {
1616 result_code = MEDIA_PROXY_CMD_CANNOT_BE_COMPLETED;
1617 }
1618
1619 break;
1620 case MEDIA_PROXY_OP_NEXT_SEGMENT:
1621 if (media_player.group->track->segment != NULL) {
1622 do_next_segment(&media_player);
1623 set_track_position(media_player.group->track->segment->pos);
1624 } else {
1625 result_code = MEDIA_PROXY_CMD_CANNOT_BE_COMPLETED;
1626 }
1627
1628 break;
1629 case MEDIA_PROXY_OP_FIRST_SEGMENT:
1630 if (media_player.group->track->segment != NULL) {
1631 do_first_segment(&media_player);
1632 set_track_position(media_player.group->track->segment->pos);
1633 } else {
1634 result_code = MEDIA_PROXY_CMD_CANNOT_BE_COMPLETED;
1635 }
1636
1637 break;
1638 case MEDIA_PROXY_OP_LAST_SEGMENT:
1639 if (media_player.group->track->segment != NULL) {
1640 do_last_segment(&media_player);
1641 set_track_position(media_player.group->track->segment->pos);
1642 } else {
1643 result_code = MEDIA_PROXY_CMD_CANNOT_BE_COMPLETED;
1644 }
1645
1646 break;
1647 case MEDIA_PROXY_OP_GOTO_SEGMENT:
1648 if (command->use_param && media_player.group->track->segment != NULL) {
1649 if (command->param != 0) {
1650 do_goto_segment(&media_player, command->param);
1651 }
1652 /* If the argument to "goto segment" is zero, */
1653 /* the segment shall stay the same, and the */
1654 /* track position shall not change. */
1655 } else {
1656 result_code = MEDIA_PROXY_CMD_CANNOT_BE_COMPLETED;
1657 }
1658
1659 break;
1660 case MEDIA_PROXY_OP_PREV_TRACK:
1661 do_prev_track(&media_player);
1662 break;
1663 case MEDIA_PROXY_OP_NEXT_TRACK:
1664 do_next_track(&media_player);
1665 /* For next track, the position is kept if the track */
1666 /* does not change */
1667 break;
1668 case MEDIA_PROXY_OP_FIRST_TRACK:
1669 do_first_track(&media_player, false);
1670 break;
1671 case MEDIA_PROXY_OP_LAST_TRACK:
1672 do_last_track(&media_player);
1673 break;
1674 case MEDIA_PROXY_OP_GOTO_TRACK:
1675 if (command->use_param) {
1676 do_goto_track(&media_player, command->param);
1677 } else {
1678 result_code = MEDIA_PROXY_CMD_CANNOT_BE_COMPLETED;
1679 }
1680
1681 break;
1682 case MEDIA_PROXY_OP_PREV_GROUP:
1683 do_full_prev_group(&media_player);
1684 break;
1685 case MEDIA_PROXY_OP_NEXT_GROUP:
1686 do_full_next_group(&media_player);
1687 break;
1688 case MEDIA_PROXY_OP_FIRST_GROUP:
1689 do_full_first_group(&media_player);
1690 break;
1691 case MEDIA_PROXY_OP_LAST_GROUP:
1692 do_full_last_group(&media_player);
1693 break;
1694 case MEDIA_PROXY_OP_GOTO_GROUP:
1695 if (command->use_param) {
1696 do_full_goto_group(&media_player, command->param);
1697 } else {
1698 result_code = MEDIA_PROXY_CMD_CANNOT_BE_COMPLETED;
1699 }
1700
1701 break;
1702 default:
1703 LOG_DBG("Invalid command: %d", command->opcode);
1704 result_code = MEDIA_PROXY_CMD_NOT_SUPPORTED;
1705 break;
1706 }
1707
1708 return result_code;
1709 }
1710
seeking_state_command_handler(const struct mpl_cmd * command)1711 static uint8_t seeking_state_command_handler(const struct mpl_cmd *command)
1712 {
1713 uint8_t result_code = MEDIA_PROXY_CMD_SUCCESS;
1714
1715 LOG_DBG("Command opcode: %d", command->opcode);
1716 if (IS_ENABLED(CONFIG_BT_MPL_LOG_LEVEL_DBG)) {
1717 if (command->use_param) {
1718 LOG_DBG("Command parameter: %d", command->param);
1719 }
1720 }
1721
1722 switch (command->opcode) {
1723 case MEDIA_PROXY_OP_PLAY:
1724 media_player.seeking_speed_factor = MEDIA_PROXY_SEEKING_SPEED_FACTOR_ZERO;
1725 mpl_set_state(MEDIA_PROXY_STATE_PLAYING);
1726 media_proxy_pl_seeking_speed_cb(media_player.seeking_speed_factor);
1727 break;
1728 case MEDIA_PROXY_OP_PAUSE:
1729 media_player.seeking_speed_factor = MEDIA_PROXY_SEEKING_SPEED_FACTOR_ZERO;
1730 /* TODO: Set track and track position */
1731 mpl_set_state(MEDIA_PROXY_STATE_PAUSED);
1732 media_proxy_pl_seeking_speed_cb(media_player.seeking_speed_factor);
1733 break;
1734 case MEDIA_PROXY_OP_FAST_REWIND:
1735 /* TODO: Here, and for FAST_FORWARD */
1736 /* Decide on algorithm for multiple presses - add step (as */
1737 /* now) or double/half? */
1738 /* What about FR followed by FF? */
1739 /* Currently, the seeking speed may also become zero */
1740 /* Lowest value allowed by spec is -64, notify on change only */
1741 if (media_player.seeking_speed_factor >= -(MEDIA_PROXY_SEEKING_SPEED_FACTOR_MAX
1742 - MPL_SEEKING_SPEED_FACTOR_STEP)) {
1743 media_player.seeking_speed_factor -= MPL_SEEKING_SPEED_FACTOR_STEP;
1744 media_proxy_pl_seeking_speed_cb(media_player.seeking_speed_factor);
1745 }
1746 break;
1747 case MEDIA_PROXY_OP_FAST_FORWARD:
1748 /* Highest value allowed by spec is 64, notify on change only */
1749 if (media_player.seeking_speed_factor <= (MEDIA_PROXY_SEEKING_SPEED_FACTOR_MAX
1750 - MPL_SEEKING_SPEED_FACTOR_STEP)) {
1751 media_player.seeking_speed_factor += MPL_SEEKING_SPEED_FACTOR_STEP;
1752 media_proxy_pl_seeking_speed_cb(media_player.seeking_speed_factor);
1753 }
1754 break;
1755 case MEDIA_PROXY_OP_STOP:
1756 media_player.seeking_speed_factor = MEDIA_PROXY_SEEKING_SPEED_FACTOR_ZERO;
1757 set_track_position(0);
1758 mpl_set_state(MEDIA_PROXY_STATE_PAUSED);
1759 media_proxy_pl_seeking_speed_cb(media_player.seeking_speed_factor);
1760 break;
1761 case MEDIA_PROXY_OP_MOVE_RELATIVE:
1762 if (command->use_param) {
1763 set_relative_track_position(command->param);
1764 } else {
1765 result_code = MEDIA_PROXY_CMD_CANNOT_BE_COMPLETED;
1766 }
1767
1768 break;
1769 case MEDIA_PROXY_OP_PREV_SEGMENT:
1770 /* Switch to previous segment if we are less than 5 seconds */
1771 /* into the segment, otherwise go to start of segment */
1772 if (media_player.track_pos - PREV_MARGIN <
1773 media_player.group->track->segment->pos) {
1774 do_prev_segment(&media_player);
1775 }
1776 set_track_position(media_player.group->track->segment->pos);
1777 break;
1778 case MEDIA_PROXY_OP_NEXT_SEGMENT:
1779 do_next_segment(&media_player);
1780 set_track_position(media_player.group->track->segment->pos);
1781 break;
1782 case MEDIA_PROXY_OP_FIRST_SEGMENT:
1783 do_first_segment(&media_player);
1784 set_track_position(media_player.group->track->segment->pos);
1785 break;
1786 case MEDIA_PROXY_OP_LAST_SEGMENT:
1787 do_last_segment(&media_player);
1788 set_track_position(media_player.group->track->segment->pos);
1789 break;
1790 case MEDIA_PROXY_OP_GOTO_SEGMENT:
1791 if (command->use_param) {
1792 if (command->param != 0) {
1793 do_goto_segment(&media_player, command->param);
1794 }
1795 /* If the argument to "goto segment" is zero, */
1796 /* the segment shall stay the same, and the */
1797 /* track position shall not change. */
1798 } else {
1799 result_code = MEDIA_PROXY_CMD_CANNOT_BE_COMPLETED;
1800 }
1801 break;
1802 case MEDIA_PROXY_OP_PREV_TRACK:
1803 do_prev_track(&media_player);
1804 media_player.seeking_speed_factor = MEDIA_PROXY_SEEKING_SPEED_FACTOR_ZERO;
1805 mpl_set_state(MEDIA_PROXY_STATE_PAUSED);
1806 break;
1807 case MEDIA_PROXY_OP_NEXT_TRACK:
1808 do_next_track(&media_player);
1809 /* For next track, the position is kept if the track */
1810 /* does not change */
1811 media_player.seeking_speed_factor = MEDIA_PROXY_SEEKING_SPEED_FACTOR_ZERO;
1812 mpl_set_state(MEDIA_PROXY_STATE_PAUSED);
1813 break;
1814 case MEDIA_PROXY_OP_FIRST_TRACK:
1815 do_first_track(&media_player, false);
1816 media_player.seeking_speed_factor = MEDIA_PROXY_SEEKING_SPEED_FACTOR_ZERO;
1817 mpl_set_state(MEDIA_PROXY_STATE_PAUSED);
1818 break;
1819 case MEDIA_PROXY_OP_LAST_TRACK:
1820 do_last_track(&media_player);
1821 media_player.seeking_speed_factor = MEDIA_PROXY_SEEKING_SPEED_FACTOR_ZERO;
1822 mpl_set_state(MEDIA_PROXY_STATE_PAUSED);
1823 break;
1824 case MEDIA_PROXY_OP_GOTO_TRACK:
1825 if (command->use_param) {
1826 do_goto_track(&media_player, command->param);
1827 media_player.seeking_speed_factor = MEDIA_PROXY_SEEKING_SPEED_FACTOR_ZERO;
1828 mpl_set_state(MEDIA_PROXY_STATE_PAUSED);
1829 } else {
1830 result_code = MEDIA_PROXY_CMD_CANNOT_BE_COMPLETED;
1831 }
1832 break;
1833 case MEDIA_PROXY_OP_PREV_GROUP:
1834 do_full_prev_group(&media_player);
1835 mpl_set_state(MEDIA_PROXY_STATE_PAUSED);
1836 break;
1837 case MEDIA_PROXY_OP_NEXT_GROUP:
1838 do_full_next_group(&media_player);
1839 mpl_set_state(MEDIA_PROXY_STATE_PAUSED);
1840 break;
1841 case MEDIA_PROXY_OP_FIRST_GROUP:
1842 do_full_first_group(&media_player);
1843 mpl_set_state(MEDIA_PROXY_STATE_PAUSED);
1844 break;
1845 case MEDIA_PROXY_OP_LAST_GROUP:
1846 do_full_last_group(&media_player);
1847 mpl_set_state(MEDIA_PROXY_STATE_PAUSED);
1848 break;
1849 case MEDIA_PROXY_OP_GOTO_GROUP:
1850 if (command->use_param) {
1851 do_full_goto_group(&media_player, command->param);
1852 mpl_set_state(MEDIA_PROXY_STATE_PAUSED);
1853 } else {
1854 result_code = MEDIA_PROXY_CMD_CANNOT_BE_COMPLETED;
1855 }
1856 break;
1857 default:
1858 LOG_DBG("Invalid command: %d", command->opcode);
1859 result_code = MEDIA_PROXY_CMD_NOT_SUPPORTED;
1860 break;
1861 }
1862
1863 return result_code;
1864 }
1865
1866 static uint8_t (*command_handlers[MEDIA_PROXY_STATE_LAST])(const struct mpl_cmd *command) = {
1867 inactive_state_command_handler,
1868 playing_state_command_handler,
1869 paused_state_command_handler,
1870 seeking_state_command_handler,
1871 };
1872
1873 #ifdef CONFIG_BT_MPL_OBJECTS
1874 /* Find a track by ID
1875 *
1876 * If found, return pointers to the group of the track and the track,
1877 * otherwise, the pointers returned are NULL
1878 *
1879 * Returns true if found, false otherwise
1880 */
find_track_by_id(const struct mpl_mediaplayer * pl,uint64_t id,struct mpl_group ** group,struct mpl_track ** track)1881 static bool find_track_by_id(const struct mpl_mediaplayer *pl, uint64_t id,
1882 struct mpl_group **group, struct mpl_track **track)
1883 {
1884 struct mpl_group *tmp_group = pl->group;
1885 struct mpl_track *tmp_track;
1886
1887 while (tmp_group->prev != NULL) {
1888 tmp_group = tmp_group->prev;
1889 }
1890
1891 while (tmp_group != NULL) {
1892 tmp_track = tmp_group->track;
1893
1894 while (tmp_track->prev != NULL) {
1895 tmp_track = tmp_track->prev;
1896 }
1897
1898 while (tmp_track != 0) {
1899 if (tmp_track->id == id) {
1900 /* Found the track */
1901 *group = tmp_group;
1902 *track = tmp_track;
1903 return true;
1904 }
1905
1906 tmp_track = tmp_track->next;
1907 }
1908
1909 tmp_group = tmp_group->next;
1910 }
1911
1912 /* Track not found */
1913 *group = NULL;
1914 *track = NULL;
1915 return false;
1916 }
1917
1918 /* Find a group by ID
1919 *
1920 * If found, return pointer to the group, otherwise, the pointer returned is NULL
1921 *
1922 * Returns true if found, false otherwise
1923 */
find_group_by_id(const struct mpl_mediaplayer * pl,uint64_t id,struct mpl_group ** group)1924 static bool find_group_by_id(const struct mpl_mediaplayer *pl, uint64_t id,
1925 struct mpl_group **group)
1926 {
1927 struct mpl_group *tmp_group = pl->group;
1928
1929 while (tmp_group->prev != NULL) {
1930 tmp_group = tmp_group->prev;
1931 }
1932
1933 while (tmp_group != NULL) {
1934 if (tmp_group->id == id) {
1935 /* Found the group */
1936 *group = tmp_group;
1937 return true;
1938 }
1939
1940 tmp_group = tmp_group->next;
1941 }
1942
1943 /* Group not found */
1944 *group = NULL;
1945 return false;
1946 }
1947 #endif /* CONFIG_BT_MPL_OBJECTS */
1948
get_player_name(void)1949 static const char *get_player_name(void)
1950 {
1951 return media_player.name;
1952 }
1953
1954 #ifdef CONFIG_BT_MPL_OBJECTS
get_icon_id(void)1955 static uint64_t get_icon_id(void)
1956 {
1957 return media_player.icon_id;
1958 }
1959 #endif /* CONFIG_BT_MPL_OBJECTS */
1960
get_icon_url(void)1961 static const char *get_icon_url(void)
1962 {
1963 return media_player.icon_url;
1964 }
1965
get_track_title(void)1966 static const char *get_track_title(void)
1967 {
1968 return media_player.group->track->title;
1969 }
1970
get_track_duration(void)1971 static int32_t get_track_duration(void)
1972 {
1973 return media_player.group->track->duration;
1974 }
1975
get_track_position(void)1976 static int32_t get_track_position(void)
1977 {
1978 return media_player.track_pos;
1979 }
1980
set_track_position(int32_t position)1981 static void set_track_position(int32_t position)
1982 {
1983 int32_t old_pos = media_player.track_pos;
1984 int32_t new_pos;
1985
1986 if (position >= 0) {
1987 if (position > media_player.group->track->duration) {
1988 /* Do not go beyond end of track */
1989 new_pos = media_player.group->track->duration;
1990 } else {
1991 new_pos = position;
1992 }
1993 } else {
1994 /* Negative position, handle as offset from _end_ of track */
1995 /* (Note minus sign below) */
1996 if (position < -media_player.group->track->duration) {
1997 new_pos = 0;
1998 } else {
1999 /* (Remember position is negative) */
2000 new_pos = media_player.group->track->duration + position;
2001 }
2002 }
2003
2004 LOG_DBG("Pos. given: %d, resulting pos.: %d (duration is %d)", position, new_pos,
2005 media_player.group->track->duration);
2006
2007 /* Notify when the position changes when not in the playing state, or if the position is set
2008 * to 0 which is a special value that typically indicates that the track has stopped or
2009 * changed. Since this might occur when media_player.group->track->duration is still 0, we
2010 * should always notify this value.
2011 */
2012 if (new_pos != old_pos || new_pos == 0) {
2013 /* Set new position and notify it */
2014 media_player.track_pos = new_pos;
2015
2016 /* MCS 1.0, section 3.7.1, states:
2017 * to avoid an excessive number of notifications, the Track Position should
2018 * not be notified when the Media State is set to “Playing” and playback happens
2019 * at a constant speed.
2020 */
2021 if (media_player.state != MEDIA_PROXY_STATE_PLAYING) {
2022 media_proxy_pl_track_position_cb(new_pos);
2023 }
2024 }
2025 }
2026
set_relative_track_position(int32_t rel_pos)2027 static void set_relative_track_position(int32_t rel_pos)
2028 {
2029 int64_t pos;
2030
2031 pos = media_player.track_pos + rel_pos;
2032 /* Clamp to allowed values */
2033 pos = CLAMP(pos, 0, media_player.group->track->duration);
2034
2035 set_track_position((int32_t)pos);
2036 }
2037
get_playback_speed(void)2038 static int8_t get_playback_speed(void)
2039 {
2040 return media_player.playback_speed_param;
2041 }
2042
set_playback_speed(int8_t speed)2043 static void set_playback_speed(int8_t speed)
2044 {
2045 /* Set new speed parameter and notify, if different from current */
2046 if (speed != media_player.playback_speed_param) {
2047 media_player.playback_speed_param = speed;
2048 media_proxy_pl_playback_speed_cb(media_player.playback_speed_param);
2049 }
2050 }
2051
get_seeking_speed(void)2052 static int8_t get_seeking_speed(void)
2053 {
2054 return media_player.seeking_speed_factor;
2055 }
2056
2057 #ifdef CONFIG_BT_MPL_OBJECTS
get_track_segments_id(void)2058 static uint64_t get_track_segments_id(void)
2059 {
2060 return media_player.group->track->segments_id;
2061 }
2062
get_current_track_id(void)2063 static uint64_t get_current_track_id(void)
2064 {
2065 return media_player.group->track->id;
2066 }
2067
set_current_track_id(uint64_t id)2068 static void set_current_track_id(uint64_t id)
2069 {
2070 struct mpl_group *group;
2071 struct mpl_track *track;
2072
2073 LOG_DBG_OBJ_ID("Track ID to set: ", id);
2074
2075 if (find_track_by_id(&media_player, id, &group, &track)) {
2076 if (media_player.group != group) {
2077 media_player.group = group;
2078 do_group_change_notifications(&media_player);
2079
2080 /* Group change implies track change (even if same track in other group) */
2081 media_player.group->track = track;
2082 do_track_change_notifications(&media_player);
2083
2084 } else if (media_player.group->track != track) {
2085 media_player.group->track = track;
2086 do_track_change_notifications(&media_player);
2087 }
2088 return;
2089 }
2090
2091 LOG_DBG("Track not found");
2092
2093 /* TODO: Should an error be returned here?
2094 * That would require a rewrite of the MPL api to add return values to the functions.
2095 */
2096 }
2097
get_next_track_id(void)2098 static uint64_t get_next_track_id(void)
2099 {
2100 /* If the next track has been set explicitly */
2101 if (media_player.next_track_set) {
2102 return media_player.next.track->id;
2103 }
2104
2105 /* Normal playing order */
2106 if (media_player.group->track->next) {
2107 return media_player.group->track->next->id;
2108 }
2109
2110 /* Return zero value to indicate that there is no next track */
2111 return MPL_NO_TRACK_ID;
2112 }
2113
set_next_track_id(uint64_t id)2114 static void set_next_track_id(uint64_t id)
2115 {
2116 struct mpl_group *group;
2117 struct mpl_track *track;
2118
2119 LOG_DBG_OBJ_ID("Next Track ID to set: ", id);
2120
2121 if (find_track_by_id(&media_player, id, &group, &track)) {
2122
2123 media_player.next_track_set = true;
2124 media_player.next.group = group;
2125 media_player.next.track = track;
2126 media_proxy_pl_next_track_id_cb(id);
2127 return;
2128 }
2129
2130 LOG_DBG("Track not found");
2131 }
2132
get_parent_group_id(void)2133 static uint64_t get_parent_group_id(void)
2134 {
2135 return media_player.group->parent->id;
2136 }
2137
get_current_group_id(void)2138 static uint64_t get_current_group_id(void)
2139 {
2140 return media_player.group->id;
2141 }
2142
set_current_group_id(uint64_t id)2143 static void set_current_group_id(uint64_t id)
2144 {
2145 struct mpl_group *group;
2146
2147 LOG_DBG_OBJ_ID("Group ID to set: ", id);
2148
2149 if (find_group_by_id(&media_player, id, &group)) {
2150
2151 if (media_player.group != group) {
2152 /* Change to found group */
2153 media_player.group = group;
2154 do_group_change_notifications(&media_player);
2155
2156 /* And change to first track in group */
2157 do_first_track(&media_player, false);
2158 }
2159 return;
2160 }
2161
2162 LOG_DBG("Group not found");
2163 }
2164 #endif /* CONFIG_BT_MPL_OBJECTS */
2165
get_playing_order(void)2166 static uint8_t get_playing_order(void)
2167 {
2168 return media_player.playing_order;
2169 }
2170
set_playing_order(uint8_t order)2171 static void set_playing_order(uint8_t order)
2172 {
2173 if (order != media_player.playing_order) {
2174 if (BIT(order - 1) & media_player.playing_orders_supported) {
2175 media_player.playing_order = order;
2176 media_proxy_pl_playing_order_cb(media_player.playing_order);
2177 }
2178 }
2179 }
2180
get_playing_orders_supported(void)2181 static uint16_t get_playing_orders_supported(void)
2182 {
2183 return media_player.playing_orders_supported;
2184 }
2185
get_media_state(void)2186 static uint8_t get_media_state(void)
2187 {
2188 return media_player.state;
2189 }
2190
send_command(const struct mpl_cmd * command)2191 static void send_command(const struct mpl_cmd *command)
2192 {
2193 struct mpl_cmd_ntf ntf;
2194
2195 if (command->use_param) {
2196 LOG_DBG("opcode: %d, param: %d", command->opcode, command->param);
2197 } else {
2198 LOG_DBG("opcode: %d", command->opcode);
2199 }
2200
2201 if (media_player.state < MEDIA_PROXY_STATE_LAST) {
2202 ntf.requested_opcode = command->opcode;
2203 ntf.result_code = command_handlers[media_player.state](command);
2204
2205 media_proxy_pl_command_cb(&ntf);
2206 } else {
2207 LOG_DBG("INVALID STATE");
2208 }
2209 }
2210
get_commands_supported(void)2211 static uint32_t get_commands_supported(void)
2212 {
2213 return media_player.opcodes_supported;
2214 }
2215
2216 #ifdef CONFIG_BT_MPL_OBJECTS
2217
parse_sci(struct bt_data * data,void * user_data)2218 static bool parse_sci(struct bt_data *data, void *user_data)
2219 {
2220 LOG_DBG("type: %u len %u", data->type, data->data_len);
2221 LOG_HEXDUMP_DBG(data->data, data->data_len, "param:");
2222
2223 if (data->type < MEDIA_PROXY_SEARCH_TYPE_TRACK_NAME ||
2224 data->type > MEDIA_PROXY_SEARCH_TYPE_ONLY_GROUPS) {
2225 LOG_DBG("Invalid search type: %u", data->type);
2226 return false;
2227 }
2228
2229 return true;
2230 }
2231
parse_search(const struct mpl_search * search)2232 static void parse_search(const struct mpl_search *search)
2233 {
2234 bool search_failed = false;
2235
2236 if (search->len > SEARCH_LEN_MAX) {
2237 LOG_WRN("Search too long (%d) - aborting", search->len);
2238 search_failed = true;
2239 } else {
2240 uint8_t search_ltv[SEARCH_LEN_MAX];
2241 struct net_buf_simple buf;
2242
2243 /* Copy so that we can parse it using the net_buf_simple when search is const */
2244 memcpy(search_ltv, search->search, search->len);
2245
2246 net_buf_simple_init_with_data(&buf, search_ltv, search->len);
2247
2248 bt_data_parse(&buf, parse_sci, NULL);
2249
2250 if (buf.len != 0U) {
2251 search_failed = true;
2252 }
2253 }
2254
2255 /* TODO: Add real search functionality. */
2256 /* For now, just fake it. */
2257
2258 if (search_failed) {
2259 media_player.search_results_id = 0;
2260 media_proxy_pl_search_cb(MEDIA_PROXY_SEARCH_FAILURE);
2261 } else {
2262 /* Use current group as search result for now */
2263 media_player.search_results_id = media_player.group->id;
2264 media_proxy_pl_search_cb(MEDIA_PROXY_SEARCH_SUCCESS);
2265 }
2266
2267 media_proxy_pl_search_results_id_cb(media_player.search_results_id);
2268 }
2269
send_search(const struct mpl_search * search)2270 static void send_search(const struct mpl_search *search)
2271 {
2272 if (search->len > SEARCH_LEN_MAX) {
2273 LOG_WRN("Search too long: %d", search->len);
2274 }
2275
2276 LOG_HEXDUMP_DBG(search->search, search->len, "Search");
2277
2278 parse_search(search);
2279 }
2280
get_search_results_id(void)2281 static uint64_t get_search_results_id(void)
2282 {
2283 return media_player.search_results_id;
2284 }
2285 #endif /* CONFIG_BT_MPL_OBJECTS */
2286
get_content_ctrl_id(void)2287 static uint8_t get_content_ctrl_id(void)
2288 {
2289 return media_player.content_ctrl_id;
2290 }
2291
pos_work_cb(struct k_work * work)2292 static void pos_work_cb(struct k_work *work)
2293 {
2294 const int32_t pos_diff_cs = TRACK_POS_WORK_DELAY_MS / 10; /* position is in centiseconds*/
2295
2296 if (media_player.state == MEDIA_PROXY_STATE_SEEKING) {
2297 /* When seeking, apply the seeking speed factor */
2298 set_relative_track_position(pos_diff_cs * media_player.seeking_speed_factor);
2299 } else if (media_player.state == MEDIA_PROXY_STATE_PLAYING) {
2300 set_relative_track_position(pos_diff_cs);
2301 }
2302
2303 if (media_player.track_pos == media_player.group->track->duration) {
2304 /* Go to next track */
2305 do_next_track(&media_player);
2306 }
2307
2308 (void)k_work_schedule(&media_player.pos_work, TRACK_POS_WORK_DELAY);
2309 }
2310
media_proxy_pl_init(void)2311 int media_proxy_pl_init(void)
2312 {
2313 static bool initialized;
2314 int ret;
2315
2316 if (initialized) {
2317 LOG_DBG("Already initialized");
2318 return -EALREADY;
2319 }
2320
2321 /* Get a Content Control ID */
2322 ret = bt_ccid_alloc_value();
2323 if (ret < 0) {
2324 LOG_DBG("Could not allocate CCID: %d", ret);
2325 return ret;
2326 }
2327 media_player.content_ctrl_id = (uint8_t)ret;
2328
2329 /* Set up the media control service */
2330 /* TODO: Fix initialization - who initializes what
2331 * https://github.com/zephyrproject-rtos/zephyr/issues/42965
2332 * Temporarily only initializing if service is present
2333 */
2334 #ifdef CONFIG_BT_MCS
2335 #ifdef CONFIG_BT_MPL_OBJECTS
2336 /* The test here is arguably needed as the objects cannot be accessed before bt_mcs_init is
2337 * called, but the set is to avoid the objects being accessed before properly initialized
2338 */
2339 if (atomic_test_and_set_bit(obj.flags, MPL_OBJ_FLAG_BUSY)) {
2340 LOG_ERR("Object busy");
2341 return -EBUSY;
2342 }
2343
2344 ret = bt_mcs_init(&ots_cbs);
2345 if (ret < 0) {
2346 LOG_ERR("Could not init MCS: %d", ret);
2347 atomic_clear_bit(obj.flags, MPL_OBJ_FLAG_BUSY);
2348
2349 return ret;
2350 }
2351 #else
2352 ret = bt_mcs_init(NULL);
2353 if (ret < 0) {
2354 LOG_ERR("Could not init MCS: %d", ret);
2355 return ret;
2356 }
2357 #endif /* CONFIG_BT_MPL_OBJECTS */
2358 /* TODO: If anything below fails we should unregister MCS */
2359 #else
2360 LOG_WRN("MCS not configured");
2361 #endif /* CONFIG_BT_MCS */
2362
2363 #ifdef CONFIG_BT_MPL_OBJECTS
2364 /* Initialize the object content buffer */
2365 net_buf_simple_init(obj.content, 0);
2366
2367 /* Icon Object */
2368 ret = add_icon_object(&media_player);
2369 if (ret < 0) {
2370 LOG_ERR("Unable to add icon object, error %d", ret);
2371 atomic_clear_bit(obj.flags, MPL_OBJ_FLAG_BUSY);
2372 return ret;
2373 }
2374
2375 /* Add all tracks and groups to OTS */
2376 ret = add_group_and_track_objects(&media_player);
2377 if (ret < 0) {
2378 LOG_ERR("Error adding tracks and groups to OTS, error %d", ret);
2379 atomic_clear_bit(obj.flags, MPL_OBJ_FLAG_BUSY);
2380 return ret;
2381 }
2382
2383 /* Initial setup of Track Segments Object */
2384 /* TODO: Later, this should be done when the tracks are added */
2385 /* but for no only one of the tracks has segments .*/
2386 ret = add_current_track_segments_object(&media_player);
2387 if (ret < 0) {
2388 LOG_ERR("Error adding Track Segments Object to OTS, error %d", ret);
2389 atomic_clear_bit(obj.flags, MPL_OBJ_FLAG_BUSY);
2390 return ret;
2391 }
2392
2393 atomic_clear_bit(obj.flags, MPL_OBJ_FLAG_BUSY);
2394 #endif /* CONFIG_BT_MPL_OBJECTS */
2395
2396 /* Set up the calls structure */
2397 media_player.calls.get_player_name = get_player_name;
2398 #ifdef CONFIG_BT_MPL_OBJECTS
2399 media_player.calls.get_icon_id = get_icon_id;
2400 #endif /* CONFIG_BT_MPL_OBJECTS */
2401 media_player.calls.get_icon_url = get_icon_url;
2402 media_player.calls.get_track_title = get_track_title;
2403 media_player.calls.get_track_duration = get_track_duration;
2404 media_player.calls.get_track_position = get_track_position;
2405 media_player.calls.set_track_position = set_track_position;
2406 media_player.calls.get_playback_speed = get_playback_speed;
2407 media_player.calls.set_playback_speed = set_playback_speed;
2408 media_player.calls.get_seeking_speed = get_seeking_speed;
2409 #ifdef CONFIG_BT_MPL_OBJECTS
2410 media_player.calls.get_track_segments_id = get_track_segments_id;
2411 media_player.calls.get_current_track_id = get_current_track_id;
2412 media_player.calls.set_current_track_id = set_current_track_id;
2413 media_player.calls.get_next_track_id = get_next_track_id;
2414 media_player.calls.set_next_track_id = set_next_track_id;
2415 media_player.calls.get_parent_group_id = get_parent_group_id;
2416 media_player.calls.get_current_group_id = get_current_group_id;
2417 media_player.calls.set_current_group_id = set_current_group_id;
2418 #endif /* CONFIG_BT_MPL_OBJECTS */
2419 media_player.calls.get_playing_order = get_playing_order;
2420 media_player.calls.set_playing_order = set_playing_order;
2421 media_player.calls.get_playing_orders_supported = get_playing_orders_supported;
2422 media_player.calls.get_media_state = get_media_state;
2423 media_player.calls.send_command = send_command;
2424 media_player.calls.get_commands_supported = get_commands_supported;
2425 #ifdef CONFIG_BT_MPL_OBJECTS
2426 media_player.calls.send_search = send_search;
2427 media_player.calls.get_search_results_id = get_search_results_id;
2428 #endif /* CONFIG_BT_MPL_OBJECTS */
2429 media_player.calls.get_content_ctrl_id = get_content_ctrl_id;
2430
2431 ret = media_proxy_pl_register(&media_player.calls);
2432 if (ret < 0) {
2433 LOG_ERR("Unable to register player");
2434 return ret;
2435 }
2436
2437 k_work_init_delayable(&media_player.pos_work, pos_work_cb);
2438
2439 initialized = true;
2440 return 0;
2441 }
2442
2443 #if CONFIG_BT_MPL_LOG_LEVEL_DBG /* Special commands for debugging */
2444
mpl_debug_dump_state(void)2445 void mpl_debug_dump_state(void)
2446 {
2447 #if CONFIG_BT_MPL_OBJECTS
2448 char t[BT_OTS_OBJ_ID_STR_LEN];
2449 struct mpl_group *group;
2450 struct mpl_track *track;
2451 #endif /* CONFIG_BT_MPL_OBJECTS */
2452
2453 LOG_DBG("Mediaplayer name: %s", media_player.name);
2454
2455 #if CONFIG_BT_MPL_OBJECTS
2456 (void)bt_ots_obj_id_to_str(media_player.icon_id, t, sizeof(t));
2457 LOG_DBG("Icon ID: %s", t);
2458 #endif /* CONFIG_BT_MPL_OBJECTS */
2459
2460 LOG_DBG("Icon URL: %s", media_player.icon_url);
2461 LOG_DBG("Track position: %d", media_player.track_pos);
2462 LOG_DBG("Media state: %d", media_player.state);
2463 LOG_DBG("Playback speed parameter: %d", media_player.playback_speed_param);
2464 LOG_DBG("Seeking speed factor: %d", media_player.seeking_speed_factor);
2465 LOG_DBG("Playing order: %d", media_player.playing_order);
2466 LOG_DBG("Playing orders supported: 0x%x", media_player.playing_orders_supported);
2467 LOG_DBG("Opcodes supported: %d", media_player.opcodes_supported);
2468 LOG_DBG("Content control ID: %d", media_player.content_ctrl_id);
2469
2470 #if CONFIG_BT_MPL_OBJECTS
2471 (void)bt_ots_obj_id_to_str(media_player.group->parent->id, t, sizeof(t));
2472 LOG_DBG("Current group's parent: %s", t);
2473
2474 (void)bt_ots_obj_id_to_str(media_player.group->id, t, sizeof(t));
2475 LOG_DBG("Current group: %s", t);
2476
2477 (void)bt_ots_obj_id_to_str(media_player.group->track->id, t, sizeof(t));
2478 LOG_DBG("Current track: %s", t);
2479
2480 if (media_player.next_track_set) {
2481 (void)bt_ots_obj_id_to_str(media_player.next.track->id, t, sizeof(t));
2482 LOG_DBG("Next track: %s", t);
2483 } else if (media_player.group->track->next) {
2484 (void)bt_ots_obj_id_to_str(media_player.group->track->next->id, t,
2485 sizeof(t));
2486 LOG_DBG("Next track: %s", t);
2487 } else {
2488 LOG_DBG("No next track");
2489 }
2490
2491 if (media_player.search_results_id) {
2492 (void)bt_ots_obj_id_to_str(media_player.search_results_id, t, sizeof(t));
2493 LOG_DBG("Search results: %s", t);
2494 } else {
2495 LOG_DBG("No search results");
2496 }
2497
2498 LOG_DBG("Groups and tracks:");
2499 group = media_player.group;
2500
2501 while (group->prev != NULL) {
2502 group = group->prev;
2503 }
2504
2505 while (group) {
2506 (void)bt_ots_obj_id_to_str(group->id, t, sizeof(t));
2507 LOG_DBG("Group: %s, %s", t, group->title);
2508
2509 (void)bt_ots_obj_id_to_str(group->parent->id, t, sizeof(t));
2510 LOG_DBG("\tParent: %s, %s", t, group->parent->title);
2511
2512 track = group->track;
2513 while (track->prev != NULL) {
2514 track = track->prev;
2515 }
2516
2517 while (track) {
2518 (void)bt_ots_obj_id_to_str(track->id, t, sizeof(t));
2519 LOG_DBG("\tTrack: %s, %s, duration: %d", t, track->title, track->duration);
2520 track = track->next;
2521 }
2522
2523 group = group->next;
2524 }
2525 #endif /* CONFIG_BT_MPL_OBJECTS */
2526 }
2527 #endif /* CONFIG_BT_MPL_LOG_LEVEL_DBG */
2528
2529 #if defined(CONFIG_BT_MPL_LOG_LEVEL_DBG) && \
2530 defined(CONFIG_BT_TESTING) /* Special commands for testing */
2531
2532 #if CONFIG_BT_MPL_OBJECTS
mpl_test_unset_parent_group(void)2533 void mpl_test_unset_parent_group(void)
2534 {
2535 LOG_DBG("Setting current group to be it's own parent");
2536 media_player.group->parent = media_player.group;
2537 }
2538 #endif /* CONFIG_BT_MPL_OBJECTS */
2539
mpl_test_media_state_set(uint8_t state)2540 void mpl_test_media_state_set(uint8_t state)
2541 {
2542 mpl_set_state(state);
2543 }
2544
mpl_test_player_name_changed_cb(void)2545 void mpl_test_player_name_changed_cb(void)
2546 {
2547 media_proxy_pl_name_cb(media_player.name);
2548 }
2549
mpl_test_player_icon_url_changed_cb(void)2550 void mpl_test_player_icon_url_changed_cb(void)
2551 {
2552 media_proxy_pl_icon_url_cb(media_player.icon_url);
2553 }
2554
mpl_test_track_changed_cb(void)2555 void mpl_test_track_changed_cb(void)
2556 {
2557 media_proxy_pl_track_changed_cb();
2558 }
2559
mpl_test_title_changed_cb(void)2560 void mpl_test_title_changed_cb(void)
2561 {
2562 media_proxy_pl_track_title_cb(media_player.group->track->title);
2563 }
2564
mpl_test_duration_changed_cb(void)2565 void mpl_test_duration_changed_cb(void)
2566 {
2567 media_proxy_pl_track_duration_cb(media_player.group->track->duration);
2568 }
2569
mpl_test_position_changed_cb(void)2570 void mpl_test_position_changed_cb(void)
2571 {
2572 media_proxy_pl_track_position_cb(media_player.track_pos);
2573 }
2574
mpl_test_playback_speed_changed_cb(void)2575 void mpl_test_playback_speed_changed_cb(void)
2576 {
2577 media_proxy_pl_playback_speed_cb(media_player.playback_speed_param);
2578 }
2579
mpl_test_seeking_speed_changed_cb(void)2580 void mpl_test_seeking_speed_changed_cb(void)
2581 {
2582 media_proxy_pl_seeking_speed_cb(media_player.seeking_speed_factor);
2583 }
2584
2585 #ifdef CONFIG_BT_MPL_OBJECTS
mpl_test_current_track_id_changed_cb(void)2586 void mpl_test_current_track_id_changed_cb(void)
2587 {
2588 media_proxy_pl_current_track_id_cb(media_player.group->track->id);
2589 }
2590
mpl_test_next_track_id_changed_cb(void)2591 void mpl_test_next_track_id_changed_cb(void)
2592 {
2593 media_proxy_pl_next_track_id_cb(media_player.group->track->next->id);
2594 }
2595
mpl_test_parent_group_id_changed_cb(void)2596 void mpl_test_parent_group_id_changed_cb(void)
2597 {
2598 media_proxy_pl_parent_group_id_cb(media_player.group->id);
2599 }
2600
mpl_test_current_group_id_changed_cb(void)2601 void mpl_test_current_group_id_changed_cb(void)
2602 {
2603 media_proxy_pl_current_group_id_cb(media_player.group->id);
2604 }
2605 #endif /* CONFIG_BT_MPL_OBJECTS */
2606
mpl_test_playing_order_changed_cb(void)2607 void mpl_test_playing_order_changed_cb(void)
2608 {
2609 media_proxy_pl_playing_order_cb(media_player.playing_order);
2610 }
2611
mpl_test_media_state_changed_cb(void)2612 void mpl_test_media_state_changed_cb(void)
2613 {
2614 media_proxy_pl_media_state_cb(media_player.playing_order);
2615 }
2616
mpl_test_opcodes_supported_changed_cb(void)2617 void mpl_test_opcodes_supported_changed_cb(void)
2618 {
2619 media_proxy_pl_commands_supported_cb(media_player.opcodes_supported);
2620 }
2621
2622 #ifdef CONFIG_BT_MPL_OBJECTS
mpl_test_search_results_changed_cb(void)2623 void mpl_test_search_results_changed_cb(void)
2624 {
2625 media_proxy_pl_search_cb(media_player.search_results_id);
2626 }
2627 #endif /* CONFIG_BT_MPL_OBJECTS */
2628
2629 #endif /* CONFIG_BT_MPL_LOG_LEVEL_DBG && CONFIG_BT_TESTING */
2630