[pulseaudio-discuss] [PATCH v2 1/4] pulse: Fix hole handling in pa_stream_peek().
David Henningsson
david.henningsson at canonical.com
Thu Nov 8 06:52:48 PST 2012
On 11/07/2012 03:52 PM, Tanu Kaskinen wrote:
> Previously, if there was a hole in a recording stream,
> pa_stream_peek() would crash. Holes could be handled silently inside
> pa_stream_peek() by generating silence (wouldn't work for compressed
> streams, though) or by skipping any holes. However, I think it's
> better to let the caller decide how the holes should be handled, so
> in case of holes, pa_stream_peek() will return NULL data pointer and
> the length of the hole in the nbytes argument.
>
> This change is technically an interface break, because previously the
> documentation didn't mention the possibility of holes that need
> special handling. However, since holes caused crashing anyway in the
> past, it's not a regression if applications keep misbehaving due to
> not handing holes properly.
>
> Some words about when holes can appear in recording streams: I think
> it would be reasonable behavior if overruns due to the application
> reading data too slowly would cause holes. Currently that's not the
> case - overruns will just cause audio to be skipped. But the point is
> that this might change some day. I'm not sure how holes can occur
> with the current code, but as the linked bug shows, they can happen.
> It's most likely due to recording from a monitor source where the
> thing being monitored has holes in its playback stream.
>
> BugLink: http://bugs.launchpad.net/bugs/1058200
This one reviewed and applied.
> ---
> src/pulse/stream.c | 20 ++++++++++++++++----
> src/pulse/stream.h | 20 +++++++++++++++-----
> 2 files changed, 31 insertions(+), 9 deletions(-)
>
> diff --git a/src/pulse/stream.c b/src/pulse/stream.c
> index 2b6d306..f692d37 100644
> --- a/src/pulse/stream.c
> +++ b/src/pulse/stream.c
> @@ -1593,9 +1593,17 @@ int pa_stream_peek(pa_stream *s, const void **data, size_t *length) {
> if (!s->peek_memchunk.memblock) {
>
> if (pa_memblockq_peek(s->record_memblockq, &s->peek_memchunk) < 0) {
> + /* record_memblockq is empty. */
> *data = NULL;
> *length = 0;
> return 0;
> +
> + } else if (!s->peek_memchunk.memblock) {
> + /* record_memblockq isn't empty, but it doesn't have any data at
> + * the current read index. */
> + *data = NULL;
> + *length = s->peek_memchunk.length;
> + return 0;
> }
>
> s->peek_data = pa_memblock_acquire(s->peek_memchunk.memblock);
> @@ -1614,7 +1622,7 @@ int pa_stream_drop(pa_stream *s) {
> PA_CHECK_VALIDITY(s->context, !pa_detect_fork(), PA_ERR_FORKED);
> PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
> PA_CHECK_VALIDITY(s->context, s->direction == PA_STREAM_RECORD, PA_ERR_BADSTATE);
> - PA_CHECK_VALIDITY(s->context, s->peek_memchunk.memblock, PA_ERR_BADSTATE);
> + PA_CHECK_VALIDITY(s->context, s->peek_memchunk.length > 0, PA_ERR_BADSTATE);
>
> pa_memblockq_drop(s->record_memblockq, s->peek_memchunk.length);
>
> @@ -1622,9 +1630,13 @@ int pa_stream_drop(pa_stream *s) {
> if (s->timing_info_valid && !s->timing_info.read_index_corrupt)
> s->timing_info.read_index += (int64_t) s->peek_memchunk.length;
>
> - pa_assert(s->peek_data);
> - pa_memblock_release(s->peek_memchunk.memblock);
> - pa_memblock_unref(s->peek_memchunk.memblock);
> + if (s->peek_memchunk.memblock) {
> + pa_assert(s->peek_data);
> + s->peek_data = NULL;
> + pa_memblock_release(s->peek_memchunk.memblock);
> + pa_memblock_unref(s->peek_memchunk.memblock);
> + }
> +
> pa_memchunk_reset(&s->peek_memchunk);
>
> return 0;
> diff --git a/src/pulse/stream.h b/src/pulse/stream.h
> index b4464fa..a6785ec 100644
> --- a/src/pulse/stream.h
> +++ b/src/pulse/stream.h
> @@ -534,11 +534,21 @@ int pa_stream_write(
> pa_seek_mode_t seek /**< Seek mode, must be PA_SEEK_RELATIVE for upload streams */);
>
> /** Read the next fragment from the buffer (for recording streams).
> - * \a data will point to the actual data and \a nbytes will contain the size
> - * of the data in bytes (which can be less or more than a complete
> - * fragment). Use pa_stream_drop() to actually remove the data from
> - * the buffer. If no data is available this will return a NULL
> - * pointer. */
> + * If there is data at the current read index, \a data will point to
> + * the actual data and \a nbytes will contain the size of the data in
> + * bytes (which can be less or more than a complete fragment).
> + *
> + * If there is no data at the current read index, it means that either
> + * the buffer is empty or it contains a hole (that is, the write index
> + * is ahead of the read index but there's no data where the read index
> + * points at). If the buffer is empty, \a data will be NULL and
> + * \a nbytes will be 0. If there is a hole, \a data will be NULL and
> + * \a nbytes will contain the length of the hole.
> + *
> + * Use pa_stream_drop() to actually remove the data from the buffer
> + * and move the read index forward. pa_stream_drop() should not be
> + * called if the buffer is empty, but it should be called if there is
> + * a hole. */
> int pa_stream_peek(
> pa_stream *p /**< The stream to use */,
> const void **data /**< Pointer to pointer that will point to data */,
>
--
David Henningsson, Canonical Ltd.
https://launchpad.net/~diwic
More information about the pulseaudio-discuss
mailing list