1 /*
2 *
3 * BlueZ - Bluetooth protocol stack for Linux
4 *
5 * Copyright (C) 2006-2007 Nokia Corporation
6 * Copyright (C) 2004-2009 Marcel Holtmann <marcel@holtmann.org>
7 *
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 *
23 */
24
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
28
29 #include <errno.h>
30
31 #include <glib.h>
32 #include <gdbus.h>
33
34 #include "../src/adapter.h"
35 #include "../src/dbus-common.h"
36
37 #include "log.h"
38 #include "error.h"
39 #include "device.h"
40 #include "avdtp.h"
41 #include "media.h"
42 #include "transport.h"
43 #include "a2dp.h"
44 #include "headset.h"
45 #include "manager.h"
46
47 #ifndef DBUS_TYPE_UNIX_FD
48 #define DBUS_TYPE_UNIX_FD -1
49 #endif
50
51 #define MEDIA_INTERFACE "org.bluez.Media"
52 #define MEDIA_ENDPOINT_INTERFACE "org.bluez.MediaEndpoint"
53
54 #define REQUEST_TIMEOUT (3 * 1000) /* 3 seconds */
55
56 struct media_adapter {
57 bdaddr_t src; /* Adapter address */
58 char *path; /* Adapter path */
59 DBusConnection *conn; /* Adapter connection */
60 GSList *endpoints; /* Endpoints list */
61 };
62
63 struct endpoint_request {
64 DBusMessage *msg;
65 DBusPendingCall *call;
66 media_endpoint_cb_t cb;
67 void *user_data;
68 };
69
70 struct media_endpoint {
71 struct a2dp_sep *sep;
72 char *sender; /* Endpoint DBus bus id */
73 char *path; /* Endpoint object path */
74 char *uuid; /* Endpoint property UUID */
75 uint8_t codec; /* Endpoint codec */
76 uint8_t *capabilities; /* Endpoint property capabilities */
77 size_t size; /* Endpoint capabilities size */
78 guint hs_watch;
79 guint watch;
80 struct endpoint_request *request;
81 struct media_transport *transport;
82 struct media_adapter *adapter;
83 };
84
85 static GSList *adapters = NULL;
86
endpoint_request_free(struct endpoint_request * request)87 static void endpoint_request_free(struct endpoint_request *request)
88 {
89 if (request->call)
90 dbus_pending_call_unref(request->call);
91
92 dbus_message_unref(request->msg);
93 g_free(request);
94 }
95
media_endpoint_cancel(struct media_endpoint * endpoint)96 static void media_endpoint_cancel(struct media_endpoint *endpoint)
97 {
98 struct endpoint_request *request = endpoint->request;
99
100 if (request->call)
101 dbus_pending_call_cancel(request->call);
102
103 endpoint_request_free(request);
104 endpoint->request = NULL;
105 }
106
media_endpoint_remove(struct media_endpoint * endpoint)107 static void media_endpoint_remove(struct media_endpoint *endpoint)
108 {
109 struct media_adapter *adapter = endpoint->adapter;
110
111 if (g_slist_find(adapter->endpoints, endpoint) == NULL)
112 return;
113
114 info("Endpoint unregistered: sender=%s path=%s", endpoint->sender,
115 endpoint->path);
116
117 adapter->endpoints = g_slist_remove(adapter->endpoints, endpoint);
118
119 if (endpoint->sep)
120 a2dp_remove_sep(endpoint->sep);
121
122 if (endpoint->hs_watch)
123 headset_remove_state_cb(endpoint->hs_watch);
124
125 if (endpoint->request)
126 media_endpoint_cancel(endpoint);
127
128 if (endpoint->transport)
129 media_transport_destroy(endpoint->transport);
130
131 g_dbus_remove_watch(adapter->conn, endpoint->watch);
132 g_free(endpoint->capabilities);
133 g_free(endpoint->sender);
134 g_free(endpoint->path);
135 g_free(endpoint->uuid);
136 g_free(endpoint);
137 }
138
media_endpoint_exit(DBusConnection * connection,void * user_data)139 static void media_endpoint_exit(DBusConnection *connection, void *user_data)
140 {
141 struct media_endpoint *endpoint = user_data;
142
143 endpoint->watch = 0;
144 media_endpoint_remove(endpoint);
145 }
146
headset_setconf_cb(struct media_endpoint * endpoint,void * ret,int size,void * user_data)147 static void headset_setconf_cb(struct media_endpoint *endpoint, void *ret,
148 int size, void *user_data)
149 {
150 struct audio_device *dev = user_data;
151
152 if (ret != NULL)
153 return;
154
155 headset_set_state(dev, HEADSET_STATE_DISCONNECTED);
156 }
157
headset_state_changed(struct audio_device * dev,headset_state_t old_state,headset_state_t new_state,void * user_data)158 static void headset_state_changed(struct audio_device *dev,
159 headset_state_t old_state,
160 headset_state_t new_state,
161 void *user_data)
162 {
163 struct media_endpoint *endpoint = user_data;
164
165 DBG("");
166
167 switch (new_state) {
168 case HEADSET_STATE_DISCONNECTED:
169 media_endpoint_clear_configuration(endpoint);
170 break;
171 case HEADSET_STATE_CONNECTING:
172 media_endpoint_set_configuration(endpoint, dev, NULL, 0,
173 headset_setconf_cb, dev);
174 break;
175 case HEADSET_STATE_CONNECTED:
176 break;
177 case HEADSET_STATE_PLAY_IN_PROGRESS:
178 break;
179 case HEADSET_STATE_PLAYING:
180 break;
181 }
182 }
183
media_endpoint_create(struct media_adapter * adapter,const char * sender,const char * path,const char * uuid,gboolean delay_reporting,uint8_t codec,uint8_t * capabilities,int size,int * err)184 static struct media_endpoint *media_endpoint_create(struct media_adapter *adapter,
185 const char *sender,
186 const char *path,
187 const char *uuid,
188 gboolean delay_reporting,
189 uint8_t codec,
190 uint8_t *capabilities,
191 int size,
192 int *err)
193 {
194 struct media_endpoint *endpoint;
195
196 endpoint = g_new0(struct media_endpoint, 1);
197 endpoint->sender = g_strdup(sender);
198 endpoint->path = g_strdup(path);
199 endpoint->uuid = g_strdup(uuid);
200 endpoint->codec = codec;
201
202 if (size > 0) {
203 endpoint->capabilities = g_new(uint8_t, size);
204 memcpy(endpoint->capabilities, capabilities, size);
205 endpoint->size = size;
206 }
207
208 endpoint->adapter = adapter;
209
210 if (strcasecmp(uuid, A2DP_SOURCE_UUID) == 0) {
211 endpoint->sep = a2dp_add_sep(&adapter->src,
212 AVDTP_SEP_TYPE_SOURCE, codec,
213 delay_reporting, endpoint, err);
214 if (endpoint->sep == NULL)
215 goto failed;
216 } else if (strcasecmp(uuid, A2DP_SINK_UUID) == 0) {
217 endpoint->sep = a2dp_add_sep(&adapter->src,
218 AVDTP_SEP_TYPE_SINK, codec,
219 delay_reporting, endpoint, err);
220 if (endpoint->sep == NULL)
221 goto failed;
222 } else if (strcasecmp(uuid, HFP_AG_UUID) == 0 ||
223 g_strcmp0(uuid, HSP_AG_UUID) == 0) {
224 struct audio_device *dev;
225
226 endpoint->hs_watch = headset_add_state_cb(headset_state_changed,
227 endpoint);
228 dev = manager_find_device(NULL, &adapter->src, BDADDR_ANY,
229 AUDIO_HEADSET_INTERFACE, TRUE);
230 if (dev)
231 media_endpoint_set_configuration(endpoint, dev, NULL,
232 0, headset_setconf_cb,
233 dev);
234 } else {
235 if (err)
236 *err = -EINVAL;
237 goto failed;
238 }
239
240 endpoint->watch = g_dbus_add_disconnect_watch(adapter->conn, sender,
241 media_endpoint_exit, endpoint,
242 NULL);
243
244 adapter->endpoints = g_slist_append(adapter->endpoints, endpoint);
245 info("Endpoint registered: sender=%s path=%s", sender, path);
246
247 if (err)
248 *err = 0;
249 return endpoint;
250
251 failed:
252 g_free(endpoint);
253 return NULL;
254 }
255
media_adapter_find_endpoint(struct media_adapter * adapter,const char * sender,const char * path,const char * uuid)256 static struct media_endpoint *media_adapter_find_endpoint(
257 struct media_adapter *adapter,
258 const char *sender,
259 const char *path,
260 const char *uuid)
261 {
262 GSList *l;
263
264 for (l = adapter->endpoints; l; l = l->next) {
265 struct media_endpoint *endpoint = l->data;
266
267 if (sender && g_strcmp0(endpoint->sender, sender) != 0)
268 continue;
269
270 if (path && g_strcmp0(endpoint->path, path) != 0)
271 continue;
272
273 if (uuid && g_strcmp0(endpoint->uuid, uuid) != 0)
274 continue;
275
276 return endpoint;
277 }
278
279 return NULL;
280 }
281
media_endpoint_get_sender(struct media_endpoint * endpoint)282 const char *media_endpoint_get_sender(struct media_endpoint *endpoint)
283 {
284 return endpoint->sender;
285 }
286
parse_properties(DBusMessageIter * props,const char ** uuid,gboolean * delay_reporting,uint8_t * codec,uint8_t ** capabilities,int * size)287 static int parse_properties(DBusMessageIter *props, const char **uuid,
288 gboolean *delay_reporting, uint8_t *codec,
289 uint8_t **capabilities, int *size)
290 {
291 gboolean has_uuid = FALSE;
292 gboolean has_codec = FALSE;
293
294 while (dbus_message_iter_get_arg_type(props) == DBUS_TYPE_DICT_ENTRY) {
295 const char *key;
296 DBusMessageIter value, entry;
297 int var;
298
299 dbus_message_iter_recurse(props, &entry);
300 dbus_message_iter_get_basic(&entry, &key);
301
302 dbus_message_iter_next(&entry);
303 dbus_message_iter_recurse(&entry, &value);
304
305 var = dbus_message_iter_get_arg_type(&value);
306 if (strcasecmp(key, "UUID") == 0) {
307 if (var != DBUS_TYPE_STRING)
308 return -EINVAL;
309 dbus_message_iter_get_basic(&value, uuid);
310 has_uuid = TRUE;
311 } else if (strcasecmp(key, "Codec") == 0) {
312 if (var != DBUS_TYPE_BYTE)
313 return -EINVAL;
314 dbus_message_iter_get_basic(&value, codec);
315 has_codec = TRUE;
316 } else if (strcasecmp(key, "DelayReporting") == 0) {
317 if (var != DBUS_TYPE_BOOLEAN)
318 return -EINVAL;
319 dbus_message_iter_get_basic(&value, delay_reporting);
320 } else if (strcasecmp(key, "Capabilities") == 0) {
321 DBusMessageIter array;
322
323 if (var != DBUS_TYPE_ARRAY)
324 return -EINVAL;
325
326 dbus_message_iter_recurse(&value, &array);
327 dbus_message_iter_get_fixed_array(&array, capabilities,
328 size);
329 }
330
331 dbus_message_iter_next(props);
332 }
333
334 return (has_uuid && has_codec) ? 0 : -EINVAL;
335 }
336
register_endpoint(DBusConnection * conn,DBusMessage * msg,void * data)337 static DBusMessage *register_endpoint(DBusConnection *conn, DBusMessage *msg,
338 void *data)
339 {
340 struct media_adapter *adapter = data;
341 DBusMessageIter args, props;
342 const char *sender, *path, *uuid;
343 gboolean delay_reporting = FALSE;
344 uint8_t codec;
345 uint8_t *capabilities;
346 int size = 0;
347 int err;
348
349 sender = dbus_message_get_sender(msg);
350
351 dbus_message_iter_init(msg, &args);
352
353 dbus_message_iter_get_basic(&args, &path);
354 dbus_message_iter_next(&args);
355
356 if (media_adapter_find_endpoint(adapter, sender, path, NULL) != NULL)
357 return btd_error_already_exists(msg);
358
359 dbus_message_iter_recurse(&args, &props);
360 if (dbus_message_iter_get_arg_type(&props) != DBUS_TYPE_DICT_ENTRY)
361 return btd_error_invalid_args(msg);
362
363 if (parse_properties(&props, &uuid, &delay_reporting, &codec,
364 &capabilities, &size) < 0)
365 return btd_error_invalid_args(msg);
366
367 if (media_endpoint_create(adapter, sender, path, uuid, delay_reporting,
368 codec, capabilities, size, &err) == FALSE) {
369 if (err == -EPROTONOSUPPORT)
370 return btd_error_not_supported(msg);
371 else
372 return btd_error_invalid_args(msg);
373 }
374
375 return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
376 }
377
unregister_endpoint(DBusConnection * conn,DBusMessage * msg,void * data)378 static DBusMessage *unregister_endpoint(DBusConnection *conn, DBusMessage *msg,
379 void *data)
380 {
381 struct media_adapter *adapter = data;
382 struct media_endpoint *endpoint;
383 const char *sender, *path;
384
385 if (!dbus_message_get_args(msg, NULL,
386 DBUS_TYPE_OBJECT_PATH, &path,
387 DBUS_TYPE_INVALID))
388 return NULL;
389
390 sender = dbus_message_get_sender(msg);
391
392 endpoint = media_adapter_find_endpoint(adapter, sender, path, NULL);
393 if (endpoint == NULL)
394 return btd_error_does_not_exist(msg);
395
396 media_endpoint_remove(endpoint);
397
398 return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
399 }
400
401 static GDBusMethodTable media_methods[] = {
402 { "RegisterEndpoint", "oa{sv}", "", register_endpoint },
403 { "UnregisterEndpoint", "o", "", unregister_endpoint },
404 { },
405 };
406
path_free(void * data)407 static void path_free(void *data)
408 {
409 struct media_adapter *adapter = data;
410
411 g_slist_foreach(adapter->endpoints, (GFunc) media_endpoint_release,
412 NULL);
413 g_slist_free(adapter->endpoints);
414
415 dbus_connection_unref(adapter->conn);
416
417 adapters = g_slist_remove(adapters, adapter);
418
419 g_free(adapter->path);
420 g_free(adapter);
421 }
422
media_register(DBusConnection * conn,const char * path,const bdaddr_t * src)423 int media_register(DBusConnection *conn, const char *path, const bdaddr_t *src)
424 {
425 struct media_adapter *adapter;
426
427 if (DBUS_TYPE_UNIX_FD < 0)
428 return -EPERM;
429
430 adapter = g_new0(struct media_adapter, 1);
431 adapter->conn = dbus_connection_ref(conn);
432 bacpy(&adapter->src, src);
433 adapter->path = g_strdup(path);
434
435 if (!g_dbus_register_interface(conn, path, MEDIA_INTERFACE,
436 media_methods, NULL, NULL,
437 adapter, path_free)) {
438 error("D-Bus failed to register %s path", path);
439 path_free(adapter);
440 return -1;
441 }
442
443 adapters = g_slist_append(adapters, adapter);
444
445 return 0;
446 }
447
media_unregister(const char * path)448 void media_unregister(const char *path)
449 {
450 GSList *l;
451
452 for (l = adapters; l; l = l->next) {
453 struct media_adapter *adapter = l->data;
454
455 if (g_strcmp0(path, adapter->path) == 0) {
456 g_dbus_unregister_interface(adapter->conn, path,
457 MEDIA_INTERFACE);
458 return;
459 }
460 }
461 }
462
media_endpoint_get_capabilities(struct media_endpoint * endpoint,uint8_t ** capabilities)463 size_t media_endpoint_get_capabilities(struct media_endpoint *endpoint,
464 uint8_t **capabilities)
465 {
466 *capabilities = endpoint->capabilities;
467 return endpoint->size;
468 }
469
endpoint_reply(DBusPendingCall * call,void * user_data)470 static void endpoint_reply(DBusPendingCall *call, void *user_data)
471 {
472 struct media_endpoint *endpoint = user_data;
473 struct endpoint_request *request = endpoint->request;
474 DBusMessage *reply;
475 DBusError err;
476 gboolean value;
477 void *ret = NULL;
478 int size = -1;
479
480 /* steal_reply will always return non-NULL since the callback
481 * is only called after a reply has been received */
482 reply = dbus_pending_call_steal_reply(call);
483
484 dbus_error_init(&err);
485 if (dbus_set_error_from_message(&err, reply)) {
486 error("Endpoint replied with an error: %s",
487 err.name);
488
489 /* Clear endpoint configuration in case of NO_REPLY error */
490 if (dbus_error_has_name(&err, DBUS_ERROR_NO_REPLY)) {
491 if (request->cb)
492 request->cb(endpoint, NULL, size,
493 request->user_data);
494 media_endpoint_clear_configuration(endpoint);
495 dbus_message_unref(reply);
496 dbus_error_free(&err);
497 return;
498 }
499
500 dbus_error_free(&err);
501 goto done;
502 }
503
504 dbus_error_init(&err);
505 if (dbus_message_is_method_call(request->msg, MEDIA_ENDPOINT_INTERFACE,
506 "SelectConfiguration")) {
507 DBusMessageIter args, array;
508 uint8_t *configuration;
509
510 dbus_message_iter_init(reply, &args);
511
512 dbus_message_iter_recurse(&args, &array);
513
514 dbus_message_iter_get_fixed_array(&array, &configuration, &size);
515
516 ret = configuration;
517 goto done;
518 } else if (!dbus_message_get_args(reply, &err, DBUS_TYPE_INVALID)) {
519 error("Wrong reply signature: %s", err.message);
520 dbus_error_free(&err);
521 goto done;
522 }
523
524 size = 1;
525 value = TRUE;
526 ret = &value;
527
528 done:
529 dbus_message_unref(reply);
530
531 if (request->cb)
532 request->cb(endpoint, ret, size, request->user_data);
533
534 endpoint_request_free(request);
535 endpoint->request = NULL;
536 }
537
media_endpoint_async_call(DBusConnection * conn,DBusMessage * msg,struct media_endpoint * endpoint,media_endpoint_cb_t cb,void * user_data)538 static gboolean media_endpoint_async_call(DBusConnection *conn,
539 DBusMessage *msg,
540 struct media_endpoint *endpoint,
541 media_endpoint_cb_t cb,
542 void *user_data)
543 {
544 struct endpoint_request *request;
545
546 if (endpoint->request)
547 return FALSE;
548
549 request = g_new0(struct endpoint_request, 1);
550
551 /* Timeout should be less than avdtp request timeout (4 seconds) */
552 if (dbus_connection_send_with_reply(conn, msg, &request->call,
553 REQUEST_TIMEOUT) == FALSE) {
554 error("D-Bus send failed");
555 g_free(request);
556 return FALSE;
557 }
558
559 dbus_pending_call_set_notify(request->call, endpoint_reply, endpoint, NULL);
560
561 request->msg = msg;
562 request->cb = cb;
563 request->user_data = user_data;
564 endpoint->request = request;
565
566 DBG("Calling %s: name = %s path = %s", dbus_message_get_member(msg),
567 dbus_message_get_destination(msg),
568 dbus_message_get_path(msg));
569
570 return TRUE;
571 }
572
media_endpoint_set_configuration(struct media_endpoint * endpoint,struct audio_device * device,uint8_t * configuration,size_t size,media_endpoint_cb_t cb,void * user_data)573 gboolean media_endpoint_set_configuration(struct media_endpoint *endpoint,
574 struct audio_device *device,
575 uint8_t *configuration, size_t size,
576 media_endpoint_cb_t cb,
577 void *user_data)
578 {
579 DBusConnection *conn;
580 DBusMessage *msg;
581 const char *path;
582 DBusMessageIter iter;
583
584 if (endpoint->transport != NULL || endpoint->request != NULL)
585 return FALSE;
586
587 conn = endpoint->adapter->conn;
588
589 endpoint->transport = media_transport_create(conn, endpoint, device,
590 configuration, size);
591 if (endpoint->transport == NULL)
592 return FALSE;
593
594 msg = dbus_message_new_method_call(endpoint->sender, endpoint->path,
595 MEDIA_ENDPOINT_INTERFACE,
596 "SetConfiguration");
597 if (msg == NULL) {
598 error("Couldn't allocate D-Bus message");
599 return FALSE;
600 }
601
602 dbus_message_iter_init_append(msg, &iter);
603
604 path = media_transport_get_path(endpoint->transport);
605 dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, &path);
606
607 transport_get_properties(endpoint->transport, &iter);
608
609 return media_endpoint_async_call(conn, msg, endpoint, cb, user_data);
610 }
611
media_endpoint_select_configuration(struct media_endpoint * endpoint,uint8_t * capabilities,size_t length,media_endpoint_cb_t cb,void * user_data)612 gboolean media_endpoint_select_configuration(struct media_endpoint *endpoint,
613 uint8_t *capabilities,
614 size_t length,
615 media_endpoint_cb_t cb,
616 void *user_data)
617 {
618 DBusConnection *conn;
619 DBusMessage *msg;
620
621 if (endpoint->request != NULL)
622 return FALSE;
623
624 conn = endpoint->adapter->conn;
625
626 msg = dbus_message_new_method_call(endpoint->sender, endpoint->path,
627 MEDIA_ENDPOINT_INTERFACE,
628 "SelectConfiguration");
629 if (msg == NULL) {
630 error("Couldn't allocate D-Bus message");
631 return FALSE;
632 }
633
634 dbus_message_append_args(msg, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE,
635 &capabilities, length,
636 DBUS_TYPE_INVALID);
637
638 return media_endpoint_async_call(conn, msg, endpoint, cb, user_data);
639 }
640
media_endpoint_clear_configuration(struct media_endpoint * endpoint)641 void media_endpoint_clear_configuration(struct media_endpoint *endpoint)
642 {
643 DBusConnection *conn;
644 DBusMessage *msg;
645 const char *path;
646
647 if (endpoint->transport == NULL)
648 return;
649
650 if (endpoint->request)
651 media_endpoint_cancel(endpoint);
652
653 conn = endpoint->adapter->conn;
654
655 msg = dbus_message_new_method_call(endpoint->sender, endpoint->path,
656 MEDIA_ENDPOINT_INTERFACE,
657 "ClearConfiguration");
658 if (msg == NULL) {
659 error("Couldn't allocate D-Bus message");
660 goto done;
661 }
662
663 path = media_transport_get_path(endpoint->transport);
664 dbus_message_append_args(msg, DBUS_TYPE_OBJECT_PATH, &path,
665 DBUS_TYPE_INVALID);
666 g_dbus_send_message(conn, msg);
667 done:
668 media_transport_destroy(endpoint->transport);
669 endpoint->transport = NULL;
670 }
671
media_endpoint_release(struct media_endpoint * endpoint)672 void media_endpoint_release(struct media_endpoint *endpoint)
673 {
674 DBusMessage *msg;
675
676 DBG("sender=%s path=%s", endpoint->sender, endpoint->path);
677
678 /* already exit */
679 if (endpoint->watch == 0)
680 return;
681
682 msg = dbus_message_new_method_call(endpoint->sender, endpoint->path,
683 MEDIA_ENDPOINT_INTERFACE,
684 "Release");
685 if (msg == NULL) {
686 error("Couldn't allocate D-Bus message");
687 return;
688 }
689
690 g_dbus_send_message(endpoint->adapter->conn, msg);
691
692 media_endpoint_remove(endpoint);
693 }
694
media_endpoint_get_sep(struct media_endpoint * endpoint)695 struct a2dp_sep *media_endpoint_get_sep(struct media_endpoint *endpoint)
696 {
697 return endpoint->sep;
698 }
699
media_endpoint_get_uuid(struct media_endpoint * endpoint)700 const char *media_endpoint_get_uuid(struct media_endpoint *endpoint)
701 {
702 return endpoint->uuid;
703 }
704
media_endpoint_get_codec(struct media_endpoint * endpoint)705 uint8_t media_endpoint_get_codec(struct media_endpoint *endpoint)
706 {
707 return endpoint->codec;
708 }
709
media_endpoint_get_transport(struct media_endpoint * endpoint)710 struct media_transport *media_endpoint_get_transport(
711 struct media_endpoint *endpoint)
712 {
713 return endpoint->transport;
714 }
715