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
This commit is contained in:
Lennart Poettering 2008-05-07 01:37:42 +00:00
parent 6c28f1d5b9
commit 9d7fde5fe3
2 changed files with 75 additions and 73 deletions

View file

@ -250,6 +250,7 @@ pa_sink_input* pa_sink_input_new(
i->thread_info.muted = i->muted; i->thread_info.muted = i->muted;
i->thread_info.requested_sink_latency = (pa_usec_t) -1; i->thread_info.requested_sink_latency = (pa_usec_t) -1;
i->thread_info.rewrite_nbytes = 0; i->thread_info.rewrite_nbytes = 0;
i->thread_info.rewrite_flush = FALSE;
i->thread_info.underrun_for = (uint64_t) -1; i->thread_info.underrun_for = (uint64_t) -1;
i->thread_info.playing_for = 0; 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 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 apparently doesn't support rewinding, we pass 0 here. This still
allows rewinding through the render buffer. */ 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); 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); */ /* pa_log_debug("rewind(%lu, %lu)", (unsigned long) nbytes, (unsigned long) i->thread_info.rewrite_nbytes); */
if (i->thread_info.underrun_for > 0) { if (nbytes > 0) {
/* We don't rewind when we are underrun */ pa_log_debug("Have to rewind %lu bytes on render memblockq.", (unsigned long) nbytes);
i->thread_info.rewrite_nbytes = 0; pa_memblockq_rewind(i->thread_info.render_memblockq, nbytes);
return;
} }
if (nbytes > 0) if (i->thread_info.rewrite_nbytes == (size_t) -1) {
pa_log_debug("Have to rewind %lu bytes on render memblockq.", (unsigned long) nbytes);
if (i->thread_info.rewrite_nbytes > 0) { /* We were asked to drop all buffered data, and rerequest new
size_t max_rewrite; * data from implementor the next time push() is called */
pa_memblockq_flush(i->thread_info.render_memblockq);
} else if (i->thread_info.rewrite_nbytes > 0) {
size_t max_rewrite, amount;
/* Calculate how much make sense to rewrite at most */ /* Calculate how much make sense to rewrite at most */
if ((max_rewrite = nbytes + pa_memblockq_get_length(i->thread_info.render_memblockq)) > 0) { max_rewrite = nbytes + pa_memblockq_get_length(i->thread_info.render_memblockq);
size_t amount, r;
/* Transform into local domain */ /* Transform into local domain */
if (i->thread_info.resampler) if (i->thread_info.resampler)
max_rewrite = pa_resampler_request(i->thread_info.resampler, max_rewrite); max_rewrite = pa_resampler_request(i->thread_info.resampler, max_rewrite);
/* Calculate how much of the rewinded data should actually be rewritten */ /* Calculate how much of the rewinded data should actually be rewritten */
amount = PA_MIN(i->thread_info.rewrite_nbytes, max_rewrite); amount = PA_MIN(i->thread_info.rewrite_nbytes, max_rewrite);
/* Convert back to to sink domain */ if (amount > 0) {
r = i->thread_info.resampler ? pa_resampler_result(i->thread_info.resampler, amount) : amount; pa_log_debug("Have to rewind %lu bytes on implementor.", (unsigned long) amount);
if (r > 0) /* Tell the implementor */
/* Ok, now update the write pointer */ if (i->process_rewind)
pa_memblockq_seek(i->thread_info.render_memblockq, -r, PA_SEEK_RELATIVE); i->process_rewind(i, amount);
if (amount > 0) { if (i->thread_info.rewrite_flush)
pa_log_debug("Have to rewind %lu bytes on implementor.", (unsigned long) amount); pa_memblockq_silence(i->thread_info.render_memblockq);
else {
/* Tell the implementor */ /* Convert back to to sink domain */
if (i->process_rewind)
i->process_rewind(i, amount);
/* And reset the resampler */
if (i->thread_info.resampler) if (i->thread_info.resampler)
pa_resampler_reset(i->thread_info.resampler); amount = pa_resampler_result(i->thread_info.resampler, amount);
}
}
i->thread_info.rewrite_nbytes = 0; if (amount > 0)
/* Ok, now update the write pointer */
pa_memblockq_seek(i->thread_info.render_memblockq, - ((int64_t) amount), PA_SEEK_RELATIVE);
}
/* And reset the resampler */
if (i->thread_info.resampler)
pa_resampler_reset(i->thread_info.resampler);
}
} }
if (nbytes > 0) i->thread_info.rewrite_nbytes = 0;
pa_memblockq_rewind(i->thread_info.render_memblockq, nbytes); i->thread_info.rewrite_flush = FALSE;
} }
/* Called from thread context */ /* 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) { 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 /* This will tell the implementing sink input driver to rewind
* so that the unplayed already mixed data is not lost */ * 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) { } 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 /* OK, we're being uncorked. Make sure we're not rewound when
* the hw buffer is remixed and request a remix. */ * 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) 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) { switch (code) {
case PA_SINK_INPUT_MESSAGE_SET_VOLUME: case PA_SINK_INPUT_MESSAGE_SET_VOLUME:
i->thread_info.volume = *((pa_cvolume*) userdata); 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; return 0;
case PA_SINK_INPUT_MESSAGE_SET_MUTE: case PA_SINK_INPUT_MESSAGE_SET_MUTE:
i->thread_info.muted = PA_PTR_TO_UINT(userdata); 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; return 0;
case PA_SINK_INPUT_MESSAGE_GET_LATENCY: { 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; 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; 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_sink_input_assert_ref(i);
pa_assert(i->thread_info.rewrite_nbytes == 0);
/* We don't take rewind requests while we are corked */ /* We don't take rewind requests while we are corked */
if (i->state == PA_SINK_INPUT_CORKED) if (i->state == PA_SINK_INPUT_CORKED)
return; return;
pa_assert(rewrite || flush);
/* Calculate how much we can rewind locally without having to /* Calculate how much we can rewind locally without having to
* touch the sink */ * touch the sink */
if (not_here) if (rewrite)
lbq = 0;
else
lbq = pa_memblockq_get_length(i->thread_info.render_memblockq); 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 */ /* Check if rewinding for the maximum is requested, and if so, fix up */
if (nbytes <= 0) { 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; nbytes = i->sink->thread_info.max_rewind + lbq;
/* Transform from sink domain */ /* Transform from sink domain */
nbytes = if (i->thread_info.resampler)
i->thread_info.resampler ? nbytes = pa_resampler_request(i->thread_info.resampler, nbytes);
pa_resampler_request(i->thread_info.resampler, nbytes) :
nbytes;
} }
if (not_here) { if (rewrite) {
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;
/* Make sure to not overwrite over underruns */ /* Make sure to not overwrite over underruns */
if (!ignore_underruns) if (nbytes > i->thread_info.playing_for)
if (nbytes > i->thread_info.playing_for) nbytes = (size_t) i->thread_info.playing_for;
nbytes = (size_t) i->thread_info.playing_for;
i->thread_info.rewrite_nbytes = nbytes; i->thread_info.rewrite_nbytes = nbytes;
} } else
i->thread_info.rewrite_nbytes = (size_t) -1;
i->thread_info.rewrite_flush = flush && i->thread_info.rewrite_nbytes != 0;
/* Transform to sink domain */ /* Transform to sink domain */
nbytes = if (i->thread_info.resampler)
i->thread_info.resampler ? nbytes = pa_resampler_result(i->thread_info.resampler, nbytes);
pa_resampler_result(i->thread_info.resampler, nbytes) :
nbytes;
if (nbytes <= 0)
return;
if (nbytes > lbq) if (nbytes > lbq)
pa_sink_request_rewind(i->sink, nbytes - lbq); pa_sink_request_rewind(i->sink, nbytes - lbq);

View file

@ -156,8 +156,8 @@ struct pa_sink_input {
pa_memblockq *render_memblockq; pa_memblockq *render_memblockq;
size_t rewrite_nbytes; size_t rewrite_nbytes;
pa_bool_t rewrite_flush;
uint64_t underrun_for, playing_for; uint64_t underrun_for, playing_for;
pa_bool_t ignore_rewind;
pa_sink_input *sync_prev, *sync_next; 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 sink driver will call ->rewind() and pass the number of bytes that
could be rewound in the HW device. This functionality is required for could be rewound in the HW device. This functionality is required for
implementing the "zero latency" write-through functionality. */ 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*/ /* Callable by everyone from main thread*/