X-Git-Url: https://code.delx.au/pulseaudio/blobdiff_plain/27c6a80ccb9ccfdd00524360938599cf2d14d4b6..e418e49ecbd643f3cac87438d00baaf86275927c:/src/pulsecore/protocol-native.c?ds=sidebyside diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c index b7471c0a..ee2bc4f5 100644 --- a/src/pulsecore/protocol-native.c +++ b/src/pulsecore/protocol-native.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include @@ -125,6 +126,10 @@ typedef struct playback_stream { uint32_t drain_tag; uint32_t syncid; + /* Optimization to avoid too many rewinds with a lot of small blocks */ + pa_atomic_t seek_or_post_in_queue; + int64_t seek_windex; + pa_atomic_t missing; pa_usec_t configured_sink_latency; pa_buffer_attr buffer_attr; @@ -858,6 +863,18 @@ static void fix_playback_buffer_attr(playback_stream *s) { pa_assert(s); + /* pa_log("Client requested: maxlength=%li bytes tlength=%li bytes minreq=%li bytes prebuf=%li bytes", */ + /* (long) s->buffer_attr.maxlength, */ + /* (long) s->buffer_attr.tlength, */ + /* (long) s->buffer_attr.minreq, */ + /* (long) s->buffer_attr.prebuf); */ + + /* pa_log("Client requested: maxlength=%lu ms tlength=%lu ms minreq=%lu ms prebuf=%lu ms", */ + /* (unsigned long) (pa_bytes_to_usec(s->buffer_attr.maxlength, &s->sink_input->sample_spec) / PA_USEC_PER_MSEC), */ + /* (unsigned long) (pa_bytes_to_usec(s->buffer_attr.tlength, &s->sink_input->sample_spec) / PA_USEC_PER_MSEC), */ + /* (unsigned long) (pa_bytes_to_usec(s->buffer_attr.minreq, &s->sink_input->sample_spec) / PA_USEC_PER_MSEC), */ + /* (unsigned long) (pa_bytes_to_usec(s->buffer_attr.prebuf, &s->sink_input->sample_spec) / PA_USEC_PER_MSEC)); */ + /* This function will be called from the main thread, before as * well as after the sink input has been activated using * pa_sink_input_put()! That means it may not touch any @@ -984,6 +1001,12 @@ static void fix_playback_buffer_attr(playback_stream *s) { if (s->buffer_attr.prebuf == (uint32_t) -1 || s->buffer_attr.prebuf > max_prebuf) s->buffer_attr.prebuf = max_prebuf; + + /* pa_log("Client accepted: maxlength=%lu ms tlength=%lu ms minreq=%lu ms prebuf=%lu ms", */ + /* (unsigned long) (pa_bytes_to_usec(s->buffer_attr.maxlength, &s->sink_input->sample_spec) / PA_USEC_PER_MSEC), */ + /* (unsigned long) (pa_bytes_to_usec(s->buffer_attr.tlength, &s->sink_input->sample_spec) / PA_USEC_PER_MSEC), */ + /* (unsigned long) (pa_bytes_to_usec(s->buffer_attr.minreq, &s->sink_input->sample_spec) / PA_USEC_PER_MSEC), */ + /* (unsigned long) (pa_bytes_to_usec(s->buffer_attr.prebuf, &s->sink_input->sample_spec) / PA_USEC_PER_MSEC)); */ } /* Called from main context */ @@ -992,6 +1015,7 @@ static playback_stream* playback_stream_new( pa_sink *sink, pa_sample_spec *ss, pa_channel_map *map, + pa_idxset *formats, pa_buffer_attr *a, pa_cvolume *volume, pa_bool_t muted, @@ -1002,6 +1026,7 @@ static playback_stream* playback_stream_new( pa_proplist *p, pa_bool_t adjust_latency, pa_bool_t early_requests, + pa_bool_t relative_volume, int *ret) { playback_stream *s, *ssync; @@ -1018,7 +1043,7 @@ static playback_stream* playback_stream_new( pa_assert(ret); /* Find syncid group */ - for (ssync = pa_idxset_first(c->output_streams, &idx); ssync; ssync = pa_idxset_next(c->output_streams, &idx)) { + PA_IDXSET_FOREACH(ssync, c->output_streams, idx) { if (!playback_stream_isinstance(ssync)) continue; @@ -1044,15 +1069,23 @@ static playback_stream* playback_stream_new( data.driver = __FILE__; data.module = c->options->module; data.client = c->client; - data.sink = sink; - pa_sink_input_new_data_set_sample_spec(&data, ss); - pa_sink_input_new_data_set_channel_map(&data, map); + if (sink) + pa_sink_input_new_data_set_sink(&data, sink, TRUE); + if (pa_sample_spec_valid(ss)) + pa_sink_input_new_data_set_sample_spec(&data, ss); + if (pa_channel_map_valid(map)) + pa_sink_input_new_data_set_channel_map(&data, map); + if (formats) + pa_sink_input_new_data_set_formats(&data, formats); if (volume) { pa_sink_input_new_data_set_volume(&data, volume); - data.volume_is_absolute = TRUE; + data.volume_is_absolute = !relative_volume; + data.save_volume = TRUE; } - if (muted_set) + if (muted_set) { pa_sink_input_new_data_set_muted(&data, muted); + data.save_muted = TRUE; + } data.sync_base = ssync ? ssync->sink_input : NULL; data.flags = flags; @@ -1075,6 +1108,8 @@ static playback_stream* playback_stream_new( s->buffer_attr = *a; s->adjust_latency = adjust_latency; s->early_requests = early_requests; + pa_atomic_store(&s->seek_or_post_in_queue, 0); + s->seek_windex = -1; s->sink_input->parent.process_msg = sink_input_process_msg; s->sink_input->pop = sink_input_pop_cb; @@ -1107,6 +1142,8 @@ static playback_stream* playback_stream_new( *missing = (uint32_t) pa_memblockq_pop_missing(s->memblockq); + /* pa_log("missing original: %li", (long int) *missing); */ + *ss = s->sink_input->sample_spec; *map = s->sink_input->channel_map; @@ -1131,11 +1168,12 @@ static void playback_stream_request_bytes(playback_stream *s) { m = pa_memblockq_pop_missing(s->memblockq); - /* pa_log("request_bytes(%lu) (tlength=%lu minreq=%lu length=%lu)", */ + /* pa_log("request_bytes(%lu) (tlength=%lu minreq=%lu length=%lu really missing=%lli)", */ /* (unsigned long) m, */ /* pa_memblockq_get_tlength(s->memblockq), */ /* pa_memblockq_get_minreq(s->memblockq), */ - /* pa_memblockq_get_length(s->memblockq)); */ + /* pa_memblockq_get_length(s->memblockq), */ + /* (long long) pa_memblockq_get_tlength(s->memblockq) - (long long) pa_memblockq_get_length(s->memblockq)); */ if (m <= 0) return; @@ -1259,7 +1297,7 @@ static void native_connection_send_memblock(pa_native_connection *c) { else if (start == c->rrobin_index) return; - if (pa_memblockq_peek(r->memblockq, &chunk) >= 0) { + if (pa_memblockq_peek(r->memblockq, &chunk) >= 0) { pa_memchunk schunk = chunk; if (schunk.length > r->buffer_attr.fragsize) @@ -1316,6 +1354,10 @@ static void handle_seek(playback_stream *s, int64_t indexw) { playback_stream_request_bytes(s); } +static void flush_write_no_account(pa_memblockq *q) { + pa_memblockq_flush_write(q, FALSE); +} + /* Called from thread context */ static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk) { pa_sink_input *i = PA_SINK_INPUT(o); @@ -1327,42 +1369,35 @@ static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int switch (code) { - case SINK_INPUT_MESSAGE_SEEK: { - int64_t windex; - - windex = pa_memblockq_get_write_index(s->memblockq); - - /* The client side is incapable of accounting correctly - * for seeks of a type != PA_SEEK_RELATIVE. We need to be - * able to deal with that. */ - - pa_memblockq_seek(s->memblockq, offset, PA_PTR_TO_UINT(userdata), PA_PTR_TO_UINT(userdata) == PA_SEEK_RELATIVE); - - handle_seek(s, windex); - return 0; - } - + case SINK_INPUT_MESSAGE_SEEK: case SINK_INPUT_MESSAGE_POST_DATA: { - int64_t windex; - - pa_assert(chunk); - - windex = pa_memblockq_get_write_index(s->memblockq); + int64_t windex = pa_memblockq_get_write_index(s->memblockq); -/* pa_log("sink input post: %lu %lli", (unsigned long) chunk->length, (long long) windex); */ + if (code == SINK_INPUT_MESSAGE_SEEK) { + /* The client side is incapable of accounting correctly + * for seeks of a type != PA_SEEK_RELATIVE. We need to be + * able to deal with that. */ - if (pa_memblockq_push_align(s->memblockq, chunk) < 0) { + pa_memblockq_seek(s->memblockq, offset, PA_PTR_TO_UINT(userdata), PA_PTR_TO_UINT(userdata) == PA_SEEK_RELATIVE); + windex = PA_MIN(windex, pa_memblockq_get_write_index(s->memblockq)); + } - if (pa_log_ratelimit()) + if (chunk && pa_memblockq_push_align(s->memblockq, chunk) < 0) { + if (pa_log_ratelimit(PA_LOG_WARN)) pa_log_warn("Failed to push data into queue"); pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_OVERFLOW, NULL, 0, NULL, NULL); pa_memblockq_seek(s->memblockq, (int64_t) chunk->length, PA_SEEK_RELATIVE, TRUE); } - handle_seek(s, windex); - -/* pa_log("sink input post2: %lu", (unsigned long) pa_memblockq_get_length(s->memblockq)); */ - + /* If more data is in queue, we rewind later instead. */ + if (s->seek_windex != -1) + windex = PA_MIN(windex, s->seek_windex); + if (pa_atomic_dec(&s->seek_or_post_in_queue) > 1) + s->seek_windex = windex; + else { + s->seek_windex = -1; + handle_seek(s, windex); + } return 0; } @@ -1375,9 +1410,9 @@ static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int pa_sink_input *isync; void (*func)(pa_memblockq *bq); - switch (code) { + switch (code) { case SINK_INPUT_MESSAGE_FLUSH: - func = pa_memblockq_flush_write; + func = flush_write_no_account; break; case SINK_INPUT_MESSAGE_PREBUF_FORCE: @@ -1840,11 +1875,18 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u early_requests = FALSE, dont_inhibit_auto_suspend = FALSE, muted_set = FALSE, - fail_on_suspend = FALSE; + fail_on_suspend = FALSE, + relative_volume = FALSE, + passthrough = FALSE; + pa_sink_input_flags_t flags = 0; - pa_proplist *p; + pa_proplist *p = NULL; pa_bool_t volume_set = TRUE; int ret = PA_ERR_INVALID; + uint8_t n_formats = 0; + pa_format_info *format; + pa_idxset *formats = NULL; + uint32_t i; pa_native_connection_assert_ref(c); pa_assert(t); @@ -1867,24 +1909,21 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u PA_TAG_INVALID) < 0) { protocol_error(c); - return; + goto error; } CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS); CHECK_VALIDITY(c->pstream, !sink_name || pa_namereg_is_valid_name_or_wildcard(sink_name, PA_NAMEREG_SINK), tag, PA_ERR_INVALID); CHECK_VALIDITY(c->pstream, sink_index == PA_INVALID_INDEX || !sink_name, tag, PA_ERR_INVALID); CHECK_VALIDITY(c->pstream, !sink_name || sink_index == PA_INVALID_INDEX, tag, PA_ERR_INVALID); - CHECK_VALIDITY(c->pstream, pa_channel_map_valid(&map), tag, PA_ERR_INVALID); - CHECK_VALIDITY(c->pstream, pa_sample_spec_valid(&ss), tag, PA_ERR_INVALID); CHECK_VALIDITY(c->pstream, pa_cvolume_valid(&volume), tag, PA_ERR_INVALID); - CHECK_VALIDITY(c->pstream, map.channels == ss.channels && volume.channels == ss.channels, tag, PA_ERR_INVALID); p = pa_proplist_new(); if (name) pa_proplist_sets(p, PA_PROP_MEDIA_NAME, name); - if (c->version >= 12) { + if (c->version >= 12) { /* Since 0.9.8 the user can ask for a couple of additional flags */ if (pa_tagstruct_get_boolean(t, &no_remap) < 0 || @@ -1896,8 +1935,7 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u pa_tagstruct_get_boolean(t, &variable_rate) < 0) { protocol_error(c); - pa_proplist_free(p); - return; + goto error; } } @@ -1906,9 +1944,9 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u if (pa_tagstruct_get_boolean(t, &muted) < 0 || pa_tagstruct_get_boolean(t, &adjust_latency) < 0 || pa_tagstruct_get_proplist(t, p) < 0) { + protocol_error(c); - pa_proplist_free(p); - return; + goto error; } } @@ -1916,9 +1954,9 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u if (pa_tagstruct_get_boolean(t, &volume_set) < 0 || pa_tagstruct_get_boolean(t, &early_requests) < 0) { + protocol_error(c); - pa_proplist_free(p); - return; + goto error; } } @@ -1927,52 +1965,92 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u if (pa_tagstruct_get_boolean(t, &muted_set) < 0 || pa_tagstruct_get_boolean(t, &dont_inhibit_auto_suspend) < 0 || pa_tagstruct_get_boolean(t, &fail_on_suspend) < 0) { + protocol_error(c); - pa_proplist_free(p); - return; + goto error; + } + } + + if (c->version >= 17) { + + if (pa_tagstruct_get_boolean(t, &relative_volume) < 0) { + + protocol_error(c); + goto error; + } + } + + if (c->version >= 18) { + + if (pa_tagstruct_get_boolean(t, &passthrough) < 0 ) { + protocol_error(c); + goto error; + } + } + + if (c->version >= 21) { + + if (pa_tagstruct_getu8(t, &n_formats) < 0) { + protocol_error(c); + goto error; + } + + if (n_formats) + formats = pa_idxset_new(NULL, NULL); + + for (i = 0; i < n_formats; i++) { + format = pa_format_info_new(); + if (pa_tagstruct_get_format_info(t, format) < 0) { + protocol_error(c); + goto error; + } + pa_idxset_put(formats, format, NULL); } } + CHECK_VALIDITY(c->pstream, n_formats > 0 || pa_sample_spec_valid(&ss), tag, PA_ERR_INVALID); + CHECK_VALIDITY(c->pstream, n_formats > 0 || (map.channels == ss.channels && volume.channels == ss.channels), tag, PA_ERR_INVALID); + CHECK_VALIDITY(c->pstream, n_formats > 0 || pa_channel_map_valid(&map), tag, PA_ERR_INVALID); + /* XXX: add checks on formats. At least inverse checks of the 3 above */ + if (!pa_tagstruct_eof(t)) { protocol_error(c); - pa_proplist_free(p); - return; + goto error; } if (sink_index != PA_INVALID_INDEX) { if (!(sink = pa_idxset_get_by_index(c->protocol->core->sinks, sink_index))) { pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY); - pa_proplist_free(p); - return; + goto error; } } else if (sink_name) { if (!(sink = pa_namereg_get(c->protocol->core, sink_name, PA_NAMEREG_SINK))) { pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY); - pa_proplist_free(p); - return; + goto error; } } flags = - (corked ? PA_SINK_INPUT_START_CORKED : 0) | - (no_remap ? PA_SINK_INPUT_NO_REMAP : 0) | - (no_remix ? PA_SINK_INPUT_NO_REMIX : 0) | - (fix_format ? PA_SINK_INPUT_FIX_FORMAT : 0) | - (fix_rate ? PA_SINK_INPUT_FIX_RATE : 0) | - (fix_channels ? PA_SINK_INPUT_FIX_CHANNELS : 0) | - (no_move ? PA_SINK_INPUT_DONT_MOVE : 0) | - (variable_rate ? PA_SINK_INPUT_VARIABLE_RATE : 0) | + (corked ? PA_SINK_INPUT_START_CORKED : 0) | + (no_remap ? PA_SINK_INPUT_NO_REMAP : 0) | + (no_remix ? PA_SINK_INPUT_NO_REMIX : 0) | + (fix_format ? PA_SINK_INPUT_FIX_FORMAT : 0) | + (fix_rate ? PA_SINK_INPUT_FIX_RATE : 0) | + (fix_channels ? PA_SINK_INPUT_FIX_CHANNELS : 0) | + (no_move ? PA_SINK_INPUT_DONT_MOVE : 0) | + (variable_rate ? PA_SINK_INPUT_VARIABLE_RATE : 0) | (dont_inhibit_auto_suspend ? PA_SINK_INPUT_DONT_INHIBIT_AUTO_SUSPEND : 0) | - (fail_on_suspend ? PA_SINK_INPUT_NO_CREATE_ON_SUSPEND|PA_SINK_INPUT_KILL_ON_SUSPEND : 0); + (fail_on_suspend ? PA_SINK_INPUT_NO_CREATE_ON_SUSPEND|PA_SINK_INPUT_KILL_ON_SUSPEND : 0) | + (passthrough ? PA_SINK_INPUT_PASSTHROUGH : 0); /* Only since protocol version 15 there's a seperate muted_set * flag. For older versions we synthesize it here */ muted_set = muted_set || muted; - s = playback_stream_new(c, sink, &ss, &map, &attr, volume_set ? &volume : NULL, muted, muted_set, syncid, &missing, flags, p, adjust_latency, early_requests, &ret); + s = playback_stream_new(c, sink, &ss, &map, formats, &attr, volume_set ? &volume : NULL, muted, muted_set, syncid, &missing, flags, p, adjust_latency, early_requests, relative_volume, &ret); pa_proplist_free(p); CHECK_VALIDITY(c->pstream, s, tag, ret); @@ -2011,7 +2089,26 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u if (c->version >= 13) pa_tagstruct_put_usec(reply, s->configured_sink_latency); + if (c->version >= 21) { + /* Send back the format we negotiated */ + if (s->sink_input->format) + pa_tagstruct_put_format_info(reply, s->sink_input->format); + else { + pa_format_info *f = pa_format_info_new(); + pa_tagstruct_put_format_info(reply, f); + pa_format_info_free(f); + } + } + pa_pstream_send_tagstruct(c->pstream, reply); + return; + +error: + if (p) + pa_proplist_free(p); + if (formats) + pa_idxset_free(formats, (pa_free2_cb_t) pa_format_info_free2, NULL); + return; } static void command_delete_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { @@ -2132,7 +2229,7 @@ static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uin if (name) pa_proplist_sets(p, PA_PROP_MEDIA_NAME, name); - if (c->version >= 12) { + if (c->version >= 12) { /* Since 0.9.8 the user can ask for a couple of additional flags */ if (pa_tagstruct_get_boolean(t, &no_remap) < 0 || @@ -2213,14 +2310,14 @@ static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uin } flags = - (corked ? PA_SOURCE_OUTPUT_START_CORKED : 0) | - (no_remap ? PA_SOURCE_OUTPUT_NO_REMAP : 0) | - (no_remix ? PA_SOURCE_OUTPUT_NO_REMIX : 0) | - (fix_format ? PA_SOURCE_OUTPUT_FIX_FORMAT : 0) | - (fix_rate ? PA_SOURCE_OUTPUT_FIX_RATE : 0) | - (fix_channels ? PA_SOURCE_OUTPUT_FIX_CHANNELS : 0) | - (no_move ? PA_SOURCE_OUTPUT_DONT_MOVE : 0) | - (variable_rate ? PA_SOURCE_OUTPUT_VARIABLE_RATE : 0) | + (corked ? PA_SOURCE_OUTPUT_START_CORKED : 0) | + (no_remap ? PA_SOURCE_OUTPUT_NO_REMAP : 0) | + (no_remix ? PA_SOURCE_OUTPUT_NO_REMIX : 0) | + (fix_format ? PA_SOURCE_OUTPUT_FIX_FORMAT : 0) | + (fix_rate ? PA_SOURCE_OUTPUT_FIX_RATE : 0) | + (fix_channels ? PA_SOURCE_OUTPUT_FIX_CHANNELS : 0) | + (no_move ? PA_SOURCE_OUTPUT_DONT_MOVE : 0) | + (variable_rate ? PA_SOURCE_OUTPUT_VARIABLE_RATE : 0) | (dont_inhibit_auto_suspend ? PA_SOURCE_OUTPUT_DONT_INHIBIT_AUTO_SUSPEND : 0) | (fail_on_suspend ? PA_SOURCE_OUTPUT_NO_CREATE_ON_SUSPEND|PA_SOURCE_OUTPUT_KILL_ON_SUSPEND : 0); @@ -2614,7 +2711,7 @@ static void command_get_record_latency(pa_pdispatch *pd, uint32_t command, uint3 pa_tagstruct_put_usec(reply, s->current_monitor_latency); pa_tagstruct_put_usec(reply, s->current_source_latency + - pa_bytes_to_usec(s->on_the_fly_snapshot, &s->source_output->sample_spec)); + pa_bytes_to_usec(s->on_the_fly_snapshot, &s->source_output->source->sample_spec)); pa_tagstruct_put_boolean(reply, pa_source_get_state(s->source_output->source) == PA_SOURCE_RUNNING && pa_source_output_get_state(s->source_output) == PA_SOURCE_OUTPUT_RUNNING); @@ -2849,7 +2946,7 @@ static void sink_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_sin PA_TAG_STRING, sink->monitor_source ? sink->monitor_source->name : NULL, PA_TAG_USEC, pa_sink_get_latency(sink), PA_TAG_STRING, sink->driver, - PA_TAG_U32, sink->flags, + PA_TAG_U32, sink->flags & ~PA_SINK_SHARE_VOLUME_WITH_MASTER, PA_TAG_INVALID); if (c->version >= 13) { @@ -3003,12 +3100,19 @@ static void sink_input_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_sample_spec fixed_ss; pa_usec_t sink_latency; pa_cvolume v; + pa_bool_t has_volume = FALSE; pa_assert(t); pa_sink_input_assert_ref(s); fixup_sample_spec(c, &fixed_ss, &s->sample_spec); + has_volume = pa_sink_input_is_volume_readable(s); + if (has_volume) + pa_sink_input_get_volume(s, &v, TRUE); + else + pa_cvolume_reset(&v, fixed_ss.channels); + pa_tagstruct_putu32(t, s->index); pa_tagstruct_puts(t, pa_strnull(pa_proplist_gets(s->proplist, PA_PROP_MEDIA_NAME))); pa_tagstruct_putu32(t, s->module ? s->module->index : PA_INVALID_INDEX); @@ -3016,7 +3120,7 @@ static void sink_input_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_tagstruct_putu32(t, s->sink->index); pa_tagstruct_put_sample_spec(t, &fixed_ss); pa_tagstruct_put_channel_map(t, &s->channel_map); - pa_tagstruct_put_cvolume(t, pa_sink_input_get_volume(s, &v, TRUE)); + pa_tagstruct_put_cvolume(t, &v); pa_tagstruct_put_usec(t, pa_sink_input_get_latency(s, &sink_latency)); pa_tagstruct_put_usec(t, sink_latency); pa_tagstruct_puts(t, pa_resample_method_to_string(pa_sink_input_get_resample_method(s))); @@ -3025,6 +3129,12 @@ static void sink_input_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_tagstruct_put_boolean(t, pa_sink_input_get_mute(s)); if (c->version >= 13) pa_tagstruct_put_proplist(t, s->proplist); + if (c->version >= 19) + pa_tagstruct_put_boolean(t, (pa_sink_input_get_state(s) == PA_SINK_INPUT_CORKED)); + if (c->version >= 20) { + pa_tagstruct_put_boolean(t, has_volume); + pa_tagstruct_put_boolean(t, s->volume_writable); + } } static void source_output_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_source_output *s) { @@ -3047,9 +3157,10 @@ static void source_output_fill_tagstruct(pa_native_connection *c, pa_tagstruct * pa_tagstruct_put_usec(t, source_latency); pa_tagstruct_puts(t, pa_resample_method_to_string(pa_source_output_get_resample_method(s))); pa_tagstruct_puts(t, s->driver); - if (c->version >= 13) pa_tagstruct_put_proplist(t, s->proplist); + if (c->version >= 19) + pa_tagstruct_put_boolean(t, (pa_source_output_get_state(s) == PA_SOURCE_OUTPUT_CORKED)); } static void scache_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_scache_entry *e) { @@ -3405,6 +3516,7 @@ static void command_set_volume( pa_log_debug("Client %s changes volume of source %s.", client_name, source->name); pa_source_set_volume(source, &volume, TRUE); } else if (si) { + CHECK_VALIDITY(c->pstream, si->volume_writable, tag, PA_ERR_BADSTATE); CHECK_VALIDITY(c->pstream, volume.channels == 1 || pa_cvolume_compatible(&volume, &si->sample_spec), tag, PA_ERR_INVALID); pa_log_debug("Client %s changes volume of sink input %s.", @@ -4407,11 +4519,12 @@ static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, int64_t o if (playback_stream_isinstance(stream)) { playback_stream *ps = PLAYBACK_STREAM(stream); + pa_atomic_inc(&ps->seek_or_post_in_queue); if (chunk->memblock) { if (seek != PA_SEEK_RELATIVE || offset != 0) - pa_asyncmsgq_post(ps->sink_input->sink->asyncmsgq, PA_MSGOBJECT(ps->sink_input), SINK_INPUT_MESSAGE_SEEK, PA_UINT_TO_PTR(seek), offset, NULL, NULL); - - pa_asyncmsgq_post(ps->sink_input->sink->asyncmsgq, PA_MSGOBJECT(ps->sink_input), SINK_INPUT_MESSAGE_POST_DATA, NULL, 0, chunk, NULL); + pa_asyncmsgq_post(ps->sink_input->sink->asyncmsgq, PA_MSGOBJECT(ps->sink_input), SINK_INPUT_MESSAGE_SEEK, PA_UINT_TO_PTR(seek), offset, chunk, NULL); + else + pa_asyncmsgq_post(ps->sink_input->sink->asyncmsgq, PA_MSGOBJECT(ps->sink_input), SINK_INPUT_MESSAGE_POST_DATA, NULL, 0, chunk, NULL); } else pa_asyncmsgq_post(ps->sink_input->sink->asyncmsgq, PA_MSGOBJECT(ps->sink_input), SINK_INPUT_MESSAGE_SEEK, PA_UINT_TO_PTR(seek), offset+chunk->length, NULL, NULL); @@ -4870,7 +4983,7 @@ pa_pstream* pa_native_connection_get_pstream(pa_native_connection *c) { } pa_client* pa_native_connection_get_client(pa_native_connection *c) { - pa_native_connection_assert_ref(c); + pa_native_connection_assert_ref(c); - return c->client; + return c->client; }