device: add device object

Make a device object, let the v4l2 monitor create device objects
The device object is responsible for dynamically creating nodes.
This commit is contained in:
Wim Taymans 2018-11-23 12:43:47 +01:00
parent 79253c6d46
commit e1bd12e599
43 changed files with 1784 additions and 237 deletions

View file

@ -0,0 +1,90 @@
/* Simple Plugin API
*
* Copyright © 2018 Wim Taymans
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#ifndef __SPA_DEVICE_H__
#define __SPA_DEVICE_H__
#ifdef __cplusplus
extern "C" {
#endif
struct spa_device;
#include <spa/utils/defs.h>
#include <spa/utils/dict.h>
#include <spa/support/plugin.h>
/**
* spa_device_callbacks:
*/
struct spa_device_callbacks {
/** version of the structure */
#define SPA_VERSION_DEVICE_CALLBACKS 0
uint32_t version;
/**< add a new object managed by the device */
void (*add) (void *data, uint32_t id,
const struct spa_handle_factory *factory, uint32_t type,
const struct spa_dict *info);
/**< remove an object */
void (*remove) (void *data, uint32_t id);
};
/**
* spa_device:
*
* The device interface.
*/
struct spa_device {
/* the version of this device. This can be used to expand this
* structure in the future */
#define SPA_VERSION_DEVICE 0
uint32_t version;
/**
* Extra information about the device
*/
const struct spa_dict *info;
/**
* Set callbacks to receive asynchronous notifications from
* the device.
*
* \param device: a #spa_device
* \param callback: a #callbacks
* \return 0 on success
* < 0 errno on error
*/
int (*set_callbacks) (struct spa_device *device,
const struct spa_device_callbacks *callbacks,
void *data);
};
#define spa_device_set_callbacks(m,...) (m)->set_callbacks((m),__VA_ARGS__)
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* __SPA_DEVICE_H__ */

View file

@ -68,6 +68,7 @@ enum spa_monitor_item {
SPA_MONITOR_ITEM_class,
SPA_MONITOR_ITEM_info,
SPA_MONITOR_ITEM_factory,
SPA_MONITOR_ITEM_type,
};
/**

View file

@ -79,6 +79,7 @@ static const struct spa_type_info spa_type_monitor_item[] = {
{ SPA_MONITOR_ITEM_class, SPA_TYPE_MONITOR_ITEM_BASE "class", SPA_TYPE_String, },
{ SPA_MONITOR_ITEM_info, SPA_TYPE_MONITOR_ITEM_BASE "info", SPA_TYPE_Pod, },
{ SPA_MONITOR_ITEM_factory, SPA_TYPE_MONITOR_ITEM_BASE "factory", SPA_TYPE_Pointer, },
{ SPA_MONITOR_ITEM_type, SPA_TYPE_MONITOR_ITEM_BASE "type", SPA_TYPE_Id, },
{ 0, NULL, },
};

View file

@ -147,6 +147,7 @@ static const struct spa_type_info spa_types[] = {
{ SPA_TYPE_INTERFACE_DBus, SPA_TYPE_INTERFACE_BASE "DBus", SPA_TYPE_Pointer, },
{ SPA_TYPE_INTERFACE_Monitor, SPA_TYPE_INTERFACE_BASE "Monitor", SPA_TYPE_Pointer, },
{ SPA_TYPE_INTERFACE_Node, SPA_TYPE_INTERFACE_BASE "Node", SPA_TYPE_Pointer, },
{ SPA_TYPE_INTERFACE_Device, SPA_TYPE_INTERFACE_BASE "Device", SPA_TYPE_Pointer, },
{ SPA_TYPE_EVENT_START, SPA_TYPE__Event, SPA_TYPE_Object, },
{ SPA_TYPE_EVENT_Monitor, SPA_TYPE_EVENT_BASE "Monitor", SPA_TYPE_Object, spa_type_monitor_event },

View file

@ -74,6 +74,7 @@ enum {
SPA_TYPE_INTERFACE_DBus,
SPA_TYPE_INTERFACE_Monitor,
SPA_TYPE_INTERFACE_Node,
SPA_TYPE_INTERFACE_Device,
/* Events */
SPA_TYPE_EVENT_START = 0x30000,

View file

@ -186,6 +186,7 @@ fill_item(struct impl *this, snd_ctl_card_info_t *card_info,
SPA_MONITOR_ITEM_name, &SPA_POD_Stringv(name),
SPA_MONITOR_ITEM_class, &SPA_POD_Stringv(klass),
SPA_MONITOR_ITEM_factory, &SPA_POD_Pointer(SPA_TYPE_INTERFACE_HandleFactory, factory),
SPA_MONITOR_ITEM_type, &SPA_POD_Id(SPA_TYPE_INTERFACE_Node),
0);
spa_pod_builder_prop(builder, SPA_MONITOR_ITEM_info, 0);

View file

@ -29,6 +29,7 @@
extern const struct spa_handle_factory spa_alsa_source_factory;
extern const struct spa_handle_factory spa_alsa_sink_factory;
extern const struct spa_handle_factory spa_alsa_monitor_factory;
extern const struct spa_handle_factory spa_alsa_device_factory;
int spa_handle_factory_enum(const struct spa_handle_factory **factory, uint32_t *index)
{
@ -45,6 +46,9 @@ int spa_handle_factory_enum(const struct spa_handle_factory **factory, uint32_t
case 2:
*factory = &spa_alsa_monitor_factory;
break;
case 3:
*factory = &spa_alsa_device_factory;
break;
default:
return 0;
}

View file

@ -1,5 +1,6 @@
spa_alsa_sources = ['alsa.c',
'alsa-monitor.c',
'alsa-device.c',
'alsa-sink.c',
'alsa-source.c',
'alsa-utils.c']

View file

@ -1,4 +1,5 @@
v4l2_sources = ['v4l2.c',
'v4l2-device.c',
'v4l2-monitor.c',
'v4l2-source.c']

View file

@ -0,0 +1,218 @@
/* Spa V4l2 Source
*
* Copyright © 2018 Wim Taymans
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include <stddef.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <linux/videodev2.h>
#include <spa/support/log.h>
#include <spa/support/loop.h>
#include <spa/monitor/device.h>
#include <spa/debug/pod.h>
#include "v4l2.h"
#define NAME "v4l2-device"
static const char default_device[] = "/dev/video0";
extern const struct spa_handle_factory spa_v4l2_source_factory;
struct props {
char device[64];
char device_name[128];
int device_fd;
};
static void reset_props(struct props *props)
{
strncpy(props->device, default_device, 64);
}
struct impl {
struct spa_handle handle;
struct spa_device device;
struct spa_log *log;
struct spa_loop *main_loop;
struct props props;
const struct spa_device_callbacks *callbacks;
void *callbacks_data;
struct spa_v4l2_device dev;
};
static int impl_device_set_callbacks(struct spa_device *device,
const struct spa_device_callbacks *callbacks,
void *data)
{
struct impl *this;
struct spa_dict_item items[1];
spa_return_val_if_fail(device != NULL, -EINVAL);
this = SPA_CONTAINER_OF(device, struct impl, device);
this->callbacks = callbacks;
this->callbacks_data = data;
if (callbacks) {
if (spa_v4l2_is_capture(&this->dev)) {
items[0] = SPA_DICT_ITEM_INIT("device.path", this->props.device);
if (callbacks->add)
callbacks->add(data, 0,
&spa_v4l2_source_factory,
SPA_TYPE_INTERFACE_Node,
&SPA_DICT_INIT(items, 1));
}
}
return 0;
}
static const struct spa_dict_item info_items[] = {
{ "media.class", "Video/Device" },
};
static const struct spa_dict info = {
info_items,
SPA_N_ELEMENTS(info_items)
};
static const struct spa_device impl_device = {
SPA_VERSION_DEVICE,
&info,
impl_device_set_callbacks,
};
static int impl_get_interface(struct spa_handle *handle, uint32_t type, void **interface)
{
struct impl *this;
spa_return_val_if_fail(handle != NULL, -EINVAL);
spa_return_val_if_fail(interface != NULL, -EINVAL);
this = (struct impl *) handle;
if (type == SPA_TYPE_INTERFACE_Device)
*interface = &this->device;
else
return -ENOENT;
return 0;
}
static int impl_clear(struct spa_handle *handle)
{
return 0;
}
static size_t
impl_get_size(const struct spa_handle_factory *factory,
const struct spa_dict *params)
{
return sizeof(struct impl);
}
static int
impl_init(const struct spa_handle_factory *factory,
struct spa_handle *handle,
const struct spa_dict *info,
const struct spa_support *support,
uint32_t n_support)
{
struct impl *this;
uint32_t i;
const char *str;
int res;
spa_return_val_if_fail(factory != NULL, -EINVAL);
spa_return_val_if_fail(handle != NULL, -EINVAL);
handle->get_interface = impl_get_interface;
handle->clear = impl_clear, this = (struct impl *) handle;
for (i = 0; i < n_support; i++) {
if (support[i].type == SPA_TYPE_INTERFACE_Log)
this->log = support[i].data;
else if (support[i].type == SPA_TYPE_INTERFACE_MainLoop)
this->main_loop = support[i].data;
}
if (this->main_loop == NULL) {
spa_log_error(this->log, "a main_loop is needed");
return -EINVAL;
}
this->device = impl_device;
this->dev.log = this->log;
this->dev.fd = -1;
reset_props(&this->props);
if (info && (str = spa_dict_lookup(info, "device.path"))) {
strncpy(this->props.device, str, 63);
if ((res = spa_v4l2_open(&this->dev, this->props.device)) < 0)
return res;
spa_v4l2_close(&this->dev);
}
return 0;
}
static const struct spa_interface_info impl_interfaces[] = {
{SPA_TYPE_INTERFACE_Device,},
};
static int impl_enum_interface_info(const struct spa_handle_factory *factory,
const struct spa_interface_info **info,
uint32_t *index)
{
spa_return_val_if_fail(factory != NULL, -EINVAL);
spa_return_val_if_fail(info != NULL, -EINVAL);
spa_return_val_if_fail(index != NULL, -EINVAL);
if (*index >= SPA_N_ELEMENTS(impl_interfaces))
return 0;
*info = &impl_interfaces[(*index)++];
return 1;
}
const struct spa_handle_factory spa_v4l2_device_factory = {
SPA_VERSION_HANDLE_FACTORY,
NAME,
NULL,
impl_get_size,
impl_init,
impl_enum_interface_info,
};

View file

@ -39,7 +39,7 @@
#define NAME "v4l2-monitor"
extern const struct spa_handle_factory spa_v4l2_source_factory;
extern const struct spa_handle_factory spa_v4l2_device_factory;
struct item {
struct udev_device *udevice;
@ -114,8 +114,9 @@ static void fill_item(struct impl *this, struct item *item, struct udev_device *
SPA_MONITOR_ITEM_flags, &SPA_POD_Id(SPA_MONITOR_ITEM_FLAG_NONE),
SPA_MONITOR_ITEM_state, &SPA_POD_Id(SPA_MONITOR_ITEM_STATE_Available),
SPA_MONITOR_ITEM_name, &SPA_POD_Stringv(name),
SPA_MONITOR_ITEM_class, &SPA_POD_Stringc("Video/Source"),
SPA_MONITOR_ITEM_factory, &SPA_POD_Pointer(SPA_TYPE_INTERFACE_HandleFactory, &spa_v4l2_source_factory),
SPA_MONITOR_ITEM_class, &SPA_POD_Stringc("Video/Device"),
SPA_MONITOR_ITEM_factory, &SPA_POD_Pointer(SPA_TYPE_INTERFACE_HandleFactory, &spa_v4l2_device_factory),
SPA_MONITOR_ITEM_type, &SPA_POD_Id(SPA_TYPE_INTERFACE_Device),
0);
spa_pod_builder_prop(builder, SPA_MONITOR_ITEM_info, 0);

View file

@ -40,6 +40,8 @@
#include <spa/control/control.h>
#include <spa/debug/pod.h>
#include "v4l2.h"
#define NAME "v4l2-source"
static const char default_device[] = "/dev/video0";
@ -82,7 +84,6 @@ struct port {
struct impl *impl;
bool export_buf;
bool started;
bool next_fmtdesc;
struct v4l2_fmtdesc fmtdesc;
@ -94,10 +95,9 @@ struct port {
struct spa_video_info current_format;
struct spa_fraction rate;
int fd;
bool opened;
struct spa_v4l2_device dev;
bool have_query_ext_ctrl;
struct v4l2_capability cap;
struct v4l2_format fmt;
enum v4l2_buf_type type;
enum v4l2_memory memtype;
@ -128,6 +128,7 @@ struct impl {
struct props props;
const struct spa_node_callbacks *callbacks;
void *callbacks_data;
@ -604,7 +605,7 @@ static int port_set_format(struct spa_node *node,
spa_v4l2_stream_off(this);
spa_v4l2_clear_buffers(this);
port->have_format = false;
spa_v4l2_close(this);
spa_v4l2_close(&port->dev);
return 0;
} else {
if ((res = spa_format_parse(format, &info.media_type, &info.media_subtype)) < 0)
@ -839,7 +840,7 @@ static void set_control(struct impl *this, struct port *port, uint32_t control_i
spa_zero(c);
c.id = control_id;
c.value = value;
if (ioctl(port->fd, VIDIOC_S_CTRL, &c) < 0)
if (ioctl(port->dev.fd, VIDIOC_S_CTRL, &c) < 0)
spa_log_error(this->log, "VIDIOC_S_CTRL %m");
}
@ -1029,12 +1030,14 @@ impl_init(const struct spa_handle_factory *factory,
SPA_PORT_INFO_FLAG_TERMINAL;
port->export_buf = true;
port->have_query_ext_ctrl = true;
port->dev.log = this->log;
port->dev.fd = -1;
if (info && (str = spa_dict_lookup(info, "device.path"))) {
strncpy(this->props.device, str, 63);
if ((res = spa_v4l2_open(this)) < 0)
if ((res = spa_v4l2_open(&port->dev, this->props.device)) < 0)
return res;
spa_v4l2_close(this);
spa_v4l2_close(&port->dev);
}
return 0;

View file

@ -44,67 +44,81 @@ static int xioctl(int fd, int request, void *arg)
}
static int spa_v4l2_open(struct impl *this)
int spa_v4l2_open(struct spa_v4l2_device *dev, const char *path)
{
struct port *port = &this->out_ports[0];
struct stat st;
struct props *props = &this->props;
int err;
if (port->opened)
if (dev->fd != -1)
return 0;
if (props->device[0] == '\0') {
spa_log_error(this->log, "v4l2: Device property not set");
if (path == NULL) {
spa_log_error(dev->log, "v4l2: Device property not set");
return -EIO;
}
spa_log_info(this->log, "v4l2: Playback device is '%s'", props->device);
spa_log_info(dev->log, "v4l2: Playback device is '%s'", path);
if (stat(props->device, &st) < 0) {
if (stat(path, &st) < 0) {
err = errno;
spa_log_error(this->log, "v4l2: Cannot identify '%s': %d, %s",
props->device, err, strerror(err));
return -err;
spa_log_error(dev->log, "v4l2: Cannot identify '%s': %d, %s",
path, err, strerror(err));
goto error;
}
if (!S_ISCHR(st.st_mode)) {
spa_log_error(this->log, "v4l2: %s is no device", props->device);
return -ENODEV;
spa_log_error(dev->log, "v4l2: %s is no device", path);
err = ENODEV;
goto error;
}
port->fd = open(props->device, O_RDWR | O_NONBLOCK, 0);
dev->fd = open(path, O_RDWR | O_NONBLOCK, 0);
if (port->fd == -1) {
if (dev->fd == -1) {
err = errno;
spa_log_error(this->log, "v4l2: Cannot open '%s': %d, %s",
props->device, err, strerror(err));
return -err;
spa_log_error(dev->log, "v4l2: Cannot open '%s': %d, %s",
path, err, strerror(err));
goto error;
}
if (xioctl(port->fd, VIDIOC_QUERYCAP, &port->cap) < 0) {
if (xioctl(dev->fd, VIDIOC_QUERYCAP, &dev->cap) < 0) {
err = errno;
spa_log_error(this->log, "QUERYCAP: %m");
return -err;
spa_log_error(dev->log, "QUERYCAP: %m");
goto error_close;
}
return 0;
if ((port->cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) == 0 ||
((port->cap.capabilities & V4L2_CAP_DEVICE_CAPS) &&
(port->cap.device_caps & V4L2_CAP_VIDEO_CAPTURE) == 0)) {
spa_log_error(this->log, "v4l2: %s is no video capture device", props->device);
close(port->fd);
port->fd = -1;
return -ENODEV;
error_close:
close(dev->fd);
dev->fd = -1;
error:
return -err;
}
int spa_v4l2_is_capture(struct spa_v4l2_device *dev)
{
if ((dev->cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) == 0 ||
((dev->cap.capabilities & V4L2_CAP_DEVICE_CAPS) &&
(dev->cap.device_caps & V4L2_CAP_VIDEO_CAPTURE) == 0)) {
return 0;
}
return 1;
}
port->source.func = v4l2_on_fd_events;
port->source.data = this;
port->source.fd = port->fd;
port->source.mask = SPA_IO_IN | SPA_IO_ERR;
port->source.rmask = 0;
int spa_v4l2_close(struct spa_v4l2_device *dev)
{
if (dev->fd == -1)
return 0;
port->opened = true;
if (dev->active)
return 0;
spa_log_info(dev->log, "v4l2: close");
if (close(dev->fd))
spa_log_warn(dev->log, "close: %m");
dev->fd = -1;
return 0;
}
@ -112,6 +126,7 @@ static int spa_v4l2_buffer_recycle(struct impl *this, uint32_t buffer_id)
{
struct port *port = &this->out_ports[0];
struct buffer *b = &port->buffers[buffer_id];
struct spa_v4l2_device *dev = &port->dev;
int err;
if (!SPA_FLAG_CHECK(b->flags, BUFFER_FLAG_OUTSTANDING))
@ -120,7 +135,7 @@ static int spa_v4l2_buffer_recycle(struct impl *this, uint32_t buffer_id)
SPA_FLAG_UNSET(b->flags, BUFFER_FLAG_OUTSTANDING);
spa_log_trace(this->log, "v4l2 %p: recycle buffer %d", this, buffer_id);
if (xioctl(port->fd, VIDIOC_QBUF, &b->v4l2_buffer) < 0) {
if (xioctl(dev->fd, VIDIOC_QBUF, &b->v4l2_buffer) < 0) {
err = errno;
spa_log_error(this->log, "VIDIOC_QBUF: %m");
return -err;
@ -164,7 +179,7 @@ static int spa_v4l2_clear_buffers(struct impl *this)
reqbuf.memory = port->memtype;
reqbuf.count = 0;
if (xioctl(port->fd, VIDIOC_REQBUFS, &reqbuf) < 0) {
if (xioctl(port->dev.fd, VIDIOC_REQBUFS, &reqbuf) < 0) {
spa_log_warn(this->log, "VIDIOC_REQBUFS: %m");
}
port->n_buffers = 0;
@ -172,26 +187,6 @@ static int spa_v4l2_clear_buffers(struct impl *this)
return 0;
}
static int spa_v4l2_close(struct impl *this)
{
struct port *port = &this->out_ports[0];
if (!port->opened)
return 0;
if (port->have_format)
return 0;
spa_log_info(this->log, "v4l2: close");
if (close(port->fd))
spa_log_warn(this->log, "close: %m");
port->fd = -1;
port->opened = false;
return 0;
}
struct format_info {
uint32_t fourcc;
@ -536,8 +531,9 @@ spa_v4l2_enum_format(struct impl *this,
const struct format_info *info;
struct spa_pod_choice *choice;
uint32_t filter_media_type, filter_media_subtype, video_format;
struct spa_v4l2_device *dev = &port->dev;
if ((res = spa_v4l2_open(this)) < 0)
if ((res = spa_v4l2_open(dev, this->props.device)) < 0)
return res;
if (*index == 0) {
@ -578,7 +574,7 @@ spa_v4l2_enum_format(struct impl *this,
port->fmtdesc.pixelformat = info->fourcc;
} else {
if ((res = xioctl(port->fd, VIDIOC_ENUM_FMT, &port->fmtdesc)) < 0) {
if ((res = xioctl(dev->fd, VIDIOC_ENUM_FMT, &port->fmtdesc)) < 0) {
res = -errno;
if (errno != EINVAL)
spa_log_error(this->log, "VIDIOC_ENUM_FMT: %m");
@ -621,7 +617,7 @@ spa_v4l2_enum_format(struct impl *this,
}
}
do_frmsize:
if ((res = xioctl(port->fd, VIDIOC_ENUM_FRAMESIZES, &port->frmsize)) < 0) {
if ((res = xioctl(dev->fd, VIDIOC_ENUM_FRAMESIZES, &port->frmsize)) < 0) {
if (errno == EINVAL)
goto next_fmtdesc;
@ -706,7 +702,7 @@ spa_v4l2_enum_format(struct impl *this,
port->frmival.index = 0;
while (true) {
if ((res = xioctl(port->fd, VIDIOC_ENUM_FRAMEINTERVALS, &port->frmival)) < 0) {
if ((res = xioctl(dev->fd, VIDIOC_ENUM_FRAMEINTERVALS, &port->frmival)) < 0) {
res = -errno;
if (errno == EINVAL) {
port->frmsize.index++;
@ -812,7 +808,7 @@ spa_v4l2_enum_format(struct impl *this,
res = 1;
exit:
spa_v4l2_close(this);
spa_v4l2_close(dev);
return res;
@ -824,6 +820,7 @@ spa_v4l2_enum_format(struct impl *this,
static int spa_v4l2_set_format(struct impl *this, struct spa_video_info *format, bool try_only)
{
struct port *port = &this->out_ports[0];
struct spa_v4l2_device *dev = &port->dev;
int res, cmd;
struct v4l2_format reqfmt, fmt;
struct v4l2_streamparm streamparm;
@ -882,18 +879,18 @@ static int spa_v4l2_set_format(struct impl *this, struct spa_video_info *format,
reqfmt = fmt;
if ((res = spa_v4l2_open(this)) < 0)
if ((res = spa_v4l2_open(dev, this->props.device)) < 0)
return res;
cmd = try_only ? VIDIOC_TRY_FMT : VIDIOC_S_FMT;
if (xioctl(port->fd, cmd, &fmt) < 0) {
if (xioctl(dev->fd, cmd, &fmt) < 0) {
res = -errno;
spa_log_error(this->log, "VIDIOC_S_FMT: %m");
return res;
}
/* some cheap USB cam's won't accept any change */
if (xioctl(port->fd, VIDIOC_S_PARM, &streamparm) < 0)
if (xioctl(dev->fd, VIDIOC_S_PARM, &streamparm) < 0)
spa_log_warn(this->log, "VIDIOC_S_PARM: %m");
spa_log_info(this->log, "v4l2: got %08x %dx%d %d/%d", fmt.fmt.pix.pixelformat,
@ -927,17 +924,18 @@ static int spa_v4l2_set_format(struct impl *this, struct spa_video_info *format,
static int query_ext_ctrl_ioctl(struct port *port, struct v4l2_query_ext_ctrl *qctrl)
{
struct spa_v4l2_device *dev = &port->dev;
struct v4l2_queryctrl qc;
int res;
if (port->have_query_ext_ctrl) {
res = ioctl(port->fd, VIDIOC_QUERY_EXT_CTRL, qctrl);
res = xioctl(dev->fd, VIDIOC_QUERY_EXT_CTRL, qctrl);
if (errno != ENOTTY)
return res;
port->have_query_ext_ctrl = false;
}
qc.id = qctrl->id;
res = ioctl(port->fd, VIDIOC_QUERYCTRL, &qc);
res = xioctl(dev->fd, VIDIOC_QUERYCTRL, &qc);
if (res == 0) {
qctrl->type = qc.type;
memcpy(qctrl->name, qc.name, sizeof(qctrl->name));
@ -1003,6 +1001,7 @@ spa_v4l2_enum_controls(struct impl *this,
struct spa_pod_builder *builder)
{
struct port *port = &this->out_ports[0];
struct spa_v4l2_device *dev = &port->dev;
struct v4l2_query_ext_ctrl queryctrl;
struct spa_pod *param;
struct spa_pod_builder b = { 0 };
@ -1011,7 +1010,7 @@ spa_v4l2_enum_controls(struct impl *this,
int res;
const unsigned next_fl = V4L2_CTRL_FLAG_NEXT_CTRL | V4L2_CTRL_FLAG_NEXT_COMPOUND;
if ((res = spa_v4l2_open(this)) < 0)
if ((res = spa_v4l2_open(dev, this->props.device)) < 0)
return res;
next:
@ -1109,7 +1108,7 @@ spa_v4l2_enum_controls(struct impl *this,
for (querymenu.index = queryctrl.minimum;
querymenu.index <= queryctrl.maximum;
querymenu.index++) {
if (ioctl(port->fd, VIDIOC_QUERYMENU, &querymenu) == 0) {
if (xioctl(dev->fd, VIDIOC_QUERYMENU, &querymenu) == 0) {
spa_pod_builder_int(&b, querymenu.index);
spa_pod_builder_string(&b, (const char *)querymenu.name);
}
@ -1133,7 +1132,7 @@ spa_v4l2_enum_controls(struct impl *this,
res = 1;
exit:
spa_v4l2_close(this);
spa_v4l2_close(dev);
return res;
@ -1145,6 +1144,7 @@ spa_v4l2_enum_controls(struct impl *this,
static int mmap_read(struct impl *this)
{
struct port *port = &this->out_ports[0];
struct spa_v4l2_device *dev = &port->dev;
struct v4l2_buffer buf;
struct buffer *b;
struct spa_data *d;
@ -1154,7 +1154,7 @@ static int mmap_read(struct impl *this)
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = port->memtype;
if (xioctl(port->fd, VIDIOC_DQBUF, &buf) < 0)
if (xioctl(dev->fd, VIDIOC_DQBUF, &buf) < 0)
return -errno;
pts = SPA_TIMEVAL_TO_NSEC(&buf.timestamp);
@ -1212,6 +1212,7 @@ static void v4l2_on_fd_events(struct spa_source *source)
static int spa_v4l2_use_buffers(struct impl *this, struct spa_buffer **buffers, uint32_t n_buffers)
{
struct port *port = &this->out_ports[0];
struct spa_v4l2_device *dev = &port->dev;
struct v4l2_requestbuffers reqbuf;
int i;
struct spa_data *d;
@ -1235,7 +1236,7 @@ static int spa_v4l2_use_buffers(struct impl *this, struct spa_buffer **buffers,
reqbuf.memory = port->memtype;
reqbuf.count = n_buffers;
if (xioctl(port->fd, VIDIOC_REQBUFS, &reqbuf) < 0) {
if (xioctl(dev->fd, VIDIOC_REQBUFS, &reqbuf) < 0) {
spa_log_error(this->log, "v4l2: VIDIOC_REQBUFS %m");
return -errno;
}
@ -1308,6 +1309,7 @@ mmap_init(struct impl *this,
uint32_t *n_buffers)
{
struct port *port = &this->out_ports[0];
struct spa_v4l2_device *dev = &port->dev;
struct v4l2_requestbuffers reqbuf;
int i;
@ -1318,7 +1320,7 @@ mmap_init(struct impl *this,
reqbuf.memory = port->memtype;
reqbuf.count = *n_buffers;
if (xioctl(port->fd, VIDIOC_REQBUFS, &reqbuf) < 0) {
if (xioctl(dev->fd, VIDIOC_REQBUFS, &reqbuf) < 0) {
spa_log_error(this->log, "VIDIOC_REQBUFS: %m");
return -errno;
}
@ -1352,7 +1354,7 @@ mmap_init(struct impl *this,
b->v4l2_buffer.memory = port->memtype;
b->v4l2_buffer.index = i;
if (xioctl(port->fd, VIDIOC_QUERYBUF, &b->v4l2_buffer) < 0) {
if (xioctl(dev->fd, VIDIOC_QUERYBUF, &b->v4l2_buffer) < 0) {
spa_log_error(this->log, "VIDIOC_QUERYBUF: %m");
return -errno;
}
@ -1371,7 +1373,7 @@ mmap_init(struct impl *this,
expbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
expbuf.index = i;
expbuf.flags = O_CLOEXEC | O_RDONLY;
if (xioctl(port->fd, VIDIOC_EXPBUF, &expbuf) < 0) {
if (xioctl(dev->fd, VIDIOC_EXPBUF, &expbuf) < 0) {
spa_log_error(this->log, "VIDIOC_EXPBUF: %m");
continue;
}
@ -1385,7 +1387,7 @@ mmap_init(struct impl *this,
d[0].data = mmap(NULL,
b->v4l2_buffer.length,
PROT_READ, MAP_SHARED,
port->fd,
dev->fd,
b->v4l2_buffer.m.offset);
if (d[0].data == MAP_FAILED) {
spa_log_error(this->log, "mmap: %m");
@ -1420,15 +1422,16 @@ spa_v4l2_alloc_buffers(struct impl *this,
{
int res;
struct port *port = &this->out_ports[0];
struct spa_v4l2_device *dev = &port->dev;
if (port->n_buffers > 0)
return -EIO;
if (port->cap.capabilities & V4L2_CAP_STREAMING) {
if (dev->cap.capabilities & V4L2_CAP_STREAMING) {
if ((res = mmap_init(this, params, n_params, buffers, n_buffers)) < 0)
if ((res = userptr_init(this)) < 0)
return res;
} else if (port->cap.capabilities & V4L2_CAP_READWRITE) {
} else if (dev->cap.capabilities & V4L2_CAP_READWRITE) {
if ((res = read_init(this)) < 0)
return res;
} else
@ -1440,25 +1443,31 @@ spa_v4l2_alloc_buffers(struct impl *this,
static int spa_v4l2_stream_on(struct impl *this)
{
struct port *port = &this->out_ports[0];
struct spa_v4l2_device *dev = &port->dev;
enum v4l2_buf_type type;
if (!port->opened)
if (dev->fd == -1)
return -EIO;
if (port->started)
if (dev->active)
return 0;
spa_log_debug(this->log, "starting");
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (xioctl(port->fd, VIDIOC_STREAMON, &type) < 0) {
if (xioctl(dev->fd, VIDIOC_STREAMON, &type) < 0) {
spa_log_error(this->log, "VIDIOC_STREAMON: %m");
return -errno;
}
port->source.func = v4l2_on_fd_events;
port->source.data = this;
port->source.fd = dev->fd;
port->source.mask = SPA_IO_IN | SPA_IO_ERR;
port->source.rmask = 0;
spa_loop_add_source(this->data_loop, &port->source);
port->started = true;
dev->active = true;
return 0;
}
@ -1479,13 +1488,14 @@ static int do_remove_source(struct spa_loop *loop,
static int spa_v4l2_stream_off(struct impl *this)
{
struct port *port = &this->out_ports[0];
struct spa_v4l2_device *dev = &port->dev;
enum v4l2_buf_type type;
int i;
if (!port->opened)
if (dev->fd == -1)
return -EIO;
if (!port->started)
if (!dev->active)
return 0;
spa_log_debug(this->log, "stopping");
@ -1493,7 +1503,7 @@ static int spa_v4l2_stream_off(struct impl *this)
spa_loop_invoke(this->data_loop, do_remove_source, 0, NULL, 0, true, port);
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (xioctl(port->fd, VIDIOC_STREAMOFF, &type) < 0) {
if (xioctl(dev->fd, VIDIOC_STREAMOFF, &type) < 0) {
spa_log_error(this->log, "VIDIOC_STREAMOFF: %m");
return -errno;
}
@ -1502,12 +1512,12 @@ static int spa_v4l2_stream_off(struct impl *this)
b = &port->buffers[i];
if (!SPA_FLAG_CHECK(b->flags, BUFFER_FLAG_OUTSTANDING)) {
if (xioctl(port->fd, VIDIOC_QBUF, &b->v4l2_buffer) < 0)
if (xioctl(dev->fd, VIDIOC_QBUF, &b->v4l2_buffer) < 0)
spa_log_warn(this->log, "VIDIOC_QBUF: %s", strerror(errno));
}
}
spa_list_init(&port->queue);
port->started = false;
dev->active = false;
return 0;
}

View file

@ -28,6 +28,7 @@
extern const struct spa_handle_factory spa_v4l2_source_factory;
extern const struct spa_handle_factory spa_v4l2_monitor_factory;
extern const struct spa_handle_factory spa_v4l2_device_factory;
int
spa_handle_factory_enum(const struct spa_handle_factory **factory,
@ -43,6 +44,9 @@ spa_handle_factory_enum(const struct spa_handle_factory **factory,
case 1:
*factory = &spa_v4l2_monitor_factory;
break;
case 2:
*factory = &spa_v4l2_device_factory;
break;
default:
return 0;
}

36
spa/plugins/v4l2/v4l2.h Normal file
View file

@ -0,0 +1,36 @@
/* Spa V4l2 support
*
* Copyright © 2018 Wim Taymans
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include <errno.h>
struct spa_v4l2_device {
struct spa_log *log;
int fd;
struct v4l2_capability cap;
bool active;
};
int spa_v4l2_open(struct spa_v4l2_device *dev, const char *path);
int spa_v4l2_close(struct spa_v4l2_device *dev);
int spa_v4l2_is_capture(struct spa_v4l2_device *dev);

View file

@ -31,14 +31,11 @@ extern "C" {
#include <spa/utils/defs.h>
#include <spa/param/param.h>
#include <spa/node/node.h>
#include <pipewire/proxy.h>
struct pw_client_node_proxy;
#define PW_TYPE_INTERFACE__ClientNode PW_TYPE_INTERFACE_BASE "ClientNode"
#define PW_VERSION_CLIENT_NODE 0
/** information about a buffer */

View file

@ -33,8 +33,7 @@ extern "C" {
#include <spa/param/param.h>
#include <spa/node/node.h>
#define PW_TYPE_PROTOCOL__Native PW_TYPE_PROTOCOL_BASE "Native"
#define PW_TYPE_PROTOCOL_NATIVE_BASE PW_TYPE_PROTOCOL__Native ":"
#define PW_TYPE_PROTOCOL__Native PW_TYPE_PROTOCOL_BASE "Native"
struct pw_protocol_native_demarshal {
int (*func) (void *object, void *data, size_t size);

View file

@ -593,6 +593,160 @@ static int module_demarshal_info(void *object, void *data, size_t size)
return 0;
}
static void device_marshal_info(void *object, struct pw_device_info *info)
{
struct pw_resource *resource = object;
struct spa_pod_builder *b;
uint32_t i, n_items;
b = pw_protocol_native_begin_resource(resource, PW_DEVICE_PROXY_EVENT_INFO);
n_items = info->props ? info->props->n_items : 0;
spa_pod_builder_add(b,
"[",
"i", info->id,
"l", info->change_mask,
"i", n_items, NULL);
for (i = 0; i < n_items; i++) {
spa_pod_builder_add(b,
"s", info->props->items[i].key,
"s", info->props->items[i].value, NULL);
}
spa_pod_builder_add(b, "]", NULL);
pw_protocol_native_end_resource(resource, b);
}
static int device_demarshal_info(void *object, void *data, size_t size)
{
struct pw_proxy *proxy = object;
struct spa_pod_parser prs;
struct spa_dict props;
struct pw_device_info info;
uint32_t i;
spa_pod_parser_init(&prs, data, size, 0);
if (spa_pod_parser_get(&prs,
"["
"i", &info.id,
"l", &info.change_mask,
"i", &props.n_items, NULL) < 0)
return -EINVAL;
info.props = &props;
props.items = alloca(props.n_items * sizeof(struct spa_dict_item));
for (i = 0; i < props.n_items; i++) {
if (spa_pod_parser_get(&prs, "ss",
&props.items[i].key, &props.items[i].value, NULL) < 0)
return -EINVAL;
}
pw_proxy_notify(proxy, struct pw_device_proxy_events, info, 0, &info);
return 0;
}
static void device_marshal_param(void *object, uint32_t id, uint32_t index, uint32_t next,
const struct spa_pod *param)
{
struct pw_resource *resource = object;
struct spa_pod_builder *b;
b = pw_protocol_native_begin_resource(resource, PW_DEVICE_PROXY_EVENT_PARAM);
spa_pod_builder_add_struct(b, "I", id, "i", index, "i", next, "P", param);
pw_protocol_native_end_resource(resource, b);
}
static int device_demarshal_param(void *object, void *data, size_t size)
{
struct pw_proxy *proxy = object;
struct spa_pod_parser prs;
uint32_t id, index, next;
struct spa_pod *param;
spa_pod_parser_init(&prs, data, size, 0);
if (spa_pod_parser_get(&prs,
"[ I", &id,
"i", &index,
"i", &next,
"P", &param, NULL) < 0)
return -EINVAL;
pw_proxy_notify(proxy, struct pw_device_proxy_events, param, 0, id, index, next, param);
return 0;
}
static void device_marshal_enum_params(void *object, uint32_t id, uint32_t index, uint32_t num,
const struct spa_pod *filter)
{
struct pw_proxy *proxy = object;
struct spa_pod_builder *b;
b = pw_protocol_native_begin_proxy(proxy, PW_DEVICE_PROXY_METHOD_ENUM_PARAMS);
spa_pod_builder_add_struct(b,
"I", id,
"i", index,
"i", num,
"P", filter);
pw_protocol_native_end_proxy(proxy, b);
}
static int device_demarshal_enum_params(void *object, void *data, size_t size)
{
struct pw_resource *resource = object;
struct spa_pod_parser prs;
uint32_t id, index, num;
struct spa_pod *filter;
spa_pod_parser_init(&prs, data, size, 0);
if (spa_pod_parser_get(&prs,
"[ I", &id,
"i", &index,
"i", &num,
"P", &filter, NULL) < 0)
return -EINVAL;
pw_resource_do(resource, struct pw_device_proxy_methods, enum_params, 0, id, index, num, filter);
return 0;
}
static void device_marshal_set_param(void *object, uint32_t id, uint32_t flags,
const struct spa_pod *param)
{
struct pw_proxy *proxy = object;
struct spa_pod_builder *b;
b = pw_protocol_native_begin_proxy(proxy, PW_DEVICE_PROXY_METHOD_SET_PARAM);
spa_pod_builder_add_struct(b,
"I", id,
"i", flags,
"P", param);
pw_protocol_native_end_proxy(proxy, b);
}
static int device_demarshal_set_param(void *object, void *data, size_t size)
{
struct pw_resource *resource = object;
struct spa_pod_parser prs;
uint32_t id, flags;
struct spa_pod *param;
spa_pod_parser_init(&prs, data, size, 0);
if (spa_pod_parser_get(&prs,
"[ I", &id,
"i", &flags,
"P", &param, NULL) < 0)
return -EINVAL;
pw_resource_do(resource, struct pw_device_proxy_methods, set_param, 0, id, flags, param);
return 0;
}
static void factory_marshal_info(void *object, struct pw_factory_info *info)
{
struct pw_resource *resource = object;
@ -1436,6 +1590,39 @@ const struct pw_protocol_marshal pw_protocol_native_factory_marshal = {
PW_FACTORY_PROXY_EVENT_NUM,
};
static const struct pw_device_proxy_methods pw_protocol_native_device_method_marshal = {
PW_VERSION_DEVICE_PROXY_METHODS,
&device_marshal_enum_params,
&device_marshal_set_param,
};
static const struct pw_protocol_native_demarshal pw_protocol_native_device_method_demarshal[] = {
{ &device_demarshal_enum_params, 0, },
{ &device_demarshal_set_param, PW_PERM_W, },
};
static const struct pw_device_proxy_events pw_protocol_native_device_event_marshal = {
PW_VERSION_DEVICE_PROXY_EVENTS,
&device_marshal_info,
&device_marshal_param,
};
static const struct pw_protocol_native_demarshal pw_protocol_native_device_event_demarshal[] = {
{ &device_demarshal_info, 0, },
{ &device_demarshal_param, 0, }
};
static const struct pw_protocol_marshal pw_protocol_native_device_marshal = {
PW_TYPE_INTERFACE_Device,
PW_VERSION_DEVICE,
&pw_protocol_native_device_method_marshal,
pw_protocol_native_device_method_demarshal,
PW_DEVICE_PROXY_METHOD_NUM,
&pw_protocol_native_device_event_marshal,
pw_protocol_native_device_event_demarshal,
PW_DEVICE_PROXY_EVENT_NUM,
};
static const struct pw_node_proxy_methods pw_protocol_native_node_method_marshal = {
PW_VERSION_NODE_PROXY_METHODS,
&node_marshal_enum_params,
@ -1561,6 +1748,7 @@ void pw_protocol_native_init(struct pw_protocol *protocol)
pw_protocol_add_marshal(protocol, &pw_protocol_native_core_marshal);
pw_protocol_add_marshal(protocol, &pw_protocol_native_registry_marshal);
pw_protocol_add_marshal(protocol, &pw_protocol_native_module_marshal);
pw_protocol_add_marshal(protocol, &pw_protocol_native_device_marshal);
pw_protocol_add_marshal(protocol, &pw_protocol_native_node_marshal);
pw_protocol_add_marshal(protocol, &pw_protocol_native_port_marshal);
pw_protocol_add_marshal(protocol, &pw_protocol_native_factory_marshal);

View file

@ -4,7 +4,7 @@ pipewire_module_spa_c_args = [
]
pipewire_module_spa_monitor = shared_library('pipewire-module-spa-monitor',
[ 'module-monitor.c', 'spa-monitor.c', 'spa-node.c' ],
[ 'module-monitor.c', 'spa-monitor.c', 'spa-node.c', 'spa-device.c' ],
c_args : pipewire_module_spa_c_args,
include_directories : [configinc, spa_inc],
install : true,
@ -21,6 +21,15 @@ pipewire_module_spa_node = shared_library('pipewire-module-spa-node',
dependencies : [mathlib, dl_lib, pipewire_dep],
)
pipewire_module_spa_device = shared_library('pipewire-module-spa-device',
[ 'module-device.c', 'spa-device.c' ],
c_args : pipewire_module_spa_c_args,
include_directories : [configinc, spa_inc],
install : true,
install_dir : modules_install_dir,
dependencies : [mathlib, dl_lib, pipewire_dep],
)
pipewire_module_spa_node_factory = shared_library('pipewire-module-spa-node-factory',
[ 'module-node-factory.c', 'spa-node.c' ],
c_args : pipewire_module_spa_c_args,

View file

@ -0,0 +1,117 @@
/* PipeWire
* Copyright © 2018 Wim Taymans
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <errno.h>
#include <getopt.h>
#include <limits.h>
#include <pipewire/core.h>
#include <pipewire/log.h>
#include <pipewire/module.h>
#include <pipewire/utils.h>
#include "spa-monitor.h"
#include "spa-device.h"
static const struct spa_dict_item module_props[] = {
{ PW_MODULE_PROP_AUTHOR, "Wim Taymans <wim.taymans@gmail.com>" },
{ PW_MODULE_PROP_DESCRIPTION, "Load and manage an SPA device" },
{ PW_MODULE_PROP_VERSION, PACKAGE_VERSION },
};
struct device_data {
struct pw_device *this;
struct pw_core *core;
struct pw_properties *properties;
struct spa_hook module_listener;
};
static void module_destroy(void *_data)
{
struct device_data *data = _data;
pw_device_destroy(data->this);
}
static const struct pw_module_events module_events = {
PW_VERSION_MODULE_EVENTS,
.destroy = module_destroy,
};
int pipewire__module_init(struct pw_module *module, const char *args)
{
struct pw_properties *props = NULL;
char **argv;
int n_tokens;
struct pw_core *core = pw_module_get_core(module);
struct pw_device *device;
struct device_data *data;
if (args == NULL)
goto wrong_arguments;
argv = pw_split_strv(args, " \t", 4, &n_tokens);
if (n_tokens < 3)
goto not_enough_arguments;
if (n_tokens == 4) {
props = pw_properties_new_string(argv[3]);
if (props == NULL)
return -ENOMEM;
}
device = pw_spa_device_load(core,
NULL,
pw_module_get_global(module),
argv[0], argv[1], argv[2],
0,
props,
sizeof(struct device_data));
pw_free_strv(argv);
if (device == NULL)
return -ENOMEM;
data = pw_spa_device_get_user_data(device);
data->this = device;
data->core = core;
data->properties = props;
pw_log_debug("module %p: new", module);
pw_module_add_listener(module, &data->module_listener, &module_events, data);
pw_module_update_properties(module, &SPA_DICT_INIT_ARRAY(module_props));
return 0;
not_enough_arguments:
pw_free_strv(argv);
wrong_arguments:
pw_log_error("usage: module-spa-device <plugin> <factory> <name> [key=value ...]");
return -EINVAL;
}

View file

@ -0,0 +1,164 @@
/* PipeWire
*
* Copyright © 2018 Wim Taymans
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <string.h>
#include <stdio.h>
#include <dlfcn.h>
#include <spa/param/props.h>
#include <spa/pod/iter.h>
#include <spa/debug/types.h>
#include "spa-device.h"
#include "pipewire/device.h"
#include "pipewire/port.h"
#include "pipewire/log.h"
#include "pipewire/private.h"
struct impl {
struct pw_device *this;
struct pw_client *owner;
struct pw_global *parent;
enum pw_spa_device_flags flags;
void *unload;
struct spa_handle *handle;
struct spa_device *device;
char *lib;
char *factory_name;
struct spa_hook device_listener;
void *user_data;
};
static void device_destroy(void *data)
{
struct impl *impl = data;
struct pw_device *device = impl->this;
pw_log_debug("spa-device %p: free", device);
spa_hook_remove(&impl->device_listener);
if (impl->unload)
pw_unload_spa_interface(impl->unload);
if (impl->handle) {
spa_handle_clear(impl->handle);
free(impl->handle);
}
free(impl->lib);
free(impl->factory_name);
}
static const struct pw_device_events device_events = {
PW_VERSION_DEVICE_EVENTS,
.destroy = device_destroy,
};
struct pw_device *
pw_spa_device_new(struct pw_core *core,
struct pw_client *owner,
struct pw_global *parent,
const char *name,
enum pw_spa_device_flags flags,
struct spa_device *device,
struct spa_handle *handle,
struct pw_properties *properties,
size_t user_data_size)
{
struct pw_device *this;
struct impl *impl;
this = pw_device_new(core, name, properties, sizeof(struct impl) + user_data_size);
if (this == NULL)
return NULL;
impl = this->user_data;
impl->this = this;
impl->owner = owner;
impl->parent = parent;
impl->device = device;
impl->flags = flags;
if (user_data_size > 0)
impl->user_data = SPA_MEMBER(impl, sizeof(struct impl), void);
pw_device_add_listener(this, &impl->device_listener, &device_events, impl);
if (!SPA_FLAG_CHECK(impl->flags, PW_SPA_DEVICE_FLAG_NO_REGISTER))
pw_device_register(this, impl->owner, impl->parent, NULL);
pw_device_set_implementation(this, impl->device);
return this;
}
void *pw_spa_device_get_user_data(struct pw_device *device)
{
struct impl *impl = device->user_data;
return impl->user_data;
}
struct pw_device *pw_spa_device_load(struct pw_core *core,
struct pw_client *owner,
struct pw_global *parent,
const char *lib,
const char *factory_name,
const char *name,
enum pw_spa_device_flags flags,
struct pw_properties *properties,
size_t user_data_size)
{
struct pw_device *this;
struct impl *impl;
struct spa_device *device;
const struct spa_support *support;
uint32_t n_support;
support = pw_core_get_support(core, &n_support);
device = pw_load_spa_interface(lib, factory_name, SPA_TYPE_INTERFACE_Device,
properties ? &properties->dict : NULL, n_support, support);
if (device == NULL)
goto open_failed;
this = pw_spa_device_new(core, owner, parent, name, flags,
device, NULL, properties, user_data_size);
impl = this->user_data;
impl->unload = device;
impl->lib = strdup(lib);
impl->factory_name = strdup(factory_name);
return this;
open_failed:
return NULL;
}

View file

@ -0,0 +1,70 @@
/* PipeWire
*
* Copyright © 2018 Wim Taymans
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#ifndef __PIPEWIRE_SPA_DEVICE_H__
#define __PIPEWIRE_SPA_DEVICE_H__
#include <spa/monitor/device.h>
#include <pipewire/core.h>
#include <pipewire/device.h>
#ifdef __cplusplus
extern "C" {
#endif
enum pw_spa_device_flags {
PW_SPA_DEVICE_FLAG_DISABLE = (1 << 0),
PW_SPA_DEVICE_FLAG_NO_REGISTER = (1 << 1),
};
struct pw_device *
pw_spa_device_new(struct pw_core *core,
struct pw_client *owner, /**< optional owner */
struct pw_global *parent, /**< optional parent */
const char *name,
enum pw_spa_device_flags flags,
struct spa_device *device,
struct spa_handle *handle,
struct pw_properties *properties,
size_t user_data_size);
struct pw_device *
pw_spa_device_load(struct pw_core *core,
struct pw_client *owner, /**< optional owner */
struct pw_global *parent, /**< optional parent */
const char *lib,
const char *factory_name,
const char *name,
enum pw_spa_device_flags flags,
struct pw_properties *properties,
size_t user_data_size);
void *pw_spa_device_get_user_data(struct pw_device *device);
#ifdef __cplusplus
}
#endif
#endif /* __PIPEWIRE_SPA_DEVICE_H__ */

View file

@ -41,15 +41,19 @@
#include <pipewire/log.h>
#include <pipewire/type.h>
#include <pipewire/node.h>
#include <pipewire/device.h>
#include "spa-monitor.h"
#include "spa-node.h"
#include "spa-device.h"
struct monitor_item {
char *id;
struct spa_list link;
struct pw_node *node;
struct spa_handle *handle;
uint32_t type;
void *iface;
void *object;
};
struct impl {
@ -70,7 +74,7 @@ static struct monitor_item *add_item(struct pw_spa_monitor *this,
int res;
struct spa_handle *handle;
struct monitor_item *mitem;
void *node_iface;
void *iface;
struct pw_properties *props = NULL;
const char *name, *id, *klass, *str;
struct spa_handle_factory *factory;
@ -78,7 +82,7 @@ static struct monitor_item *add_item(struct pw_spa_monitor *this,
struct spa_pod *info = NULL;
const struct spa_support *support;
enum pw_spa_node_flags flags;
uint32_t n_support;
uint32_t n_support, type;
if (spa_pod_object_parse(item,
":", SPA_MONITOR_ITEM_id, "s", &id,
@ -86,6 +90,7 @@ static struct monitor_item *add_item(struct pw_spa_monitor *this,
":", SPA_MONITOR_ITEM_name, "s", &name,
":", SPA_MONITOR_ITEM_class, "s", &klass,
":", SPA_MONITOR_ITEM_factory, "p", &factory,
":", SPA_MONITOR_ITEM_type, "I", &type,
":", SPA_MONITOR_ITEM_info, "T", &info, NULL) < 0) {
pw_log_warn("monitor %p: could not parse item", this);
spa_debug_pod(0, NULL, item);
@ -128,21 +133,34 @@ static struct monitor_item *add_item(struct pw_spa_monitor *this,
pw_log_error("can't make factory instance: %d", res);
return NULL;
}
if ((res = spa_handle_get_interface(handle, SPA_TYPE_INTERFACE_Node, &node_iface)) < 0) {
if ((res = spa_handle_get_interface(handle, type, &iface)) < 0) {
pw_log_error("can't get NODE interface: %d", res);
pw_properties_free(props);
return NULL;
}
flags = PW_SPA_NODE_FLAG_ACTIVATE;
flags |= (state == SPA_MONITOR_ITEM_STATE_Available) ? 0 : PW_SPA_NODE_FLAG_DISABLE;
mitem = calloc(1, sizeof(struct monitor_item));
mitem->id = strdup(id);
mitem->handle = handle;
mitem->node = pw_spa_node_new(impl->core, NULL, impl->parent, name,
mitem->type = type;
switch (type) {
case SPA_TYPE_INTERFACE_Node:
flags = PW_SPA_NODE_FLAG_ACTIVATE;
flags |= (state == SPA_MONITOR_ITEM_STATE_Available) ? 0 : PW_SPA_NODE_FLAG_DISABLE;
mitem->object = pw_spa_node_new(impl->core, NULL, impl->parent, name,
flags,
node_iface, handle, props, 0);
iface, handle, props, 0);
break;
case SPA_TYPE_INTERFACE_Device:
mitem->object = pw_spa_device_new(impl->core, NULL, impl->parent, name,
0, iface, handle, props, 0);
break;
default:
return NULL;
}
spa_list_append(&impl->item_list, &mitem->link);
@ -164,7 +182,16 @@ static struct monitor_item *find_item(struct pw_spa_monitor *this, const char *i
void destroy_item(struct monitor_item *mitem)
{
pw_node_destroy(mitem->node);
switch (mitem->type) {
case SPA_TYPE_INTERFACE_Node:
pw_node_destroy(mitem->object);
break;
case SPA_TYPE_INTERFACE_Device:
pw_device_destroy(mitem->object);
break;
default:
break;
}
spa_list_remove(&mitem->link);
spa_handle_clear(mitem->handle);
free(mitem->handle);
@ -209,11 +236,11 @@ static void change_item(struct pw_spa_monitor *this, struct spa_pod *item, uint6
switch (state) {
case SPA_MONITOR_ITEM_STATE_Available:
pw_node_set_enabled(mitem->node, true);
pw_node_set_enabled(mitem->object, true);
break;
case SPA_MONITOR_ITEM_STATE_Disabled:
case SPA_MONITOR_ITEM_STATE_Unavailable:
pw_node_set_enabled(mitem->node, false);
pw_node_set_enabled(mitem->object, false);
break;
default:
break;

View file

@ -56,9 +56,6 @@ struct pw_client;
#include <pipewire/resource.h>
#include <pipewire/permission.h>
#define PW_TYPE__Client PW_TYPE_OBJECT_BASE "Client"
#define PW_TYPE_CLIENT_BASE PW_TYPE__Client ":"
/** \page page_client Client
*
* \section sec_page_client_overview Overview

View file

@ -29,9 +29,6 @@
extern "C" {
#endif
#define PW_TYPE__Control "PipeWire:Object:Control"
#define PW_TYPE_CONTROL_BASE PW_TYPE__Control ":"
#include <spa/utils/hook.h>
/** \page page_control Control

View file

@ -38,6 +38,7 @@
#include <pipewire/protocol.h>
#include <pipewire/core.h>
#include <pipewire/data-loop.h>
#include <pipewire/device.h>
/** \cond */
struct impl {
@ -441,6 +442,7 @@ struct pw_core *pw_core_new(struct pw_loop *main_loop, struct pw_properties *pro
spa_list_init(&this->registry_resource_list);
spa_list_init(&this->global_list);
spa_list_init(&this->module_list);
spa_list_init(&this->device_list);
spa_list_init(&this->client_list);
spa_list_init(&this->node_list);
spa_list_init(&this->factory_list);
@ -503,6 +505,7 @@ void pw_core_destroy(struct pw_core *core)
struct impl *impl = SPA_CONTAINER_OF(core, struct impl, this);
struct pw_global *global;
struct pw_module *module;
struct pw_device *device;
struct pw_remote *remote;
struct pw_node *node;
@ -517,6 +520,9 @@ void pw_core_destroy(struct pw_core *core)
spa_list_consume(module, &core->module_list, link)
pw_module_destroy(module);
spa_list_consume(device, &core->device_list, link)
pw_device_destroy(device);
spa_list_consume(node, &core->node_list, link)
pw_node_destroy(node);

305
src/pipewire/device.c Normal file
View file

@ -0,0 +1,305 @@
/* PipeWire
*
* Copyright © 2018 Wim Taymans
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include <errno.h>
#include <spa/debug/types.h>
#include "pipewire/pipewire.h"
#include "pipewire/device.h"
#include "pipewire/private.h"
struct resource_data {
struct spa_hook resource_listener;
};
struct node_data {
struct spa_list link;
struct pw_node *node;
struct spa_handle *handle;
uint32_t id;
};
struct pw_device *pw_device_new(struct pw_core *core,
const char *name,
struct pw_properties *properties,
size_t user_data_size)
{
struct pw_device *this;
this = calloc(1, sizeof(*this) + user_data_size);
this->core = core;
this->properties = properties;
this->info.name = strdup(name);
this->info.props = properties ? &properties->dict : NULL;
spa_hook_list_init(&this->listener_list);
spa_list_init(&this->node_list);
if (user_data_size > 0)
this->user_data = SPA_MEMBER(this, sizeof(*this), void);
pw_log_debug("device %p: new %s", this, name);
return this;
}
void pw_device_destroy(struct pw_device *device)
{
pw_log_debug("device %p: destroy", device);
pw_device_events_destroy(device);
if (device->registered)
spa_list_remove(&device->link);
if (device->global) {
spa_hook_remove(&device->global_listener);
pw_global_destroy(device->global);
}
if (device->info.name)
free((char *)device->info.name);
if (device->properties)
pw_properties_free(device->properties);
free(device);
}
static void device_unbind_func(void *data)
{
struct pw_resource *resource = data;
spa_list_remove(&resource->link);
}
static const struct pw_resource_events resource_events = {
PW_VERSION_RESOURCE_EVENTS,
.destroy = device_unbind_func,
};
static void
global_bind(void *_data, struct pw_client *client, uint32_t permissions,
uint32_t version, uint32_t id)
{
struct pw_device *this = _data;
struct pw_global *global = this->global;
struct pw_resource *resource;
struct resource_data *data;
resource = pw_resource_new(client, id, permissions, global->type, version, sizeof(*data));
if (resource == NULL)
goto no_mem;
data = pw_resource_get_user_data(resource);
pw_resource_add_listener(resource, &data->resource_listener, &resource_events, resource);
pw_log_debug("device %p: bound to %d", this, resource->id);
spa_list_append(&global->resource_list, &resource->link);
this->info.change_mask = ~0;
pw_device_resource_info(resource, &this->info);
this->info.change_mask = 0;
return;
no_mem:
pw_log_error("can't create device resource");
pw_core_resource_error(client->core_resource, id, -ENOMEM, "no memory");
return;
}
static void global_destroy(void *object)
{
struct pw_device *device = object;
spa_hook_remove(&device->global_listener);
device->global = NULL;
pw_device_destroy(device);
}
static const struct pw_global_events global_events = {
PW_VERSION_GLOBAL_EVENTS,
.destroy = global_destroy,
.bind = global_bind,
};
int pw_device_register(struct pw_device *device,
struct pw_client *owner,
struct pw_global *parent,
struct pw_properties *properties)
{
struct pw_core *core = device->core;
if (properties == NULL)
properties = pw_properties_new(NULL, NULL);
if (properties == NULL)
return -ENOMEM;
pw_properties_set(properties, "device.name", device->info.name);
spa_list_append(&core->device_list, &device->link);
device->registered = true;
device->global = pw_global_new(core,
PW_TYPE_INTERFACE_Device, PW_VERSION_DEVICE,
properties,
device);
if (device->global == NULL)
return -ENOMEM;
pw_global_add_listener(device->global, &device->global_listener, &global_events, device);
pw_global_register(device->global, owner, parent);
device->info.id = device->global->id;
return 0;
}
static void device_add(void *data, uint32_t id,
const struct spa_handle_factory *factory, uint32_t type,
const struct spa_dict *info)
{
struct pw_device *device = data;
const struct spa_support *support;
uint32_t n_support;
struct pw_node *node;
struct node_data *nd;
int res;
void *iface;
if (type != SPA_TYPE_INTERFACE_Node) {
pw_log_warn("device %p: unknown type %d", device, type);
return;
}
support = pw_core_get_support(device->core, &n_support);
node = pw_node_new(device->core,
device->info.name,
pw_properties_copy(device->properties),
sizeof(struct node_data) +
spa_handle_factory_get_size(factory, info));
nd = pw_node_get_user_data(node);
nd->id = id;
nd->node = node;
nd->handle = SPA_MEMBER(nd, sizeof(struct node_data), void);
spa_list_append(&device->node_list, &nd->link);
if ((res = spa_handle_factory_init(factory,
nd->handle,
info,
support,
n_support)) < 0) {
pw_log_error("can't make factory instance: %d", res);
goto error;;
}
if ((res = spa_handle_get_interface(nd->handle, type, &iface)) < 0) {
pw_log_error("can't get NODE interface: %d", res);
goto error;;
}
pw_node_set_implementation(node, iface);
pw_node_register(node, NULL, device->global, NULL);
pw_node_set_active(node, true);
return;
error:
pw_node_destroy(node);
return;
}
static void device_remove(void *data, uint32_t id)
{
}
static const struct spa_device_callbacks device_callbacks = {
SPA_VERSION_DEVICE_CALLBACKS,
.add = device_add,
.remove = device_remove,
};
void pw_device_set_implementation(struct pw_device *device, struct spa_device *spa_device)
{
device->implementation = spa_device;
spa_device_set_callbacks(device->implementation, &device_callbacks, device);
if (spa_device && spa_device->info)
pw_device_update_properties(device, spa_device->info);
}
struct spa_device *pw_device_get_implementation(struct pw_device *device)
{
return device->implementation;
}
const struct pw_properties *pw_device_get_properties(struct pw_device *device)
{
return device->properties;
}
int pw_device_update_properties(struct pw_device *device, const struct spa_dict *dict)
{
struct pw_resource *resource;
uint32_t i, changed = 0;
for (i = 0; i < dict->n_items; i++)
changed += pw_properties_set(device->properties, dict->items[i].key, dict->items[i].value);
pw_log_debug("device %p: updated %d properties", device, changed);
if (!changed)
return 0;
device->info.props = &device->properties->dict;
device->info.change_mask |= PW_DEVICE_CHANGE_MASK_PROPS;
pw_device_events_info_changed(device, &device->info);
if (device->global)
spa_list_for_each(resource, &device->global->resource_list, link)
pw_device_resource_info(resource, &device->info);
device->info.change_mask = 0;
return changed;
}
void *pw_device_get_user_data(struct pw_device *device)
{
return device->user_data;
}
struct pw_global *pw_device_get_global(struct pw_device *device)
{
return device->global;
}
void pw_device_add_listener(struct pw_device *device,
struct spa_hook *listener,
const struct pw_device_events *events,
void *data)
{
spa_hook_list_append(&device->listener_list, listener, events, data);
}

100
src/pipewire/device.h Normal file
View file

@ -0,0 +1,100 @@
/* PipeWire
*
* Copyright © 2018 Wim Taymans
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#ifndef __PIPEWIRE_DEVICE_H__
#define __PIPEWIRE_DEVICE_H__
#ifdef __cplusplus
extern "C" {
#endif
/** \class pw_device
*
* \brief PipeWire device interface.
*
* The device is an object that manages nodes. It typically
* corresponds to a physical hardware device but it does not
* have to be.
*
* The purpose of the device is to provide an interface to
* dynamically create/remove/configure the nodes it manages.
*/
struct pw_device;
#include <spa/monitor/device.h>
#include <pipewire/core.h>
#include <pipewire/global.h>
#include <pipewire/properties.h>
#include <pipewire/resource.h>
/** Device events, listen to them with \ref pw_device_add_listener */
struct pw_device_events {
#define PW_VERSION_DEVICE_EVENTS 0
uint32_t version;
/** the device is destroyed */
void (*destroy) (void *data);
/** the device info changed */
void (*info_changed) (void *data, struct pw_device_info *info);
};
struct pw_device *pw_device_new(struct pw_core *core,
const char *name,
struct pw_properties *properties,
size_t user_data_size);
int pw_device_register(struct pw_device *device,
struct pw_client *owner,
struct pw_global *parent,
struct pw_properties *properties);
void pw_device_destroy(struct pw_device *device);
void *pw_device_get_user_data(struct pw_device *device);
/** Set the device implementation */
void pw_device_set_implementation(struct pw_device *device, struct spa_device *spa_device);
/** Get the device implementation */
struct spa_device *pw_device_get_implementation(struct pw_device *device);
/** Get the global of this device */
struct pw_global *pw_device_get_global(struct pw_device *device);
/** Add an event listener */
void pw_device_add_listener(struct pw_device *device,
struct spa_hook *listener,
const struct pw_device_events *events,
void *data);
int pw_device_update_properties(struct pw_device *device, const struct spa_dict *dict);
const struct pw_properties *pw_device_get_properties(struct pw_device *device);
#ifdef __cplusplus
}
#endif
#endif /* __PIPEWIRE_DEVICE_H__ */

View file

@ -29,9 +29,6 @@
extern "C" {
#endif
#define PW_TYPE_INTERFACE__Factory PW_TYPE_INTERFACE_BASE "Factory"
#define PW_TYPE_FACTORY_BASE PW_TYPE_INTERFACE__Factory ":"
/** \class pw_factory
*
* \brief PipeWire factory interface.
@ -48,7 +45,7 @@ struct pw_factory;
/** Factory events, listen to them with \ref pw_factory_add_listener */
struct pw_factory_events {
#define PW_VERSION_FACRORY_EVENTS 0
#define PW_VERSION_FACTORY_EVENTS 0
uint32_t version;
/** the factory is destroyed */

View file

@ -31,7 +31,6 @@ extern "C" {
#include <spa/utils/defs.h>
#include <spa/param/param.h>
#include <spa/node/node.h>
#include <pipewire/introspect.h>
#include <pipewire/proxy.h>
@ -40,6 +39,7 @@ extern "C" {
struct pw_core_proxy;
struct pw_registry_proxy;
struct pw_module_proxy;
struct pw_device_proxy;
struct pw_node_proxy;
struct pw_port_proxy;
struct pw_factory_proxy;
@ -419,6 +419,14 @@ pw_registry_proxy_add_listener(struct pw_registry_proxy *registry,
#define PW_VERSION_MODULE 0
#define PW_MODULE_PROXY_METHOD_NUM 0
/** Module methods */
struct pw_module_proxy_methods {
#define PW_VERSION_MODULE_PROXY_METHODS 0
uint32_t version;
};
#define PW_MODULE_PROXY_EVENT_INFO 0
#define PW_MODULE_PROXY_EVENT_NUM 1
@ -445,24 +453,57 @@ pw_module_proxy_add_listener(struct pw_module_proxy *module,
#define pw_module_resource_info(r,...) pw_resource_notify(r,struct pw_module_proxy_events,info,__VA_ARGS__)
#define PW_VERSION_NODE 0
#define PW_NODE_PROXY_EVENT_INFO 0
#define PW_NODE_PROXY_EVENT_PARAM 1
#define PW_NODE_PROXY_EVENT_NUM 2
#define PW_VERSION_DEVICE 0
/** Node events */
struct pw_node_proxy_events {
#define PW_VERSION_NODE_PROXY_EVENTS 0
#define PW_DEVICE_PROXY_METHOD_ENUM_PARAMS 0
#define PW_DEVICE_PROXY_METHOD_SET_PARAM 1
#define PW_DEVICE_PROXY_METHOD_NUM 2
/** Device methods */
struct pw_device_proxy_methods {
#define PW_VERSION_DEVICE_PROXY_METHODS 0
uint32_t version;
/**
* Notify node info
* Enumerate device parameters
*
* \param info info about the node
* Start enumeration of device parameters. For each param, a
* param event will be emited.
*
* \param id the parameter id to enum or SPA_ID_INVALID for all
* \param start the start index or 0 for the first param
* \param num the maximum number of params to retrieve
* \param filter a param filter or NULL
*/
void (*info) (void *object, struct pw_node_info *info);
void (*enum_params) (void *object, uint32_t id, uint32_t start, uint32_t num,
const struct spa_pod *filter);
/**
* Notify a node param
* Set a parameter on the device
*
* \param id the parameter id to set
* \param flags extra parameter flags
* \param param the parameter to set
*/
void (*set_param) (void *object, uint32_t id, uint32_t flags,
const struct spa_pod *param);
};
#define PW_DEVICE_PROXY_EVENT_INFO 0
#define PW_DEVICE_PROXY_EVENT_PARAM 1
#define PW_DEVICE_PROXY_EVENT_NUM 2
/** Device events */
struct pw_device_proxy_events {
#define PW_VERSION_DEVICE_PROXY_EVENTS 0
uint32_t version;
/**
* Notify device info
*
* \param info info about the device
*/
void (*info) (void *object, struct pw_device_info *info);
/**
* Notify a device param
*
* Event emited as a result of the enum_params method.
*
@ -477,16 +518,18 @@ struct pw_node_proxy_events {
};
static inline void
pw_node_proxy_add_listener(struct pw_node_proxy *node,
struct spa_hook *listener,
const struct pw_node_proxy_events *events,
void *data)
pw_device_proxy_add_listener(struct pw_device_proxy *device,
struct spa_hook *listener,
const struct pw_device_proxy_events *events,
void *data)
{
pw_proxy_add_proxy_listener((struct pw_proxy*)node, listener, events, data);
pw_proxy_add_proxy_listener((struct pw_proxy*)device, listener, events, data);
}
#define pw_node_resource_info(r,...) pw_resource_notify(r,struct pw_node_proxy_events,info,__VA_ARGS__)
#define pw_node_resource_param(r,...) pw_resource_notify(r,struct pw_node_proxy_events,param,__VA_ARGS__)
#define pw_device_resource_info(r,...) pw_resource_notify(r,struct pw_device_proxy_events,info,__VA_ARGS__)
#define pw_device_resource_param(r,...) pw_resource_notify(r,struct pw_device_proxy_events,param,__VA_ARGS__)
#define PW_VERSION_NODE 0
#define PW_NODE_PROXY_METHOD_ENUM_PARAMS 0
#define PW_NODE_PROXY_METHOD_SET_PARAM 1
@ -553,8 +596,81 @@ pw_node_proxy_send_command(struct pw_node_proxy *node, const struct spa_command
command);
}
#define PW_NODE_PROXY_EVENT_INFO 0
#define PW_NODE_PROXY_EVENT_PARAM 1
#define PW_NODE_PROXY_EVENT_NUM 2
/** Node events */
struct pw_node_proxy_events {
#define PW_VERSION_NODE_PROXY_EVENTS 0
uint32_t version;
/**
* Notify node info
*
* \param info info about the node
*/
void (*info) (void *object, struct pw_node_info *info);
/**
* Notify a node param
*
* Event emited as a result of the enum_params method.
*
* \param id the param id
* \param index the param index
* \param next the param index of the next param
* \param param the parameter
*/
void (*param) (void *object,
uint32_t id, uint32_t index, uint32_t next,
const struct spa_pod *param);
};
static inline void
pw_node_proxy_add_listener(struct pw_node_proxy *node,
struct spa_hook *listener,
const struct pw_node_proxy_events *events,
void *data)
{
pw_proxy_add_proxy_listener((struct pw_proxy*)node, listener, events, data);
}
#define pw_node_resource_info(r,...) pw_resource_notify(r,struct pw_node_proxy_events,info,__VA_ARGS__)
#define pw_node_resource_param(r,...) pw_resource_notify(r,struct pw_node_proxy_events,param,__VA_ARGS__)
#define PW_VERSION_PORT 0
#define PW_PORT_PROXY_METHOD_ENUM_PARAMS 0
#define PW_PORT_PROXY_METHOD_NUM 1
/** Port methods */
struct pw_port_proxy_methods {
#define PW_VERSION_PORT_PROXY_METHODS 0
uint32_t version;
/**
* Enumerate port parameters
*
* Start enumeration of port parameters. For each param, a
* param event will be emited.
*
* \param id the parameter id to enumerate
* \param start the start index or 0 for the first param
* \param num the maximum number of params to retrieve
* \param filter a param filter or NULL
*/
void (*enum_params) (void *object, uint32_t id, uint32_t start, uint32_t num,
const struct spa_pod *filter);
};
/** Port params */
static inline void
pw_port_proxy_enum_params(struct pw_port_proxy *port, uint32_t id, uint32_t index,
uint32_t num, const struct spa_pod *filter)
{
pw_proxy_do((struct pw_proxy*)port, struct pw_port_proxy_methods, enum_params,
id, index, num, filter);
}
#define PW_PORT_PROXY_EVENT_INFO 0
#define PW_PORT_PROXY_EVENT_PARAM 1
#define PW_PORT_PROXY_EVENT_NUM 2
@ -596,39 +712,16 @@ pw_port_proxy_add_listener(struct pw_port_proxy *port,
#define pw_port_resource_info(r,...) pw_resource_notify(r,struct pw_port_proxy_events,info,__VA_ARGS__)
#define pw_port_resource_param(r,...) pw_resource_notify(r,struct pw_port_proxy_events,param,__VA_ARGS__)
#define PW_PORT_PROXY_METHOD_ENUM_PARAMS 0
#define PW_PORT_PROXY_METHOD_NUM 1
/** Port methods */
struct pw_port_proxy_methods {
#define PW_VERSION_PORT_PROXY_METHODS 0
uint32_t version;
/**
* Enumerate port parameters
*
* Start enumeration of port parameters. For each param, a
* param event will be emited.
*
* \param id the parameter id to enumerate
* \param start the start index or 0 for the first param
* \param num the maximum number of params to retrieve
* \param filter a param filter or NULL
*/
void (*enum_params) (void *object, uint32_t id, uint32_t start, uint32_t num,
const struct spa_pod *filter);
};
/** Port params */
static inline void
pw_port_proxy_enum_params(struct pw_port_proxy *port, uint32_t id, uint32_t index,
uint32_t num, const struct spa_pod *filter)
{
pw_proxy_do((struct pw_proxy*)port, struct pw_port_proxy_methods, enum_params,
id, index, num, filter);
}
#define PW_VERSION_FACTORY 0
#define PW_FACTORY_PROXY_METHOD_NUM 0
/** Factory methods */
struct pw_factory_proxy_methods {
#define PW_VERSION_FACTORY_PROXY_METHODS 0
uint32_t version;
};
#define PW_FACTORY_PROXY_EVENT_INFO 0
#define PW_FACTORY_PROXY_EVENT_NUM 1
@ -658,49 +751,6 @@ pw_factory_proxy_add_listener(struct pw_factory_proxy *factory,
#define PW_VERSION_CLIENT 0
#define PW_CLIENT_PROXY_EVENT_INFO 0
#define PW_CLIENT_PROXY_EVENT_PERMISSIONS 1
#define PW_CLIENT_PROXY_EVENT_NUM 2
/** Client events */
struct pw_client_proxy_events {
#define PW_VERSION_CLIENT_PROXY_EVENTS 0
uint32_t version;
/**
* Notify client info
*
* \param info info about the client
*/
void (*info) (void *object, struct pw_client_info *info);
/**
* Notify a client permission
*
* Event emited as a result of the get_permissions method.
*
* \param default_permissions the default permissions
* \param index the index of the first permission entry
* \param n_permissions the number of permissions
* \param permissions the permissions
*/
void (*permissions) (void *object,
uint32_t index,
uint32_t n_permissions,
struct pw_permission *permissions);
};
/** Client */
static inline void
pw_client_proxy_add_listener(struct pw_client_proxy *client,
struct spa_hook *listener,
const struct pw_client_proxy_events *events,
void *data)
{
pw_proxy_add_proxy_listener((struct pw_proxy*)client, listener, events, data);
}
#define pw_client_resource_info(r,...) pw_resource_notify(r,struct pw_client_proxy_events,info,__VA_ARGS__)
#define pw_client_resource_permissions(r,...) pw_resource_notify(r,struct pw_client_proxy_events,permissions,__VA_ARGS__)
#define PW_CLIENT_PROXY_METHOD_ERROR 0
#define PW_CLIENT_PROXY_METHOD_GET_PERMISSIONS 1
#define PW_CLIENT_PROXY_METHOD_UPDATE_PERMISSIONS 2
@ -760,10 +810,61 @@ pw_client_proxy_update_permissions(struct pw_client_proxy *client, uint32_t n_pe
n_permissions, permissions);
}
#define PW_CLIENT_PROXY_EVENT_INFO 0
#define PW_CLIENT_PROXY_EVENT_PERMISSIONS 1
#define PW_CLIENT_PROXY_EVENT_NUM 2
/** Client events */
struct pw_client_proxy_events {
#define PW_VERSION_CLIENT_PROXY_EVENTS 0
uint32_t version;
/**
* Notify client info
*
* \param info info about the client
*/
void (*info) (void *object, struct pw_client_info *info);
/**
* Notify a client permission
*
* Event emited as a result of the get_permissions method.
*
* \param default_permissions the default permissions
* \param index the index of the first permission entry
* \param n_permissions the number of permissions
* \param permissions the permissions
*/
void (*permissions) (void *object,
uint32_t index,
uint32_t n_permissions,
struct pw_permission *permissions);
};
/** Client */
static inline void
pw_client_proxy_add_listener(struct pw_client_proxy *client,
struct spa_hook *listener,
const struct pw_client_proxy_events *events,
void *data)
{
pw_proxy_add_proxy_listener((struct pw_proxy*)client, listener, events, data);
}
#define pw_client_resource_info(r,...) pw_resource_notify(r,struct pw_client_proxy_events,info,__VA_ARGS__)
#define pw_client_resource_permissions(r,...) pw_resource_notify(r,struct pw_client_proxy_events,permissions,__VA_ARGS__)
#define PW_VERSION_LINK 0
#define PW_LINK_PROXY_METHOD_NUM 0
/** Link methods */
struct pw_link_proxy_methods {
#define PW_VERSION_LINK_PROXY_METHODS 0
uint32_t version;
};
#define PW_LINK_PROXY_EVENT_INFO 0
#define PW_LINK_PROXY_EVENT_NUM 1
#define PW_LINK_PROXY_EVENT_NUM 1
/** Link events */
struct pw_link_proxy_events {

View file

@ -353,6 +353,35 @@ void pw_module_info_free(struct pw_module_info *info)
}
struct pw_device_info *pw_device_info_update(struct pw_device_info *info,
const struct pw_device_info *update)
{
if (update == NULL)
return info;
if (info == NULL) {
info = calloc(1, sizeof(struct pw_device_info));
if (info == NULL)
return NULL;
}
info->id = update->id;
info->change_mask = update->change_mask;
if (update->change_mask & PW_CLIENT_CHANGE_MASK_PROPS) {
if (info->props)
pw_spa_dict_destroy(info->props);
info->props = pw_spa_dict_copy(update->props);
}
return info;
}
void pw_device_info_free(struct pw_device_info *info)
{
if (info->props)
pw_spa_dict_destroy(info->props);
free(info);
}
struct pw_client_info *pw_client_info_update(struct pw_client_info *info,
const struct pw_client_info *update)
{

View file

@ -126,6 +126,24 @@ pw_module_info_update(struct pw_module_info *info,
/** Free a \ref pw_module_info \memberof pw_introspect */
void pw_module_info_free(struct pw_module_info *info);
/** The device information. Extra information can be added in later versions \memberof pw_introspect */
struct pw_device_info {
uint32_t id; /**< id of the global */
const char *name; /**< name the device */
#define PW_DEVICE_CHANGE_MASK_PROPS (1 << 0)
uint64_t change_mask; /**< bitfield of changed fields since last call */
struct spa_dict *props; /**< extra properties */
};
/** Update and existing \ref pw_device_info with \a update \memberof pw_introspect */
struct pw_device_info *
pw_device_info_update(struct pw_device_info *info,
const struct pw_device_info *update);
/** Free a \ref pw_device_info \memberof pw_introspect */
void pw_device_info_free(struct pw_device_info *info);
/** The client information. Extra information can be added in later versions \memberof pw_introspect */
struct pw_client_info {
uint32_t id; /**< id of the global */
@ -190,7 +208,7 @@ pw_port_info_free(struct pw_port_info *info);
struct pw_factory_info {
uint32_t id; /**< id of the global */
const char *name; /**< name the factory */
uint32_t type; /**< type of the factory */
uint32_t type; /**< type of the objects created by this factory */
uint32_t version; /**< version of the objects */
#define PW_FACTORY_CHANGE_MASK_PROPS (1 << 0)
uint64_t change_mask; /**< bitfield of changed fields since last call */

View file

@ -39,9 +39,6 @@ struct pw_link;
#include <pipewire/introspect.h>
#include <pipewire/port.h>
#define PW_TYPE__Link PW_TYPE_OBJECT_BASE "Link"
#define PW_TYPE_LINK_BASE PW_TYPE__Link ":"
/** \page page_link Link
*
* \section page_link_overview Overview

View file

@ -5,6 +5,7 @@ pipewire_headers = [
'control.h',
'core.h',
'data-loop.h',
'device.h',
'global.h',
'interfaces.h',
'introspect.h',
@ -37,6 +38,7 @@ pipewire_sources = [
'control.c',
'core.c',
'data-loop.c',
'device.c',
'global.c',
'introspect.c',
'link.c',

View file

@ -34,9 +34,6 @@ extern "C" {
#include <pipewire/core.h>
#define PW_TYPE__Module PW_TYPE_OBJECT_BASE "Module"
#define PW_TYPE_MODULE_BASE PW_TYPE__Module ":"
#define PIPEWIRE_SYMBOL_MODULE_INIT "pipewire__module_init"
/** \class pw_module

View file

@ -29,9 +29,6 @@
extern "C" {
#endif
#define PW_TYPE__Node PW_TYPE_OBJECT_BASE "Node"
#define PW_TYPE_NODE_BASE PW_TYPE__Node ":"
/** \page page_node Node
*
* \section page_node_overview Overview

View file

@ -322,7 +322,7 @@ const struct spa_support *pw_get_support(uint32_t *n_support)
void *pw_load_spa_interface(const char *lib, const char *factory_name, uint32_t type,
const struct spa_dict *info,
uint32_t n_support,
struct spa_support support[])
const struct spa_support support[])
{
struct support *sup = &global_support;
struct spa_support extra_support[MAX_SUPPORT];
@ -610,6 +610,7 @@ static const struct spa_type_info type_info[] = {
{ PW_TYPE_INTERFACE_Client, PW_TYPE_INTERFACE_BASE "Client", SPA_TYPE_Pointer, },
{ PW_TYPE_INTERFACE_Module, PW_TYPE_INTERFACE_BASE "Module", SPA_TYPE_Pointer, },
{ PW_TYPE_INTERFACE_ClientNode, PW_TYPE_INTERFACE_BASE "ClientNode", SPA_TYPE_Pointer, },
{ PW_TYPE_INTERFACE_Device, PW_TYPE_INTERFACE_BASE "Device", SPA_TYPE_Pointer, },
{ SPA_ID_INVALID, "spa_types", SPA_ID_INVALID, spa_types },
{ 0, NULL, },
};

View file

@ -141,7 +141,7 @@ pw_get_support_interface(uint32_t type);
void *pw_load_spa_interface(const char *lib, const char *factory_name, uint32_t type,
const struct spa_dict *info,
uint32_t n_support,
struct spa_support support[]);
const struct spa_support support[]);
void *pw_load_spa_dbus_interface(struct pw_loop *loop);

View file

@ -29,9 +29,6 @@
extern "C" {
#endif
#define PW_TYPE__Port "PipeWire:Object:Port"
#define PW_TYPE_PORT_BASE PW_TYPE__Port ":"
#include <spa/utils/hook.h>
/** \page page_port Port

View file

@ -172,6 +172,7 @@ struct pw_core {
struct spa_list remote_list; /**< list of remote connections */
struct spa_list registry_resource_list; /**< list of registry resources */
struct spa_list module_list; /**< list of modules */
struct spa_list device_list; /**< list of devices */
struct spa_list global_list; /**< list of globals */
struct spa_list client_list; /**< list of clients */
struct spa_list node_list; /**< list of nodes */
@ -242,6 +243,28 @@ static inline void free_allocation(struct allocation *alloc)
alloc->n_buffers = 0;
}
#define pw_device_events_emit(o,m,v,...) spa_hook_list_call(&o->listener_list, struct pw_device_events, m, v, ##__VA_ARGS__)
#define pw_device_events_destroy(m) pw_device_events_emit(m, destroy, 0)
#define pw_device_events_info_changed(n,i) pw_device_events_emit(n, info_changed, 0, i)
struct pw_device {
struct pw_core *core; /**< the core object */
struct spa_list link; /**< link in the core device_list */
struct pw_global *global; /**< global object for this device */
struct spa_hook global_listener;
bool registered;
struct pw_properties *properties; /**< properties of the device */
struct pw_device_info info; /**< introspectable device info */
struct spa_device *implementation; /**< implementation */
struct spa_hook_list listener_list;
struct spa_list node_list;
void *user_data; /**< device user_data */
};
#define pw_module_events_emit(o,m,v,...) spa_hook_list_call(&o->listener_list, struct pw_module_events, m, v, ##__VA_ARGS__)
#define pw_module_events_destroy(m) pw_module_events_emit(m, destroy, 0)

View file

@ -29,9 +29,6 @@
extern "C" {
#endif
#define PW_TYPE__Resource "PipeWire:Object:Resource"
#define PW_TYPE_RESOURCE_BASE PW_TYPE__Resource ":"
#include <spa/utils/hook.h>
/** \page page_resource Resource

View file

@ -49,6 +49,7 @@ enum {
PW_TYPE_INTERFACE_Client,
PW_TYPE_INTERFACE_Module,
PW_TYPE_INTERFACE_ClientNode,
PW_TYPE_INTERFACE_Device,
};

View file

@ -456,6 +456,42 @@ static const struct pw_link_proxy_events link_events = {
.info = link_event_info
};
static void device_event_info(void *object, struct pw_device_info *info)
{
struct proxy_data *data = object;
bool print_all, print_mark;
print_all = true;
if (data->info == NULL) {
printf("added:\n");
print_mark = false;
}
else {
printf("changed:\n");
print_mark = true;
}
info = data->info = pw_device_info_update(data->info, info);
printf("\tid: %d\n", data->id);
printf("\tparent_id: %d\n", data->parent_id);
printf("\tpermissions: %c%c%c\n", data->permissions & PW_PERM_R ? 'r' : '-',
data->permissions & PW_PERM_W ? 'w' : '-',
data->permissions & PW_PERM_X ? 'x' : '-');
printf("\ttype: %s (version %d)\n",
spa_debug_type_find_name(pw_type_info(), data->type), data->version);
if (print_all) {
print_properties(info->props, MARK_CHANGE(0));
}
}
static const struct pw_device_proxy_events device_events = {
PW_VERSION_DEVICE_PROXY_EVENTS,
.info = device_event_info
};
static void
destroy_proxy (void *data)
{
@ -507,6 +543,11 @@ static void registry_event_global(void *data, uint32_t id, uint32_t parent_id,
client_version = PW_VERSION_MODULE;
destroy = (pw_destroy_t) pw_module_info_free;
break;
case PW_TYPE_INTERFACE_Device:
events = &device_events;
client_version = PW_VERSION_DEVICE;
destroy = (pw_destroy_t) pw_device_info_free;
break;
case PW_TYPE_INTERFACE_Factory:
events = &factory_events;
client_version = PW_VERSION_FACTORY;