<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  </head>
  <body text="#000000" bgcolor="#FFFFFF">
    On 10/3/18 8:37 PM, EHfive wrote:<br>
    <blockquote type="cite"
      cite="mid:a551ef52-6fe5-9b8d-affd-075b43ce4ba4@sokka.cn">Require
      bluez-tools/mpris-proxy running. (No hurt if dosen't)
      <br>
      <br>
      If you need play/pause/next... controls , add configurations below
      to /etc/dbus-1/system.d/
      <br>
      <br>
      ------------------------ mpris.conf
      <br>
      <br>
      <!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus
      Configuration 1.0//EN"
      <br>
       <a class="moz-txt-link-rfc2396E" href="http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd"</a>>
      <br>
      <busconfig>
      <br>
        <policy context="default">
      <br>
          <allow send_interface="org.mpris.MediaPlayer2.Player"/>
      <br>
        </policy>
      <br>
      </busconfig>
      <br>
      <br>
      ------------------------
      <br>
      <br>
    </blockquote>
    An alternative<br>
    <p><a
        href="https://gist.github.com/EHfive/e2a28d0279a6247fab4bac93d73b8571">https://gist.github.com/EHfive/e2a28d0279a6247fab4bac93d73b8571</a><br>
    </p>
    <p>A python script which implement <span class="pl-s">org.mpris.MediaPlayer2.Player
        and register mpris player object by calling
        org.bluez.Media1.RegisterPlayer.</span></p>
    <blockquote type="cite"
      cite="mid:a551ef52-6fe5-9b8d-affd-075b43ce4ba4@sokka.cn">===================
      <br>
      <br>
      diff --git a/src/modules/bluetooth/bluez5-util.c
      b/src/modules/bluetooth/bluez5-util.c
      <br>
      index 2d83373..72cd05a 100644
      <br>
      --- a/src/modules/bluetooth/bluez5-util.c
      <br>
      +++ b/src/modules/bluetooth/bluez5-util.c
      <br>
      @@ -348,6 +348,50 @@ void
      pa_bluetooth_transport_free(pa_bluetooth_transport *t) {
      <br>
           pa_xfree(t);
      <br>
       }
      <br>
      <br>
      +static int bluez5_transport_set_property(pa_bluetooth_transport
      *t, const char *prop_name, int prop_type, void *prop_value){
      <br>
      +    DBusMessage *m, *r;
      <br>
      +    DBusError err;
      <br>
      +    DBusMessageIter i;
      <br>
      +    const char * interface = BLUEZ_MEDIA_TRANSPORT_INTERFACE;
      <br>
      +
      <br>
      +    pa_log_debug("Setting property, Owner: %s; Path: %s;
      Property: %s",t->owner, t->path, prop_name);
      <br>
      +
      <br>
      +    pa_assert(t);
      <br>
      +    pa_assert(t->device);
      <br>
      +    pa_assert(t->device->discovery);
      <br>
      +
      <br>
      +    dbus_error_init(&err);
      <br>
      +
      <br>
      +    pa_assert_se(m = dbus_message_new_method_call(t->owner,
      t->path, "org.freedesktop.DBus.Properties", "Set"));
      <br>
      +
      <br>
      +    dbus_message_iter_init_append(m, &i);
      <br>
      +
      <br>
      +    pa_assert_se(dbus_message_iter_append_basic(&i,
      DBUS_TYPE_STRING, &interface));
      <br>
      +    pa_assert_se(dbus_message_iter_append_basic(&i,
      DBUS_TYPE_STRING, &prop_name));
      <br>
      +    pa_dbus_append_basic_variant(&i, prop_type, prop_value);
      <br>
      +
      <br>
      +    r =
dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(t->device->discovery->connection),
      m, -1, &err);
      <br>
      +    dbus_message_unref(m);
      <br>
      +    m = NULL;
      <br>
      +    if(r) {
      <br>
      +        dbus_message_unref(r);
      <br>
      +        r = NULL;
      <br>
      +    }
      <br>
      +
      <br>
      +    if(dbus_error_is_set(&err)) {
      <br>
      +        pa_log_debug("Failed to set property \"%s.%s\"",
      interface, prop_name);
      <br>
      +        return -1;
      <br>
      +    }
      <br>
      +
      <br>
      +    return 0;
      <br>
      +}
      <br>
      +
      <br>
      +static int bluez5_transport_set_volume(pa_bluetooth_transport *t,
      uint16_t volume){
      <br>
      +    if(t->a2dp_gain == volume)
      <br>
      +        return 0;
      <br>
      +    return bluez5_transport_set_property(t, "Volume",
      DBUS_TYPE_UINT16, &volume);
      <br>
      +}
      <br>
      +
      <br>
       static int bluez5_transport_acquire_cb(pa_bluetooth_transport *t,
      bool optional, size_t *imtu, size_t *omtu) {
      <br>
           DBusMessage *m, *r;
      <br>
           DBusError err;
      <br>
      @@ -441,6 +485,14 @@ bool
      pa_bluetooth_device_any_transport_connected(const
      pa_bluetooth_device *d) {
      <br>
           return false;
      <br>
       }
      <br>
      <br>
      +void pa_transport_set_a2dp_gain(pa_bluetooth_transport *t,
      uint16_t a2dp_gain){
      <br>
      +    if(t->a2dp_gain == a2dp_gain)
      <br>
      +        return;
      <br>
      +    t->a2dp_gain = a2dp_gain;
      <br>
      +
      pa_hook_fire(pa_bluetooth_discovery_hook(t->device->discovery,
      PA_BLUETOOTH_HOOK_TRANSPORT_A2DP_GAIN_CHANGED), t);
      <br>
      +}
      <br>
      +
      <br>
      +
      <br>
       static int transport_state_from_string(const char* value,
      pa_bluetooth_transport_state_t *state) {
      <br>
           pa_assert(value);
      <br>
           pa_assert(state);
      <br>
      @@ -483,6 +535,18 @@ static void
      parse_transport_property(pa_bluetooth_transport *t,
      DBusMessageIter
      <br>
                       pa_bluetooth_transport_set_state(t, state);
      <br>
                   }
      <br>
      <br>
      +            break;
      <br>
      +        }
      <br>
      +        case DBUS_TYPE_UINT16: {
      <br>
      +
      <br>
      +            uint16_t value;
      <br>
      +            dbus_message_iter_get_basic(&variant_i,
      &value);
      <br>
      +
      <br>
      +            if (pa_streq(key, "Volume")) {
      <br>
      +                pa_log_debug("Transport Volume Changed to %u ",
      value);
      <br>
      +                pa_transport_set_a2dp_gain(t, value);
      <br>
      +            }
      <br>
      +
      <br>
                   break;
      <br>
               }
      <br>
           }
      <br>
      @@ -1468,6 +1532,7 @@ static DBusMessage
      *endpoint_set_configuration(DBusConnection *conn, DBusMessage
      <br>
           t = pa_bluetooth_transport_new(d, sender, path, p, config,
      size);
      <br>
           t->acquire = bluez5_transport_acquire_cb;
      <br>
           t->release = bluez5_transport_release_cb;
      <br>
      +    t->set_volume = bluez5_transport_set_volume;
      <br>
           pa_bluetooth_transport_put(t);
      <br>
      <br>
           pa_log_debug("Transport %s available for profile %s",
      t->path, pa_bluetooth_profile_to_string(t->profile));
      <br>
      diff --git a/src/modules/bluetooth/bluez5-util.h
      b/src/modules/bluetooth/bluez5-util.h
      <br>
      index ad30708..5b8149d 100644
      <br>
      --- a/src/modules/bluetooth/bluez5-util.h
      <br>
      +++ b/src/modules/bluetooth/bluez5-util.h
      <br>
      @@ -47,6 +47,7 @@ typedef enum pa_bluetooth_hook {
      <br>
           PA_BLUETOOTH_HOOK_TRANSPORT_STATE_CHANGED,            /* Call
      data: pa_bluetooth_transport */
      <br>
           PA_BLUETOOTH_HOOK_TRANSPORT_MICROPHONE_GAIN_CHANGED,  /* Call
      data: pa_bluetooth_transport */
      <br>
           PA_BLUETOOTH_HOOK_TRANSPORT_SPEAKER_GAIN_CHANGED,     /* Call
      data: pa_bluetooth_transport */
      <br>
      +    PA_BLUETOOTH_HOOK_TRANSPORT_A2DP_GAIN_CHANGED,        /* Call
      data: pa_bluetooth_transport */
      <br>
           PA_BLUETOOTH_HOOK_MAX
      <br>
       } pa_bluetooth_hook_t;
      <br>
      <br>
      @@ -70,6 +71,7 @@ typedef void
      (*pa_bluetooth_transport_release_cb)(pa_bluetooth_transport *t);
      <br>
       typedef void
      (*pa_bluetooth_transport_destroy_cb)(pa_bluetooth_transport *t);
      <br>
       typedef void
      (*pa_bluetooth_transport_set_speaker_gain_cb)(pa_bluetooth_transport
      *t, uint16_t gain);
      <br>
       typedef void
      (*pa_bluetooth_transport_set_microphone_gain_cb)(pa_bluetooth_transport
      *t, uint16_t gain);
      <br>
      +typedef int
      (*pa_bluetooth_transport_set_volume_cb)(pa_bluetooth_transport *t,
      uint16_t volume);
      <br>
      <br>
       struct pa_bluetooth_transport {
      <br>
           pa_bluetooth_device *device;
      <br>
      @@ -84,6 +86,7 @@ struct pa_bluetooth_transport {
      <br>
      <br>
           uint16_t microphone_gain;
      <br>
           uint16_t speaker_gain;
      <br>
      +    uint16_t a2dp_gain;
      <br>
      <br>
           pa_bluetooth_transport_state_t state;
      <br>
      <br>
      @@ -92,6 +95,7 @@ struct pa_bluetooth_transport {
      <br>
           pa_bluetooth_transport_destroy_cb destroy;
      <br>
           pa_bluetooth_transport_set_speaker_gain_cb set_speaker_gain;
      <br>
           pa_bluetooth_transport_set_microphone_gain_cb
      set_microphone_gain;
      <br>
      +    pa_bluetooth_transport_set_volume_cb set_volume;
      <br>
           void *userdata;
      <br>
       };
      <br>
      <br>
      diff --git a/src/modules/bluetooth/module-bluez5-device.c
      b/src/modules/bluetooth/module-bluez5-device.c
      <br>
      index 351ad12..49a6dba 100644
      <br>
      --- a/src/modules/bluetooth/module-bluez5-device.c
      <br>
      +++ b/src/modules/bluetooth/module-bluez5-device.c
      <br>
      @@ -64,6 +64,7 @@ PA_MODULE_USAGE("path=<device object
      path>"
      <br>
       #define BITPOOL_DEC_LIMIT 32
      <br>
       #define BITPOOL_DEC_STEP 5
      <br>
       #define HSP_MAX_GAIN 15
      <br>
      +#define BLUEZ_MAX_GAIN 127
      <br>
      <br>
       static const char* const valid_modargs[] = {
      <br>
           "path",
      <br>
      @@ -113,6 +114,7 @@ struct userdata {
      <br>
           pa_hook_slot *transport_state_changed_slot;
      <br>
           pa_hook_slot *transport_speaker_gain_changed_slot;
      <br>
           pa_hook_slot *transport_microphone_gain_changed_slot;
      <br>
      +    pa_hook_slot *transport_a2dp_gain_changed_slot;
      <br>
      <br>
           pa_bluetooth_discovery *discovery;
      <br>
           pa_bluetooth_device *device;
      <br>
      @@ -1056,6 +1058,37 @@ static void source_set_volume_cb(pa_source
      *s) {
      <br>
           u->transport->set_microphone_gain(u->transport,
      gain);
      <br>
       }
      <br>
      <br>
      +static void source_set_a2dp_volume_cb(pa_source *s) {
      <br>
      +    uint16_t gain;
      <br>
      +    pa_volume_t volume;
      <br>
      +    struct userdata *u;
      <br>
      +
      <br>
      +    pa_assert(s);
      <br>
      +    pa_assert(s->core);
      <br>
      +
      <br>
      +    u = s->userdata;
      <br>
      +
      <br>
      +    pa_assert(u);
      <br>
      +    pa_assert(u->source == s);
      <br>
      +
      <br>
      +    if (u->transport->set_volume == NULL)
      <br>
      +        return;
      <br>
      +
      <br>
      +    gain = (uint16_t) ((pa_cvolume_max(&s->real_volume) *
      BLUEZ_MAX_GAIN) / PA_VOLUME_NORM);
      <br>
      +
      <br>
      +    pa_log_debug("Real Volume Gain:%u", gain);
      <br>
      +
      <br>
      +    if (gain > BLUEZ_MAX_GAIN)
      <br>
      +        gain = BLUEZ_MAX_GAIN;
      <br>
      +
      <br>
      +    volume = (pa_volume_t) (gain * PA_VOLUME_NORM /
      BLUEZ_MAX_GAIN);
      <br>
      +
      <br>
      +    pa_cvolume_set(&s->real_volume,
      u->sample_spec.channels, volume);
      <br>
      +    pa_cvolume_set(&s->soft_volume,
      u->sample_spec.channels, volume);
      <br>
      +
      <br>
      +    u->transport->set_volume(u->transport, gain);
      <br>
      +}
      <br>
      +
      <br>
       /* Run from main thread */
      <br>
       static int add_source(struct userdata *u) {
      <br>
           pa_source_new_data data;
      <br>
      @@ -1109,6 +1142,9 @@ static int add_source(struct userdata *u) {
      <br>
           if (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT
      || u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY) {
      <br>
               pa_source_set_set_volume_callback(u->source,
      source_set_volume_cb);
      <br>
               u->source->n_volume_steps = 16;
      <br>
      +    } else if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE)
      {
      <br>
      +        pa_source_set_set_volume_callback(u->source,
      source_set_a2dp_volume_cb);
      <br>
      +        u->source->n_volume_steps = 1;
      <br>
           }
      <br>
           return 0;
      <br>
       }
      <br>
      @@ -1230,6 +1266,40 @@ static void sink_set_volume_cb(pa_sink *s)
      {
      <br>
           u->transport->set_speaker_gain(u->transport, gain);
      <br>
       }
      <br>
      <br>
      +static void sink_set_a2dp_volume_cb(pa_sink *s) {
      <br>
      +    uint16_t gain;
      <br>
      +    pa_volume_t volume;
      <br>
      +    struct userdata *u;
      <br>
      +
      <br>
      +    pa_assert(s);
      <br>
      +    pa_assert(s->core);
      <br>
      +
      <br>
      +    u = s->userdata;
      <br>
      +
      <br>
      +    pa_assert(u);
      <br>
      +    pa_assert(u->sink == s);
      <br>
      +
      <br>
      +    if (u->transport->set_volume == NULL)
      <br>
      +        return;
      <br>
      +
      <br>
      +    gain = (uint16_t) ((pa_cvolume_max(&s->real_volume) *
      BLUEZ_MAX_GAIN) / PA_VOLUME_NORM);
      <br>
      +
      <br>
      +    pa_log_debug("Real Volume Gain:%u", gain);
      <br>
      +
      <br>
      +    if (gain > BLUEZ_MAX_GAIN)
      <br>
      +        gain = BLUEZ_MAX_GAIN;
      <br>
      +
      <br>
      +    volume = (pa_volume_t) (gain * PA_VOLUME_NORM /
      BLUEZ_MAX_GAIN);
      <br>
      +
      <br>
      +    pa_cvolume_set(&s->real_volume,
      u->sample_spec.channels, volume);
      <br>
      +
      <br>
      +    if(u->transport->set_volume(u->transport, gain) <
      0) {
      <br>
      +        pa_cvolume_set(&s->soft_volume,
      u->sample_spec.channels, PA_VOLUME_NORM);
      <br>
      +        pa_sink_set_set_volume_callback(s, NULL);
      <br>
      +        u->transport->a2dp_gain = 0xFFu;
      <br>
      +    }
      <br>
      +}
      <br>
      +
      <br>
       /* Run from main thread */
      <br>
       static int add_sink(struct userdata *u) {
      <br>
           pa_sink_new_data data;
      <br>
      @@ -1284,6 +1354,9 @@ static int add_sink(struct userdata *u) {
      <br>
           if (u->profile == PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT
      || u->profile == PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY) {
      <br>
               pa_sink_set_set_volume_callback(u->sink,
      sink_set_volume_cb);
      <br>
               u->sink->n_volume_steps = 16;
      <br>
      +    } else if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK) {
      <br>
      +        pa_sink_set_set_volume_callback(u->sink,
      sink_set_a2dp_volume_cb);
      <br>
      +        u->sink->n_volume_steps = 1;
      <br>
           }
      <br>
           return 0;
      <br>
       }
      <br>
      @@ -2340,6 +2413,53 @@ static pa_hook_result_t
      transport_microphone_gain_changed_cb(pa_bluetooth_discov
      <br>
           return PA_HOOK_OK;
      <br>
       }
      <br>
      <br>
      +static pa_hook_result_t
      transport_a2dp_gain_changed_cb(pa_bluetooth_discovery *y,
      pa_bluetooth_transport *t, struct userdata *u) {
      <br>
      +    pa_volume_t volume;
      <br>
      +    pa_cvolume v;
      <br>
      +    uint16_t gain;
      <br>
      +
      <br>
      +    pa_assert(t);
      <br>
      +    pa_assert(u);
      <br>
      +
      <br>
      +    if (t != u->transport)
      <br>
      +        return PA_HOOK_OK;
      <br>
      +
      <br>
      +    gain = t->a2dp_gain;
      <br>
      +    volume = (pa_volume_t) (gain * PA_VOLUME_NORM /
      BLUEZ_MAX_GAIN);
      <br>
      +
      <br>
      +    pa_cvolume_set(&v, u->sample_spec.channels, volume);
      <br>
      +
      <br>
      +    if(u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK){
      <br>
      +        pa_assert(u->sink);
      <br>
      +
      <br>
      +        if(!u->sink->set_volume){
      <br>
      +            pa_cvolume_set(&u->sink->soft_volume,
      u->sample_spec.channels, PA_VOLUME_NORM);
      <br>
      +            pa_sink_set_set_volume_callback(u->sink,
      sink_set_a2dp_volume_cb);
      <br>
      +        }
      <br>
      +
      <br>
      +
      <br>
      +        if (gain == 0)
      <br>
      +            pa_sink_mute_changed(u->sink, true);
      <br>
      +        else if(u->sink->muted)
      <br>
      +            pa_sink_mute_changed(u->sink, false);
      <br>
      +
      <br>
      +        pa_sink_volume_changed(u->sink, &v);
      <br>
      +    } else if(u->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE){
      <br>
      +        pa_assert(u->source);
      <br>
      +
      <br>
      +
      <br>
      +        if (gain == 0)
      <br>
      +            pa_source_mute_changed(u->source, true);
      <br>
      +        else if(u->source->muted)
      <br>
      +            pa_source_mute_changed(u->source, false);
      <br>
      +
      <br>
      +        pa_source_volume_changed(u->source, &v);
      <br>
      +    }
      <br>
      +
      <br>
      +
      <br>
      +    return PA_HOOK_OK;
      <br>
      +}
      <br>
      +
      <br>
       /* Run from main thread context */
      <br>
       static int device_process_msg(pa_msgobject *obj, int code, void
      *data, int64_t offset, pa_memchunk *chunk) {
      <br>
           struct bluetooth_msg *m = BLUETOOTH_MSG(obj);
      <br>
      @@ -2430,6 +2550,9 @@ int pa__init(pa_module* m) {
      <br>
           u->transport_microphone_gain_changed_slot =
      <br>
      pa_hook_connect(pa_bluetooth_discovery_hook(u->discovery,
      PA_BLUETOOTH_HOOK_TRANSPORT_MICROPHONE_GAIN_CHANGED),
      PA_HOOK_NORMAL, (pa_hook_cb_t)
      transport_microphone_gain_changed_cb, u);
      <br>
      <br>
      +    u->transport_a2dp_gain_changed_slot =
      <br>
      + pa_hook_connect(pa_bluetooth_discovery_hook(u->discovery,
      PA_BLUETOOTH_HOOK_TRANSPORT_A2DP_GAIN_CHANGED), PA_HOOK_NORMAL,
      (pa_hook_cb_t) transport_a2dp_gain_changed_cb, u);
      <br>
      +
      <br>
           if (add_card(u) < 0)
      <br>
               goto fail;
      <br>
      <br>
      @@ -2491,6 +2614,9 @@ void pa__done(pa_module *m) {
      <br>
           if (u->transport_microphone_gain_changed_slot)
      <br>
      pa_hook_slot_free(u->transport_microphone_gain_changed_slot);
      <br>
      <br>
      +    if (u->transport_a2dp_gain_changed_slot)
      <br>
      + pa_hook_slot_free(u->transport_a2dp_gain_changed_slot);
      <br>
      +
      <br>
           if (u->sbc_info.buffer)
      <br>
               pa_xfree(u->sbc_info.buffer);
      <br>
      <br>
      <br>
      _______________________________________________
      <br>
      pulseaudio-discuss mailing list
      <br>
      <a class="moz-txt-link-abbreviated" href="mailto:pulseaudio-discuss@lists.freedesktop.org">pulseaudio-discuss@lists.freedesktop.org</a>
      <br>
      <a class="moz-txt-link-freetext" href="https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss">https://lists.freedesktop.org/mailman/listinfo/pulseaudio-discuss</a>
      <br>
    </blockquote>
  </body>
</html>