X-Git-Url: https://code.delx.au/pulseaudio/blobdiff_plain/6152c524203649e1592a0ef21fc28dd7d2c4d101..18b13a89a516dbc33acbddbd9600c05cb9cb0246:/src/pulsecore/source-output.c diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c index 27f24cd1..1509807b 100644 --- a/src/pulsecore/source-output.c +++ b/src/pulsecore/source-output.c @@ -41,14 +41,14 @@ #define MEMBLOCKQ_MAXLENGTH (32*1024*1024) -static PA_DEFINE_CHECK_TYPE(pa_source_output, pa_msgobject); +PA_DEFINE_PUBLIC_CLASS(pa_source_output, pa_msgobject); static void source_output_free(pa_object* mo); pa_source_output_new_data* pa_source_output_new_data_init(pa_source_output_new_data *data) { pa_assert(data); - memset(data, 0, sizeof(*data)); + pa_zero(*data); data->resample_method = PA_RESAMPLER_INVALID; data->proplist = pa_proplist_new(); @@ -84,9 +84,11 @@ static void reset_callbacks(pa_source_output *o) { o->update_max_rewind = NULL; o->update_source_requested_latency = NULL; o->update_source_latency_range = NULL; + o->update_source_fixed_latency = NULL; o->attach = NULL; o->detach = NULL; o->suspend = NULL; + o->suspend_within_thread = NULL; o->moving = NULL; o->kill = NULL; o->get_latency = NULL; @@ -99,8 +101,7 @@ static void reset_callbacks(pa_source_output *o) { int pa_source_output_new( pa_source_output**_o, pa_core *core, - pa_source_output_new_data *data, - pa_source_output_flags_t flags) { + pa_source_output_new_data *data) { pa_source_output *o; pa_resampler *resampler = NULL; @@ -110,6 +111,7 @@ int pa_source_output_new( pa_assert(_o); pa_assert(core); pa_assert(data); + pa_assert_ctl_context(); if (data->client) pa_proplist_update(data->proplist, PA_UPDATE_MERGE, data->client->proplist); @@ -143,13 +145,13 @@ int pa_source_output_new( pa_return_val_if_fail(pa_channel_map_valid(&data->channel_map), -PA_ERR_INVALID); pa_return_val_if_fail(pa_channel_map_compatible(&data->channel_map, &data->sample_spec), -PA_ERR_INVALID); - if (flags & PA_SOURCE_OUTPUT_FIX_FORMAT) + if (data->flags & PA_SOURCE_OUTPUT_FIX_FORMAT) data->sample_spec.format = data->source->sample_spec.format; - if (flags & PA_SOURCE_OUTPUT_FIX_RATE) + if (data->flags & PA_SOURCE_OUTPUT_FIX_RATE) data->sample_spec.rate = data->source->sample_spec.rate; - if (flags & PA_SOURCE_OUTPUT_FIX_CHANNELS) { + if (data->flags & PA_SOURCE_OUTPUT_FIX_CHANNELS) { data->sample_spec.channels = data->source->sample_spec.channels; data->channel_map = data->source->channel_map; } @@ -165,7 +167,7 @@ int pa_source_output_new( if ((r = pa_hook_fire(&core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_FIXATE], data)) < 0) return r; - if ((flags & PA_SOURCE_OUTPUT_FAIL_ON_SUSPEND) && + if ((data->flags & PA_SOURCE_OUTPUT_NO_CREATE_ON_SUSPEND) && pa_source_get_state(data->source) == PA_SOURCE_SUSPENDED) { pa_log("Failed to create source output: source is suspended."); return -PA_ERR_BADSTATE; @@ -176,7 +178,7 @@ int pa_source_output_new( return -PA_ERR_TOOLARGE; } - if ((flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) || + if ((data->flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) || !pa_sample_spec_equal(&data->sample_spec, &data->source->sample_spec) || !pa_channel_map_equal(&data->channel_map, &data->source->channel_map)) { @@ -185,9 +187,9 @@ int pa_source_output_new( &data->source->sample_spec, &data->source->channel_map, &data->sample_spec, &data->channel_map, data->resample_method, - ((flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) | - ((flags & PA_SOURCE_OUTPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) | - (core->disable_remixing || (flags & PA_SOURCE_OUTPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0) | + ((data->flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) | + ((data->flags & PA_SOURCE_OUTPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) | + (core->disable_remixing || (data->flags & PA_SOURCE_OUTPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0) | (core->disable_lfe_remixing ? PA_RESAMPLER_NO_LFE : 0)))) { pa_log_warn("Unsupported resampling operation."); return -PA_ERR_NOTSUPPORTED; @@ -200,7 +202,7 @@ int pa_source_output_new( o->core = core; o->state = PA_SOURCE_OUTPUT_INIT; - o->flags = flags; + o->flags = data->flags; o->proplist = pa_proplist_copy(data->proplist); o->driver = pa_xstrdup(pa_path_get_filename(data->driver)); o->module = data->module; @@ -261,6 +263,7 @@ int pa_source_output_new( /* Called from main context */ static void update_n_corked(pa_source_output *o, pa_source_output_state_t state) { pa_assert(o); + pa_assert_ctl_context(); if (!o->source) return; @@ -274,6 +277,7 @@ static void update_n_corked(pa_source_output *o, pa_source_output_state_t state) /* Called from main context */ static void source_output_set_state(pa_source_output *o, pa_source_output_state_t state) { pa_assert(o); + pa_assert_ctl_context(); if (o->state == state) return; @@ -293,6 +297,7 @@ static void source_output_set_state(pa_source_output *o, pa_source_output_state_ void pa_source_output_unlink(pa_source_output*o) { pa_bool_t linked; pa_assert(o); + pa_assert_ctl_context(); /* See pa_sink_unlink() for a couple of comments how this function * works */ @@ -345,6 +350,7 @@ static void source_output_free(pa_object* mo) { pa_source_output *o = PA_SOURCE_OUTPUT(mo); pa_assert(o); + pa_assert_ctl_context(); pa_assert(pa_source_output_refcnt(o) == 0); if (PA_SOURCE_OUTPUT_IS_LINKED(o->state)) @@ -352,8 +358,6 @@ static void source_output_free(pa_object* mo) { pa_log_info("Freeing output %u \"%s\"", o->index, pa_strnull(pa_proplist_gets(o->proplist, PA_PROP_MEDIA_NAME))); - pa_assert(!o->thread_info.attached); - if (o->thread_info.delay_memblockq) pa_memblockq_free(o->thread_info.delay_memblockq); @@ -370,7 +374,9 @@ static void source_output_free(pa_object* mo) { /* Called from main context */ void pa_source_output_put(pa_source_output *o) { pa_source_output_state_t state; + pa_source_output_assert_ref(o); + pa_assert_ctl_context(); pa_assert(o->state == PA_SOURCE_OUTPUT_INIT); @@ -394,6 +400,7 @@ void pa_source_output_put(pa_source_output *o) { /* Called from main context */ void pa_source_output_kill(pa_source_output*o) { pa_source_output_assert_ref(o); + pa_assert_ctl_context(); pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state)); o->kill(o); @@ -404,6 +411,7 @@ pa_usec_t pa_source_output_get_latency(pa_source_output *o, pa_usec_t *source_la pa_usec_t r[2] = { 0, 0 }; pa_source_output_assert_ref(o); + pa_assert_ctl_context(); pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state)); pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_GET_LATENCY, r, 0, NULL) == 0); @@ -423,6 +431,7 @@ void pa_source_output_push(pa_source_output *o, const pa_memchunk *chunk) { size_t limit, mbs = 0; pa_source_output_assert_ref(o); + pa_source_output_assert_io_context(o); pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->thread_info.state)); pa_assert(chunk); pa_assert(pa_frame_aligned(chunk->length, &o->source->sample_spec)); @@ -434,11 +443,30 @@ void pa_source_output_push(pa_source_output *o, const pa_memchunk *chunk) { if (pa_memblockq_push(o->thread_info.delay_memblockq, chunk) < 0) { pa_log_debug("Delay queue overflow!"); - pa_memblockq_seek(o->thread_info.delay_memblockq, (int64_t) chunk->length, PA_SEEK_RELATIVE); + pa_memblockq_seek(o->thread_info.delay_memblockq, (int64_t) chunk->length, PA_SEEK_RELATIVE, TRUE); } limit = o->process_rewind ? 0 : o->source->thread_info.max_rewind; + if (limit > 0 && o->source->monitor_of) { + pa_usec_t latency; + size_t n; + + /* Hmm, check the latency for knowing how much of the buffered + * data is actually still unplayed and might hence still + * change. This is suboptimal. Ideally we'd have a call like + * pa_sink_get_changeable_size() or so that tells us how much + * of the queued data is actually still changeable. Hence + * FIXME! */ + + latency = pa_sink_get_latency_within_thread(o->source->monitor_of); + + n = pa_usec_to_bytes(latency, &o->source->sample_spec); + + if (n < limit) + limit = n; + } + /* Implement the delay queue */ while ((length = pa_memblockq_get_length(o->thread_info.delay_memblockq)) > limit) { pa_memchunk qchunk; @@ -479,8 +507,9 @@ void pa_source_output_push(pa_source_output *o, const pa_memchunk *chunk) { /* Called from thread context */ void pa_source_output_process_rewind(pa_source_output *o, size_t nbytes /* in source sample spec */) { - pa_source_output_assert_ref(o); + pa_source_output_assert_ref(o); + pa_source_output_assert_io_context(o); pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->thread_info.state)); pa_assert(pa_frame_aligned(nbytes, &o->source->sample_spec)); @@ -505,9 +534,18 @@ void pa_source_output_process_rewind(pa_source_output *o, size_t nbytes /* in so pa_memblockq_rewind(o->thread_info.delay_memblockq, nbytes); } +/* Called from thread context */ +size_t pa_source_output_get_max_rewind(pa_source_output *o) { + pa_source_output_assert_ref(o); + pa_source_output_assert_io_context(o); + + return o->thread_info.resampler ? pa_resampler_request(o->thread_info.resampler, o->source->thread_info.max_rewind) : o->source->thread_info.max_rewind; +} + /* Called from thread context */ void pa_source_output_update_max_rewind(pa_source_output *o, size_t nbytes /* in the source's sample spec */) { pa_source_output_assert_ref(o); + pa_source_output_assert_io_context(o); pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->thread_info.state)); pa_assert(pa_frame_aligned(nbytes, &o->source->sample_spec)); @@ -518,21 +556,24 @@ void pa_source_output_update_max_rewind(pa_source_output *o, size_t nbytes /* i /* Called from thread context */ pa_usec_t pa_source_output_set_requested_latency_within_thread(pa_source_output *o, pa_usec_t usec) { pa_source_output_assert_ref(o); + pa_source_output_assert_io_context(o); + + if (!(o->source->flags & PA_SOURCE_DYNAMIC_LATENCY)) + usec = o->source->thread_info.fixed_latency; if (usec != (pa_usec_t) -1) usec = PA_CLAMP(usec, o->source->thread_info.min_latency, o->source->thread_info.max_latency); o->thread_info.requested_source_latency = usec; - pa_source_invalidate_requested_latency(o->source); + pa_source_invalidate_requested_latency(o->source, TRUE); return usec; } /* Called from main context */ pa_usec_t pa_source_output_set_requested_latency(pa_source_output *o, pa_usec_t usec) { - pa_usec_t min_latency, max_latency; - pa_source_output_assert_ref(o); + pa_assert_ctl_context(); if (PA_SOURCE_OUTPUT_IS_LINKED(o->state) && o->source) { pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_SET_REQUESTED_LATENCY, &usec, 0, NULL) == 0); @@ -543,10 +584,14 @@ pa_usec_t pa_source_output_set_requested_latency(pa_source_output *o, pa_usec_t * have to touch the thread info data directly */ if (o->source) { - pa_source_get_latency_range(o->source, &min_latency, &max_latency); + if (!(o->source->flags & PA_SOURCE_DYNAMIC_LATENCY)) + usec = pa_source_get_fixed_latency(o->source); - if (usec != (pa_usec_t) -1) + if (usec != (pa_usec_t) -1) { + pa_usec_t min_latency, max_latency; + pa_source_get_latency_range(o->source, &min_latency, &max_latency); usec = PA_CLAMP(usec, min_latency, max_latency); + } } o->thread_info.requested_source_latency = usec; @@ -557,6 +602,7 @@ pa_usec_t pa_source_output_set_requested_latency(pa_source_output *o, pa_usec_t /* Called from main context */ pa_usec_t pa_source_output_get_requested_latency(pa_source_output *o) { pa_source_output_assert_ref(o); + pa_assert_ctl_context(); if (PA_SOURCE_OUTPUT_IS_LINKED(o->state) && o->source) { pa_usec_t usec = 0; @@ -573,6 +619,7 @@ pa_usec_t pa_source_output_get_requested_latency(pa_source_output *o) { /* Called from main context */ void pa_source_output_cork(pa_source_output *o, pa_bool_t b) { pa_source_output_assert_ref(o); + pa_assert_ctl_context(); pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state)); source_output_set_state(o, b ? PA_SOURCE_OUTPUT_CORKED : PA_SOURCE_OUTPUT_RUNNING); @@ -581,6 +628,7 @@ void pa_source_output_cork(pa_source_output *o, pa_bool_t b) { /* Called from main context */ int pa_source_output_set_rate(pa_source_output *o, uint32_t rate) { pa_source_output_assert_ref(o); + pa_assert_ctl_context(); pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state)); pa_return_val_if_fail(o->thread_info.resampler, -PA_ERR_BADSTATE); @@ -598,6 +646,7 @@ int pa_source_output_set_rate(pa_source_output *o, uint32_t rate) { /* Called from main context */ void pa_source_output_set_name(pa_source_output *o, const char *name) { const char *old; + pa_assert_ctl_context(); pa_source_output_assert_ref(o); if (!name && !pa_proplist_contains(o->proplist, PA_PROP_MEDIA_NAME)) @@ -622,11 +671,12 @@ void pa_source_output_set_name(pa_source_output *o, const char *name) { /* Called from main thread */ void pa_source_output_update_proplist(pa_source_output *o, pa_update_mode_t mode, pa_proplist *p) { pa_source_output_assert_ref(o); + pa_assert_ctl_context(); if (p) pa_proplist_update(o->proplist, mode, p); - if (PA_SINK_IS_LINKED(o->state)) { + if (PA_SOURCE_OUTPUT_IS_LINKED(o->state)) { pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PROPLIST_CHANGED], o); pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index); } @@ -635,6 +685,7 @@ void pa_source_output_update_proplist(pa_source_output *o, pa_update_mode_t mode /* Called from main context */ pa_resample_method_t pa_source_output_get_resample_method(pa_source_output *o) { pa_source_output_assert_ref(o); + pa_assert_ctl_context(); return o->actual_resample_method; } @@ -642,6 +693,7 @@ pa_resample_method_t pa_source_output_get_resample_method(pa_source_output *o) { /* Called from main context */ pa_bool_t pa_source_output_may_move(pa_source_output *o) { pa_source_output_assert_ref(o); + pa_assert_ctl_context(); pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state)); if (o->flags & PA_SOURCE_OUTPUT_DONT_MOVE) @@ -683,6 +735,7 @@ int pa_source_output_start_move(pa_source_output *o) { int r; pa_source_output_assert_ref(o); + pa_assert_ctl_context(); pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state)); pa_assert(o->source); @@ -704,6 +757,8 @@ int pa_source_output_start_move(pa_source_output *o) { pa_source_update_status(o->source); o->source = NULL; + pa_source_output_unref(o); + return 0; } @@ -712,6 +767,7 @@ int pa_source_output_finish_move(pa_source_output *o, pa_source *dest, pa_bool_t pa_resampler *new_resampler; pa_source_output_assert_ref(o); + pa_assert_ctl_context(); pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state)); pa_assert(!o->source); pa_source_assert_ref(dest); @@ -751,7 +807,7 @@ int pa_source_output_finish_move(pa_source_output *o, pa_source *dest, pa_bool_t o->source = dest; o->save_source = save; - pa_idxset_put(o->source->outputs, o, NULL); + pa_idxset_put(o->source->outputs, pa_source_output_ref(o), NULL); if (pa_source_output_get_state(o) == PA_SOURCE_OUTPUT_CORKED) o->source->n_corked++; @@ -788,11 +844,30 @@ int pa_source_output_finish_move(pa_source_output *o, pa_source *dest, pa_bool_t return 0; } +/* Called from main context */ +void pa_source_output_fail_move(pa_source_output *o) { + + pa_source_output_assert_ref(o); + pa_assert_ctl_context(); + pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state)); + pa_assert(!o->source); + + /* Check if someone wants this source output? */ + if (pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_FAIL], o) == PA_HOOK_STOP) + return; + + if (o->moving) + o->moving(o, NULL); + + pa_source_output_kill(o); +} + /* Called from main context */ int pa_source_output_move_to(pa_source_output *o, pa_source *dest, pa_bool_t save) { int r; pa_source_output_assert_ref(o); + pa_assert_ctl_context(); pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state)); pa_assert(o->source); pa_source_assert_ref(dest); @@ -803,11 +878,20 @@ int pa_source_output_move_to(pa_source_output *o, pa_source *dest, pa_bool_t sav if (!pa_source_output_may_move_to(o, dest)) return -PA_ERR_NOTSUPPORTED; - if ((r = pa_source_output_start_move(o)) < 0) + pa_source_output_ref(o); + + if ((r = pa_source_output_start_move(o)) < 0) { + pa_source_output_unref(o); return r; + } - if ((r = pa_source_output_finish_move(o, dest, save)) < 0) + if ((r = pa_source_output_finish_move(o, dest, save)) < 0) { + pa_source_output_fail_move(o); + pa_source_output_unref(o); return r; + } + + pa_source_output_unref(o); return 0; } @@ -815,6 +899,7 @@ int pa_source_output_move_to(pa_source_output *o, pa_source *dest, pa_bool_t sav /* Called from IO thread context */ void pa_source_output_set_state_within_thread(pa_source_output *o, pa_source_output_state_t state) { pa_source_output_assert_ref(o); + pa_source_output_assert_io_context(o); if (state == o->thread_info.state) return; @@ -834,12 +919,9 @@ int pa_source_output_process_msg(pa_msgobject *mo, int code, void *userdata, int case PA_SOURCE_OUTPUT_MESSAGE_GET_LATENCY: { pa_usec_t *r = userdata; - pa_usec_t source_usec = 0; r[0] += pa_bytes_to_usec(pa_memblockq_get_length(o->thread_info.delay_memblockq), &o->source->sample_spec); - - if (o->source->parent.process_msg(PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_GET_LATENCY, &source_usec, 0, NULL) >= 0) - r[1] += source_usec; + r[1] += pa_source_get_latency_within_thread(o->source); return 0; } @@ -874,11 +956,13 @@ int pa_source_output_process_msg(pa_msgobject *mo, int code, void *userdata, int return -PA_ERR_NOTIMPLEMENTED; } +/* Called from main context */ void pa_source_output_send_event(pa_source_output *o, const char *event, pa_proplist *data) { pa_proplist *pl = NULL; pa_source_output_send_event_hook_data hook_data; pa_source_output_assert_ref(o); + pa_assert_ctl_context(); pa_assert(event); if (!o->send_event)