]> code.delx.au - pulseaudio/blobdiff - src/modules/module-combine-sink.c
combine-sink: fix zero adjust_time behaviour.
[pulseaudio] / src / modules / module-combine-sink.c
index f6d645312120972372622d19d1b79017568359cb..ccb2a493a36287f51f5b72a5fc67a7e0b345c2fc 100644 (file)
 #include <pulsecore/core-util.h>
 #include <pulsecore/modargs.h>
 #include <pulsecore/namereg.h>
-#include <pulsecore/mutex.h>
 #include <pulsecore/thread.h>
 #include <pulsecore/thread-mq.h>
 #include <pulsecore/rtpoll.h>
-#include <pulsecore/core-error.h>
 #include <pulsecore/time-smoother.h>
 #include <pulsecore/strlist.h>
 
@@ -54,7 +52,7 @@
 PA_MODULE_AUTHOR("Lennart Poettering");
 PA_MODULE_DESCRIPTION("Combine multiple sinks to one");
 PA_MODULE_VERSION(PACKAGE_VERSION);
-PA_MODULE_LOAD_ONCE(FALSE);
+PA_MODULE_LOAD_ONCE(false);
 PA_MODULE_USAGE(
         "sink_name=<name for the sink> "
         "sink_properties=<properties for the sink> "
@@ -92,7 +90,7 @@ struct output {
 
     pa_sink *sink;
     pa_sink_input *sink_input;
-    pa_bool_t ignore_state_change;
+    bool ignore_state_change;
 
     pa_asyncmsgq *inq,    /* Message queue from the sink thread to this sink input */
                  *outq;   /* Message queue from this sink input to the sink thread */
@@ -104,7 +102,7 @@ struct output {
     /* For communication of the stream latencies to the main thread */
     pa_usec_t total_latency;
 
-    /* For coomunication of the stream parameters to the sink thread */
+    /* For communication of the stream parameters to the sink thread */
     pa_atomic_t max_request;
     pa_atomic_t requested_latency;
 
@@ -123,8 +121,8 @@ struct userdata {
     pa_time_event *time_event;
     pa_usec_t adjust_time;
 
-    pa_bool_t automatic;
-    pa_bool_t auto_desc;
+    bool automatic;
+    bool auto_desc;
 
     pa_strlist *unlinked_slaves;
 
@@ -140,7 +138,7 @@ struct userdata {
         PA_LLIST_HEAD(struct output, active_outputs); /* managed in IO thread context */
         pa_atomic_t running;  /* we cache that value here, so that every thread can query it cheaply */
         pa_usec_t timestamp;
-        pa_bool_t in_null_mode;
+        bool in_null_mode;
         pa_smoother *smoother;
         uint64_t counter;
     } thread_info;
@@ -218,11 +216,13 @@ static void adjust_rates(struct userdata *u) {
 
     PA_IDXSET_FOREACH(o, u->outputs, idx) {
         uint32_t new_rate = base_rate;
-        uint32_t current_rate = o->sink_input->sample_spec.rate;
+        uint32_t current_rate;
 
         if (!o->sink_input || !PA_SINK_IS_OPENED(pa_sink_get_state(o->sink)))
             continue;
 
+        current_rate = o->sink_input->sample_spec.rate;
+
         if (o->total_latency != target_latency)
             new_rate += (uint32_t) (((double) o->total_latency - (double) target_latency) / (double) u->adjust_time * (double) new_rate);
 
@@ -254,12 +254,18 @@ static void time_callback(pa_mainloop_api *a, pa_time_event *e, const struct tim
 
     adjust_rates(u);
 
-    pa_core_rttime_restart(u->core, e, pa_rtclock_now() + u->adjust_time);
+    if (pa_sink_get_state(u->sink) == PA_SINK_SUSPENDED) {
+        u->core->mainloop->time_free(e);
+        u->time_event = NULL;
+    } else
+        pa_core_rttime_restart(u->core, e, pa_rtclock_now() + u->adjust_time);
 }
 
 static void process_render_null(struct userdata *u, pa_usec_t now) {
     size_t ate = 0;
+
     pa_assert(u);
+    pa_assert(u->sink->thread_info.state == PA_SINK_RUNNING);
 
     if (u->thread_info.in_null_mode)
         u->thread_info.timestamp = now;
@@ -300,17 +306,16 @@ static void thread_func(void *userdata) {
     pa_thread_mq_install(&u->thread_mq);
 
     u->thread_info.timestamp = pa_rtclock_now();
-    u->thread_info.in_null_mode = FALSE;
+    u->thread_info.in_null_mode = false;
 
     for (;;) {
         int ret;
 
-        if (PA_SINK_IS_OPENED(u->sink->thread_info.state))
-            if (u->sink->thread_info.rewind_requested)
-                pa_sink_process_rewind(u->sink, 0);
+        if (PA_UNLIKELY(u->sink->thread_info.rewind_requested))
+            pa_sink_process_rewind(u->sink, 0);
 
         /* If no outputs are connected, render some data and drop it immediately. */
-        if (PA_SINK_IS_OPENED(u->sink->thread_info.state) && !u->thread_info.active_outputs) {
+        if (u->sink->thread_info.state == PA_SINK_RUNNING && !u->thread_info.active_outputs) {
             pa_usec_t now;
 
             now = pa_rtclock_now();
@@ -319,14 +324,14 @@ static void thread_func(void *userdata) {
                 process_render_null(u, now);
 
             pa_rtpoll_set_timer_absolute(u->rtpoll, u->thread_info.timestamp);
-            u->thread_info.in_null_mode = TRUE;
+            u->thread_info.in_null_mode = true;
         } else {
             pa_rtpoll_set_timer_disabled(u->rtpoll);
-            u->thread_info.in_null_mode = FALSE;
+            u->thread_info.in_null_mode = false;
         }
 
         /* Hmm, nothing to do. Let's sleep */
-        if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0) {
+        if ((ret = pa_rtpoll_run(u->rtpoll, true)) < 0) {
             pa_log_info("pa_rtpoll_run() = %i", ret);
             goto fail;
         }
@@ -510,7 +515,7 @@ static void sink_input_attach_cb(pa_sink_input *i) {
             PA_RTPOLL_EARLY,
             o->outq);
 
-    pa_sink_input_request_rewind(i, 0, FALSE, TRUE, TRUE);
+    pa_sink_input_request_rewind(i, 0, false, true, true);
 
     pa_atomic_store(&o->max_request, (int) pa_sink_input_get_max_request(i));
 
@@ -546,7 +551,8 @@ static void sink_input_kill_cb(pa_sink_input *i) {
     pa_sink_input_assert_ref(i);
     pa_assert_se(o = i->userdata);
 
-    pa_module_unload_request(o->userdata->module, TRUE);
+    pa_module_unload_request(o->userdata->module, true);
+    pa_idxset_remove_by_data(o->userdata->outputs, o, NULL);
     output_free(o);
 }
 
@@ -571,7 +577,7 @@ static int sink_input_process_msg(pa_msgobject *obj, int code, void *data, int64
             if (PA_SINK_IS_OPENED(o->sink_input->sink->thread_info.state))
                 pa_memblockq_push_align(o->memblockq, chunk);
             else
-                pa_memblockq_flush_write(o->memblockq, TRUE);
+                pa_memblockq_flush_write(o->memblockq, true);
 
             return 0;
     }
@@ -604,6 +610,9 @@ static void unsuspend(struct userdata *u) {
     PA_IDXSET_FOREACH(o, u->outputs, idx)
         output_enable(o);
 
+    if (!u->time_event && u->adjust_time > 0)
+        u->time_event = pa_core_rttime_new(u->core, pa_rtclock_now() + u->adjust_time, time_callback, u);
+
     pa_log_info("Resumed successfully...");
 }
 
@@ -732,15 +741,18 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
 
     switch (code) {
 
-        case PA_SINK_MESSAGE_SET_STATE:
-            pa_atomic_store(&u->thread_info.running, PA_PTR_TO_UINT(data) == PA_SINK_RUNNING);
+        case PA_SINK_MESSAGE_SET_STATE: {
+            bool running = (PA_PTR_TO_UINT(data) == PA_SINK_RUNNING);
 
-            if (PA_PTR_TO_UINT(data) == PA_SINK_SUSPENDED)
-                pa_smoother_pause(u->thread_info.smoother, pa_rtclock_now());
+            pa_atomic_store(&u->thread_info.running, running);
+
+            if (running)
+                pa_smoother_resume(u->thread_info.smoother, pa_rtclock_now(), true);
             else
-                pa_smoother_resume(u->thread_info.smoother, pa_rtclock_now(), TRUE);
+                pa_smoother_pause(u->thread_info.smoother, pa_rtclock_now());
 
             break;
+        }
 
         case PA_SINK_MESSAGE_GET_LATENCY: {
             pa_usec_t x, y, c, *delay = data;
@@ -802,7 +814,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
 }
 
 static void update_description(struct userdata *u) {
-    pa_bool_t first = TRUE;
+    bool first = true;
     char *t;
     struct output *o;
     uint32_t idx;
@@ -824,7 +836,7 @@ static void update_description(struct userdata *u) {
 
         if (first) {
             e = pa_sprintf_malloc("%s %s", t, pa_strnull(pa_proplist_gets(o->sink->proplist, PA_PROP_DEVICE_DESCRIPTION)));
-            first = FALSE;
+            first = false;
         } else
             e = pa_sprintf_malloc("%s, %s", t, pa_strnull(pa_proplist_gets(o->sink->proplist, PA_PROP_DEVICE_DESCRIPTION)));
 
@@ -845,7 +857,7 @@ static int output_create_sink_input(struct output *o) {
         return 0;
 
     pa_sink_input_new_data_init(&data);
-    pa_sink_input_new_data_set_sink(&data, o->sink, FALSE);
+    pa_sink_input_new_data_set_sink(&data, o->sink, false);
     data.driver = __FILE__;
     pa_proplist_setf(data.proplist, PA_PROP_MEDIA_NAME, "Simultaneous output on %s", pa_strnull(pa_proplist_gets(o->sink->proplist, PA_PROP_DEVICE_DESCRIPTION)));
     pa_proplist_sets(data.proplist, PA_PROP_MEDIA_ROLE, "filter");
@@ -892,10 +904,11 @@ static struct output *output_new(struct userdata *u, pa_sink *sink) {
     o->outq = pa_asyncmsgq_new(0);
     o->sink = sink;
     o->memblockq = pa_memblockq_new(
+            "module-combine-sink output memblockq",
             0,
             MEMBLOCKQ_MAXLENGTH,
             MEMBLOCKQ_MAXLENGTH,
-            pa_frame_size(&u->sink->sample_spec),
+            &u->sink->sample_spec,
             1,
             0,
             0,
@@ -912,8 +925,6 @@ static void output_free(struct output *o) {
     pa_assert(o);
 
     output_disable(o);
-
-    pa_assert_se(pa_idxset_remove_by_data(o->userdata->outputs, o, NULL));
     update_description(o->userdata);
 
     if (o->inq_rtpoll_item_read)
@@ -949,7 +960,7 @@ static void output_enable(struct output *o) {
      * of the sink might hence be called from here, which might then
      * cause us to be called in a loop. Make sure that state changes
      * for this output don't cause this loop by setting a flag here */
-    o->ignore_state_change = TRUE;
+    o->ignore_state_change = true;
 
     if (output_create_sink_input(o) >= 0) {
 
@@ -968,7 +979,7 @@ static void output_enable(struct output *o) {
             output_add_within_thread(o);
     }
 
-    o->ignore_state_change = FALSE;
+    o->ignore_state_change = false;
 }
 
 /* Called from main context */
@@ -986,14 +997,14 @@ static void output_disable(struct output *o) {
      * pass any further data to this output */
     pa_asyncmsgq_send(o->userdata->sink->asyncmsgq, PA_MSGOBJECT(o->userdata->sink), SINK_MESSAGE_REMOVE_OUTPUT, o, 0, NULL);
 
-    /* Now dellocate the stream */
+    /* Now deallocate the stream */
     pa_sink_input_unref(o->sink_input);
     o->sink_input = NULL;
 
     /* Finally, drop all queued data */
-    pa_memblockq_flush_write(o->memblockq, TRUE);
-    pa_asyncmsgq_flush(o->inq, FALSE);
-    pa_asyncmsgq_flush(o->outq, FALSE);
+    pa_memblockq_flush_write(o->memblockq, true);
+    pa_asyncmsgq_flush(o->inq, false);
+    pa_asyncmsgq_flush(o->outq, false);
 }
 
 /* Called from main context */
@@ -1007,25 +1018,25 @@ static void output_verify(struct output *o) {
 }
 
 /* Called from main context */
-static pa_bool_t is_suitable_sink(struct userdata *u, pa_sink *s) {
+static bool is_suitable_sink(struct userdata *u, pa_sink *s) {
     const char *t;
 
     pa_sink_assert_ref(s);
 
     if (s == u->sink)
-        return FALSE;
+        return false;
 
     if (!(s->flags & PA_SINK_HARDWARE))
-        return FALSE;
+        return false;
 
     if (!(s->flags & PA_SINK_LATENCY))
-        return FALSE;
+        return false;
 
     if ((t = pa_proplist_gets(s->proplist, PA_PROP_DEVICE_CLASS)))
         if (!pa_streq(t, "sound"))
-            return FALSE;
+            return false;
 
-    return TRUE;
+    return true;
 }
 
 /* Called from main context */
@@ -1097,6 +1108,7 @@ static pa_hook_result_t sink_unlink_hook_cb(pa_core *c, pa_sink *s, struct userd
     if (!u->automatic)
         u->unlinked_slaves = pa_strlist_prepend(u->unlinked_slaves, s->name);
 
+    pa_idxset_remove_by_data(u->outputs, o, NULL);
     output_free(o);
 
     return PA_HOOK_OK;
@@ -1156,11 +1168,11 @@ int pa__init(pa_module*m) {
     u->thread_info.smoother = pa_smoother_new(
             PA_USEC_PER_SEC,
             PA_USEC_PER_SEC*2,
-            TRUE,
-            TRUE,
+            true,
+            true,
             10,
-            0,
-            FALSE);
+            pa_rtclock_now(),
+            true);
 
     adjust_time_sec = DEFAULT_ADJUST_TIME_USEC / PA_USEC_PER_SEC;
     if (pa_modargs_get_value_u32(ma, "adjust_time", &adjust_time_sec) < 0) {
@@ -1185,7 +1197,7 @@ int pa__init(pa_module*m) {
         char *n = NULL;
         pa_sample_spec slaves_spec;
         pa_channel_map slaves_map;
-        pa_bool_t is_first_slave = TRUE;
+        bool is_first_slave = true;
 
         pa_sample_spec_init(&slaves_spec);
 
@@ -1203,7 +1215,7 @@ int pa__init(pa_module*m) {
             if (is_first_slave) {
                 slaves_spec = slave_sink->sample_spec;
                 slaves_map = slave_sink->channel_map;
-                is_first_slave = FALSE;
+                is_first_slave = false;
             } else {
                 if (slaves_spec.format != slave_sink->sample_spec.format)
                     slaves_spec.format = PA_SAMPLE_INVALID;
@@ -1235,7 +1247,7 @@ int pa__init(pa_module*m) {
     }
 
     pa_sink_new_data_init(&data);
-    data.namereg_fail = FALSE;
+    data.namereg_fail = false;
     data.driver = __FILE__;
     data.module = m;
     pa_sink_new_data_set_name(&data, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME));
@@ -1253,9 +1265,9 @@ int pa__init(pa_module*m) {
     }
 
     /* Check proplist for a description & fill in a default value if not */
-    u->auto_desc = FALSE;
+    u->auto_desc = false;
     if (NULL == pa_proplist_gets(data.proplist, PA_PROP_DEVICE_DESCRIPTION)) {
-        u->auto_desc = TRUE;
+        u->auto_desc = true;
         pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Simultaneous Output");
     }
 
@@ -1358,7 +1370,6 @@ fail:
 
 void pa__done(pa_module*m) {
     struct userdata *u;
-    struct output *o;
 
     pa_assert(m);
 
@@ -1376,12 +1387,8 @@ void pa__done(pa_module*m) {
     if (u->sink_state_changed_slot)
         pa_hook_slot_free(u->sink_state_changed_slot);
 
-    if (u->outputs) {
-        while ((o = pa_idxset_first(u->outputs, NULL)))
-            output_free(o);
-
-        pa_idxset_free(u->outputs, NULL, NULL);
-    }
+    if (u->outputs)
+        pa_idxset_free(u->outputs, (pa_free_cb_t) output_free);
 
     if (u->sink)
         pa_sink_unlink(u->sink);