]> code.delx.au - pulseaudio/blobdiff - src/pulsecore/protocol-native.c
introduce default channel map in addition to the default sample spec
[pulseaudio] / src / pulsecore / protocol-native.c
index e8080bfe5bef11accc6a193322018a8d92a63c6a..10b9e7da330909ef5ad6ed9506ebba0a9862c21d 100644 (file)
@@ -207,6 +207,7 @@ static void sink_input_moved_cb(pa_sink_input *i);
 static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes);
 static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes);
 static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes);
+static void sink_input_send_event_cb(pa_sink_input *i, const char *event, pa_proplist *pl);
 
 static void native_connection_send_memblock(pa_native_connection *c);
 static void playback_stream_request_bytes(struct playback_stream*s);
@@ -216,6 +217,7 @@ static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk)
 static void source_output_suspend_cb(pa_source_output *o, pa_bool_t suspend);
 static void source_output_moved_cb(pa_source_output *o);
 static pa_usec_t source_output_get_latency_cb(pa_source_output *o);
+static void source_output_send_event_cb(pa_source_output *o, const char *event, pa_proplist *pl);
 
 static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk);
 
@@ -591,10 +593,11 @@ static record_stream* record_stream_new(
         pa_proplist *p,
         pa_bool_t adjust_latency,
         pa_sink_input *direct_on_input,
-        pa_bool_t early_requests) {
+        pa_bool_t early_requests,
+        int *ret) {
 
     record_stream *s;
-    pa_source_output *source_output;
+    pa_source_output *source_output = NULL;
     size_t base;
     pa_source_output_new_data data;
 
@@ -602,6 +605,7 @@ static record_stream* record_stream_new(
     pa_assert(ss);
     pa_assert(maxlength);
     pa_assert(p);
+    pa_assert(ret);
 
     pa_source_output_new_data_init(&data);
 
@@ -616,7 +620,7 @@ static record_stream* record_stream_new(
     if (peak_detect)
         data.resample_method = PA_RESAMPLER_PEAKS;
 
-    source_output = pa_source_output_new(c->protocol->core, &data, flags);
+    *ret = -pa_source_output_new(&source_output, c->protocol->core, &data, flags);
 
     pa_source_output_new_data_done(&data);
 
@@ -634,6 +638,7 @@ static record_stream* record_stream_new(
     s->source_output->get_latency = source_output_get_latency_cb;
     s->source_output->moved = source_output_moved_cb;
     s->source_output->suspend = source_output_suspend_cb;
+    s->source_output->send_event = source_output_send_event_cb;
     s->source_output->userdata = s;
 
     fix_record_buffer_attr_pre(s, adjust_latency, early_requests, maxlength, fragsize);
@@ -965,10 +970,11 @@ static playback_stream* playback_stream_new(
         pa_sink_input_flags_t flags,
         pa_proplist *p,
         pa_bool_t adjust_latency,
-        pa_bool_t early_requests) {
+        pa_bool_t early_requests,
+        int *ret) {
 
     playback_stream *s, *ssync;
-    pa_sink_input *sink_input;
+    pa_sink_input *sink_input = NULL;
     pa_memchunk silence;
     uint32_t idx;
     int64_t start_index;
@@ -982,6 +988,7 @@ static playback_stream* playback_stream_new(
     pa_assert(minreq);
     pa_assert(missing);
     pa_assert(p);
+    pa_assert(ret);
 
     /* Find syncid group */
     for (ssync = pa_idxset_first(c->output_streams, &idx); ssync; ssync = pa_idxset_next(c->output_streams, &idx)) {
@@ -998,8 +1005,10 @@ static playback_stream* playback_stream_new(
 
         if (!sink)
             sink = ssync->sink_input->sink;
-        else if (sink != ssync->sink_input->sink)
+        else if (sink != ssync->sink_input->sink) {
+            *ret = PA_ERR_INVALID;
             return NULL;
+        }
     }
 
     pa_sink_input_new_data_init(&data);
@@ -1012,12 +1021,12 @@ static playback_stream* playback_stream_new(
     pa_sink_input_new_data_set_sample_spec(&data, ss);
     pa_sink_input_new_data_set_channel_map(&data, map);
     if (volume)
-        pa_sink_input_new_data_set_virtual_volume(&data, volume);
+        pa_sink_input_new_data_set_volume(&data, volume);
     if (muted_set)
         pa_sink_input_new_data_set_muted(&data, muted);
     data.sync_base = ssync ? ssync->sink_input : NULL;
 
-    sink_input = pa_sink_input_new(c->protocol->core, &data, flags);
+    *ret = -pa_sink_input_new(&sink_input, c->protocol->core, &data, flags);
 
     pa_sink_input_new_data_done(&data);
 
@@ -1042,6 +1051,7 @@ static playback_stream* playback_stream_new(
     s->sink_input->kill = sink_input_kill_cb;
     s->sink_input->moved = sink_input_moved_cb;
     s->sink_input->suspend = sink_input_suspend_cb;
+    s->sink_input->send_event = sink_input_send_event_cb;
     s->sink_input->userdata = s;
 
     start_index = ssync ? pa_memblockq_get_read_index(ssync->memblockq) : 0;
@@ -1487,6 +1497,27 @@ static void sink_input_kill_cb(pa_sink_input *i) {
     playback_stream_unlink(s);
 }
 
+/* Called from main context */
+static void sink_input_send_event_cb(pa_sink_input *i, const char *event, pa_proplist *pl) {
+    playback_stream *s;
+    pa_tagstruct *t;
+
+    pa_sink_input_assert_ref(i);
+    s = PLAYBACK_STREAM(i->userdata);
+    playback_stream_assert_ref(s);
+
+    if (s->connection->version < 15)
+      return;
+
+    t = pa_tagstruct_new(NULL, 0);
+    pa_tagstruct_putu32(t, PA_COMMAND_PLAYBACK_STREAM_EVENT);
+    pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
+    pa_tagstruct_putu32(t, s->index);
+    pa_tagstruct_puts(t, event);
+    pa_tagstruct_put_proplist(t, pl);
+    pa_pstream_send_tagstruct(s->connection->pstream, t);
+}
+
 /* Called from main context */
 static void sink_input_suspend_cb(pa_sink_input *i, pa_bool_t suspend) {
     playback_stream *s;
@@ -1588,6 +1619,27 @@ static pa_usec_t source_output_get_latency_cb(pa_source_output *o) {
     return pa_bytes_to_usec(pa_memblockq_get_length(s->memblockq), &o->sample_spec);
 }
 
+/* Called from main context */
+static void source_output_send_event_cb(pa_source_output *o, const char *event, pa_proplist *pl) {
+    record_stream *s;
+    pa_tagstruct *t;
+
+    pa_source_output_assert_ref(o);
+    s = RECORD_STREAM(o->userdata);
+    record_stream_assert_ref(s);
+
+    if (s->connection->version < 15)
+      return;
+
+    t = pa_tagstruct_new(NULL, 0);
+    pa_tagstruct_putu32(t, PA_COMMAND_RECORD_STREAM_EVENT);
+    pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
+    pa_tagstruct_putu32(t, s->index);
+    pa_tagstruct_puts(t, event);
+    pa_tagstruct_put_proplist(t, pl);
+    pa_pstream_send_tagstruct(s->connection->pstream, t);
+}
+
 /* Called from main context */
 static void source_output_suspend_cb(pa_source_output *o, pa_bool_t suspend) {
     record_stream *s;
@@ -1696,6 +1748,7 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
     pa_sink_input_flags_t flags = 0;
     pa_proplist *p;
     pa_bool_t volume_set = TRUE;
+    int ret = PA_ERR_INVALID;
 
     pa_native_connection_assert_ref(c);
     pa_assert(t);
@@ -1822,10 +1875,10 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
      * flag. For older versions we synthesize it here */
     muted_set = muted_set || muted;
 
-    s = playback_stream_new(c, sink, &ss, &map, &maxlength, &tlength, &prebuf, &minreq, volume_set ? &volume : NULL, muted, muted_set, syncid, &missing, flags, p, adjust_latency, early_requests);
+    s = playback_stream_new(c, sink, &ss, &map, &maxlength, &tlength, &prebuf, &minreq, volume_set ? &volume : NULL, muted, muted_set, syncid, &missing, flags, p, adjust_latency, early_requests, &ret);
     pa_proplist_free(p);
 
-    CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_INVALID);
+    CHECK_VALIDITY(c->pstream, s, tag, ret);
 
     reply = reply_new(tag);
     pa_tagstruct_putu32(reply, s->index);
@@ -1950,6 +2003,7 @@ static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uin
     pa_proplist *p;
     uint32_t direct_on_input_idx = PA_INVALID_INDEX;
     pa_sink_input *direct_on_input = NULL;
+    int ret = PA_ERR_INVALID;
 
     pa_native_connection_assert_ref(c);
     pa_assert(t);
@@ -2071,10 +2125,10 @@ static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uin
         (dont_inhibit_auto_suspend ? PA_SOURCE_OUTPUT_DONT_INHIBIT_AUTO_SUSPEND : 0) |
         (fail_on_suspend ? PA_SOURCE_OUTPUT_FAIL_ON_SUSPEND : 0);
 
-    s = record_stream_new(c, source, &ss, &map, peak_detect, &maxlength, &fragment_size, flags, p, adjust_latency, direct_on_input, early_requests);
+    s = record_stream_new(c, source, &ss, &map, peak_detect, &maxlength, &fragment_size, flags, p, adjust_latency, direct_on_input, early_requests, &ret);
     pa_proplist_free(p);
 
-    CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_INVALID);
+    CHECK_VALIDITY(c->pstream, s, tag, ret);
 
     reply = reply_new(tag);
     pa_tagstruct_putu32(reply, s->index);
@@ -2287,11 +2341,9 @@ static void command_set_client_name(pa_pdispatch *pd, uint32_t command, uint32_t
             return;
         }
 
-    pa_proplist_update(c->client->proplist, PA_UPDATE_REPLACE, p);
+    pa_client_update_proplist(c->client, PA_UPDATE_REPLACE, p);
     pa_proplist_free(p);
 
-    pa_subscription_post(c->protocol->core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_CHANGE, c->client->index);
-
     reply = reply_new(tag);
 
     if (c->version >= 13)
@@ -2508,7 +2560,10 @@ static void command_create_upload_stream(pa_pdispatch *pd, uint32_t command, uin
         if (!(name = pa_proplist_gets(p, PA_PROP_EVENT_ID)))
             name = pa_proplist_gets(p, PA_PROP_MEDIA_NAME);
 
-    CHECK_VALIDITY(c->pstream, name && pa_namereg_is_valid_name(name), tag, PA_ERR_INVALID);
+    if (!name || !pa_namereg_is_valid_name(name)) {
+        pa_proplist_free(p);
+        CHECK_VALIDITY(c->pstream, FALSE, tag, PA_ERR_INVALID);
+    }
 
     s = upload_stream_new(c, &ss, &map, name, length, p);
     pa_proplist_free(p);
@@ -2697,6 +2752,7 @@ static void sink_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_sin
             pa_log_error("Internal sink state is invalid.");
         pa_tagstruct_putu32(t, pa_sink_get_state(sink));
         pa_tagstruct_putu32(t, sink->n_volume_steps);
+        pa_tagstruct_putu32(t, sink->card ? sink->card->index : PA_INVALID_INDEX);
     }
 }
 
@@ -2736,6 +2792,7 @@ static void source_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_s
             pa_log_error("Internal source state is invalid.");
         pa_tagstruct_putu32(t, pa_source_get_state(source));
         pa_tagstruct_putu32(t, source->n_volume_steps);
+        pa_tagstruct_putu32(t, source->card ? source->card->index : PA_INVALID_INDEX);
     }
 }
 
@@ -2770,6 +2827,8 @@ static void card_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_car
         while ((p = pa_hashmap_iterate(card->profiles, &state, NULL))) {
             pa_tagstruct_puts(t, p->name);
             pa_tagstruct_puts(t, p->description);
+            pa_tagstruct_putu32(t, p->n_sinks);
+            pa_tagstruct_putu32(t, p->n_sources);
         }
     }
 
@@ -2955,7 +3014,7 @@ static void command_get_info(pa_pdispatch *pd, uint32_t command, uint32_t tag, p
         source_fill_tagstruct(c, reply, source);
     else if (client)
         client_fill_tagstruct(c, reply, client);
-    else if (client)
+    else if (card)
         card_fill_tagstruct(c, reply, card);
     else if (module)
         module_fill_tagstruct(c, reply, module);
@@ -3066,6 +3125,9 @@ static void command_get_server_info(pa_pdispatch *pd, uint32_t command, uint32_t
 
     pa_tagstruct_putu32(reply, c->protocol->core->cookie);
 
+    if (c->version >= 15)
+        pa_tagstruct_put_channel_map(reply, &c->protocol->core->default_channel_map);
+
     pa_pstream_send_tagstruct(c->pstream, reply);
 }
 
@@ -3534,34 +3596,38 @@ static void command_update_proplist(pa_pdispatch *pd, uint32_t command, uint32_t
         }
     }
 
-    CHECK_VALIDITY(c->pstream, mode == PA_UPDATE_SET || mode == PA_UPDATE_MERGE || mode == PA_UPDATE_REPLACE, tag, PA_ERR_INVALID);
+    if (!(mode == PA_UPDATE_SET || mode == PA_UPDATE_MERGE || mode == PA_UPDATE_REPLACE)) {
+        pa_proplist_free(p);
+        CHECK_VALIDITY(c->pstream, FALSE, tag, PA_ERR_INVALID);
+    }
 
     if (command == PA_COMMAND_UPDATE_PLAYBACK_STREAM_PROPLIST) {
         playback_stream *s;
 
         s = pa_idxset_get_by_index(c->output_streams, idx);
-        CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
-        CHECK_VALIDITY(c->pstream, playback_stream_isinstance(s), tag, PA_ERR_NOENTITY);
-
-        pa_proplist_update(s->sink_input->proplist, mode, p);
-        pa_subscription_post(c->protocol->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, s->sink_input->index);
+        if (!s || !playback_stream_isinstance(s)) {
+            pa_proplist_free(p);
+            CHECK_VALIDITY(c->pstream, FALSE, tag, PA_ERR_NOENTITY);
+        }
+        pa_sink_input_update_proplist(s->sink_input, mode, p);
 
     } else if (command == PA_COMMAND_UPDATE_RECORD_STREAM_PROPLIST) {
         record_stream *s;
 
-        s = pa_idxset_get_by_index(c->record_streams, idx);
-        CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
+        if (!(s = pa_idxset_get_by_index(c->record_streams, idx))) {
+            pa_proplist_free(p);
+            CHECK_VALIDITY(c->pstream, FALSE, tag, PA_ERR_NOENTITY);
+        }
+        pa_source_output_update_proplist(s->source_output, mode, p);
 
-        pa_proplist_update(s->source_output->proplist, mode, p);
-        pa_subscription_post(c->protocol->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, s->source_output->index);
     } else {
         pa_assert(command == PA_COMMAND_UPDATE_CLIENT_PROPLIST);
 
-        pa_proplist_update(c->client->proplist, mode, p);
-        pa_subscription_post(c->protocol->core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_CHANGE, c->client->index);
+        pa_client_update_proplist(c->client, mode, p);
     }
 
     pa_pstream_send_simple_ack(c->pstream, tag);
+    pa_proplist_free(p);
 }
 
 static void command_remove_proplist(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
@@ -4017,7 +4083,7 @@ static void command_extension(pa_pdispatch *pd, uint32_t command, uint32_t tag,
     CHECK_VALIDITY(c->pstream, m->load_once || idx != PA_INVALID_INDEX, tag, PA_ERR_INVALID);
 
     cb = (pa_native_protocol_ext_cb_t) (unsigned long) pa_hashmap_get(c->protocol->extensions, m);
-    CHECK_VALIDITY(c->pstream, m, tag, PA_ERR_NOEXTENSION);
+    CHECK_VALIDITY(c->pstream, cb, tag, PA_ERR_NOEXTENSION);
 
     if (cb(c->protocol, m, c, tag, t) < 0)
         protocol_error(c);
@@ -4095,17 +4161,20 @@ 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);
 
-        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);
+        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_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);
 
     } else {
         upload_stream *u = UPLOAD_STREAM(stream);
         size_t l;
 
         if (!u->memchunk.memblock) {
-            if (u->length == chunk->length) {
+            if (u->length == chunk->length && chunk->memblock) {
                 u->memchunk = *chunk;
                 pa_memblock_ref(u->memchunk.memblock);
                 u->length = 0;
@@ -4121,17 +4190,22 @@ static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, int64_t o
         if (l > chunk->length)
             l = chunk->length;
 
-
         if (l > 0) {
-            void *src, *dst;
+            void *dst;
             dst = pa_memblock_acquire(u->memchunk.memblock);
-            src = pa_memblock_acquire(chunk->memblock);
 
-            memcpy((uint8_t*) dst + u->memchunk.index + u->memchunk.length,
-                   (uint8_t*) src+chunk->index, l);
+            if (chunk->memblock) {
+                void *src;
+                src = pa_memblock_acquire(chunk->memblock);
+
+                memcpy((uint8_t*) dst + u->memchunk.index + u->memchunk.length,
+                       (uint8_t*) src + chunk->index, l);
+
+                pa_memblock_release(chunk->memblock);
+            } else
+                pa_silence_memory((uint8_t*) dst + u->memchunk.index + u->memchunk.length, l, &u->sample_spec);
 
             pa_memblock_release(u->memchunk.memblock);
-            pa_memblock_release(chunk->memblock);
 
             u->memchunk.length += l;
             u->length -= l;
@@ -4185,6 +4259,25 @@ static void client_kill_cb(pa_client *c) {
     pa_log_info("Connection killed.");
 }
 
+static void client_send_event_cb(pa_client *client, const char*event, pa_proplist *pl) {
+    pa_tagstruct *t;
+    pa_native_connection *c;
+
+    pa_assert(client);
+    c = PA_NATIVE_CONNECTION(client->userdata);
+    pa_native_connection_assert_ref(c);
+
+    if (c->version < 15)
+      return;
+
+    t = pa_tagstruct_new(NULL, 0);
+    pa_tagstruct_putu32(t, PA_COMMAND_CLIENT_EVENT);
+    pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
+    pa_tagstruct_puts(t, event);
+    pa_tagstruct_put_proplist(t, pl);
+    pa_pstream_send_tagstruct(c->pstream, t);
+}
+
 /*** module entry points ***/
 
 static void auth_timeout(pa_mainloop_api*m, pa_time_event *e, const struct timeval *tv, void *userdata) {
@@ -4262,6 +4355,7 @@ void pa_native_protocol_connect(pa_native_protocol *p, pa_iochannel *io, pa_nati
 
     c->client = client;
     c->client->kill = client_kill_cb;
+    c->client->send_event = client_send_event_cb;
     c->client->userdata = c;
 
     c->pstream = pa_pstream_new(p->core->mainloop, io, p->core->mempool);