mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-10-29 05:40:27 -04:00
Port videotestsrc to LoopUtils
The `LoopUtils` interface can be used on platforms that don't support the Linux-specific `timerfd` interface. Added `local-videotestsrc` to validate the plugin still functions. Restructured the SDL event loop as the window would not update under WSL2, resulting in a black window being shown. All rendering in SDL2 must happen on the same thread that originally created the renderer. To prevent the SDL event loop from being starved, we make sure to poll it at least every 100 ms.
This commit is contained in:
parent
c94d5ed215
commit
dd652d01b8
5 changed files with 577 additions and 113 deletions
|
|
@ -43,7 +43,7 @@
|
|||
static SPA_LOG_IMPL(default_log);
|
||||
|
||||
#define MAX_BUFFERS 8
|
||||
|
||||
#define LOOP_TIMEOUT_MS 100
|
||||
#define USE_BUFFER false
|
||||
|
||||
struct buffer {
|
||||
|
|
@ -401,57 +401,33 @@ static int negotiate_formats(struct data *data)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void *loop(void *user_data)
|
||||
static void loop(struct data *data)
|
||||
{
|
||||
struct data *data = user_data;
|
||||
|
||||
printf("enter thread\n");
|
||||
spa_loop_control_enter(data->control);
|
||||
|
||||
while (data->running) {
|
||||
spa_loop_control_iterate(data->control, -1);
|
||||
}
|
||||
|
||||
printf("leave thread\n");
|
||||
spa_loop_control_leave(data->control);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void run_async_source(struct data *data)
|
||||
{
|
||||
int res, err;
|
||||
int res;
|
||||
struct spa_command cmd;
|
||||
SDL_Event event;
|
||||
bool running = true;
|
||||
|
||||
printf("starting...\n\n");
|
||||
cmd = SPA_NODE_COMMAND_INIT(SPA_NODE_COMMAND_Start);
|
||||
if ((res = spa_node_send_command(data->source, &cmd)) < 0)
|
||||
printf("got error %d\n", res);
|
||||
|
||||
spa_loop_control_leave(data->control);
|
||||
|
||||
data->running = true;
|
||||
if ((err = pthread_create(&data->thread, NULL, loop, data)) != 0) {
|
||||
printf("can't create thread: %d %s", err, strerror(err));
|
||||
data->running = false;
|
||||
}
|
||||
|
||||
while (running && SDL_WaitEvent(&event)) {
|
||||
switch (event.type) {
|
||||
case SDL_QUIT:
|
||||
running = false;
|
||||
break;
|
||||
while (data->running) {
|
||||
// must be called from the thread that created the renderer
|
||||
while (SDL_PollEvent(&event)) {
|
||||
switch (event.type) {
|
||||
case SDL_QUIT:
|
||||
data->running = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (data->running) {
|
||||
data->running = false;
|
||||
pthread_join(data->thread, NULL);
|
||||
// small timeout to make sure we don't starve the SDL loop
|
||||
spa_loop_control_iterate(data->control, LOOP_TIMEOUT_MS);
|
||||
}
|
||||
|
||||
spa_loop_control_enter(data->control);
|
||||
|
||||
printf("pausing...\n\n");
|
||||
cmd = SPA_NODE_COMMAND_INIT(SPA_NODE_COMMAND_Pause);
|
||||
if ((res = spa_node_send_command(data->source, &cmd)) < 0)
|
||||
|
|
@ -531,7 +507,7 @@ int main(int argc, char *argv[])
|
|||
}
|
||||
|
||||
spa_loop_control_enter(data.control);
|
||||
run_async_source(&data);
|
||||
loop(&data);
|
||||
spa_loop_control_leave(data.control);
|
||||
|
||||
SDL_DestroyRenderer(data.renderer);
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@
|
|||
static SPA_LOG_IMPL(default_log);
|
||||
|
||||
#define MAX_BUFFERS 8
|
||||
#define LOOP_TIMEOUT_MS 100
|
||||
|
||||
struct buffer {
|
||||
struct spa_buffer buffer;
|
||||
|
|
@ -396,56 +397,34 @@ static int negotiate_formats(struct data *data)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void *loop(void *user_data)
|
||||
static void loop(struct data *data)
|
||||
{
|
||||
struct data *data = user_data;
|
||||
|
||||
printf("enter thread\n");
|
||||
spa_loop_control_enter(data->control);
|
||||
|
||||
while (data->running) {
|
||||
spa_loop_control_iterate(data->control, -1);
|
||||
}
|
||||
|
||||
printf("leave thread\n");
|
||||
spa_loop_control_leave(data->control);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void run_async_source(struct data *data)
|
||||
{
|
||||
int res, err;
|
||||
int res;
|
||||
struct spa_command cmd;
|
||||
SDL_Event event;
|
||||
bool running = true;
|
||||
|
||||
printf("starting...\n\n");
|
||||
cmd = SPA_NODE_COMMAND_INIT(SPA_NODE_COMMAND_Start);
|
||||
if ((res = spa_node_send_command(data->source, &cmd)) < 0)
|
||||
printf("got error %d\n", res);
|
||||
|
||||
spa_loop_control_leave(data->control);
|
||||
|
||||
data->running = true;
|
||||
if ((err = pthread_create(&data->thread, NULL, loop, data)) != 0) {
|
||||
printf("can't create thread: %d %s", err, strerror(err));
|
||||
data->running = false;
|
||||
}
|
||||
|
||||
while (running && SDL_WaitEvent(&event)) {
|
||||
switch (event.type) {
|
||||
case SDL_QUIT:
|
||||
running = false;
|
||||
break;
|
||||
while (data->running) {
|
||||
// must be called from the thread that created the renderer
|
||||
while (SDL_PollEvent(&event)) {
|
||||
switch (event.type) {
|
||||
case SDL_QUIT:
|
||||
data->running = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// small timeout to make sure we don't starve the SDL loop
|
||||
spa_loop_control_iterate(data->control, LOOP_TIMEOUT_MS);
|
||||
}
|
||||
|
||||
if (data->running) {
|
||||
data->running = false;
|
||||
pthread_join(data->thread, NULL);
|
||||
}
|
||||
|
||||
spa_loop_control_enter(data->control);
|
||||
|
||||
printf("pausing...\n\n");
|
||||
cmd = SPA_NODE_COMMAND_INIT(SPA_NODE_COMMAND_Pause);
|
||||
if ((res = spa_node_send_command(data->source, &cmd)) < 0)
|
||||
printf("got error %d\n", res);
|
||||
|
|
@ -524,7 +503,7 @@ int main(int argc, char *argv[])
|
|||
}
|
||||
|
||||
spa_loop_control_enter(data.control);
|
||||
run_async_source(&data);
|
||||
loop(&data);
|
||||
spa_loop_control_leave(data.control);
|
||||
|
||||
SDL_DestroyRenderer(data.renderer);
|
||||
|
|
|
|||
535
spa/examples/local-videotestsrc.c
Normal file
535
spa/examples/local-videotestsrc.c
Normal file
|
|
@ -0,0 +1,535 @@
|
|||
/* Spa */
|
||||
/* SPDX-FileCopyrightText: Copyright © 2020 Collabora Ltd. */
|
||||
/* @author Raghavendra Rao Sidlagatta <raghavendra.rao@collabora.com> */
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
|
||||
/*
|
||||
[title]
|
||||
Example using libspa-videotestsrc, with only \ref api_spa
|
||||
[title]
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <dlfcn.h>
|
||||
#include <poll.h>
|
||||
#include <pthread.h>
|
||||
#include <errno.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include <SDL2/SDL.h>
|
||||
|
||||
#include <spa/support/plugin.h>
|
||||
#include <spa/utils/names.h>
|
||||
#include <spa/utils/result.h>
|
||||
#include <spa/utils/string.h>
|
||||
#include <spa/support/log-impl.h>
|
||||
#include <spa/support/loop.h>
|
||||
#include <spa/node/node.h>
|
||||
#include <spa/node/io.h>
|
||||
#include <spa/node/utils.h>
|
||||
#include <spa/param/param.h>
|
||||
#include <spa/param/props.h>
|
||||
#include <spa/param/video/format-utils.h>
|
||||
#include <spa/debug/pod.h>
|
||||
|
||||
#define WIDTH 640
|
||||
#define HEIGHT 480
|
||||
|
||||
static SPA_LOG_IMPL(default_log);
|
||||
|
||||
#define MAX_BUFFERS 8
|
||||
#define LOOP_TIMEOUT_MS 100
|
||||
#define USE_BUFFER true
|
||||
|
||||
struct buffer {
|
||||
struct spa_buffer buffer;
|
||||
struct spa_meta metas[1];
|
||||
struct spa_meta_header header;
|
||||
struct spa_data datas[1];
|
||||
struct spa_chunk chunks[1];
|
||||
SDL_Texture *texture;
|
||||
};
|
||||
|
||||
struct data {
|
||||
const char *plugin_dir;
|
||||
struct spa_log *log;
|
||||
struct spa_system *system;
|
||||
struct spa_loop *loop;
|
||||
struct spa_loop_control *control;
|
||||
struct spa_loop_utils *loop_utils;
|
||||
|
||||
struct spa_support support[6];
|
||||
uint32_t n_support;
|
||||
|
||||
struct spa_node *source;
|
||||
struct spa_hook listener;
|
||||
struct spa_io_buffers source_output[1];
|
||||
|
||||
SDL_Renderer *renderer;
|
||||
SDL_Window *window;
|
||||
SDL_Texture *texture;
|
||||
|
||||
bool use_buffer;
|
||||
|
||||
bool running;
|
||||
pthread_t thread;
|
||||
|
||||
struct spa_buffer *bp[MAX_BUFFERS];
|
||||
struct buffer buffers[MAX_BUFFERS];
|
||||
unsigned int n_buffers;
|
||||
};
|
||||
|
||||
static int load_handle(struct data *data, struct spa_handle **handle, const char *lib, const char *name)
|
||||
{
|
||||
int res;
|
||||
void *hnd;
|
||||
spa_handle_factory_enum_func_t enum_func;
|
||||
uint32_t i;
|
||||
|
||||
char *path = NULL;
|
||||
|
||||
if ((path = spa_aprintf("%s/%s", data->plugin_dir, lib)) == NULL) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
if ((hnd = dlopen(path, RTLD_NOW)) == NULL) {
|
||||
printf("can't load %s: %s\n", path, dlerror());
|
||||
free(path);
|
||||
return -errno;
|
||||
}
|
||||
free(path);
|
||||
if ((enum_func = dlsym(hnd, SPA_HANDLE_FACTORY_ENUM_FUNC_NAME)) == NULL) {
|
||||
printf("can't find enum function\n");
|
||||
return -errno;
|
||||
}
|
||||
|
||||
for (i = 0;;) {
|
||||
const struct spa_handle_factory *factory;
|
||||
|
||||
if ((res = enum_func(&factory, &i)) <= 0) {
|
||||
if (res != 0)
|
||||
printf("can't enumerate factories: %s\n", spa_strerror(res));
|
||||
break;
|
||||
}
|
||||
if (!spa_streq(factory->name, name))
|
||||
continue;
|
||||
|
||||
*handle = calloc(1, spa_handle_factory_get_size(factory, NULL));
|
||||
if ((res = spa_handle_factory_init(factory, *handle,
|
||||
NULL, data->support,
|
||||
data->n_support)) < 0) {
|
||||
printf("can't make factory instance: %d\n", res);
|
||||
return res;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return -EBADF;
|
||||
}
|
||||
|
||||
static int make_node(struct data *data, struct spa_node **node, const char *lib, const char *name)
|
||||
{
|
||||
struct spa_handle *handle = NULL;
|
||||
void *iface;
|
||||
int res;
|
||||
|
||||
if ((res = load_handle(data, &handle, lib, name)) < 0)
|
||||
return res;
|
||||
|
||||
if ((res = spa_handle_get_interface(handle, SPA_TYPE_INTERFACE_Node, &iface)) < 0) {
|
||||
printf("can't get interface %d\n", res);
|
||||
return res;
|
||||
}
|
||||
*node = iface;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int on_source_ready(void *_data, int status)
|
||||
{
|
||||
struct data *data = _data;
|
||||
int res;
|
||||
struct buffer *b;
|
||||
void *sdata, *ddata;
|
||||
int sstride, dstride;
|
||||
int i;
|
||||
uint8_t *src, *dst;
|
||||
struct spa_data *datas;
|
||||
struct spa_io_buffers *io = &data->source_output[0];
|
||||
|
||||
if (io->status != SPA_STATUS_HAVE_DATA ||
|
||||
io->buffer_id >= MAX_BUFFERS)
|
||||
return -EINVAL;
|
||||
|
||||
b = &data->buffers[io->buffer_id];
|
||||
io->status = SPA_STATUS_NEED_DATA;
|
||||
|
||||
datas = b->buffer.datas;
|
||||
|
||||
if (b->texture) {
|
||||
SDL_Texture *texture = b->texture;
|
||||
|
||||
SDL_UnlockTexture(texture);
|
||||
if (SDL_RenderClear(data->renderer) < 0) {
|
||||
fprintf(stderr, "Couldn't render clear: %s\n", SDL_GetError());
|
||||
return -EIO;
|
||||
}
|
||||
if (SDL_RenderCopy(data->renderer, texture, NULL, NULL) < 0) {
|
||||
fprintf(stderr, "Couldn't render copy: %s\n", SDL_GetError());
|
||||
return -EIO;
|
||||
}
|
||||
SDL_RenderPresent(data->renderer);
|
||||
|
||||
if (SDL_LockTexture(texture, NULL, &sdata, &sstride) < 0) {
|
||||
fprintf(stderr, "Couldn't lock texture: %s\n", SDL_GetError());
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
datas[0].data = sdata;
|
||||
} else {
|
||||
uint8_t *map;
|
||||
|
||||
if (SDL_LockTexture(data->texture, NULL, &ddata, &dstride) < 0) {
|
||||
fprintf(stderr, "Couldn't lock texture: %s\n", SDL_GetError());
|
||||
return -EIO;
|
||||
}
|
||||
sdata = datas[0].data;
|
||||
if (datas[0].type == SPA_DATA_MemFd ||
|
||||
datas[0].type == SPA_DATA_DmaBuf) {
|
||||
map = mmap(NULL, datas[0].maxsize, PROT_READ,
|
||||
MAP_PRIVATE, datas[0].fd, datas[0].mapoffset);
|
||||
if (map == MAP_FAILED)
|
||||
return -errno;
|
||||
sdata = map;
|
||||
} else if (datas[0].type == SPA_DATA_MemPtr) {
|
||||
map = NULL;
|
||||
sdata = datas[0].data;
|
||||
} else
|
||||
return -EIO;
|
||||
|
||||
sstride = datas[0].chunk->stride;
|
||||
|
||||
for (i = 0; i < HEIGHT; i++) {
|
||||
src = ((uint8_t *) sdata + i * sstride);
|
||||
dst = ((uint8_t *) ddata + i * dstride);
|
||||
memcpy(dst, src, SPA_MIN(sstride, dstride));
|
||||
}
|
||||
SDL_UnlockTexture(data->texture);
|
||||
|
||||
SDL_RenderClear(data->renderer);
|
||||
SDL_RenderCopy(data->renderer, data->texture, NULL, NULL);
|
||||
SDL_RenderPresent(data->renderer);
|
||||
|
||||
if (map)
|
||||
munmap(map, datas[0].maxsize);
|
||||
}
|
||||
|
||||
if ((res = spa_node_process(data->source)) < 0)
|
||||
printf("got process error %d\n", res);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spa_node_callbacks source_callbacks = {
|
||||
SPA_VERSION_NODE_CALLBACKS,
|
||||
.ready = on_source_ready,
|
||||
};
|
||||
|
||||
static int make_nodes(struct data *data, uint32_t pattern)
|
||||
{
|
||||
int res;
|
||||
struct spa_pod *props;
|
||||
struct spa_pod_builder b = { 0 };
|
||||
uint8_t buffer[256];
|
||||
uint32_t index;
|
||||
|
||||
if ((res =
|
||||
make_node(data, &data->source,
|
||||
"videotestsrc/libspa-videotestsrc.so",
|
||||
"videotestsrc")) < 0) {
|
||||
printf("can't create videotestsrc: %d\n", res);
|
||||
return res;
|
||||
}
|
||||
|
||||
spa_node_set_callbacks(data->source, &source_callbacks, data);
|
||||
|
||||
index = 0;
|
||||
spa_pod_builder_init(&b, buffer, sizeof(buffer));
|
||||
if ((res = spa_node_enum_params_sync(data->source, SPA_PARAM_Props,
|
||||
&index, NULL, &props, &b)) == 1) {
|
||||
spa_debug_pod(0, NULL, props);
|
||||
}
|
||||
|
||||
spa_pod_builder_init(&b, buffer, sizeof(buffer));
|
||||
props = spa_pod_builder_add_object(&b,
|
||||
SPA_TYPE_OBJECT_Props, 0,
|
||||
SPA_PROP_patternType, SPA_POD_Int(pattern));
|
||||
|
||||
if ((res = spa_node_set_param(data->source, SPA_PARAM_Props, 0, props)) < 0)
|
||||
printf("got set_props error %d\n", res);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int setup_buffers(struct data *data)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAX_BUFFERS; i++) {
|
||||
struct buffer *b = &data->buffers[i];
|
||||
|
||||
data->bp[i] = &b->buffer;
|
||||
|
||||
b->texture = NULL;
|
||||
|
||||
b->buffer.metas = b->metas;
|
||||
b->buffer.n_metas = 1;
|
||||
b->buffer.datas = b->datas;
|
||||
b->buffer.n_datas = 1;
|
||||
|
||||
b->header.flags = 0;
|
||||
b->header.seq = 0;
|
||||
b->header.pts = 0;
|
||||
b->header.dts_offset = 0;
|
||||
b->metas[0].type = SPA_META_Header;
|
||||
b->metas[0].data = &b->header;
|
||||
b->metas[0].size = sizeof(b->header);
|
||||
|
||||
b->datas[0].type = SPA_DATA_DmaBuf;
|
||||
b->datas[0].flags = 0;
|
||||
b->datas[0].fd = -1;
|
||||
b->datas[0].mapoffset = 0;
|
||||
b->datas[0].maxsize = 0;
|
||||
b->datas[0].data = NULL;
|
||||
b->datas[0].chunk = &b->chunks[0];
|
||||
b->datas[0].chunk->offset = 0;
|
||||
b->datas[0].chunk->size = 0;
|
||||
b->datas[0].chunk->stride = 0;
|
||||
}
|
||||
data->n_buffers = MAX_BUFFERS;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sdl_alloc_buffers(struct data *data)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAX_BUFFERS; i++) {
|
||||
struct buffer *b = &data->buffers[i];
|
||||
SDL_Texture *texture;
|
||||
void *ptr;
|
||||
int stride;
|
||||
|
||||
texture = SDL_CreateTexture(data->renderer,
|
||||
SDL_PIXELFORMAT_RGB24,
|
||||
SDL_TEXTUREACCESS_STREAMING, WIDTH, HEIGHT);
|
||||
if (!texture) {
|
||||
printf("can't create texture: %s\n", SDL_GetError());
|
||||
return -ENOMEM;
|
||||
}
|
||||
if (SDL_LockTexture(texture, NULL, &ptr, &stride) < 0) {
|
||||
fprintf(stderr, "Couldn't lock texture: %s\n", SDL_GetError());
|
||||
return -EIO;
|
||||
}
|
||||
b->texture = texture;
|
||||
|
||||
b->datas[0].type = SPA_DATA_DmaBuf;
|
||||
b->datas[0].maxsize = stride * HEIGHT;
|
||||
b->datas[0].data = ptr;
|
||||
b->datas[0].chunk->offset = 0;
|
||||
b->datas[0].chunk->size = stride * HEIGHT;
|
||||
b->datas[0].chunk->stride = stride;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int negotiate_formats(struct data *data)
|
||||
{
|
||||
int res;
|
||||
struct spa_pod *format;
|
||||
uint8_t buffer[256];
|
||||
struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
|
||||
|
||||
data->source_output[0] = SPA_IO_BUFFERS_INIT;
|
||||
|
||||
if ((res =
|
||||
spa_node_port_set_io(data->source,
|
||||
SPA_DIRECTION_OUTPUT, 0,
|
||||
SPA_IO_Buffers,
|
||||
&data->source_output[0], sizeof(data->source_output[0]))) < 0)
|
||||
return res;
|
||||
|
||||
format = spa_format_video_raw_build(&b, 0,
|
||||
&SPA_VIDEO_INFO_RAW_INIT(
|
||||
.format = SPA_VIDEO_FORMAT_RGB,
|
||||
.size = SPA_RECTANGLE(WIDTH, HEIGHT),
|
||||
.framerate = SPA_FRACTION(25,1)));
|
||||
|
||||
if ((res = spa_node_port_set_param(data->source,
|
||||
SPA_DIRECTION_OUTPUT, 0,
|
||||
SPA_PARAM_Format, 0,
|
||||
format)) < 0)
|
||||
return res;
|
||||
|
||||
|
||||
setup_buffers(data);
|
||||
|
||||
if (data->use_buffer) {
|
||||
if ((res = sdl_alloc_buffers(data)) < 0)
|
||||
return res;
|
||||
|
||||
if ((res = spa_node_port_use_buffers(data->source,
|
||||
SPA_DIRECTION_OUTPUT, 0, 0,
|
||||
data->bp, data->n_buffers)) < 0) {
|
||||
printf("can't allocate buffers: %s\n", spa_strerror(res));
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
unsigned int n_buffers;
|
||||
|
||||
data->texture = SDL_CreateTexture(data->renderer,
|
||||
SDL_PIXELFORMAT_RGB24,
|
||||
SDL_TEXTUREACCESS_STREAMING, WIDTH, HEIGHT);
|
||||
if (!data->texture) {
|
||||
printf("can't create texture: %s\n", SDL_GetError());
|
||||
return -1;
|
||||
}
|
||||
n_buffers = MAX_BUFFERS;
|
||||
if ((res = spa_node_port_use_buffers(data->source,
|
||||
SPA_DIRECTION_OUTPUT, 0,
|
||||
SPA_NODE_BUFFERS_FLAG_ALLOC,
|
||||
data->bp, n_buffers)) < 0) {
|
||||
printf("can't allocate buffers: %s\n", spa_strerror(res));
|
||||
return -1;
|
||||
}
|
||||
data->n_buffers = n_buffers;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void loop(struct data *data)
|
||||
{
|
||||
int res;
|
||||
struct spa_command cmd;
|
||||
SDL_Event event;
|
||||
|
||||
printf("starting...\n\n");
|
||||
cmd = SPA_NODE_COMMAND_INIT(SPA_NODE_COMMAND_Start);
|
||||
if ((res = spa_node_send_command(data->source, &cmd)) < 0)
|
||||
printf("got error %d\n", res);
|
||||
|
||||
data->running = true;
|
||||
|
||||
while (data->running) {
|
||||
// must be called from the thread that created the renderer
|
||||
while (SDL_PollEvent(&event)) {
|
||||
switch (event.type) {
|
||||
case SDL_QUIT:
|
||||
data->running = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// small timeout to make sure we don't starve the SDL loop
|
||||
spa_loop_control_iterate(data->control, LOOP_TIMEOUT_MS);
|
||||
}
|
||||
|
||||
printf("pausing...\n\n");
|
||||
cmd = SPA_NODE_COMMAND_INIT(SPA_NODE_COMMAND_Pause);
|
||||
if ((res = spa_node_send_command(data->source, &cmd)) < 0)
|
||||
printf("got error %d\n", res);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct data data = { 0 };
|
||||
int res;
|
||||
const char *str;
|
||||
struct spa_handle *handle = NULL;
|
||||
void *iface;
|
||||
uint32_t pattern = 0;
|
||||
|
||||
if ((str = getenv("SPA_PLUGIN_DIR")) == NULL)
|
||||
str = PLUGINDIR;
|
||||
data.plugin_dir = str;
|
||||
|
||||
if ((res = load_handle(&data, &handle,
|
||||
"support/libspa-support.so",
|
||||
SPA_NAME_SUPPORT_SYSTEM)) < 0)
|
||||
return res;
|
||||
|
||||
if ((res = spa_handle_get_interface(handle, SPA_TYPE_INTERFACE_System, &iface)) < 0) {
|
||||
printf("can't get System interface %d\n", res);
|
||||
return res;
|
||||
}
|
||||
data.system = iface;
|
||||
data.support[data.n_support++] = SPA_SUPPORT_INIT(SPA_TYPE_INTERFACE_System, data.system);
|
||||
data.support[data.n_support++] = SPA_SUPPORT_INIT(SPA_TYPE_INTERFACE_DataSystem, data.system);
|
||||
|
||||
if ((res = load_handle(&data, &handle,
|
||||
"support/libspa-support.so",
|
||||
SPA_NAME_SUPPORT_LOOP)) < 0)
|
||||
return res;
|
||||
|
||||
if ((res = spa_handle_get_interface(handle, SPA_TYPE_INTERFACE_Loop, &iface)) < 0) {
|
||||
printf("can't get interface %d\n", res);
|
||||
return res;
|
||||
}
|
||||
data.loop = iface;
|
||||
if ((res = spa_handle_get_interface(handle, SPA_TYPE_INTERFACE_LoopControl, &iface)) < 0) {
|
||||
printf("can't get interface %d\n", res);
|
||||
return res;
|
||||
}
|
||||
data.control = iface;
|
||||
if ((res = spa_handle_get_interface(handle, SPA_TYPE_INTERFACE_LoopUtils, &iface)) < 0) {
|
||||
printf("can't get interface %d\n", res);
|
||||
return res;
|
||||
}
|
||||
data.loop_utils = iface;
|
||||
|
||||
data.use_buffer = USE_BUFFER;
|
||||
|
||||
data.log = &default_log.log;
|
||||
|
||||
if ((str = getenv("SPA_DEBUG")))
|
||||
data.log->level = atoi(str);
|
||||
|
||||
data.support[data.n_support++] = SPA_SUPPORT_INIT(SPA_TYPE_INTERFACE_Log, data.log);
|
||||
data.support[data.n_support++] = SPA_SUPPORT_INIT(SPA_TYPE_INTERFACE_Loop, data.loop);
|
||||
data.support[data.n_support++] = SPA_SUPPORT_INIT(SPA_TYPE_INTERFACE_DataLoop, data.loop);
|
||||
data.support[data.n_support++] = SPA_SUPPORT_INIT(SPA_TYPE_INTERFACE_LoopUtils, data.loop_utils);
|
||||
|
||||
if (SDL_Init(SDL_INIT_VIDEO) < 0) {
|
||||
printf("can't initialize SDL: %s\n", SDL_GetError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (SDL_CreateWindowAndRenderer
|
||||
(WIDTH, HEIGHT, SDL_WINDOW_RESIZABLE, &data.window, &data.renderer)) {
|
||||
printf("can't create window: %s\n", SDL_GetError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (argv[1] != NULL)
|
||||
pattern = atoi(argv[1]);
|
||||
|
||||
if ((res = make_nodes(&data, pattern)) < 0) {
|
||||
printf("can't make nodes: %d\n", res);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((res = negotiate_formats(&data)) < 0) {
|
||||
printf("can't negotiate nodes: %d\n", res);
|
||||
return -1;
|
||||
}
|
||||
|
||||
spa_loop_control_enter(data.control);
|
||||
loop(&data);
|
||||
spa_loop_control_leave(data.control);
|
||||
|
||||
SDL_DestroyRenderer(data.renderer);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@ spa_examples = [
|
|||
'example-control',
|
||||
'local-libcamera',
|
||||
'local-v4l2',
|
||||
'local-videotestsrc',
|
||||
]
|
||||
|
||||
if not get_option('examples').allowed() or not get_option('spa-plugins').allowed()
|
||||
|
|
@ -12,6 +13,7 @@ endif
|
|||
|
||||
spa_examples_extra_deps = {
|
||||
'local-v4l2': [sdl_dep],
|
||||
'local-videotestsrc': [sdl_dep],
|
||||
'local-libcamera': [sdl_dep, libcamera_dep],
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ struct impl {
|
|||
|
||||
struct spa_log *log;
|
||||
struct spa_loop *data_loop;
|
||||
struct spa_system *data_system;
|
||||
struct spa_loop_utils *loop_utils;
|
||||
|
||||
uint64_t info_all;
|
||||
struct spa_node_info info;
|
||||
|
|
@ -98,7 +98,7 @@ struct impl {
|
|||
struct spa_callbacks callbacks;
|
||||
|
||||
bool async;
|
||||
struct spa_source timer_source;
|
||||
struct spa_source *timer_source;
|
||||
struct itimerspec timerspec;
|
||||
|
||||
bool started;
|
||||
|
|
@ -277,27 +277,10 @@ static void set_timer(struct impl *this, bool enabled)
|
|||
this->timerspec.it_value.tv_sec = 0;
|
||||
this->timerspec.it_value.tv_nsec = 0;
|
||||
}
|
||||
spa_system_timerfd_settime(this->data_system,
|
||||
this->timer_source.fd, SPA_FD_TIMER_ABSTIME, &this->timerspec, NULL);
|
||||
spa_loop_utils_update_timer(this->loop_utils, this->timer_source, &this->timerspec.it_value, &this->timerspec.it_interval, true);
|
||||
}
|
||||
}
|
||||
|
||||
static int read_timer(struct impl *this)
|
||||
{
|
||||
uint64_t expirations;
|
||||
int res = 0;
|
||||
|
||||
if (this->async || this->props.live) {
|
||||
if ((res = spa_system_timerfd_read(this->data_system,
|
||||
this->timer_source.fd, &expirations)) < 0) {
|
||||
if (res != -EAGAIN)
|
||||
spa_log_error(this->log, "%p: timerfd error: %s",
|
||||
this, spa_strerror(res));
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static int make_buffer(struct impl *this)
|
||||
{
|
||||
struct buffer *b;
|
||||
|
|
@ -305,9 +288,6 @@ static int make_buffer(struct impl *this)
|
|||
struct spa_io_buffers *io = port->io;
|
||||
uint32_t n_bytes;
|
||||
|
||||
if (read_timer(this) < 0)
|
||||
return 0;
|
||||
|
||||
if (spa_list_is_empty(&port->empty)) {
|
||||
set_timer(this, false);
|
||||
spa_log_error(this->log, "%p: out of buffers", this);
|
||||
|
|
@ -343,9 +323,9 @@ static int make_buffer(struct impl *this)
|
|||
return io->status;
|
||||
}
|
||||
|
||||
static void on_output(struct spa_source *source)
|
||||
static void on_output(void* data, uint64_t expirations)
|
||||
{
|
||||
struct impl *this = source->data;
|
||||
struct impl *this = data;
|
||||
int res;
|
||||
|
||||
res = make_buffer(this);
|
||||
|
|
@ -859,7 +839,7 @@ static int impl_get_interface(struct spa_handle *handle, const char *type, void
|
|||
static int do_remove_timer(struct spa_loop *loop, bool async, uint32_t seq, const void *data, size_t size, void *user_data)
|
||||
{
|
||||
struct impl *this = user_data;
|
||||
spa_loop_remove_source(this->data_loop, &this->timer_source);
|
||||
spa_loop_remove_source(this->data_loop, this->timer_source);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -873,7 +853,7 @@ static int impl_clear(struct spa_handle *handle)
|
|||
|
||||
if (this->data_loop)
|
||||
spa_loop_invoke(this->data_loop, do_remove_timer, 0, NULL, 0, true, this);
|
||||
spa_system_close(this->data_system, this->timer_source.fd);
|
||||
spa_loop_utils_destroy_source(this->loop_utils, this->timer_source);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -905,7 +885,7 @@ impl_init(const struct spa_handle_factory *factory,
|
|||
|
||||
this->log = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log);
|
||||
this->data_loop = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_DataLoop);
|
||||
this->data_system = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_DataSystem);
|
||||
this->loop_utils = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_LoopUtils);
|
||||
|
||||
spa_hook_list_init(&this->hooks);
|
||||
|
||||
|
|
@ -926,20 +906,12 @@ impl_init(const struct spa_handle_factory *factory,
|
|||
this->info.n_params = 2;
|
||||
reset_props(&this->props);
|
||||
|
||||
this->timer_source.func = on_output;
|
||||
this->timer_source.data = this;
|
||||
this->timer_source.fd = spa_system_timerfd_create(this->data_system, CLOCK_MONOTONIC,
|
||||
SPA_FD_CLOEXEC | SPA_FD_NONBLOCK);
|
||||
this->timer_source.mask = SPA_IO_IN;
|
||||
this->timer_source.rmask = 0;
|
||||
this->timer_source = spa_loop_utils_add_timer(this->loop_utils, on_output, this);
|
||||
this->timerspec.it_value.tv_sec = 0;
|
||||
this->timerspec.it_value.tv_nsec = 0;
|
||||
this->timerspec.it_interval.tv_sec = 0;
|
||||
this->timerspec.it_interval.tv_nsec = 0;
|
||||
|
||||
if (this->data_loop)
|
||||
spa_loop_add_source(this->data_loop, &this->timer_source);
|
||||
|
||||
port = &this->port;
|
||||
port->info_all = SPA_PORT_CHANGE_MASK_FLAGS |
|
||||
SPA_PORT_CHANGE_MASK_PARAMS;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue