mirror of
https://gitlab.freedesktop.org/pulseaudio/pulseaudio.git
synced 2025-11-04 13:29:59 -05:00
bluetooth: SCO over PCM
This commit is contained in:
parent
b35ae7f531
commit
c8a240cddd
2 changed files with 131 additions and 46 deletions
|
|
@ -43,6 +43,7 @@
|
||||||
#include <pulsecore/rtpoll.h>
|
#include <pulsecore/rtpoll.h>
|
||||||
#include <pulsecore/time-smoother.h>
|
#include <pulsecore/time-smoother.h>
|
||||||
#include <pulsecore/rtclock.h>
|
#include <pulsecore/rtclock.h>
|
||||||
|
#include <pulsecore/namereg.h>
|
||||||
|
|
||||||
#include <modules/dbus-util.h>
|
#include <modules/dbus-util.h>
|
||||||
|
|
||||||
|
|
@ -71,7 +72,9 @@ PA_MODULE_USAGE(
|
||||||
"profile=<a2dp|hsp> "
|
"profile=<a2dp|hsp> "
|
||||||
"rate=<sample rate> "
|
"rate=<sample rate> "
|
||||||
"channels=<number of channels> "
|
"channels=<number of channels> "
|
||||||
"path=<device object path>");
|
"path=<device object path> "
|
||||||
|
"sco_sink=<SCO over PCM sink name> "
|
||||||
|
"sco_source=<SCO over PCM source name>");
|
||||||
|
|
||||||
static const char* const valid_modargs[] = {
|
static const char* const valid_modargs[] = {
|
||||||
"name",
|
"name",
|
||||||
|
|
@ -83,6 +86,8 @@ static const char* const valid_modargs[] = {
|
||||||
"rate",
|
"rate",
|
||||||
"channels",
|
"channels",
|
||||||
"path",
|
"path",
|
||||||
|
"sco_sink",
|
||||||
|
"sco_source",
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -98,6 +103,11 @@ struct a2dp_info {
|
||||||
uint16_t seq_num; /* Cumulative packet sequence */
|
uint16_t seq_num; /* Cumulative packet sequence */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct hsp_info {
|
||||||
|
pa_sink *sco_sink;
|
||||||
|
pa_source *sco_source;
|
||||||
|
};
|
||||||
|
|
||||||
enum profile {
|
enum profile {
|
||||||
PROFILE_A2DP,
|
PROFILE_A2DP,
|
||||||
PROFILE_HSP,
|
PROFILE_HSP,
|
||||||
|
|
@ -132,6 +142,7 @@ struct userdata {
|
||||||
size_t block_size;
|
size_t block_size;
|
||||||
|
|
||||||
struct a2dp_info a2dp;
|
struct a2dp_info a2dp;
|
||||||
|
struct hsp_info hsp;
|
||||||
pa_dbus_connection *connection;
|
pa_dbus_connection *connection;
|
||||||
|
|
||||||
enum profile profile;
|
enum profile profile;
|
||||||
|
|
@ -1215,7 +1226,21 @@ static char *get_name(const char *type, pa_modargs *ma, const char *device_id, p
|
||||||
return pa_sprintf_malloc("bluez_%s.%s", type, n);
|
return pa_sprintf_malloc("bluez_%s.%s", type, n);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define USE_SCO_OVER_PCM(u) (u->profile == PROFILE_HSP && (u->hsp.sco_sink && u->hsp.sco_source))
|
||||||
|
|
||||||
static int add_sink(struct userdata *u) {
|
static int add_sink(struct userdata *u) {
|
||||||
|
|
||||||
|
if (USE_SCO_OVER_PCM(u)) {
|
||||||
|
pa_proplist *p;
|
||||||
|
|
||||||
|
u->sink = u->hsp.sco_sink;
|
||||||
|
u->sink->card = u->card; /* FIXME! */
|
||||||
|
p = pa_proplist_new();
|
||||||
|
pa_proplist_sets(p, "bluetooth.protocol", "sco");
|
||||||
|
pa_proplist_update(u->sink->proplist, PA_UPDATE_MERGE, p);
|
||||||
|
pa_proplist_free(p);
|
||||||
|
|
||||||
|
} else {
|
||||||
pa_sink_new_data data;
|
pa_sink_new_data data;
|
||||||
pa_bool_t b;
|
pa_bool_t b;
|
||||||
|
|
||||||
|
|
@ -1238,16 +1263,30 @@ static int add_sink(struct userdata *u) {
|
||||||
|
|
||||||
u->sink->userdata = u;
|
u->sink->userdata = u;
|
||||||
u->sink->parent.process_msg = sink_process_msg;
|
u->sink->parent.process_msg = sink_process_msg;
|
||||||
/* u->sink->get_volume = sink_get_volume_cb; */
|
|
||||||
/* u->sink->set_volume = sink_set_volume_cb; */
|
|
||||||
|
|
||||||
pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
|
pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
|
||||||
pa_sink_set_rtpoll(u->sink, u->rtpoll);
|
pa_sink_set_rtpoll(u->sink, u->rtpoll);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* u->sink->get_volume = sink_get_volume_cb; */
|
||||||
|
/* u->sink->set_volume = sink_set_volume_cb; */
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int add_source(struct userdata *u) {
|
static int add_source(struct userdata *u) {
|
||||||
|
|
||||||
|
if (USE_SCO_OVER_PCM(u)) {
|
||||||
|
pa_proplist *p;
|
||||||
|
|
||||||
|
u->source = u->hsp.sco_source;
|
||||||
|
u->source->card = u->card; /* FIXME! */
|
||||||
|
p = pa_proplist_new();
|
||||||
|
pa_proplist_sets(p, "bluetooth.protocol", "sco");
|
||||||
|
pa_proplist_update(u->source->proplist, PA_UPDATE_MERGE, p);
|
||||||
|
pa_proplist_free(p);
|
||||||
|
|
||||||
|
} else {
|
||||||
pa_source_new_data data;
|
pa_source_new_data data;
|
||||||
pa_bool_t b;
|
pa_bool_t b;
|
||||||
|
|
||||||
|
|
@ -1270,11 +1309,13 @@ static int add_source(struct userdata *u) {
|
||||||
|
|
||||||
u->source->userdata = u;
|
u->source->userdata = u;
|
||||||
u->source->parent.process_msg = source_process_msg;
|
u->source->parent.process_msg = source_process_msg;
|
||||||
/* u->source->get_volume = source_get_volume_cb; */
|
|
||||||
/* u->source->set_volume = source_set_volume_cb; */
|
|
||||||
|
|
||||||
pa_source_set_asyncmsgq(u->source, u->thread_mq.inq);
|
pa_source_set_asyncmsgq(u->source, u->thread_mq.inq);
|
||||||
pa_source_set_rtpoll(u->source, u->rtpoll);
|
pa_source_set_rtpoll(u->source, u->rtpoll);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* u->source->get_volume = source_get_volume_cb; */
|
||||||
|
/* u->source->set_volume = source_set_volume_cb; */
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -1318,6 +1359,11 @@ static int setup_bt(struct userdata *u) {
|
||||||
|
|
||||||
pa_log_debug("Connection to the device configured");
|
pa_log_debug("Connection to the device configured");
|
||||||
|
|
||||||
|
if (USE_SCO_OVER_PCM(u)) {
|
||||||
|
pa_log_debug("Configured to use SCO over PCM");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (setup_stream_fd(u) < 0)
|
if (setup_stream_fd(u) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
|
@ -1377,6 +1423,12 @@ static int start_thread(struct userdata *u) {
|
||||||
pa_assert(!u->thread);
|
pa_assert(!u->thread);
|
||||||
pa_assert(!u->rtpoll_item);
|
pa_assert(!u->rtpoll_item);
|
||||||
|
|
||||||
|
if (USE_SCO_OVER_PCM(u)) {
|
||||||
|
pa_sink_ref(u->sink);
|
||||||
|
pa_source_ref(u->source);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
u->rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, 1);
|
u->rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, 1);
|
||||||
pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
|
pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
|
||||||
pollfd->fd = u->stream_fd;
|
pollfd->fd = u->stream_fd;
|
||||||
|
|
@ -1410,11 +1462,13 @@ static int card_set_profile(pa_card *c, pa_card_profile *new_profile) {
|
||||||
|
|
||||||
if (u->sink) {
|
if (u->sink) {
|
||||||
inputs = pa_sink_move_all_start(u->sink);
|
inputs = pa_sink_move_all_start(u->sink);
|
||||||
|
if (!USE_SCO_OVER_PCM(u))
|
||||||
pa_sink_unlink(u->sink);
|
pa_sink_unlink(u->sink);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (u->source) {
|
if (u->source) {
|
||||||
outputs = pa_source_move_all_start(u->source);
|
outputs = pa_source_move_all_start(u->source);
|
||||||
|
if (!USE_SCO_OVER_PCM(u))
|
||||||
pa_source_unlink(u->source);
|
pa_source_unlink(u->source);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1604,6 +1658,18 @@ int pa__init(pa_module* m) {
|
||||||
u->sample_spec = m->core->default_sample_spec;
|
u->sample_spec = m->core->default_sample_spec;
|
||||||
u->modargs = ma;
|
u->modargs = ma;
|
||||||
|
|
||||||
|
if (pa_modargs_get_value(ma, "sco_sink", NULL) &&
|
||||||
|
!(u->hsp.sco_sink = pa_namereg_get(m->core, pa_modargs_get_value(ma, "sco_sink", NULL), PA_NAMEREG_SINK))) {
|
||||||
|
pa_log("SCO sink not found");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pa_modargs_get_value(ma, "sco_source", NULL) &&
|
||||||
|
!(u->hsp.sco_source = pa_namereg_get(m->core, pa_modargs_get_value(ma, "sco_source", NULL), PA_NAMEREG_SOURCE))) {
|
||||||
|
pa_log("SCO source not found");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
if (pa_modargs_get_value_u32(ma, "rate", &u->sample_spec.rate) < 0 ||
|
if (pa_modargs_get_value_u32(ma, "rate", &u->sample_spec.rate) < 0 ||
|
||||||
u->sample_spec.rate <= 0 || u->sample_spec.rate > PA_RATE_MAX) {
|
u->sample_spec.rate <= 0 || u->sample_spec.rate > PA_RATE_MAX) {
|
||||||
pa_log_error("Failed to get rate from module arguments");
|
pa_log_error("Failed to get rate from module arguments");
|
||||||
|
|
@ -1704,10 +1770,10 @@ void pa__done(pa_module *m) {
|
||||||
if (!(u = m->userdata))
|
if (!(u = m->userdata))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (u->sink)
|
if (u->sink && !USE_SCO_OVER_PCM(u))
|
||||||
pa_sink_unlink(u->sink);
|
pa_sink_unlink(u->sink);
|
||||||
|
|
||||||
if (u->source)
|
if (u->source && !USE_SCO_OVER_PCM(u))
|
||||||
pa_source_unlink(u->source);
|
pa_source_unlink(u->source);
|
||||||
|
|
||||||
stop_thread(u);
|
stop_thread(u);
|
||||||
|
|
|
||||||
|
|
@ -42,15 +42,20 @@
|
||||||
PA_MODULE_AUTHOR("Joao Paulo Rechi Vita");
|
PA_MODULE_AUTHOR("Joao Paulo Rechi Vita");
|
||||||
PA_MODULE_DESCRIPTION("Detect available bluetooth audio devices and load bluetooth audio drivers");
|
PA_MODULE_DESCRIPTION("Detect available bluetooth audio devices and load bluetooth audio drivers");
|
||||||
PA_MODULE_VERSION(PACKAGE_VERSION);
|
PA_MODULE_VERSION(PACKAGE_VERSION);
|
||||||
PA_MODULE_USAGE("async=<Asynchronous initialization?>");
|
PA_MODULE_USAGE("sco_sink=<name of sink> "
|
||||||
|
"sco_source=<name of source>"
|
||||||
|
"async=<Asynchronous initialization?>");
|
||||||
|
|
||||||
static const char* const valid_modargs[] = {
|
static const char* const valid_modargs[] = {
|
||||||
|
"sco_sink",
|
||||||
|
"sco_source",
|
||||||
"async",
|
"async",
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
struct userdata {
|
struct userdata {
|
||||||
pa_module *module;
|
pa_module *module;
|
||||||
|
pa_modargs *ma;
|
||||||
pa_core *core;
|
pa_core *core;
|
||||||
pa_dbus_connection *connection;
|
pa_dbus_connection *connection;
|
||||||
pa_bluetooth_discovery *discovery;
|
pa_bluetooth_discovery *discovery;
|
||||||
|
|
@ -71,6 +76,16 @@ static void load_module_for_device(struct userdata *u, pa_bluetooth_device *d, p
|
||||||
/* Oh, awesome, a new device has shown up and been connected! */
|
/* Oh, awesome, a new device has shown up and been connected! */
|
||||||
|
|
||||||
args = pa_sprintf_malloc("address=\"%s\" path=\"%s\"", d->address, d->path);
|
args = pa_sprintf_malloc("address=\"%s\" path=\"%s\"", d->address, d->path);
|
||||||
|
|
||||||
|
if (pa_modargs_get_value(u->ma, "sco_sink", NULL) &&
|
||||||
|
pa_modargs_get_value(u->ma, "sco_source", NULL)) {
|
||||||
|
char *tmp;
|
||||||
|
|
||||||
|
tmp = pa_sprintf_malloc("%s sco_sink=\"%s\" sco_source=\"%s\"", args, pa_modargs_get_value(u->ma, "sco_sink", NULL), pa_modargs_get_value(u->ma, "sco_source", NULL));
|
||||||
|
pa_xfree(args);
|
||||||
|
args = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
pa_log_debug("Loading module-bluetooth-device %s", args);
|
pa_log_debug("Loading module-bluetooth-device %s", args);
|
||||||
m = pa_module_load(u->module->core, "module-bluetooth-device", args);
|
m = pa_module_load(u->module->core, "module-bluetooth-device", args);
|
||||||
pa_xfree(args);
|
pa_xfree(args);
|
||||||
|
|
@ -130,6 +145,7 @@ int pa__init(pa_module* m) {
|
||||||
m->userdata = u = pa_xnew0(struct userdata, 1);
|
m->userdata = u = pa_xnew0(struct userdata, 1);
|
||||||
u->module = m;
|
u->module = m;
|
||||||
u->core = m->core;
|
u->core = m->core;
|
||||||
|
u->ma = ma;
|
||||||
|
|
||||||
if (setup_dbus(u) < 0)
|
if (setup_dbus(u) < 0)
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
@ -162,5 +178,8 @@ void pa__done(pa_module* m) {
|
||||||
if (u->connection)
|
if (u->connection)
|
||||||
pa_dbus_connection_unref(u->connection);
|
pa_dbus_connection_unref(u->connection);
|
||||||
|
|
||||||
|
if (u->ma)
|
||||||
|
pa_modargs_free(u->ma);
|
||||||
|
|
||||||
pa_xfree(u);
|
pa_xfree(u);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue