mirror of
https://gitlab.freedesktop.org/pulseaudio/pulseaudio.git
synced 2026-05-20 23:08:10 +02:00
This uses the messaging API to initiate a codec switch.
While a particular codec might be applicable only for a particular
profile, for eg. aptX can only be applicable for A2DP sink or source
and not for let's say HSP, the codec switching logic has not been
tied to the logic for switching profiles.
Codec can be switched by running the following on the command line.
pacmd send-message /card/bluez_card.XX_XX_XX_XX_XX_XX/bluez switch-codec{"ldac_hq"}
pacmd send-message /card/bluez_card.XX_XX_XX_XX_XX_XX/bluez switch-codec {"ldac_mq"}
pacmd send-message /card/bluez_card.XX_XX_XX_XX_XX_XX/bluez switch-codec {"ldac_sq"}
pacmd send-message /card/bluez_card.XX_XX_XX_XX_XX_XX/bluez switch-codec {"aptx_hd"}
pacmd send-message /card/bluez_card.XX_XX_XX_XX_XX_XX/bluez switch-codec {"aptx"}
pacmd send-message /card/bluez_card.XX_XX_XX_XX_XX_XX/bluez switch-codec {"sbc"}
Codec name passed above is matched against pa_a2dp_codec->name. Note that
the match is case sensitive. XX_XX_XX_XX_XX_XX needs to be substituted with
the actual bluetooth device id.
Part-of: <https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/merge_requests/440>
175 lines
5.3 KiB
C
175 lines
5.3 KiB
C
/***
|
|
This file is part of PulseAudio.
|
|
|
|
Copyright 2008-2013 João Paulo Rechi Vita
|
|
|
|
PulseAudio is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU Lesser General Public License as
|
|
published by the Free Software Foundation; either version 2.1 of the
|
|
License, or (at your option) any later version.
|
|
|
|
PulseAudio is distributed in the hope that it will be useful, but
|
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public
|
|
License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
|
|
***/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
|
|
#include <pulsecore/core.h>
|
|
#include <pulsecore/core-util.h>
|
|
#include <pulsecore/macro.h>
|
|
#include <pulsecore/module.h>
|
|
#include <pulsecore/modargs.h>
|
|
#include <pulsecore/shared.h>
|
|
|
|
#include "bluez5-util.h"
|
|
|
|
PA_MODULE_AUTHOR("João Paulo Rechi Vita");
|
|
PA_MODULE_DESCRIPTION("Detect available BlueZ 5 Bluetooth audio devices and load BlueZ 5 Bluetooth audio drivers");
|
|
PA_MODULE_VERSION(PACKAGE_VERSION);
|
|
PA_MODULE_LOAD_ONCE(true);
|
|
PA_MODULE_USAGE(
|
|
"headset=ofono|native|auto"
|
|
"autodetect_mtu=<boolean>"
|
|
);
|
|
|
|
static const char* const valid_modargs[] = {
|
|
"headset",
|
|
"autodetect_mtu",
|
|
NULL
|
|
};
|
|
|
|
struct userdata {
|
|
pa_module *module;
|
|
pa_core *core;
|
|
pa_hashmap *loaded_device_paths;
|
|
pa_hook_slot *device_connection_changed_slot;
|
|
pa_bluetooth_discovery *discovery;
|
|
bool autodetect_mtu;
|
|
};
|
|
|
|
static pa_hook_result_t device_connection_changed_cb(pa_bluetooth_discovery *y, const pa_bluetooth_device *d, struct userdata *u) {
|
|
bool module_loaded;
|
|
|
|
pa_assert(d);
|
|
pa_assert(u);
|
|
|
|
module_loaded = pa_hashmap_get(u->loaded_device_paths, d->path) ? true : false;
|
|
|
|
/* When changing A2DP codec there is no transport connected, ensure that no module is unloaded */
|
|
if (module_loaded && !pa_bluetooth_device_any_transport_connected(d) &&
|
|
!d->codec_switching_in_progress) {
|
|
/* disconnection, the module unloads itself */
|
|
pa_log_debug("Unregistering module for %s", d->path);
|
|
pa_hashmap_remove(u->loaded_device_paths, d->path);
|
|
return PA_HOOK_OK;
|
|
}
|
|
|
|
if (!module_loaded && pa_bluetooth_device_any_transport_connected(d)) {
|
|
/* a new device has been connected */
|
|
pa_module *m;
|
|
char *args = pa_sprintf_malloc("path=%s autodetect_mtu=%i", d->path, (int)u->autodetect_mtu);
|
|
|
|
pa_log_debug("Loading module-bluez5-device %s", args);
|
|
pa_module_load(&m, u->module->core, "module-bluez5-device", args);
|
|
pa_xfree(args);
|
|
|
|
if (m)
|
|
/* No need to duplicate the path here since the device object will
|
|
* exist for the whole hashmap entry lifespan */
|
|
pa_hashmap_put(u->loaded_device_paths, d->path, d->path);
|
|
else
|
|
pa_log_warn("Failed to load module for device %s", d->path);
|
|
|
|
return PA_HOOK_OK;
|
|
}
|
|
|
|
return PA_HOOK_OK;
|
|
}
|
|
|
|
#ifdef HAVE_BLUEZ_5_NATIVE_HEADSET
|
|
const char *default_headset_backend = "auto";
|
|
#else
|
|
const char *default_headset_backend = "ofono";
|
|
#endif
|
|
|
|
int pa__init(pa_module *m) {
|
|
struct userdata *u;
|
|
pa_modargs *ma;
|
|
const char *headset_str;
|
|
int headset_backend;
|
|
bool autodetect_mtu;
|
|
|
|
pa_assert(m);
|
|
|
|
if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
|
|
pa_log("failed to parse module arguments.");
|
|
goto fail;
|
|
}
|
|
|
|
pa_assert_se(headset_str = pa_modargs_get_value(ma, "headset", default_headset_backend));
|
|
if (pa_streq(headset_str, "ofono"))
|
|
headset_backend = HEADSET_BACKEND_OFONO;
|
|
else if (pa_streq(headset_str, "native"))
|
|
headset_backend = HEADSET_BACKEND_NATIVE;
|
|
else if (pa_streq(headset_str, "auto"))
|
|
headset_backend = HEADSET_BACKEND_AUTO;
|
|
else {
|
|
pa_log("headset parameter must be either ofono, native or auto (found %s)", headset_str);
|
|
goto fail;
|
|
}
|
|
|
|
autodetect_mtu = false;
|
|
if (pa_modargs_get_value_boolean(ma, "autodetect_mtu", &autodetect_mtu) < 0) {
|
|
pa_log("Invalid boolean value for autodetect_mtu parameter");
|
|
goto fail;
|
|
}
|
|
|
|
m->userdata = u = pa_xnew0(struct userdata, 1);
|
|
u->module = m;
|
|
u->core = m->core;
|
|
u->autodetect_mtu = autodetect_mtu;
|
|
u->loaded_device_paths = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
|
|
|
|
if (!(u->discovery = pa_bluetooth_discovery_get(u->core, headset_backend)))
|
|
goto fail;
|
|
|
|
u->device_connection_changed_slot =
|
|
pa_hook_connect(pa_bluetooth_discovery_hook(u->discovery, PA_BLUETOOTH_HOOK_DEVICE_CONNECTION_CHANGED),
|
|
PA_HOOK_NORMAL, (pa_hook_cb_t) device_connection_changed_cb, u);
|
|
|
|
pa_modargs_free(ma);
|
|
return 0;
|
|
|
|
fail:
|
|
if (ma)
|
|
pa_modargs_free(ma);
|
|
pa__done(m);
|
|
return -1;
|
|
}
|
|
|
|
void pa__done(pa_module *m) {
|
|
struct userdata *u;
|
|
|
|
pa_assert(m);
|
|
|
|
if (!(u = m->userdata))
|
|
return;
|
|
|
|
if (u->device_connection_changed_slot)
|
|
pa_hook_slot_free(u->device_connection_changed_slot);
|
|
|
|
if (u->loaded_device_paths)
|
|
pa_hashmap_free(u->loaded_device_paths);
|
|
|
|
if (u->discovery)
|
|
pa_bluetooth_discovery_unref(u->discovery);
|
|
|
|
pa_xfree(u);
|
|
}
|