mirror of
https://gitlab.freedesktop.org/pulseaudio/pulseaudio.git
synced 2025-11-04 13:29:59 -05:00
update master
This commit is contained in:
commit
7138fa0272
227 changed files with 67492 additions and 3107 deletions
|
|
@ -46,7 +46,12 @@
|
|||
#include <pulsecore/socket-util.h>
|
||||
#include <pulsecore/thread.h>
|
||||
#include <pulsecore/thread-mq.h>
|
||||
|
||||
#ifdef USE_SMOOTHER_2
|
||||
#include <pulsecore/time-smoother_2.h>
|
||||
#else
|
||||
#include <pulsecore/time-smoother.h>
|
||||
#endif
|
||||
|
||||
#include "a2dp-codecs.h"
|
||||
#include "a2dp-codec-util.h"
|
||||
|
|
@ -110,6 +115,11 @@ struct userdata {
|
|||
|
||||
pa_hook_slot *sink_volume_changed_slot;
|
||||
pa_hook_slot *source_volume_changed_slot;
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
|
||||
pa_hook_slot *source_output_new_hook_slot;
|
||||
>>>>>>> c1990dd02647405b0c13aab59f75d05cbb202336
|
||||
|
||||
pa_bluetooth_discovery *discovery;
|
||||
pa_bluetooth_device *device;
|
||||
|
|
@ -138,7 +148,13 @@ struct userdata {
|
|||
uint64_t read_index;
|
||||
uint64_t write_index;
|
||||
pa_usec_t started_at;
|
||||
|
||||
#ifdef USE_SMOOTHER_2
|
||||
pa_smoother_2 *read_smoother;
|
||||
#else
|
||||
pa_smoother *read_smoother;
|
||||
#endif
|
||||
|
||||
pa_memchunk write_memchunk;
|
||||
|
||||
const pa_bt_codec *bt_codec;
|
||||
|
|
@ -260,25 +276,65 @@ static void connect_ports(struct userdata *u, void *new_data, pa_direction_t dir
|
|||
|
||||
static bool bt_prepare_encoder_buffer(struct userdata *u)
|
||||
{
|
||||
<<<<<<< HEAD
|
||||
size_t encoded_size, reserved_size;
|
||||
=======
|
||||
size_t encoded_size, reserved_size, encoded_frames;
|
||||
>>>>>>> c1990dd02647405b0c13aab59f75d05cbb202336
|
||||
pa_assert(u);
|
||||
pa_assert(u->bt_codec);
|
||||
|
||||
/* If socket write MTU is less than encoded frame size, there could be
|
||||
* up to one write MTU of data left in encoder buffer from previous round.
|
||||
*
|
||||
<<<<<<< HEAD
|
||||
* Reserve space for 2 encoded frames to cover that.
|
||||
=======
|
||||
* Reserve space for at least 2 encoded frames to cover that.
|
||||
>>>>>>> c1990dd02647405b0c13aab59f75d05cbb202336
|
||||
*
|
||||
* Note for A2DP codecs it is expected that size of encoded frame is less
|
||||
* than write link MTU. Therefore each encoded frame is sent out completely
|
||||
* and there is no used space in encoder buffer before next encoder call.
|
||||
<<<<<<< HEAD
|
||||
*/
|
||||
=======
|
||||
*
|
||||
* For SCO socket all writes will be of MTU size to match payload length
|
||||
* of HCI packet. Depending on selected USB Alternate Setting the payload
|
||||
* length of HCI packet may exceed encoded frame size. For mSBC frame size
|
||||
* is 60 bytes, payload length of HCI packet in USB Alts 3 is 72 byte,
|
||||
* in USB Alts 5 it is 144 bytes.
|
||||
*
|
||||
* Reserve space for up to 1 + MTU / (encoded frame size) encoded frames
|
||||
* to cover that.
|
||||
*
|
||||
* Note for current linux kernel (up to 5.13.x at least) there is no way to
|
||||
* reliably detect socket MTU size. For now we just set SCO socket MTU to be
|
||||
* large enough to cover all known sizes (largest is USB ALts 5 with 144 bytes)
|
||||
* and adjust SCO write size to be equal to last SCO read size. This makes
|
||||
* write size less or equal to MTU size. Reserving the same number of encoded
|
||||
* frames to cover full MTU is still enough.
|
||||
* See also https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/merge_requests/254#note_779802
|
||||
*/
|
||||
|
||||
>>>>>>> c1990dd02647405b0c13aab59f75d05cbb202336
|
||||
if (u->bt_codec->get_encoded_block_size)
|
||||
encoded_size = u->bt_codec->get_encoded_block_size(u->encoder_info, u->write_block_size);
|
||||
else
|
||||
encoded_size = u->write_block_size;
|
||||
<<<<<<< HEAD
|
||||
|
||||
reserved_size = 2 * encoded_size;
|
||||
=======
|
||||
|
||||
encoded_frames = u->write_link_mtu / u->write_block_size + 1;
|
||||
|
||||
if (encoded_frames < 2)
|
||||
encoded_frames = 2;
|
||||
|
||||
reserved_size = encoded_frames * encoded_size;
|
||||
>>>>>>> c1990dd02647405b0c13aab59f75d05cbb202336
|
||||
|
||||
if (u->encoder_buffer_size < reserved_size) {
|
||||
u->encoder_buffer = pa_xrealloc(u->encoder_buffer, reserved_size);
|
||||
|
|
@ -288,11 +344,19 @@ static bool bt_prepare_encoder_buffer(struct userdata *u)
|
|||
u->encoder_buffer_used = 0;
|
||||
}
|
||||
}
|
||||
<<<<<<< HEAD
|
||||
|
||||
/* Report if there is still not enough space for new block */
|
||||
if (u->encoder_buffer_size < u->encoder_buffer_used + encoded_size)
|
||||
return false;
|
||||
|
||||
=======
|
||||
|
||||
/* Report if there is still not enough space for new block */
|
||||
if (u->encoder_buffer_size < u->encoder_buffer_used + encoded_size)
|
||||
return false;
|
||||
|
||||
>>>>>>> c1990dd02647405b0c13aab59f75d05cbb202336
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -358,6 +422,8 @@ static int bt_process_render(struct userdata *u) {
|
|||
|
||||
if (processed != u->write_memchunk.length) {
|
||||
pa_log_error("Encoding error");
|
||||
pa_memblock_unref(u->write_memchunk.memblock);
|
||||
pa_memchunk_reset(&u->write_memchunk);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
@ -375,10 +441,17 @@ static int bt_process_render(struct userdata *u) {
|
|||
u->write_index += (uint64_t) u->write_memchunk.length;
|
||||
pa_memblock_unref(u->write_memchunk.memblock);
|
||||
pa_memchunk_reset(&u->write_memchunk);
|
||||
<<<<<<< HEAD
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
=======
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
>>>>>>> c1990dd02647405b0c13aab59f75d05cbb202336
|
||||
static void bt_prepare_decoder_buffer(struct userdata *u) {
|
||||
pa_assert(u);
|
||||
|
||||
|
|
@ -391,11 +464,19 @@ static void bt_prepare_decoder_buffer(struct userdata *u) {
|
|||
* decode method would produce larger output then read_block_size */
|
||||
u->decoder_buffer_size = u->read_link_mtu;
|
||||
}
|
||||
<<<<<<< HEAD
|
||||
|
||||
/* Run from IO thread */
|
||||
static ssize_t bt_transport_read(pa_bluetooth_transport *t, int fd, void *buffer, size_t size, pa_usec_t *p_timestamp) {
|
||||
ssize_t received = 0;
|
||||
|
||||
=======
|
||||
|
||||
/* Run from IO thread */
|
||||
static ssize_t bt_transport_read(pa_bluetooth_transport *t, int fd, void *buffer, size_t size, pa_usec_t *p_timestamp) {
|
||||
ssize_t received = 0;
|
||||
|
||||
>>>>>>> c1990dd02647405b0c13aab59f75d05cbb202336
|
||||
pa_assert(t);
|
||||
for (;;) {
|
||||
uint8_t aux[1024];
|
||||
|
|
@ -478,6 +559,7 @@ static int bt_process_push(struct userdata *u) {
|
|||
pa_assert(u->read_smoother);
|
||||
pa_assert(u->bt_codec);
|
||||
pa_assert(u->transport);
|
||||
<<<<<<< HEAD
|
||||
|
||||
bt_prepare_decoder_buffer(u);
|
||||
|
||||
|
|
@ -486,6 +568,52 @@ static int bt_process_push(struct userdata *u) {
|
|||
if (received <= 0) {
|
||||
return received;
|
||||
}
|
||||
=======
|
||||
|
||||
bt_prepare_decoder_buffer(u);
|
||||
|
||||
received = bt_transport_read(u->transport, u->stream_fd, u->decoder_buffer, u->decoder_buffer_size, &tstamp);
|
||||
|
||||
if (received <= 0) {
|
||||
return received;
|
||||
}
|
||||
|
||||
pa_memchunk memchunk;
|
||||
|
||||
memchunk.memblock = pa_memblock_new(u->core->mempool, u->read_block_size);
|
||||
memchunk.index = memchunk.length = 0;
|
||||
|
||||
ptr = pa_memblock_acquire(memchunk.memblock);
|
||||
memchunk.length = pa_memblock_get_length(memchunk.memblock);
|
||||
|
||||
memchunk.length = u->bt_codec->decode_buffer(u->decoder_info, u->decoder_buffer, received, ptr, memchunk.length, &processed);
|
||||
|
||||
pa_memblock_release(memchunk.memblock);
|
||||
|
||||
if (processed != (size_t) received) {
|
||||
pa_log_error("Decoding error");
|
||||
pa_memblock_unref(memchunk.memblock);
|
||||
return -1;
|
||||
}
|
||||
|
||||
u->read_index += (uint64_t) memchunk.length;
|
||||
#ifdef USE_SMOOTHER_2
|
||||
pa_smoother_2_resume(u->read_smoother, tstamp);
|
||||
pa_smoother_2_put(u->read_smoother, tstamp, u->read_index);
|
||||
#else
|
||||
pa_smoother_put(u->read_smoother, tstamp, pa_bytes_to_usec(u->read_index, &u->decoder_sample_spec));
|
||||
pa_smoother_resume(u->read_smoother, tstamp, true);
|
||||
#endif
|
||||
|
||||
/* Decoding of data may result in empty buffer, in this case
|
||||
* do not post empty audio samples. It may happen due to algorithmic
|
||||
* delay of audio codec. */
|
||||
if (PA_LIKELY(memchunk.length))
|
||||
pa_source_post(u->source, &memchunk);
|
||||
|
||||
/* report decoded size */
|
||||
received = memchunk.length;
|
||||
>>>>>>> c1990dd02647405b0c13aab59f75d05cbb202336
|
||||
|
||||
pa_memchunk memchunk;
|
||||
|
||||
|
|
@ -566,7 +694,11 @@ static void teardown_stream(struct userdata *u) {
|
|||
}
|
||||
|
||||
if (u->read_smoother) {
|
||||
#ifdef USE_SMOOTHER_2
|
||||
pa_smoother_2_free(u->read_smoother);
|
||||
#else
|
||||
pa_smoother_free(u->read_smoother);
|
||||
#endif
|
||||
u->read_smoother = NULL;
|
||||
}
|
||||
|
||||
|
|
@ -723,7 +855,11 @@ static int setup_stream(struct userdata *u) {
|
|||
u->stream_setup_done = true;
|
||||
|
||||
if (u->source)
|
||||
#ifdef USE_SMOOTHER_2
|
||||
u->read_smoother = pa_smoother_2_new(5*PA_USEC_PER_SEC, pa_rtclock_now(), pa_frame_size(&u->decoder_sample_spec), u->decoder_sample_spec.rate);
|
||||
#else
|
||||
u->read_smoother = pa_smoother_new(PA_USEC_PER_SEC, 2*PA_USEC_PER_SEC, true, true, 10, pa_rtclock_now(), true);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -789,13 +925,19 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
|
|||
switch (code) {
|
||||
|
||||
case PA_SOURCE_MESSAGE_GET_LATENCY: {
|
||||
#ifndef USE_SMOOTHER_2
|
||||
int64_t wi, ri;
|
||||
#endif
|
||||
|
||||
if (u->read_smoother) {
|
||||
#ifdef USE_SMOOTHER_2
|
||||
*((int64_t*) data) = u->source->thread_info.fixed_latency - pa_smoother_2_get_delay(u->read_smoother, pa_rtclock_now(), u->read_index);
|
||||
#else
|
||||
wi = pa_smoother_get(u->read_smoother, pa_rtclock_now());
|
||||
ri = pa_bytes_to_usec(u->read_index, &u->decoder_sample_spec);
|
||||
|
||||
*((int64_t*) data) = u->source->thread_info.fixed_latency + wi - ri;
|
||||
#endif
|
||||
} else
|
||||
*((int64_t*) data) = 0;
|
||||
|
||||
|
|
@ -840,8 +982,11 @@ static int source_set_state_in_io_thread_cb(pa_source *s, pa_source_state_t new_
|
|||
transport_release(u);
|
||||
|
||||
if (u->read_smoother)
|
||||
#ifdef USE_SMOOTHER_2
|
||||
pa_smoother_2_pause(u->read_smoother, pa_rtclock_now());
|
||||
#else
|
||||
pa_smoother_pause(u->read_smoother, pa_rtclock_now());
|
||||
|
||||
#endif
|
||||
break;
|
||||
|
||||
case PA_SOURCE_IDLE:
|
||||
|
|
@ -889,6 +1034,7 @@ static void source_set_volume_cb(pa_source *s) {
|
|||
|
||||
pa_cvolume_set(&s->real_volume, u->decoder_sample_spec.channels, volume);
|
||||
}
|
||||
<<<<<<< HEAD
|
||||
|
||||
/* Run from main thread */
|
||||
static void source_setup_volume_callback(pa_source *s) {
|
||||
|
|
@ -926,6 +1072,51 @@ static void source_setup_volume_callback(pa_source *s) {
|
|||
u->source_volume_changed_slot = pa_hook_connect(&s->core->hooks[PA_CORE_HOOK_SOURCE_VOLUME_CHANGED],
|
||||
PA_HOOK_NORMAL, sink_source_volume_changed_cb, u);
|
||||
|
||||
=======
|
||||
|
||||
/* Run from main thread */
|
||||
static void source_setup_volume_callback(pa_source *s) {
|
||||
struct userdata *u;
|
||||
|
||||
pa_assert(s);
|
||||
pa_assert(s->core);
|
||||
|
||||
u = s->userdata;
|
||||
pa_assert(u);
|
||||
pa_assert(u->source == s);
|
||||
pa_assert(u->transport);
|
||||
|
||||
if (pa_bluetooth_profile_is_a2dp(u->profile) && !u->transport->device->avrcp_absolute_volume)
|
||||
return;
|
||||
|
||||
/* Do not use hardware volume controls for backchannel of A2DP sink */
|
||||
if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK) {
|
||||
pa_assert_fp(u->transport->bt_codec && u->transport->bt_codec->support_backchannel);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Remote volume control has to be supported for the callback to make sense,
|
||||
* otherwise this source should continue performing attenuation in software
|
||||
* without HW_VOLUME_CTL.
|
||||
* If the peer is an AG however backend-native unconditionally provides this
|
||||
* function, PA in the role of HS/HF is responsible for signalling support
|
||||
* by emitting an initial volume command.
|
||||
* For A2DP bluez-util also unconditionally provides this function to keep
|
||||
* the peer informed about volume changes.
|
||||
*/
|
||||
if (!u->transport->set_source_volume)
|
||||
return;
|
||||
|
||||
if (pa_bluetooth_profile_should_attenuate_volume(u->profile)) {
|
||||
if (u->source_volume_changed_slot)
|
||||
return;
|
||||
|
||||
pa_log_debug("%s: Attaching volume hook to notify peer of changes", s->name);
|
||||
|
||||
u->source_volume_changed_slot = pa_hook_connect(&s->core->hooks[PA_CORE_HOOK_SOURCE_VOLUME_CHANGED],
|
||||
PA_HOOK_NORMAL, sink_source_volume_changed_cb, u);
|
||||
|
||||
>>>>>>> c1990dd02647405b0c13aab59f75d05cbb202336
|
||||
/* Send initial volume to peer, signalling support for volume control */
|
||||
u->transport->set_source_volume(u->transport, pa_cvolume_max(&s->real_volume));
|
||||
} else {
|
||||
|
|
@ -973,6 +1164,12 @@ static int add_source(struct userdata *u) {
|
|||
|
||||
if (!u->transport_acquired)
|
||||
switch (u->profile) {
|
||||
case PA_BLUETOOTH_PROFILE_A2DP_SINK:
|
||||
if (u->bt_codec && u->bt_codec->support_backchannel)
|
||||
data.suspend_cause = PA_SUSPEND_USER;
|
||||
else
|
||||
pa_assert_not_reached();
|
||||
break;
|
||||
case PA_BLUETOOTH_PROFILE_A2DP_SOURCE:
|
||||
case PA_BLUETOOTH_PROFILE_HFP_AG:
|
||||
case PA_BLUETOOTH_PROFILE_HSP_AG:
|
||||
|
|
@ -987,7 +1184,6 @@ static int add_source(struct userdata *u) {
|
|||
else
|
||||
pa_assert_not_reached();
|
||||
break;
|
||||
case PA_BLUETOOTH_PROFILE_A2DP_SINK:
|
||||
case PA_BLUETOOTH_PROFILE_OFF:
|
||||
pa_assert_not_reached();
|
||||
break;
|
||||
|
|
@ -1019,17 +1215,26 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
|
|||
switch (code) {
|
||||
|
||||
case PA_SINK_MESSAGE_GET_LATENCY: {
|
||||
int64_t wi = 0, ri = 0;
|
||||
int64_t wi, ri, delay = 0;
|
||||
|
||||
if (u->read_smoother) {
|
||||
#ifdef USE_SMOOTHER_2
|
||||
/* This is only used for SCO where encoder and decoder sample specs are
|
||||
* equal and output timing is based on the source. Therefore we can pass
|
||||
* the write index without conversion. */
|
||||
delay = pa_smoother_2_get_delay(u->read_smoother, pa_rtclock_now(), u->write_index + u->write_block_size);
|
||||
#else
|
||||
ri = pa_smoother_get(u->read_smoother, pa_rtclock_now());
|
||||
wi = pa_bytes_to_usec(u->write_index + u->write_block_size, &u->encoder_sample_spec);
|
||||
delay = wi - ri;
|
||||
#endif
|
||||
} else if (u->started_at) {
|
||||
ri = pa_rtclock_now() - u->started_at;
|
||||
wi = pa_bytes_to_usec(u->write_index, &u->encoder_sample_spec);
|
||||
delay = wi - ri;
|
||||
}
|
||||
|
||||
*((int64_t*) data) = u->sink->thread_info.fixed_latency + wi - ri;
|
||||
*((int64_t*) data) = u->sink->thread_info.fixed_latency + delay;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1099,6 +1304,7 @@ static int sink_set_state_in_io_thread_cb(pa_sink *s, pa_sink_state_t new_state,
|
|||
/* Run from main thread */
|
||||
static void sink_set_volume_cb(pa_sink *s) {
|
||||
pa_volume_t volume;
|
||||
<<<<<<< HEAD
|
||||
struct userdata *u;
|
||||
|
||||
pa_assert(s);
|
||||
|
|
@ -1120,6 +1326,8 @@ static void sink_set_volume_cb(pa_sink *s) {
|
|||
|
||||
/* Run from main thread */
|
||||
static void sink_setup_volume_callback(pa_sink *s) {
|
||||
=======
|
||||
>>>>>>> c1990dd02647405b0c13aab59f75d05cbb202336
|
||||
struct userdata *u;
|
||||
|
||||
pa_assert(s);
|
||||
|
|
@ -1128,6 +1336,7 @@ static void sink_setup_volume_callback(pa_sink *s) {
|
|||
u = s->userdata;
|
||||
pa_assert(u);
|
||||
pa_assert(u->sink == s);
|
||||
<<<<<<< HEAD
|
||||
pa_assert(u->transport);
|
||||
|
||||
if (pa_bluetooth_profile_is_a2dp(u->profile) && !u->transport->device->avrcp_absolute_volume)
|
||||
|
|
@ -1170,6 +1379,76 @@ static void sink_setup_volume_callback(pa_sink *s) {
|
|||
/* Reset local attenuation */
|
||||
pa_sink_set_soft_volume(s, NULL);
|
||||
|
||||
=======
|
||||
pa_assert(!pa_bluetooth_profile_should_attenuate_volume(u->profile));
|
||||
pa_assert(u->transport);
|
||||
pa_assert(u->transport->set_sink_volume);
|
||||
|
||||
/* In the AG role, send a command to change speaker gain on the HS/HF */
|
||||
volume = u->transport->set_sink_volume(u->transport, pa_cvolume_max(&s->real_volume));
|
||||
|
||||
pa_cvolume_set(&s->real_volume, u->encoder_sample_spec.channels, volume);
|
||||
}
|
||||
|
||||
/* Run from main thread */
|
||||
static void sink_setup_volume_callback(pa_sink *s) {
|
||||
struct userdata *u;
|
||||
|
||||
pa_assert(s);
|
||||
pa_assert(s->core);
|
||||
|
||||
u = s->userdata;
|
||||
pa_assert(u);
|
||||
pa_assert(u->sink == s);
|
||||
pa_assert(u->transport);
|
||||
|
||||
if (pa_bluetooth_profile_is_a2dp(u->profile) && !u->transport->device->avrcp_absolute_volume)
|
||||
return;
|
||||
|
||||
/* Do not use hardware volume controls for backchannel of A2DP source */
|
||||
if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE) {
|
||||
pa_assert_fp(u->transport->bt_codec && u->transport->bt_codec->support_backchannel);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Remote volume control has to be supported for the callback to make sense,
|
||||
* otherwise this sink should continue performing attenuation in software
|
||||
* without HW_VOLUME_CTL.
|
||||
* If the peer is an AG however backend-native unconditionally provides this
|
||||
* function, PA in the role of HS/HF is responsible for signalling support
|
||||
* by emitting an initial volume command.
|
||||
*/
|
||||
if (!u->transport->set_sink_volume)
|
||||
return;
|
||||
|
||||
if (pa_bluetooth_profile_should_attenuate_volume(u->profile)) {
|
||||
/* It is yet unknown how (if at all) volume is synchronized for bidirectional
|
||||
* A2DP codecs. Disallow attaching hooks to a pa_sink if the peer is in
|
||||
* A2DP_SOURCE role. This assert should be replaced with the proper logic
|
||||
* when bidirectional codecs are implemented.
|
||||
*/
|
||||
pa_assert(u->profile != PA_BLUETOOTH_PROFILE_A2DP_SOURCE);
|
||||
|
||||
if (u->sink_volume_changed_slot)
|
||||
return;
|
||||
|
||||
pa_log_debug("%s: Attaching volume hook to notify peer of changes", s->name);
|
||||
|
||||
u->sink_volume_changed_slot = pa_hook_connect(&s->core->hooks[PA_CORE_HOOK_SINK_VOLUME_CHANGED],
|
||||
PA_HOOK_NORMAL, sink_source_volume_changed_cb, u);
|
||||
|
||||
/* Send initial volume to peer, signalling support for volume control */
|
||||
u->transport->set_sink_volume(u->transport, pa_cvolume_max(&s->real_volume));
|
||||
} else {
|
||||
if (s->set_volume == sink_set_volume_cb)
|
||||
return;
|
||||
|
||||
pa_log_debug("%s: Resetting software volume for hardware attenuation by peer", s->name);
|
||||
|
||||
/* Reset local attenuation */
|
||||
pa_sink_set_soft_volume(s, NULL);
|
||||
|
||||
>>>>>>> c1990dd02647405b0c13aab59f75d05cbb202336
|
||||
pa_sink_set_set_volume_callback(s, sink_set_volume_cb);
|
||||
|
||||
if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK)
|
||||
|
|
@ -1254,6 +1533,7 @@ static pa_direction_t get_profile_direction(pa_bluetooth_profile_t p) {
|
|||
|
||||
return profile_direction[p];
|
||||
}
|
||||
<<<<<<< HEAD
|
||||
|
||||
/* Run from main thread */
|
||||
static int transport_config(struct userdata *u) {
|
||||
|
|
@ -1271,13 +1551,41 @@ static int transport_config(struct userdata *u) {
|
|||
|
||||
if (get_profile_direction(u->profile) & PA_DIRECTION_OUTPUT) {
|
||||
u->encoder_info = u->bt_codec->init(true, false, u->transport->config, u->transport->config_size, &u->encoder_sample_spec, u->core);
|
||||
=======
|
||||
|
||||
/* Run from main thread */
|
||||
static int transport_config(struct userdata *u) {
|
||||
bool reverse_backchannel;
|
||||
pa_assert(u);
|
||||
pa_assert(u->transport);
|
||||
pa_assert(!u->bt_codec);
|
||||
pa_assert(!u->encoder_info);
|
||||
pa_assert(!u->decoder_info);
|
||||
|
||||
u->bt_codec = u->transport->bt_codec;
|
||||
pa_assert(u->bt_codec);
|
||||
|
||||
/* reset encoder buffer contents */
|
||||
u->encoder_buffer_used = 0;
|
||||
|
||||
/* forward encoding direction */
|
||||
reverse_backchannel = u->bt_codec->support_backchannel && !(get_profile_direction(u->profile) & PA_DIRECTION_OUTPUT);
|
||||
|
||||
if ((get_profile_direction(u->profile) & PA_DIRECTION_OUTPUT) || u->bt_codec->support_backchannel) {
|
||||
u->encoder_info = u->bt_codec->init(true, reverse_backchannel, u->transport->config, u->transport->config_size, &u->encoder_sample_spec, u->core);
|
||||
>>>>>>> c1990dd02647405b0c13aab59f75d05cbb202336
|
||||
|
||||
if (!u->encoder_info)
|
||||
return -1;
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
if (get_profile_direction(u->profile) & PA_DIRECTION_INPUT) {
|
||||
u->decoder_info = u->bt_codec->init(false, false, u->transport->config, u->transport->config_size, &u->decoder_sample_spec, u->core);
|
||||
=======
|
||||
if ((get_profile_direction(u->profile) & PA_DIRECTION_INPUT) || u->bt_codec->support_backchannel) {
|
||||
u->decoder_info = u->bt_codec->init(false, reverse_backchannel, u->transport->config, u->transport->config_size, &u->decoder_sample_spec, u->core);
|
||||
>>>>>>> c1990dd02647405b0c13aab59f75d05cbb202336
|
||||
|
||||
if (!u->decoder_info) {
|
||||
if (u->encoder_info) {
|
||||
|
|
@ -1335,11 +1643,11 @@ static int init_profile(struct userdata *u) {
|
|||
|
||||
pa_assert(u->transport);
|
||||
|
||||
if (get_profile_direction (u->profile) & PA_DIRECTION_OUTPUT)
|
||||
if ((get_profile_direction(u->profile) & PA_DIRECTION_OUTPUT) || u->bt_codec->support_backchannel)
|
||||
if (add_sink(u) < 0)
|
||||
r = -1;
|
||||
|
||||
if (get_profile_direction (u->profile) & PA_DIRECTION_INPUT)
|
||||
if ((get_profile_direction(u->profile) & PA_DIRECTION_INPUT) || u->bt_codec->support_backchannel)
|
||||
if (add_source(u) < 0)
|
||||
r = -1;
|
||||
|
||||
|
|
@ -1458,8 +1766,8 @@ static void thread_func(void *userdata) {
|
|||
writable = true;
|
||||
|
||||
/* If we have a source, we let the source determine the timing
|
||||
* for the sink */
|
||||
if (have_source) {
|
||||
* for the sink unless peer has not sent any data yet */
|
||||
if (have_source && u->read_index > 0) {
|
||||
|
||||
/* If the stream is writable, send some data if necessary */
|
||||
if (writable) {
|
||||
|
|
@ -1540,11 +1848,22 @@ static void thread_func(void *userdata) {
|
|||
skip_bytes -= bytes_to_render;
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
if (u->write_index > 0 && (get_profile_direction(u->profile) & PA_DIRECTION_OUTPUT)) {
|
||||
size_t new_write_block_size = u->bt_codec->reduce_encoder_bitrate(u->encoder_info, u->write_link_mtu);
|
||||
if (new_write_block_size) {
|
||||
u->write_block_size = new_write_block_size;
|
||||
handle_sink_block_size_change(u);
|
||||
=======
|
||||
if (u->write_index > 0 && (get_profile_direction(u->profile) & PA_DIRECTION_OUTPUT || u->bt_codec->support_backchannel)) {
|
||||
if (u->bt_codec->reduce_encoder_bitrate) {
|
||||
size_t new_write_block_size = u->bt_codec->reduce_encoder_bitrate(u->encoder_info, u->write_link_mtu);
|
||||
if (new_write_block_size) {
|
||||
u->write_block_size = new_write_block_size;
|
||||
handle_sink_block_size_change(u);
|
||||
}
|
||||
pa_gettimeofday(&tv_last_output_rate_change);
|
||||
>>>>>>> c1990dd02647405b0c13aab59f75d05cbb202336
|
||||
}
|
||||
pa_gettimeofday(&tv_last_output_rate_change);
|
||||
}
|
||||
|
|
@ -1570,6 +1889,15 @@ static void thread_func(void *userdata) {
|
|||
goto fail;
|
||||
|
||||
if (result) {
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
if (have_source && u->read_index <= 0) {
|
||||
/* We have a source but peer has not sent any data yet, log this */
|
||||
if (pa_log_ratelimit(PA_LOG_DEBUG))
|
||||
pa_log_debug("Still no data received from source, sent one more block to sink");
|
||||
}
|
||||
|
||||
>>>>>>> c1990dd02647405b0c13aab59f75d05cbb202336
|
||||
writable = false;
|
||||
have_written = true;
|
||||
}
|
||||
|
|
@ -1589,7 +1917,11 @@ static void thread_func(void *userdata) {
|
|||
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); */
|
||||
|
||||
<<<<<<< HEAD
|
||||
if ((get_profile_direction(u->profile) & PA_DIRECTION_OUTPUT) && u->write_memchunk.memblock == NULL) {
|
||||
=======
|
||||
if ((get_profile_direction(u->profile) & PA_DIRECTION_OUTPUT || u->bt_codec->support_backchannel) && u->write_memchunk.memblock == NULL) {
|
||||
>>>>>>> c1990dd02647405b0c13aab59f75d05cbb202336
|
||||
/* bt_write_buffer() is keeping up with input, try increasing bitrate */
|
||||
if (u->bt_codec->increase_encoder_bitrate
|
||||
&& pa_timeval_age(&tv_last_output_rate_change) >= u->device->output_rate_refresh_interval_ms * PA_USEC_PER_MSEC) {
|
||||
|
|
@ -1772,7 +2104,11 @@ static void stop_thread(struct userdata *u) {
|
|||
}
|
||||
|
||||
if (u->read_smoother) {
|
||||
#ifdef USE_SMOOTHER_2
|
||||
pa_smoother_2_free(u->read_smoother);
|
||||
#else
|
||||
pa_smoother_free(u->read_smoother);
|
||||
#endif
|
||||
u->read_smoother = NULL;
|
||||
}
|
||||
|
||||
|
|
@ -1817,10 +2153,10 @@ static pa_available_t get_port_availability(struct userdata *u, pa_direction_t d
|
|||
for (i = 0; i < PA_BLUETOOTH_PROFILE_COUNT; i++) {
|
||||
pa_bluetooth_transport *transport;
|
||||
|
||||
if (!(get_profile_direction(i) & direction))
|
||||
if (!(transport = u->device->transports[i]))
|
||||
continue;
|
||||
|
||||
if (!(transport = u->device->transports[i]))
|
||||
if (!(get_profile_direction(i) & direction || (transport->bt_codec && transport->bt_codec->support_backchannel)))
|
||||
continue;
|
||||
|
||||
switch(transport->state) {
|
||||
|
|
@ -2172,8 +2508,18 @@ static int add_card(struct userdata *u) {
|
|||
if (uuid_to_profile(uuid, &profile) < 0)
|
||||
continue;
|
||||
|
||||
if (pa_hashmap_get(data.profiles, pa_bluetooth_profile_to_string(profile)))
|
||||
pa_log_debug("Trying to create profile %s (%s) for device %s (%s)",
|
||||
pa_bluetooth_profile_to_string(profile), uuid, d->alias, d->address);
|
||||
|
||||
if (pa_hashmap_get(data.profiles, pa_bluetooth_profile_to_string(profile))) {
|
||||
pa_log_debug("%s already exists", pa_bluetooth_profile_to_string(profile));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!pa_bluetooth_device_supports_profile(d, profile)) {
|
||||
pa_log_debug("%s is not supported by the device or adapter", pa_bluetooth_profile_to_string(profile));
|
||||
continue;
|
||||
}
|
||||
|
||||
cp = create_card_profile(u, profile, data.ports);
|
||||
pa_hashmap_put(data.profiles, cp->name, cp);
|
||||
|
|
@ -2490,10 +2836,17 @@ static int bluez5_device_message_handler(const char *object_path, const char *me
|
|||
pa_bluetooth_profile_t profile;
|
||||
const pa_a2dp_endpoint_conf *endpoint_conf;
|
||||
const char *codec_name;
|
||||
<<<<<<< HEAD
|
||||
struct userdata *u;
|
||||
bool is_a2dp_sink;
|
||||
|
||||
pa_assert(u = (struct userdata *)userdata);
|
||||
=======
|
||||
struct userdata *u = userdata;
|
||||
bool is_a2dp_sink;
|
||||
|
||||
pa_assert(u);
|
||||
>>>>>>> c1990dd02647405b0c13aab59f75d05cbb202336
|
||||
pa_assert(message);
|
||||
pa_assert(response);
|
||||
|
||||
|
|
@ -2650,6 +3003,30 @@ static int device_process_msg(pa_msgobject *obj, int code, void *data, int64_t o
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* Run from main thread */
|
||||
static pa_hook_result_t a2dp_source_output_fixate_hook_callback(pa_core *c, pa_source_output_new_data *new_data, struct userdata *u) {
|
||||
double volume_factor_dB;
|
||||
pa_cvolume cv;
|
||||
|
||||
pa_assert(c);
|
||||
pa_assert(new_data);
|
||||
pa_assert(u);
|
||||
|
||||
/* When transport is released, there is no decoder and no codec */
|
||||
if (!u->bt_codec || !u->decoder_info)
|
||||
return PA_HOOK_OK;
|
||||
|
||||
if (!u->bt_codec->get_source_output_volume_factor_dB)
|
||||
return PA_HOOK_OK;
|
||||
|
||||
volume_factor_dB = u->bt_codec->get_source_output_volume_factor_dB(u->decoder_info);
|
||||
|
||||
pa_cvolume_set(&cv, u->decoder_sample_spec.channels, pa_sw_volume_from_dB(volume_factor_dB));
|
||||
pa_source_output_new_data_apply_volume_factor_source(new_data, &cv);
|
||||
|
||||
return PA_HOOK_OK;
|
||||
}
|
||||
|
||||
int pa__init(pa_module* m) {
|
||||
struct userdata *u;
|
||||
const char *path;
|
||||
|
|
@ -2730,6 +3107,11 @@ int pa__init(pa_module* m) {
|
|||
|
||||
u->transport_source_volume_changed_slot =
|
||||
pa_hook_connect(pa_bluetooth_discovery_hook(u->discovery, PA_BLUETOOTH_HOOK_TRANSPORT_SOURCE_VOLUME_CHANGED), PA_HOOK_NORMAL, (pa_hook_cb_t) transport_source_volume_changed_cb, u);
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
|
||||
u->source_output_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_NEW], PA_HOOK_EARLY, (pa_hook_cb_t) a2dp_source_output_fixate_hook_callback, u);
|
||||
>>>>>>> c1990dd02647405b0c13aab59f75d05cbb202336
|
||||
|
||||
if (add_card(u) < 0)
|
||||
goto fail;
|
||||
|
|
@ -2794,6 +3176,9 @@ void pa__done(pa_module *m) {
|
|||
|
||||
stop_thread(u);
|
||||
|
||||
if (u->source_output_new_hook_slot)
|
||||
pa_hook_slot_free(u->source_output_new_hook_slot);
|
||||
|
||||
if (u->device_connection_changed_slot)
|
||||
pa_hook_slot_free(u->device_connection_changed_slot);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue