core: sample_spec.rate is in Hz, not kHz; change logging output
[platform/upstream/pulseaudio.git] / src / pulsecore / sink-input.c
index 8f157ec..23b6b4b 100644 (file)
@@ -41,6 +41,8 @@
 
 #include "sink-input.h"
 
+/* #define SINK_INPUT_DEBUG */
+
 #define MEMBLOCKQ_MAXLENGTH (32*1024*1024)
 #define CONVERT_BUFFER_LENGTH (PA_PAGE_SIZE)
 
@@ -242,6 +244,7 @@ int pa_sink_input_new(
     pa_channel_map original_cm;
     int r;
     char *pt;
+    char *memblockq_name;
     pa_sample_spec ss;
     pa_channel_map map;
 
@@ -366,6 +369,19 @@ int pa_sink_input_new(
     pa_assert(pa_sample_spec_valid(&data->sample_spec));
     pa_assert(pa_channel_map_valid(&data->channel_map));
 
+    if (!(data->flags & PA_SINK_INPUT_VARIABLE_RATE) &&
+        !pa_sample_spec_equal(&data->sample_spec, &data->sink->sample_spec)) {
+        /* try to change sink rate. This is done before the FIXATE hook since
+           module-suspend-on-idle can resume a sink */
+
+        pa_log_info("Trying to change sample rate");
+        if (pa_sink_update_rate(data->sink, data->sample_spec.rate, pa_sink_input_new_data_is_passthrough(data)) == TRUE)
+            pa_log_info("Rate changed to %u Hz",
+                        data->sink->sample_spec.rate);
+        else
+            pa_log_info("Resampling enabled to %u Hz", data->sink->sample_spec.rate);
+    }
+
     /* Due to the fixing of the sample spec the volume might not match anymore */
     pa_cvolume_remap(&data->volume, &original_cm, &data->channel_map);
 
@@ -481,21 +497,24 @@ int pa_sink_input_new(
     i->thread_info.playing_for = 0;
     i->thread_info.direct_outputs = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
 
+    pa_assert_se(pa_idxset_put(core->sink_inputs, i, &i->index) == 0);
+    pa_assert_se(pa_idxset_put(i->sink->inputs, pa_sink_input_ref(i), NULL) == 0);
+
+    if (i->client)
+        pa_assert_se(pa_idxset_put(i->client->sink_inputs, i, NULL) >= 0);
+
+    memblockq_name = pa_sprintf_malloc("sink input render_memblockq [%u]", i->index);
     i->thread_info.render_memblockq = pa_memblockq_new(
+            memblockq_name,
             0,
             MEMBLOCKQ_MAXLENGTH,
             0,
-            pa_frame_size(&i->sink->sample_spec),
+            &i->sink->sample_spec,
             0,
             1,
             0,
             &i->sink->silence);
-
-    pa_assert_se(pa_idxset_put(core->sink_inputs, i, &i->index) == 0);
-    pa_assert_se(pa_idxset_put(i->sink->inputs, pa_sink_input_ref(i), NULL) == 0);
-
-    if (i->client)
-        pa_assert_se(pa_idxset_put(i->client->sink_inputs, i, NULL) >= 0);
+    pa_xfree(memblockq_name);
 
     pt = pa_proplist_to_string_sep(i->proplist, "\n    ");
     pa_log_info("Created input %u \"%s\" on %s with sample spec %s and channel map %s\n    %s",
@@ -539,6 +558,12 @@ static void sink_input_set_state(pa_sink_input *i, pa_sink_input_state_t state)
     if (i->state == state)
         return;
 
+    if (i->state == PA_SINK_INPUT_CORKED && state == PA_SINK_INPUT_RUNNING && pa_sink_used_by(i->sink) == 0) {
+        /* We were uncorked and the sink was not playing anything -- let's try
+         * to update the sample rate to avoid resampling */
+        pa_sink_update_rate(i->sink, i->sample_spec.rate, pa_sink_input_is_passthrough(i));
+    }
+
     pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_STATE, PA_UINT_TO_PTR(state), 0, NULL) == 0);
 
     update_n_corked(i, state);
@@ -768,7 +793,9 @@ void pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, p
     pa_assert(chunk);
     pa_assert(volume);
 
-/*     pa_log_debug("peek"); */
+#ifdef SINK_INPUT_DEBUG
+    pa_log_debug("peek");
+#endif
 
     block_size_max_sink_input = i->thread_info.resampler ?
         pa_resampler_max_block_size(i->thread_info.resampler) :
@@ -874,7 +901,9 @@ void pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, p
                 pa_memchunk rchunk;
                 pa_resampler_run(i->thread_info.resampler, &wchunk, &rchunk);
 
-/*                 pa_log_debug("pushing %lu", (unsigned long) rchunk.length); */
+#ifdef SINK_INPUT_DEBUG
+                pa_log_debug("pushing %lu", (unsigned long) rchunk.length);
+#endif
 
                 if (rchunk.memblock) {
 
@@ -902,7 +931,9 @@ void pa_sink_input_peek(pa_sink_input *i, size_t slength /* in sink frames */, p
     pa_assert(chunk->length > 0);
     pa_assert(chunk->memblock);
 
-/*     pa_log_debug("peeking %lu", (unsigned long) chunk->length); */
+#ifdef SINK_INPUT_DEBUG
+    pa_log_debug("peeking %lu", (unsigned long) chunk->length);
+#endif
 
     if (chunk->length > block_size_max_sink)
         chunk->length = block_size_max_sink;
@@ -929,7 +960,9 @@ void pa_sink_input_drop(pa_sink_input *i, size_t nbytes /* in sink sample spec *
     pa_assert(pa_frame_aligned(nbytes, &i->sink->sample_spec));
     pa_assert(nbytes > 0);
 
-/*     pa_log_debug("dropping %lu", (unsigned long) nbytes); */
+#ifdef SINK_INPUT_DEBUG
+    pa_log_debug("dropping %lu", (unsigned long) nbytes);
+#endif
 
     pa_memblockq_drop(i->thread_info.render_memblockq, nbytes);
 }
@@ -944,7 +977,9 @@ void pa_sink_input_process_rewind(pa_sink_input *i, size_t nbytes /* in sink sam
     pa_assert(PA_SINK_INPUT_IS_LINKED(i->thread_info.state));
     pa_assert(pa_frame_aligned(nbytes, &i->sink->sample_spec));
 
-/*     pa_log_debug("rewind(%lu, %lu)", (unsigned long) nbytes, (unsigned long) i->thread_info.rewrite_nbytes); */
+#ifdef SINK_INPUT_DEBUG
+    pa_log_debug("rewind(%lu, %lu)", (unsigned long) nbytes, (unsigned long) i->thread_info.rewrite_nbytes);
+#endif
 
     lbq = pa_memblockq_get_length(i->thread_info.render_memblockq);
 
@@ -1438,12 +1473,12 @@ static void update_volume_due_to_moving(pa_sink_input *i, pa_sink *dest) {
     pa_assert(i->sink); /* The destination sink should already be set. */
 
     if (i->origin_sink && (i->origin_sink->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER)) {
-        pa_sink *root_sink = i->sink;
+        pa_sink *root_sink = pa_sink_get_master(i->sink);
         pa_sink_input *origin_sink_input;
         uint32_t idx;
 
-        while (root_sink->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER)
-            root_sink = root_sink->input_to_master->sink;
+        if (PA_UNLIKELY(!root_sink))
+            return;
 
         if (pa_sink_flat_volume_enabled(i->sink)) {
             /* Ok, so the origin sink uses volume sharing, and flat volume is
@@ -1579,8 +1614,6 @@ static void update_volume_due_to_moving(pa_sink_input *i, pa_sink *dest) {
 
 /* Called from main context */
 int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, pa_bool_t save) {
-    pa_resampler *new_resampler;
-
     pa_sink_input_assert_ref(i);
     pa_assert_ctl_context();
     pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
@@ -1601,33 +1634,20 @@ int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, pa_bool_t save) {
         return -PA_ERR_NOTSUPPORTED;
     }
 
-    if (i->thread_info.resampler &&
-        pa_sample_spec_equal(pa_resampler_output_sample_spec(i->thread_info.resampler), &dest->sample_spec) &&
-        pa_channel_map_equal(pa_resampler_output_channel_map(i->thread_info.resampler), &dest->channel_map))
-
-        /* Try to reuse the old resampler if possible */
-        new_resampler = i->thread_info.resampler;
+    if (!(i->flags & PA_SINK_INPUT_VARIABLE_RATE) &&
+        !pa_sample_spec_equal(&i->sample_spec, &dest->sample_spec)) {
+        /* try to change dest sink rate if possible without glitches.
+           module-suspend-on-idle resumes destination sink with
+           SINK_INPUT_MOVE_FINISH hook */
 
-    else if (!pa_sink_input_is_passthrough(i) &&
-             ((i->flags & PA_SINK_INPUT_VARIABLE_RATE) ||
-              !pa_sample_spec_equal(&i->sample_spec, &dest->sample_spec) ||
-              !pa_channel_map_equal(&i->channel_map, &dest->channel_map))) {
-
-        /* Okay, we need a new resampler for the new sink */
-
-        if (!(new_resampler = pa_resampler_new(
-                      i->core->mempool,
-                      &i->sample_spec, &i->channel_map,
-                      &dest->sample_spec, &dest->channel_map,
-                      i->requested_resample_method,
-                      ((i->flags & PA_SINK_INPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) |
-                      ((i->flags & PA_SINK_INPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) |
-                      (i->core->disable_remixing || (i->flags & PA_SINK_INPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0)))) {
-            pa_log_warn("Unsupported resampling operation.");
-            return -PA_ERR_NOTSUPPORTED;
-        }
-    } else
-        new_resampler = NULL;
+        pa_log_info("Trying to change sample rate");
+        if (pa_sink_update_rate(dest, i->sample_spec.rate, pa_sink_input_is_passthrough(i)) == TRUE)
+            pa_log_info("Rate changed to %u Hz",
+                        dest->sample_spec.rate);
+        else
+            pa_log_info("Resampling enabled to %u Hz",
+                        dest->sample_spec.rate);
+    }
 
     if (i->moving)
         i->moving(i, dest);
@@ -1641,26 +1661,7 @@ int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, pa_bool_t save) {
     if (pa_sink_input_get_state(i) == PA_SINK_INPUT_CORKED)
         i->sink->n_corked++;
 
-    /* Replace resampler and render queue */
-    if (new_resampler != i->thread_info.resampler) {
-
-        if (i->thread_info.resampler)
-            pa_resampler_free(i->thread_info.resampler);
-        i->thread_info.resampler = new_resampler;
-
-        pa_memblockq_free(i->thread_info.render_memblockq);
-
-        i->thread_info.render_memblockq = pa_memblockq_new(
-                0,
-                MEMBLOCKQ_MAXLENGTH,
-                0,
-                pa_frame_size(&i->sink->sample_spec),
-                0,
-                1,
-                0,
-                &i->sink->silence);
-        i->actual_resample_method = new_resampler ? pa_resampler_get_method(new_resampler) : PA_RESAMPLER_INVALID;
-    }
+    pa_sink_input_update_rate(i);
 
     pa_sink_update_status(dest);
 
@@ -1907,7 +1908,9 @@ void pa_sink_input_request_rewind(
 
     nbytes = PA_MAX(i->thread_info.rewrite_nbytes, nbytes);
 
-    /* pa_log_debug("request rewrite %zu", nbytes); */
+#ifdef SINK_INPUT_DEBUG
+    pa_log_debug("request rewrite %zu", nbytes);
+#endif
 
     /* Calculate how much we can rewind locally without having to
      * touch the sink */
@@ -2007,3 +2010,69 @@ finish:
     if (pl)
         pa_proplist_free(pl);
 }
+
+/* Called from main context */
+/* Updates the sink input's resampler with whatever the current sink requires
+ * -- useful when the underlying sink's rate might have changed */
+int pa_sink_input_update_rate(pa_sink_input *i) {
+    pa_resampler *new_resampler;
+    char *memblockq_name;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert_ctl_context();
+
+    if (i->thread_info.resampler &&
+        pa_sample_spec_equal(pa_resampler_output_sample_spec(i->thread_info.resampler), &i->sink->sample_spec) &&
+        pa_channel_map_equal(pa_resampler_output_channel_map(i->thread_info.resampler), &i->sink->channel_map))
+
+        new_resampler = i->thread_info.resampler;
+
+    else if (!pa_sink_input_is_passthrough(i) &&
+        ((i->flags & PA_SINK_INPUT_VARIABLE_RATE) ||
+         !pa_sample_spec_equal(&i->sample_spec, &i->sink->sample_spec) ||
+         !pa_channel_map_equal(&i->channel_map, &i->sink->channel_map))) {
+
+        new_resampler = pa_resampler_new(i->core->mempool,
+                                     &i->sample_spec, &i->channel_map,
+                                     &i->sink->sample_spec, &i->sink->channel_map,
+                                     i->requested_resample_method,
+                                     ((i->flags & PA_SINK_INPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) |
+                                     ((i->flags & PA_SINK_INPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) |
+                                     (i->core->disable_remixing || (i->flags & PA_SINK_INPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0));
+
+        if (!new_resampler) {
+            pa_log_warn("Unsupported resampling operation.");
+            return -PA_ERR_NOTSUPPORTED;
+        }
+    } else
+        new_resampler = NULL;
+
+    if (new_resampler == i->thread_info.resampler)
+        return 0;
+
+    if (i->thread_info.resampler)
+        pa_resampler_free(i->thread_info.resampler);
+
+    i->thread_info.resampler = new_resampler;
+
+    pa_memblockq_free(i->thread_info.render_memblockq);
+
+    memblockq_name = pa_sprintf_malloc("sink input render_memblockq [%u]", i->index);
+    i->thread_info.render_memblockq = pa_memblockq_new(
+            memblockq_name,
+            0,
+            MEMBLOCKQ_MAXLENGTH,
+            0,
+            &i->sink->sample_spec,
+            0,
+            1,
+            0,
+            &i->sink->silence);
+    pa_xfree(memblockq_name);
+
+    i->actual_resample_method = new_resampler ? pa_resampler_get_method(new_resampler) : PA_RESAMPLER_INVALID;
+
+    pa_log_debug("Updated resmpler for sink input %d", i->index);
+
+    return 0;
+}