sink-input: adjust log level of empty-pop operations
[platform/upstream/pulseaudio.git] / src / pulsecore / sink.c
index 50e17c1..ae3be39 100644 (file)
@@ -25,6 +25,9 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#ifdef TIZEN_PCM_DUMP
+#include <time.h>
+#endif
 
 #include <pulse/introspect.h>
 #include <pulse/format.h>
@@ -46,6 +49,9 @@
 #include <pulsecore/macro.h>
 #include <pulsecore/play-memblockq.h>
 #include <pulsecore/flist.h>
+#ifdef __TIZEN__
+#include <pulsecore/proplist-util.h>
+#endif
 
 #include "sink.h"
 
@@ -64,9 +70,9 @@ struct pa_sink_volume_change {
     PA_LLIST_FIELDS(pa_sink_volume_change);
 };
 
-struct sink_message_set_port {
-    pa_device_port *port;
-    int ret;
+struct set_state_data {
+    pa_sink_state_t state;
+    pa_suspend_cause_t suspend_cause;
 };
 
 static void sink_free(pa_object *s);
@@ -75,6 +81,69 @@ static void pa_sink_volume_change_push(pa_sink *s);
 static void pa_sink_volume_change_flush(pa_sink *s);
 static void pa_sink_volume_change_rewind(pa_sink *s, size_t nbytes);
 
+#ifdef TIZEN_PCM_DUMP
+static void pa_sink_write_pcm_dump(pa_sink *s, pa_memchunk *chunk)
+{
+    char *dump_time = NULL, *dump_path_surfix = NULL;
+    const char *s_device_api_str, *card_name_str, *device_idx_str;
+    struct timeval now;
+    struct tm tm;
+    char datetime[7];
+
+    /* open file for dump pcm */
+    if (s->core->pcm_dump & PA_PCM_DUMP_SINK && !s->pcm_dump_fp && s->state == PA_SINK_RUNNING) {
+        pa_gettimeofday(&now);
+        localtime_r(&now.tv_sec, &tm);
+        memset(&datetime[0], 0x00, sizeof(datetime));
+        strftime(&datetime[0], sizeof(datetime), "%H%M%S", &tm);
+        dump_time = pa_sprintf_malloc("%s.%03ld", &datetime[0], now.tv_usec / 1000);
+
+        if ((s_device_api_str = pa_proplist_gets(s->proplist, PA_PROP_DEVICE_API))) {
+            if (pa_streq(s_device_api_str, "alsa")) {
+                card_name_str = pa_proplist_gets(s->proplist, "alsa.card_name");
+                device_idx_str = pa_proplist_gets(s->proplist, "alsa.device");
+                dump_path_surfix = pa_sprintf_malloc("%s.%s", pa_strnull(card_name_str), pa_strnull(device_idx_str));
+            } else {
+                dump_path_surfix = pa_sprintf_malloc("%s", s_device_api_str);
+            }
+        } else {
+            dump_path_surfix = pa_sprintf_malloc("%s", s->name);
+        }
+
+        s->dump_path = pa_sprintf_malloc("%s_%s_pa-sink%d-%s_%dch_%d.raw", PA_PCM_DUMP_PATH_PREFIX, pa_strempty(dump_time),
+            s->index, pa_strempty(dump_path_surfix), s->sample_spec.channels, s->sample_spec.rate);
+        if (s->dump_path) {
+            s->pcm_dump_fp = fopen(s->dump_path, "w");
+            if (!s->pcm_dump_fp)
+                pa_log_warn("%s open failed", s->dump_path);
+            else
+                pa_log_info("%s opened", s->dump_path);
+        }
+        pa_xfree(dump_time);
+        pa_xfree(dump_path_surfix);
+    /* close file for dump pcm when config is changed */
+    } else if (~s->core->pcm_dump & PA_PCM_DUMP_SINK && s->pcm_dump_fp) {
+        fclose(s->pcm_dump_fp);
+        pa_log_info("%s closed", s->dump_path);
+        pa_xfree(s->dump_path);
+        s->pcm_dump_fp = NULL;
+    }
+
+    /* dump pcm */
+    if (s->pcm_dump_fp) {
+        void *ptr = NULL;
+
+        ptr = pa_memblock_acquire(chunk->memblock);
+        if (ptr)
+            fwrite((uint8_t *)ptr + chunk->index, 1, chunk->length, s->pcm_dump_fp);
+        else
+            pa_log_warn("pa_memblock_acquire is failed. ptr is NULL");
+
+        pa_memblock_release(chunk->memblock);
+    }
+}
+#endif
+
 pa_sink_new_data* pa_sink_new_data_init(pa_sink_new_data *data) {
     pa_assert(data);
 
@@ -113,6 +182,13 @@ void pa_sink_new_data_set_alternate_sample_rate(pa_sink_new_data *data, const ui
     data->alternate_sample_rate = alternate_sample_rate;
 }
 
+void pa_sink_new_data_set_avoid_resampling(pa_sink_new_data *data, bool avoid_resampling) {
+    pa_assert(data);
+
+    data->avoid_resampling_is_set = true;
+    data->avoid_resampling = avoid_resampling;
+}
+
 void pa_sink_new_data_set_volume(pa_sink_new_data *data, const pa_cvolume *volume) {
     pa_assert(data);
 
@@ -150,7 +226,8 @@ void pa_sink_new_data_done(pa_sink_new_data *data) {
 static void reset_callbacks(pa_sink *s) {
     pa_assert(s);
 
-    s->set_state = NULL;
+    s->set_state_in_main_thread = NULL;
+    s->set_state_in_io_thread = NULL;
     s->get_volume = NULL;
     s->set_volume = NULL;
     s->write_volume = NULL;
@@ -253,7 +330,6 @@ pa_sink* pa_sink_new(
     s->flags = flags;
     s->priority = 0;
     s->suspend_cause = data->suspend_cause;
-    pa_sink_set_mixer_dirty(s, false);
     s->name = pa_xstrdup(name);
     s->proplist = pa_proplist_copy(data->proplist);
     s->driver = pa_xstrdup(pa_path_get_filename(data->driver));
@@ -271,10 +347,14 @@ pa_sink* pa_sink_new(
     else
         s->alternate_sample_rate = s->core->alternate_sample_rate;
 
-    if (s->sample_spec.rate == s->alternate_sample_rate) {
-        pa_log_warn("Default and alternate sample rates are the same.");
-        s->alternate_sample_rate = 0;
-    }
+    if (data->avoid_resampling_is_set)
+        s->avoid_resampling = data->avoid_resampling;
+    else
+        s->avoid_resampling = s->core->avoid_resampling;
+#ifdef __TIZEN__
+    s->selected_sample_format = s->sample_spec.format;
+    s->selected_sample_rate = s->sample_spec.rate;
+#endif
 
     s->inputs = pa_idxset_new(NULL, NULL);
     s->n_corked = 0;
@@ -316,6 +396,10 @@ pa_sink* pa_sink_new(
 
     s->save_volume = data->save_volume;
     s->save_muted = data->save_muted;
+#ifdef TIZEN_PCM_DUMP
+    s->pcm_dump_fp = NULL;
+    s->dump_path = NULL;
+#endif
 
     pa_silence_memchunk_get(
             &core->silence_cache,
@@ -366,6 +450,7 @@ pa_sink* pa_sink_new(
     pa_source_new_data_set_sample_spec(&source_data, &s->sample_spec);
     pa_source_new_data_set_channel_map(&source_data, &s->channel_map);
     pa_source_new_data_set_alternate_sample_rate(&source_data, s->alternate_sample_rate);
+    pa_source_new_data_set_avoid_resampling(&source_data, s->avoid_resampling);
     source_data.name = pa_sprintf_malloc("%s.monitor", name);
     source_data.driver = data->driver;
     source_data.module = data->module;
@@ -403,6 +488,8 @@ static int sink_set_state(pa_sink *s, pa_sink_state_t state, pa_suspend_cause_t
     bool suspend_cause_changed;
     bool suspending;
     bool resuming;
+    pa_sink_state_t old_state;
+    pa_suspend_cause_t old_suspend_cause;
 
     pa_assert(s);
     pa_assert_ctl_context();
@@ -427,21 +514,61 @@ static int sink_set_state(pa_sink *s, pa_sink_state_t state, pa_suspend_cause_t
      * cause, or it might just add unnecessary complexity, given that the
      * current approach of not setting any suspend cause works well enough. */
 
-    if (s->set_state && state_changed) {
-        ret = s->set_state(s, state);
-        /* set_state() is allowed to fail only when resuming. */
-        pa_assert(ret >= 0 || resuming);
+    if (s->set_state_in_main_thread) {
+        if ((ret = s->set_state_in_main_thread(s, state, suspend_cause)) < 0) {
+            /* set_state_in_main_thread() is allowed to fail only when resuming. */
+            pa_assert(resuming);
+
+            /* If resuming fails, we set the state to SUSPENDED and
+             * suspend_cause to 0. */
+            state = PA_SINK_SUSPENDED;
+            suspend_cause = 0;
+            state_changed = false;
+            suspend_cause_changed = suspend_cause != s->suspend_cause;
+            resuming = false;
+
+            /* We know the state isn't changing. If the suspend cause isn't
+             * changing either, then there's nothing more to do. */
+            if (!suspend_cause_changed)
+                return ret;
+        }
     }
 
-    if (ret >= 0 && s->asyncmsgq && state_changed)
-        if ((ret = pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_STATE, PA_UINT_TO_PTR(state), 0, NULL)) < 0) {
+    if (s->asyncmsgq) {
+        struct set_state_data data = { .state = state, .suspend_cause = suspend_cause };
+
+        if ((ret = pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_STATE, &data, 0, NULL)) < 0) {
             /* SET_STATE is allowed to fail only when resuming. */
             pa_assert(resuming);
 
-            if (s->set_state)
-                s->set_state(s, PA_SINK_SUSPENDED);
+            if (s->set_state_in_main_thread)
+                s->set_state_in_main_thread(s, PA_SINK_SUSPENDED, 0);
+
+            /* If resuming fails, we set the state to SUSPENDED and
+             * suspend_cause to 0. */
+            state = PA_SINK_SUSPENDED;
+            suspend_cause = 0;
+            state_changed = false;
+            suspend_cause_changed = suspend_cause != s->suspend_cause;
+            resuming = false;
+
+            /* We know the state isn't changing. If the suspend cause isn't
+             * changing either, then there's nothing more to do. */
+            if (!suspend_cause_changed)
+                return ret;
         }
+    }
 
+#ifdef TIZEN_PCM_DUMP
+    /* close file for dump pcm */
+    if (s->pcm_dump_fp && (s->core->pcm_dump & PA_PCM_DUMP_SEPARATED) && suspending) {
+        fclose(s->pcm_dump_fp);
+        pa_log_info("%s closed", s->dump_path);
+        pa_xfree(s->dump_path);
+        s->pcm_dump_fp = NULL;
+    }
+#endif
+    old_suspend_cause = s->suspend_cause;
     if (suspend_cause_changed) {
         char old_cause_buf[PA_SUSPEND_CAUSE_TO_STRING_BUF_SIZE];
         char new_cause_buf[PA_SUSPEND_CAUSE_TO_STRING_BUF_SIZE];
@@ -451,9 +578,7 @@ static int sink_set_state(pa_sink *s, pa_sink_state_t state, pa_suspend_cause_t
         s->suspend_cause = suspend_cause;
     }
 
-    if (ret < 0)
-        goto finish;
-
+    old_state = s->state;
     if (state_changed) {
         pa_log_debug("%s: state: %s -> %s", s->name, pa_sink_state_to_string(s->state), pa_sink_state_to_string(state));
         s->state = state;
@@ -466,7 +591,7 @@ static int sink_set_state(pa_sink *s, pa_sink_state_t state, pa_suspend_cause_t
         }
     }
 
-    if (suspending || resuming) {
+    if (suspending || resuming || suspend_cause_changed) {
         pa_sink_input *i;
         uint32_t idx;
 
@@ -477,11 +602,10 @@ static int sink_set_state(pa_sink *s, pa_sink_state_t state, pa_suspend_cause_t
                 (i->flags & PA_SINK_INPUT_KILL_ON_SUSPEND))
                 pa_sink_input_kill(i);
             else if (i->suspend)
-                i->suspend(i, state == PA_SINK_SUSPENDED);
+                i->suspend(i, old_state, old_suspend_cause);
     }
 
-finish:
-    if ((suspending || resuming || suspend_cause_changed) && s->monitor_source)
+    if ((suspending || resuming || suspend_cause_changed) && s->monitor_source && state != PA_SINK_UNLINKED)
         pa_source_sync_suspend(s->monitor_source);
 
     return ret;
@@ -666,8 +790,8 @@ void pa_sink_put(pa_sink* s) {
         pa_cvolume_remap(&s->real_volume, &root_sink->channel_map, &s->channel_map);
     } else
         /* We assume that if the sink implementor changed the default
-         * volume he did so in real_volume, because that is the usual
-         * place where he is supposed to place his changes.  */
+         * volume they did so in real_volume, because that is the usual
+         * place where they are supposed to place their changes.  */
         s->reference_volume = s->real_volume;
 
     s->thread_info.soft_volume = s->soft_volume;
@@ -696,9 +820,14 @@ void pa_sink_put(pa_sink* s) {
     pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_NEW, s->index);
     pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_PUT], s);
 
-    /* This function must be called after the PA_CORE_HOOK_SINK_PUT hook,
-     * because module-switch-on-connect needs to know the old default sink */
+    /* It's good to fire the SINK_PUT hook before updating the default sink,
+     * because module-switch-on-connect will set the new sink as the default
+     * sink, and if we were to call pa_core_update_default_sink() before that,
+     * the default sink might change twice, causing unnecessary stream moving. */
+
     pa_core_update_default_sink(s->core);
+
+    pa_core_move_streams_to_newly_available_preferred_sink(s->core, s);
 }
 
 /* Called from main context */
@@ -729,6 +858,9 @@ void pa_sink_unlink(pa_sink* s) {
 
     pa_core_update_default_sink(s->core);
 
+    if (linked && s->core->rescue_streams)
+       pa_sink_move_streams_to_default_sink(s->core, s, false);
+
     if (s->card)
         pa_idxset_remove_by_data(s->card->sinks, s, NULL);
 
@@ -738,16 +870,21 @@ void pa_sink_unlink(pa_sink* s) {
         j = i;
     }
 
+    /* Unlink monitor source before unlinking the sink */
+    if (s->monitor_source)
+        pa_source_unlink(s->monitor_source);
+
     if (linked)
-        sink_set_state(s, PA_SINK_UNLINKED, 0);
+        /* It's important to keep the suspend cause unchanged when unlinking,
+         * because if we remove the SESSION suspend cause here, the alsa sink
+         * will sync its volume with the hardware while another user is
+         * active, messing up the volume for that other user. */
+        sink_set_state(s, PA_SINK_UNLINKED, s->suspend_cause);
     else
         s->state = PA_SINK_UNLINKED;
 
     reset_callbacks(s);
 
-    if (s->monitor_source)
-        pa_source_unlink(s->monitor_source);
-
     if (linked) {
         pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_REMOVE, s->index);
         pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_UNLINK_POST], s);
@@ -787,6 +924,15 @@ static void sink_free(pa_object *o) {
     if (s->ports)
         pa_hashmap_free(s->ports);
 
+#ifdef TIZEN_PCM_DUMP
+    /* close file for dump pcm */
+    if (s->pcm_dump_fp) {
+        fclose(s->pcm_dump_fp);
+        pa_log_info("%s closed", s->dump_path);
+        pa_xfree(s->dump_path);
+        s->pcm_dump_fp = NULL;
+    }
+#endif
     pa_xfree(s);
 }
 
@@ -865,11 +1011,6 @@ int pa_sink_update_status(pa_sink*s) {
     return sink_set_state(s, pa_sink_used_by(s) ? PA_SINK_RUNNING : PA_SINK_IDLE, 0);
 }
 
-/* Called from any context - must be threadsafe */
-void pa_sink_set_mixer_dirty(pa_sink *s, bool is_dirty) {
-    pa_atomic_store(&s->mixer_dirty, is_dirty ? 1 : 0);
-}
-
 /* Called from main context */
 int pa_sink_suspend(pa_sink *s, bool suspend, pa_suspend_cause_t cause) {
     pa_suspend_cause_t merged_cause;
@@ -884,27 +1025,6 @@ int pa_sink_suspend(pa_sink *s, bool suspend, pa_suspend_cause_t cause) {
     else
         merged_cause = s->suspend_cause & ~cause;
 
-    if (!(merged_cause & PA_SUSPEND_SESSION) && (pa_atomic_load(&s->mixer_dirty) != 0)) {
-        /* This might look racy but isn't: If somebody sets mixer_dirty exactly here,
-           it'll be handled just fine. */
-        pa_sink_set_mixer_dirty(s, false);
-        pa_log_debug("Mixer is now accessible. Updating alsa mixer settings.");
-        if (s->active_port && s->set_port) {
-            if (s->flags & PA_SINK_DEFERRED_VOLUME) {
-                struct sink_message_set_port msg = { .port = s->active_port, .ret = 0 };
-                pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_PORT, &msg, 0, NULL) == 0);
-            }
-            else
-                s->set_port(s, s->active_port);
-        }
-        else {
-            if (s->set_mute)
-                s->set_mute(s);
-            if (s->set_volume)
-                s->set_volume(s);
-        }
-    }
-
     if (merged_cause)
         return sink_set_state(s, PA_SINK_SUSPENDED, merged_cause);
     else
@@ -1046,6 +1166,11 @@ void pa_sink_process_rewind(pa_sink *s, size_t nbytes) {
         pa_log_debug("Processing rewind...");
         if (s->flags & PA_SINK_DEFERRED_VOLUME)
             pa_sink_volume_change_rewind(s, nbytes);
+#ifdef TIZEN_PCM_DUMP
+        /* rewind pcm */
+        if (s->pcm_dump_fp)
+            fseeko(s->pcm_dump_fp, (off_t)nbytes * (-1), SEEK_CUR);
+#endif
     }
 
     PA_HASHMAP_FOREACH(i, s->thread_info.inputs, state) {
@@ -1279,6 +1404,9 @@ void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result) {
 
     inputs_drop(s, info, n, result);
 
+#ifdef TIZEN_PCM_DUMP
+    pa_sink_write_pcm_dump(s, result);
+#endif
     pa_sink_unref(s);
 }
 
@@ -1364,6 +1492,9 @@ void pa_sink_render_into(pa_sink*s, pa_memchunk *target) {
 
     inputs_drop(s, info, n, target);
 
+#ifdef TIZEN_PCM_DUMP
+    pa_sink_write_pcm_dump(s, target);
+#endif
     pa_sink_unref(s);
 }
 
@@ -1440,8 +1571,7 @@ void pa_sink_render_full(pa_sink *s, size_t length, pa_memchunk *result) {
 }
 
 /* Called from main thread */
-int pa_sink_reconfigure(pa_sink *s, pa_sample_spec *spec, bool passthrough) {
-    int ret = -1;
+void pa_sink_reconfigure(pa_sink *s, pa_sample_spec *spec, bool passthrough) {
     pa_sample_spec desired_spec;
     uint32_t default_rate = s->default_sample_rate;
     uint32_t alternate_rate = s->alternate_sample_rate;
@@ -1449,60 +1579,71 @@ int pa_sink_reconfigure(pa_sink *s, pa_sample_spec *spec, bool passthrough) {
     pa_sink_input *i;
     bool default_rate_is_usable = false;
     bool alternate_rate_is_usable = false;
-    bool avoid_resampling = s->core->avoid_resampling;
-
-    /* We currently only try to reconfigure the sample rate */
+    bool avoid_resampling = s->avoid_resampling;
 
     if (pa_sample_spec_equal(spec, &s->sample_spec))
-        return 0;
+        return;
 
     if (!s->reconfigure)
-        return -1;
+        return;
 
+#ifndef __TIZEN__
     if (PA_UNLIKELY(default_rate == alternate_rate && !passthrough && !avoid_resampling)) {
         pa_log_debug("Default and alternate sample rates are the same, so there is no point in switching.");
-        return -1;
+        return;
     }
+#endif
 
     if (PA_SINK_IS_RUNNING(s->state)) {
-        pa_log_info("Cannot update rate, SINK_IS_RUNNING, will keep using %u Hz",
-                    s->sample_spec.rate);
-        return -1;
+        pa_log_info("Cannot update sample spec, SINK_IS_RUNNING, will keep using %s and %u Hz",
+                    pa_sample_format_to_string(s->sample_spec.format), s->sample_spec.rate);
+        return;
     }
 
     if (s->monitor_source) {
         if (PA_SOURCE_IS_RUNNING(s->monitor_source->state) == true) {
-            pa_log_info("Cannot update rate, monitor source is RUNNING");
-            return -1;
+            pa_log_info("Cannot update sample spec, monitor source is RUNNING");
+            return;
         }
     }
 
     if (PA_UNLIKELY(!pa_sample_spec_valid(spec)))
-        return -1;
+        return;
 
     desired_spec = s->sample_spec;
 
+#ifdef __TIZEN__
+    if (!avoid_resampling) {
+        default_rate = alternate_rate = s->selected_sample_rate;
+        desired_spec.format = s->selected_sample_format;
+    }
+#endif
     if (passthrough) {
-        /* We have to try to use the sink input rate */
+        /* We have to try to use the sink input format and rate */
+        desired_spec.format = spec->format;
         desired_spec.rate = spec->rate;
 
-    } else if (avoid_resampling && (spec->rate >= default_rate || spec->rate >= alternate_rate)) {
+    } else if (avoid_resampling) {
         /* We just try to set the sink input's sample rate if it's not too low */
-        desired_spec.rate = spec->rate;
+        if (spec->rate >= default_rate || spec->rate >= alternate_rate)
+            desired_spec.rate = spec->rate;
+        desired_spec.format = spec->format;
 
     } else if (default_rate == spec->rate || alternate_rate == spec->rate) {
         /* We can directly try to use this rate */
         desired_spec.rate = spec->rate;
 
-    } else {
+    }
+
+    if (desired_spec.rate != spec->rate) {
         /* See if we can pick a rate that results in less resampling effort */
         if (default_rate % 11025 == 0 && spec->rate % 11025 == 0)
             default_rate_is_usable = true;
         if (default_rate % 4000 == 0 && spec->rate % 4000 == 0)
             default_rate_is_usable = true;
-        if (alternate_rate && alternate_rate % 11025 == 0 && spec->rate % 11025 == 0)
+        if (alternate_rate % 11025 == 0 && spec->rate % 11025 == 0)
             alternate_rate_is_usable = true;
-        if (alternate_rate && alternate_rate % 4000 == 0 && spec->rate % 4000 == 0)
+        if (alternate_rate % 4000 == 0 && spec->rate % 4000 == 0)
             alternate_rate_is_usable = true;
 
         if (alternate_rate_is_usable && !default_rate_is_usable)
@@ -1512,31 +1653,35 @@ int pa_sink_reconfigure(pa_sink *s, pa_sample_spec *spec, bool passthrough) {
     }
 
     if (pa_sample_spec_equal(&desired_spec, &s->sample_spec) && passthrough == pa_sink_is_passthrough(s))
-        return -1;
+#ifdef __TIZEN__
+    {
+        pa_log_info("desired spec is same as sink->sample_spec");
+        return;
+    }
+#else
+        return;
+#endif
 
     if (!passthrough && pa_sink_used_by(s) > 0)
-        return -1;
+        return;
 
-    pa_log_debug("Suspending sink %s due to changing format.", s->name);
+    pa_log_debug("Suspending sink %s due to changing format, desired format = %s rate = %u",
+                 s->name, pa_sample_format_to_string(desired_spec.format), desired_spec.rate);
     pa_sink_suspend(s, true, PA_SUSPEND_INTERNAL);
 
-    if (s->reconfigure(s, &desired_spec, passthrough) >= 0) {
-        /* update monitor source as well */
-        if (s->monitor_source && !passthrough)
-            pa_source_reconfigure(s->monitor_source, &desired_spec, false);
-        pa_log_info("Changed format successfully");
+    s->reconfigure(s, &desired_spec, passthrough);
 
-        PA_IDXSET_FOREACH(i, s->inputs, idx) {
-            if (i->state == PA_SINK_INPUT_CORKED)
-                pa_sink_input_update_rate(i);
-        }
+    /* update monitor source as well */
+    if (s->monitor_source && !passthrough)
+        pa_source_reconfigure(s->monitor_source, &s->sample_spec, false);
+    pa_log_info("Reconfigured successfully");
 
-        ret = 0;
+    PA_IDXSET_FOREACH(i, s->inputs, idx) {
+        if (i->state == PA_SINK_INPUT_CORKED)
+            pa_sink_input_update_resampler(i);
     }
 
     pa_sink_suspend(s, false, PA_SUSPEND_INTERNAL);
-
-    return ret;
 }
 
 /* Called from main thread */
@@ -2359,7 +2504,11 @@ void pa_sink_mute_changed(pa_sink *s, bool new_muted) {
 }
 
 /* Called from main thread */
+#ifdef __TIZEN__
+void pa_sink_update_proplist(pa_sink *s, pa_update_mode_t mode, pa_proplist *p) {
+#else
 bool pa_sink_update_proplist(pa_sink *s, pa_update_mode_t mode, pa_proplist *p) {
+#endif
     pa_sink_assert_ref(s);
     pa_assert_ctl_context();
 
@@ -2371,9 +2520,30 @@ bool pa_sink_update_proplist(pa_sink *s, pa_update_mode_t mode, pa_proplist *p)
         pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
     }
 
+#ifndef __TIZEN__
     return true;
+#endif
 }
 
+#ifdef __TIZEN__
+/* Called from main thread */
+void pa_sink_update_proplist_remote_access_permission(pa_sink *s, bool allowed) {
+    pa_proplist* p = NULL;
+
+    pa_sink_assert_ref(s);
+    pa_assert_ctl_context();
+
+    p = pa_proplist_new();
+
+    if (pa_proplist_set_remote_access_permission(p, allowed) == 0)
+         pa_sink_update_proplist(s, PA_UPDATE_REPLACE, p);
+    else
+        pa_log_error("set remote access permission %d on proplist %p failed", allowed, p);
+
+    pa_proplist_free(p);
+}
+#endif /* __TIZEN__ */
+
 /* Called from main thread */
 /* FIXME -- this should be dropped and be merged into pa_sink_update_proplist() */
 void pa_sink_set_description(pa_sink *s, const char *description) {
@@ -2459,22 +2629,18 @@ unsigned pa_sink_check_suspend(pa_sink *s, pa_sink_input *ignore_input, pa_sourc
     ret = 0;
 
     PA_IDXSET_FOREACH(i, s->inputs, idx) {
-        pa_sink_input_state_t st;
-
         if (i == ignore_input)
             continue;
 
-        st = pa_sink_input_get_state(i);
-
         /* We do not assert here. It is perfectly valid for a sink input to
          * be in the INIT state (i.e. created, marked done but not yet put)
          * and we should not care if it's unlinked as it won't contribute
          * towards our busy status.
          */
-        if (!PA_SINK_INPUT_IS_LINKED(st))
+        if (!PA_SINK_INPUT_IS_LINKED(i->state))
             continue;
 
-        if (st == PA_SINK_INPUT_CORKED)
+        if (i->state == PA_SINK_INPUT_CORKED)
             continue;
 
         if (i->flags & PA_SINK_INPUT_DONT_INHIBIT_AUTO_SUSPEND)
@@ -2845,12 +3011,19 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse
             return 0;
 
         case PA_SINK_MESSAGE_SET_STATE: {
-
+            struct set_state_data *data = userdata;
             bool suspend_change =
-                (s->thread_info.state == PA_SINK_SUSPENDED && PA_SINK_IS_OPENED(PA_PTR_TO_UINT(userdata))) ||
-                (PA_SINK_IS_OPENED(s->thread_info.state) && PA_PTR_TO_UINT(userdata) == PA_SINK_SUSPENDED);
+                (s->thread_info.state == PA_SINK_SUSPENDED && PA_SINK_IS_OPENED(data->state)) ||
+                (PA_SINK_IS_OPENED(s->thread_info.state) && data->state == PA_SINK_SUSPENDED);
 
-            s->thread_info.state = PA_PTR_TO_UINT(userdata);
+            if (s->set_state_in_io_thread) {
+                int r;
+
+                if ((r = s->set_state_in_io_thread(s, data->state, data->suspend_cause)) < 0)
+                    return r;
+            }
+
+            s->thread_info.state = data->state;
 
             if (s->thread_info.state == PA_SINK_SUSPENDED) {
                 s->thread_info.rewind_nbytes = 0;
@@ -2930,15 +3103,6 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse
             pa_sink_set_max_request_within_thread(s, (size_t) offset);
             return 0;
 
-        case PA_SINK_MESSAGE_SET_PORT:
-
-            pa_assert(userdata);
-            if (s->set_port) {
-                struct sink_message_set_port *msg_data = userdata;
-                msg_data->ret = s->set_port(s, msg_data->port);
-            }
-            return 0;
-
         case PA_SINK_MESSAGE_UPDATE_VOLUME_AND_MUTE:
             /* This message is sent from IO-thread and handled in main thread. */
             pa_assert_ctl_context();
@@ -3393,7 +3557,6 @@ size_t pa_sink_get_max_request(pa_sink *s) {
 /* Called from main context */
 int pa_sink_set_port(pa_sink *s, const char *name, bool save) {
     pa_device_port *port;
-    int ret;
 
     pa_sink_assert_ref(s);
     pa_assert_ctl_context();
@@ -3414,15 +3577,9 @@ int pa_sink_set_port(pa_sink *s, const char *name, bool save) {
         return 0;
     }
 
-    if (s->flags & PA_SINK_DEFERRED_VOLUME) {
-        struct sink_message_set_port msg = { .port = port, .ret = 0 };
-        pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_PORT, &msg, 0, NULL) == 0);
-        ret = msg.ret;
-    }
-    else
-        ret = s->set_port(s, port);
+    s->port_changing = true;
 
-    if (ret < 0)
+    if (s->set_port(s, port) < 0)
         return -PA_ERR_NOENTITY;
 
     pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
@@ -3439,6 +3596,8 @@ int pa_sink_set_port(pa_sink *s, const char *name, bool save) {
 
     pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_PORT_CHANGED], s);
 
+    s->port_changing = false;
+
     return 0;
 }
 
@@ -3569,6 +3728,14 @@ unsigned pa_device_init_priority(pa_proplist *p) {
 
     pa_assert(p);
 
+    /* JACK sinks and sources get very high priority so that we'll switch the
+     * default devices automatically when jackd starts and
+     * module-jackdbus-detect creates the jack sink and source. */
+    if ((s = pa_proplist_gets(p, PA_PROP_DEVICE_API))) {
+        if (pa_streq(s, "jack"))
+            priority += 10000;
+    }
+
     if ((s = pa_proplist_gets(p, PA_PROP_DEVICE_CLASS))) {
 
         if (pa_streq(s, "sound"))
@@ -3601,10 +3768,18 @@ unsigned pa_device_init_priority(pa_proplist *p) {
 
     if ((s = pa_proplist_gets(p, PA_PROP_DEVICE_PROFILE_NAME))) {
 
-        if (pa_startswith(s, "analog-"))
+        if (pa_startswith(s, "analog-")) {
             priority += 9;
+
+            /* If an analog device has an intended role of "phone", it probably
+             * co-exists with another device that is meant for everything else,
+             * and that other device should have higher priority than the phone
+             * device. */
+            if (pa_str_in_list_spaces(pa_proplist_gets(p, PA_PROP_DEVICE_INTENDED_ROLES), "phone"))
+                priority -= 1;
+        }
         else if (pa_startswith(s, "iec958-"))
-            priority += 8;
+            priority += 7;
     }
 
     return priority;
@@ -3880,6 +4055,43 @@ done:
     return out_formats;
 }
 
+/* Called from the main thread */
+void pa_sink_set_sample_format(pa_sink *s, pa_sample_format_t format) {
+    pa_sample_format_t old_format;
+
+    pa_assert(s);
+    pa_assert(pa_sample_format_valid(format));
+
+    old_format = s->sample_spec.format;
+    if (old_format == format)
+        return;
+
+    pa_log_info("%s: format: %s -> %s",
+                s->name, pa_sample_format_to_string(old_format), pa_sample_format_to_string(format));
+
+    s->sample_spec.format = format;
+
+    pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+}
+
+/* Called from the main thread */
+void pa_sink_set_sample_rate(pa_sink *s, uint32_t rate) {
+    uint32_t old_rate;
+
+    pa_assert(s);
+    pa_assert(pa_sample_rate_valid(rate));
+
+    old_rate = s->sample_spec.rate;
+    if (old_rate == rate)
+        return;
+
+    pa_log_info("%s: rate: %u -> %u", s->name, old_rate, rate);
+
+    s->sample_spec.rate = rate;
+
+    pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK | PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+}
+
 /* Called from the main thread. */
 void pa_sink_set_reference_volume_direct(pa_sink *s, const pa_cvolume *volume) {
     pa_cvolume old_volume;
@@ -3904,3 +4116,48 @@ void pa_sink_set_reference_volume_direct(pa_sink *s, const pa_cvolume *volume) {
     pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
     pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_VOLUME_CHANGED], s);
 }
+
+void pa_sink_move_streams_to_default_sink(pa_core *core, pa_sink *old_sink, bool default_sink_changed) {
+    pa_sink_input *i;
+    uint32_t idx;
+
+    pa_assert(core);
+    pa_assert(old_sink);
+
+    if (core->state == PA_CORE_SHUTDOWN)
+        return;
+
+    if (core->default_sink == NULL || core->default_sink->unlink_requested)
+        return;
+
+    if (old_sink == core->default_sink)
+        return;
+
+    PA_IDXSET_FOREACH(i, old_sink->inputs, idx) {
+        if (!PA_SINK_INPUT_IS_LINKED(i->state))
+            continue;
+
+        if (!i->sink)
+            continue;
+
+        /* Don't move sink-inputs which connect filter sinks to their target sinks */
+        if (i->origin_sink)
+            continue;
+
+        /* If default_sink_changed is false, the old sink became unavailable, so all streams must be moved. */
+        if (pa_safe_streq(old_sink->name, i->preferred_sink) && default_sink_changed)
+            continue;
+
+        if (!pa_sink_input_may_move_to(i, core->default_sink))
+            continue;
+
+        if (default_sink_changed)
+            pa_log_info("The sink input %u \"%s\" is moving to %s due to change of the default sink.",
+                        i->index, pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_APPLICATION_NAME)), core->default_sink->name);
+        else
+            pa_log_info("The sink input %u \"%s\" is moving to %s, because the old sink became unavailable.",
+                        i->index, pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_APPLICATION_NAME)), core->default_sink->name);
+
+        pa_sink_input_move_to(i, core->default_sink, false);
+    }
+}