From 1dd09dcc22ae16694e7ed504d66e06151bc095c6 Mon Sep 17 00:00:00 2001 From: DoHyun Pyun Date: Tue, 18 Feb 2020 13:12:23 +0900 Subject: [PATCH] Bluetooth : Support AVC target feature BT A2DP stream will be sent to the remote headset by 3 types. (AVC_OFF / AVC_NULL / AVC_MAX) Change-Id: I54e1e75ad401649a04abef9202482fa1216a5421 Signed-off-by: DoHyun Pyun --- packaging/pulseaudio.spec | 2 +- src/modules/bluetooth/bluez5-util.c | 69 ++++++++++++++++++++++++++++ src/modules/bluetooth/bluez5-util.h | 16 +++++++ src/modules/bluetooth/module-bluez5-device.c | 39 ++++++++++++++++ src/pulsecore/sink-input.c | 31 ++++++++++++- src/pulsecore/sink.c | 24 ++++++++++ src/pulsecore/sink.h | 15 ++++++ 7 files changed, 194 insertions(+), 2 deletions(-) diff --git a/packaging/pulseaudio.spec b/packaging/pulseaudio.spec index 1a1076c..0960dc3 100644 --- a/packaging/pulseaudio.spec +++ b/packaging/pulseaudio.spec @@ -160,7 +160,7 @@ cp src/daemon/systemd/system/pulseaudio-tv.service.in src/daemon/systemd/system/ %endif %build -export CFLAGS="%{optflags} -fno-strict-aliasing -DBLUETOOTH_APTX_SUPPORT -D__TIZEN__ -DTIZEN_BT_A2DP_MULTISTREAM -D__TIZEN_BT__ -D__TIZEN_LOG__ %{?asan:-ldl -fPIC }" +export CFLAGS="%{optflags} -fno-strict-aliasing -DBLUETOOTH_APTX_SUPPORT -DTIZEN_BT_AVC_TARGET -D__TIZEN__ -DTIZEN_BT_A2DP_MULTISTREAM -D__TIZEN_BT__ -D__TIZEN_LOG__ %{?asan:-ldl -fPIC }" %if 0%{?sec_build_binary_debug_enable} export CFLAGS+=" -DTIZEN_DEBUG_ENABLE" export CXXFLAGS="$CXXFLAGS -DTIZEN_DEBUG_ENABLE" diff --git a/src/modules/bluetooth/bluez5-util.c b/src/modules/bluetooth/bluez5-util.c index e3cc3dc..70eb7b8 100644 --- a/src/modules/bluetooth/bluez5-util.c +++ b/src/modules/bluetooth/bluez5-util.c @@ -1402,6 +1402,29 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *us return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; #endif +#ifdef TIZEN_BT_AVC_TARGET + } else if (dbus_message_is_signal(m, "org.projectx.bt_event", "AvcModeChanged")) { + pa_bluetooth_transport *t; + dbus_uint32_t mode = PA_BLUETOOTH_AVC_OFF; + + if (!dbus_message_get_args(m, &err, + DBUS_TYPE_UINT32, &mode, + DBUS_TYPE_INVALID)) { + pa_log_error("Failed to parse org.projectx.bt_event.AvcModeChanged: %s", err.message); + goto fail; + } + + if (!(t = pa_hashmap_first(y->transports))) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + pa_log_debug("Previous mode [%d], New mode [%d]", t->avc_mode, mode); + + t->avc_mode = mode; + + pa_hook_fire(&t->device->discovery->hooks[PA_BLUETOOTH_HOOK_AVC_MODE_CHANGED], t); + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +#endif } fail: @@ -1948,6 +1971,46 @@ fail: pa_assert_se(r = dbus_message_new_error(m, "org.bluez.Error.InvalidArguments", "Unable to clear configuration")); return r; } + +#ifdef TIZEN_BT_AVC_TARGET +int pa_bluetooth_transport_get_avc_mode(pa_bluetooth_transport *t, unsigned int *avc_mode) +{ + DBusMessage *m, *r; + DBusError err; + int ret = 0; + uint32_t mode = 0; + + pa_assert(t); + pa_assert(t->device); + pa_assert(t->device->discovery); + + pa_assert_se(m = dbus_message_new_method_call("org.projectx.bt", "/org/projectx/bt_service", "org.projectx.bt", "get_avc_mode")); + + dbus_error_init(&err); + + r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(t->device->discovery->connection), m, -1, &err); + dbus_message_unref(m); + + m = NULL; + if (!r) { + dbus_error_free(&err); + return -1; + } + + if (!dbus_message_get_args(r, &err, DBUS_TYPE_UINT32, &mode, DBUS_TYPE_INVALID)) { + pa_log_error("Failed to parse reply: %s", err.message); + dbus_error_free(&err); + ret = -1; + goto finish; + } + + *avc_mode = mode; + +finish: + dbus_message_unref(r); + return ret; +} +#endif #endif static DBusHandlerResult endpoint_handler(DBusConnection *c, DBusMessage *m, void *userdata) { @@ -2119,6 +2182,9 @@ pa_bluetooth_discovery* pa_bluetooth_discovery_get(pa_core *c, int headset_backe ",arg0='" BLUEZ_MEDIA_TRANSPORT_INTERFACE "'", #ifdef __TIZEN_BT__ "type='signal',interface='org.bluez.ag_agent',member='SuspendMedia'", +#ifdef TIZEN_BT_AVC_TARGET + "type='signal',interface='org.projectx.bt_event',member='AvcModeChanged',path='/org/projectx/bt/avc_mode'", +#endif #endif NULL) < 0) { pa_log_error("Failed to add D-Bus matches: %s", err.message); @@ -2201,6 +2267,9 @@ void pa_bluetooth_discovery_unref(pa_bluetooth_discovery *y) { "member='PropertiesChanged',arg0='" BLUEZ_MEDIA_TRANSPORT_INTERFACE "'", #ifdef __TIZEN_BT__ "type='signal',interface='org.bluez.ag_agent',member='SuspendMedia'", +#ifdef TIZEN_BT_AVC_TARGET + "type='signal',interface='org.projectx.bt_event',member='AvcModeChanged',path='/org/projectx/bt/avc_mode'", +#endif #endif NULL); diff --git a/src/modules/bluetooth/bluez5-util.h b/src/modules/bluetooth/bluez5-util.h index 9b8ed9c..aa384b2 100644 --- a/src/modules/bluetooth/bluez5-util.h +++ b/src/modules/bluetooth/bluez5-util.h @@ -47,6 +47,7 @@ typedef enum pa_bluetooth_hook { PA_BLUETOOTH_HOOK_TRANSPORT_STATE_CHANGED, /* Call data: pa_bluetooth_transport */ #ifdef __TIZEN_BT__ PA_BLUETOOTH_HOOK_SCO_STATE_CHANGED, /* Call data: pa_bluetooth_transport */ + PA_BLUETOOTH_HOOK_AVC_MODE_CHANGED, /* Call data: pa_bluetooth_transport */ #endif PA_BLUETOOTH_HOOK_TRANSPORT_MICROPHONE_GAIN_CHANGED, /* Call data: pa_bluetooth_transport */ PA_BLUETOOTH_HOOK_TRANSPORT_SPEAKER_GAIN_CHANGED, /* Call data: pa_bluetooth_transport */ @@ -68,6 +69,14 @@ typedef enum pa_bluetooth_transport_state { PA_BLUETOOTH_TRANSPORT_STATE_PLAYING } pa_bluetooth_transport_state_t; +#ifdef TIZEN_BT_AVC_TARGET +typedef enum pa_bluetooth_avc_mode { + PA_BLUETOOTH_AVC_OFF, /* Absolute Volume Control (AVC) : No */ + PA_BLUETOOTH_AVC_NULL, /* Absolute Volume Control (AVC) : Volume 0 */ + PA_BLUETOOTH_AVC_MAX /* Absolute Volume Control (AVC) : MAX Volume */ +} pa_bluetooth_avc_mode_t; +#endif + typedef int (*pa_bluetooth_transport_acquire_cb)(pa_bluetooth_transport *t, bool optional, size_t *imtu, size_t *omtu); typedef void (*pa_bluetooth_transport_release_cb)(pa_bluetooth_transport *t); typedef void (*pa_bluetooth_transport_destroy_cb)(pa_bluetooth_transport *t); @@ -88,6 +97,10 @@ struct pa_bluetooth_transport { uint16_t microphone_gain; uint16_t speaker_gain; +#ifdef TIZEN_BT_AVC_TARGET + uint32_t avc_mode; +#endif + pa_bluetooth_transport_state_t state; pa_bluetooth_transport_acquire_cb acquire; @@ -162,6 +175,9 @@ bool pa_bluetooth_device_any_transport_connected(const pa_bluetooth_device *d); #ifdef __TIZEN_BT__ bool pa_bluetooth_device_sink_transport_connected(const pa_bluetooth_device *d); bool pa_bluetooth_device_source_transport_connected(const pa_bluetooth_device *d); +#ifdef TIZEN_BT_AVC_TARGET +int pa_bluetooth_transport_get_avc_mode(pa_bluetooth_transport *t, unsigned int *mode); +#endif #endif pa_bluetooth_device* pa_bluetooth_discovery_get_device_by_path(pa_bluetooth_discovery *y, const char *path); diff --git a/src/modules/bluetooth/module-bluez5-device.c b/src/modules/bluetooth/module-bluez5-device.c index 0e2ca66..e7be9c6 100644 --- a/src/modules/bluetooth/module-bluez5-device.c +++ b/src/modules/bluetooth/module-bluez5-device.c @@ -131,6 +131,9 @@ struct userdata { pa_hook_slot *transport_state_changed_slot; #ifdef __TIZEN_BT__ pa_hook_slot *sco_state_changed_slot; +#ifdef TIZEN_BT_AVC_TARGET + pa_hook_slot *avc_mode_changed_slot; +#endif #endif pa_hook_slot *transport_speaker_gain_changed_slot; pa_hook_slot *transport_microphone_gain_changed_slot; @@ -1438,6 +1441,19 @@ static int add_sink(struct userdata *u) { return -1; } +#ifdef TIZEN_BT_AVC_TARGET + if (u->transport) { + unsigned int avc_mode = PA_BLUETOOTH_AVC_OFF; + + if (pa_bluetooth_transport_get_avc_mode(u->transport, &avc_mode) != 0) + pa_log_info("Fail to get the initial AVC mode"); + + u->transport->avc_mode = avc_mode; + + pa_sink_set_avc_mode(u->sink, u->transport->avc_mode); + } +#endif + u->sink->userdata = u; u->sink->parent.process_msg = sink_process_msg; @@ -2507,6 +2523,21 @@ static pa_hook_result_t sco_state_changed_cb(pa_bluetooth_discovery *y, pa_bluet return PA_HOOK_OK; } + +#ifdef TIZEN_BT_AVC_TARGET +static pa_hook_result_t avc_mode_changed_cb(pa_bluetooth_discovery *y, pa_bluetooth_transport *t, struct userdata *u) { + pa_assert(t); + pa_assert(u); + + if (t == u->transport && t->state <= PA_BLUETOOTH_TRANSPORT_STATE_IDLE) + return PA_HOOK_OK; + + if (t->device == u->device) + pa_sink_set_avc_mode(u->sink, t->avc_mode); + + return PA_HOOK_OK; +} +#endif #endif @@ -2673,6 +2704,12 @@ int pa__init(pa_module* m) { u->sco_state_changed_slot = pa_hook_connect(pa_bluetooth_discovery_hook(u->discovery, PA_BLUETOOTH_HOOK_SCO_STATE_CHANGED), PA_HOOK_NORMAL, (pa_hook_cb_t) sco_state_changed_cb, u); + +#ifdef TIZEN_BT_AVC_TARGET + u->avc_mode_changed_slot = + pa_hook_connect(pa_bluetooth_discovery_hook(u->discovery, PA_BLUETOOTH_HOOK_AVC_MODE_CHANGED), + PA_HOOK_NORMAL, (pa_hook_cb_t) avc_mode_changed_cb, u); +#endif #endif if (add_card(u) < 0) @@ -2750,6 +2787,8 @@ void pa__done(pa_module *m) { #ifdef __TIZEN_BT__ if (u->sco_state_changed_slot) pa_hook_slot_free(u->sco_state_changed_slot); + if (u->avc_mode_changed_slot) + pa_hook_slot_free(u->avc_mode_changed_slot); #endif if (u->transport_microphone_gain_changed_slot) diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c index f0bcf4f..fcde714 100644 --- a/src/pulsecore/sink-input.c +++ b/src/pulsecore/sink-input.c @@ -1086,6 +1086,10 @@ void pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink bytes */, pa size_t ilength; size_t ilength_full; +#ifdef __TIZEN__ + pa_sink_avc_mode_t avc_mode = PA_SINK_AVC_OFF; +#endif + pa_sink_input_assert_ref(i); pa_sink_input_assert_io_context(i); pa_assert(PA_SINK_INPUT_IS_LINKED(i->thread_info.state)); @@ -1137,6 +1141,16 @@ void pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink bytes */, pa volume_is_norm = pa_cvolume_is_norm(&i->thread_info.soft_volume) && !i->thread_info.muted; need_volume_factor_sink = !pa_cvolume_is_norm(&i->volume_factor_sink); +#ifdef __TIZEN__ + avc_mode = pa_sink_get_avc_mode(i->sink); +#ifdef SINK_INPUT_DEBUG + pa_log("idx(%u), avc mode(%d), do(%d), norm(%d), nvfs(%d), muted(%d), resampler(%p)", + i->index, avc_mode, + do_volume_adj_here, volume_is_norm, need_volume_factor_sink, + i->thread_info.muted, i->thread_info.resampler); +#endif +#endif + while (!pa_memblockq_is_readable(i->thread_info.render_memblockq)) { pa_memchunk tchunk; @@ -1179,10 +1193,18 @@ void pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink bytes */, pa wchunk.length = block_size_max_sink_input; /* It might be necessary to adjust the volume here */ +#ifdef __TIZEN__ + if (do_volume_adj_here && !volume_is_norm && avc_mode != PA_SINK_AVC_NORM) { +#else if (do_volume_adj_here && !volume_is_norm) { +#endif pa_memchunk_make_writable(&wchunk, 0); +#ifdef __TIZEN__ + if (i->thread_info.muted || avc_mode == PA_SINK_AVC_SILENT) { +#else if (i->thread_info.muted) { +#endif pa_silence_memchunk(&wchunk, &i->thread_info.sample_spec); nvfs = false; @@ -1202,7 +1224,6 @@ void pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink bytes */, pa #ifdef TIZEN_VOLUME_RAMP check_and_apply_silence(i); - prev_ramp_finished = i->thread_info.ramp.finished; #endif if (!i->thread_info.resampler) { @@ -1263,10 +1284,18 @@ void pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink bytes */, pa /* Let's see if we had to apply the volume adjustment ourselves, * or if this can be done by the sink for us */ +#ifdef __TIZEN__ + if (do_volume_adj_here || avc_mode == PA_SINK_AVC_NORM) +#else if (do_volume_adj_here) +#endif /* We had different channel maps, so we already did the adjustment */ pa_cvolume_reset(volume, i->sink->sample_spec.channels); +#ifdef __TIZEN__ + else if (i->thread_info.muted || avc_mode == PA_SINK_AVC_SILENT) +#else else if (i->thread_info.muted) +#endif /* We've both the same channel map, so let's have the sink do the adjustment for us*/ pa_cvolume_mute(volume, i->sink->sample_spec.channels); else diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c index 7a56869..6c4af87 100644 --- a/src/pulsecore/sink.c +++ b/src/pulsecore/sink.c @@ -4058,3 +4058,27 @@ void pa_sink_set_reference_volume_direct(pa_sink *s, const pa_cvolume *volume) { pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_VOLUME_CHANGED], s); } + +#ifdef __TIZEN__ +/* Called from the main thread. */ +void pa_sink_set_avc_mode(pa_sink *s, pa_sink_avc_mode_t mode) { + pa_proplist *pl = NULL; + + pa_assert(s); + + pl = pa_proplist_new(); + pa_proplist_setf(pl, "device.avc_mode", "%d", mode); + pa_sink_update_proplist(s, PA_UPDATE_REPLACE, pl); + pa_proplist_free(pl); + + pa_log_info("setting avc mode = %d", mode); + + s->avc_mode = mode; +} + +pa_sink_avc_mode_t pa_sink_get_avc_mode(pa_sink *s) { + pa_assert(s); + + return s->avc_mode; +} +#endif \ No newline at end of file diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h index b7d7659..7e967fe 100644 --- a/src/pulsecore/sink.h +++ b/src/pulsecore/sink.h @@ -46,6 +46,14 @@ #define PA_MAX_INPUTS_PER_SINK 256 +#ifdef __TIZEN__ +typedef enum pa_sink_avc_mode { + PA_SINK_AVC_OFF, + PA_SINK_AVC_SILENT, + PA_SINK_AVC_NORM, +} pa_sink_avc_mode_t; +#endif + /* Returns true if sink is linked: registered and accessible from client side. */ static inline bool PA_SINK_IS_LINKED(pa_sink_state_t x) { return x == PA_SINK_RUNNING || x == PA_SINK_IDLE || x == PA_SINK_SUSPENDED; @@ -319,6 +327,7 @@ struct pa_sink { char *dump_path; void *device_item; bool use_internal_codec; + pa_sink_avc_mode_t avc_mode; #endif void *userdata; }; @@ -555,6 +564,12 @@ int64_t pa_sink_get_latency_within_thread(pa_sink *s, bool allow_negative); * s->reference_volume and fires change notifications. */ void pa_sink_set_reference_volume_direct(pa_sink *s, const pa_cvolume *volume); + +#ifdef __TIZEN__ +void pa_sink_set_avc_mode(pa_sink *s, pa_sink_avc_mode_t mode); +pa_sink_avc_mode_t pa_sink_get_avc_mode(pa_sink *s); +#endif + /* Verify that we called in IO context (aka 'thread context), or that * the sink is not yet set up, i.e. the thread not set up yet. See * pa_assert_io_context() in thread-mq.h for more information. */ -- 2.7.4