summaryrefslogtreecommitdiffstats
path: root/src/common.c
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2007-10-01 20:16:28 +0000
committerLennart Poettering <lennart@poettering.net>2007-10-01 20:16:28 +0000
commit7d83e5c7816b5e343695a75ba58b32dbe1be969a (patch)
treebfd1dfc9b7c8f4a2aaf66c1b30e78355dee8c88a /src/common.c
parent762196328ab7e60f1d2908fd5a337d2ca99726dd (diff)
move all sources down to a seperate src/ tree
git-svn-id: file:///home/lennart/svn/public/libsydney/trunk@34 9ba3c220-e4d3-45a2-8aa3-73fcc9aff6ce
Diffstat (limited to 'src/common.c')
-rw-r--r--src/common.c1266
1 files changed, 1266 insertions, 0 deletions
diff --git a/src/common.c b/src/common.c
new file mode 100644
index 0000000..5a81d02
--- /dev/null
+++ b/src/common.c
@@ -0,0 +1,1266 @@
+#include <string.h>
+#include <errno.h>
+#include <liboil/liboil.h>
+
+#include "sydney.h"
+#include "macro.h"
+#include "malloc.h"
+#include "common.h"
+#include "driver.h"
+#include "mutex.h"
+
+/* contains code */
+#include "meta-name-table.h"
+
+static sa_stream_t *stream_alloc(void) {
+ sa_stream_t *d;
+
+ if (!(d = sa_new0(sa_stream_t, 1)))
+ return NULL;
+
+ /* All fields a carefully chosen in a way that initializing them
+ * NUL bytes is sufficient */
+
+ if (!(d->mutex = sa_mutex_new(0))) {
+ sa_free(d);
+ return NULL;
+ }
+
+ return d;
+}
+
+int sa_stream_create_opaque(
+ sa_stream_t **s,
+ const char *client_name,
+ sa_mode_t mode,
+ const char *codec) {
+
+ int error;
+
+ sa_return_val_if_fail(s, SA_ERROR_INVALID);
+ sa_return_val_if_fail(mode == SA_MODE_RDONLY || mode == SA_MODE_WRONLY || mode == SA_MODE_RDWR, SA_ERROR_INVALID);
+ sa_return_val_if_fail(codec, SA_ERROR_INVALID);
+
+ if (!(*s = stream_alloc()))
+ return SA_ERROR_OOM;
+
+ (*s)->mode = mode;
+
+ if (!((*s)->codec = sa_strdup(codec))) {
+ error = SA_ERROR_OOM;
+ goto fail;
+ }
+
+ oil_init();
+
+ if (client_name)
+ if ((error = sa_stream_change_meta_data(*s, SA_META_CLIENT_NAME, client_name, strlen(client_name)+1)) < 0)
+ goto fail;
+
+ return SA_SUCCESS;
+
+fail:
+ sa_stream_destroy(*s);
+ return error;
+}
+
+int sa_stream_create_pcm(
+ sa_stream_t **s,
+ const char *client_name,
+ sa_mode_t mode,
+ sa_pcm_format_t format,
+ unsigned rate,
+ unsigned nchannels) {
+
+ int ret;
+ size_t lwm;
+
+ sa_return_val_if_fail(s, SA_ERROR_INVALID);
+ sa_return_val_if_fail(mode == SA_MODE_RDONLY || mode == SA_MODE_WRONLY || mode == SA_MODE_RDWR, SA_ERROR_INVALID);
+ sa_return_val_if_fail(format < _SA_PCM_FORMAT_MAX, SA_ERROR_INVALID);
+ sa_return_val_if_fail(rate > 0, SA_ERROR_INVALID);
+ sa_return_val_if_fail(nchannels > 0, SA_ERROR_INVALID);
+
+ if (!(*s = stream_alloc()))
+ return SA_ERROR_OOM;
+
+ (*s)->mode = mode;
+ (*s)->pcm_attrs.format = format;
+ (*s)->pcm_attrs.nchannels = nchannels;
+ (*s)->pcm_sample_size = sa_get_pcm_sample_size(format);
+ (*s)->pcm_frame_size = (*s)->pcm_sample_size * nchannels;
+
+ if (nchannels <= 2) {
+ static const sa_channel_t map_stereo[2] = { SA_CHANNEL_LEFT, SA_CHANNEL_RIGHT };
+ static const sa_channel_t map_mono[1] = { SA_CHANNEL_MONO };
+
+ if ((ret = sa_stream_set_channel_map(*s, nchannels == 2 ? map_stereo : map_mono, nchannels)))
+ goto fail;
+ }
+
+ if ((ret = sa_stream_change_rate(*s, rate)))
+ goto fail;
+
+ lwm = ((*s)->pcm_frame_size * (*s)->pcm_attrs.rate) / 20; /* 50 ms */
+
+ if (lwm <= 0)
+ lwm = (*s)->pcm_frame_size * (*s)->pcm_attrs.rate; /* 1s */
+
+ if (mode & SA_MODE_RDONLY) {
+
+ if ((ret = sa_stream_set_read_lower_watermark(*s, lwm)))
+ goto fail;
+
+ if ((ret = sa_stream_set_read_upper_watermark(*s, lwm*2)))
+ goto fail;
+ }
+
+ if (mode & SA_MODE_WRONLY) {
+
+ if ((ret = sa_stream_set_write_lower_watermark(*s, lwm)))
+ goto fail;
+
+ if ((ret = sa_stream_set_write_upper_watermark(*s, lwm*2)))
+ goto fail;
+ }
+
+ oil_init();
+
+ if (client_name)
+ if ((ret = sa_stream_change_meta_data(*s, SA_META_CLIENT_NAME, client_name, strlen(client_name))) < 0)
+ goto fail;
+
+ return SA_SUCCESS;
+
+fail:
+
+ sa_stream_destroy(*s);
+ return ret;
+}
+
+int sa_stream_open(sa_stream_t *s) {
+ int ret;
+
+ sa_return_val_if_fail(s, SA_ERROR_INVALID);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, s->state == SA_STATE_INIT, SA_ERROR_STATE);
+ sa_return_val_if_fail_mutex(s->mutex, s->codec || s->pcm_attrs.channel_map, SA_ERROR_NO_INIT);
+ sa_return_val_if_fail_mutex(s->mutex, !(s->mode & SA_MODE_RDONLY) || (s->read_lower_watermark <= s->read_upper_watermark), SA_ERROR_INVALID);
+ sa_return_val_if_fail_mutex(s->mutex, !(s->mode & SA_MODE_WRONLY) || (s->write_lower_watermark <= s->write_upper_watermark), SA_ERROR_INVALID);
+ sa_return_val_if_fail_mutex(s->mutex, !(s->mode & SA_MODE_RDONLY) || !s->codec || (s->read_lower_watermark > 0 && s->read_upper_watermark > 0), SA_ERROR_NO_INIT);
+ sa_return_val_if_fail_mutex(s->mutex, !(s->mode & SA_MODE_WRONLY) || !s->codec || (s->write_lower_watermark > 0 && s->write_upper_watermark > 0), SA_ERROR_NO_INIT);
+
+ if ((ret = driver_open(s)) == 0)
+ s->state = SA_STATE_STOPPED;
+
+ sa_mutex_unlock(s->mutex);
+
+ return ret;
+}
+
+int sa_stream_destroy(sa_stream_t *s) {
+ int ret;
+ unsigned u;
+
+ sa_return_val_if_fail_mutex(s->mutex, s, SA_ERROR_INVALID);
+
+ ret = driver_destroy(s);
+
+ sa_free(s->codec);
+ sa_free(s->driver);
+ sa_free(s->device);
+ sa_free(s->pcm_attrs.channel_map);
+ sa_free(s->read_volume);
+ sa_free(s->write_volume);
+
+ for (u = 0; u < _META_NAMES_MAX; u++)
+ sa_free(s->meta_data[u]);
+
+ sa_mutex_free(s->mutex);
+ sa_free(s);
+ return ret;
+}
+
+int sa_stream_set_write_lower_watermark(sa_stream_t *s, size_t size) {
+
+ sa_return_val_if_fail(s, SA_ERROR_INVALID);
+ sa_return_val_if_fail(size > 0, SA_ERROR_INVALID);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, s->state == SA_STATE_INIT, SA_ERROR_STATE);
+ sa_return_val_if_fail_mutex(s->mutex, s->mode & SA_MODE_WRONLY, SA_ERROR_STATE);
+
+ s->write_lower_watermark = size;
+
+ sa_mutex_unlock(s->mutex);
+
+ return SA_SUCCESS;
+}
+
+int sa_stream_set_read_lower_watermark(sa_stream_t *s, size_t size) {
+
+ sa_return_val_if_fail(s, SA_ERROR_INVALID);
+ sa_return_val_if_fail(size > 0, SA_ERROR_INVALID);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, s->state == SA_STATE_INIT, SA_ERROR_STATE);
+ sa_return_val_if_fail_mutex(s->mutex, s->mode & SA_MODE_RDONLY, SA_ERROR_STATE);
+
+ s->read_lower_watermark = size;
+
+ sa_mutex_unlock(s->mutex);
+ return SA_SUCCESS;
+}
+
+int sa_stream_set_write_upper_watermark(sa_stream_t *s, size_t size) {
+
+ sa_return_val_if_fail(s, SA_ERROR_INVALID);
+ sa_return_val_if_fail(size > 0, SA_ERROR_INVALID);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, s->state == SA_STATE_INIT, SA_ERROR_STATE);
+ sa_return_val_if_fail_mutex(s->mutex, s->mode & SA_MODE_WRONLY, SA_ERROR_STATE);
+
+ s->write_upper_watermark = size;
+
+ sa_mutex_unlock(s->mutex);
+ return SA_SUCCESS;
+}
+
+int sa_stream_set_read_upper_watermark(sa_stream_t *s, size_t size) {
+
+ sa_return_val_if_fail(s, SA_ERROR_INVALID);
+ sa_return_val_if_fail(size > 0, SA_ERROR_INVALID);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, s->state == SA_STATE_INIT, SA_ERROR_STATE);
+ sa_return_val_if_fail_mutex(s->mutex, s->mode & SA_MODE_RDONLY, SA_ERROR_STATE);
+
+ s->read_upper_watermark = size;
+
+ sa_mutex_unlock(s->mutex);
+ return SA_SUCCESS;
+}
+
+int sa_stream_set_channel_map(sa_stream_t *s, const sa_channel_t *map, unsigned n) {
+ const sa_channel_t *c;
+ sa_channel_t *m;
+
+ sa_return_val_if_fail(s, SA_ERROR_INVALID);
+ sa_return_val_if_fail(map, SA_ERROR_INVALID);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, s->state == SA_STATE_INIT, SA_ERROR_STATE);
+ sa_return_val_if_fail_mutex(s->mutex, !s->codec, SA_ERROR_STATE);
+ sa_return_val_if_fail_mutex(s->mutex, n == s->pcm_attrs.nchannels, SA_ERROR_INVALID);
+
+ for (c = map; n > 0; c++, n--)
+ if (*c >= _SA_CHANNEL_MAX) {
+ sa_mutex_unlock(s->mutex);
+ return SA_ERROR_INVALID;
+ }
+
+ if (!(m = sa_memdup(map, sizeof(sa_channel_t) * s->pcm_attrs.nchannels))) {
+ sa_mutex_unlock(s->mutex);
+ return SA_ERROR_OOM;
+ }
+
+ sa_free(s->pcm_attrs.channel_map);
+ s->pcm_attrs.channel_map = m;
+
+ sa_mutex_unlock(s->mutex);
+ return SA_SUCCESS;
+}
+
+int sa_stream_set_xrun_mode(sa_stream_t *s, sa_xrun_mode_t mode) {
+ sa_return_val_if_fail(s, SA_ERROR_INVALID);
+ sa_return_val_if_fail(mode == SA_XRUN_MODE_STOP || mode == SA_XRUN_MODE_SPIN, SA_ERROR_INVALID);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, s->state == SA_STATE_INIT, SA_ERROR_STATE);
+
+ s->xrun_mode = mode;
+
+ sa_mutex_unlock(s->mutex);
+ return SA_SUCCESS;
+}
+
+int sa_stream_set_non_interleaved(sa_stream_t *s, int enable) {
+ sa_return_val_if_fail(s, SA_ERROR_INVALID);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, s->state == SA_STATE_INIT, SA_ERROR_INVALID);
+ sa_return_val_if_fail_mutex(s->mutex, !s->codec, SA_ERROR_STATE);
+
+ s->ni_enabled = !!enable;
+
+ sa_mutex_unlock(s->mutex);
+ return SA_SUCCESS;
+}
+
+int sa_stream_set_dynamic_rate(sa_stream_t *s, int enable) {
+ sa_return_val_if_fail(s, SA_ERROR_INVALID);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, s->state == SA_STATE_INIT, SA_ERROR_INVALID);
+ sa_return_val_if_fail_mutex(s->mutex, !s->codec, SA_ERROR_STATE);
+
+ s->dynamic_rate_enabled = !!enable;
+
+ sa_mutex_unlock(s->mutex);
+ return SA_SUCCESS;
+}
+
+int sa_stream_set_driver(sa_stream_t *s, const char *driver) {
+ char *d;
+
+ sa_return_val_if_fail(s, SA_ERROR_INVALID);
+ sa_return_val_if_fail(driver, SA_ERROR_INVALID);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, s->state == SA_STATE_INIT, SA_ERROR_STATE);
+
+ if (!(d = sa_strdup(driver))) {
+ sa_mutex_unlock(s->mutex);
+ return SA_ERROR_OOM;
+ }
+
+ sa_free(s->driver);
+ s->driver = d;
+
+ sa_mutex_unlock(s->mutex);
+ return SA_SUCCESS;
+}
+
+int sa_stream_start_thread(sa_stream_t *s, sa_event_callback_t callback) {
+ int r;
+
+ sa_return_val_if_fail(s, SA_ERROR_INVALID);
+ sa_return_val_if_fail(callback, SA_ERROR_INVALID);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, s->state == SA_STATE_RUNNING || s->state == SA_STATE_STOPPED, SA_ERROR_STATE);
+ sa_return_val_if_fail_mutex(s->mutex, !s->callback, SA_ERROR_STATE);
+
+ r = driver_start_thread(s, callback);
+
+ if (r == SA_SUCCESS)
+ s->callback = callback;
+
+ sa_mutex_unlock(s->mutex);
+ return r;
+}
+
+int sa_stream_stop_thread(sa_stream_t *s) {
+ int r;
+
+ sa_return_val_if_fail(s, SA_ERROR_INVALID);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, s->state == SA_STATE_RUNNING || s->state == SA_STATE_STOPPED, SA_ERROR_STATE);
+ sa_return_val_if_fail_mutex(s->mutex, s->callback, SA_ERROR_STATE);
+
+ r = driver_stop_thread(s);
+
+ if (r == SA_SUCCESS)
+ s->callback = NULL;
+
+ sa_mutex_unlock(s->mutex);
+ return r;
+}
+
+int sa_stream_change_device(sa_stream_t *s, const char *device_name) {
+ char *d;
+ int ret;
+
+ sa_return_val_if_fail(s, SA_ERROR_INVALID);
+ sa_return_val_if_fail(device_name, SA_ERROR_INVALID);
+
+ if (!(d = sa_strdup(device_name)))
+ return SA_ERROR_OOM;
+
+ sa_mutex_lock(s->mutex);
+
+ ret = s->state == SA_STATE_INIT ? SA_SUCCESS : driver_change_device(s, device_name);
+
+ if (ret == SA_SUCCESS) {
+ sa_free(s->device);
+ s->device = d;
+ } else
+ sa_free(d);
+
+ sa_mutex_unlock(s->mutex);
+
+ return ret;
+}
+
+int sa_stream_change_read_volume(sa_stream_t *s, const int32_t vol[], unsigned n) {
+ int *v, ret;
+
+ sa_return_val_if_fail(s, SA_ERROR_INVALID);
+ sa_return_val_if_fail(vol, SA_ERROR_INVALID);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, s->mode & SA_MODE_RDONLY, SA_ERROR_STATE);
+ sa_return_val_if_fail_mutex(s->mutex, (!s->codec && n == s->pcm_attrs.nchannels) || s->pcm_attrs.nchannels == 1, SA_ERROR_INVALID);
+
+ if (s->codec || s->pcm_attrs.nchannels == n) {
+ if (!(v = sa_newdup(int32_t, vol, n))) {
+ sa_mutex_unlock(s->mutex);
+ return SA_ERROR_OOM;
+ }
+ } else {
+ unsigned i;
+
+ if (!(v = sa_new(int32_t, s->pcm_attrs.nchannels))) {
+ sa_mutex_unlock(s->mutex);
+ return SA_ERROR_OOM;
+ }
+
+ for (i = 0; i < s->pcm_attrs.nchannels; i++)
+ v[i] = vol[0];
+ }
+
+ ret = s->state == SA_STATE_INIT ? SA_SUCCESS : driver_change_read_volume(s, v);
+
+ if (ret == SA_SUCCESS) {
+ sa_free(s->read_volume);
+ s->read_volume = v;
+ } else
+ sa_free(v);
+
+ sa_mutex_unlock(s->mutex);
+ return ret;
+}
+
+int sa_stream_change_write_volume(sa_stream_t *s, const int32_t vol[], unsigned n) {
+ int *v, ret;
+
+ sa_return_val_if_fail(s, SA_ERROR_INVALID);
+ sa_return_val_if_fail(vol, SA_ERROR_INVALID);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, s->mode & SA_MODE_WRONLY, SA_ERROR_STATE);
+ sa_return_val_if_fail_mutex(s->mutex, (!s->codec && n == s->pcm_attrs.nchannels) || s->pcm_attrs.nchannels == 1, SA_ERROR_INVALID);
+
+ if (s->codec || s->pcm_attrs.nchannels == n) {
+ if (!(v = sa_newdup(int32_t, vol, n))) {
+ sa_mutex_unlock(s->mutex);
+ return SA_ERROR_OOM;
+ }
+ } else {
+ unsigned i;
+
+ if (!(v = sa_new(int32_t, s->pcm_attrs.nchannels))) {
+ sa_mutex_unlock(s->mutex);
+ return SA_ERROR_OOM;
+ }
+
+ for (i = 0; i < s->pcm_attrs.nchannels; i++)
+ v[i] = vol[0];
+ }
+
+ ret = s->state == SA_STATE_INIT ? SA_SUCCESS : driver_change_write_volume(s, v);
+
+ if (ret == SA_SUCCESS) {
+ sa_free(s->write_volume);
+ s->write_volume = v;
+ } else
+ sa_free(v);
+
+ sa_mutex_unlock(s->mutex);
+ return ret;
+}
+
+int sa_stream_change_rate(sa_stream_t *s, unsigned rate) {
+ int ret;
+
+ sa_return_val_if_fail(s, SA_ERROR_INVALID);
+ sa_return_val_if_fail(rate > 0, SA_ERROR_INVALID);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, !s->codec, SA_ERROR_STATE);
+ sa_return_val_if_fail_mutex(s->mutex, s->dynamic_rate_enabled || s->state == SA_STATE_INIT, SA_ERROR_STATE);
+
+ ret = s->state == SA_STATE_INIT ? SA_SUCCESS : driver_change_rate(s, rate);
+
+ if (ret == SA_SUCCESS)
+ s->pcm_attrs.rate = rate;
+
+ sa_mutex_unlock(s->mutex);
+ return ret;
+}
+
+int sa_stream_change_user_data(sa_stream_t *s, const void *value) {
+ sa_return_val_if_fail(s->mutex, SA_ERROR_INVALID);
+ sa_mutex_lock(s->mutex);
+
+ s->user_data = (void*) value;
+
+ sa_mutex_unlock(s->mutex);
+ return SA_SUCCESS;
+}
+
+int sa_stream_set_adjust_rate(sa_stream_t *s, sa_adjust_t direction) {
+ sa_return_val_if_fail(s, SA_ERROR_INVALID);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, !s->codec, SA_ERROR_STATE);
+ sa_return_val_if_fail_mutex(s->mutex, s->state == SA_STATE_INIT, SA_ERROR_STATE);
+
+ s->adjust_rate = direction;
+
+ sa_mutex_unlock(s->mutex);
+ return SA_SUCCESS;
+}
+
+int sa_stream_set_adjust_nchannels(sa_stream_t *s, sa_adjust_t direction) {
+ sa_return_val_if_fail(s, SA_ERROR_INVALID);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, !s->codec, SA_ERROR_STATE);
+ sa_return_val_if_fail_mutex(s->mutex, s->state == SA_STATE_INIT, SA_ERROR_STATE);
+
+ s->adjust_rate = direction;
+
+ sa_mutex_unlock(s->mutex);
+ return SA_SUCCESS;
+}
+
+int sa_stream_set_adjust_pcm_format(sa_stream_t *s, sa_adjust_t direction) {
+ sa_return_val_if_fail(s, SA_ERROR_INVALID);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, !s->codec, SA_ERROR_STATE);
+ sa_return_val_if_fail_mutex(s->mutex, s->state == SA_STATE_INIT, SA_ERROR_STATE);
+
+ s->adjust_pcm_format = direction;
+
+ sa_mutex_unlock(s->mutex);
+ return SA_SUCCESS;
+}
+
+int sa_stream_set_adjust_watermarks(sa_stream_t *s, sa_adjust_t direction) {
+ sa_return_val_if_fail(s, SA_ERROR_INVALID);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, s->state == SA_STATE_INIT, SA_ERROR_STATE);
+
+ s->adjust_watermarks = direction;
+
+ sa_mutex_unlock(s->mutex);
+ return SA_SUCCESS;
+}
+
+int sa_stream_get_state(sa_stream_t *s, sa_state_t *state) {
+ int ret;
+ sa_return_val_if_fail(s, SA_ERROR_INVALID);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, state, SA_ERROR_INVALID);
+
+ if (s->state == SA_STATE_INIT) {
+ *state = s->state;
+ ret = SA_SUCCESS;
+ } else
+ ret = driver_get_state(s, state);
+
+ sa_mutex_unlock(s->mutex);
+ return ret;
+}
+
+int sa_stream_get_rate(sa_stream_t *s, unsigned *rate) {
+ sa_return_val_if_fail(s, SA_ERROR_INVALID);
+ sa_return_val_if_fail(rate, SA_ERROR_INVALID);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, !s->codec, SA_ERROR_STATE);
+
+ *rate = s->pcm_attrs.rate;
+
+ sa_mutex_unlock(s->mutex);
+ return SA_SUCCESS;
+}
+
+int sa_stream_get_nchannels(sa_stream_t *s, int *nchannels) {
+ sa_return_val_if_fail(s, SA_ERROR_INVALID);
+ sa_return_val_if_fail(nchannels, SA_ERROR_INVALID);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, !s->codec, SA_ERROR_STATE);
+
+ *nchannels = s->pcm_attrs.nchannels;
+
+ sa_mutex_unlock(s->mutex);
+ return SA_SUCCESS;
+}
+
+int sa_stream_get_pcm_format(sa_stream_t *s, sa_pcm_format_t *pcm_format) {
+ sa_return_val_if_fail(s, SA_ERROR_INVALID);
+ sa_return_val_if_fail(pcm_format, SA_ERROR_INVALID);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, !s->codec, SA_ERROR_STATE);
+
+ *pcm_format = s->pcm_attrs.format;
+ sa_mutex_unlock(s->mutex);
+ return SA_SUCCESS;
+}
+
+int sa_stream_get_mode(sa_stream_t *s, sa_mode_t *access_mode) {
+ sa_return_val_if_fail(s, SA_ERROR_INVALID);
+ sa_return_val_if_fail(access_mode, SA_ERROR_INVALID);
+ sa_mutex_lock(s->mutex);
+
+ *access_mode = s->mode;
+ sa_mutex_unlock(s->mutex);
+ return SA_SUCCESS;
+}
+
+int sa_stream_get_codec(sa_stream_t *s, char *codec, size_t *size) {
+ size_t n;
+
+ sa_return_val_if_fail(s, SA_ERROR_INVALID);
+ sa_return_val_if_fail(size && (*size == 0 || codec), SA_ERROR_INVALID);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, s->codec, SA_ERROR_STATE);
+
+ n = strlen(s->codec)+1;
+ if (*size < n) {
+ sa_mutex_unlock(s->mutex);
+ return SA_ERROR_NO_SPACE;
+ }
+
+ if (codec)
+ strcpy(codec, s->codec);
+ *size = n;
+
+ sa_mutex_unlock(s->mutex);
+ return SA_SUCCESS;
+}
+
+int sa_stream_get_write_lower_watermark(sa_stream_t *s, size_t *size) {
+ sa_return_val_if_fail(s, SA_ERROR_INVALID);
+ sa_return_val_if_fail(size, SA_ERROR_INVALID);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, s->mode & SA_MODE_WRONLY, SA_ERROR_STATE);
+
+ *size = s->write_lower_watermark;
+
+ sa_mutex_unlock(s->mutex);
+ return SA_SUCCESS;
+}
+
+int sa_stream_get_read_lower_watermark(sa_stream_t *s, size_t *size) {
+ sa_return_val_if_fail(s, SA_ERROR_INVALID);
+ sa_return_val_if_fail(size, SA_ERROR_INVALID);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, s->mode & SA_MODE_RDONLY, SA_ERROR_STATE);
+
+ *size = s->read_lower_watermark;
+
+ sa_mutex_unlock(s->mutex);
+ return SA_SUCCESS;
+}
+
+int sa_stream_get_write_upper_watermark(sa_stream_t *s, size_t *size) {
+ sa_return_val_if_fail(s, SA_ERROR_INVALID);
+ sa_return_val_if_fail(size, SA_ERROR_INVALID);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, s->mode & SA_MODE_WRONLY, SA_ERROR_STATE);
+
+ *size = s->write_upper_watermark;
+
+ sa_mutex_unlock(s->mutex);
+ return SA_SUCCESS;
+}
+
+int sa_stream_get_read_upper_watermark(sa_stream_t *s, size_t *size) {
+ sa_return_val_if_fail(s, SA_ERROR_INVALID);
+ sa_return_val_if_fail(size, SA_ERROR_INVALID);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, s->mode & SA_MODE_RDONLY, SA_ERROR_STATE);
+
+ *size = s->read_upper_watermark;
+
+ sa_mutex_unlock(s->mutex);
+ return SA_SUCCESS;
+}
+
+int sa_stream_get_channel_map(sa_stream_t *s, sa_channel_t *map, unsigned *n) {
+ sa_return_val_if_fail(s, SA_ERROR_INVALID);
+ sa_return_val_if_fail(n && (*n == 0 || map), SA_ERROR_INVALID);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, !s->codec, SA_ERROR_STATE);
+
+ if (*n < s->pcm_attrs.nchannels) {
+ sa_mutex_unlock(s->mutex);
+ return SA_ERROR_NO_SPACE;
+ }
+
+ if (map)
+ memcpy(map, s->pcm_attrs.channel_map, s->pcm_attrs.nchannels * sizeof(sa_channel_t));
+ *n = s->pcm_attrs.nchannels;
+
+ sa_mutex_unlock(s->mutex);
+ return SA_SUCCESS;
+}
+
+int sa_stream_get_xrun_mode(sa_stream_t *s, sa_xrun_mode_t *mode) {
+ sa_return_val_if_fail(s, SA_ERROR_INVALID);
+ sa_return_val_if_fail(mode, SA_ERROR_INVALID);
+ sa_mutex_lock(s->mutex);
+
+ *mode = s->xrun_mode;
+
+ sa_mutex_unlock(s->mutex);
+ return SA_SUCCESS;
+}
+
+int sa_stream_get_non_interleaved(sa_stream_t *s, int *enabled) {
+ sa_return_val_if_fail(s, SA_ERROR_INVALID);
+ sa_return_val_if_fail(enabled, SA_ERROR_INVALID);
+ sa_mutex_lock(s->mutex);
+
+ *enabled = s->ni_enabled;
+
+ sa_mutex_unlock(s->mutex);
+ return SA_SUCCESS;
+}
+
+int sa_stream_get_dynamic_rate(sa_stream_t *s, int *enabled) {
+ sa_return_val_if_fail(s, SA_ERROR_INVALID);
+ sa_return_val_if_fail(enabled, SA_ERROR_INVALID);
+ sa_mutex_lock(s->mutex);
+
+ *enabled = s->dynamic_rate_enabled;
+
+ sa_mutex_unlock(s->mutex);
+ return SA_SUCCESS;
+}
+
+int sa_stream_get_driver(sa_stream_t *s, char *driver, size_t *size) {
+ size_t n;
+
+ sa_return_val_if_fail(s, SA_ERROR_INVALID);
+ sa_return_val_if_fail(size && (*size == 0 || driver), SA_ERROR_INVALID);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, s->driver, SA_ERROR_STATE);
+
+ n = strlen(s->driver)+1;
+ if (*size < n) {
+ sa_mutex_unlock(s->mutex);
+ return SA_ERROR_NO_SPACE;
+ }
+
+ if (driver)
+ strcpy(driver, s->driver);
+ *size = n;
+
+ sa_mutex_unlock(s->mutex);
+ return SA_SUCCESS;
+}
+
+int sa_stream_get_device(sa_stream_t *s, char *device, size_t *size) {
+ size_t n;
+
+ sa_return_val_if_fail(s, SA_ERROR_INVALID);
+ sa_return_val_if_fail(size && (*size == 0 || device), SA_ERROR_INVALID);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, s->device, SA_ERROR_STATE);
+
+ n = strlen(s->device)+1;
+ if (*size < n) {
+ sa_mutex_unlock(s->mutex);
+ return SA_ERROR_NO_SPACE;
+ }
+
+ if (device)
+ strcpy(device, s->device);
+ *size = n;
+
+ sa_mutex_unlock(s->mutex);
+ return SA_SUCCESS;
+}
+
+int sa_stream_get_read_volume(sa_stream_t *s, int32_t vol[], unsigned *n) {
+ sa_return_val_if_fail(s, SA_ERROR_INVALID);
+ sa_return_val_if_fail(n && (*n == 0 || vol), SA_ERROR_INVALID);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, s->mode & SA_MODE_RDONLY, SA_ERROR_STATE);
+ sa_return_val_if_fail_mutex(s->mutex, s->read_volume, SA_ERROR_STATE);
+
+ if (*n < s->pcm_attrs.nchannels) {
+ sa_mutex_unlock(s->mutex);
+ return SA_ERROR_NO_SPACE;
+ }
+
+ if (vol)
+ memcpy(vol, s->read_volume, s->pcm_attrs.nchannels * sizeof(int32_t));
+ *n = s->pcm_attrs.nchannels;
+
+ sa_mutex_unlock(s->mutex);
+ return SA_SUCCESS;
+}
+
+int sa_stream_get_write_volume(sa_stream_t *s, int32_t vol[], unsigned *n) {
+ sa_return_val_if_fail(s, SA_ERROR_INVALID);
+ sa_return_val_if_fail(n && (*n == 0 || vol), SA_ERROR_INVALID);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, s->mode & SA_MODE_WRONLY, SA_ERROR_STATE);
+ sa_return_val_if_fail_mutex(s->mutex, s->write_volume, SA_ERROR_STATE);
+
+ if (*n < s->pcm_attrs.nchannels) {
+ sa_mutex_unlock(s->mutex);
+ return SA_ERROR_NO_SPACE;
+ }
+
+ if (vol)
+ memcpy(vol, s->write_volume, s->pcm_attrs.nchannels * sizeof(int32_t));
+ *n = s->pcm_attrs.nchannels;
+
+ sa_mutex_unlock(s->mutex);
+ return SA_SUCCESS;
+}
+
+int sa_stream_get_adjust_rate(sa_stream_t *s, sa_adjust_t *direction) {
+ sa_return_val_if_fail(s, SA_ERROR_INVALID);
+ sa_return_val_if_fail(direction, SA_ERROR_INVALID);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, !s->codec, SA_ERROR_STATE);
+
+ *direction = s->adjust_rate;
+
+ sa_mutex_unlock(s->mutex);
+ return SA_SUCCESS;
+}
+
+int sa_stream_get_adjust_nchannels(sa_stream_t *s, sa_adjust_t *direction) {
+ sa_return_val_if_fail(s, SA_ERROR_INVALID);
+ sa_return_val_if_fail(direction, SA_ERROR_INVALID);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, !s->codec, SA_ERROR_STATE);
+
+ *direction = s->adjust_nchannels;
+
+ sa_mutex_unlock(s->mutex);
+ return SA_SUCCESS;
+}
+
+int sa_stream_get_adjust_pcm_format(sa_stream_t *s, sa_adjust_t *direction) {
+ sa_return_val_if_fail(s, SA_ERROR_INVALID);
+ sa_return_val_if_fail(direction, SA_ERROR_INVALID);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, !s->codec, SA_ERROR_STATE);
+
+ *direction = s->adjust_pcm_format;
+
+ sa_mutex_unlock(s->mutex);
+ return SA_SUCCESS;
+}
+
+int sa_stream_get_adjust_watermarks(sa_stream_t *s, sa_adjust_t *direction) {
+ sa_return_val_if_fail_mutex(s->mutex, s, SA_ERROR_INVALID);
+ sa_return_val_if_fail_mutex(s->mutex, direction, SA_ERROR_INVALID);
+ sa_mutex_lock(s->mutex);
+
+ *direction = s->adjust_watermarks;
+
+ sa_mutex_unlock(s->mutex);
+ return SA_SUCCESS;
+}
+
+int sa_stream_get_user_data(sa_stream_t *s, void **value) {
+ sa_return_val_if_fail(s, SA_ERROR_INVALID);
+ sa_return_val_if_fail(value, SA_ERROR_INVALID);
+ sa_mutex_lock(s->mutex);
+
+ *value = s->user_data;
+
+ sa_mutex_unlock(s->mutex);
+ return SA_SUCCESS;
+}
+
+int sa_stream_get_event_error(sa_stream_t *s, sa_error_t *error) {
+ sa_return_val_if_fail(s, SA_ERROR_INVALID);
+ sa_return_val_if_fail(error, SA_ERROR_INVALID);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, s->event == SA_EVENT_ERROR, SA_ERROR_STATE);
+
+ *error = s->error;
+
+ sa_mutex_unlock(s->mutex);
+ return SA_SUCCESS;
+}
+
+int sa_stream_get_event_notify(sa_stream_t *s, sa_notify_t *notify) {
+ sa_return_val_if_fail(s, SA_ERROR_INVALID);
+ sa_return_val_if_fail(notify, SA_ERROR_INVALID);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, s->event == SA_EVENT_NOTIFY, SA_ERROR_STATE);
+
+ *notify = s->notify;
+
+ sa_mutex_unlock(s->mutex);
+ return SA_SUCCESS;
+}
+
+int sa_stream_get_position(sa_stream_t *s, sa_position_t position, int64_t *pos) {
+ int ret;
+
+ sa_return_val_if_fail(s, SA_ERROR_INVALID);
+ sa_return_val_if_fail(pos, SA_ERROR_INVALID);
+ sa_return_val_if_fail(position < _SA_POSITION_MAX, SA_ERROR_INVALID);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, s->state == SA_STATE_RUNNING || s->state == SA_STATE_STOPPED, SA_ERROR_STATE);
+
+ ret = driver_get_position(s, position, pos);
+
+ sa_mutex_unlock(s->mutex);
+ return ret;
+}
+
+int sa_stream_read(sa_stream_t *s, void *data, size_t nbytes) {
+ int ret;
+ sa_return_val_if_fail(s, SA_ERROR_INVALID);
+ sa_return_val_if_fail(data, SA_ERROR_INVALID);
+ sa_return_val_if_fail(nbytes > 0, SA_ERROR_INVALID);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, !s->ni_enabled, SA_ERROR_STATE);
+ sa_return_val_if_fail_mutex(s->mutex, s->codec || (nbytes % s->pcm_frame_size) == 0, SA_ERROR_INVALID);
+ sa_return_val_if_fail_mutex(s->mutex, s->mode & SA_MODE_RDONLY, SA_ERROR_STATE);
+ sa_return_val_if_fail_mutex(s->mutex, s->state == SA_STATE_RUNNING || s->state == SA_STATE_STOPPED, SA_ERROR_STATE);
+
+ ret = driver_read(s, data, nbytes);
+
+ sa_mutex_unlock(s->mutex);
+ return ret;
+}
+
+int sa_stream_read_ni(sa_stream_t *s, unsigned channel, void *data, size_t nbytes) {
+ int ret;
+
+ sa_return_val_if_fail(s, SA_ERROR_INVALID);
+ sa_return_val_if_fail(data, SA_ERROR_INVALID);
+ sa_return_val_if_fail(nbytes > 0, SA_ERROR_INVALID);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, !s->codec, SA_ERROR_STATE);
+ sa_return_val_if_fail_mutex(s->mutex, channel < s->pcm_attrs.nchannels, SA_ERROR_INVALID);
+ sa_return_val_if_fail_mutex(s->mutex, s->ni_enabled, SA_ERROR_STATE);
+ sa_return_val_if_fail_mutex(s->mutex, (nbytes % s->pcm_sample_size) == 0, SA_ERROR_INVALID);
+ sa_return_val_if_fail_mutex(s->mutex, s->mode & SA_MODE_RDONLY, SA_ERROR_STATE);
+ sa_return_val_if_fail_mutex(s->mutex, s->state == SA_STATE_RUNNING || s->state == SA_STATE_STOPPED, SA_ERROR_STATE);
+
+ ret = driver_read_ni(s, channel, data, nbytes);
+
+ sa_mutex_unlock(s->mutex);
+ return ret;
+}
+
+int sa_stream_write(sa_stream_t *s, const void *data, size_t nbytes) {
+ return sa_stream_pwrite(s, data, nbytes, 0, SA_SEEK_RELATIVE);
+}
+
+int sa_stream_write_ni(sa_stream_t *s, unsigned channel, const void *data, size_t nbytes) {
+ return sa_stream_pwrite_ni(s, channel, data, nbytes, 0, SA_SEEK_RELATIVE);
+}
+
+int sa_stream_pwrite(sa_stream_t *s, const void *data, size_t nbytes, int64_t offset, sa_seek_t whence) {
+ int ret;
+
+ sa_return_val_if_fail(s, SA_ERROR_INVALID);
+ sa_return_val_if_fail(data, SA_ERROR_INVALID);
+ sa_return_val_if_fail(nbytes > 0, SA_ERROR_INVALID);
+ sa_return_val_if_fail(whence == SA_SEEK_RELATIVE || whence == SA_SEEK_ABSOLUTE || whence == SA_SEEK_RELATIVE_END, SA_ERROR_INVALID);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, !s->ni_enabled, SA_ERROR_STATE);
+ sa_return_val_if_fail_mutex(s->mutex, s->codec || (nbytes % s->pcm_frame_size) == 0, SA_ERROR_INVALID);
+ sa_return_val_if_fail_mutex(s->mutex, s->codec || (offset % s->pcm_frame_size) == 0, SA_ERROR_INVALID);
+ sa_return_val_if_fail_mutex(s->mutex, s->mode & SA_MODE_WRONLY, SA_ERROR_STATE);
+ sa_return_val_if_fail_mutex(s->mutex, s->state == SA_STATE_RUNNING || s->state == SA_STATE_STOPPED, SA_ERROR_STATE);
+
+ ret = driver_pwrite(s, data, nbytes, offset, whence);
+
+ sa_mutex_unlock(s->mutex);
+ return ret;
+}
+
+int sa_stream_pwrite_ni(sa_stream_t *s, unsigned channel, const void *data, size_t nbytes, int64_t offset, sa_seek_t whence) {
+ int ret;
+
+ sa_return_val_if_fail(s, SA_ERROR_INVALID);
+ sa_return_val_if_fail(data, SA_ERROR_INVALID);
+ sa_return_val_if_fail(nbytes > 0, SA_ERROR_INVALID);
+ sa_return_val_if_fail(whence == SA_SEEK_RELATIVE || whence == SA_SEEK_ABSOLUTE || whence == SA_SEEK_RELATIVE_END, SA_ERROR_INVALID);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, !s->codec, SA_ERROR_STATE);
+ sa_return_val_if_fail_mutex(s->mutex, channel < s->pcm_attrs.nchannels, SA_ERROR_INVALID);
+ sa_return_val_if_fail_mutex(s->mutex, s->ni_enabled, SA_ERROR_STATE);
+ sa_return_val_if_fail_mutex(s->mutex, (nbytes % s->pcm_sample_size) == 0, SA_ERROR_INVALID);
+ sa_return_val_if_fail_mutex(s->mutex, (offset % s->pcm_sample_size) == 0, SA_ERROR_INVALID);
+ sa_return_val_if_fail_mutex(s->mutex, s->mode & SA_MODE_WRONLY, SA_ERROR_STATE);
+ sa_return_val_if_fail_mutex(s->mutex, s->state == SA_STATE_RUNNING || s->state == SA_STATE_STOPPED, SA_ERROR_STATE);
+
+ ret = driver_pwrite_ni(s, channel, data, nbytes, offset, whence);
+
+ sa_mutex_unlock(s->mutex);
+ return ret;
+}
+
+int sa_stream_get_read_size(sa_stream_t *s, size_t *size) {
+ int ret;
+
+ sa_return_val_if_fail(s, SA_ERROR_INVALID);
+ sa_return_val_if_fail(size, SA_ERROR_INVALID);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, s->mode & SA_MODE_RDONLY, SA_ERROR_STATE);
+ sa_return_val_if_fail_mutex(s->mutex, s->state == SA_STATE_RUNNING || s->state == SA_STATE_STOPPED, SA_ERROR_STATE);
+
+ ret = driver_get_read_size(s, size);
+
+ sa_mutex_unlock(s->mutex);
+ return ret;
+}
+
+int sa_stream_get_write_size(sa_stream_t *s, size_t *size) {
+ int ret;
+
+ sa_return_val_if_fail(s, SA_ERROR_INVALID);
+ sa_return_val_if_fail(size, SA_ERROR_INVALID);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, s->mode & SA_MODE_WRONLY, SA_ERROR_STATE);
+ sa_return_val_if_fail_mutex(s->mutex, s->state == SA_STATE_RUNNING || s->state == SA_STATE_STOPPED, SA_ERROR_STATE);
+
+ ret = driver_get_write_size(s, size);
+
+ sa_mutex_unlock(s->mutex);
+ return ret;
+}
+
+int sa_stream_resume(sa_stream_t *s) {
+ int ret;
+
+ sa_return_val_if_fail(s, SA_ERROR_INVALID);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, s->state == SA_STATE_RUNNING || s->state == SA_STATE_STOPPED, SA_ERROR_STATE);
+
+ ret = driver_resume(s);
+
+ sa_mutex_unlock(s->mutex);
+ return ret;
+}
+
+int sa_stream_pause(sa_stream_t *s) {
+ int ret;
+
+ sa_return_val_if_fail(s, SA_ERROR_INVALID);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, s->state == SA_STATE_RUNNING || s->state == SA_STATE_STOPPED, SA_ERROR_STATE);
+
+ ret = driver_pause(s);
+
+ sa_mutex_unlock(s->mutex);
+ return ret;
+}
+
+int sa_stream_drain(sa_stream_t *s) {
+ int ret;
+
+ sa_return_val_if_fail(s, SA_ERROR_INVALID);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, s->mode & SA_MODE_WRONLY, SA_ERROR_STATE);
+ sa_return_val_if_fail_mutex(s->mutex, s->state == SA_STATE_RUNNING || s->state == SA_STATE_STOPPED, SA_ERROR_STATE);
+
+ ret = driver_drain(s);
+
+ sa_mutex_unlock(s->mutex);
+ return ret;
+}
+
+size_t sa_get_pcm_sample_size(sa_pcm_format_t f) {
+
+ switch (f) {
+ case SA_PCM_FORMAT_U8:
+ case SA_PCM_FORMAT_ULAW:
+ case SA_PCM_FORMAT_ALAW:
+ return 1;
+
+ case SA_PCM_FORMAT_S16_LE:
+ case SA_PCM_FORMAT_S16_BE:
+ return 2;
+ case SA_PCM_FORMAT_S24_LE:
+ case SA_PCM_FORMAT_S24_BE:
+ return 3;
+
+ case SA_PCM_FORMAT_S32_LE:
+ case SA_PCM_FORMAT_S32_BE:
+ case SA_PCM_FORMAT_FLOAT32_LE:
+ case SA_PCM_FORMAT_FLOAT32_BE:
+ return 4;
+
+ case _SA_PCM_FORMAT_MAX:
+ ;
+ }
+
+ sa_assert_not_reached();
+}
+
+static int meta_check_png(const void *data, size_t size) {
+ static const uint8_t png_signature[] = { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A };
+ sa_assert(data);
+
+ if (size < sizeof(png_signature))
+ return 0;
+
+ return memcmp(data, png_signature, 8) == 0;
+}
+
+static int meta_check_utf8(const void *data, size_t size) {
+ int32_t idx;
+
+ oil_utf8_validate(&idx, data, size);
+
+ return (size_t) idx == size;
+}
+
+static int meta_check_pid(const void *data, size_t size) {
+ long int pid;
+ char *t;
+
+ if (size <= 0)
+ return 0;
+
+ if (memchr(data, 0, size))
+ return 0;
+
+ if (!(t = sa_strndup(data, size)))
+ return 0;
+
+ errno = 0;
+ pid = strtol(t, NULL, 10);
+ sa_free(t);
+
+ if (errno != 0)
+ return 0;
+
+ if (pid <= 1)
+ return 0;
+
+ return 1;
+}
+
+static int meta_check_icon_name(const void *data, size_t size) {
+ const char *t = data;
+
+ if (size <= 0)
+ return 0;
+
+ if (memchr(data, 0, size))
+ return 0;
+
+ if (size == 1 && t[0] == '.')
+ return 0;
+
+ if (size == 2 && t[0] == '.' && t[1] == '.')
+ return 0;
+
+ if (memchr(t, '/', size))
+ return 0;
+
+ return 1;
+}
+
+static int meta_check_word(const void *data, size_t size) {
+ const char *t = data;
+
+ for (; size > 0; size --, t++)
+ if (*t <= 32 || *t >= 127)
+ return 0;
+
+ return 1;
+}
+
+typedef int (*meta_check_func_t)(const void *data, size_t size);
+
+int sa_stream_change_meta_data(sa_stream_t *s, const char *name, const void *data, size_t size) {
+ void *d = NULL;
+ const struct meta_name *m;
+ int ret;
+
+ static const meta_check_func_t check_table[] = {
+ meta_check_utf8,
+ meta_check_pid,
+ meta_check_word, /* FIXME */
+ meta_check_utf8,
+ meta_check_icon_name,
+ meta_check_png,
+ meta_check_word, /* FIXME */
+ meta_check_word, /* FIXME */
+ };
+
+ sa_return_val_if_fail(s, SA_ERROR_INVALID);
+ sa_return_val_if_fail(name, SA_ERROR_INVALID);
+ sa_mutex_lock(s->mutex);
+ sa_return_val_if_fail_mutex(s->mutex, data || size == 0, SA_ERROR_INVALID);
+
+ if (!(m = sa_lookup_meta_name(name, strlen(name)))) {
+ sa_mutex_unlock(s->mutex);
+ return SA_ERROR_NO_META;
+ }
+
+ if (!check_table[m->idx](data, size)) {
+ sa_mutex_unlock(s->mutex);
+ return SA_ERROR_INVALID;
+ }
+
+ if (data)
+ if (!(d = sa_memdup(data, size))) {
+ sa_mutex_unlock(s->mutex);
+ return SA_ERROR_OOM;
+ }
+
+ ret = s->state == SA_STATE_INIT ? SA_SUCCESS : driver_change_meta_data(s, name, data, size);
+
+ if (ret == SA_SUCCESS) {
+ sa_free(s->meta_data[m->idx]);
+ s->meta_data[m->idx] = d;
+ s->meta_data_size[m->idx] = size;
+ } else
+ sa_free(d);
+
+ sa_mutex_unlock(s->mutex);
+ return ret;
+}
+
+int sa_stream_get_meta_data(sa_stream_t *s, const char *name, void *data, size_t *size) {
+ const struct meta_name *m;
+
+ sa_return_val_if_fail(s, SA_ERROR_INVALID);
+ sa_return_val_if_fail(name, SA_ERROR_INVALID);
+ sa_return_val_if_fail(size && (*size == 0 || data), SA_ERROR_INVALID);
+ sa_mutex_lock(s->mutex);
+
+ if (!(m = sa_lookup_meta_name(name, strlen(name)))) {
+ sa_mutex_unlock(s->mutex);
+ return SA_ERROR_NO_META;
+ }
+
+ if (!s->meta_data[m->idx]) {
+ sa_mutex_unlock(s->mutex);
+ return SA_ERROR_NO_DATA;
+ }
+
+ if (*size < s->meta_data_size[m->idx]) {
+ sa_mutex_unlock(s->mutex);
+ return SA_ERROR_NO_SPACE;
+ }
+
+ if (data)
+ memcpy(data, s->meta_data[m->idx], s->meta_data_size[m->idx]);
+
+ *size = s->meta_data_size[m->idx];
+
+ sa_mutex_unlock(s->mutex);
+ return SA_SUCCESS;
+}
+
+const char *sa_strerror(int code) {
+ const char * const error_table[-_SA_ERROR_MAX] = {
+ [-SA_SUCCESS] = "Success",
+ [-SA_ERROR_NOT_SUPPORTED] = "Operation not supported",
+ [-SA_ERROR_INVALID] = "Invalid argument",
+ [-SA_ERROR_STATE] = "Invalid state",
+ [-SA_ERROR_OOM] = "Out of memory",
+ [-SA_ERROR_NO_DEVICE] = "No such device",
+ [-SA_ERROR_NO_DRIVER] = "No such driver",
+ [-SA_ERROR_NO_CODEC] = "No such codec",
+ [-SA_ERROR_NO_PCM_FORMAT] = "No such PCM format",
+ [-SA_ERROR_SYSTEM] = "System error",
+ [-SA_ERROR_NO_INIT] = "Not initialized",
+ [-SA_ERROR_NO_META] = "No such meta name",
+ [-SA_ERROR_NO_DATA] = "No such data"
+ };
+
+ sa_return_val_if_fail(code <= 0, NULL);
+ sa_return_val_if_fail(code > _SA_ERROR_MAX, NULL);
+
+ return error_table[-code];
+}