From: Pierre-Louis Bossart <pierre-louis.bossart@xxxxxxxxx> Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart at intel.com> diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c index 61fe369..d12c4e3 100644 --- a/src/modules/bluetooth/module-bluetooth-device.c +++ b/src/modules/bluetooth/module-bluetooth-device.c @@ -109,6 +109,9 @@ static const char* const valid_modargs[] = { struct a2dp_info { sbc_capabilities_t sbc_capabilities; + mpeg_capabilities_t mpeg_capabilities; + pa_bool_t has_mpeg; + sbc_t sbc; /* Codec data */ pa_bool_t sbc_initialized; /* Keep track if the encoder is initialized */ size_t codesize, frame_length; /* SBC Codesize, frame_length. We simply cache those values here */ @@ -131,6 +134,7 @@ struct hsp_info { enum profile { PROFILE_A2DP, + PROFILE_A2DP_PASSTHROUGH, PROFILE_A2DP_SOURCE, PROFILE_HSP, PROFILE_HFGW, @@ -157,7 +161,7 @@ struct userdata { pa_rtpoll_item *rtpoll_item; pa_thread *thread; - uint64_t read_index, write_index; + uint64_t s_read_index, s_write_index; /* count in samples instead of bytes */ pa_usec_t started_at; pa_smoother *read_smoother; @@ -169,7 +173,8 @@ struct userdata { int stream_fd; size_t link_mtu; - size_t block_size; + size_t block_size; /* in bytes */ + size_t samples_per_block; /* in samples */ struct a2dp_info a2dp; struct hsp_info hsp; @@ -182,8 +187,22 @@ struct userdata { int service_write_type, service_read_type; pa_bool_t filter_added; + + /* required for PASSTHROUGH profile */ + size_t leftover_bytes; + pa_memchunk leftover_memchunk; /* storage for bytes that could not be sent + in the last packet */ + + }; +static pa_usec_t samples_to_usec(uint64_t length, const pa_sample_spec *spec) { + pa_assert(spec); + pa_return_val_if_fail(pa_sample_spec_valid(spec), 0); + + return ((pa_usec_t) (length * PA_USEC_PER_SEC) / spec->rate); +} + #define FIXED_LATENCY_PLAYBACK_A2DP (25*PA_USEC_PER_MSEC) #define FIXED_LATENCY_RECORD_A2DP (25*PA_USEC_PER_MSEC) #define FIXED_LATENCY_PLAYBACK_HSP (125*PA_USEC_PER_MSEC) @@ -300,6 +319,68 @@ static ssize_t service_expect(struct userdata*u, bt_audio_msg_header_t *rsp, siz } /* Run from main thread */ +static int parse_mpeg_caps(struct userdata *u, uint8_t seid, const struct bt_get_capabilities_rsp *rsp) { + uint16_t bytes_left; + const codec_capabilities_t *codec; + pa_bool_t has_mpeg=FALSE; + + pa_assert(u); + pa_assert(rsp); + pa_assert( u->profile == PROFILE_A2DP_PASSTHROUGH ); + + bytes_left = rsp->h.length - sizeof(*rsp); + + if (bytes_left < sizeof(codec_capabilities_t)) { + pa_log_error("Packet too small to store codec information."); + return -1; + } + + codec = (codec_capabilities_t *) rsp->data; /** ALIGNMENT? **/ + + pa_log_debug("Payload size is %lu %lu", (unsigned long) bytes_left, (unsigned long) sizeof(*codec)); + + if ((u->profile == PROFILE_A2DP_PASSTHROUGH) && codec->transport != BT_CAPABILITIES_TRANSPORT_A2DP) { + pa_log_error("Got capabilities for wrong codec."); + return -1; + } + + while (bytes_left > 0) { + if ((codec->type == BT_A2DP_MPEG12_SINK) && !codec->lock) { + has_mpeg = TRUE; + break; + } + bytes_left -= codec->length; + codec = (const codec_capabilities_t*) ((const uint8_t*) codec + codec->length); + } + + if (bytes_left <= 0 || codec->length != sizeof(u->a2dp.mpeg_capabilities)) + return -1; + + pa_assert(codec->type == BT_A2DP_MPEG12_SINK); + + if (codec->configured && seid == 0) + return codec->seid; + + has_mpeg = TRUE; + memcpy(&u->a2dp.mpeg_capabilities, codec, sizeof(u->a2dp.mpeg_capabilities)); + + pa_log_info("MPEG caps detected"); + pa_log_info("channel_mode %d crc %d layer %d frequency %d mpf %d bitrate %d", + u->a2dp.mpeg_capabilities.channel_mode, + u->a2dp.mpeg_capabilities.crc, + u->a2dp.mpeg_capabilities.layer, + u->a2dp.mpeg_capabilities.frequency, + u->a2dp.mpeg_capabilities.mpf, + u->a2dp.mpeg_capabilities. bitrate); + + u->a2dp.has_mpeg = has_mpeg; + + return 0; +} + + + +/* Run from main thread */ static int parse_caps(struct userdata *u, uint8_t seid, const struct bt_get_capabilities_rsp *rsp) { uint16_t bytes_left; const codec_capabilities_t *codec; @@ -380,44 +461,91 @@ static int parse_caps(struct userdata *u, uint8_t seid, const struct bt_get_capa return 0; } -/* Run from main thread */ -static int get_caps(struct userdata *u, uint8_t seid) { - union { - struct bt_get_capabilities_req getcaps_req; - struct bt_get_capabilities_rsp getcaps_rsp; - bt_audio_error_t error; - uint8_t buf[BT_SUGGESTED_BUFFER_SIZE]; - } msg; - int ret; +typedef union { + struct bt_get_capabilities_req getcaps_req; + struct bt_get_capabilities_rsp getcaps_rsp; + bt_audio_error_t error; + uint8_t buf[BT_SUGGESTED_BUFFER_SIZE]; +} bt_getcaps_msg_t; + + +static int get_caps_msg(struct userdata *u, uint8_t seid, bt_getcaps_msg_t *msg) { pa_assert(u); - memset(&msg, 0, sizeof(msg)); - msg.getcaps_req.h.type = BT_REQUEST; - msg.getcaps_req.h.name = BT_GET_CAPABILITIES; - msg.getcaps_req.h.length = sizeof(msg.getcaps_req); - msg.getcaps_req.seid = seid; - - pa_strlcpy(msg.getcaps_req.object, u->path, sizeof(msg.getcaps_req.object)); - if (u->profile == PROFILE_A2DP || u->profile == PROFILE_A2DP_SOURCE) - msg.getcaps_req.transport = BT_CAPABILITIES_TRANSPORT_A2DP; + memset(msg, 0, sizeof(bt_getcaps_msg_t)); + msg->getcaps_req.h.type = BT_REQUEST; + msg->getcaps_req.h.name = BT_GET_CAPABILITIES; + msg->getcaps_req.h.length = sizeof(struct bt_get_capabilities_req); + msg->getcaps_req.seid = seid; + + pa_strlcpy(msg->getcaps_req.object, u->path, sizeof(msg->getcaps_req.object)); + if (u->profile == PROFILE_A2DP || u->profile == PROFILE_A2DP_PASSTHROUGH || + u->profile == PROFILE_A2DP_SOURCE) + msg->getcaps_req.transport = BT_CAPABILITIES_TRANSPORT_A2DP; else { pa_assert(u->profile == PROFILE_HSP || u->profile == PROFILE_HFGW); - msg.getcaps_req.transport = BT_CAPABILITIES_TRANSPORT_SCO; + msg->getcaps_req.transport = BT_CAPABILITIES_TRANSPORT_SCO; } - msg.getcaps_req.flags = u->auto_connect ? BT_FLAG_AUTOCONNECT : 0; + msg->getcaps_req.flags = u->auto_connect ? BT_FLAG_AUTOCONNECT : 0; - if (service_send(u, &msg.getcaps_req.h) < 0) + if (service_send(u, &msg->getcaps_req.h) < 0) return -1; - if (service_expect(u, &msg.getcaps_rsp.h, sizeof(msg), BT_GET_CAPABILITIES, 0) < 0) + if (service_expect(u, &msg->getcaps_rsp.h, sizeof(bt_getcaps_msg_t), BT_GET_CAPABILITIES, 0) < 0) { + pa_log("BT_GET_CAPABILITIES expect failed"); + return -1; + } + + return 0; +} + +/* Run from main thread */ +static int get_caps(struct userdata *u, uint8_t seid) { + bt_getcaps_msg_t msg; + int ret; + + pa_assert(u); + + if( get_caps_msg(u,seid,&msg) < 0) return -1; ret = parse_caps(u, seid, &msg.getcaps_rsp); - if (ret <= 0) - return ret; + if (ret < 0) + return -1; + + if( ret > 0 ) { + /* refine seid caps */ + if( get_caps_msg(u,ret,&msg) < 0) + return -1; + + ret = parse_caps(u, ret, &msg.getcaps_rsp); + if (ret < 0) + return -1; + } + + if (u->profile == PROFILE_A2DP_PASSTHROUGH ) { + seid = 0; - return get_caps(u, ret); + /* try to find mpeg end-point */ + if( get_caps_msg(u,seid,&msg) < 0) + return -1; + + ret = parse_mpeg_caps(u, seid, &msg.getcaps_rsp); + if (ret < 0) + return -1; + + if( ret > 0 ) { + /* refine seid caps */ + if( get_caps_msg(u,ret,&msg) < 0) + return -1; + + ret = parse_mpeg_caps(u, ret, &msg.getcaps_rsp); + if (ret < 0) + return -1; + } + } + return 0; } /* Run from main thread */ @@ -482,95 +610,142 @@ static int setup_a2dp(struct userdata *u) { }; pa_assert(u); - pa_assert(u->profile == PROFILE_A2DP || u->profile == PROFILE_A2DP_SOURCE); + pa_assert(u->profile == PROFILE_A2DP || u->profile == PROFILE_A2DP_PASSTHROUGH || u->profile == PROFILE_A2DP_SOURCE); + + + if (u->profile == PROFILE_A2DP_PASSTHROUGH) { + mpeg_capabilities_t *mcap; + + if (u->a2dp.has_mpeg) { + int rate; + + mcap = &u->a2dp.mpeg_capabilities; + rate = u->sample_spec.rate; + + if( u->sample_spec.channels == 1) + mcap->channel_mode = BT_A2DP_CHANNEL_MODE_MONO; + else + mcap->channel_mode = BT_A2DP_CHANNEL_MODE_STEREO; + + mcap->crc = 0; /* CRC is broken in some encoders */ + mcap->layer = BT_MPEG_LAYER_3; + + if (rate == 44100) + mcap->frequency = BT_MPEG_SAMPLING_FREQ_44100; + else if (rate == 48000) + mcap->frequency = BT_MPEG_SAMPLING_FREQ_48000; + else if (rate == 32000) + mcap->frequency = BT_MPEG_SAMPLING_FREQ_32000; + else if (rate == 24000) + mcap->frequency = BT_MPEG_SAMPLING_FREQ_24000; + else if (rate == 22050) + mcap->frequency = BT_MPEG_SAMPLING_FREQ_22050; + else if (rate == 16000) + mcap->frequency = BT_MPEG_SAMPLING_FREQ_16000; + else { + pa_log("unsupported sampling frequency"); + return -1; + } - cap = &u->a2dp.sbc_capabilities; + mcap->mpf = 0; /* mpf - we will only use the mandatory one */ - /* Find the lowest freq that is at least as high as the requested - * sampling rate */ - for (i = 0; (unsigned) i < PA_ELEMENTSOF(freq_table); i++) - if (freq_table[i].rate >= u->sample_spec.rate && (cap->frequency & freq_table[i].cap)) { - u->sample_spec.rate = freq_table[i].rate; - cap->frequency = freq_table[i].cap; - break; + /* vbr - we always say its vbr, we don't have how to know it */ + mcap->bitrate = 0x8000; + + } + else { + pa_log("setup_a2dp: Trying to set-up A2DP Passthrough configuration but no MPEG endpoint available"); + return -1; /* this should not happen */ } + } else { - if ((unsigned) i == PA_ELEMENTSOF(freq_table)) { - for (--i; i >= 0; i--) { - if (cap->frequency & freq_table[i].cap) { + cap = &u->a2dp.sbc_capabilities; + + /* Find the lowest freq that is at least as high as the requested + * sampling rate */ + for (i = 0; (unsigned) i < PA_ELEMENTSOF(freq_table); i++) + if (freq_table[i].rate >= u->sample_spec.rate && (cap->frequency & freq_table[i].cap)) { u->sample_spec.rate = freq_table[i].rate; cap->frequency = freq_table[i].cap; break; } - } - if (i < 0) { - pa_log("Not suitable sample rate"); - return -1; + if ((unsigned) i == PA_ELEMENTSOF(freq_table)) { + for (--i; i >= 0; i--) { + if (cap->frequency & freq_table[i].cap) { + u->sample_spec.rate = freq_table[i].rate; + cap->frequency = freq_table[i].cap; + break; + } + } + + if (i < 0) { + pa_log("Not suitable sample rate"); + return -1; + } } - } - pa_assert((unsigned) i < PA_ELEMENTSOF(freq_table)); + pa_assert((unsigned) i < PA_ELEMENTSOF(freq_table)); - if (cap->capability.configured) - return 0; + if (cap->capability.configured) + return 0; - if (u->sample_spec.channels <= 1) { - if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_MONO) { - cap->channel_mode = BT_A2DP_CHANNEL_MODE_MONO; - u->sample_spec.channels = 1; - } else + if (u->sample_spec.channels <= 1) { + if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_MONO) { + cap->channel_mode = BT_A2DP_CHANNEL_MODE_MONO; + u->sample_spec.channels = 1; + } else + u->sample_spec.channels = 2; + } + + if (u->sample_spec.channels >= 2) { u->sample_spec.channels = 2; - } - if (u->sample_spec.channels >= 2) { - u->sample_spec.channels = 2; + if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_JOINT_STEREO) + cap->channel_mode = BT_A2DP_CHANNEL_MODE_JOINT_STEREO; + else if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_STEREO) + cap->channel_mode = BT_A2DP_CHANNEL_MODE_STEREO; + else if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL) + cap->channel_mode = BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL; + else if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_MONO) { + cap->channel_mode = BT_A2DP_CHANNEL_MODE_MONO; + u->sample_spec.channels = 1; + } else { + pa_log("No supported channel modes"); + return -1; + } + } - if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_JOINT_STEREO) - cap->channel_mode = BT_A2DP_CHANNEL_MODE_JOINT_STEREO; - else if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_STEREO) - cap->channel_mode = BT_A2DP_CHANNEL_MODE_STEREO; - else if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL) - cap->channel_mode = BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL; - else if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_MONO) { - cap->channel_mode = BT_A2DP_CHANNEL_MODE_MONO; - u->sample_spec.channels = 1; - } else { - pa_log("No supported channel modes"); + if (cap->block_length & BT_A2DP_BLOCK_LENGTH_16) + cap->block_length = BT_A2DP_BLOCK_LENGTH_16; + else if (cap->block_length & BT_A2DP_BLOCK_LENGTH_12) + cap->block_length = BT_A2DP_BLOCK_LENGTH_12; + else if (cap->block_length & BT_A2DP_BLOCK_LENGTH_8) + cap->block_length = BT_A2DP_BLOCK_LENGTH_8; + else if (cap->block_length & BT_A2DP_BLOCK_LENGTH_4) + cap->block_length = BT_A2DP_BLOCK_LENGTH_4; + else { + pa_log_error("No supported block lengths"); return -1; } - } - if (cap->block_length & BT_A2DP_BLOCK_LENGTH_16) - cap->block_length = BT_A2DP_BLOCK_LENGTH_16; - else if (cap->block_length & BT_A2DP_BLOCK_LENGTH_12) - cap->block_length = BT_A2DP_BLOCK_LENGTH_12; - else if (cap->block_length & BT_A2DP_BLOCK_LENGTH_8) - cap->block_length = BT_A2DP_BLOCK_LENGTH_8; - else if (cap->block_length & BT_A2DP_BLOCK_LENGTH_4) - cap->block_length = BT_A2DP_BLOCK_LENGTH_4; - else { - pa_log_error("No supported block lengths"); - return -1; - } - - if (cap->subbands & BT_A2DP_SUBBANDS_8) - cap->subbands = BT_A2DP_SUBBANDS_8; - else if (cap->subbands & BT_A2DP_SUBBANDS_4) - cap->subbands = BT_A2DP_SUBBANDS_4; - else { - pa_log_error("No supported subbands"); - return -1; - } - - if (cap->allocation_method & BT_A2DP_ALLOCATION_LOUDNESS) - cap->allocation_method = BT_A2DP_ALLOCATION_LOUDNESS; - else if (cap->allocation_method & BT_A2DP_ALLOCATION_SNR) - cap->allocation_method = BT_A2DP_ALLOCATION_SNR; + if (cap->subbands & BT_A2DP_SUBBANDS_8) + cap->subbands = BT_A2DP_SUBBANDS_8; + else if (cap->subbands & BT_A2DP_SUBBANDS_4) + cap->subbands = BT_A2DP_SUBBANDS_4; + else { + pa_log_error("No supported subbands"); + return -1; + } - cap->min_bitpool = (uint8_t) PA_MAX(MIN_BITPOOL, cap->min_bitpool); - cap->max_bitpool = (uint8_t) PA_MIN(a2dp_default_bitpool(cap->frequency, cap->channel_mode), cap->max_bitpool); + if (cap->allocation_method & BT_A2DP_ALLOCATION_LOUDNESS) + cap->allocation_method = BT_A2DP_ALLOCATION_LOUDNESS; + else if (cap->allocation_method & BT_A2DP_ALLOCATION_SNR) + cap->allocation_method = BT_A2DP_ALLOCATION_SNR; + cap->min_bitpool = (uint8_t) PA_MAX(MIN_BITPOOL, cap->min_bitpool); + cap->max_bitpool = (uint8_t) PA_MIN(a2dp_default_bitpool(cap->frequency, cap->channel_mode), cap->max_bitpool); + } return 0; } @@ -683,17 +858,37 @@ static int set_conf(struct userdata *u) { msg.open_req.h.length = sizeof(msg.open_req); pa_strlcpy(msg.open_req.object, u->path, sizeof(msg.open_req.object)); - msg.open_req.seid = (u->profile == PROFILE_A2DP || u->profile == PROFILE_A2DP_SOURCE) ? u->a2dp.sbc_capabilities.capability.seid : BT_A2DP_SEID_RANGE + 1; - msg.open_req.lock = (u->profile == PROFILE_A2DP) ? BT_WRITE_LOCK : BT_READ_LOCK | BT_WRITE_LOCK; + + if (u->profile == PROFILE_A2DP || u->profile == PROFILE_A2DP_SOURCE) { + msg.open_req.seid = u->a2dp.sbc_capabilities.capability.seid; + } else if (u->profile == PROFILE_A2DP_PASSTHROUGH) { + if (u->a2dp.has_mpeg) { + msg.open_req.seid = u->a2dp.mpeg_capabilities.capability.seid; + } else { + pa_log("set_conf(): Trying to open MPEG endpoint but no MPEG endpoint available"); + return -1; + } + } else { + msg.open_req.seid = BT_A2DP_SEID_RANGE + 1; + } + + msg.open_req.lock = (u->profile == PROFILE_A2DP || u->profile == PROFILE_A2DP_PASSTHROUGH) ? BT_WRITE_LOCK : BT_READ_LOCK | BT_WRITE_LOCK; if (service_send(u, &msg.open_req.h) < 0) return -1; - if (service_expect(u, &msg.open_rsp.h, sizeof(msg), BT_OPEN, sizeof(msg.open_rsp)) < 0) + if (service_expect(u, &msg.open_rsp.h, sizeof(msg), BT_OPEN, sizeof(msg.open_rsp)) < 0) { + pa_log("BT_OPEN expect failed"); return -1; + } - if (u->profile == PROFILE_A2DP || u->profile == PROFILE_A2DP_SOURCE) { - u->sample_spec.format = PA_SAMPLE_S16LE; + if (u->profile == PROFILE_A2DP || u->profile == PROFILE_A2DP_PASSTHROUGH || u->profile == PROFILE_A2DP_SOURCE ) { + + if (u->profile == PROFILE_A2DP_PASSTHROUGH) { + u->sample_spec.format = PA_MP3; + } else { + u->sample_spec.format = PA_SAMPLE_S16LE; + } if (setup_a2dp(u) < 0) return -1; @@ -712,6 +907,13 @@ static int set_conf(struct userdata *u) { if (u->profile == PROFILE_A2DP || u->profile == PROFILE_A2DP_SOURCE) { memcpy(&msg.setconf_req.codec, &u->a2dp.sbc_capabilities, sizeof(u->a2dp.sbc_capabilities)); + } else if (u->profile == PROFILE_A2DP_PASSTHROUGH) { + if(u->a2dp.has_mpeg) { + memcpy(&msg.setconf_req.codec, &u->a2dp.mpeg_capabilities, sizeof(u->a2dp.mpeg_capabilities)); + } else { + pa_log("set_conf(): Trying to configure MPEG endpoint but no MPEG endpoint available"); + return -1; + } } else { msg.setconf_req.codec.transport = BT_CAPABILITIES_TRANSPORT_SCO; msg.setconf_req.codec.seid = BT_A2DP_SEID_RANGE + 1; @@ -719,28 +921,50 @@ static int set_conf(struct userdata *u) { } msg.setconf_req.h.length += msg.setconf_req.codec.length - sizeof(msg.setconf_req.codec); - if (service_send(u, &msg.setconf_req.h) < 0) + if (service_send(u, &msg.setconf_req.h) < 0) { + pa_log("BT_SET_CONFIGURATION send failed"); return -1; - - if (service_expect(u, &msg.setconf_rsp.h, sizeof(msg), BT_SET_CONFIGURATION, sizeof(msg.setconf_rsp)) < 0) + } + if (service_expect(u, &msg.setconf_rsp.h, sizeof(msg), BT_SET_CONFIGURATION, sizeof(msg.setconf_rsp)) < 0) { + pa_log("BT_SET_CONFIGURATION expect failed"); return -1; - + } u->link_mtu = msg.setconf_rsp.link_mtu; /* setup SBC encoder now we agree on parameters */ if (u->profile == PROFILE_A2DP || u->profile == PROFILE_A2DP_SOURCE) { + setup_sbc(&u->a2dp); + /* number of input bytes per packet */ u->block_size = - ((u->link_mtu - sizeof(struct rtp_header) - sizeof(struct rtp_payload)) - / u->a2dp.frame_length - * u->a2dp.codesize); + (u->link_mtu - sizeof(struct rtp_header) - sizeof(struct sbc_rtp_payload)) + / (u->a2dp.frame_length) + * (u->a2dp.codesize); + + /* number of samples per packet */ + u->samples_per_block = + (u->link_mtu - sizeof(struct rtp_header) - sizeof(struct sbc_rtp_payload)) + / (u->a2dp.frame_length) + * (u->a2dp.sbc.subbands ? 8 : 4) * (4 + (u->a2dp.sbc.blocks * 4)); pa_log_info("SBC parameters:\n\tallocation=%u\n\tsubbands=%u\n\tblocks=%u\n\tbitpool=%u\n", u->a2dp.sbc.allocation, u->a2dp.sbc.subbands, u->a2dp.sbc.blocks, u->a2dp.sbc.bitpool); - } else - u->block_size = u->link_mtu; + } else if (u->profile == PROFILE_A2DP_PASSTHROUGH) { + /* available payload per packet */ + u->block_size = + ((u->link_mtu - sizeof(struct rtp_header) - sizeof(struct mpeg_rtp_payload)) /4 ) * 4; /* force 32-bit length to simplify synchronization */ + + /* FIXME : number of samples per packet */ + u->samples_per_block = 1152; + + u->leftover_bytes=0; + + } else { + u->block_size = u->link_mtu; + u->samples_per_block = u->link_mtu/pa_frame_size(&u->sample_spec); + } return 0; } @@ -795,7 +1019,7 @@ static int start_stream_fd(struct userdata *u) { pollfd->fd = u->stream_fd; pollfd->events = pollfd->revents = 0; - u->read_index = u->write_index = 0; + u->s_read_index = u->s_write_index = 0; u->started_at = 0; if (u->source) @@ -902,14 +1126,14 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse pa_usec_t wi, ri; ri = pa_smoother_get(u->read_smoother, pa_rtclock_now()); - wi = pa_bytes_to_usec(u->write_index + u->block_size, &u->sample_spec); + wi = samples_to_usec(u->s_write_index + u->samples_per_block, &u->sample_spec); *((pa_usec_t*) data) = wi > ri ? wi - ri : 0; } else { pa_usec_t ri, wi; ri = pa_rtclock_now() - u->started_at; - wi = pa_bytes_to_usec(u->write_index, &u->sample_spec); + wi = samples_to_usec(u->s_write_index, &u->sample_spec); *((pa_usec_t*) data) = wi > ri ? wi - ri : 0; } @@ -975,7 +1199,7 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off if (u->read_smoother) { wi = pa_smoother_get(u->read_smoother, pa_rtclock_now()); - ri = pa_bytes_to_usec(u->read_index, &u->sample_spec); + ri = samples_to_usec(u->s_read_index, &u->sample_spec); *((pa_usec_t*) data) = (wi > ri ? wi - ri : 0) + u->source->thread_info.fixed_latency; } else @@ -1043,8 +1267,7 @@ static int hsp_process_render(struct userdata *u) { ret = -1; break; } - - u->write_index += (uint64_t) u->write_memchunk.length; + u->s_write_index += (uint64_t) u->write_memchunk.length/pa_frame_size(&u->sample_spec); pa_memblock_unref(u->write_memchunk.memblock); pa_memchunk_reset(&u->write_memchunk); @@ -1111,7 +1334,7 @@ static int hsp_process_push(struct userdata *u) { pa_assert((size_t) l <= pa_memblock_get_length(memchunk.memblock)); memchunk.length = (size_t) l; - u->read_index += (uint64_t) l; + u->s_read_index += (uint64_t) l/pa_frame_size(&u->sample_spec); for (cm = CMSG_FIRSTHDR(&m); cm; cm = CMSG_NXTHDR(&m, cm)) if (cm->cmsg_level == SOL_SOCKET && cm->cmsg_type == SO_TIMESTAMP) { @@ -1127,7 +1350,7 @@ static int hsp_process_push(struct userdata *u) { tstamp = pa_rtclock_now(); } - pa_smoother_put(u->read_smoother, tstamp, pa_bytes_to_usec(u->read_index, &u->sample_spec)); + pa_smoother_put(u->read_smoother, tstamp, samples_to_usec(u->s_read_index, &u->sample_spec)); pa_smoother_resume(u->read_smoother, tstamp, TRUE); pa_source_post(u->source, &memchunk); @@ -1157,7 +1380,7 @@ static void a2dp_prepare_buffer(struct userdata *u) { static int a2dp_process_render(struct userdata *u) { struct a2dp_info *a2dp; struct rtp_header *header; - struct rtp_payload *payload; + struct sbc_rtp_payload *payload; size_t nbytes; void *d; const void *p; @@ -1179,7 +1402,7 @@ static int a2dp_process_render(struct userdata *u) { a2dp = &u->a2dp; header = a2dp->buffer; - payload = (struct rtp_payload*) ((uint8_t*) a2dp->buffer + sizeof(*header)); + payload = (struct sbc_rtp_payload*) ((uint8_t*) a2dp->buffer + sizeof(*header)); frame_count = 0; @@ -1237,7 +1460,7 @@ static int a2dp_process_render(struct userdata *u) { header->v = 2; header->pt = 1; header->sequence_number = htons(a2dp->seq_num++); - header->timestamp = htonl(u->write_index / pa_frame_size(&u->sample_spec)); + header->timestamp = htonl(u->s_write_index); /* sample count */ header->ssrc = htonl(1); payload->frame_count = frame_count; @@ -1275,7 +1498,7 @@ static int a2dp_process_render(struct userdata *u) { break; } - u->write_index += (uint64_t) u->write_memchunk.length; + u->s_write_index += (uint64_t) u->samples_per_block; pa_memblock_unref(u->write_memchunk.memblock); pa_memchunk_reset(&u->write_memchunk); @@ -1287,6 +1510,334 @@ static int a2dp_process_render(struct userdata *u) { return ret; } + +static uint32_t uextract(uint32_t x, uint32_t position, uint32_t nbits) +{ + int mask; + + pa_assert(position <= 31); + pa_assert(nbits <= 31); + + x = x >> position; + mask = (1<<nbits)-1; + x = x & mask; + + return x; +} + + +#define MPEG_LAYER_INDEX 4 +#define MPEG_BITRATE_INDEX 16 +#define MPEG_SAMPFREQ_INDEX 4 +#define MPEG_INDEX 2 + +unsigned short const mpeg_sampling_frequencies[MPEG_INDEX][MPEG_SAMPFREQ_INDEX] = { + {22050, 24000, 16000, 0}, + {44100, 48000, 32000, 0} +}; + +short const mpeg_layer_bitrates[MPEG_INDEX][MPEG_BITRATE_INDEX] = { + { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, -1}, + { 0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, -1} +}; + +short const mpeg_samples_per_frame[MPEG_INDEX][MPEG_LAYER_INDEX] = { + {0, 576, 1152, 384}, + {0, 1152, 1152, 384} +}; + +static int mp3_synclength(uint32_t hi, uint32_t *len, uint32_t *sample_len) +{ + unsigned int tmp; + unsigned int idex; + unsigned int id; + unsigned int bitrate; + unsigned int freq; + unsigned int bits; + + tmp = uextract(hi, 21, 11); + if (tmp == 0x7ff) { /* valid sync word */ + tmp = uextract(hi,19,2); + if (tmp != 1) { /* valid IDex */ + + idex = tmp >> 1; + if (idex != 0) { /* MP3 2.5, not supported by A2DP */ + + id = tmp & 1; + tmp = uextract(hi,17,2); + + if (tmp == 0x1) { /* layer 3 */ + + bitrate = uextract(hi,12,4); + if ((bitrate != 0) && /* not free format */ + (bitrate != 0xf)) { /* not reserved */ + + freq = uextract(hi,10, 2); + + if (freq != 3) { /* valid sampling frequency */ + + tmp = uextract(hi,9,1); + + bitrate = mpeg_layer_bitrates[id][bitrate] * 1000; + + bits = + (unsigned int) ((bitrate * mpeg_samples_per_frame[id][1]) / + mpeg_sampling_frequencies[id][freq]); + + + bits /= 8; /* # of bytes */ + if (tmp) { /* padding */ + bits += 1; + } + + /* sanity check */ + if (bits > 4 && bits <= 1728) { /* max frame length */ + *len = bits; + *sample_len = mpeg_samples_per_frame[id][1]; + return 0; + } + } + } + } + } + } + } else { + if(hi!=0) + pa_log("no sync word found %x",hi); + } + return 1; +} + +static int do_sync(uint8_t **p_src,uint8_t **p_dest, uint32_t *input_bytes, uint32_t *output_bytes, + uint32_t *frame_count, uint32_t *sample_count) +{ + int sync_found=0; + uint32_t len, sample_len; + uint8_t *src=*p_src; + uint8_t *dest=*p_dest; + + len = 0; /* remove compiler warning */ + while (1) { + + if ( *input_bytes< 4 ) + break; + + /* we need 4 bytes to detect the frame length */ + if (!mp3_synclength(src[0]<<24|src[1]<<16|src[2]<<8|src[3], + &len, &sample_len) ) { + + sync_found = 1; + + if (len>*input_bytes) { + /* we don't have a full frame in the input buffer */ + break; + } + + /* make sure there's enough room in the output buffer */ + if (len > *output_bytes) + break; + + /* copy complete frame */ + memcpy(dest,src,len); + dest = dest + len; + src = src + len; + + *input_bytes -= len; + *output_bytes -= len; + + *frame_count += 1; + *sample_count += sample_len; + + } else { + + sync_found = 0; + + /* try to find a new syncword */ + src += 1; + *input_bytes -= 1; + } + } + *p_src=src; + *p_dest=dest; + + return sync_found; +} /* end do_sync() */ + + +static int a2dp_passthrough_process_render(struct userdata *u) { + struct a2dp_info *a2dp; + struct rtp_header *header; + struct mpeg_rtp_payload *payload; + size_t nbytes; + void *d; + const void *p; + size_t dest_bytes; /* in the destination buffer */ + size_t orig_bytes=0; /* in the sink buffer */ + uint32_t frame_count; + uint32_t sample_count=0; + uint32_t sync_found = 0; + uint32_t talk_spurt; + + int ret = 0; + + pa_assert(u); + pa_assert(u->profile == PROFILE_A2DP_PASSTHROUGH); + pa_assert(u->sink); + + /* inits for output buffer */ + a2dp_prepare_buffer(u); + + a2dp = &u->a2dp; + header = a2dp->buffer; + payload = (struct mpeg_rtp_payload*) ((uint8_t*) a2dp->buffer + sizeof(*header)); + d = (uint8_t*) a2dp->buffer + sizeof(*header) + sizeof(*payload); + dest_bytes = a2dp->buffer_size - sizeof(*header) - sizeof(*payload); + + if (dest_bytes > u->block_size) { + dest_bytes = u->block_size; + } + + frame_count = 0; + + + /* render some data */ + if (!u->write_memchunk.memblock) { + pa_memchunk *c; + + pa_assert(u->leftover_bytes == 0); + c = &u->write_memchunk; + c->memblock = pa_memblock_new(u->sink->core->mempool,u->block_size); + c->index = 0; + c->length = u->block_size; + } + + if (u->leftover_bytes) { /* incomplete frame that wasn't handled in the previous call */ + pa_memchunk *c; + pa_memblock *n; + void *tdata, *sdata; + size_t l; + + c = &u->write_memchunk; + n = pa_memblock_new(u->sink->core->mempool,u->block_size); + + sdata = pa_memblock_acquire(c->memblock); + tdata = pa_memblock_acquire(n); + + l = c->length; + memcpy(tdata, (uint8_t*) sdata + c->index, l); + + pa_memblock_release(c->memblock); + pa_memblock_release(n); + + pa_memblock_unref(c->memblock); + + c->memblock = n; + c->index = l; + c->length = u->block_size - l; + } + + /* fill memchunck, previous leftover bytes have been copied into beginning of frame already */ + pa_sink_render_into_full(u->sink, &u->write_memchunk); + + orig_bytes = u->block_size; + + p = (const uint8_t*) pa_memblock_acquire(u->write_memchunk.memblock); + + sync_found = do_sync((uint8_t**)&p,(uint8_t**)&d, &orig_bytes, &dest_bytes, + &frame_count, &sample_count); + + u->write_memchunk.index = u->block_size-orig_bytes; + u->write_memchunk.length = orig_bytes; + + pa_memblock_release(u->write_memchunk.memblock); + + if (sync_found) { + nbytes = (uint8_t*) d - (uint8_t*) a2dp->buffer; + + u->leftover_bytes=orig_bytes; + + if (u->leftover_bytes == 0) { + pa_memblock_unref(u->write_memchunk.memblock); + pa_memchunk_reset(&u->write_memchunk); + } + + talk_spurt = 1; + } else { + /* we lost the sync here, zero out rest of buffer */ + memset(d, 0, dest_bytes); + + u->leftover_bytes=0; + pa_memblock_unref(u->write_memchunk.memblock); + pa_memchunk_reset(&u->write_memchunk); + + /* FIXME: this is completely broken, force the write index to some value */ + u->s_write_index += (uint64_t) 1152; + ret = 1; + + return ret; + } + + pa_assert(nbytes!=0); + pa_assert(nbytes<=(u->block_size+sizeof(*header)+sizeof(*payload))); + + /* write it to the fifo */ + + memset(a2dp->buffer, 0, sizeof(*header) + sizeof(*payload)); + header->v = 2; /* rtp packet v2 */ + header->pt = 14; /* MPA payload type */ + header->timestamp = htonl((u->s_write_index*90000)/u->sample_spec.rate); /* 90kHz timestamp */ + header->m = talk_spurt; + header->sequence_number = htons(a2dp->seq_num++); + header->ssrc = htonl(1); /* should in theory be random */ + + payload->mbz = 0; + payload->frag_offset = 0; + + pa_assert(nbytes != 0); + pa_assert(nbytes <= u->link_mtu); + + for (;;) { + ssize_t l; + + l = pa_write(u->stream_fd, a2dp->buffer, nbytes, &u->stream_write_type); + + pa_assert(l != 0); + + if (l < 0) { + + if (errno == EINTR) + /* Retry right away if we got interrupted */ + continue; + + else if (errno == EAGAIN) + /* Hmm, apparently the socket was not writable, give up for now */ + break; + + pa_log_error("Failed to write data to socket: %s", pa_cstrerror(errno)); + ret = -1; + break; + } + + pa_assert((size_t) l <= nbytes); + + if ((size_t) l != nbytes) { + pa_log_warn("Wrote memory block to socket only partially! %llu written, wanted to write %llu.", + (unsigned long long) l, + (unsigned long long) nbytes); + ret = -1; + break; + } + + u->s_write_index += (uint64_t) sample_count; + + ret = 1; + + break; + } + + return ret; +} + static int a2dp_process_push(struct userdata *u) { int ret = 0; pa_memchunk memchunk; @@ -1304,7 +1855,7 @@ static int a2dp_process_push(struct userdata *u) { pa_usec_t tstamp; struct a2dp_info *a2dp; struct rtp_header *header; - struct rtp_payload *payload; + struct sbc_rtp_payload *payload; const void *p; void *d; ssize_t l; @@ -1315,7 +1866,7 @@ static int a2dp_process_push(struct userdata *u) { a2dp = &u->a2dp; header = a2dp->buffer; - payload = (struct rtp_payload*) ((uint8_t*) a2dp->buffer + sizeof(*header)); + payload = (struct sbc_rtp_payload*) ((uint8_t*) a2dp->buffer + sizeof(*header)); l = pa_read(u->stream_fd, a2dp->buffer, a2dp->buffer_size, &u->stream_write_type); @@ -1336,7 +1887,7 @@ static int a2dp_process_push(struct userdata *u) { pa_assert((size_t) l <= a2dp->buffer_size); - u->read_index += (uint64_t) l; + u->s_read_index += (uint64_t) l/pa_frame_size(&u->sample_spec); /* TODO: get timestamp from rtp */ if (!found_tstamp) { @@ -1344,7 +1895,7 @@ static int a2dp_process_push(struct userdata *u) { tstamp = pa_rtclock_now(); } - pa_smoother_put(u->read_smoother, tstamp, pa_bytes_to_usec(u->read_index, &u->sample_spec)); + pa_smoother_put(u->read_smoother, tstamp, samples_to_usec(u->s_read_index, &u->sample_spec)); pa_smoother_resume(u->read_smoother, tstamp, TRUE); p = (uint8_t*) a2dp->buffer + sizeof(*header) + sizeof(*payload); @@ -1428,8 +1979,7 @@ static void thread_func(void *userdata) { /* We should send two blocks to the device before we expect * a response. */ - - if (u->write_index == 0 && u->read_index <= 0) + if (u->s_write_index == 0 && u->s_read_index <= 0) do_write = 2; if (pollfd && (pollfd->revents & POLLIN)) { @@ -1465,19 +2015,20 @@ static void thread_func(void *userdata) { * to. So let's do things by time */ time_passed = pa_rtclock_now() - u->started_at; - audio_sent = pa_bytes_to_usec(u->write_index, &u->sample_spec); + audio_sent = samples_to_usec(u->s_write_index, &u->sample_spec); if (audio_sent <= time_passed) { pa_usec_t audio_to_send = time_passed - audio_sent; /* Never try to catch up for more than 100ms */ - if (u->write_index > 0 && audio_to_send > MAX_PLAYBACK_CATCH_UP_USEC) { + if (u->s_write_index > 0 && audio_to_send > MAX_PLAYBACK_CATCH_UP_USEC) { pa_usec_t skip_usec; uint64_t skip_bytes; skip_usec = audio_to_send - MAX_PLAYBACK_CATCH_UP_USEC; skip_bytes = pa_usec_to_bytes(skip_usec, &u->sample_spec); + /* FIXME: this doesn't work for PASSTHROUGH */ if (skip_bytes > 0) { pa_memchunk tmp; @@ -1487,9 +2038,10 @@ static void thread_func(void *userdata) { pa_sink_render_full(u->sink, skip_bytes, &tmp); pa_memblock_unref(tmp.memblock); - u->write_index += skip_bytes; - } - } + pa_assert(u->profile != PROFILE_A2DP_PASSTHROUGH); + u->s_write_index += skip_bytes/pa_frame_size(&u->sample_spec); + } + } do_write = 1; } @@ -1498,12 +2050,15 @@ static void thread_func(void *userdata) { if (writable && do_write > 0) { int n_written; - if (u->write_index <= 0) + if (u->s_write_index <= 0) u->started_at = pa_rtclock_now(); if (u->profile == PROFILE_A2DP) { if ((n_written = a2dp_process_render(u)) < 0) goto fail; + } else if (u->profile == PROFILE_A2DP_PASSTHROUGH) { + if ((n_written = a2dp_passthrough_process_render(u)) < 0) + goto fail; } else { if ((n_written = hsp_process_render(u)) < 0) goto fail; @@ -1523,7 +2078,7 @@ static void thread_func(void *userdata) { * to. So let's estimate when we need to wake up the latest */ time_passed = pa_rtclock_now() - u->started_at; - next_write_at = pa_bytes_to_usec(u->write_index, &u->sample_spec); + next_write_at = samples_to_usec(u->s_write_index, &u->sample_spec); sleep_for = time_passed < next_write_at ? next_write_at - time_passed : 0; /* pa_log("Sleeping for %lu; time passed %lu, next write at %lu", (unsigned long) sleep_for, (unsigned long) time_passed, (unsigned long)next_write_at); */ @@ -1782,7 +2337,7 @@ static int add_sink(struct userdata *u) { 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"); + pa_proplist_sets(data.proplist, "bluetooth.protocol", (u->profile == PROFILE_A2DP || u->profile == PROFILE_A2DP_PASSTHROUGH) ? "a2dp" : "sco"); if (u->profile == PROFILE_HSP) pa_proplist_sets(data.proplist, PA_PROP_DEVICE_INTENDED_ROLES, "phone"); data.card = u->card; @@ -1807,16 +2362,24 @@ static int add_sink(struct userdata *u) { u->sink->parent.process_msg = sink_process_msg; pa_sink_set_max_request(u->sink, u->block_size); - pa_sink_set_fixed_latency(u->sink, - (u->profile == PROFILE_A2DP ? FIXED_LATENCY_PLAYBACK_A2DP : FIXED_LATENCY_PLAYBACK_HSP) + - pa_bytes_to_usec(u->block_size, &u->sample_spec)); - } + if (u->profile == PROFILE_A2DP_PASSTHROUGH) { + /* FIXME: latency is broken for now */ + pa_sink_set_fixed_latency(u->sink,FIXED_LATENCY_PLAYBACK_A2DP); + } else { + pa_sink_set_fixed_latency(u->sink, + ((u->profile == PROFILE_A2DP)? FIXED_LATENCY_PLAYBACK_A2DP : FIXED_LATENCY_PLAYBACK_HSP) + + pa_bytes_to_usec(u->block_size, &u->sample_spec)); + } + } if (u->profile == PROFILE_HSP) { u->sink->set_volume = sink_set_volume_cb; u->sink->n_volume_steps = 16; } + if (u->profile == PROFILE_A2DP_PASSTHROUGH) { + u->sink->flags |= PA_SINK_PASSTHROUGH; + } return 0; } @@ -1962,6 +2525,7 @@ static int init_profile(struct userdata *u) { return -1; if (u->profile == PROFILE_A2DP || + u->profile == PROFILE_A2DP_PASSTHROUGH || u->profile == PROFILE_HSP || u->profile == PROFILE_HFGW) if (add_sink(u) < 0) @@ -2104,6 +2668,10 @@ static int card_set_profile(pa_card *c, pa_card_profile *new_profile) { pa_log_warn("A2DP is not connected, refused to switch profile"); return -PA_ERR_IO; } + else if (device->audio_sink_state < PA_BT_AUDIO_STATE_CONNECTED && *d == PROFILE_A2DP_PASSTHROUGH) { + pa_log_warn("A2DP Passthrough is not connected, refused to switch profile"); + return -PA_ERR_IO; + } else if (device->hfgw_state <= PA_BT_AUDIO_STATE_CONNECTED && *d == PROFILE_HFGW) { pa_log_warn("HandsfreeGateway is not connected, refused to switch profile"); return -PA_ERR_IO; @@ -2212,6 +2780,20 @@ static int add_card(struct userdata *u, const pa_bluetooth_device *device) { *d = PROFILE_A2DP; pa_hashmap_put(data.profiles, p->name, p); + + /* add passthrough profile */ + p = pa_card_profile_new("passthrough", _("MP3 passthrough (A2DP)"), sizeof(enum profile)); + p->priority = 5; + p->n_sinks = 1; + p->n_sources = 0; + p->max_sink_channels = 2; + p->max_source_channels = 0; + + d = PA_CARD_PROFILE_DATA(p); + *d = PROFILE_A2DP_PASSTHROUGH; + + pa_hashmap_put(data.profiles, p->name, p); + } if (pa_bluetooth_uuid_has(device->uuids, A2DP_SOURCE_UUID)) { @@ -2286,6 +2868,7 @@ static int add_card(struct userdata *u, const pa_bluetooth_device *device) { if ((device->headset_state < PA_BT_AUDIO_STATE_CONNECTED && *d == PROFILE_HSP) || (device->audio_sink_state < PA_BT_AUDIO_STATE_CONNECTED && *d == PROFILE_A2DP) || + (device->audio_sink_state < PA_BT_AUDIO_STATE_CONNECTED && *d == PROFILE_A2DP_PASSTHROUGH) || (device->hfgw_state < PA_BT_AUDIO_STATE_CONNECTED && *d == PROFILE_HFGW)) { pa_log_warn("Default profile not connected, selecting off profile"); u->card->active_profile = pa_hashmap_get(u->card->profiles, "off"); diff --git a/src/modules/bluetooth/rtp.h b/src/modules/bluetooth/rtp.h index 1457362..19307ac 100644 --- a/src/modules/bluetooth/rtp.h +++ b/src/modules/bluetooth/rtp.h @@ -38,7 +38,7 @@ struct rtp_header { uint32_t csrc[0]; } __attribute__ ((packed)); -struct rtp_payload { +struct sbc_rtp_payload { unsigned frame_count:4; unsigned rfa0:1; unsigned is_last_fragment:1; @@ -46,6 +46,11 @@ struct rtp_payload { unsigned is_fragmented:1; } __attribute__ ((packed)); +struct mpeg_rtp_payload { + unsigned mbz:16; + unsigned frag_offset:16; +} __attribute__ ((packed)); + #elif __BYTE_ORDER == __BIG_ENDIAN struct rtp_header { @@ -63,7 +68,7 @@ struct rtp_header { uint32_t csrc[0]; } __attribute__ ((packed)); -struct rtp_payload { +struct sbc_rtp_payload { unsigned is_fragmented:1; unsigned is_first_fragment:1; unsigned is_last_fragment:1; @@ -71,6 +76,11 @@ struct rtp_payload { unsigned frame_count:4; } __attribute__ ((packed)); +struct mpeg_rtp_payload { + unsigned frag_offset:16; + unsigned mbz:16; +} __attribute__ ((packed)); + #else #error "Unknown byte order" #endif -- 1.7.2.3