poll: remove threads from alsa-sink

Remove the thread from alsa sink and use the pollfd event.
Make it possible to pass multiple fds in one pollfd event
Add 3 callbacks to the pollfd event and add support for timeouts
This commit is contained in:
Wim Taymans 2016-07-08 12:18:01 +02:00
parent ac59fa9371
commit 5fa334a89b
10 changed files with 313 additions and 202 deletions

View file

@ -20,7 +20,6 @@
#include <stddef.h>
#include <asoundlib.h>
#include <pthread.h>
#include <spa/node.h>
#include <spa/audio/format.h>
@ -58,8 +57,9 @@ typedef struct {
snd_pcm_sframes_t buffer_size;
snd_pcm_sframes_t period_size;
snd_pcm_channel_area_t areas[16];
pthread_t thread;
bool running;
SpaPollFd fds[16];
SpaPollItem poll;
} SpaALSAState;
typedef struct _ALSABuffer ALSABuffer;

View file

@ -175,137 +175,6 @@ xrun_recovery (snd_pcm_t *handle, int err)
return err;
}
/*
* Transfer method - direct write only
*/
static void *
direct_loop (void *user_data)
{
SpaALSASink *this = user_data;
SpaALSAState *state = &this->state;
snd_pcm_t *handle = state->handle;
const snd_pcm_channel_area_t *my_areas;
snd_pcm_uframes_t offset, frames, size;
snd_pcm_sframes_t avail, commitres;
snd_pcm_state_t st;
int err, first = 1;
while (state->running) {
st = snd_pcm_state(handle);
if (st == SND_PCM_STATE_XRUN) {
err = xrun_recovery(handle, -EPIPE);
if (err < 0) {
printf("XRUN recovery failed: %s\n", snd_strerror(err));
return NULL;
}
first = 1;
} else if (st == SND_PCM_STATE_SUSPENDED) {
err = xrun_recovery(handle, -ESTRPIPE);
if (err < 0) {
printf("SUSPEND recovery failed: %s\n", snd_strerror(err));
return NULL;
}
}
avail = snd_pcm_avail_update(handle);
if (avail < 0) {
err = xrun_recovery(handle, avail);
if (err < 0) {
printf("avail update failed: %s\n", snd_strerror(err));
return NULL;
}
first = 1;
continue;
}
if (avail < state->period_size) {
if (first) {
first = 0;
err = snd_pcm_start(handle);
if (err < 0) {
printf("Start error: %s\n", snd_strerror(err));
exit(EXIT_FAILURE);
}
} else {
err = snd_pcm_wait(handle, -1);
if (err < 0) {
if ((err = xrun_recovery(handle, err)) < 0) {
printf("snd_pcm_wait error: %s\n", snd_strerror(err));
exit(EXIT_FAILURE);
}
first = 1;
}
}
continue;
}
size = state->period_size;
while (size > 0) {
frames = size;
err = snd_pcm_mmap_begin(handle, &my_areas, &offset, &frames);
if (err < 0) {
if ((err = xrun_recovery(handle, err)) < 0) {
printf("MMAP begin avail error: %s\n", snd_strerror(err));
exit(EXIT_FAILURE);
}
first = 1;
}
{
SpaEvent event;
ALSABuffer *buffer = &this->buffer;
event.refcount = 1;
event.notify = NULL;
event.type = SPA_EVENT_TYPE_PULL_INPUT;
event.port_id = 0;
event.size = frames * sizeof (uint16_t) * 2;
event.data = buffer;
buffer->buffer.refcount = 1;
buffer->buffer.notify = NULL;
buffer->buffer.size = frames * sizeof (uint16_t) * 2;
buffer->buffer.n_metas = 1;
buffer->buffer.metas = buffer->meta;
buffer->buffer.n_datas = 1;
buffer->buffer.datas = buffer->data;
buffer->header.flags = 0;
buffer->header.seq = 0;
buffer->header.pts = 0;
buffer->header.dts_offset = 0;
buffer->meta[0].type = SPA_META_TYPE_HEADER;
buffer->meta[0].data = &buffer->header;
buffer->meta[0].size = sizeof (buffer->header);
buffer->data[0].type = SPA_DATA_TYPE_MEMPTR;
buffer->data[0].data = (uint8_t *)my_areas[0].addr + (offset * sizeof (uint16_t) * 2);
buffer->data[0].size = frames * sizeof (uint16_t) * 2;
this->event_cb (&this->handle, &event,this->user_data);
spa_buffer_unref ((SpaBuffer *)event.data);
}
if (this->input_buffer) {
if (this->input_buffer != &this->buffer.buffer) {
/* FIXME, copy input */
}
spa_buffer_unref (this->input_buffer);
this->input_buffer = NULL;
}
commitres = snd_pcm_mmap_commit(handle, offset, frames);
if (commitres < 0 || (snd_pcm_uframes_t)commitres != frames) {
if ((err = xrun_recovery(handle, commitres >= 0 ? -EPIPE : commitres)) < 0) {
printf("MMAP commit error: %s\n", snd_strerror(err));
exit(EXIT_FAILURE);
}
first = 1;
}
size -= frames;
}
}
return NULL;
}
static int
spa_alsa_open (SpaALSASink *this)
{
@ -332,6 +201,130 @@ spa_alsa_open (SpaALSASink *this)
return 0;
}
static void
pull_input (SpaALSASink *this, void *data, snd_pcm_uframes_t frames)
{
SpaEvent event;
ALSABuffer *buffer = &this->buffer;
event.refcount = 1;
event.notify = NULL;
event.type = SPA_EVENT_TYPE_PULL_INPUT;
event.port_id = 0;
event.size = frames * sizeof (uint16_t) * 2;
event.data = buffer;
buffer->buffer.refcount = 1;
buffer->buffer.notify = NULL;
buffer->buffer.size = frames * sizeof (uint16_t) * 2;
buffer->buffer.n_metas = 1;
buffer->buffer.metas = buffer->meta;
buffer->buffer.n_datas = 1;
buffer->buffer.datas = buffer->data;
buffer->header.flags = 0;
buffer->header.seq = 0;
buffer->header.pts = 0;
buffer->header.dts_offset = 0;
buffer->meta[0].type = SPA_META_TYPE_HEADER;
buffer->meta[0].data = &buffer->header;
buffer->meta[0].size = sizeof (buffer->header);
buffer->data[0].type = SPA_DATA_TYPE_MEMPTR;
buffer->data[0].data = data;
buffer->data[0].size = frames * sizeof (uint16_t) * 2;
this->event_cb (&this->handle, &event,this->user_data);
spa_buffer_unref ((SpaBuffer *)event.data);
}
static int
mmap_write (SpaALSASink *this)
{
SpaALSAState *state = &this->state;
snd_pcm_t *handle = state->handle;
int err;
snd_pcm_sframes_t avail, commitres;
snd_pcm_uframes_t offset, frames, size;
const snd_pcm_channel_area_t *my_areas;
if ((avail = snd_pcm_avail_update (handle)) < 0) {
if ((err = xrun_recovery (handle, avail)) < 0) {
printf ("Write error: %s\n", snd_strerror (err));
return -1;
}
}
size = avail;
while (size > 0) {
frames = size;
if ((err = snd_pcm_mmap_begin (handle, &my_areas, &offset, &frames)) < 0) {
if ((err = xrun_recovery(handle, err)) < 0) {
printf("MMAP begin avail error: %s\n", snd_strerror(err));
return -1;
}
}
pull_input (this,
(uint8_t *)my_areas[0].addr + (offset * sizeof (uint16_t) * 2),
frames);
if (this->input_buffer) {
if (this->input_buffer != &this->buffer.buffer) {
/* FIXME, copy input */
}
spa_buffer_unref (this->input_buffer);
this->input_buffer = NULL;
}
commitres = snd_pcm_mmap_commit (handle, offset, frames);
if (commitres < 0 || (snd_pcm_uframes_t)commitres != frames) {
if ((err = xrun_recovery (handle, commitres >= 0 ? -EPIPE : commitres)) < 0) {
printf("MMAP commit error: %s\n", snd_strerror(err));
return -1;
}
}
size -= frames;
}
return 0;
}
static int
alsa_on_fd_events (SpaPollNotifyData *data)
{
SpaALSASink *this = data->user_data;
SpaALSAState *state = &this->state;
snd_pcm_t *handle = state->handle;
int err;
unsigned short revents;
snd_pcm_poll_descriptors_revents (handle,
(struct pollfd *)data->fds,
data->n_fds,
&revents);
if (revents & POLLERR) {
if (snd_pcm_state (handle) == SND_PCM_STATE_XRUN ||
snd_pcm_state (handle) == SND_PCM_STATE_SUSPENDED) {
err = snd_pcm_state (handle) == SND_PCM_STATE_XRUN ? -EPIPE : -ESTRPIPE;
if ((err = xrun_recovery (handle, err)) < 0) {
printf ("Write error: %s\n", snd_strerror(err));
return -1;
}
} else {
printf("Wait for poll failed\n");
return -1;
}
}
if (!(revents & POLLOUT))
return -1;
mmap_write (this);
return 0;
}
static int
spa_alsa_close (SpaALSASink *this)
{
@ -353,6 +346,7 @@ spa_alsa_start (SpaALSASink *this)
{
SpaALSAState *state = &this->state;
int err;
SpaEvent event;
if (spa_alsa_open (this) < 0)
return -1;
@ -360,13 +354,34 @@ spa_alsa_start (SpaALSASink *this)
CHECK (set_hwparams (this), "hwparams");
CHECK (set_swparams (this), "swparams");
snd_pcm_dump (this->state.handle, this->state.output);
snd_pcm_dump (state->handle, state->output);
state->running = true;
if ((err = pthread_create (&state->thread, NULL, direct_loop, this)) != 0) {
printf ("can't create thread: %d", err);
state->running = false;
if ((state->poll.n_fds = snd_pcm_poll_descriptors_count (state->handle)) <= 0) {
printf ("Invalid poll descriptors count\n");
return state->poll.n_fds;
}
if ((err = snd_pcm_poll_descriptors (state->handle, (struct pollfd *)state->fds, state->poll.n_fds)) < 0) {
printf ("Unable to obtain poll descriptors for playback: %s\n", snd_strerror(err));
return err;
}
event.refcount = 1;
event.notify = NULL;
event.type = SPA_EVENT_TYPE_ADD_POLL;
event.port_id = 0;
event.data = &state->poll;
event.size = sizeof (state->poll);
state->poll.fds = state->fds;
state->poll.idle_cb = NULL;
state->poll.before_cb = NULL;
state->poll.after_cb = alsa_on_fd_events;
state->poll.user_data = this;
this->event_cb (&this->handle, &event, this->user_data);
mmap_write (this);
err = snd_pcm_start (state->handle);
return err;
}
@ -374,11 +389,18 @@ static int
spa_alsa_stop (SpaALSASink *this)
{
SpaALSAState *state = &this->state;
SpaEvent event;
snd_pcm_drop (state->handle);
event.refcount = 1;
event.notify = NULL;
event.type = SPA_EVENT_TYPE_REMOVE_POLL;
event.port_id = 0;
event.data = &state->poll;
event.size = sizeof (state->poll);
this->event_cb (&this->handle, &event, this->user_data);
if (state->running) {
state->running = false;
pthread_join (state->thread, NULL);
}
spa_alsa_close (this);
return 0;

View file

@ -69,7 +69,8 @@ typedef struct {
V4l2Buffer buffers[MAX_BUFFERS];
V4l2Buffer *ready;
uint32_t ready_count;
SpaEventPoll poll;
SpaPollFd fds[1];
SpaPollItem poll;
} SpaV4l2State;
struct _SpaV4l2Source {
@ -91,14 +92,6 @@ struct _SpaV4l2Source {
#include "v4l2-utils.c"
static const uint32_t min_uint32 = 1;
static const uint32_t max_uint32 = UINT32_MAX;
static const SpaPropRangeInfo uint32_range[] = {
{ "min", "Minimum value", 4, &min_uint32 },
{ "max", "Maximum value", 4, &max_uint32 },
};
enum {
PROP_ID_DEVICE,
PROP_ID_DEVICE_NAME,

View file

@ -343,7 +343,6 @@ mmap_read (SpaV4l2Source *this)
return -1;
}
}
fprintf (stderr, "captured buffer %d\n", buf.index);
b = &state->buffers[buf.index];
b->next = state->ready;
@ -353,10 +352,10 @@ mmap_read (SpaV4l2Source *this)
return 0;
}
static void
v4l2_on_fd_events (void *user_data)
static int
v4l2_on_fd_events (SpaPollNotifyData *data)
{
SpaV4l2Source *this = user_data;
SpaV4l2Source *this = data->user_data;
SpaEvent event;
mmap_read (this);
@ -368,6 +367,8 @@ v4l2_on_fd_events (void *user_data)
event.size = 0;
event.data = NULL;
this->event_cb (&this->handle, &event, this->user_data);
return 0;
}
static void
@ -386,8 +387,6 @@ v4l2_buffer_free (void *data)
b->buffer.refcount = 1;
b->outstanding = false;
fprintf (stderr, "queue buffer %d\n", buf.index);
if (xioctl (state->fd, VIDIOC_QBUF, &buf) < 0) {
perror ("VIDIOC_QBUF");
}
@ -523,10 +522,15 @@ spa_v4l2_start (SpaV4l2Source *this)
event.data = &state->poll;
event.size = sizeof (state->poll);
state->poll.fd = state->fd;
state->poll.events = POLLIN | POLLPRI | POLLERR;
state->poll.revents = 0;
state->poll.callback = v4l2_on_fd_events;
state->fds[0].fd = state->fd;
state->fds[0].events = POLLIN | POLLPRI | POLLERR;
state->fds[0].revents = 0;
state->poll.fds = state->fds;
state->poll.n_fds = 1;
state->poll.idle_cb = NULL;
state->poll.before_cb = NULL;
state->poll.after_cb = v4l2_on_fd_events;
state->poll.user_data = this;
this->event_cb (&this->handle, &event, this->user_data);

View file

@ -22,7 +22,6 @@
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <linux/videodev2.h>
#include <spa/node.h>
@ -47,11 +46,6 @@ reset_xv_sink_props (SpaXvSinkProps *props)
#define MAX_BUFFERS 256
typedef struct {
SpaEventPoll poll;
SpaXvSink *sink;
} XvEventPoll;
typedef struct _XvBuffer XvBuffer;
struct _XvBuffer {
@ -68,8 +62,6 @@ struct _XvBuffer {
typedef struct {
bool opened;
int fd;
pthread_t thread;
bool running;
XvBuffer buffers[MAX_BUFFERS];
XvBuffer *ready;
uint32_t ready_count;
@ -95,14 +87,6 @@ struct _SpaXvSink {
#include "xv-utils.c"
static const uint32_t min_uint32 = 1;
static const uint32_t max_uint32 = UINT32_MAX;
static const SpaPropRangeInfo uint32_range[] = {
{ "min", "Minimum value", 4, &min_uint32 },
{ "max", "Maximum value", 4, &max_uint32 },
};
enum {
PROP_ID_DEVICE,
PROP_ID_DEVICE_NAME,