📄 a2dp.c
字号:
struct avdtp_error start_err; error("avdtp_start failed"); avdtp_error_init(&start_err, AVDTP_ERROR_ERRNO, EIO); setup->err = err; finalize_suspend(setup); }}static gboolean close_ind(struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_stream *stream, uint8_t *err, void *user_data){ struct a2dp_sep *a2dp_sep = user_data; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) debug("SBC Sink: Close_Ind"); else debug("SBC Source: Close_Ind"); return TRUE;}static gboolean reconfigure(gpointer data){ struct a2dp_setup *setup = data; struct avdtp_local_sep *lsep; struct avdtp_remote_sep *rsep; int posix_err; posix_err = avdtp_get_seps(setup->session, AVDTP_SEP_TYPE_SINK, AVDTP_MEDIA_TYPE_AUDIO, A2DP_CODEC_SBC, &lsep, &rsep); if (posix_err < 0) { error("No matching ACP and INT SEPs found"); finalize_config_errno(setup, posix_err); } posix_err = avdtp_set_configuration(setup->session, rsep, lsep, setup->client_caps, &setup->stream); if (posix_err < 0) { error("avdtp_set_configuration: %s", strerror(-posix_err)); finalize_config_errno(setup, posix_err); } return FALSE;}static void close_cfm(struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_stream *stream, struct avdtp_error *err, void *user_data){ struct a2dp_sep *a2dp_sep = user_data; struct a2dp_setup *setup; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) debug("SBC Sink: Close_Cfm"); else debug("SBC Source: Close_Cfm"); setup = find_setup_by_session(session); if (!setup) return; if (setup->canceled) { setup_unref(setup); return; } if (err) { setup->stream = NULL; setup->err = err; finalize_config(setup); return; } if (setup->reconfigure) g_timeout_add(RECONFIGURE_TIMEOUT, reconfigure, setup);}static gboolean abort_ind(struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_stream *stream, uint8_t *err, void *user_data){ struct a2dp_sep *a2dp_sep = user_data; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) debug("SBC Sink: Abort_Ind"); else debug("SBC Source: Abort_Ind"); a2dp_sep->stream = NULL; return TRUE;}static void abort_cfm(struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_stream *stream, struct avdtp_error *err, void *user_data){ struct a2dp_sep *a2dp_sep = user_data; struct a2dp_setup *setup; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) debug("SBC Sink: Abort_Cfm"); else debug("SBC Source: Abort_Cfm"); setup = find_setup_by_session(session); if (!setup) return; setup_unref(setup);}static gboolean reconf_ind(struct avdtp *session, struct avdtp_local_sep *sep, uint8_t *err, void *user_data){ struct a2dp_sep *a2dp_sep = user_data; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) debug("SBC Sink: ReConfigure_Ind"); else debug("SBC Source: ReConfigure_Ind"); return TRUE;}static void reconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep, struct avdtp_stream *stream, struct avdtp_error *err, void *user_data){ struct a2dp_sep *a2dp_sep = user_data; struct a2dp_setup *setup; if (a2dp_sep->type == AVDTP_SEP_TYPE_SINK) debug("SBC Sink: ReConfigure_Cfm"); else debug("SBC Source: ReConfigure_Cfm"); setup = find_setup_by_session(session); if (!setup) return; if (setup->canceled) { if (!err) avdtp_close(session, stream); setup_unref(setup); return; } if (err) { setup->stream = NULL; setup->err = err; finalize_config(setup); } else finalize_config_errno(setup, 0);}static struct avdtp_sep_cfm cfm = { .set_configuration = setconf_cfm, .get_configuration = getconf_cfm, .open = open_cfm, .start = start_cfm, .suspend = suspend_cfm, .close = close_cfm, .abort = abort_cfm, .reconfigure = reconf_cfm};static struct avdtp_sep_ind ind = { .get_capability = getcap_ind, .set_configuration = setconf_ind, .get_configuration = getconf_ind, .open = open_ind, .start = start_ind, .suspend = suspend_ind, .close = close_ind, .abort = abort_ind, .reconfigure = reconf_ind};static int a2dp_source_record(sdp_buf_t *buf){ sdp_list_t *svclass_id, *pfseq, *apseq, *root; uuid_t root_uuid, l2cap, avdtp, a2src; sdp_profile_desc_t profile[1]; sdp_list_t *aproto, *proto[2]; sdp_record_t record; sdp_data_t *psm, *version, *features; uint16_t lp = AVDTP_UUID, ver = 0x0100, feat = 0x000F; int ret = 0; memset(&record, 0, sizeof(sdp_record_t)); sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); root = sdp_list_append(0, &root_uuid); sdp_set_browse_groups(&record, root); sdp_uuid16_create(&a2src, AUDIO_SOURCE_SVCLASS_ID); svclass_id = sdp_list_append(0, &a2src); sdp_set_service_classes(&record, svclass_id); sdp_uuid16_create(&profile[0].uuid, ADVANCED_AUDIO_PROFILE_ID); profile[0].version = 0x0100; pfseq = sdp_list_append(0, &profile[0]); sdp_set_profile_descs(&record, pfseq); sdp_uuid16_create(&l2cap, L2CAP_UUID); proto[0] = sdp_list_append(0, &l2cap); psm = sdp_data_alloc(SDP_UINT16, &lp); proto[0] = sdp_list_append(proto[0], psm); apseq = sdp_list_append(0, proto[0]); sdp_uuid16_create(&avdtp, AVDTP_UUID); proto[1] = sdp_list_append(0, &avdtp); version = sdp_data_alloc(SDP_UINT16, &ver); proto[1] = sdp_list_append(proto[1], version); apseq = sdp_list_append(apseq, proto[1]); aproto = sdp_list_append(0, apseq); sdp_set_access_protos(&record, aproto); features = sdp_data_alloc(SDP_UINT16, &feat); sdp_attr_add(&record, SDP_ATTR_SUPPORTED_FEATURES, features); sdp_set_info_attr(&record, "Audio Source", 0, 0); if (sdp_gen_record_pdu(&record, buf) < 0) ret = -1; else ret = 0; free(psm); free(version); sdp_list_free(proto[0], 0); sdp_list_free(proto[1], 0); sdp_list_free(apseq, 0); sdp_list_free(pfseq, 0); sdp_list_free(aproto, 0); sdp_list_free(root, 0); sdp_list_free(svclass_id, 0); sdp_list_free(record.attrlist, (sdp_free_func_t) sdp_data_free); sdp_list_free(record.pattern, free); return ret;}static int a2dp_sink_record(sdp_buf_t *buf){ return 0;}static struct a2dp_sep *a2dp_add_sep(DBusConnection *conn, uint8_t type){ struct a2dp_sep *sep; GSList **l; int (*create_record)(sdp_buf_t *buf); uint32_t *record_id; sdp_buf_t buf; sep = g_new0(struct a2dp_sep, 1); sep->sep = avdtp_register_sep(type, AVDTP_MEDIA_TYPE_AUDIO, &ind, &cfm, sep); if (sep->sep == NULL) { g_free(sep); return NULL; } sep->type = type; if (type == AVDTP_SEP_TYPE_SOURCE) { l = &sources; create_record = a2dp_source_record; record_id = &source_record_id; } else { l = &sinks; create_record = a2dp_sink_record; record_id = &sink_record_id; } if (*record_id != 0) goto add; if (create_record(&buf) < 0) { error("Unable to allocate new service record"); avdtp_unregister_sep(sep->sep); g_free(sep); return NULL; } *record_id = add_service_record(conn, &buf); free(buf.data); if (!*record_id) { error("Unable to register A2DP service record"); avdtp_unregister_sep(sep->sep); g_free(sep); return NULL; }add: *l = g_slist_append(*l, sep); return sep;}int a2dp_init(DBusConnection *conn, int sources, int sinks){ int i; if (!sources && !sinks) return 0; connection = dbus_connection_ref(conn); avdtp_init(); for (i = 0; i < sources; i++) a2dp_add_sep(conn, AVDTP_SEP_TYPE_SOURCE); for (i = 0; i < sinks; i++) a2dp_add_sep(conn, AVDTP_SEP_TYPE_SINK); return 0;}static void a2dp_unregister_sep(struct a2dp_sep *sep){ avdtp_unregister_sep(sep->sep); g_free(sep);}void a2dp_exit(){ g_slist_foreach(sinks, (GFunc) a2dp_unregister_sep, NULL); g_slist_free(sinks); sinks = NULL; g_slist_foreach(sources, (GFunc) a2dp_unregister_sep, NULL); g_slist_free(sources); sources = NULL; if (source_record_id) { remove_service_record(connection, source_record_id); source_record_id = 0; } if (sink_record_id) { remove_service_record(connection, sink_record_id); sink_record_id = 0; } dbus_connection_unref(connection);}gboolean a2dp_source_cancel(struct device *dev, unsigned int id){ struct a2dp_setup_cb *cb_data; struct a2dp_setup *setup; GSList *l; setup = find_setup_by_dev(dev); if (!setup) return FALSE; for (cb_data = NULL, l = setup->cb; l != NULL; l = g_slist_next(l)) { struct a2dp_setup_cb *cb = l->data; if (cb->id == id) { cb_data = cb; break; } } if (!cb_data) return FALSE; setup->cb = g_slist_remove(setup->cb, cb_data); g_free(cb_data); if (!setup->cb) { setup->canceled = TRUE; setup->sep = NULL; } return TRUE;}unsigned int a2dp_source_config(struct avdtp *session, a2dp_config_cb_t cb, GSList *caps, void *user_data){ struct a2dp_setup_cb *cb_data; GSList *l; struct a2dp_setup *setup; struct a2dp_sep *sep = NULL; struct avdtp_local_sep *lsep; struct avdtp_remote_sep *rsep; int posix_err; for (l = sources; l != NULL; l = l->next) { struct a2dp_sep *tmp = l->data; if (tmp->locked) continue; if (!tmp->stream || avdtp_has_stream(session, tmp->stream)) { sep = tmp; break; } } if (!sep) { error("a2dp_source_cfg: no available SEP found"); return 0; } debug("a2dp_source_config: selected SEP %p", sep); cb_data = g_new0(struct a2dp_setup_cb, 1); cb_data->config_cb = cb; cb_data->user_data = user_data; cb_data->id = ++cb_id; setup = find_setup_by_session(session); if (!setup) { setup = g_new0(struct a2dp_setup, 1); setup->session = avdtp_ref(session); setups = g_slist_append(setups, setup); } setup_ref(setup); setup->cb = g_slist_append(setup->cb, cb_data); setup->sep = sep; setup->stream = sep->stream; setup->client_caps = caps; switch (avdtp_sep_get_state(sep->sep)) { case AVDTP_STATE_IDLE: if (avdtp_get_seps(session, AVDTP_SEP_TYPE_SINK, AVDTP_MEDIA_TYPE_AUDIO, A2DP_CODEC_SBC, &lsep, &rsep) < 0) { error("No matching ACP and INT SEPs found"); goto failed; } posix_err = avdtp_set_configuration(session, rsep, lsep, caps, &setup->stream); if (posix_err < 0) { error("avdtp_set_configuration: %s", strerror(-posix_err)); goto failed; } break; case AVDTP_STATE_OPEN: case AVDTP_STATE_STREAMING: if (avdtp_stream_has_capabilities(setup->stream, caps)) g_idle_add((GSourceFunc) finalize_config, setup); else if (!setup->reconfigure) { setup->reconfigure = TRUE; if (avdtp_close(session, sep->stream) < 0) { error("avdtp_close failed"); goto failed; } } break; default: error("SEP in bad state for requesting a new stream"); goto failed; } return cb_data->id;failed: setup_unref(setup); cb_id--; return 0;}unsigned int a2dp_source_resume(struct avdtp *session, struct a2dp_sep *sep, a2dp_stream_cb_t cb, void *user_data){ struct a2dp_setup_cb *cb_data; struct a2dp_setup *setup; cb_data = g_new0(struct a2dp_setup_cb, 1); cb_data->resume_cb = cb; cb_data->user_data = user_data; cb_data->id = ++cb_id; setup = find_setup_by_session(session); if (!setup) { setup = g_new0(struct a2dp_setup, 1); setup->session = avdtp_ref(session); setups = g_slist_append(setups, setup); } setup_ref(setup); setup->cb = g_slist_append(setup->cb, cb_data); setup->sep = sep; setup->stream = sep->stream; switch (avdtp_sep_get_state(sep->sep)) { case AVDTP_STATE_IDLE: goto failed; break; case AVDTP_STATE_OPEN: if (avdtp_start(session, sep->stream) < 0) { error("avdtp_start failed"); goto failed; } break; case AVDTP_STATE_STREAMING: if (!sep->suspending && sep->suspend_timer) { g_source_remove(sep->suspend_timer); sep->suspend_timer = 0; avdtp_unref(sep->session); sep->session = NULL; } if (sep->suspending) setup->start = TRUE; else g_idle_add((GSourceFunc) finalize_resume, setup); break; default: error("SEP in bad state"); goto failed; } return cb_data->id;failed: setup_unref(setup); cb_id--; return 0;}unsigned int a2dp_source_suspend(struct avdtp *session, struct a2dp_sep *sep, a2dp_stream_cb_t cb, void *user_data){ struct a2dp_setup_cb *cb_data; struct a2dp_setup *setup; cb_data = g_new0(struct a2dp_setup_cb, 1); cb_data->suspend_cb = cb; cb_data->user_data = user_data; cb_data->id = ++cb_id; setup = find_setup_by_session(session); if (!setup) { setup = g_new0(struct a2dp_setup, 1); setup->session = avdtp_ref(session); setups = g_slist_append(setups, setup); } setup_ref(setup); setup->cb = g_slist_append(setup->cb, cb_data); setup->sep = sep; setup->stream = sep->stream; switch (avdtp_sep_get_state(sep->sep)) { case AVDTP_STATE_IDLE: error("a2dp_source_suspend: no stream to suspend"); goto failed; break; case AVDTP_STATE_OPEN: g_idle_add((GSourceFunc) finalize_suspend, setup); break; case AVDTP_STATE_STREAMING: if (avdtp_suspend(session, sep->stream) < 0) { error("avdtp_suspend failed"); goto failed; } break; default: error("SEP in bad state for resume"); goto failed; } return cb_data->id;failed: setup_unref(setup); cb_id--; return 0;}gboolean a2dp_sep_lock(struct a2dp_sep *sep, struct avdtp *session){ if (sep->locked) return FALSE; debug("SBC Source SEP %p locked", sep); sep->locked = TRUE; return TRUE;}gboolean a2dp_sep_unlock(struct a2dp_sep *sep, struct avdtp *session){ avdtp_state_t state; state = avdtp_sep_get_state(sep->sep); sep->locked = FALSE; debug("SBC Source SEP %p unlocked", sep); if (!sep->stream || state == AVDTP_STATE_IDLE) return TRUE; switch (state) { case AVDTP_STATE_OPEN: /* Set timer here */ break; case AVDTP_STATE_STREAMING: if (avdtp_suspend(session, sep->stream) == 0) sep->suspending = TRUE; break; default: break; } return TRUE;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -