mirror of
				https://gitlab.freedesktop.org/pulseaudio/pulseaudio.git
				synced 2025-11-03 09:01:50 -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/time-smoother.h>
 | 
			
		||||
#include <pulsecore/rtclock.h>
 | 
			
		||||
#include <pulsecore/namereg.h>
 | 
			
		||||
 | 
			
		||||
#include <modules/dbus-util.h>
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -71,7 +72,9 @@ PA_MODULE_USAGE(
 | 
			
		|||
        "profile=<a2dp|hsp> "
 | 
			
		||||
        "rate=<sample rate> "
 | 
			
		||||
        "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[] = {
 | 
			
		||||
    "name",
 | 
			
		||||
| 
						 | 
				
			
			@ -83,6 +86,8 @@ static const char* const valid_modargs[] = {
 | 
			
		|||
    "rate",
 | 
			
		||||
    "channels",
 | 
			
		||||
    "path",
 | 
			
		||||
    "sco_sink",
 | 
			
		||||
    "sco_source",
 | 
			
		||||
    NULL
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -98,6 +103,11 @@ struct a2dp_info {
 | 
			
		|||
    uint16_t seq_num;                    /* Cumulative packet sequence */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct hsp_info {
 | 
			
		||||
    pa_sink *sco_sink;
 | 
			
		||||
    pa_source *sco_source;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum profile {
 | 
			
		||||
    PROFILE_A2DP,
 | 
			
		||||
    PROFILE_HSP,
 | 
			
		||||
| 
						 | 
				
			
			@ -132,6 +142,7 @@ struct userdata {
 | 
			
		|||
    size_t block_size;
 | 
			
		||||
 | 
			
		||||
    struct a2dp_info a2dp;
 | 
			
		||||
    struct hsp_info hsp;
 | 
			
		||||
    pa_dbus_connection *connection;
 | 
			
		||||
 | 
			
		||||
    enum profile profile;
 | 
			
		||||
| 
						 | 
				
			
			@ -1215,67 +1226,97 @@ static char *get_name(const char *type, pa_modargs *ma, const char *device_id, p
 | 
			
		|||
    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) {
 | 
			
		||||
    pa_sink_new_data data;
 | 
			
		||||
    pa_bool_t b;
 | 
			
		||||
 | 
			
		||||
    pa_sink_new_data_init(&data);
 | 
			
		||||
    data.driver = __FILE__;
 | 
			
		||||
    data.module = u->module;
 | 
			
		||||
    pa_sink_new_data_set_sample_spec(&data, &u->sample_spec);
 | 
			
		||||
    pa_proplist_sets(data.proplist, "bluetooth.protocol", u->profile == PROFILE_A2DP ? "a2dp" : "sco");
 | 
			
		||||
    data.card = u->card;
 | 
			
		||||
    data.name = get_name("sink", u->modargs, u->device->address, &b);
 | 
			
		||||
    data.namereg_fail = b;
 | 
			
		||||
    if (USE_SCO_OVER_PCM(u)) {
 | 
			
		||||
        pa_proplist *p;
 | 
			
		||||
 | 
			
		||||
    u->sink = pa_sink_new(u->core, &data, PA_SINK_HARDWARE|PA_SINK_LATENCY);
 | 
			
		||||
    pa_sink_new_data_done(&data);
 | 
			
		||||
        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);
 | 
			
		||||
 | 
			
		||||
    if (!u->sink) {
 | 
			
		||||
        pa_log_error("Failed to create sink");
 | 
			
		||||
        return -1;
 | 
			
		||||
    } else {
 | 
			
		||||
        pa_sink_new_data data;
 | 
			
		||||
        pa_bool_t b;
 | 
			
		||||
 | 
			
		||||
        pa_sink_new_data_init(&data);
 | 
			
		||||
        data.driver = __FILE__;
 | 
			
		||||
        data.module = u->module;
 | 
			
		||||
        pa_sink_new_data_set_sample_spec(&data, &u->sample_spec);
 | 
			
		||||
        pa_proplist_sets(data.proplist, "bluetooth.protocol", u->profile == PROFILE_A2DP ? "a2dp" : "sco");
 | 
			
		||||
        data.card = u->card;
 | 
			
		||||
        data.name = get_name("sink", u->modargs, u->device->address, &b);
 | 
			
		||||
        data.namereg_fail = b;
 | 
			
		||||
 | 
			
		||||
        u->sink = pa_sink_new(u->core, &data, PA_SINK_HARDWARE|PA_SINK_LATENCY);
 | 
			
		||||
        pa_sink_new_data_done(&data);
 | 
			
		||||
 | 
			
		||||
        if (!u->sink) {
 | 
			
		||||
            pa_log_error("Failed to create sink");
 | 
			
		||||
            return -1;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        u->sink->userdata = u;
 | 
			
		||||
        u->sink->parent.process_msg = sink_process_msg;
 | 
			
		||||
 | 
			
		||||
        pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
 | 
			
		||||
        pa_sink_set_rtpoll(u->sink, u->rtpoll);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    u->sink->userdata = u;
 | 
			
		||||
    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_rtpoll(u->sink, u->rtpoll);
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int add_source(struct userdata *u) {
 | 
			
		||||
    pa_source_new_data data;
 | 
			
		||||
    pa_bool_t b;
 | 
			
		||||
 | 
			
		||||
    pa_source_new_data_init(&data);
 | 
			
		||||
    data.driver = __FILE__;
 | 
			
		||||
    data.module = u->module;
 | 
			
		||||
    pa_source_new_data_set_sample_spec(&data, &u->sample_spec);
 | 
			
		||||
    pa_proplist_sets(data.proplist, "bluetooth.protocol", u->profile == PROFILE_A2DP ? "a2dp" : "sco");
 | 
			
		||||
    data.card = u->card;
 | 
			
		||||
    data.name = get_name("source", u->modargs, u->device->address, &b);
 | 
			
		||||
    data.namereg_fail = b;
 | 
			
		||||
    if (USE_SCO_OVER_PCM(u)) {
 | 
			
		||||
        pa_proplist *p;
 | 
			
		||||
 | 
			
		||||
    u->source = pa_source_new(u->core, &data, PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY);
 | 
			
		||||
    pa_source_new_data_done(&data);
 | 
			
		||||
        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);
 | 
			
		||||
 | 
			
		||||
    if (!u->source) {
 | 
			
		||||
        pa_log_error("Failed to create source");
 | 
			
		||||
        return -1;
 | 
			
		||||
    } else {
 | 
			
		||||
        pa_source_new_data data;
 | 
			
		||||
        pa_bool_t b;
 | 
			
		||||
 | 
			
		||||
        pa_source_new_data_init(&data);
 | 
			
		||||
        data.driver = __FILE__;
 | 
			
		||||
        data.module = u->module;
 | 
			
		||||
        pa_source_new_data_set_sample_spec(&data, &u->sample_spec);
 | 
			
		||||
        pa_proplist_sets(data.proplist, "bluetooth.protocol", u->profile == PROFILE_A2DP ? "a2dp" : "sco");
 | 
			
		||||
        data.card = u->card;
 | 
			
		||||
        data.name = get_name("source", u->modargs, u->device->address, &b);
 | 
			
		||||
        data.namereg_fail = b;
 | 
			
		||||
 | 
			
		||||
        u->source = pa_source_new(u->core, &data, PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY);
 | 
			
		||||
        pa_source_new_data_done(&data);
 | 
			
		||||
 | 
			
		||||
        if (!u->source) {
 | 
			
		||||
            pa_log_error("Failed to create source");
 | 
			
		||||
            return -1;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        u->source->userdata = u;
 | 
			
		||||
        u->source->parent.process_msg = source_process_msg;
 | 
			
		||||
 | 
			
		||||
        pa_source_set_asyncmsgq(u->source, u->thread_mq.inq);
 | 
			
		||||
        pa_source_set_rtpoll(u->source, u->rtpoll);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    u->source->userdata = u;
 | 
			
		||||
    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_rtpoll(u->source, u->rtpoll);
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1318,6 +1359,11 @@ static int setup_bt(struct userdata *u) {
 | 
			
		|||
 | 
			
		||||
    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)
 | 
			
		||||
        return -1;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1377,6 +1423,12 @@ static int start_thread(struct userdata *u) {
 | 
			
		|||
    pa_assert(!u->thread);
 | 
			
		||||
    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);
 | 
			
		||||
    pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
 | 
			
		||||
    pollfd->fd = u->stream_fd;
 | 
			
		||||
| 
						 | 
				
			
			@ -1410,12 +1462,14 @@ static int card_set_profile(pa_card *c, pa_card_profile *new_profile) {
 | 
			
		|||
 | 
			
		||||
    if (u->sink) {
 | 
			
		||||
        inputs = pa_sink_move_all_start(u->sink);
 | 
			
		||||
        pa_sink_unlink(u->sink);
 | 
			
		||||
	if (!USE_SCO_OVER_PCM(u))
 | 
			
		||||
            pa_sink_unlink(u->sink);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (u->source) {
 | 
			
		||||
        outputs = pa_source_move_all_start(u->source);
 | 
			
		||||
        pa_source_unlink(u->source);
 | 
			
		||||
	if (!USE_SCO_OVER_PCM(u))
 | 
			
		||||
            pa_source_unlink(u->source);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    stop_thread(u);
 | 
			
		||||
| 
						 | 
				
			
			@ -1604,6 +1658,18 @@ int pa__init(pa_module* m) {
 | 
			
		|||
    u->sample_spec = m->core->default_sample_spec;
 | 
			
		||||
    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 ||
 | 
			
		||||
        u->sample_spec.rate <= 0 || u->sample_spec.rate > PA_RATE_MAX) {
 | 
			
		||||
        pa_log_error("Failed to get rate from module arguments");
 | 
			
		||||
| 
						 | 
				
			
			@ -1704,10 +1770,10 @@ void pa__done(pa_module *m) {
 | 
			
		|||
    if (!(u = m->userdata))
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    if (u->sink)
 | 
			
		||||
    if (u->sink && !USE_SCO_OVER_PCM(u))
 | 
			
		||||
        pa_sink_unlink(u->sink);
 | 
			
		||||
 | 
			
		||||
    if (u->source)
 | 
			
		||||
    if (u->source && !USE_SCO_OVER_PCM(u))
 | 
			
		||||
        pa_source_unlink(u->source);
 | 
			
		||||
 | 
			
		||||
    stop_thread(u);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue