On Sat, Apr 12, 2014 at 09:02:34PM -0300, Felipe Sateler wrote:
> Hi David,
> 
> On Mon, Dec 26, 2011 at 04:01:21PM -0600, David Fries wrote:
> > Package: pulseaudio
> > Version: 0.9.21-3+squeeze1
> > Severity: normal
> > 
> > 
> > I found that pa_stream_write with an offset that's not a multiple of
> > the base sample size causes pulseaudio to assert and abort.  This is
> > not the case for the nbytes parameter of how many bytes to write, that
> > is allowed to not be a multiple of the sample size, but the seek
> > offset parameter must be a multiple of the sample size or it asserts.
> > 
> > E: memblockq.c: Assertion 'length % bq->base == 0' failed at
> > pulsecore/memblockq.c:519, function pa_memblockq_drop(). Aborting.
> > Aborted
> > 
> > pulseaudio 1.0-4 gives the following,
> > E: [alsa-sink] memblockq.c: Assertion 'length % bq->base == 0' failed
> > at pulsecore/memblockq.c:600, function pa_memblockq_drop(). Aborting.
> > 
> > I've fixed the program causing it, I just wanted to make others aware
> > that any program that can play audio can take down the audio daemon.
> 
> You reported this issue a long time ago, and the problem might have been
> fixed since then.
> 
> Can you still reproduce this problem? If so, please reply (preferably
> with a short offending program) so we may debug this. Otherwise I'd like
> to close this bug.
> 
> Thanks
> 
> 
> -- 
> Saludos,
> Felipe Sateler

Thanks for asking about this bug, I modified a test (attached) to
demonstrate it is still a problem.  There might be a shorter setup
sequence, but it comes down to giving SEEK as something other than a
multiple of the sample size.
pa_stream_write(p, buffer, nbytes, NULL, SEEK, PA_SEEK_RELATIVE);

pulseaudio 2.0-6.1

cc -g -Wall -g  PulseSeekBug.c -lpulse -o PulseSeekBug && ./PulseSeekBug
wrote 65472, offset 1
Stream state 3, suspended -15, corked -15, writable size -1
Stream state 3, suspended -15, corked -15, writable size -1
Stream state 3, suspended -15, corked -15, writable size -1
Stream state 3, suspended -15, corked -15, writable size -1

/usr/bin/pulseaudio
W: [pulseaudio] pid.c: Stale PID file, overwriting.
E: [alsa-sink] memblockq.c: Assertion 'length % bq->base == 0' failed at 
pulsecore/memblockq.c:613, function pa_memblockq_drop(). Aborting.
Aborted

-- 
David Fries <da...@fries.net>    PGP pub CB1EE8F0
http://fries.net/~david/
/* Test program using the asynchronous API.
 * This is demonstrating a problem where the playback under-runs, but
 * yet the stream lists a writable size of 0.  Those are contracidtory,
 * if it is under-running it should have space available, if there isn't
 * any space available it shouldn't be under-running.  Adding some
 * debugging to src/pulse/stream.c shows that the s->requested_bytes
 * is negative, but returns 0 on negative.
 *
 * At least this is the case as of PulseAudio 0.9.21, seems to be fixed
 * in the git code grabbed on 09-18-2010, I didn't look into when it
 * was fixed.
 */

#include <pulse/pulseaudio.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

// If it isn't a multiple of the sample size, 4, 8, 12, etc the pulseaudio
// daemon asserts and dies.
#define SEEK 1

void writable(pa_stream *p, size_t nbytes, void *userdata)
{
	char *buffer;

	/* pa_stream_begin_write "If you place (size_t) -1 in *nbytes on
	 * invocation the memory size will be chosen automatically (which is
	 * recommended to do)." (from the OggPulseAudio.h)
	 */
	nbytes=(size_t)-1;
	pa_stream_begin_write(p, (void**)&buffer, &nbytes);
	
	// fill the buffer with audio here...
	memset(buffer, 0, nbytes);

	pa_stream_write(p, buffer, nbytes, NULL, SEEK, PA_SEEK_RELATIVE);
	printf("wrote %ld, offset %d\n", 1L*nbytes, SEEK);
}

int main(int argc, char **argv)
{
	pa_threaded_mainloop *mainloop=NULL;
	pa_mainloop_api *api=NULL;
	pa_context *context=NULL;
	pa_stream *stream=NULL;
	pa_sample_spec spec;
	pa_context_state_t cstate;

	mainloop=pa_threaded_mainloop_new();
	if(!mainloop)
	{
		fprintf(stderr, "Failed to create PulseAudio main loop\n");
		return 1;
	}
	api=pa_threaded_mainloop_get_api(mainloop);
	pa_threaded_mainloop_start(mainloop);

	pa_threaded_mainloop_lock(mainloop);
	context=pa_context_new(api, __PRETTY_FUNCTION__);
	pa_context_connect(context, NULL, (pa_context_flags_t)0, NULL);
	pa_threaded_mainloop_unlock(mainloop);

	spec.format=PA_SAMPLE_S16LE;
	spec.rate=48000;
	spec.channels=2;
	pa_channel_map map;
	pa_channel_map_init_auto(&map, spec.channels, PA_CHANNEL_MAP_DEFAULT);

	for(;;)
	{
		pa_threaded_mainloop_lock(mainloop);
		cstate=pa_context_get_state(context);
		pa_threaded_mainloop_unlock(mainloop);
		if(cstate>=PA_CONTEXT_READY)
			break;
		usleep(1000);
	}

	pa_threaded_mainloop_lock(mainloop);
	stream=pa_stream_new(context, __func__, &spec, &map);
	if(!stream)
		return 1;
	pa_stream_set_write_callback(stream, writable, NULL);
	pa_stream_flags_t flags = (pa_stream_flags_t)(
		PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE);
	pa_stream_connect_playback(stream, NULL, NULL, flags, NULL, NULL);
	pa_threaded_mainloop_unlock(mainloop);

	for(;;)
	{
		sleep(1);
		pa_threaded_mainloop_lock(mainloop);
		printf("Stream state %d, suspended %d, "
			"corked %d, writable size %ld\n",
			pa_stream_get_state(stream),
			pa_stream_is_suspended(stream),
			pa_stream_is_corked(stream),
			1L*pa_stream_writable_size(stream));
		pa_threaded_mainloop_unlock(mainloop);
	}

	// shutdown
	pa_stream_disconnect(stream);
	pa_stream_unref(stream);
	if(mainloop)
	{
		pa_context_disconnect(context);
		pa_context_unref(context);
		context=NULL;
		pa_threaded_mainloop_free(mainloop);
		mainloop=NULL;
		api=NULL; // freed with mainloop
	}
	return 0;
}

Reply via email to