mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-10-31 22:25:38 -04:00
More work on implementing remote protocol
Rework things so that we negotiate buffer pools beforehand and only pass buffer ids around We can then remove the refcount of buffers, events and commands. More work on buffer reuse Use the node state changes to trigger the next step in the configuration sequence. Move most of the client-node to a plugin Do buffer allocation in the port link.
This commit is contained in:
parent
05829f33e6
commit
3ace7e9648
36 changed files with 1780 additions and 1450 deletions
|
|
@ -161,11 +161,11 @@ on_sink_event (SpaNode *node, SpaEvent *event, void *user_data)
|
|||
|
||||
iinfo.port_id = event->port_id;
|
||||
iinfo.flags = SPA_INPUT_FLAG_NONE;
|
||||
iinfo.id = 0;
|
||||
iinfo.buffer_id = 0;
|
||||
iinfo.offset = 0;
|
||||
iinfo.size = total;
|
||||
|
||||
g_debug ("push sink %d", iinfo.id);
|
||||
g_debug ("push sink %d", iinfo.buffer_id);
|
||||
if ((res = spa_node_port_push_input (priv->sink, 1, &iinfo)) < 0)
|
||||
g_debug ("got error %d", res);
|
||||
break;
|
||||
|
|
@ -427,13 +427,14 @@ free_mem_block (MemBlock *b)
|
|||
|
||||
static gboolean
|
||||
on_received_buffer (PinosPort *port,
|
||||
SpaBuffer *buffer,
|
||||
uint32_t buffer_id,
|
||||
GError **error,
|
||||
gpointer user_data)
|
||||
{
|
||||
PinosSpaAlsaSink *this = user_data;
|
||||
PinosSpaAlsaSinkPrivate *priv = this->priv;
|
||||
unsigned int i;
|
||||
SpaBuffer *buffer = NULL;
|
||||
|
||||
for (i = 0; i < buffer->n_datas; i++) {
|
||||
SpaData *d = &buffer->datas[i];
|
||||
|
|
@ -460,11 +461,19 @@ on_received_buffer (PinosPort *port,
|
|||
|
||||
pinos_ringbuffer_write_advance (priv->ringbuffer, total);
|
||||
}
|
||||
spa_buffer_unref (buffer);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
on_received_event (PinosPort *port,
|
||||
SpaEvent *event,
|
||||
GError **error,
|
||||
gpointer user_data)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
on_format_change (GObject *obj,
|
||||
GParamSpec *pspec,
|
||||
|
|
@ -499,7 +508,7 @@ add_port (PinosNode *node,
|
|||
data->port = PINOS_NODE_CLASS (pinos_spa_alsa_sink_parent_class)
|
||||
->add_port (node, direction, id, error);
|
||||
|
||||
pinos_port_set_received_buffer_cb (data->port, on_received_buffer, sink, NULL);
|
||||
pinos_port_set_received_cb (data->port, on_received_buffer, on_received_event, sink, NULL);
|
||||
|
||||
formats = g_bytes_new ("ANY", strlen ("ANY") + 1);
|
||||
g_object_set (data->port, "possible-formats", formats, NULL);
|
||||
|
|
|
|||
|
|
@ -48,8 +48,6 @@ typedef struct {
|
|||
|
||||
struct _PinosSpaV4l2SourcePrivate
|
||||
{
|
||||
SpaNode *source;
|
||||
|
||||
SpaPollFd fds[16];
|
||||
unsigned int n_fds;
|
||||
SpaPollItem poll;
|
||||
|
|
@ -112,143 +110,6 @@ make_node (SpaNode **node, const char *lib, const char *name)
|
|||
return SPA_RESULT_ERROR;
|
||||
}
|
||||
|
||||
static void
|
||||
on_source_event (SpaNode *node, SpaEvent *event, void *user_data)
|
||||
{
|
||||
PinosSpaV4l2Source *source = user_data;
|
||||
PinosSpaV4l2SourcePrivate *priv = source->priv;
|
||||
|
||||
switch (event->type) {
|
||||
case SPA_EVENT_TYPE_CAN_PULL_OUTPUT:
|
||||
{
|
||||
SpaOutputInfo info[1] = { 0, };
|
||||
SpaResult res;
|
||||
SpaBuffer *b;
|
||||
GList *walk;
|
||||
|
||||
if ((res = spa_node_port_pull_output (priv->source, 1, info)) < 0)
|
||||
g_debug ("spa-v4l2-source %p: got pull error %d", source, res);
|
||||
|
||||
b = NULL; /* info[0].id; */
|
||||
|
||||
for (walk = priv->ports; walk; walk = g_list_next (walk)) {
|
||||
SourcePortData *data = walk->data;
|
||||
GError *error = NULL;
|
||||
|
||||
if (!data->have_format) {
|
||||
g_object_set (data->port, "format", priv->format, NULL);
|
||||
data->have_format = TRUE;
|
||||
}
|
||||
|
||||
if (!pinos_port_send_buffer (data->port, b, &error)) {
|
||||
g_debug ("send failed: %s", error->message);
|
||||
g_clear_error (&error);
|
||||
}
|
||||
}
|
||||
spa_buffer_unref (b);
|
||||
break;
|
||||
}
|
||||
|
||||
case SPA_EVENT_TYPE_ADD_POLL:
|
||||
{
|
||||
SpaPollItem *poll = event->data;
|
||||
|
||||
priv->poll = *poll;
|
||||
priv->fds[0] = poll->fds[0];
|
||||
priv->n_fds = 1;
|
||||
priv->poll.fds = priv->fds;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
g_debug ("got event %d", event->type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
create_pipeline (PinosSpaV4l2Source *this)
|
||||
{
|
||||
PinosSpaV4l2SourcePrivate *priv = this->priv;
|
||||
SpaResult res;
|
||||
SpaProps *props;
|
||||
SpaPropValue value;
|
||||
|
||||
if ((res = make_node (&priv->source,
|
||||
"spa/build/plugins/v4l2/libspa-v4l2.so",
|
||||
"v4l2-source")) < 0) {
|
||||
g_error ("can't create v4l2-source: %d", res);
|
||||
return;
|
||||
}
|
||||
spa_node_set_event_callback (priv->source, on_source_event, this);
|
||||
|
||||
if ((res = spa_node_get_props (priv->source, &props)) < 0)
|
||||
g_debug ("got get_props error %d", res);
|
||||
|
||||
value.type = SPA_PROP_TYPE_STRING;
|
||||
value.value = "/dev/video1";
|
||||
value.size = strlen (value.value)+1;
|
||||
spa_props_set_prop (props, spa_props_index_for_name (props, "device"), &value);
|
||||
|
||||
if ((res = spa_node_set_props (priv->source, props)) < 0)
|
||||
g_debug ("got set_props error %d", res);
|
||||
}
|
||||
|
||||
static SpaResult
|
||||
negotiate_formats (PinosSpaV4l2Source *this)
|
||||
{
|
||||
PinosSpaV4l2SourcePrivate *priv = this->priv;
|
||||
SpaResult res;
|
||||
SpaFormat *format;
|
||||
SpaProps *props;
|
||||
uint32_t val;
|
||||
SpaPropValue value;
|
||||
void *state = NULL;
|
||||
SpaFraction frac;
|
||||
SpaRectangle rect;
|
||||
const gchar *str;
|
||||
|
||||
if ((res = spa_node_port_enum_formats (priv->source, 0, &format, NULL, &state)) < 0)
|
||||
return res;
|
||||
|
||||
props = &format->props;
|
||||
|
||||
value.type = SPA_PROP_TYPE_UINT32;
|
||||
value.size = sizeof (uint32_t);
|
||||
value.value = &val;
|
||||
|
||||
val = SPA_VIDEO_FORMAT_YUY2;
|
||||
if ((res = spa_props_set_prop (props, spa_props_index_for_id (props, SPA_PROP_ID_VIDEO_FORMAT), &value)) < 0)
|
||||
return res;
|
||||
|
||||
value.type = SPA_PROP_TYPE_RECTANGLE;
|
||||
value.size = sizeof (SpaRectangle);
|
||||
value.value = ▭
|
||||
rect.width = 320;
|
||||
rect.height = 240;
|
||||
if ((res = spa_props_set_prop (props, spa_props_index_for_id (props, SPA_PROP_ID_VIDEO_SIZE), &value)) < 0)
|
||||
return res;
|
||||
|
||||
value.type = SPA_PROP_TYPE_FRACTION;
|
||||
value.size = sizeof (SpaFraction);
|
||||
value.value = &frac;
|
||||
frac.num = 25;
|
||||
frac.denom = 1;
|
||||
if ((res = spa_props_set_prop (props, spa_props_index_for_id (props, SPA_PROP_ID_VIDEO_FRAMERATE), &value)) < 0)
|
||||
return res;
|
||||
|
||||
if ((res = spa_node_port_set_format (priv->source, 0, 0, format)) < 0)
|
||||
return res;
|
||||
|
||||
str = "video/x-raw,"
|
||||
" format=(string)YUY2,"
|
||||
" width=(int)320,"
|
||||
" height=(int)240,"
|
||||
" framerate=(fraction)30/1";
|
||||
priv->format = g_bytes_new_static (str, strlen (str)+1);
|
||||
|
||||
return SPA_RESULT_OK;
|
||||
}
|
||||
|
||||
static void *
|
||||
loop (void *user_data)
|
||||
{
|
||||
|
|
@ -282,35 +143,186 @@ loop (void *user_data)
|
|||
}
|
||||
|
||||
static void
|
||||
start_pipeline (PinosSpaV4l2Source *source)
|
||||
on_source_event (SpaNode *node, SpaEvent *event, void *user_data)
|
||||
{
|
||||
PinosSpaV4l2SourcePrivate *priv = source->priv;
|
||||
SpaResult res;
|
||||
SpaCommand cmd;
|
||||
int err;
|
||||
PinosSpaV4l2Source *this = user_data;
|
||||
PinosSpaV4l2SourcePrivate *priv = this->priv;
|
||||
|
||||
g_debug ("spa-v4l2-source %p: starting pipeline", source);
|
||||
negotiate_formats (source);
|
||||
switch (event->type) {
|
||||
case SPA_EVENT_TYPE_CAN_PULL_OUTPUT:
|
||||
{
|
||||
SpaOutputInfo info[1] = { 0, };
|
||||
SpaResult res;
|
||||
GList *walk;
|
||||
|
||||
cmd.type = SPA_COMMAND_START;
|
||||
if ((res = spa_node_send_command (priv->source, &cmd)) < 0)
|
||||
g_debug ("got error %d", res);
|
||||
if ((res = spa_node_port_pull_output (node, 1, info)) < 0)
|
||||
g_debug ("spa-v4l2-source %p: got pull error %d, %d", this, res, info[0].status);
|
||||
|
||||
priv->running = true;
|
||||
if ((err = pthread_create (&priv->thread, NULL, loop, source)) != 0) {
|
||||
g_debug ("spa-v4l2-source %p: can't create thread", strerror (err));
|
||||
priv->running = false;
|
||||
for (walk = priv->ports; walk; walk = g_list_next (walk)) {
|
||||
SourcePortData *data = walk->data;
|
||||
GError *error = NULL;
|
||||
|
||||
if (!pinos_port_send_buffer (data->port, info[0].buffer_id, &error)) {
|
||||
g_debug ("send failed: %s", error->message);
|
||||
g_clear_error (&error);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SPA_EVENT_TYPE_ADD_POLL:
|
||||
{
|
||||
SpaPollItem *poll = event->data;
|
||||
int err;
|
||||
|
||||
priv->poll = *poll;
|
||||
priv->fds[0] = poll->fds[0];
|
||||
priv->n_fds = 1;
|
||||
priv->poll.fds = priv->fds;
|
||||
|
||||
if (!priv->running) {
|
||||
priv->running = true;
|
||||
if ((err = pthread_create (&priv->thread, NULL, loop, this)) != 0) {
|
||||
g_debug ("spa-v4l2-source %p: can't create thread: %s", this, strerror (err));
|
||||
priv->running = false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SPA_EVENT_TYPE_REMOVE_POLL:
|
||||
{
|
||||
if (priv->running) {
|
||||
priv->running = false;
|
||||
pthread_join (priv->thread, NULL);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
g_debug ("got event %d", event->type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
stop_pipeline (PinosSpaV4l2Source *source)
|
||||
setup_node (PinosSpaV4l2Source *this)
|
||||
{
|
||||
PinosSpaV4l2SourcePrivate *priv = source->priv;
|
||||
PinosNode *node = PINOS_NODE (this);
|
||||
PinosSpaV4l2SourcePrivate *priv = this->priv;
|
||||
SpaResult res;
|
||||
SpaProps *props;
|
||||
SpaPropValue value;
|
||||
|
||||
spa_node_set_event_callback (node->node, on_source_event, this);
|
||||
|
||||
if ((res = spa_node_get_props (node->node, &props)) < 0)
|
||||
g_debug ("got get_props error %d", res);
|
||||
|
||||
value.type = SPA_PROP_TYPE_STRING;
|
||||
value.value = "/dev/video1";
|
||||
value.size = strlen (value.value)+1;
|
||||
spa_props_set_prop (props, spa_props_index_for_name (props, "device"), &value);
|
||||
|
||||
if ((res = spa_node_set_props (node->node, props)) < 0)
|
||||
g_debug ("got set_props error %d", res);
|
||||
}
|
||||
|
||||
#if 0
|
||||
static SpaResult
|
||||
negotiate_formats (PinosSpaV4l2Source *this)
|
||||
{
|
||||
PinosNode *node = PINOS_NODE (this);
|
||||
PinosSpaV4l2SourcePrivate *priv = this->priv;
|
||||
SpaResult res;
|
||||
SpaFormat *format;
|
||||
SpaProps *props;
|
||||
uint32_t val;
|
||||
SpaPropValue value;
|
||||
void *state = NULL;
|
||||
SpaFraction frac;
|
||||
SpaRectangle rect;
|
||||
const gchar *str;
|
||||
|
||||
if ((res = spa_node_port_enum_formats (node->node, 0, &format, NULL, &state)) < 0)
|
||||
return res;
|
||||
|
||||
props = &format->props;
|
||||
|
||||
value.type = SPA_PROP_TYPE_UINT32;
|
||||
value.size = sizeof (uint32_t);
|
||||
value.value = &val;
|
||||
|
||||
val = SPA_VIDEO_FORMAT_YUY2;
|
||||
if ((res = spa_props_set_prop (props, spa_props_index_for_id (props, SPA_PROP_ID_VIDEO_FORMAT), &value)) < 0)
|
||||
return res;
|
||||
|
||||
value.type = SPA_PROP_TYPE_RECTANGLE;
|
||||
value.size = sizeof (SpaRectangle);
|
||||
value.value = ▭
|
||||
rect.width = 320;
|
||||
rect.height = 240;
|
||||
if ((res = spa_props_set_prop (props, spa_props_index_for_id (props, SPA_PROP_ID_VIDEO_SIZE), &value)) < 0)
|
||||
return res;
|
||||
|
||||
value.type = SPA_PROP_TYPE_FRACTION;
|
||||
value.size = sizeof (SpaFraction);
|
||||
value.value = &frac;
|
||||
frac.num = 25;
|
||||
frac.denom = 1;
|
||||
if ((res = spa_props_set_prop (props, spa_props_index_for_id (props, SPA_PROP_ID_VIDEO_FRAMERATE), &value)) < 0)
|
||||
return res;
|
||||
|
||||
if ((res = spa_node_port_set_format (node->node, 0, 0, format)) < 0)
|
||||
return res;
|
||||
|
||||
str = "video/x-raw,"
|
||||
" format=(string)YUY2,"
|
||||
" width=(int)320,"
|
||||
" height=(int)240,"
|
||||
" framerate=(fraction)30/1";
|
||||
priv->format = g_bytes_new_static (str, strlen (str)+1);
|
||||
|
||||
priv->n_buffers = 16;
|
||||
if ((res = spa_node_port_alloc_buffers (node->node, 0, NULL, 0, priv->buffers, &priv->n_buffers)) < 0)
|
||||
return res;
|
||||
|
||||
return SPA_RESULT_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
start_pipeline (PinosSpaV4l2Source *this)
|
||||
{
|
||||
#if 0
|
||||
PinosSpaV4l2SourcePrivate *priv = this->priv;
|
||||
PinosNode *node = PINOS_NODE (this);
|
||||
SpaResult res;
|
||||
SpaCommand cmd;
|
||||
int err;
|
||||
|
||||
g_debug ("spa-v4l2-source %p: starting pipeline", this);
|
||||
negotiate_formats (this);
|
||||
|
||||
cmd.type = SPA_COMMAND_START;
|
||||
if ((res = spa_node_send_command (node->node, &cmd)) < 0)
|
||||
g_debug ("got error %d", res);
|
||||
|
||||
priv->running = true;
|
||||
if ((err = pthread_create (&priv->thread, NULL, loop, this)) != 0) {
|
||||
g_debug ("spa-v4l2-source %p: can't create thread", strerror (err));
|
||||
priv->running = false;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
stop_pipeline (PinosSpaV4l2Source *this)
|
||||
{
|
||||
PinosSpaV4l2SourcePrivate *priv = this->priv;
|
||||
PinosNode *node = PINOS_NODE (this);
|
||||
SpaResult res;
|
||||
SpaCommand cmd;
|
||||
|
||||
g_debug ("spa-v4l2-source %p: stopping pipeline", source);
|
||||
g_debug ("spa-v4l2-source %p: stopping pipeline", this);
|
||||
|
||||
if (priv->running) {
|
||||
priv->running = false;
|
||||
|
|
@ -318,14 +330,14 @@ stop_pipeline (PinosSpaV4l2Source *source)
|
|||
}
|
||||
|
||||
cmd.type = SPA_COMMAND_STOP;
|
||||
if ((res = spa_node_send_command (priv->source, &cmd)) < 0)
|
||||
if ((res = spa_node_send_command (node->node, &cmd)) < 0)
|
||||
g_debug ("got error %d", res);
|
||||
}
|
||||
|
||||
static void
|
||||
destroy_pipeline (PinosSpaV4l2Source *source)
|
||||
destroy_pipeline (PinosSpaV4l2Source *this)
|
||||
{
|
||||
g_debug ("spa-v4l2-source %p: destroy pipeline", source);
|
||||
g_debug ("spa-v4l2-source %p: destroy pipeline", this);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
|
|
@ -440,7 +452,7 @@ source_constructed (GObject * object)
|
|||
|
||||
G_OBJECT_CLASS (pinos_spa_v4l2_source_parent_class)->constructed (object);
|
||||
|
||||
create_pipeline (source);
|
||||
setup_node (source);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -454,6 +466,42 @@ source_finalize (GObject * object)
|
|||
G_OBJECT_CLASS (pinos_spa_v4l2_source_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
on_received_buffer (PinosPort *port, uint32_t buffer_id, GError **error, gpointer user_data)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
on_received_event (PinosPort *port, SpaEvent *event, GError **error, gpointer user_data)
|
||||
{
|
||||
PinosNode *node = user_data;
|
||||
PinosSpaV4l2Source *source = PINOS_SPA_V4L2_SOURCE (node);
|
||||
PinosSpaV4l2SourcePrivate *priv = source->priv;
|
||||
SpaResult res;
|
||||
|
||||
switch (event->type) {
|
||||
case SPA_EVENT_TYPE_REUSE_BUFFER:
|
||||
{
|
||||
SpaEventReuseBuffer *rb = event->data;
|
||||
|
||||
if ((res = spa_node_port_reuse_buffer (node->node,
|
||||
event->port_id,
|
||||
rb->buffer_id,
|
||||
rb->offset,
|
||||
rb->size)) < 0)
|
||||
g_warning ("client-node %p: error reuse buffer: %d", node, res);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
if ((res = spa_node_port_push_event (node->node, port->id, event)) < 0)
|
||||
g_warning ("client-node %p: error pushing event: %d", node, res);
|
||||
break;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
static PinosPort *
|
||||
add_port (PinosNode *node,
|
||||
PinosDirection direction,
|
||||
|
|
@ -472,6 +520,8 @@ add_port (PinosNode *node,
|
|||
data->port = PINOS_NODE_CLASS (pinos_spa_v4l2_source_parent_class)
|
||||
->add_port (node, direction, id, error);
|
||||
|
||||
pinos_port_set_received_cb (data->port, on_received_buffer, on_received_event, node, NULL);
|
||||
|
||||
g_debug ("connecting signals");
|
||||
g_signal_connect (data->port, "activate", (GCallback) on_activate, data);
|
||||
g_signal_connect (data->port, "deactivate", (GCallback) on_deactivate, data);
|
||||
|
|
@ -511,11 +561,21 @@ pinos_spa_v4l2_source_new (PinosDaemon *daemon,
|
|||
PinosProperties *properties)
|
||||
{
|
||||
PinosNode *node;
|
||||
SpaNode *n;
|
||||
SpaResult res;
|
||||
|
||||
if ((res = make_node (&n,
|
||||
"spa/build/plugins/v4l2/libspa-v4l2.so",
|
||||
"v4l2-source")) < 0) {
|
||||
g_error ("can't create v4l2-source: %d", res);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
node = g_object_new (PINOS_TYPE_SPA_V4L2_SOURCE,
|
||||
"daemon", daemon,
|
||||
"name", name,
|
||||
"properties", properties,
|
||||
"node", n,
|
||||
NULL);
|
||||
|
||||
return node;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue