diff --git a/src/daemon/pipewire-pulse.conf.in b/src/daemon/pipewire-pulse.conf.in index 1ee3494a0..2d381a145 100644 --- a/src/daemon/pipewire-pulse.conf.in +++ b/src/daemon/pipewire-pulse.conf.in @@ -90,6 +90,7 @@ pulse.properties = { #pulse.default.frag = 96000/48000 # 2 seconds #pulse.default.tlength = 96000/48000 # 2 seconds #pulse.min.quantum = 256/48000 # 5ms + #pulse.idle.timeout = 5 # pause after 5s of underruns #pulse.default.format = F32 #pulse.default.position = [ FL FR ] # These overrides are only applied when running in a vm. @@ -141,6 +142,7 @@ pulse.rules = [ update-props = { pulse.min.req = 1024/48000 # 21ms pulse.min.quantum = 1024/48000 # 21ms + #pulse.idle.timeout = 0 } } } diff --git a/src/modules/module-protocol-pulse/internal.h b/src/modules/module-protocol-pulse/internal.h index fe8070f7c..1d5731c99 100644 --- a/src/modules/module-protocol-pulse/internal.h +++ b/src/modules/module-protocol-pulse/internal.h @@ -53,6 +53,7 @@ struct defs { struct sample_spec sample_spec; struct channel_map channel_map; uint32_t quantum_limit; + uint32_t idle_timeout; }; struct stats { diff --git a/src/modules/module-protocol-pulse/pulse-server.c b/src/modules/module-protocol-pulse/pulse-server.c index 8e657e927..84bf3b56c 100644 --- a/src/modules/module-protocol-pulse/pulse-server.c +++ b/src/modules/module-protocol-pulse/pulse-server.c @@ -85,6 +85,7 @@ #define DEFAULT_MIN_QUANTUM "256/48000" #define DEFAULT_FORMAT "F32" #define DEFAULT_POSITION "[ FL FR ]" +#define DEFAULT_IDLE_TIMEOUT "5" #define MAX_FORMATS 32 /* The max amount of data we send in one block when capturing. In PulseAudio this @@ -1276,6 +1277,7 @@ struct process_data { uint32_t minreq; uint32_t quantum; unsigned int underrun:1; + unsigned int idle:1; }; static int @@ -1315,6 +1317,17 @@ do_process_done(struct spa_loop *loop, else stream_send_started(stream); } + if (pd->idle) { + if (!stream->is_idle) { + stream->idle_time = stream->timestamp; + } else if (!stream->is_paused && + stream->idle_timeout_sec > 0 && + stream->timestamp - stream->idle_time > + (stream->idle_timeout_sec * SPA_NSEC_PER_SEC)) { + stream_set_paused(stream, true, "long underrun"); + } + } + stream->is_idle = pd->idle; stream->playing_for += pd->playing_for; if (stream->underrun_for != (uint64_t)-1) stream->underrun_for += pd->underrun_for; @@ -1440,6 +1453,7 @@ static void stream_process(void *data) pd.playing_for = size; } + pd.idle = true; pw_log_debug("%p: [%s] underrun read:%u avail:%d max:%u", stream, client->name, index, avail, minreq); } else { @@ -5548,6 +5562,20 @@ static int parse_format(struct pw_properties *props, const char *key, const char pw_log_info(": defaults: %s = %s", key, format_id2name(res->format)); return 0; } +static int parse_uint32(struct pw_properties *props, const char *key, const char *def, + uint32_t *res) +{ + const char *str; + if (props == NULL || + (str = pw_properties_get(props, key)) == NULL) + str = def; + if (!spa_atou32(str, res, 0)) { + pw_log_warn(": invalid uint32_t %s, default to %s", str, def); + spa_atou32(def, res, 0); + } + pw_log_info(": defaults: %s = %u", key, *res); + return 0; +} static void load_defaults(struct defs *def, struct pw_properties *props) { @@ -5559,6 +5587,7 @@ static void load_defaults(struct defs *def, struct pw_properties *props) parse_frac(props, "pulse.min.quantum", DEFAULT_MIN_QUANTUM, &def->min_quantum); parse_format(props, "pulse.default.format", DEFAULT_FORMAT, &def->sample_spec); parse_position(props, "pulse.default.position", DEFAULT_POSITION, &def->channel_map); + parse_uint32(props, "pulse.idle.timeout", DEFAULT_IDLE_TIMEOUT, &def->idle_timeout); def->sample_spec.channels = def->channel_map.channels; def->quantum_limit = 8192; } diff --git a/src/modules/module-protocol-pulse/server.c b/src/modules/module-protocol-pulse/server.c index 5f2fdaba3..503746f8f 100644 --- a/src/modules/module-protocol-pulse/server.c +++ b/src/modules/module-protocol-pulse/server.c @@ -191,6 +191,9 @@ static int handle_memblock(struct client *client, struct message *msg) stream_send_request(stream); + if (stream->is_paused && !stream->corked) + stream_set_paused(stream, false, "new data"); + finish: message_free(msg, false, false); return res; diff --git a/src/modules/module-protocol-pulse/stream.c b/src/modules/module-protocol-pulse/stream.c index f0b0d3587..fb4309086 100644 --- a/src/modules/module-protocol-pulse/stream.c +++ b/src/modules/module-protocol-pulse/stream.c @@ -64,6 +64,7 @@ struct stream *stream_new(struct client *client, enum stream_type type, uint32_t { int res; struct defs *defs = &client->impl->defs; + const char *str; struct stream *stream = calloc(1, sizeof(*stream)); if (stream == NULL) @@ -90,6 +91,9 @@ struct stream *stream_new(struct client *client, enum stream_type type, uint32_t parse_frac(client->props, "pulse.default.req", &defs->default_req, &stream->default_req); parse_frac(client->props, "pulse.default.frag", &defs->default_frag, &stream->default_frag); parse_frac(client->props, "pulse.default.tlength", &defs->default_tlength, &stream->default_tlength); + stream->idle_timeout_sec = defs->idle_timeout; + if ((str = pw_properties_get(client->props, "pulse.idle.timeout")) != NULL) + spa_atou32(str, &stream->idle_timeout_sec, 0); switch (type) { case STREAM_TYPE_RECORD: diff --git a/src/modules/module-protocol-pulse/stream.h b/src/modules/module-protocol-pulse/stream.h index ac144118f..424b2a1e8 100644 --- a/src/modules/module-protocol-pulse/stream.h +++ b/src/modules/module-protocol-pulse/stream.h @@ -82,6 +82,7 @@ struct stream { uint64_t playing_for; uint64_t ticks_base; uint64_t timestamp; + uint64_t idle_time; int64_t delay; uint32_t last_quantum; @@ -93,6 +94,7 @@ struct stream { struct spa_fraction default_frag; struct spa_fraction default_tlength; struct spa_fraction min_quantum; + uint32_t idle_timeout_sec; struct sample_spec ss; struct channel_map map; @@ -115,6 +117,7 @@ struct stream { unsigned int in_prebuf:1; unsigned int killed:1; unsigned int pending:1; + unsigned int is_idle:1; unsigned int is_paused:1; };