mirror of
https://gitlab.freedesktop.org/pipewire/pipewire
synced 2024-10-14 20:02:38 +00:00
bluez5: emit & remove the A2DP source node depending on transport state
Typically a source stops the connection when it has nothing to play and this causes the transport to become "idle" and our A2DP source also stops. However, the node is still present and "running" (if linked), which causes the graph to underrun as it receives no data from this node. This patch dynamically creates and destroys the a2dp source node depending on the transport state. So, when the transport is idle, there is no node in the graph at all.
This commit is contained in:
parent
c81d44e8a9
commit
a75fe69c8e
|
@ -107,7 +107,6 @@ struct impl {
|
|||
struct props props;
|
||||
|
||||
struct spa_bt_transport *transport;
|
||||
struct spa_hook transport_listener;
|
||||
|
||||
struct port port;
|
||||
|
||||
|
@ -1165,45 +1164,6 @@ static const struct spa_node_methods impl_node = {
|
|||
.process = impl_node_process,
|
||||
};
|
||||
|
||||
static int do_transport_destroy(struct spa_loop *loop,
|
||||
bool async,
|
||||
uint32_t seq,
|
||||
const void *data,
|
||||
size_t size,
|
||||
void *user_data)
|
||||
{
|
||||
struct impl *this = user_data;
|
||||
this->transport = NULL;
|
||||
this->transport_acquired = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void transport_destroy(void *data)
|
||||
{
|
||||
struct impl *this = data;
|
||||
spa_log_debug(this->log, "transport %p destroy", this->transport);
|
||||
spa_loop_invoke(this->data_loop, do_transport_destroy, 0, NULL, 0, true, this);
|
||||
}
|
||||
|
||||
static void transport_state_changed(void *data, enum spa_bt_transport_state old,
|
||||
enum spa_bt_transport_state state)
|
||||
{
|
||||
struct impl *this = data;
|
||||
spa_log_debug(this->log, "transport %p state %d->%d started:%d",
|
||||
this->transport, old, state, this->started);
|
||||
|
||||
if (state >= SPA_BT_TRANSPORT_STATE_PENDING && old < SPA_BT_TRANSPORT_STATE_PENDING)
|
||||
transport_start(this);
|
||||
else if (state < SPA_BT_TRANSPORT_STATE_PENDING && old >= SPA_BT_TRANSPORT_STATE_PENDING)
|
||||
transport_stop(this);
|
||||
}
|
||||
|
||||
static const struct spa_bt_transport_events transport_events = {
|
||||
SPA_VERSION_BT_TRANSPORT_EVENTS,
|
||||
.destroy = transport_destroy,
|
||||
.state_changed = transport_state_changed,
|
||||
};
|
||||
|
||||
static int impl_get_interface(struct spa_handle *handle, const char *type, void **interface)
|
||||
{
|
||||
struct impl *this;
|
||||
|
@ -1226,8 +1186,6 @@ static int impl_clear(struct spa_handle *handle)
|
|||
struct impl *this = (struct impl *) handle;
|
||||
if (this->codec_data)
|
||||
this->codec->deinit(this->codec_data);
|
||||
if (this->transport)
|
||||
spa_hook_remove(&this->transport_listener);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1325,9 +1283,6 @@ impl_init(const struct spa_handle_factory *factory,
|
|||
}
|
||||
this->codec = this->transport->a2dp_codec;
|
||||
|
||||
spa_bt_transport_add_listener(this->transport,
|
||||
&this->transport_listener, &transport_events, this);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -77,6 +77,16 @@ struct node {
|
|||
float volumes[SPA_AUDIO_MAX_CHANNELS];
|
||||
};
|
||||
|
||||
struct impl;
|
||||
struct dynamic_node
|
||||
{
|
||||
struct impl *impl;
|
||||
struct spa_bt_transport *transport;
|
||||
struct spa_hook transport_listener;
|
||||
uint32_t id;
|
||||
const char *factory_name;
|
||||
};
|
||||
|
||||
struct impl {
|
||||
struct spa_handle handle;
|
||||
struct spa_device device;
|
||||
|
@ -106,6 +116,8 @@ struct impl {
|
|||
const struct a2dp_codec **supported_codecs;
|
||||
size_t supported_codec_count;
|
||||
|
||||
struct dynamic_node dyn_a2dp_source;
|
||||
|
||||
#define MAX_SETTINGS 32
|
||||
struct spa_dict_item setting_items[MAX_SETTINGS];
|
||||
struct spa_dict setting_dict;
|
||||
|
@ -206,6 +218,74 @@ static struct spa_bt_transport *find_transport(struct impl *this, int profile, c
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static void dynamic_node_transport_destroy(void *data)
|
||||
{
|
||||
struct dynamic_node *this = data;
|
||||
spa_log_debug(this->impl->log, "transport %p destroy", this->transport);
|
||||
}
|
||||
|
||||
static void dynamic_node_transport_state_changed(void *data,
|
||||
enum spa_bt_transport_state old,
|
||||
enum spa_bt_transport_state state)
|
||||
{
|
||||
struct dynamic_node *this = data;
|
||||
struct impl *impl = this->impl;
|
||||
struct spa_bt_transport *t = this->transport;
|
||||
|
||||
spa_log_debug(impl->log, "transport %p state %d->%d", t, old, state);
|
||||
|
||||
if (state >= SPA_BT_TRANSPORT_STATE_PENDING && old < SPA_BT_TRANSPORT_STATE_PENDING) {
|
||||
if (!impl->nodes[this->id].active) {
|
||||
emit_node(impl, t, this->id, this->factory_name);
|
||||
}
|
||||
} else if (state < SPA_BT_TRANSPORT_STATE_PENDING && old >= SPA_BT_TRANSPORT_STATE_PENDING) {
|
||||
if (impl->nodes[this->id].active) {
|
||||
spa_device_emit_object_info(&impl->hooks, this->id, NULL);
|
||||
impl->nodes[this->id].active = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static const struct spa_bt_transport_events dynamic_node_transport_events = {
|
||||
SPA_VERSION_BT_TRANSPORT_EVENTS,
|
||||
.destroy = dynamic_node_transport_destroy,
|
||||
.state_changed = dynamic_node_transport_state_changed,
|
||||
};
|
||||
|
||||
static void emit_dynamic_node(struct dynamic_node *this, struct impl *impl,
|
||||
struct spa_bt_transport *t, uint32_t id, const char *factory_name)
|
||||
{
|
||||
if (this->transport != NULL)
|
||||
return;
|
||||
|
||||
this->impl = impl;
|
||||
this->transport = t;
|
||||
this->id = id;
|
||||
this->factory_name = factory_name;
|
||||
|
||||
spa_bt_transport_add_listener(this->transport,
|
||||
&this->transport_listener, &dynamic_node_transport_events, this);
|
||||
|
||||
/* emits the node if the state is already pending */
|
||||
dynamic_node_transport_state_changed (this, SPA_BT_TRANSPORT_STATE_IDLE, t->state);
|
||||
}
|
||||
|
||||
static void remove_dynamic_node(struct dynamic_node *this)
|
||||
{
|
||||
if (this->transport == NULL)
|
||||
return;
|
||||
|
||||
/* destroy the node, if it exists */
|
||||
dynamic_node_transport_state_changed (this, this->transport->state,
|
||||
SPA_BT_TRANSPORT_STATE_IDLE);
|
||||
|
||||
spa_hook_remove(&this->transport_listener);
|
||||
this->impl = NULL;
|
||||
this->transport = NULL;
|
||||
this->id = 0;
|
||||
this->factory_name = NULL;
|
||||
}
|
||||
|
||||
static int emit_nodes(struct impl *this)
|
||||
{
|
||||
struct spa_bt_transport *t;
|
||||
|
@ -217,7 +297,8 @@ static int emit_nodes(struct impl *this)
|
|||
if (this->bt_dev->connected_profiles & SPA_BT_PROFILE_A2DP_SOURCE) {
|
||||
t = find_transport(this, SPA_BT_PROFILE_A2DP_SOURCE, this->selected_a2dp_codec);
|
||||
if (t)
|
||||
emit_node(this, t, DEVICE_ID_SOURCE, SPA_NAME_API_BLUEZ5_A2DP_SOURCE);
|
||||
emit_dynamic_node(&this->dyn_a2dp_source, this, t,
|
||||
DEVICE_ID_SOURCE, SPA_NAME_API_BLUEZ5_A2DP_SOURCE);
|
||||
}
|
||||
|
||||
if (this->bt_dev->connected_profiles & SPA_BT_PROFILE_A2DP_SINK) {
|
||||
|
@ -270,6 +351,8 @@ static void emit_remove_nodes(struct impl *this)
|
|||
{
|
||||
uint32_t i;
|
||||
|
||||
remove_dynamic_node (&this->dyn_a2dp_source);
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
if (this->nodes[i].active) {
|
||||
spa_device_emit_object_info(&this->hooks, i, NULL);
|
||||
|
|
Loading…
Reference in a new issue