diff --git a/src/modules/bluetooth/a2dp-codec-api.h b/src/modules/bluetooth/a2dp-codec-api.h index 744e5664a..1d7212f36 100644 --- a/src/modules/bluetooth/a2dp-codec-api.h +++ b/src/modules/bluetooth/a2dp-codec-api.h @@ -88,6 +88,11 @@ typedef struct pa_a2dp_codec { * enough */ size_t (*reduce_encoder_bitrate)(void *codec_info, size_t write_link_mtu); + /* Increase encoder bitrate for codec, returns new write block size or zero + * if not changed, called periodically when socket is keeping up with + * encoded data */ + size_t (*increase_encoder_bitrate)(void *codec_info, size_t write_link_mtu); + /* Encode input_buffer of input_size to output_buffer of output_size, * returns size of filled ouput_buffer and set processed to size of * processed input_buffer */ diff --git a/src/modules/bluetooth/bluez5-util.h b/src/modules/bluetooth/bluez5-util.h index e994a8d9a..64f37798f 100644 --- a/src/modules/bluetooth/bluez5-util.h +++ b/src/modules/bluetooth/bluez5-util.h @@ -110,6 +110,7 @@ struct pa_bluetooth_device { bool valid; bool autodetect_mtu; bool codec_switching_in_progress; + uint32_t output_rate_refresh_interval_ms; /* Device information */ char *path; diff --git a/src/modules/bluetooth/module-bluez5-device.c b/src/modules/bluetooth/module-bluez5-device.c index 54471324a..94726e8ed 100644 --- a/src/modules/bluetooth/module-bluez5-device.c +++ b/src/modules/bluetooth/module-bluez5-device.c @@ -66,6 +66,8 @@ PA_MODULE_USAGE("path=" #define HSP_MAX_GAIN 15 +#define DEFAULT_OUTPUT_RATE_REFRESH_INTERVAL_MS 500 + static const char* const valid_modargs[] = { "path", "autodetect_mtu", @@ -1362,6 +1364,8 @@ static void thread_func(void *userdata) { struct userdata *u = userdata; unsigned blocks_to_write = 0; unsigned bytes_to_write = 0; + struct timeval tv_last_output_rate_change; + pa_usec_t ts_elapsed; pa_assert(u); pa_assert(u->transport); @@ -1377,6 +1381,8 @@ static void thread_func(void *userdata) { if (u->transport_acquired) setup_stream(u); + pa_gettimeofday(&tv_last_output_rate_change); + for (;;) { struct pollfd *pollfd; int ret; @@ -1525,6 +1531,7 @@ static void thread_func(void *userdata) { u->write_block_size = new_write_block_size; handle_sink_block_size_change(u); } + pa_gettimeofday(&tv_last_output_rate_change); } } @@ -1557,6 +1564,19 @@ static void thread_func(void *userdata) { next_write_at = pa_bytes_to_usec(u->write_index, &u->encoder_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); */ + + if (u->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK && u->write_memchunk.memblock == NULL) { + /* write_block() is keeping up with input, try increasing bitrate */ + if (u->a2dp_codec->increase_encoder_bitrate + && pa_timeval_age(&tv_last_output_rate_change) >= u->device->output_rate_refresh_interval_ms * PA_USEC_PER_MSEC) { + size_t new_write_block_size = u->a2dp_codec->increase_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); + } + } } else /* We could not write because the stream was not ready. Let's try * again in 500 ms and drop audio if we still can't write. The @@ -2488,6 +2508,7 @@ int pa__init(pa_module* m) { pa_modargs *ma; bool autodetect_mtu; char *message_handler_path; + uint32_t output_rate_refresh_interval_ms = DEFAULT_OUTPUT_RATE_REFRESH_INTERVAL_MS; pa_assert(m); @@ -2526,6 +2547,13 @@ int pa__init(pa_module* m) { u->device->autodetect_mtu = autodetect_mtu; + if (pa_modargs_get_value_u32(ma, "output-rate-refresh-interval-ms", &output_rate_refresh_interval_ms) < 0) { + pa_log("Invalid output_rate_refresh_interval."); + goto fail_free_modargs; + } + + u->device->output_rate_refresh_interval_ms = output_rate_refresh_interval_ms; + pa_modargs_free(ma); u->device_connection_changed_slot =