diff --git a/src/pipewire/link.c b/src/pipewire/link.c index 679cb1b04..d51ff8e87 100644 --- a/src/pipewire/link.c +++ b/src/pipewire/link.c @@ -152,12 +152,12 @@ static void complete_ready(void *obj, void *data, int res, uint32_t id) struct pw_port *port = mix->p; if (SPA_RESULT_IS_OK(res)) { - port->state = PW_PORT_STATE_READY; - mix->state = PW_PORT_STATE_READY; + pw_port_mix_update_state(port, mix, PW_PORT_STATE_READY); + pw_port_update_state(port, PW_PORT_STATE_READY); pw_log_debug("port %p: state READY", port); } else { - port->state = PW_PORT_STATE_ERROR; - mix->state = PW_PORT_STATE_ERROR; + pw_port_mix_update_state(port, mix, PW_PORT_STATE_ERROR); + pw_port_update_state(port, PW_PORT_STATE_ERROR); pw_log_warn("port %p: failed to go to READY", port); } } @@ -168,12 +168,12 @@ static void complete_paused(void *obj, void *data, int res, uint32_t id) struct pw_port *port = mix->p; if (SPA_RESULT_IS_OK(res)) { - port->state = PW_PORT_STATE_PAUSED; - mix->state = PW_PORT_STATE_PAUSED; + pw_port_mix_update_state(port, mix, PW_PORT_STATE_PAUSED); + pw_port_update_state(port, PW_PORT_STATE_PAUSED); pw_log_debug("port %p: state PAUSED", port); } else { - port->state = PW_PORT_STATE_ERROR; - mix->state = PW_PORT_STATE_ERROR; + pw_port_mix_update_state(port, mix, PW_PORT_STATE_ERROR); + pw_port_update_state(port, PW_PORT_STATE_ERROR); pw_log_warn("port %p: failed to go to PAUSED", port); } } @@ -937,8 +937,8 @@ static int check_states(struct pw_link *this, void *user_data, int res) } if (PW_PORT_IS_CONTROL(output) && PW_PORT_IS_CONTROL(input)) { - this->rt.in_mix.state = PW_PORT_STATE_PAUSED; - this->rt.out_mix.state = PW_PORT_STATE_PAUSED; + pw_port_mix_update_state(input, &this->rt.in_mix, PW_PORT_STATE_PAUSED); + pw_port_mix_update_state(output, &this->rt.out_mix, PW_PORT_STATE_PAUSED); } in_mix_state = this->rt.in_mix.state; @@ -1152,7 +1152,7 @@ int pw_link_deactivate(struct pw_link *this) pw_log_debug("port %p: input state %d -> %d", this->input, this->input->state, PW_PORT_STATE_PAUSED); } - this->rt.in_mix.state = PW_PORT_STATE_CONFIGURE; + pw_port_mix_update_state(this->input, &this->rt.in_mix, PW_PORT_STATE_CONFIGURE); if (output_node->n_used_input_links <= output_node->idle_used_input_links && output_node->n_used_output_links <= output_node->idle_used_output_links && @@ -1161,7 +1161,7 @@ int pw_link_deactivate(struct pw_link *this) pw_log_debug("port %p: output state %d -> %d", this->output, this->output->state, PW_PORT_STATE_PAUSED); } - this->rt.out_mix.state = PW_PORT_STATE_CONFIGURE; + pw_port_mix_update_state(this->output, &this->rt.out_mix, PW_PORT_STATE_CONFIGURE); pw_link_update_state(this, PW_LINK_STATE_INIT, NULL); diff --git a/src/pipewire/port.c b/src/pipewire/port.c index 797b3b6dd..f5056c0c7 100644 --- a/src/pipewire/port.c +++ b/src/pipewire/port.c @@ -46,11 +46,37 @@ struct resource_data { /** \endcond */ - -static void port_update_state(struct pw_port *port, struct pw_port_mix *mix, enum pw_port_state state) +static inline void adjust_mix_state(struct pw_port *port, enum pw_port_state state, int dir) { - if (mix) + switch (state) { + case PW_PORT_STATE_CONFIGURE: + port->n_mix_configure += dir; + break; + case PW_PORT_STATE_READY: + port->n_mix_ready += dir; + break; + case PW_PORT_STATE_PAUSED: + port->n_mix_paused += dir; + break; + default: + break; + } +} + +void pw_port_mix_update_state(struct pw_port *port, struct pw_port_mix *mix, enum pw_port_state state) +{ + if (mix && mix->state != state) { + adjust_mix_state(port, mix->state, -1); mix->state = state; + adjust_mix_state(port, state, 1); + pw_log_debug("port %p: mix %d c:%d r:%d p:%d", port, mix->id, + port->n_mix_configure, port->n_mix_ready, + port->n_mix_paused); + } +} + +void pw_port_update_state(struct pw_port *port, enum pw_port_state state) +{ if (port->state != state) { pw_log(state == PW_PORT_STATE_ERROR ? SPA_LOG_LEVEL_ERROR : SPA_LOG_LEVEL_DEBUG, @@ -156,8 +182,11 @@ int pw_port_init_mix(struct pw_port *port, struct pw_port_mix *mix) spa_graph_port_init(&mix->port, port->direction, port_id, 0); + mix->p = port; mix->state = PW_PORT_STATE_CONFIGURE; + port->n_mix++; + adjust_mix_state(port, mix->state, 1); if (port->mix->add_port) port->mix->add_port(port->mix, port->direction, port_id); @@ -177,6 +206,8 @@ int pw_port_release_mix(struct pw_port *port, struct pw_port_mix *mix) const struct pw_port_implementation *pi = port->implementation; pw_map_remove(&port->mix_port_map, port_id); + adjust_mix_state(port, mix->state, -1); + port->n_mix--; if (pi && pi->release_mix) res = pi->release_mix(port->implementation_data, mix); @@ -592,7 +623,7 @@ int pw_port_add(struct pw_port *port, struct pw_node *node) pw_loop_invoke(node->data_loop, do_add_port, SPA_ID_INVALID, NULL, 0, false, port); if (port->state <= PW_PORT_STATE_INIT) - port_update_state(port, NULL, PW_PORT_STATE_CONFIGURE); + pw_port_update_state(port, PW_PORT_STATE_CONFIGURE); pw_node_events_port_added(node, port); @@ -848,13 +879,14 @@ int pw_port_set_param(struct pw_port *port, uint32_t mix_id, uint32_t id, uint32 if (param == NULL || res < 0) { free_allocation(&port->allocation); port->allocated = false; - port_update_state(port, mix, PW_PORT_STATE_CONFIGURE); + pw_port_mix_update_state(port, mix, PW_PORT_STATE_CONFIGURE); + if (port->n_mix_configure == port->n_mix) + pw_port_update_state(port, PW_PORT_STATE_CONFIGURE); } else if (!SPA_RESULT_IS_ASYNC(res)) { + pw_port_mix_update_state(port, mix, PW_PORT_STATE_READY); if (port->state == PW_PORT_STATE_CONFIGURE) - port_update_state(port, mix, PW_PORT_STATE_READY); - else if (mix) - mix->state = PW_PORT_STATE_READY; + pw_port_update_state(port, PW_PORT_STATE_READY); } } return res; @@ -888,8 +920,13 @@ int pw_port_use_buffers(struct pw_port *port, uint32_t mix_id, pw_log_debug("port %p: use buffers on mix: %d (%s)", port, res, spa_strerror(res)); } + if (n_buffers == 0) { + pw_port_mix_update_state(port, mix, PW_PORT_STATE_READY); + if (port->n_mix_ready + port->n_mix_configure == port->n_mix) + pw_port_update_state(port, PW_PORT_STATE_READY); + } - if (port->state == PW_PORT_STATE_READY || n_buffers == 0) { + if (port->state == PW_PORT_STATE_READY) { if (!SPA_FLAG_CHECK(port->mix_flags, PW_PORT_MIX_FLAG_MIX_ONLY)) { res = spa_node_port_use_buffers(node->node, port->direction, port->port_id, buffers, n_buffers); @@ -902,16 +939,10 @@ int pw_port_use_buffers(struct pw_port *port, uint32_t mix_id, res = pi->use_buffers(port->implementation_data, buffers, n_buffers); } - if (res < 0) - n_buffers = 0; - - if (n_buffers == 0) - port_update_state(port, mix, PW_PORT_STATE_READY); - else if (!SPA_RESULT_IS_ASYNC(res)) { + if (n_buffers > 0 && !SPA_RESULT_IS_ASYNC(res)) { + pw_port_mix_update_state(port, mix, PW_PORT_STATE_PAUSED); if (port->state == PW_PORT_STATE_READY) - port_update_state(port, mix, PW_PORT_STATE_PAUSED); - else if (mix) - mix->state = PW_PORT_STATE_PAUSED; + pw_port_update_state(port, PW_PORT_STATE_PAUSED); } return res; } @@ -957,10 +988,15 @@ int pw_port_alloc_buffers(struct pw_port *port, uint32_t mix_id, port->allocated = true; } - if (*n_buffers == 0) - port_update_state(port, mix, PW_PORT_STATE_READY); - else if (!SPA_RESULT_IS_ASYNC(res)) - port_update_state(port, mix, PW_PORT_STATE_PAUSED); + if (*n_buffers == 0) { + pw_port_mix_update_state(port, mix, PW_PORT_STATE_READY); + if (port->n_mix_ready + port->n_mix_configure == port->n_mix) + pw_port_update_state(port, PW_PORT_STATE_READY); + } + else if (!SPA_RESULT_IS_ASYNC(res)) { + pw_port_mix_update_state(port, mix, PW_PORT_STATE_PAUSED); + pw_port_update_state(port, PW_PORT_STATE_PAUSED); + } return res; } diff --git a/src/pipewire/private.h b/src/pipewire/private.h index 0b81d22fc..873270dc7 100644 --- a/src/pipewire/private.h +++ b/src/pipewire/private.h @@ -445,6 +445,10 @@ struct pw_port { #define PW_PORT_MIX_FLAG_MIX_ONLY (1<<1) /**< only negotiate mix ports */ uint32_t mix_flags; /**< flags for the mixing */ struct pw_map mix_port_map; /**< map from port_id from mixer */ + uint32_t n_mix; + uint32_t n_mix_configure; + uint32_t n_mix_ready; + uint32_t n_mix_paused; struct { struct spa_io_buffers io; /**< io area of the port */ @@ -693,6 +697,9 @@ int pw_port_add(struct pw_port *port, struct pw_node *node); int pw_port_init_mix(struct pw_port *port, struct pw_port_mix *mix); int pw_port_release_mix(struct pw_port *port, struct pw_port_mix *mix); +void pw_port_mix_update_state(struct pw_port *port, struct pw_port_mix *mix, enum pw_port_state state); +void pw_port_update_state(struct pw_port *port, enum pw_port_state state); + /** Unlink a port \memberof pw_port */ void pw_port_unlink(struct pw_port *port);