Bluetooth : Support AVC target feature 75/225175/12 submit/tizen_5.5/20200227.003648
authorDoHyun Pyun <dh79.pyun@samsung.com>
Tue, 18 Feb 2020 04:12:23 +0000 (13:12 +0900)
committerSangchul Lee <sc11.lee@samsung.com>
Wed, 26 Feb 2020 02:07:19 +0000 (11:07 +0900)
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 <dh79.pyun@samsung.com>
packaging/pulseaudio.spec
src/modules/bluetooth/bluez5-util.c
src/modules/bluetooth/bluez5-util.h
src/modules/bluetooth/module-bluez5-device.c
src/pulsecore/sink-input.c
src/pulsecore/sink.c
src/pulsecore/sink.h

index 1a1076c..0960dc3 100644 (file)
@@ -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"
index e3cc3dc..70eb7b8 100644 (file)
@@ -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);
 
index 9b8ed9c..aa384b2 100644 (file)
@@ -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);
index 0e2ca66..e7be9c6 100644 (file)
@@ -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)
index f0bcf4f..fcde714 100644 (file)
@@ -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
index 7a56869..6c4af87 100644 (file)
@@ -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
index b7d7659..7e967fe 100644 (file)
 
 #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. */