sink-input: Add volume_writable to pa_sink_input.
[profile/ivi/pulseaudio-panda.git] / src / pulsecore / protocol-native.c
index d49a78e..4952ee4 100644 (file)
@@ -125,6 +125,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 +862,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 +1000,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 */
@@ -1019,7 +1041,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;
@@ -1082,6 +1104,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;
@@ -1114,6 +1138,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;
 
@@ -1138,11 +1164,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;
@@ -1266,7 +1293,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)
@@ -1323,6 +1350,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);
@@ -1334,42 +1365,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;
+            int64_t windex = pa_memblockq_get_write_index(s->memblockq);
 
-            pa_assert(chunk);
+            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. */
 
-            windex = pa_memblockq_get_write_index(s->memblockq);
-
-/*             pa_log("sink input post: %lu %lli", (unsigned long) chunk->length, (long long) windex); */
-
-            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;
         }
 
@@ -1382,9 +1406,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:
@@ -1848,7 +1872,9 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
         dont_inhibit_auto_suspend = FALSE,
         muted_set = FALSE,
         fail_on_suspend = FALSE,
-        relative_volume = FALSE;
+        relative_volume = FALSE,
+        passthrough = FALSE;
+
     pa_sink_input_flags_t flags = 0;
     pa_proplist *p;
     pa_bool_t volume_set = TRUE;
@@ -1892,7 +1918,7 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
     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 ||
@@ -1950,6 +1976,15 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
         }
     }
 
+    if (c->version >= 18) {
+
+        if (pa_tagstruct_get_boolean(t, &passthrough) < 0 ) {
+            protocol_error(c);
+            pa_proplist_free(p);
+            return;
+        }
+    }
+
     if (!pa_tagstruct_eof(t)) {
         protocol_error(c);
         pa_proplist_free(p);
@@ -1974,16 +2009,17 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
     }
 
     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 */
@@ -2149,7 +2185,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 ||
@@ -2230,14 +2266,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);
 
@@ -2866,7 +2902,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) {
@@ -3020,12 +3056,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);
@@ -3033,7 +3076,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)));
@@ -3042,6 +3085,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) {
@@ -3064,9 +3113,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) {
@@ -3422,6 +3472,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.",
@@ -4424,11 +4475,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);
 
@@ -4887,7 +4939,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;
 }