?? a2dp.c
字號:
sdp_list_free(record.pattern, free); return ret;}static int a2dp_sink_record(sdp_buf_t *buf){ return -1;}static struct a2dp_sep *a2dp_add_sep(DBusConnection *conn, uint8_t type, uint8_t codec){ struct a2dp_sep *sep; GSList **l; int (*create_record)(sdp_buf_t *buf); uint32_t *record_id; sdp_buf_t buf; struct avdtp_sep_ind *ind; sep = g_new0(struct a2dp_sep, 1); ind = (codec == A2DP_CODEC_MPEG12) ? &mpeg_ind : &sbc_ind; sep->sep = avdtp_register_sep(type, AVDTP_MEDIA_TYPE_AUDIO, codec, ind, &cfm, sep); if (sep->sep == NULL) { g_free(sep); return NULL; } sep->codec = codec; 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; memset(&buf, 0, sizeof(buf)); 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, GKeyFile *config){ int sbc_srcs = 1, sbc_sinks = 0; int mpeg12_srcs = 0, mpeg12_sinks = 0; gboolean source = TRUE, sink = TRUE; char *str; GError *err = NULL; int i; if (!config) goto proceed; str = g_key_file_get_string(config, "General", "Disable", &err); if (err) { debug("audio.conf: %s", err->message); g_error_free(err); err = NULL; } else { if (strstr(str, "Sink")) source = FALSE; if (strstr(str, "Source")) sink = FALSE; g_free(str); } str = g_key_file_get_string(config, "A2DP", "SBCSources", &err); if (err) { debug("audio.conf: %s", err->message); g_error_free(err); err = NULL; } else { sbc_srcs = atoi(str); g_free(str); } str = g_key_file_get_string(config, "A2DP", "MPEG12Sources", &err); if (err) { debug("audio.conf: %s", err->message); g_error_free(err); err = NULL; } else { mpeg12_srcs = atoi(str); g_free(str); } str = g_key_file_get_string(config, "A2DP", "SBCSinks", &err); if (err) { debug("audio.conf: %s", err->message); g_error_free(err); err = NULL; } else { sbc_sinks = atoi(str); g_free(str); } str = g_key_file_get_string(config, "A2DP", "MPEG12Sinks", &err); if (err) { debug("audio.conf: %s", err->message); g_error_free(err); err = NULL; } else { mpeg12_sinks = atoi(str); g_free(str); }proceed: connection = dbus_connection_ref(conn); avdtp_init(); if (source) { for (i = 0; i < sbc_srcs; i++) a2dp_add_sep(conn, AVDTP_SEP_TYPE_SOURCE, A2DP_CODEC_SBC); for (i = 0; i < mpeg12_srcs; i++) a2dp_add_sep(conn, AVDTP_SEP_TYPE_SOURCE, A2DP_CODEC_MPEG12); } if (sink) { for (i = 0; i < sbc_sinks; i++) a2dp_add_sep(conn, AVDTP_SEP_TYPE_SINK, A2DP_CODEC_SBC); for (i = 0; i < mpeg12_sinks; i++) a2dp_add_sep(conn, AVDTP_SEP_TYPE_SINK, A2DP_CODEC_MPEG12); } 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; struct avdtp_service_capability *cap; struct avdtp_media_codec_capability *codec_cap = NULL; int posix_err; for (l = caps; l != NULL; l = l->next) { cap = l->data; if (cap->category != AVDTP_MEDIA_CODEC) continue; codec_cap = (void *) cap->data; break; } if (!codec_cap) return 0; for (l = sources; l != NULL; l = l->next) { struct a2dp_sep *tmp = l->data; if (tmp->locked) continue; if (tmp->codec != codec_cap->media_codec_type) 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->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, codec_cap->media_type, codec_cap->media_codec_type, &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("SEP %p locked", sep->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("SEP %p unlocked", sep->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 + -