[xiph-commits] r3703 - in browser_plugin/trunk: . src/audio
j at svn.annodex.net
j at svn.annodex.net
Sat Aug 9 07:50:21 PDT 2008
Author: j
Date: 2008-08-09 07:50:20 -0700 (Sat, 09 Aug 2008)
New Revision: 3703
Modified:
browser_plugin/trunk/configure.ac
browser_plugin/trunk/src/audio/sydney_audio_pulseaudio.c
Log:
use pulseaudio callback api, avoids crashes and should be more flexible
Modified: browser_plugin/trunk/configure.ac
===================================================================
--- browser_plugin/trunk/configure.ac 2008-08-09 06:19:35 UTC (rev 3702)
+++ browser_plugin/trunk/configure.ac 2008-08-09 14:50:20 UTC (rev 3703)
@@ -66,7 +66,7 @@
dnl
dnl Detect pulseaudio
dnl
-PKG_CHECK_MODULES(PULSE, libpulse-simple, HAVE_PULSE=yes, HAVE_PULSE=no)
+PKG_CHECK_MODULES(PULSE, libpulse, HAVE_PULSE=yes, HAVE_PULSE=no)
AC_SUBST(PULSE_CFLAGS)
AC_SUBST(PULSE_LIBS)
Modified: browser_plugin/trunk/src/audio/sydney_audio_pulseaudio.c
===================================================================
--- browser_plugin/trunk/src/audio/sydney_audio_pulseaudio.c 2008-08-09 06:19:35 UTC (rev 3702)
+++ browser_plugin/trunk/src/audio/sydney_audio_pulseaudio.c 2008-08-09 14:50:20 UTC (rev 3703)
@@ -30,7 +30,6 @@
#include <string.h>
#include <pthread.h>
#include <pulse/pulseaudio.h>
-#include <pulse/simple.h>
#include "sydney_audio.h"
/* Pulseaudio implementation based heavily on sydney_audio_alsa.c */
@@ -64,20 +63,18 @@
};
struct sa_stream {
- pa_simple* output_stream;
- pa_sample_spec output_spec;
+ pa_context* context;
+ pa_stream* stream;
+ pa_sample_spec sample_spec;
+ pa_threaded_mainloop* m;
pthread_t thread_id;
pthread_mutex_t mutex;
+
char playing;
+ int64_t bytes_written;
char client_name[255];
- int64_t bytes_written;
- /* audio format info */
- unsigned int rate;
- unsigned int n_channels;
- unsigned int bytes_per_ch;
-
/* buffer list */
sa_buf * bl_head;
sa_buf * bl_tail;
@@ -97,12 +94,67 @@
#error BUF_LIMIT must be at least 2!
#endif
-static void audio_callback(void* s);
+static void audio_callback(void* data);
+static void stream_write_callback(pa_stream *stream, size_t length, void *userdata);
+static void stream_latency_update_callback(pa_stream *stream, void *userdata);
+static void context_state_callback(pa_context *c, void *userdata);
static sa_buf *new_buffer(void);
+
/*
* -----------------------------------------------------------------------------
+ * Pulseaudio callback functions
+ * -----------------------------------------------------------------------------
+ */
+
+static void context_state_callback(pa_context *c, void *userdata) {
+ sa_stream_t* s = (sa_stream_t*)userdata;
+ switch (pa_context_get_state(c)) {
+ case PA_CONTEXT_READY:
+ case PA_CONTEXT_TERMINATED:
+ case PA_CONTEXT_FAILED:
+ pa_threaded_mainloop_signal(s->m, 0);
+ break;
+ case PA_CONTEXT_UNCONNECTED:
+ case PA_CONTEXT_CONNECTING:
+ case PA_CONTEXT_AUTHORIZING:
+ case PA_CONTEXT_SETTING_NAME:
+ break;
+ }
+}
+
+static void stream_state_callback(pa_stream *stream, void *userdata) {
+ sa_stream_t* s = (sa_stream_t*)userdata;
+ switch (pa_stream_get_state(stream)) {
+
+ case PA_STREAM_READY:
+ case PA_STREAM_FAILED:
+ case PA_STREAM_TERMINATED:
+ pa_threaded_mainloop_signal(s->m, 0);
+ break;
+ case PA_STREAM_UNCONNECTED:
+ case PA_STREAM_CREATING:
+ break;
+ }
+}
+
+static void stream_write_callback(pa_stream *stream, size_t length, void *userdata)
+{
+ sa_stream_t* s = (sa_stream_t*)userdata;
+ pa_threaded_mainloop_signal(s->m, 0);
+}
+
+static void stream_latency_update_callback(pa_stream *stream, void *userdata)
+{
+ sa_stream_t* s = (sa_stream_t*)userdata;
+ pa_threaded_mainloop_signal(s->m, 0);
+}
+
+
+
+/*
+ * -----------------------------------------------------------------------------
* Startup and shutdown functions
* -----------------------------------------------------------------------------
*/
@@ -117,6 +169,7 @@
unsigned int n_channels
) {
sa_stream_t * s = 0;
+ char *server = NULL;
/*
* Make sure we return a NULL stream pointer on failure.
@@ -143,59 +196,102 @@
free(s);
return SA_ERROR_OOM;
}
+
if (pthread_mutex_init(&s->mutex, NULL) != 0) {
free(s->bl_head);
free(s);
return SA_ERROR_SYSTEM;
}
- s->output_stream = NULL;
- s->thread_id = 0;
- s->playing = 0;
+ s->stream = NULL;
+ s->m = NULL;
+ s->thread_id = 0;
+ s->playing = 0;
s->bytes_written = 0;
- s->rate = rate;
- s->n_channels = n_channels;
- s->bytes_per_ch = 2;
- s->bl_tail = s->bl_head;
- s->n_bufs = 1;
+ s->bl_tail = s->bl_head;
+ s->n_bufs = 1;
+
+ s->sample_spec.format = PA_SAMPLE_S16LE;
+ s->sample_spec.channels = n_channels;
+ s->sample_spec.rate = rate;
+
strcpy(s->client_name, client_name);
+ /* Set up a new main loop */
+ s->m = pa_threaded_mainloop_new();
+ pa_threaded_mainloop_start(s->m);
+
+ pa_threaded_mainloop_lock(s->m);
+
+ /* Create a new connection context */
+ if (!(s->context = pa_context_new(pa_threaded_mainloop_get_api(s->m), "OggPlay"))) {
+ fprintf(stderr, "pa_context_new() failed.\n");
+ goto unlock_and_fail;
+ }
+ pa_context_set_state_callback(s->context, context_state_callback, s);
+
+ pa_context_connect(s->context, server, 0, NULL);
+
+ /* Wait until the context is ready */
+ pa_threaded_mainloop_wait(s->m);
+ if (pa_context_get_state(s->context) != PA_CONTEXT_READY) {
+ fprintf(stderr, "creating Pulseaudio Context failed\n");
+ goto unlock_and_fail;
+ }
+ pa_threaded_mainloop_unlock(s->m);
+
*_s = s;
return SA_SUCCESS;
+
+unlock_and_fail:
+ pa_threaded_mainloop_unlock(s->m);
+ free(s);
+ return SA_ERROR_OOM;
}
-
int
sa_stream_open(sa_stream_t *s) {
if (s == NULL) {
return SA_ERROR_NO_INIT;
}
- if (s->output_stream != NULL) {
+ if (s->stream != NULL) {
return SA_ERROR_INVALID;
}
- s->output_spec.format = PA_SAMPLE_S16LE;
- s->output_spec.channels = s->n_channels;
- s->output_spec.rate = s->rate;
+ pa_threaded_mainloop_lock(s->m);
+ if (!(s->stream = pa_stream_new(s->context, s->client_name, &s->sample_spec, NULL))) {
+ fprintf(stderr, "pa_stream_new() failed: %s\n", pa_strerror(pa_context_errno(s->context)));
+ goto unlock_and_fail;
+ }
- s->output_stream = pa_simple_new(NULL, // Use the default server.
- "OggPlay", // Our application's name.
- PA_STREAM_PLAYBACK,
- NULL, // Use the default device.
- s->client_name, // Description of our stream.
- &s->output_spec, // Our sample format.
- NULL, // Use default channel map
- NULL, // Use default buffering attributes.
- NULL // Ignore error code.
- );
+ pa_stream_set_state_callback(s->stream, stream_state_callback, s);
+ pa_stream_set_write_callback(s->stream, stream_write_callback, s);
+ pa_stream_set_latency_update_callback(s->stream, stream_latency_update_callback, s);
- if (!s->output_stream)
+ if (pa_stream_connect_playback(s->stream, NULL, NULL, 0, NULL, NULL) < 0) {
+ fprintf(stderr, "pa_stream_connect_playback() failed: %s\n", pa_strerror(pa_context_errno(s->context)));
+ goto unlock_and_fail;
+ }
+
+ /* Wait until the stream is ready */
+ pa_threaded_mainloop_wait(s->m);
+
+ if (pa_stream_get_state(s->stream) != PA_STREAM_READY) {
+ fprintf(stderr, "Failed to connect stream: %s", pa_strerror(pa_context_errno(s->context)));
+ goto unlock_and_fail;
+ }
+ pa_threaded_mainloop_unlock(s->m);
+
+ if (!s->stream)
return SA_ERROR_NO_DEVICE;
return SA_SUCCESS;
+
+unlock_and_fail:
+ pa_threaded_mainloop_unlock(s->m);
+ return SA_ERROR_NO_DEVICE;
}
-
int
sa_stream_destroy(sa_stream_t *s) {
if (s == NULL) {
@@ -203,13 +299,29 @@
}
pthread_mutex_lock(&s->mutex);
- /*
- * This causes the thread sending data to pulseaudio to shutdown
- */
s->thread_id = 0;
-
pthread_mutex_unlock(&s->mutex);
+ pa_threaded_mainloop_lock(s->m);
+ pa_stream_disconnect(s->stream);
+ s->stream = NULL;
+ pa_context_disconnect(s->context);
+ pa_context_unref(s->context);
+ s->context = NULL;
+ pa_threaded_mainloop_unlock(s->m);
+
+ pa_threaded_mainloop_stop(s->m);
+ pa_threaded_mainloop_free(s->m);
+
+ pthread_mutex_destroy(&s->mutex);
+
+ while (s->bl_head != NULL) {
+ sa_buf * next = s->bl_head->next;
+ free(s->bl_head);
+ s->bl_head = next;
+ }
+ free(s);
+
return SA_SUCCESS;
}
@@ -225,12 +337,15 @@
sa_stream_write(sa_stream_t *s, const void *data, size_t nbytes) {
int result = SA_SUCCESS;
- if (s == NULL || s->output_stream == NULL) {
+ if (s == NULL || s->stream == NULL) {
return SA_ERROR_NO_INIT;
}
if (nbytes == 0) {
return SA_SUCCESS;
}
+
+ pthread_mutex_lock(&s->mutex);
+
/*
* Append the new data to the end of our buffer list.
*/
@@ -296,16 +411,13 @@
} /* if (nbytes <= avail), else */
} /* while (1) */
-
pthread_mutex_unlock(&s->mutex);
-
/*
* Once we have our first block of audio data, enable the audio callback
* function. This doesn't need to be protected by the mutex, because
* s->playing is not used in the audio callback thread, and it's probably
* better not to be inside the lock when we enable the audio callback.
*/
-
if (!s->playing) {
s->playing = 1;
if (pthread_create(&s->thread_id, NULL, (void *)audio_callback, s) != 0) {
@@ -317,24 +429,41 @@
static void audio_callback(void* data)
{
- int error;
sa_stream_t* s = (sa_stream_t*)data;
+ unsigned int bytes_per_frame = s->sample_spec.channels * pa_sample_size(&s->sample_spec);
+ size_t buffer_size = s->sample_spec.rate * bytes_per_frame;
+ char* buffer = malloc(buffer_size);
- //what is a good value here for length?
- unsigned int bytes_per_frame = s->n_channels * s->bytes_per_ch;
- unsigned int length = 4096 * bytes_per_frame;
-
- char* buffer = malloc(length);
-
while(1) {
char* dst = buffer;
- unsigned int bytes_to_copy = length;
- int bytes = length;
+ size_t bytes_to_copy, bytes;
+ pa_threaded_mainloop_lock(s->m);
+ while(1) {
+ if (s == NULL || s->stream == NULL) {
+ if (s != NULL && s->m != NULL)
+ pa_threaded_mainloop_unlock(s->m);
+ goto free_buffer;
+ }
+ if ((bytes_to_copy = pa_stream_writable_size(s->stream)) == (size_t) -1) {
+ fprintf(stderr, "pa_stream_writable_size() failed: %s", pa_strerror(pa_context_errno(s->context)));
+ pa_threaded_mainloop_unlock(s->m);
+ goto free_buffer;
+ }
+ if(bytes_to_copy > 0)
+ break;
+ pa_threaded_mainloop_wait(s->m);
+ }
+ pa_threaded_mainloop_unlock(s->m);
+ if (bytes_to_copy > buffer_size)
+ bytes_to_copy = buffer_size;
+ bytes = bytes_to_copy;
+
pthread_mutex_lock(&s->mutex);
- if (!s->thread_id)
+ if (!s->thread_id) {
+ pthread_mutex_unlock(&s->mutex);
break;
-
+ }
/*
* Consume data from the start of the buffer list.
*/
@@ -348,8 +477,8 @@
*/
memcpy(dst, s->bl_head->data + s->bl_head->start, bytes_to_copy);
s->bl_head->start += bytes_to_copy;
- s->bytes_written += bytes_to_copy;
break;
+
} else {
sa_buf* next = 0;
/*
@@ -359,8 +488,6 @@
s->bl_head->start += avail;
dst += avail;
bytes_to_copy -= avail;
- s->bytes_written += avail;
-
/*
* We want to free the now-empty buffer, but not if it's also the
* current tail. If it is the tail, we don't have enough data to fill
@@ -375,50 +502,39 @@
s->bl_head = next;
s->n_bufs--;
} /* if (avail >= bytes_to_copy), else */
-
} /* while (1) */
- pthread_mutex_unlock(&s->mutex);
- if(bytes>0) {
- if(pa_simple_write(s->output_stream, buffer, bytes, &error) < 0) {
- printf( __FILE__": pa_simple_write() failed: %s\n", pa_strerror(error));
+ if(bytes > 0) {
+ pa_threaded_mainloop_lock(s->m);
+ if (pa_stream_write(s->stream, buffer, bytes, NULL, 0, PA_SEEK_RELATIVE) < 0) {
+ fprintf(stderr, "pa_stream_write() failed: %s", pa_strerror(pa_context_errno(s->context)));
+ pa_threaded_mainloop_unlock(s->m);
+ return;
}
+ pa_stream_update_timing_info(s->stream, NULL, NULL);
+ s->bytes_written += bytes;
+ pa_threaded_mainloop_unlock(s->m);
}
+ pthread_mutex_unlock(&s->mutex);
}
-
+free_buffer:
free(buffer);
+}
- if (s->output_stream != NULL) {
- pa_simple_free(s->output_stream);
- }
- /*
- * Release resources.
- */
-
- //does this need checking?
- pthread_mutex_destroy(&s->mutex);
-
- while (s->bl_head != NULL) {
- sa_buf * next = s->bl_head->next;
- free(s->bl_head);
- s->bl_head = next;
- }
- free(s);
-}
-
/*
* -----------------------------------------------------------------------------
* General query and support functions
* -----------------------------------------------------------------------------
*/
+
int
sa_stream_get_write_size(sa_stream_t *s, size_t *size) {
sa_buf * b;
size_t used = 0;
- if (s == NULL || s->output_stream == NULL) {
+ if (s == NULL || s->stream == NULL) {
return SA_ERROR_NO_INIT;
}
@@ -441,48 +557,42 @@
int
sa_stream_get_position(sa_stream_t *s, sa_position_t position, int64_t *pos) {
pa_usec_t usec;
- int64_t latency;
-
- if (s == NULL || s->output_stream == NULL) {
+ if (s == NULL || s->stream == NULL) {
return SA_ERROR_NO_INIT;
}
if (position != SA_POSITION_WRITE_SOFTWARE) {
return SA_ERROR_NOT_SUPPORTED;
}
-
- usec = pa_simple_get_latency(s->output_stream, NULL);
- latency = pa_usec_to_bytes(usec, &s->output_spec);
-
- // this part causes a crash sometimes
- // pthread_mutex_lock.c:87: __pthread_mutex_lock: Assertion `mutex->__data.__owner == 0' failed.
- pthread_mutex_lock(&s->mutex);
- *pos = s->bytes_written - latency;
- pthread_mutex_unlock(&s->mutex);
+ pa_threaded_mainloop_lock(s->m);
+ if(pa_stream_get_time(s->stream, &usec) != PA_ERR_NODATA) {
+ *pos = pa_usec_to_bytes(usec, &s->sample_spec);
+ }
+ else {
+ *pos = s->bytes_written;
+ }
+ pa_threaded_mainloop_unlock(s->m);
return SA_SUCCESS;
}
int
sa_stream_pause(sa_stream_t *s) {
- if (s == NULL || s->output_stream == NULL) {
+ if (s == NULL || s->stream == NULL) {
return SA_ERROR_NO_INIT;
}
-
- pa_simple_flush(s->output_stream, NULL);
return SA_SUCCESS;
}
int
sa_stream_resume(sa_stream_t *s) {
- if (s == NULL || s->output_stream == NULL) {
+ if (s == NULL || s->stream == NULL) {
return SA_ERROR_NO_INIT;
}
- pthread_mutex_lock(&s->mutex);
-
+ pa_threaded_mainloop_lock(s->m);
s->bytes_written = 0;
- pthread_mutex_unlock(&s->mutex);
+ pa_threaded_mainloop_unlock(s->m);
return SA_SUCCESS;
}
@@ -511,10 +621,10 @@
sa_stream_set_volume_abs(sa_stream_t *s, float vol) {
pa_cvolume cv;
- if (s == NULL || s->output_stream == NULL) {
+ if (s == NULL || s->stream == NULL) {
return SA_ERROR_NO_INIT;
}
- pa_cvolume_set(&cv, s->n_channels, pa_sw_volume_from_dB(vol));
+ pa_cvolume_set(&cv, s->sample_spec.channels, pa_sw_volume_from_dB(vol));
return SA_SUCCESS;
}
@@ -522,7 +632,7 @@
int
sa_stream_get_volume_abs(sa_stream_t *s, float *vol) {
- if (s == NULL || s->output_stream == NULL) {
+ if (s == NULL || s->stream == NULL) {
return SA_ERROR_NO_INIT;
}
printf("sa_stream_get_volume_abs not implemented\n");
More information about the commits
mailing list