videotestsrc: remove threads

Remove the threads from the element and use timerfd to schedule timeouts
Propagate live flag between node links
This commit is contained in:
Wim Taymans 2016-09-19 19:17:59 +02:00
parent 1e565a5f65
commit 4b83d6cfc8
9 changed files with 809 additions and 802 deletions

View file

@ -570,7 +570,7 @@ spa_alsa_sink_node_port_push_input (SpaNode *node,
have_enough = true;
continue;
}
SPA_QUEUE_PUSH_TAIL (&this->ready, SpaALSABuffer, &this->alloc_buffers[info[i].buffer_id]);
SPA_QUEUE_PUSH_TAIL (&this->ready, SpaALSABuffer, next, &this->alloc_buffers[info[i].buffer_id]);
}
info[i].status = SPA_RESULT_OK;
}

View file

@ -301,7 +301,7 @@ mmap_read (SpaALSAState *state)
snd_pcm_status_get_htstamp (status, &htstamp);
now = (int64_t)htstamp.tv_sec * 1000000000ll + (int64_t)htstamp.tv_nsec;
SPA_QUEUE_POP_HEAD (&state->free, SpaALSABuffer, b);
SPA_QUEUE_POP_HEAD (&state->free, SpaALSABuffer, next, b);
if (b == NULL) {
fprintf (stderr, "no more buffers\n");
} else {
@ -346,7 +346,7 @@ mmap_read (SpaALSAState *state)
d = SPA_BUFFER_DATAS (b->outbuf);
d[0].mem.size = avail * state->frame_size;
SPA_QUEUE_PUSH_TAIL (&state->ready, SpaALSABuffer, b);
SPA_QUEUE_PUSH_TAIL (&state->ready, SpaALSABuffer, next, b);
event.type = SPA_NODE_EVENT_TYPE_HAVE_OUTPUT;
event.size = sizeof (ho);

View file

@ -28,6 +28,11 @@
#include <spa/queue.h>
#include <spa/audio/format.h>
#define TIMESPEC_TO_TIME(ts) ((ts)->tv_sec * 1000000000ll + (ts)->tv_nsec)
#define SAMPLES_TO_TIME(this,s) ((s) * 1000000000ll / (this)->current_format.info.raw.rate)
#define BYTES_TO_SAMPLES(this,b) ((b)/(this)->bpf)
#define BYTES_TO_TIME(this,b) SAMPLES_TO_TIME(this, BYTES_TO_SAMPLES (this, b))
typedef struct _SpaAudioTestSrc SpaAudioTestSrc;
typedef struct {
@ -56,13 +61,13 @@ struct _ATSBuffer {
struct _SpaAudioTestSrc {
SpaHandle handle;
SpaNode node;
SpaClock clock;
SpaAudioTestSrcProps props[2];
SpaNodeEventCallback event_cb;
void *user_data;
SpaPollItem idle;
SpaPollItem timer;
SpaPollFd fds[1];
struct itimerspec timerspec;
@ -228,6 +233,95 @@ send_have_output (SpaAudioTestSrc *this)
return SPA_RESULT_OK;
}
static SpaResult
fill_buffer (SpaAudioTestSrc *this, ATSBuffer *b)
{
uint8_t *p = b->ptr;
size_t i;
for (i = 0; i < b->size; i++) {
p[i] = rand();
}
this->sample_count += b->size / this->bpf;
this->elapsed_time = SAMPLES_TO_TIME (this, this->sample_count);
return SPA_RESULT_OK;
}
static int audiotestsrc_on_output (SpaPollNotifyData *data);
static SpaResult
update_poll_enabled (SpaAudioTestSrc *this, bool enabled)
{
SpaNodeEvent event;
if (this->event_cb) {
event.type = SPA_NODE_EVENT_TYPE_UPDATE_POLL;
this->timer.enabled = enabled;
if (this->props[1].live) {
if (enabled) {
uint64_t next_time = this->start_time + this->elapsed_time;
fprintf (stderr, "%"PRIu64"\n", next_time);
this->timerspec.it_value.tv_sec = next_time / 1000000000;
this->timerspec.it_value.tv_nsec = next_time % 1000000000;
}
else {
this->timerspec.it_value.tv_sec = 0;
this->timerspec.it_value.tv_nsec = 0;
}
timerfd_settime (this->fds[0].fd, TFD_TIMER_ABSTIME, &this->timerspec, NULL);
this->timer.fds = this->fds;
this->timer.n_fds = 1;
this->timer.idle_cb = NULL;
this->timer.after_cb = audiotestsrc_on_output;
} else {
this->timer.fds = NULL;
this->timer.n_fds = 0;
this->timer.idle_cb = audiotestsrc_on_output;
this->timer.after_cb = NULL;
}
event.data = &this->timer;
event.size = sizeof (this->timer);
this->event_cb (&this->node, &event, this->user_data);
}
return SPA_RESULT_OK;
}
static int
audiotestsrc_on_output (SpaPollNotifyData *data)
{
SpaAudioTestSrc *this = data->user_data;
ATSBuffer *b;
SPA_QUEUE_POP_HEAD (&this->empty, ATSBuffer, next, b);
fprintf (stderr, "on_output %p\n", b);
if (b == NULL) {
update_poll_enabled (this, false);
return 0;
}
fill_buffer (this, b);
if (this->props[1].live) {
uint64_t expirations, next_time;
if (read (this->fds[0].fd, &expirations, sizeof (uint64_t)) < sizeof (uint64_t))
perror ("read timerfd");
next_time = this->start_time + this->elapsed_time;
this->timerspec.it_value.tv_sec = next_time / 1000000000;
this->timerspec.it_value.tv_nsec = next_time % 1000000000;
timerfd_settime (this->fds[0].fd, TFD_TIMER_ABSTIME, &this->timerspec, NULL);
}
b->next = NULL;
SPA_QUEUE_PUSH_TAIL (&this->ready, ATSBuffer, next, b);
send_have_output (this);
return 0;
}
static void
update_state (SpaAudioTestSrc *this, SpaNodeState state)
{
@ -248,37 +342,6 @@ update_state (SpaAudioTestSrc *this, SpaNodeState state)
}
}
static SpaResult
update_poll_enabled (SpaAudioTestSrc *this, bool enabled)
{
SpaNodeEvent event;
if (this->event_cb) {
event.type = SPA_NODE_EVENT_TYPE_UPDATE_POLL;
if (this->props[1].live) {
this->timer.enabled = enabled;
if (enabled) {
uint64_t next_time = this->start_time + this->elapsed_time;
this->timerspec.it_value.tv_sec = next_time / 1000000000;
this->timerspec.it_value.tv_nsec = next_time % 1000000000;
}
else {
this->timerspec.it_value.tv_sec = 0;
this->timerspec.it_value.tv_nsec = 0;
}
timerfd_settime (this->fds[0].fd, TFD_TIMER_ABSTIME, &this->timerspec, NULL);
event.data = &this->timer;
event.size = sizeof (this->timer);
} else {
this->idle.enabled = enabled;
event.data = &this->idle;
event.size = sizeof (this->idle);
}
this->event_cb (&this->node, &event, this->user_data);
}
return SPA_RESULT_OK;
}
static SpaResult
spa_audiotestsrc_node_send_command (SpaNode *node,
SpaNodeCommand *command)
@ -308,7 +371,7 @@ spa_audiotestsrc_node_send_command (SpaNode *node,
return SPA_RESULT_OK;
clock_gettime (CLOCK_MONOTONIC, &now);
this->start_time = now.tv_sec * 1000000000ll + now.tv_nsec;
this->start_time = TIMESPEC_TO_TIME (&now);
this->elapsed_time = 0;
this->started = true;
@ -356,11 +419,8 @@ spa_audiotestsrc_node_set_event_callback (SpaNode *node,
if (event_cb == NULL && this->event_cb) {
event.type = SPA_NODE_EVENT_TYPE_REMOVE_POLL;
if (this->props[1].live)
event.data = &this->timer;
else
event.data = &this->idle;
event.size = sizeof (this->idle);
event.data = &this->timer;
event.size = sizeof (this->timer);
this->event_cb (&this->node, &event, this->user_data);
}
@ -369,11 +429,8 @@ spa_audiotestsrc_node_set_event_callback (SpaNode *node,
if (this->event_cb) {
event.type = SPA_NODE_EVENT_TYPE_ADD_POLL;
if (this->props[1].live)
event.data = &this->timer;
else
event.data = &this->idle;
event.size = sizeof (this->idle);
event.data = &this->timer;
event.size = sizeof (this->timer);
this->event_cb (&this->node, &event, this->user_data);
}
return SPA_RESULT_OK;
@ -508,14 +565,13 @@ spa_audiotestsrc_node_port_set_format (SpaNode *node,
if ((res = spa_format_audio_parse (format, &this->current_format)) < 0)
return res;
this->bpf = (2 * this->current_format.info.raw.channels);
this->have_format = true;
}
if (this->have_format) {
this->info.maxbuffering = -1;
this->info.latency = -1;
this->bpf = (2 * this->current_format.info.raw.channels);
this->info.latency = BYTES_TO_TIME (this, 1024);
this->info.n_params = 1;
this->info.params = this->params;
@ -646,7 +702,7 @@ spa_audiotestsrc_node_port_use_buffers (SpaNode *node,
b->size = d[0].mem.size;
b->next = NULL;
SPA_QUEUE_PUSH_TAIL (&this->empty, ATSBuffer, b);
SPA_QUEUE_PUSH_TAIL (&this->empty, ATSBuffer, next, b);
}
this->n_buffers = n_buffers;
this->have_buffers = true;
@ -716,9 +772,9 @@ spa_audiotestsrc_node_port_reuse_buffer (SpaNode *node,
b->outstanding = false;
b->next = NULL;
SPA_QUEUE_PUSH_TAIL (&this->empty, ATSBuffer, b);
SPA_QUEUE_PUSH_TAIL (&this->empty, ATSBuffer, next, b);
if (this->empty.length == 1)
if (this->empty.length == 1 && !this->props[1].live)
update_poll_enabled (this, true);
return SPA_RESULT_OK;
@ -755,72 +811,6 @@ spa_audiotestsrc_node_port_push_input (SpaNode *node,
{
return SPA_RESULT_INVALID_PORT;
}
static SpaResult
fill_buffer (SpaAudioTestSrc *this, ATSBuffer *b)
{
uint8_t *p = b->ptr;
size_t i;
for (i = 0; i < b->size; i++) {
p[i] = rand();
}
this->sample_count += b->size / this->bpf;
this->elapsed_time = this->sample_count * 1000000000ll / this->current_format.info.raw.rate;
return SPA_RESULT_OK;
}
static int
audiotestsrc_idle (SpaPollNotifyData *data)
{
SpaAudioTestSrc *this = data->user_data;
ATSBuffer *empty;
SPA_QUEUE_POP_HEAD (&this->empty, ATSBuffer, empty);
if (!empty) {
update_poll_enabled (this, false);
return 0;
}
fill_buffer (this, empty);
empty->next = NULL;
SPA_QUEUE_PUSH_TAIL (&this->ready, ATSBuffer, empty);
send_have_output (this);
return 0;
}
static int
audiotestsrc_on_timer (SpaPollNotifyData *data)
{
SpaAudioTestSrc *this = data->user_data;
ATSBuffer *empty;
uint64_t expirations, next_time;
if (read (this->fds[0].fd, &expirations, sizeof (uint64_t)) < sizeof (uint64_t))
perror ("read timerfd");
SPA_QUEUE_POP_HEAD (&this->empty, ATSBuffer, empty);
if (empty == NULL)
return 0;
fill_buffer (this, empty);
next_time = this->start_time + this->elapsed_time;
this->timerspec.it_value.tv_sec = next_time / 1000000000;
this->timerspec.it_value.tv_nsec = next_time % 1000000000;
timerfd_settime (this->fds[0].fd, TFD_TIMER_ABSTIME, &this->timerspec, NULL);
empty->next = NULL;
SPA_QUEUE_PUSH_TAIL (&this->ready, ATSBuffer, empty);
send_have_output (this);
return 0;
}
static SpaResult
spa_audiotestsrc_node_port_pull_output (SpaNode *node,
unsigned int n_info,
@ -850,7 +840,7 @@ spa_audiotestsrc_node_port_pull_output (SpaNode *node,
continue;
}
SPA_QUEUE_POP_HEAD (&this->ready, ATSBuffer, b);
SPA_QUEUE_POP_HEAD (&this->ready, ATSBuffer, next, b);
if (b == NULL) {
info[i].status = SPA_RESULT_UNEXPECTED;
have_error = true;
@ -903,6 +893,56 @@ static const SpaNode audiotestsrc_node = {
spa_audiotestsrc_node_port_push_event,
};
static SpaResult
spa_audiotestsrc_clock_get_props (SpaClock *clock,
SpaProps **props)
{
return SPA_RESULT_NOT_IMPLEMENTED;
}
static SpaResult
spa_audiotestsrc_clock_set_props (SpaClock *clock,
const SpaProps *props)
{
return SPA_RESULT_NOT_IMPLEMENTED;
}
static SpaResult
spa_audiotestsrc_clock_get_time (SpaClock *clock,
int32_t *rate,
int64_t *ticks,
int64_t *monotonic_time)
{
struct timespec now;
uint64_t tnow;
if (clock == NULL || clock->handle == NULL)
return SPA_RESULT_INVALID_ARGUMENTS;
if (rate)
*rate = 1000000000;
clock_gettime (CLOCK_MONOTONIC, &now);
tnow = TIMESPEC_TO_TIME (&now);
if (ticks)
*ticks = tnow;
if (monotonic_time)
*monotonic_time = tnow;
return SPA_RESULT_OK;
}
static const SpaClock audiotestsrc_clock = {
NULL,
sizeof (SpaClock),
NULL,
SPA_CLOCK_STATE_STOPPED,
spa_audiotestsrc_clock_get_props,
spa_audiotestsrc_clock_set_props,
spa_audiotestsrc_clock_get_time,
};
static SpaResult
spa_audiotestsrc_get_interface (SpaHandle *handle,
uint32_t interface_id,
@ -919,6 +959,9 @@ spa_audiotestsrc_get_interface (SpaHandle *handle,
case SPA_INTERFACE_ID_NODE:
*interface = &this->node;
break;
case SPA_INTERFACE_ID_CLOCK:
*interface = &this->clock;
break;
default:
return SPA_RESULT_UNKNOWN_INTERFACE;
}
@ -928,6 +971,15 @@ spa_audiotestsrc_get_interface (SpaHandle *handle,
static SpaResult
audiotestsrc_clear (SpaHandle *handle)
{
SpaAudioTestSrc *this;
if (handle == NULL)
return SPA_RESULT_INVALID_ARGUMENTS;
this = (SpaAudioTestSrc *) handle;
close (this->fds[0].fd);
return SPA_RESULT_OK;
}
@ -947,6 +999,8 @@ audiotestsrc_init (const SpaHandleFactory *factory,
this = (SpaAudioTestSrc *) handle;
this->node = audiotestsrc_node;
this->node.handle = handle;
this->clock = audiotestsrc_clock;
this->clock.handle = handle;
this->props[1].props.n_prop_info = PROP_ID_LAST;
this->props[1].props.prop_info = prop_info;
reset_audiotestsrc_props (&this->props[1]);
@ -954,15 +1008,6 @@ audiotestsrc_init (const SpaHandleFactory *factory,
SPA_QUEUE_INIT (&this->empty);
SPA_QUEUE_INIT (&this->ready);
this->idle.id = 0;
this->idle.enabled = false;
this->idle.fds = NULL;
this->idle.n_fds = 0;
this->idle.idle_cb = audiotestsrc_idle;
this->idle.before_cb = NULL;
this->idle.after_cb = NULL;
this->idle.user_data = this;
this->fds[0].fd = timerfd_create (CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);
this->fds[0].events = POLLIN | POLLPRI | POLLERR;
this->fds[0].revents = 0;
@ -973,11 +1018,9 @@ audiotestsrc_init (const SpaHandleFactory *factory,
this->timer.id = 0;
this->timer.enabled = false;
this->timer.fds = this->fds;
this->timer.n_fds = 1;
this->timer.idle_cb = NULL;
this->timer.before_cb = NULL;
this->timer.after_cb = audiotestsrc_on_timer;
this->timer.after_cb = NULL;
this->timer.user_data = this;
this->info.flags = SPA_PORT_INFO_FLAG_CAN_USE_BUFFERS |
@ -998,6 +1041,10 @@ static const SpaInterfaceInfo audiotestsrc_interfaces[] =
SPA_INTERFACE_ID_NODE_NAME,
SPA_INTERFACE_ID_NODE_DESCRIPTION,
},
{ SPA_INTERFACE_ID_CLOCK,
SPA_INTERFACE_ID_CLOCK_NAME,
SPA_INTERFACE_ID_CLOCK_DESCRIPTION,
},
};
static SpaResult

View file

@ -0,0 +1,143 @@
/* Spa Video Test Source
* Copyright (C) 2016 Axis Communications AB
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#define PIXEL_SIZE 3
#define GET_IMAGE_WIDTH(this) this->current_format.info.raw.size.width
#define GET_IMAGE_HEIGHT(this) this->current_format.info.raw.size.height
enum
{
GRAY = 0,
YELLOW,
CYAN,
GREEN,
MAGENTA,
RED,
BLUE,
BLACK,
NEG_I,
WHITE,
POS_Q,
DARK_BLACK,
LIGHT_BLACK,
N_COLORS
};
struct pixel
{
char R;
char G;
char B;
};
static struct pixel colors[N_COLORS] =
{
{191, 191, 191}, /* GRAY */
{191, 191, 0}, /* YELLOW */
{0, 191, 191}, /* CYAN */
{0, 191, 0}, /* GREEN */
{191, 0, 191}, /* MAGENTA */
{191, 0, 0}, /* RED */
{0, 0, 191}, /* BLUE */
{19, 19, 19}, /* BLACK */
{0, 33, 76}, /* NEGATIVE I */
{255, 255, 255}, /* WHITE */
{49, 0, 107}, /* POSITIVE Q */
{9, 9, 9}, /* DARK BLACK */
{29, 29, 29}, /* LIGHT BLACK */
};
static void
draw_line (char *data, struct pixel *c, int w)
{
int i;
for (i = 0; i < w; i++) {
data[i * PIXEL_SIZE + 0] = c->R;
data[i * PIXEL_SIZE + 1] = c->G;
data[i * PIXEL_SIZE + 2] = c->B;
}
}
#define DRAW_LINE(data,line,offset,color,width) \
draw_line (data + (line * w + offset) * PIXEL_SIZE, colors + color, width);
static void
draw_smpte_snow (SpaVideoTestSrc *this, char *data)
{
int h, w;
int y1, y2;
int i, j;
w = GET_IMAGE_WIDTH (this);
h = GET_IMAGE_HEIGHT (this);
y1 = 2 * h / 3;
y2 = 3 * h / 4;
for (i = 0; i < y1; i++) {
for (j = 0; j < 7; j++) {
int x1 = j * w / 7;
int x2 = (j + 1) * w / 7;
DRAW_LINE (data, i, x1, j, x2 - x1);
}
}
for (i = y1; i < y2; i++) {
for (j = 0; j < 7; j++) {
int x1 = j * w / 7;
int x2 = (j + 1) * w / 7;
int c = (j & 1) ? BLACK : BLUE - j;
DRAW_LINE (data, i, x1, c, x2 - x1);
}
}
for (i = y2; i < h; i++) {
int x = 0;
/* negative I */
DRAW_LINE (data, i, x, NEG_I, w / 6);
x += w / 6;
/* white */
DRAW_LINE (data, i, x, WHITE, w / 6);
x += w / 6;
/* positive Q */
DRAW_LINE (data, i, x, POS_Q, w / 6);
x += w / 6;
/* pluge */
DRAW_LINE (data, i, x, DARK_BLACK, w / 12);
x += w / 12;
DRAW_LINE (data, i, x, BLACK, w / 12);
x += w / 12;
DRAW_LINE (data, i, x, LIGHT_BLACK, w / 12);
x += w / 12;
/* war of the ants (a.k.a. snow) */
for (j = x; j < w; j++) {
unsigned char r = rand ();
data[(i * w + j) * PIXEL_SIZE + 0] = r;
data[(i * w + j) * PIXEL_SIZE + 1] = r;
data[(i * w + j) * PIXEL_SIZE + 2] = r;
}
}
}

File diff suppressed because it is too large Load diff