X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;ds=sidebyside;f=src%2Fpulsecore%2Fsink.c;h=ae3be39da8b580561b39ce1a59bb36de44b2b192;hb=HEAD;hp=50e17c176b8c2930694586b75d59f10f7c6a2921;hpb=3da0de5418b29c90974d0d3e2198c471c39d229f;p=platform%2Fupstream%2Fpulseaudio.git diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c index 50e17c1..ae3be39 100644 --- a/src/pulsecore/sink.c +++ b/src/pulsecore/sink.c @@ -25,6 +25,9 @@ #include #include #include +#ifdef TIZEN_PCM_DUMP +#include +#endif #include #include @@ -46,6 +49,9 @@ #include #include #include +#ifdef __TIZEN__ +#include +#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); + } +}