From: Lennart Poettering Date: Wed, 7 May 2008 01:37:42 +0000 (+0000) Subject: rework the rewinding logic once again, fixing X-Git-Tag: 1.0_branch~2762^2^2~47 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=9d7fde5fe33aeebf2b0047f63359d4b051c9579c;p=profile%2Fivi%2Fpulseaudio.git rework the rewinding logic once again, fixing git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2379 fefdeb5f-60dc-0310-8127-8f9354f1896f --- diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c index 317693d..0be1cc9 100644 --- a/src/pulsecore/sink-input.c +++ b/src/pulsecore/sink-input.c @@ -250,6 +250,7 @@ pa_sink_input* pa_sink_input_new( i->thread_info.muted = i->muted; i->thread_info.requested_sink_latency = (pa_usec_t) -1; i->thread_info.rewrite_nbytes = 0; + i->thread_info.rewrite_flush = FALSE; i->thread_info.underrun_for = (uint64_t) -1; i->thread_info.playing_for = 0; @@ -602,8 +603,7 @@ void pa_sink_input_drop(pa_sink_input *i, size_t nbytes /* in sink sample spec * didn't do this for us, we do it here. However, since the sink apparently doesn't support rewinding, we pass 0 here. This still allows rewinding through the render buffer. */ - if (i->thread_info.rewrite_nbytes > 0) - pa_sink_input_process_rewind(i, 0); + pa_sink_input_process_rewind(i, 0); pa_memblockq_drop(i->thread_info.render_memblockq, nbytes); @@ -619,54 +619,59 @@ void pa_sink_input_process_rewind(pa_sink_input *i, size_t nbytes /* in sink sam /* pa_log_debug("rewind(%lu, %lu)", (unsigned long) nbytes, (unsigned long) i->thread_info.rewrite_nbytes); */ - if (i->thread_info.underrun_for > 0) { - /* We don't rewind when we are underrun */ - i->thread_info.rewrite_nbytes = 0; - return; + if (nbytes > 0) { + pa_log_debug("Have to rewind %lu bytes on render memblockq.", (unsigned long) nbytes); + pa_memblockq_rewind(i->thread_info.render_memblockq, nbytes); } - if (nbytes > 0) - pa_log_debug("Have to rewind %lu bytes on render memblockq.", (unsigned long) nbytes); + if (i->thread_info.rewrite_nbytes == (size_t) -1) { - if (i->thread_info.rewrite_nbytes > 0) { - size_t max_rewrite; + /* We were asked to drop all buffered data, and rerequest new + * data from implementor the next time push() is called */ - /* Calculate how much make sense to rewrite at most */ - if ((max_rewrite = nbytes + pa_memblockq_get_length(i->thread_info.render_memblockq)) > 0) { - size_t amount, r; + pa_memblockq_flush(i->thread_info.render_memblockq); - /* Transform into local domain */ - if (i->thread_info.resampler) - max_rewrite = pa_resampler_request(i->thread_info.resampler, max_rewrite); + } else if (i->thread_info.rewrite_nbytes > 0) { + size_t max_rewrite, amount; - /* Calculate how much of the rewinded data should actually be rewritten */ - amount = PA_MIN(i->thread_info.rewrite_nbytes, max_rewrite); + /* Calculate how much make sense to rewrite at most */ + max_rewrite = nbytes + pa_memblockq_get_length(i->thread_info.render_memblockq); - /* Convert back to to sink domain */ - r = i->thread_info.resampler ? pa_resampler_result(i->thread_info.resampler, amount) : amount; + /* Transform into local domain */ + if (i->thread_info.resampler) + max_rewrite = pa_resampler_request(i->thread_info.resampler, max_rewrite); - if (r > 0) - /* Ok, now update the write pointer */ - pa_memblockq_seek(i->thread_info.render_memblockq, -r, PA_SEEK_RELATIVE); + /* Calculate how much of the rewinded data should actually be rewritten */ + amount = PA_MIN(i->thread_info.rewrite_nbytes, max_rewrite); - if (amount > 0) { - pa_log_debug("Have to rewind %lu bytes on implementor.", (unsigned long) amount); + if (amount > 0) { + pa_log_debug("Have to rewind %lu bytes on implementor.", (unsigned long) amount); - /* Tell the implementor */ - if (i->process_rewind) - i->process_rewind(i, amount); + /* Tell the implementor */ + if (i->process_rewind) + i->process_rewind(i, amount); - /* And reset the resampler */ + if (i->thread_info.rewrite_flush) + pa_memblockq_silence(i->thread_info.render_memblockq); + else { + + /* Convert back to to sink domain */ if (i->thread_info.resampler) - pa_resampler_reset(i->thread_info.resampler); + amount = pa_resampler_result(i->thread_info.resampler, amount); + + if (amount > 0) + /* Ok, now update the write pointer */ + pa_memblockq_seek(i->thread_info.render_memblockq, - ((int64_t) amount), PA_SEEK_RELATIVE); } - } - i->thread_info.rewrite_nbytes = 0; + /* And reset the resampler */ + if (i->thread_info.resampler) + pa_resampler_reset(i->thread_info.resampler); + } } - if (nbytes > 0) - pa_memblockq_rewind(i->thread_info.render_memblockq, nbytes); + i->thread_info.rewrite_nbytes = 0; + i->thread_info.rewrite_flush = FALSE; } /* Called from thread context */ @@ -1016,19 +1021,15 @@ void pa_sink_input_set_state_within_thread(pa_sink_input *i, pa_sink_input_state if (state == PA_SINK_INPUT_CORKED && i->thread_info.state != PA_SINK_INPUT_CORKED) { - /* OK, we're corked, so let's make sure we have total silence - * from now on on this stream */ - pa_memblockq_silence(i->thread_info.render_memblockq); - /* This will tell the implementing sink input driver to rewind * so that the unplayed already mixed data is not lost */ - pa_sink_input_request_rewind(i, 0, FALSE, FALSE); + pa_sink_input_request_rewind(i, 0, TRUE, TRUE); } else if (i->thread_info.state == PA_SINK_INPUT_CORKED && state != PA_SINK_INPUT_CORKED) { /* OK, we're being uncorked. Make sure we're not rewound when * the hw buffer is remixed and request a remix. */ - pa_sink_input_request_rewind(i, 0, TRUE, TRUE); + pa_sink_input_request_rewind(i, 0, FALSE, TRUE); } if (i->state_change) @@ -1047,12 +1048,12 @@ int pa_sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t switch (code) { case PA_SINK_INPUT_MESSAGE_SET_VOLUME: i->thread_info.volume = *((pa_cvolume*) userdata); - pa_sink_input_request_rewind(i, 0, FALSE, FALSE); + pa_sink_input_request_rewind(i, 0, TRUE, FALSE); return 0; case PA_SINK_INPUT_MESSAGE_SET_MUTE: i->thread_info.muted = PA_PTR_TO_UINT(userdata); - pa_sink_input_request_rewind(i, 0, FALSE, FALSE); + pa_sink_input_request_rewind(i, 0, TRUE, FALSE); return 0; case PA_SINK_INPUT_MESSAGE_GET_LATENCY: { @@ -1111,59 +1112,60 @@ pa_bool_t pa_sink_input_safe_to_remove(pa_sink_input *i) { return TRUE; } -void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes /* in our sample spec */, pa_bool_t ignore_underruns, pa_bool_t not_here) { +void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes /* in our sample spec */, pa_bool_t rewrite, pa_bool_t flush) { size_t lbq; + /* If 'rewrite' is TRUE the sink is rewound as far as requested + * and possible and the exact value of this is passed back the + * implementor via process_rewind(). If 'flush' is also TRUE all + * already rendered data is also dropped. + * + * If 'rewrite' is FALSE the sink is rewound as far as requested + * and possible and the already rendered data is dropped so that + * in the next iteration we read new data from the + * implementor. This implies 'flush' is TRUE. */ + pa_sink_input_assert_ref(i); + pa_assert(i->thread_info.rewrite_nbytes == 0); /* We don't take rewind requests while we are corked */ if (i->state == PA_SINK_INPUT_CORKED) return; + pa_assert(rewrite || flush); + /* Calculate how much we can rewind locally without having to * touch the sink */ - if (not_here) - lbq = 0; - else + if (rewrite) lbq = pa_memblockq_get_length(i->thread_info.render_memblockq); + else + lbq = 0; /* Check if rewinding for the maximum is requested, and if so, fix up */ if (nbytes <= 0) { - /* Calulate maximum number of bytes that could be rewound in theory */ + /* Calculate maximum number of bytes that could be rewound in theory */ nbytes = i->sink->thread_info.max_rewind + lbq; /* Transform from sink domain */ - nbytes = - i->thread_info.resampler ? - pa_resampler_request(i->thread_info.resampler, nbytes) : - nbytes; + if (i->thread_info.resampler) + nbytes = pa_resampler_request(i->thread_info.resampler, nbytes); } - if (not_here) { - i->thread_info.playing_for = 0; - i->thread_info.underrun_for = (uint64_t) -1; - } else { - /* Increase the number of bytes to rewrite, never decrease */ - if (nbytes < i->thread_info.rewrite_nbytes) - nbytes = i->thread_info.rewrite_nbytes; - + if (rewrite) { /* Make sure to not overwrite over underruns */ - if (!ignore_underruns) - if (nbytes > i->thread_info.playing_for) - nbytes = (size_t) i->thread_info.playing_for; + if (nbytes > i->thread_info.playing_for) + nbytes = (size_t) i->thread_info.playing_for; i->thread_info.rewrite_nbytes = nbytes; - } + } else + i->thread_info.rewrite_nbytes = (size_t) -1; - /* Transform to sink domain */ - nbytes = - i->thread_info.resampler ? - pa_resampler_result(i->thread_info.resampler, nbytes) : - nbytes; + i->thread_info.rewrite_flush = flush && i->thread_info.rewrite_nbytes != 0; - if (nbytes <= 0) - return; + /* Transform to sink domain */ + if (i->thread_info.resampler) + nbytes = pa_resampler_result(i->thread_info.resampler, nbytes); if (nbytes > lbq) pa_sink_request_rewind(i->sink, nbytes - lbq); diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h index b70cb0a..8edd7ec 100644 --- a/src/pulsecore/sink-input.h +++ b/src/pulsecore/sink-input.h @@ -156,8 +156,8 @@ struct pa_sink_input { pa_memblockq *render_memblockq; size_t rewrite_nbytes; + pa_bool_t rewrite_flush; uint64_t underrun_for, playing_for; - pa_bool_t ignore_rewind; pa_sink_input *sync_prev, *sync_next; @@ -241,7 +241,7 @@ fully -- or at all. If the request for a rewrite was successful, the sink driver will call ->rewind() and pass the number of bytes that could be rewound in the HW device. This functionality is required for implementing the "zero latency" write-through functionality. */ -void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes, pa_bool_t ignore_rewind, pa_bool_t not_here); +void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes, pa_bool_t rewrite, pa_bool_t flush); /* Callable by everyone from main thread*/