--- a/media/libcubeb/src/cubeb_alsa.c
+++ b/media/libcubeb/src/cubeb_alsa.c
@@ -80,17 +80,19 @@ struct cubeb_stream {
pthread_mutex_t mutex;
snd_pcm_t * pcm;
cubeb_data_callback data_callback;
cubeb_state_callback state_callback;
void * user_ptr;
snd_pcm_uframes_t write_position;
snd_pcm_uframes_t last_position;
snd_pcm_uframes_t buffer_size;
- cubeb_stream_params params;
+ snd_pcm_uframes_t period_size;
+ cubeb_stream_params input_params;
+ cubeb_stream_params output_params;
/* Every member after this comment is protected by the owning context's
mutex rather than the stream's mutex, or is only used on the context's
run thread. */
pthread_cond_t cond; /* Signaled when the stream's state is changed. */
enum stream_state state;
@@ -311,39 +313,39 @@ alsa_refill_stream(cubeb_stream * stm)
if (got < 0) {
pthread_mutex_unlock(&stm->mutex);
stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
return ERROR;
}
if (got > 0) {
snd_pcm_sframes_t wrote;
- if (stm->params.format == CUBEB_SAMPLE_FLOAT32NE) {
+ if (stm->output_params.format == CUBEB_SAMPLE_FLOAT32NE) {
float * b = (float *) p;
- for (uint32_t i = 0; i < got * stm->params.channels; i++) {
+ for (uint32_t i = 0; i < got * stm->output_params.channels; i++) {
b[i] *= stm->volume;
}
} else {
short * b = (short *) p;
- for (uint32_t i = 0; i < got * stm->params.channels; i++) {
+ for (uint32_t i = 0; i < got * stm->output_params.channels; i++) {
b[i] *= stm->volume;
}
}
wrote = snd_pcm_writei(stm->pcm, p, got);
if (wrote == -EPIPE) {
snd_pcm_recover(stm->pcm, wrote, 1);
wrote = snd_pcm_writei(stm->pcm, p, got);
}
assert(wrote >= 0 && wrote == got);
stm->write_position += wrote;
gettimeofday(&stm->last_activity, NULL);
}
if (got != avail) {
long buffer_fill = stm->buffer_size - (avail - got);
- double buffer_time = (double) buffer_fill / stm->params.rate;
+ double buffer_time = (double) buffer_fill / stm->output_params.rate;
/* Fill the remaining buffer with silence to guarantee one full period
has been written. */
snd_pcm_writei(stm->pcm, (char *) p + got, avail - got);
set_timeout(&stm->drain_timeout, buffer_time * 1000);
draining = 1;
@@ -775,31 +777,35 @@ alsa_destroy(cubeb * ctx)
}
free(ctx);
}
static void alsa_stream_destroy(cubeb_stream * stm);
static int
-alsa_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name,
- cubeb_stream_params stream_params, unsigned int latency,
- cubeb_data_callback data_callback, cubeb_state_callback state_callback,
+alsa_stream_init(cubeb * ctx,
+ cubeb_stream ** stream,
+ char const * stream_name,
+ cubeb_stream_params* input_stream_params,
+ cubeb_stream_params* output_stream_params,
+ unsigned int latency,
+ cubeb_data_callback data_callback,
+ cubeb_state_callback state_callback,
void * user_ptr)
{
cubeb_stream * stm;
int r;
snd_pcm_format_t format;
- snd_pcm_uframes_t period_size;
assert(ctx && stream);
*stream = NULL;
- switch (stream_params.format) {
+ switch (output_stream_params->format) {
case CUBEB_SAMPLE_S16LE:
format = SND_PCM_FORMAT_S16_LE;
break;
case CUBEB_SAMPLE_S16BE:
format = SND_PCM_FORMAT_S16_BE;
break;
case CUBEB_SAMPLE_FLOAT32LE:
format = SND_PCM_FORMAT_FLOAT_LE;
@@ -821,17 +827,18 @@ alsa_stream_init(cubeb * ctx, cubeb_stre
stm = calloc(1, sizeof(*stm));
assert(stm);
stm->context = ctx;
stm->data_callback = data_callback;
stm->state_callback = state_callback;
stm->user_ptr = user_ptr;
- stm->params = stream_params;
+ stm->input_params = *input_stream_params;
+ stm->output_params = *output_stream_params;
stm->state = INACTIVE;
stm->volume = 1.0;
r = pthread_mutex_init(&stm->mutex, NULL);
assert(r == 0);
r = alsa_locked_pcm_open(&stm->pcm, SND_PCM_STREAM_PLAYBACK, ctx->local_config);
if (r < 0) {
@@ -845,24 +852,24 @@ alsa_stream_init(cubeb * ctx, cubeb_stre
/* Ugly hack: the PA ALSA plugin allows buffer configurations that can't
possibly work. See https://bugzilla.mozilla.org/show_bug.cgi?id=761274.
Only resort to this hack if the handle_underrun workaround failed. */
if (!ctx->local_config && ctx->is_pa) {
latency = latency < 500 ? 500 : latency;
}
r = snd_pcm_set_params(stm->pcm, format, SND_PCM_ACCESS_RW_INTERLEAVED,
- stm->params.channels, stm->params.rate, 1,
+ stm->output_params.channels, stm->output_params.rate, 1,
latency * 1000);
if (r < 0) {
alsa_stream_destroy(stm);
return CUBEB_ERROR_INVALID_FORMAT;
}
- r = snd_pcm_get_params(stm->pcm, &stm->buffer_size, &period_size);
+ r = snd_pcm_get_params(stm->pcm, &stm->buffer_size, &stm->period_size);
assert(r == 0);
stm->nfds = snd_pcm_poll_descriptors_count(stm->pcm);
assert(stm->nfds > 0);
stm->saved_fds = calloc(stm->nfds, sizeof(struct pollfd));
assert(stm->saved_fds);
r = snd_pcm_poll_descriptors(stm->pcm, stm->saved_fds, stm->nfds);
@@ -928,17 +935,17 @@ alsa_get_max_channel_count(cubeb * ctx,
params.rate = 44100;
params.format = CUBEB_SAMPLE_FLOAT32NE;
params.channels = 2;
snd_pcm_hw_params_alloca(&hw_params);
assert(ctx);
- r = alsa_stream_init(ctx, &stm, "", params, 100, NULL, NULL, NULL);
+ r = alsa_stream_init(ctx, &stm, "", NULL, ¶ms, 100, NULL, NULL, NULL);
if (r != CUBEB_OK) {
return CUBEB_ERROR;
}
r = snd_pcm_hw_params_any(stm->pcm, hw_params);
if (r < 0) {
return CUBEB_ERROR;
}
--- a/media/libcubeb/src/cubeb_pulse.c
+++ b/media/libcubeb/src/cubeb_pulse.c
@@ -65,42 +65,52 @@
X(pa_threaded_mainloop_lock) \
X(pa_threaded_mainloop_new) \
X(pa_threaded_mainloop_signal) \
X(pa_threaded_mainloop_start) \
X(pa_threaded_mainloop_stop) \
X(pa_threaded_mainloop_unlock) \
X(pa_threaded_mainloop_wait) \
X(pa_usec_to_bytes) \
+ X(pa_stream_set_read_callback) \
+ X(pa_stream_connect_record) \
+ X(pa_stream_readable_size) \
+ X(pa_stream_peek) \
+ X(pa_stream_drop) \
+ X(pa_stream_writable_size) \
+ X(pa_stream_trigger) \
#define MAKE_TYPEDEF(x) static typeof(x) * cubeb_##x;
LIBPULSE_API_VISIT(MAKE_TYPEDEF);
#undef MAKE_TYPEDEF
#endif
+#define MIN_LATENCY_MS 30
+
static struct cubeb_ops const pulse_ops;
struct cubeb {
struct cubeb_ops const * ops;
void * libpulse;
pa_threaded_mainloop * mainloop;
pa_context * context;
pa_sink_info * default_sink_info;
char * context_name;
int error;
};
struct cubeb_stream {
cubeb * context;
- pa_stream * stream;
+ pa_stream * output_stream;
+ pa_stream * input_stream;
cubeb_data_callback data_callback;
cubeb_state_callback state_callback;
void * user_ptr;
pa_time_event * drain_timer;
- pa_sample_spec sample_spec;
+ pa_sample_spec output_sample_spec;
int shutdown;
float volume;
};
const float PULSE_NO_GAIN = -1.0;
enum cork_state {
UNCORK = 0,
@@ -108,16 +118,17 @@ enum cork_state {
NOTIFY = 1 << 1
};
static void
sink_info_callback(pa_context * context, const pa_sink_info * info, int eol, void * u)
{
cubeb * ctx = u;
if (!eol) {
+ free(ctx->default_sink_info);
ctx->default_sink_info = malloc(sizeof(pa_sink_info));
memcpy(ctx->default_sink_info, info, sizeof(pa_sink_info));
}
WRAP(pa_threaded_mainloop_signal)(ctx->mainloop, 0);
}
static void
server_info_callback(pa_context * context, const pa_server_info * info, void * u)
@@ -165,56 +176,49 @@ stream_state_callback(pa_stream * s, voi
cubeb_stream * stm = u;
if (!PA_STREAM_IS_GOOD(WRAP(pa_stream_get_state)(s))) {
stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
}
WRAP(pa_threaded_mainloop_signal)(stm->context->mainloop, 0);
}
static void
-stream_request_callback(pa_stream * s, size_t nbytes, void * u)
+write_to_output(pa_stream * s, void* input_data, size_t nbytes, cubeb_stream * stm)
{
- cubeb_stream * stm;
void * buffer;
size_t size;
int r;
long got;
size_t towrite;
size_t frame_size;
- stm = u;
-
- if (stm->shutdown)
- return;
-
- frame_size = WRAP(pa_frame_size)(&stm->sample_spec);
-
+ frame_size = WRAP(pa_frame_size)(&stm->output_sample_spec);
assert(nbytes % frame_size == 0);
towrite = nbytes;
-
while (towrite) {
size = towrite;
r = WRAP(pa_stream_begin_write)(s, &buffer, &size);
assert(r == 0);
assert(size > 0);
assert(size % frame_size == 0);
- got = stm->data_callback(stm, stm->user_ptr, NULL, buffer, size / frame_size);
+// printf("data cb buffer size %zd\n", size);
+ got = stm->data_callback(stm, stm->user_ptr, input_data, buffer, size / frame_size);
if (got < 0) {
WRAP(pa_stream_cancel_write)(s);
stm->shutdown = 1;
return;
}
if (stm->volume != PULSE_NO_GAIN) {
- uint32_t samples = size * stm->sample_spec.channels / frame_size ;
+ uint32_t samples = size * stm->output_sample_spec.channels / frame_size ;
- if (stm->sample_spec.format == PA_SAMPLE_S16BE ||
- stm->sample_spec.format == PA_SAMPLE_S16LE) {
+ if (stm->output_sample_spec.format == PA_SAMPLE_S16BE ||
+ stm->output_sample_spec.format == PA_SAMPLE_S16LE) {
short * b = buffer;
for (uint32_t i = 0; i < samples; i++) {
b[i] *= stm->volume;
}
} else {
float * b = buffer;
for (uint32_t i = 0; i < samples; i++) {
b[i] *= stm->volume;
@@ -241,40 +245,131 @@ stream_request_callback(pa_stream * s, s
}
towrite -= size;
}
assert(towrite == 0);
}
+static void
+stream_request_callback(pa_stream * s, size_t nbytes, void * u)
+{
+// printf("-- write size %zd -->\n", nbytes);
+ cubeb_stream * stm;
+ stm = u;
+ if (stm->shutdown) {
+ return;
+ }
+
+ // input/capture + output/record operation
+ // return here it is handled by read callback
+ if (stm->input_stream) {
+ return;
+ }
+
+ // Output/record only operation
+ assert(!stm->input_stream && stm->output_stream);
+ write_to_output(s, NULL, nbytes, stm);
+}
+
+static void
+stream_read_callback(pa_stream * s, size_t nbytes, void * u)
+{
+// printf("<-- read size %zd -- ", nbytes);
+ cubeb_stream * stm;
+ stm = u;
+ if (stm->shutdown) {
+ return;
+ }
+
+ while (WRAP(pa_stream_readable_size)(s) > 0) {
+ const void *data;
+ size_t read_size;
+ if (WRAP(pa_stream_peek)(s, &data, &read_size) < 0) {
+ return;
+ }
+
+ const pa_sample_spec* in_ss = WRAP(pa_stream_get_sample_spec)(s);
+ size_t in_frame_size = WRAP(pa_frame_size)(in_ss);
+ size_t read_frames = read_size / in_frame_size;
+
+ cubeb_stream* stm = u;
+ if (stm->output_stream) {
+ // input/capture + output/record operation
+ size_t out_frame_size = WRAP(pa_frame_size)(&stm->output_sample_spec);
+ size_t write_size = read_frames * out_frame_size;
+ size_t writable_size = WRAP(pa_stream_writable_size)(stm->output_stream);
+
+// printf("writable size %zd\n", writable_size);
+ void* read_buffer = (void*)data;
+ if (writable_size< write_size) {
+ // Trancate read
+ write_to_output(stm->output_stream, read_buffer, writable_size, stm);
+ // Trigger to play output stream.
+ WRAP(pa_stream_trigger)(stm->output_stream, NULL, NULL);
+ } else {
+ write_to_output(stm->output_stream, read_buffer, write_size, stm);
+ }
+ } else {
+// printf("\n");
+ // input/capture only operation. Call callback directly
+ void* read_buffer = (void*)data;
+ if (stm->data_callback(stm, stm->user_ptr, read_buffer, NULL, read_frames) < 0) {
+ WRAP(pa_stream_cancel_write)(s);
+ stm->shutdown = 1;
+ return;
+ }
+ }
+
+ WRAP(pa_stream_drop)(s);
+ }
+}
+
static int
wait_until_context_ready(cubeb * ctx)
{
for (;;) {
pa_context_state_t state = WRAP(pa_context_get_state)(ctx->context);
if (!PA_CONTEXT_IS_GOOD(state))
return -1;
if (state == PA_CONTEXT_READY)
break;
WRAP(pa_threaded_mainloop_wait)(ctx->mainloop);
}
return 0;
}
static int
-wait_until_stream_ready(cubeb_stream * stm)
+wait_until_io_stream_ready(pa_stream * stream, pa_threaded_mainloop * mainloop)
{
+ if (!stream || !mainloop){
+ return -1;
+ }
for (;;) {
- pa_stream_state_t state = WRAP(pa_stream_get_state)(stm->stream);
+ pa_stream_state_t state = WRAP(pa_stream_get_state)(stream);
if (!PA_STREAM_IS_GOOD(state))
return -1;
if (state == PA_STREAM_READY)
break;
- WRAP(pa_threaded_mainloop_wait)(stm->context->mainloop);
+ WRAP(pa_threaded_mainloop_wait)(mainloop);
+ }
+ return 0;
+}
+
+static int
+wait_until_stream_ready(cubeb_stream * stm)
+{
+ if (stm->output_stream &&
+ wait_until_io_stream_ready(stm->output_stream, stm->context->mainloop) == -1) {
+ return -1;
+ }
+ if(stm->input_stream &&
+ wait_until_io_stream_ready(stm->input_stream, stm->context->mainloop) == -1) {
+ return -1;
}
return 0;
}
static int
operation_wait(cubeb * ctx, pa_stream * stream, pa_operation * o)
{
while (WRAP(pa_operation_get_state)(o) == PA_OPERATION_RUNNING) {
@@ -285,34 +380,71 @@ operation_wait(cubeb * ctx, pa_stream *
if (stream && !PA_STREAM_IS_GOOD(WRAP(pa_stream_get_state)(stream))) {
return -1;
}
}
return 0;
}
static void
+cork_io_stream(cubeb_stream * stm, pa_stream* io_stream, enum cork_state state)
+{
+ pa_operation * o = NULL;
+ if (!io_stream) {
+ return;
+ }
+ o = WRAP(pa_stream_cork)(io_stream, state & CORK, stream_success_callback, stm);
+ if (o) {
+ operation_wait(stm->context, io_stream, o);
+ WRAP(pa_operation_unref)(o);
+ }
+}
+
+static void
stream_cork(cubeb_stream * stm, enum cork_state state)
{
- pa_operation * o;
-
WRAP(pa_threaded_mainloop_lock)(stm->context->mainloop);
- o = WRAP(pa_stream_cork)(stm->stream, state & CORK, stream_success_callback, stm);
- if (o) {
- operation_wait(stm->context, stm->stream, o);
- WRAP(pa_operation_unref)(o);
- }
+ cork_io_stream(stm, stm->output_stream, state);
+ cork_io_stream(stm, stm->input_stream, state);
WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop);
if (state & NOTIFY) {
stm->state_callback(stm, stm->user_ptr,
state & CORK ? CUBEB_STATE_STOPPED : CUBEB_STATE_STARTED);
}
}
+static int
+stream_update_timing_info(cubeb_stream * stm)
+{
+ int r = -1;
+ pa_operation * o = NULL;
+ if (stm->output_stream) {
+ o = WRAP(pa_stream_update_timing_info)(stm->output_stream, stream_success_callback, stm);
+ if (o) {
+ r = operation_wait(stm->context, stm->output_stream, o);
+ WRAP(pa_operation_unref)(o);
+ }
+ // if this fail abort
+ if (r!=0){
+ return r;
+ }
+ }
+
+ if (stm->input_stream) {
+ o = WRAP(pa_stream_update_timing_info)(stm->input_stream, stream_success_callback, stm);
+ if (o) {
+ r = operation_wait(stm->context, stm->input_stream, o);
+ WRAP(pa_operation_unref)(o);
+ }
+ }
+
+ return r;
+}
+
static void pulse_context_destroy(cubeb * ctx);
static void pulse_destroy(cubeb * ctx);
static int
pulse_context_init(cubeb * ctx)
{
if (ctx->context) {
assert(ctx->error == 1);
@@ -480,129 +612,164 @@ pulse_destroy(cubeb * ctx)
if (ctx->default_sink_info) {
free(ctx->default_sink_info);
}
free(ctx);
}
static void pulse_stream_destroy(cubeb_stream * stm);
+pa_sample_format_t
+cube_to_pulse_format(cubeb_sample_format format)
+{
+ switch (format) {
+ case CUBEB_SAMPLE_S16LE:
+ return PA_SAMPLE_S16LE;
+ case CUBEB_SAMPLE_S16BE:
+ return PA_SAMPLE_S16BE;
+ case CUBEB_SAMPLE_FLOAT32LE:
+ return PA_SAMPLE_FLOAT32LE;
+ case CUBEB_SAMPLE_FLOAT32BE:
+ return PA_SAMPLE_FLOAT32BE;
+ default:
+ return PA_SAMPLE_INVALID;
+ }
+}
+
static int
-pulse_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_name,
- cubeb_stream_params stream_params, unsigned int latency,
- cubeb_data_callback data_callback, cubeb_state_callback state_callback,
+pulse_stream_init(cubeb * context,
+ cubeb_stream ** stream,
+ char const * stream_name,
+ cubeb_stream_params* input_stream_params,
+ cubeb_stream_params* output_stream_params,
+ unsigned int latency,
+ cubeb_data_callback data_callback,
+ cubeb_state_callback state_callback,
void * user_ptr)
{
pa_sample_spec ss;
cubeb_stream * stm;
- pa_operation * o;
pa_buffer_attr battr;
int r;
assert(context);
-
- *stream = NULL;
-
- switch (stream_params.format) {
- case CUBEB_SAMPLE_S16LE:
- ss.format = PA_SAMPLE_S16LE;
- break;
- case CUBEB_SAMPLE_S16BE:
- ss.format = PA_SAMPLE_S16BE;
- break;
- case CUBEB_SAMPLE_FLOAT32LE:
- ss.format = PA_SAMPLE_FLOAT32LE;
- break;
- case CUBEB_SAMPLE_FLOAT32BE:
- ss.format = PA_SAMPLE_FLOAT32BE;
- break;
- default:
- return CUBEB_ERROR_INVALID_FORMAT;
- }
-
// If the connection failed for some reason, try to reconnect
if (context->error == 1 && pulse_context_init(context) != 0) {
return CUBEB_ERROR;
}
- ss.rate = stream_params.rate;
- ss.channels = stream_params.channels;
+ *stream = NULL;
stm = calloc(1, sizeof(*stm));
assert(stm);
stm->context = context;
-
stm->data_callback = data_callback;
stm->state_callback = state_callback;
stm->user_ptr = user_ptr;
-
- stm->sample_spec = ss;
stm->volume = PULSE_NO_GAIN;
battr.maxlength = -1;
- battr.tlength = WRAP(pa_usec_to_bytes)(latency * PA_USEC_PER_MSEC, &stm->sample_spec);
+ battr.tlength = -1;
battr.prebuf = -1;
- battr.minreq = battr.tlength / 4;
+ battr.minreq = -1;
battr.fragsize = -1;
WRAP(pa_threaded_mainloop_lock)(stm->context->mainloop);
- stm->stream = WRAP(pa_stream_new)(stm->context->context, stream_name, &ss, NULL);
- if (!stm->stream) {
- pulse_stream_destroy(stm);
- return CUBEB_ERROR;
+ if (output_stream_params) {
+ ss.format = cube_to_pulse_format(output_stream_params->format);
+ ss.rate = output_stream_params->rate;
+ ss.channels = output_stream_params->channels;
+
+ // update buffer attributes
+ stm->output_sample_spec = ss;
+ unsigned int latency_min = (latency < MIN_LATENCY_MS)? MIN_LATENCY_MS: latency;
+ battr.tlength = WRAP(pa_usec_to_bytes)(latency_min * PA_USEC_PER_MSEC, &stm->output_sample_spec);
+ battr.minreq = battr.tlength / 4;
+ battr.fragsize = battr.minreq;
+
+ stm->output_stream = WRAP(pa_stream_new)(stm->context->context, stream_name, &ss, NULL);
+ if (!stm->output_stream) {
+ WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop);
+ pulse_stream_destroy(stm);
+ return CUBEB_ERROR;
+ }
+ WRAP(pa_stream_set_state_callback)(stm->output_stream, stream_state_callback, stm);
+ WRAP(pa_stream_set_write_callback)(stm->output_stream, stream_request_callback, stm);
+ WRAP(pa_stream_connect_playback)(stm->output_stream,
+ output_stream_params->devid,
+ &battr,
+ PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_INTERPOLATE_TIMING |
+ PA_STREAM_START_CORKED,
+ NULL, NULL);
}
- WRAP(pa_stream_set_state_callback)(stm->stream, stream_state_callback, stm);
- WRAP(pa_stream_set_write_callback)(stm->stream, stream_request_callback, stm);
- WRAP(pa_stream_connect_playback)(stm->stream, NULL, &battr,
- PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_INTERPOLATE_TIMING |
- PA_STREAM_START_CORKED,
- NULL, NULL);
+
+ // Set up input stream
+ if (input_stream_params){
+ pa_sample_spec in_ss;
+ in_ss.channels = input_stream_params->channels;
+ in_ss.rate = input_stream_params->rate;
+ in_ss.format = cube_to_pulse_format(input_stream_params->format);
+ stm->input_stream = WRAP(pa_stream_new)(stm->context->context, "input stream", &in_ss, NULL);
+ if (!stm->input_stream) {
+ WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop);
+ pulse_stream_destroy(stm);
+ return CUBEB_ERROR;
+ }
+ WRAP(pa_stream_set_state_callback)(stm->input_stream, stream_state_callback, stm);
+ WRAP(pa_stream_set_read_callback)(stm->input_stream, stream_read_callback, stm);
+ WRAP(pa_stream_connect_record)(stm->input_stream,
+ input_stream_params->devid,
+ &battr,
+ PA_STREAM_START_CORKED);
+ }
r = wait_until_stream_ready(stm);
if (r == 0) {
/* force a timing update now, otherwise timing info does not become valid
until some point after initialization has completed. */
- o = WRAP(pa_stream_update_timing_info)(stm->stream, stream_success_callback, stm);
- if (o) {
- r = operation_wait(stm->context, stm->stream, o);
- WRAP(pa_operation_unref)(o);
- }
+ r = stream_update_timing_info(stm);
}
WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop);
if (r != 0) {
pulse_stream_destroy(stm);
return CUBEB_ERROR;
}
*stream = stm;
return CUBEB_OK;
}
static void
pulse_stream_destroy(cubeb_stream * stm)
{
- if (stm->stream) {
- stream_cork(stm, CORK);
+ stream_cork(stm, CORK);
- WRAP(pa_threaded_mainloop_lock)(stm->context->mainloop);
+ WRAP(pa_threaded_mainloop_lock)(stm->context->mainloop);
+ if (stm->output_stream) {
if (stm->drain_timer) {
/* there's no pa_rttime_free, so use this instead. */
WRAP(pa_threaded_mainloop_get_api)(stm->context->mainloop)->time_free(stm->drain_timer);
}
- WRAP(pa_stream_set_state_callback)(stm->stream, NULL, NULL);
- WRAP(pa_stream_disconnect)(stm->stream);
- WRAP(pa_stream_unref)(stm->stream);
- WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop);
+ WRAP(pa_stream_set_state_callback)(stm->output_stream, NULL, NULL);
+ WRAP(pa_stream_disconnect)(stm->output_stream);
+ WRAP(pa_stream_unref)(stm->output_stream);
}
+ if (stm->input_stream) {
+ WRAP(pa_stream_set_state_callback)(stm->input_stream, NULL, NULL);
+ WRAP(pa_stream_disconnect)(stm->input_stream);
+ WRAP(pa_stream_unref)(stm->input_stream);
+ }
+ WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop);
+
free(stm);
}
static int
pulse_stream_start(cubeb_stream * stm)
{
stream_cork(stm, UNCORK | NOTIFY);
return CUBEB_OK;
@@ -617,53 +784,57 @@ pulse_stream_stop(cubeb_stream * stm)
static int
pulse_stream_get_position(cubeb_stream * stm, uint64_t * position)
{
int r, in_thread;
pa_usec_t r_usec;
uint64_t bytes;
+ if (!stm || !stm->output_stream) {
+ return CUBEB_ERROR;
+ }
+
in_thread = WRAP(pa_threaded_mainloop_in_thread)(stm->context->mainloop);
if (!in_thread) {
WRAP(pa_threaded_mainloop_lock)(stm->context->mainloop);
}
- r = WRAP(pa_stream_get_time)(stm->stream, &r_usec);
+ r = WRAP(pa_stream_get_time)(stm->output_stream, &r_usec);
if (!in_thread) {
WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop);
}
if (r != 0) {
return CUBEB_ERROR;
}
- bytes = WRAP(pa_usec_to_bytes)(r_usec, &stm->sample_spec);
- *position = bytes / WRAP(pa_frame_size)(&stm->sample_spec);
+ bytes = WRAP(pa_usec_to_bytes)(r_usec, &stm->output_sample_spec);
+ *position = bytes / WRAP(pa_frame_size)(&stm->output_sample_spec);
return CUBEB_OK;
}
int
pulse_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
{
pa_usec_t r_usec;
int negative, r;
- if (!stm) {
+ if (!stm || !stm->output_stream) {
return CUBEB_ERROR;
}
- r = WRAP(pa_stream_get_latency)(stm->stream, &r_usec, &negative);
+ r = WRAP(pa_stream_get_latency)(stm->output_stream, &r_usec, &negative);
assert(!negative);
if (r) {
return CUBEB_ERROR;
}
- *latency = r_usec * stm->sample_spec.rate / PA_USEC_PER_SEC;
+ *latency = r_usec * stm->output_sample_spec.rate / PA_USEC_PER_SEC;
return CUBEB_OK;
}
void volume_success(pa_context *c, int success, void *userdata)
{
cubeb_stream * stream = userdata;
assert(success);
WRAP(pa_threaded_mainloop_signal)(stream->context->mainloop, 0);
@@ -673,55 +844,63 @@ int
pulse_stream_set_volume(cubeb_stream * stm, float volume)
{
uint32_t index;
pa_operation * op;
pa_volume_t vol;
pa_cvolume cvol;
const pa_sample_spec * ss;
+ if (!stm->output_stream) {
+ return CUBEB_ERROR;
+ }
+
WRAP(pa_threaded_mainloop_lock)(stm->context->mainloop);
while (!stm->context->default_sink_info) {
WRAP(pa_threaded_mainloop_wait)(stm->context->mainloop);
}
/* if the pulse daemon is configured to use flat volumes,
* apply our own gain instead of changing the input volume on the sink. */
if (stm->context->default_sink_info->flags & PA_SINK_FLAT_VOLUME) {
stm->volume = volume;
} else {
- ss = WRAP(pa_stream_get_sample_spec)(stm->stream);
+ ss = WRAP(pa_stream_get_sample_spec)(stm->output_stream);
vol = WRAP(pa_sw_volume_from_linear)(volume);
WRAP(pa_cvolume_set)(&cvol, ss->channels, vol);
- index = WRAP(pa_stream_get_index)(stm->stream);
+ index = WRAP(pa_stream_get_index)(stm->output_stream);
op = WRAP(pa_context_set_sink_input_volume)(stm->context->context,
index, &cvol, volume_success,
stm);
if (op) {
- operation_wait(stm->context, stm->stream, op);
+ operation_wait(stm->context, stm->output_stream, op);
WRAP(pa_operation_unref)(op);
}
}
WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop);
return CUBEB_OK;
}
int
pulse_stream_set_panning(cubeb_stream * stream, float panning)
{
const pa_channel_map * map;
pa_cvolume vol;
- map = WRAP(pa_stream_get_channel_map)(stream->stream);
+ if (!stream->output_stream) {
+ return CUBEB_ERROR;
+ }
+
+ map = WRAP(pa_stream_get_channel_map)(stream->output_stream);
if (!WRAP(pa_channel_map_can_balance)(map)) {
return CUBEB_ERROR;
}
WRAP(pa_cvolume_set_balance)(&vol, map, panning);
return CUBEB_OK;
@@ -762,21 +941,19 @@ pulse_ensure_dev_list_data_list_size (pu
sizeof(cubeb_device_info) * list_data->max);
}
}
static cubeb_device_state
pulse_get_state_from_sink_port(pa_sink_port_info * info)
{
if (info != NULL) {
-#if PA_CHECK_VERSION(2, 0, 0)
if (info->available == PA_PORT_AVAILABLE_NO)
return CUBEB_DEVICE_STATE_UNPLUGGED;
else /*if (info->available == PA_PORT_AVAILABLE_YES) + UNKNOWN */
-#endif
return CUBEB_DEVICE_STATE_ENABLED;
}
return CUBEB_DEVICE_STATE_DISABLED;
}
static void
pulse_sink_info_cb(pa_context * context, const pa_sink_info * info,
@@ -820,21 +997,19 @@ pulse_sink_info_cb(pa_context * context,
pulse_ensure_dev_list_data_list_size (list_data);
list_data->devinfo[list_data->count++] = devinfo;
}
static cubeb_device_state
pulse_get_state_from_source_port(pa_source_port_info * info)
{
if (info != NULL) {
-#if PA_CHECK_VERSION(2, 0, 0)
if (info->available == PA_PORT_AVAILABLE_NO)
return CUBEB_DEVICE_STATE_UNPLUGGED;
else /*if (info->available == PA_PORT_AVAILABLE_YES) + UNKNOWN */
-#endif
return CUBEB_DEVICE_STATE_ENABLED;
}
return CUBEB_DEVICE_STATE_DISABLED;
}
static void
pulse_source_info_cb(pa_context * context, const pa_source_info * info,
@@ -921,21 +1096,23 @@ pulse_enumerate_devices(cubeb * context,
pulse_source_info_cb, &user_data);
if (o) {
operation_wait(context, NULL, o);
WRAP(pa_operation_unref)(o);
}
}
*collection = malloc(sizeof(cubeb_device_collection) +
- sizeof(cubeb_device_info*) * (user_data.count > 0 ? user_data.count - 1 : 0));
+ sizeof(cubeb_device_info*) * user_data.count);
(*collection)->count = user_data.count;
for (i = 0; i < user_data.count; i++)
(*collection)->device[i] = user_data.devinfo[i];
+ free(user_data.default_sink_name);
+ free(user_data.default_source_name);
free(user_data.devinfo);
return CUBEB_OK;
}
static struct cubeb_ops const pulse_ops = {
.init = pulse_init,
.get_backend_id = pulse_get_backend_id,
.get_max_channel_count = pulse_get_max_channel_count,