The purpose is explained in the function documentation in stream.h. --- src/map-file | 1 + src/pulse/internal.h | 1 + src/pulse/stream.c | 28 +++++++++++++++++++++++++--- src/pulse/stream.h | 31 +++++++++++++++++++++++++++++++ 4 files changed, 58 insertions(+), 3 deletions(-) diff --git a/src/map-file b/src/map-file index fbad1a4..8283a03 100644 --- a/src/map-file +++ b/src/map-file @@ -321,6 +321,7 @@ pa_stream_set_started_callback; pa_stream_set_state_callback; pa_stream_set_suspended_callback; pa_stream_set_underflow_callback; +pa_stream_set_volume_channel_map; pa_stream_set_write_callback; pa_stream_trigger; pa_stream_unref; diff --git a/src/pulse/internal.h b/src/pulse/internal.h index c5084d5..1577e8c 100644 --- a/src/pulse/internal.h +++ b/src/pulse/internal.h @@ -144,6 +144,7 @@ struct pa_stream { pa_sample_spec sample_spec; pa_channel_map channel_map; + bool channel_map_is_set; uint8_t n_formats; pa_format_info *req_formats[PA_MAX_FORMATS]; pa_format_info *format; diff --git a/src/pulse/stream.c b/src/pulse/stream.c index 8e35c29..5b0cce7 100644 --- a/src/pulse/stream.c +++ b/src/pulse/stream.c @@ -102,7 +102,7 @@ static pa_stream *pa_stream_new_with_proplist_internal( PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED); PA_CHECK_VALIDITY_RETURN_NULL(c, name || (p && pa_proplist_contains(p, PA_PROP_MEDIA_NAME)), PA_ERR_INVALID); - s = pa_xnew(pa_stream, 1); + s = pa_xnew0(pa_stream, 1); PA_REFCNT_INIT(s); s->context = c; s->mainloop = c->mainloop; @@ -116,9 +116,10 @@ static pa_stream *pa_stream_new_with_proplist_internal( else pa_sample_spec_init(&s->sample_spec); - if (map) + if (map) { s->channel_map = *map; - else + s->channel_map_is_set = true; + } else pa_channel_map_init(&s->channel_map); s->n_formats = 0; @@ -913,6 +914,27 @@ finish: pa_context_unref(c); } +int pa_stream_set_volume_channel_map(pa_stream *s, const pa_channel_map *map) { + pa_assert(s); + pa_assert(map); + pa_assert(pa_channel_map_valid(map)); + + if (s->state != PA_STREAM_UNCONNECTED) { + pa_log_debug("Stream state is not unconnected."); + return -PA_ERR_BADSTATE; + } + + if (s->channel_map_is_set && !pa_channel_map_equal(map, &s->channel_map)) { + pa_log_debug("Channel map is already set for the stream, changing it is not allowed."); + return -PA_ERR_INVALID; + } + + s->channel_map = *map; + s->channel_map_is_set = true; + + return 0; +} + static void invalidate_indexes(pa_stream *s, bool r, bool w) { pa_assert(s); pa_assert(PA_REFCNT_VALUE(s) >= 1); diff --git a/src/pulse/stream.h b/src/pulse/stream.h index a6785ec..9821ab8 100644 --- a/src/pulse/stream.h +++ b/src/pulse/stream.h @@ -418,6 +418,37 @@ int pa_stream_is_suspended(pa_stream *s); * not, and a negative value on error. \since 0.9.11 */ int pa_stream_is_corked(pa_stream *s); +/** Set the volume channel map. The stream volume is passed to + * pa_stream_connect_playback() or pa_stream_connect_record(), but those + * functions don't have a parameter for specifying the channel map for the + * volume. That's usually not an issue, because normally when creating the + * stream object, the stream channel map is passed already at that time. + * However, with pa_stream_new_extended() it's possible that no channel map is + * specified, in case the client wants the server to decide the stream channel + * map. In that situation the server still needs to know how the client + * intended the volume to be interpreted, i.e. the server needs a channel map + * for the volume (unless the volume has only one channel, in which case that + * volume is applied to all channels and no channel map information is needed + * from the client). + * + * This function exists to handle the specific case where all these conditions + * apply (in other cases this function does not need to be called): + * + * 1) The client creates the stream using pa_stream_new_extended(). + * 2) The client doesn't specify a channel map for the stream (so the server + * will decide the stream channel map). + * 3) The client specifies a volume for the stream. + * 4) The specified volume has more than one channel. + * + * This function must be called before calling pa_stream_connect_playback() or + * pa_stream_connect_record(). Returns a negative error code on failure. + * + * \since 5.0 */ +int pa_stream_set_volume_channel_map( + pa_stream *s, /**< The stream for which to set the volume channel map. */ + const pa_channel_map *map /**< The volume channel map. */ +); + /** Connect the stream to a sink. It is strongly recommended to pass * NULL in both \a dev and \a volume and not to set either * PA_STREAM_START_MUTED nor PA_STREAM_START_UNMUTED -- unless these -- 1.8.3.1