PipeWire 音频设计与实现分析四——事件循环的设计与实现

PipeWire 的事件循环基于两个插件的 4 个接口实现,它们是 support.system 插件的 Spa:Pointer:Interface:System 接口,support.loop 插件的 Spa:Pointer:Interface:LoopSpa:Pointer:Interface:LoopControlSpa:Pointer:Interface:LoopUtils

PipeWire 的 SPA 系统功能,即 Spa:Pointer:Interface:System 接口,用 struct spa_system 对象描述,系统功能方法集用 struct spa_system_methods 对象描述,这两个类型定义 (位于 pipewire/spa/include/spa/support/system.h) 如下:

#define SPA_TYPE_INTERFACE_System	SPA_TYPE_INFO_INTERFACE_BASE "System"
#define SPA_TYPE_INTERFACE_DataSystem	SPA_TYPE_INFO_INTERFACE_BASE "DataSystem"

#define SPA_VERSION_SYSTEM		0
struct spa_system { struct spa_interface iface; };

/* IO events */
#define SPA_IO_IN	(1 << 0)
#define SPA_IO_OUT	(1 << 2)
#define SPA_IO_ERR	(1 << 3)
#define SPA_IO_HUP	(1 << 4)

/* flags */
#define SPA_FD_CLOEXEC			(1<<0)
#define SPA_FD_NONBLOCK			(1<<1)
#define SPA_FD_EVENT_SEMAPHORE		(1<<2)
#define SPA_FD_TIMER_ABSTIME		(1<<3)
#define SPA_FD_TIMER_CANCEL_ON_SET	(1<<4)

struct spa_poll_event {
	uint32_t events;
	void *data;
};

struct spa_system_methods {
#define SPA_VERSION_SYSTEM_METHODS	0
	uint32_t version;

	/* read/write/ioctl */
	ssize_t (*read) (void *object, int fd, void *buf, size_t count);
	ssize_t (*write) (void *object, int fd, const void *buf, size_t count);
	int (*ioctl) (void *object, int fd, unsigned long request, ...);
	int (*close) (void *object, int fd);

	/* clock */
	int (*clock_gettime) (void *object,
			int clockid, struct timespec *value);
	int (*clock_getres) (void *object,
			int clockid, struct timespec *res);

	/* poll */
	int (*pollfd_create) (void *object, int flags);
	int (*pollfd_add) (void *object, int pfd, int fd, uint32_t events, void *data);
	int (*pollfd_mod) (void *object, int pfd, int fd, uint32_t events, void *data);
	int (*pollfd_del) (void *object, int pfd, int fd);
	int (*pollfd_wait) (void *object, int pfd,
			struct spa_poll_event *ev, int n_ev, int timeout);

	/* timers */
	int (*timerfd_create) (void *object, int clockid, int flags);
	int (*timerfd_settime) (void *object,
			int fd, int flags,
			const struct itimerspec *new_value,
			struct itimerspec *old_value);
	int (*timerfd_gettime) (void *object,
			int fd, struct itimerspec *curr_value);
	int (*timerfd_read) (void *object, int fd, uint64_t *expirations);

	/* events */
	int (*eventfd_create) (void *object, int flags);
	int (*eventfd_write) (void *object, int fd, uint64_t count);
	int (*eventfd_read) (void *object, int fd, uint64_t *count);

	/* signals */
	int (*signalfd_create) (void *object, int signal, int flags);
	int (*signalfd_read) (void *object, int fd, int *signal);
};

struct spa_system 对象的 iface.cb.funcs 字段将指向 struct spa_system_methods 对象。PipeWire 提供了一些宏,由 struct spa_system 对象调用 struct spa_system_methods 的各个方法,这些宏的定义 (位于 pipewire/spa/include/spa/support/system.h) 如下:

#define spa_system_method_r(o,method,version,...)			\
({									\
	int _res = -ENOTSUP;						\
	struct spa_system *_o = o;					\
	spa_interface_call_res(&_o->iface,				\
			struct spa_system_methods, _res,		\
			method, version, ##__VA_ARGS__);		\
	_res;								\
})


#define spa_system_read(s,...)			spa_system_method_r(s,read,0,__VA_ARGS__)
#define spa_system_write(s,...)			spa_system_method_r(s,write,0,__VA_ARGS__)
#define spa_system_ioctl(s,...)			spa_system_method_r(s,ioctl,0,__VA_ARGS__)
#define spa_system_close(s,...)			spa_system_method_r(s,close,0,__VA_ARGS__)

#define spa_system_clock_gettime(s,...)		spa_system_method_r(s,clock_gettime,0,__VA_ARGS__)
#define spa_system_clock_getres(s,...)		spa_system_method_r(s,clock_getres,0,__VA_ARGS__)

#define spa_system_pollfd_create(s,...)		spa_system_method_r(s,pollfd_create,0,__VA_ARGS__)
#define spa_system_pollfd_add(s,...)		spa_system_method_r(s,pollfd_add,0,__VA_ARGS__)
#define spa_system_pollfd_mod(s,...)		spa_system_method_r(s,pollfd_mod,0,__VA_ARGS__)
#define spa_system_pollfd_del(s,...)		spa_system_method_r(s,pollfd_del,0,__VA_ARGS__)
#define spa_system_pollfd_wait(s,...)		spa_system_method_r(s,pollfd_wait,0,__VA_ARGS__)

#define spa_system_timerfd_create(s,...)	spa_system_method_r(s,timerfd_create,0,__VA_ARGS__)
#define spa_system_timerfd_settime(s,...)	spa_system_method_r(s,timerfd_settime,0,__VA_ARGS__)
#define spa_system_timerfd_gettime(s,...)	spa_system_method_r(s,timerfd_gettime,0,__VA_ARGS__)
#define spa_system_timerfd_read(s,...)		spa_system_method_r(s,timerfd_read,0,__VA_ARGS__)

#define spa_system_eventfd_create(s,...)	spa_system_method_r(s,eventfd_create,0,__VA_ARGS__)
#define spa_system_eventfd_write(s,...)		spa_system_method_r(s,eventfd_write,0,__VA_ARGS__)
#define spa_system_eventfd_read(s,...)		spa_system_method_r(s,eventfd_read,0,__VA_ARGS__)

#define spa_system_signalfd_create(s,...)	spa_system_method_r(s,signalfd_create,0,__VA_ARGS__)
#define spa_system_signalfd_read(s,...)		spa_system_method_r(s,signalfd_read,0,__VA_ARGS__)

SPA 系统功能用来执行文件操作以及访问时钟,包括文件读写、poll、关闭、pollfd 操作、timerfd 操作、eventfd 操作和 signalfd 操作等。

support.loop 插件的 Spa:Pointer:Interface:LoopSpa:Pointer:Interface:LoopControlSpa:Pointer:Interface:LoopUtils 接口,分别用 struct spa_loopstruct spa_loop_controlstruct spa_loop_utils 对象描述,它们的方法集合分别为 struct spa_loop_methodsstruct spa_loop_control_methodsstruct spa_loop_utils_methods,这些类型定义 (位于 pipewire/spa/include/spa/support/loop.h) 如下:

#define SPA_TYPE_INTERFACE_Loop		SPA_TYPE_INFO_INTERFACE_BASE "Loop"
#define SPA_TYPE_INTERFACE_DataLoop	SPA_TYPE_INFO_INTERFACE_BASE "DataLoop"
#define SPA_VERSION_LOOP		0
struct spa_loop { struct spa_interface iface; };

#define SPA_TYPE_INTERFACE_LoopControl	SPA_TYPE_INFO_INTERFACE_BASE "LoopControl"
#define SPA_VERSION_LOOP_CONTROL	0
struct spa_loop_control { struct spa_interface iface; };

#define SPA_TYPE_INTERFACE_LoopUtils	SPA_TYPE_INFO_INTERFACE_BASE "LoopUtils"
#define SPA_VERSION_LOOP_UTILS		0
struct spa_loop_utils { struct spa_interface iface; };

struct spa_source;

typedef void (*spa_source_func_t) (struct spa_source *source);

struct spa_source {
	struct spa_loop *loop;
	spa_source_func_t func;
	void *data;
	int fd;
	uint32_t mask;
	uint32_t rmask;
	/* private data for the loop implementer */
	void *priv;
};

typedef int (*spa_invoke_func_t) (struct spa_loop *loop,
				  bool async,
				  uint32_t seq,
				  const void *data,
				  size_t size,
				  void *user_data);

/**
 * Register sources and work items to an event loop
 */
struct spa_loop_methods {
	/* the version of this structure. This can be used to expand this
	 * structure in the future */
#define SPA_VERSION_LOOP_METHODS	0
	uint32_t version;

	/** add a source to the loop */
	int (*add_source) (void *object,
			   struct spa_source *source);

	/** update the source io mask */
	int (*update_source) (void *object,
			struct spa_source *source);

	/** remove a source from the loop */
	int (*remove_source) (void *object,
			struct spa_source *source);

	/** invoke a function in the context of this loop */
	int (*invoke) (void *object,
		       spa_invoke_func_t func,
		       uint32_t seq,
		       const void *data,
		       size_t size,
		       bool block,
		       void *user_data);
};
 . . . . . .
/** Control hooks. These hooks can't be removed from their
 *  callbacks and must be removed from a safe place (when the loop
 *  is not running or when it is locked). */
struct spa_loop_control_hooks {
#define SPA_VERSION_LOOP_CONTROL_HOOKS	0
	uint32_t version;
	/** Executed right before waiting for events. It is typically used to
	 * release locks. */
	void (*before) (void *data);
	/** Executed right after waiting for events. It is typically used to
	 * reacquire locks. */
	void (*after) (void *data);
};
 . . . . . .
 /**
 * Control an event loop
 */
struct spa_loop_control_methods {
	/* the version of this structure. This can be used to expand this
	 * structure in the future */
#define SPA_VERSION_LOOP_CONTROL_METHODS	0
	uint32_t version;

	int (*get_fd) (void *object);

	/** Add a hook
	 * \param ctrl the control to change
	 * \param hooks the hooks to add
	 *
	 * Adds hooks to the loop controlled by \a ctrl.
	 */
	void (*add_hook) (void *object,
			  struct spa_hook *hook,
			  const struct spa_loop_control_hooks *hooks,
			  void *data);

	/** Enter a loop
	 * \param ctrl the control
	 *
	 * Start an iteration of the loop. This function should be called
	 * before calling iterate and is typically used to capture the thread
	 * that this loop will run in.
	 */
	void (*enter) (void *object);
	/** Leave a loop
	 * \param ctrl the control
	 *
	 * Ends the iteration of a loop. This should be called after calling
	 * iterate.
	 */
	void (*leave) (void *object);

	/** Perform one iteration of the loop.
	 * \param ctrl the control
	 * \param timeout an optional timeout in milliseconds.
	 *	0 for no timeout, -1 for infinite timeout.
	 *
	 * This function will block
	 * up to \a timeout milliseconds and then dispatch the fds with activity.
	 * The number of dispatched fds is returned.
	 */
	int (*iterate) (void *object, int timeout);
};
 . . . . . .
typedef void (*spa_source_io_func_t) (void *data, int fd, uint32_t mask);
typedef void (*spa_source_idle_func_t) (void *data);
typedef void (*spa_source_event_func_t) (void *data, uint64_t count);
typedef void (*spa_source_timer_func_t) (void *data, uint64_t expirations);
typedef void (*spa_source_signal_func_t) (void *data, int signal_number);

/**
 * Create sources for an event loop
 */
struct spa_loop_utils_methods {
	/* the version of this structure. This can be used to expand this
	 * structure in the future */
#define SPA_VERSION_LOOP_UTILS_METHODS	0
	uint32_t version;

	struct spa_source *(*add_io) (void *object,
				      int fd,
				      uint32_t mask,
				      bool close,
				      spa_source_io_func_t func, void *data);

	int (*update_io) (void *object, struct spa_source *source, uint32_t mask);

	struct spa_source *(*add_idle) (void *object,
					bool enabled,
					spa_source_idle_func_t func, void *data);
	int (*enable_idle) (void *object, struct spa_source *source, bool enabled);

	struct spa_source *(*add_event) (void *object,
					 spa_source_event_func_t func, void *data);
	int (*signal_event) (void *object, struct spa_source *source);

	struct spa_source *(*add_timer) (void *object,
					 spa_source_timer_func_t func, void *data);
	int (*update_timer) (void *object,
			     struct spa_source *source,
			     struct timespec *value,
			     struct timespec *interval,
			     bool absolute);
	struct spa_source *(*add_signal) (void *object,
					  int signal_number,
					  spa_source_signal_func_t func, void *data);

	/** destroy a source allocated with this interface. This function
	 * should only be called when the loop is not running or from the
	 * context of the running loop */
	void (*destroy_source) (void *object, struct spa_source *source);
};

同样的,struct spa_loopstruct spa_loop_controlstruct spa_loop_utils 通过它们的 iface.cb.funcs 字段引用对应的方法集。PipeWire SPA 也提供了宏,通过 struct spa_loopstruct spa_loop_controlstruct spa_loop_utils 访问它们的方法,这些宏定义 (位于 pipewire/spa/include/spa/support/loop.h) 如下:

#define spa_loop_method(o,method,version,...)				\
({									\
	int _res = -ENOTSUP;						\
	struct spa_loop *_o = o;					\
	spa_interface_call_res(&_o->iface,				\
			struct spa_loop_methods, _res,			\
			method, version, ##__VA_ARGS__);		\
	_res;								\
})

#define spa_loop_add_source(l,...)	spa_loop_method(l,add_source,0,##__VA_ARGS__)
#define spa_loop_update_source(l,...)	spa_loop_method(l,update_source,0,##__VA_ARGS__)
#define spa_loop_remove_source(l,...)	spa_loop_method(l,remove_source,0,##__VA_ARGS__)
#define spa_loop_invoke(l,...)		spa_loop_method(l,invoke,0,##__VA_ARGS__)
 . . . . . .
 #define spa_loop_control_hook_before(l)							\
({											\
	struct spa_hook_list *_l = l;							\
	struct spa_hook *_h;								\
	spa_list_for_each_reverse(_h, &_l->list, link)					\
		spa_callbacks_call(&_h->cb, struct spa_loop_control_hooks, before, 0);	\
})

#define spa_loop_control_hook_after(l)							\
({											\
	struct spa_hook_list *_l = l;							\
	struct spa_hook *_h;								\
	spa_list_for_each(_h, &_l->list, link)						\
		spa_callbacks_call(&_h->cb, struct spa_loop_control_hooks, after, 0);	\
})
 . . . . . .
 #define spa_loop_control_method_v(o,method,version,...)			\
({									\
	struct spa_loop_control *_o = o;				\
	spa_interface_call(&_o->iface,					\
			struct spa_loop_control_methods,		\
			method, version, ##__VA_ARGS__);		\
})

#define spa_loop_control_method_r(o,method,version,...)			\
({									\
	int _res = -ENOTSUP;						\
	struct spa_loop_control *_o = o;				\
	spa_interface_call_res(&_o->iface,				\
			struct spa_loop_control_methods, _res,		\
			method, version, ##__VA_ARGS__);		\
	_res;								\
})

#define spa_loop_control_get_fd(l)		spa_loop_control_method_r(l,get_fd,0)
#define spa_loop_control_add_hook(l,...)	spa_loop_control_method_v(l,add_hook,0,__VA_ARGS__)
#define spa_loop_control_enter(l)		spa_loop_control_method_v(l,enter,0)
#define spa_loop_control_leave(l)		spa_loop_control_method_v(l,leave,0)
#define spa_loop_control_iterate(l,...)		spa_loop_control_method_r(l,iterate,0,__VA_ARGS__)
 . . . . . .
 #define spa_loop_utils_method_v(o,method,version,...)			\
({									\
	struct spa_loop_utils *_o = o;					\
	spa_interface_call(&_o->iface,					\
			struct spa_loop_utils_methods,			\
			method, version, ##__VA_ARGS__);		\
})

#define spa_loop_utils_method_r(o,method,version,...)			\
({									\
	int _res = -ENOTSUP;						\
	struct spa_loop_utils *_o = o;					\
	spa_interface_call_res(&_o->iface,				\
			struct spa_loop_utils_methods, _res,		\
			method, version, ##__VA_ARGS__);		\
	_res;								\
})
#define spa_loop_utils_method_s(o,method,version,...)			\
({									\
	struct spa_source *_res = NULL;					\
	struct spa_loop_utils *_o = o;					\
	spa_interface_call_res(&_o->iface,				\
			struct spa_loop_utils_methods, _res,		\
			method, version, ##__VA_ARGS__);		\
	_res;								\
})


#define spa_loop_utils_add_io(l,...)		spa_loop_utils_method_s(l,add_io,0,__VA_ARGS__)
#define spa_loop_utils_update_io(l,...)		spa_loop_utils_method_r(l,update_io,0,__VA_ARGS__)
#define spa_loop_utils_add_idle(l,...)		spa_loop_utils_method_s(l,add_idle,0,__VA_ARGS__)
#define spa_loop_utils_enable_idle(l,...)	spa_loop_utils_method_r(l,enable_idle,0,__VA_ARGS__)
#define spa_loop_utils_add_event(l,...)		spa_loop_utils_method_s(l,add_event,0,__VA_ARGS__)
#define spa_loop_utils_signal_event(l,...)	spa_loop_utils_method_r(l,signal_event,0,__VA_ARGS__)
#define spa_loop_utils_add_timer(l,...)		spa_loop_utils_method_s(l,add_timer,0,__VA_ARGS__)
#define spa_loop_utils_update_timer(l,...)	spa_loop_utils_method_r(l,update_timer,0,__VA_ARGS__)
#define spa_loop_utils_add_signal(l,...)	spa_loop_utils_method_s(l,add_signal,0,__VA_ARGS__)
#define spa_loop_utils_destroy_source(l,...)	spa_loop_utils_method_v(l,destroy_source,0,__VA_ARGS__)

PipeWire 的事件循环用 struct pw_loop 对象描述,这个类型定义 (位于 pipewire/src/pipewire/loop.h) 如下:

struct pw_loop {
	struct spa_system *system;		/**< system utils */
	struct spa_loop *loop;			/**< wrapped loop */
	struct spa_loop_control *control;	/**< loop control */
	struct spa_loop_utils *utils;		/**< loop utils */
};

事件循环的实现用文件内部类型 struct impl 对象描述,该类型定义 (位于 pipewire/src/pipewire/loop.c) 如下:

struct impl {
	struct pw_loop this;

	struct spa_handle *system_handle;
	struct spa_handle *loop_handle;
};

PipeWire 提供了 pw_loop_new()pw_loop_destroy() 两个接口来创建和销毁事件循环对象,这两个接口声明 (位于 pipewire/src/pipewire/loop.h) 如下:

struct pw_loop *
pw_loop_new(const struct spa_dict *props);

void
pw_loop_destroy(struct pw_loop *loop);

这两个接口实现 (位于 pipewire/src/pipewire/loop.c) 如下:

/** Create a new loop
 * \returns a newly allocated loop
 */
SPA_EXPORT
struct pw_loop *pw_loop_new(const struct spa_dict *props)
{
	int res;
	struct impl *impl;
	struct pw_loop *this;
	void *iface;
	struct spa_support support[32];
	uint32_t n_support;
	const char *lib;

	n_support = pw_get_support(support, 32);

	impl = calloc(1, sizeof(struct impl));
	if (impl == NULL) {
		res = -errno;
		goto error_cleanup;
	}

	this = &impl->this;

	if (props)
		lib = spa_dict_lookup(props, PW_KEY_LIBRARY_NAME_SYSTEM);
	else
		lib = NULL;

	impl->system_handle = pw_load_spa_handle(lib,
			SPA_NAME_SUPPORT_SYSTEM,
			props, n_support, support);
	if (impl->system_handle == NULL) {
		res = -errno;
		pw_log_error("%p: can't make "SPA_NAME_SUPPORT_SYSTEM" handle: %m", this);
		goto error_free;
	}

        if ((res = spa_handle_get_interface(impl->system_handle,
					    SPA_TYPE_INTERFACE_System,
					    &iface)) < 0) {
                pw_log_error("%p: can't get System interface: %s", this, spa_strerror(res));
                goto error_unload_system;
	}
	this->system = iface;

	support[n_support++] = SPA_SUPPORT_INIT(SPA_TYPE_INTERFACE_System, iface);

	if (props)
		lib = spa_dict_lookup(props, PW_KEY_LIBRARY_NAME_LOOP);
	else
		lib = NULL;

	impl->loop_handle = pw_load_spa_handle(lib,
			SPA_NAME_SUPPORT_LOOP, props,
			n_support, support);
	if (impl->loop_handle == NULL) {
		res = -errno;
		pw_log_error("%p: can't make "SPA_NAME_SUPPORT_LOOP" handle: %m", this);
		goto error_unload_system;
	}

        if ((res = spa_handle_get_interface(impl->loop_handle,
					    SPA_TYPE_INTERFACE_Loop,
					    &iface)) < 0) {
		pw_log_error("%p: can't get Loop interface: %s",
				this, spa_strerror(res));
                goto error_unload_loop;
        }
	this->loop = iface;

        if ((res = spa_handle_get_interface(impl->loop_handle,
					    SPA_TYPE_INTERFACE_LoopControl,
					    &iface)) < 0) {
		pw_log_error("%p: can't get LoopControl interface: %s",
				this, spa_strerror(res));
                goto error_unload_loop;
        }
	this->control = iface;

        if ((res = spa_handle_get_interface(impl->loop_handle,
					    SPA_TYPE_INTERFACE_LoopUtils,
					    &iface)) < 0) {
		pw_log_error("%p: can't get LoopUtils interface: %s",
				this, spa_strerror(res));
                goto error_unload_loop;
        }
	this->utils = iface;

	return this;

error_unload_loop:
	pw_unload_spa_handle(impl->loop_handle);
error_unload_system:
	pw_unload_spa_handle(impl->system_handle);
error_free:
	free(impl);
error_cleanup:
	errno = -res;
	return NULL;
}

/** Destroy a loop
 * \param loop a loop to destroy
 */
SPA_EXPORT
void pw_loop_destroy(struct pw_loop *loop)
{
	struct impl *impl = SPA_CONTAINER_OF(loop, struct impl, this);

	pw_unload_spa_handle(impl->loop_handle);
	pw_unload_spa_handle(impl->system_handle);
	free(impl);
}

struct implstruct pw_loop 的之类,pw_loop_new() 创建 struct impl 对象并返回,具体的过程如下:

  1. 获得全局的支持组件,它们用来支持后面要加载的插件的实现;
  2. 分配 struct impl 对象;
  3. 从传入的参数中获得 support.system 插件的库文件路径,失败时,库文件用支持库
  4. 加载 support.system 插件并获得其 Spa:Pointer:Interface:System 接口。
  5. Spa:Pointer:Interface:System 接口被加入支持组件列表,可用于后面加载 support.loop 插件。
  6. 从传入的参数中获得 support.loop 插件的库文件路径,失败时,库文件用支持库
  7. 加载 support.loop 插件并获得其 Spa:Pointer:Interface:LoopSpa:Pointer:Interface:LoopControlSpa:Pointer:Interface:LoopUtils 接口。

pw_loop_destroy() 销毁 struct pw_loop 对象,即 struct impl 对象,它卸载 support.systemsupport.loop 插件并释放 struct impl 对象。

PipeWire 提供了一组宏通过 struct pw_loop 访问它的各个接口的方法,它们定义 (位于 pipewire/src/pipewire/loop.h) 如下:

#define pw_loop_add_source(l,...)	spa_loop_add_source((l)->loop,__VA_ARGS__)
#define pw_loop_update_source(l,...)	spa_loop_update_source((l)->loop,__VA_ARGS__)
#define pw_loop_remove_source(l,...)	spa_loop_remove_source((l)->loop,__VA_ARGS__)
#define pw_loop_invoke(l,...)		spa_loop_invoke((l)->loop,__VA_ARGS__)

#define pw_loop_get_fd(l)		spa_loop_control_get_fd((l)->control)
#define pw_loop_add_hook(l,...)		spa_loop_control_add_hook((l)->control,__VA_ARGS__)
#define pw_loop_enter(l)		spa_loop_control_enter((l)->control)
#define pw_loop_iterate(l,...)		spa_loop_control_iterate((l)->control,__VA_ARGS__)
#define pw_loop_leave(l)		spa_loop_control_leave((l)->control)

#define pw_loop_add_io(l,...)		spa_loop_utils_add_io((l)->utils,__VA_ARGS__)
#define pw_loop_update_io(l,...)	spa_loop_utils_update_io((l)->utils,__VA_ARGS__)
#define pw_loop_add_idle(l,...)		spa_loop_utils_add_idle((l)->utils,__VA_ARGS__)
#define pw_loop_enable_idle(l,...)	spa_loop_utils_enable_idle((l)->utils,__VA_ARGS__)
#define pw_loop_add_event(l,...)	spa_loop_utils_add_event((l)->utils,__VA_ARGS__)
#define pw_loop_signal_event(l,...)	spa_loop_utils_signal_event((l)->utils,__VA_ARGS__)
#define pw_loop_add_timer(l,...)	spa_loop_utils_add_timer((l)->utils,__VA_ARGS__)
#define pw_loop_update_timer(l,...)	spa_loop_utils_update_timer((l)->utils,__VA_ARGS__)
#define pw_loop_add_signal(l,...)	spa_loop_utils_add_signal((l)->utils,__VA_ARGS__)
#define pw_loop_destroy_source(l,...)	spa_loop_utils_destroy_source((l)->utils,__VA_ARGS__)

PipeWire 没有提供基于 struct pw_loop 的事件循环的启动、运行和停止等操作,但有多个基于 struct pw_loop 的场景化事件循环组件,这些事件循环组件以场景化 API 的形式支持这些操作,主要有主事件循环 main-loop、数据事件循环 data-loop 和线程化事件循环 thread-loop,它们处理事件循环的启动、运行和停止等。

主事件循环 main-loop

主事件循环用 struct pw_main_loop 对象描述,这个类型定义 (位于 pipewire/src/pipewire/private.h) 如下:

#define pw_main_loop_emit(o,m,v,...) spa_hook_list_call(&o->listener_list, struct pw_main_loop_events, m, v, ##__VA_ARGS__)
#define pw_main_loop_emit_destroy(o) pw_main_loop_emit(o, destroy, 0)

struct pw_main_loop {
        struct pw_loop *loop;

	struct spa_hook_list listener_list;

	unsigned int created:1;
	unsigned int running:1;
};

struct pw_main_loop 除了有 struct pw_loop,还有用于管理事件监听的列表,和管理生命周期的状态。

PipeWire 音频服务器主程序中,调用 pw_main_loop_new() 函数创建主事件循环,调用 pw_loop_add_signal() 向主事件循环添加 SIGINTSIGTERM 信号处理程序,调用 pw_main_loop_run() 函数运行主事件循环,在 SIGINTSIGTERM 的信号处理程序中,调用 pw_main_loop_quit() 函数退出主事件循环,主事件循环结束之后,调用 pw_main_loop_destroy() 函数销毁它。主事件循环的生命周期大体如此。

pw_loop_add_signal() 是一个宏,包装了对 SPA 事件循环接口的调用,主事件循环生命周期的其它函数定义 (位于 pipewire/src/pipewire/main-loop.c) 如下:

PW_LOG_TOPIC_EXTERN(log_main_loop);
#define PW_LOG_TOPIC_DEFAULT log_main_loop

static int do_stop(struct spa_loop *loop, bool async, uint32_t seq,
		const void *data, size_t size, void *user_data)
{
	struct pw_main_loop *this = user_data;
	pw_log_debug("%p: do stop", this);
	this->running = false;
	return 0;
}

static struct pw_main_loop *loop_new(struct pw_loop *loop, const struct spa_dict *props)
{
	struct pw_main_loop *this;
	int res;

	this = calloc(1, sizeof(struct pw_main_loop));
	if (this == NULL) {
		res = -errno;
		goto error_cleanup;
	}

	pw_log_debug("%p: new", this);

	if (loop == NULL) {
		loop = pw_loop_new(props);
		this->created = true;
	}
	if (loop == NULL) {
		res = -errno;
		goto error_free;
	}
	this->loop = loop;

	spa_hook_list_init(&this->listener_list);

	return this;

error_free:
	free(this);
error_cleanup:
	errno = -res;
	return NULL;
}

/** Create a new main loop
 * \return a newly allocated \ref pw_main_loop
 *
 */
SPA_EXPORT
struct pw_main_loop *pw_main_loop_new(const struct spa_dict *props)
{
	return loop_new(NULL, props);
}

/** Destroy a main loop
 * \param loop the main loop to destroy
 *
 */
SPA_EXPORT
void pw_main_loop_destroy(struct pw_main_loop *loop)
{
	pw_log_debug("%p: destroy", loop);
	pw_main_loop_emit_destroy(loop);

	if (loop->created)
		pw_loop_destroy(loop->loop);

	spa_hook_list_clean(&loop->listener_list);

	free(loop);
}
 . . . . . .
SPA_EXPORT
struct pw_loop * pw_main_loop_get_loop(struct pw_main_loop *loop)
{
	return loop->loop;
}

/** Stop a main loop
 * \param loop a \ref pw_main_loop to stop
 *
 * The call to \ref pw_main_loop_run() will return
 *
 */
SPA_EXPORT
int pw_main_loop_quit(struct pw_main_loop *loop)
{
	pw_log_debug("%p: quit", loop);
	return pw_loop_invoke(loop->loop, do_stop, 1, NULL, 0, false, loop);
}

/** Start a main loop
 * \param loop the main loop to start
 *
 * Start running \a loop. This function blocks until \ref pw_main_loop_quit()
 * has been called
 *
 */
SPA_EXPORT
int pw_main_loop_run(struct pw_main_loop *loop)
{
	int res = 0;

	pw_log_debug("%p: run", loop);

	loop->running = true;
	pw_loop_enter(loop->loop);
	while (loop->running) {
		if ((res = pw_loop_iterate(loop->loop, -1)) < 0) {
			if (res == -EINTR)
				continue;
			pw_log_warn("%p: iterate error %d (%s)",
					loop, res, spa_strerror(res));
		}
	}
	pw_loop_leave(loop->loop);
	return res;
}

pw_main_loop_new() 函数用来创建主事件循环,它通过内部的 loop_new() 函数来完成工作。loop_new() 函数的第一个参数 loop 有点多余。loop_new() 函数做的主要的事情是,调用 pw_loop_new() 函数创建 struct pw_loop 对象,为 struct pw_main_loop 对象分配内存并初始化其各个字段。

或许 PipeWire 的主事件循环在设计时,考虑过多个主事件循环共用相同的底层 struct pw_loop 的情况,struct pw_main_loop 对象的 created 字段用于记录 struct pw_loop 的所有者,即 struct pw_loop 对象是随哪个 struct pw_main_loop 对象一起创建的。本着谁创建谁销毁的思路,struct pw_loop 对象需要随着它的所有者一起销毁。估计最终发现这种设计并不是很安全,因而放弃了这种设计思路,但相关的代码逻辑并没有完全被清理掉。

pw_main_loop_run() 函数运行主事件循环。struct pw_main_loop 对象的 running 字段用户记录运行状态。pw_main_loop_run() 函数的运行过程如下:

  • 调用 pw_loop_enter() 进入事件循环。pw_loop_enter() 是个宏,是对 SPA 事件循环接口的封装,具体而言,是对 Spa:Pointer:Interface:LoopControl 接口的 enter() 方法的封装。
  • 执行一个 while 循环,在循环中调用 pw_loop_iterate() 迭代事件循环,处理事件循环中的事件。pw_loop_iterate() 是个宏,对 SPA 事件循环接口 Spa:Pointer:Interface:LoopControliterate() 方法的封装。
  • 循环结束后,调用 pw_loop_leave() 离开事件循环。pw_loop_enter() 是个宏,是对 SPA 事件循环接口 Spa:Pointer:Interface:LoopControlleave() 方法的封装。

主事件循环的运行,通过 SPA 事件循环接口 Spa:Pointer:Interface:LoopControl 提供的方法来实现。

pw_main_loop_quit() 函数退出主事件循环。它调用 pw_loop_invoke() 在事件循环的上下文中调用一个函数 do_stop(),do_stop() 函数更新主事件循环的运行状态。pw_loop_invoke() 是个宏,是对 SPA 事件循环接口 Spa:Pointer:Interface:Loopinvoke() 方法的封装。

PipeWire 的主事件循环支持注册监听者,通过 pw_main_loop_add_listener() 函数可以注册监听者,大概主要是为了通知主事件循环的销毁。

pw_main_loop_destroy() 函数销毁主事件循环,它的执行过程如下:

  1. 调用 pw_main_loop_emit_destroy() 通知所有监听者主事件循环即将销毁。
  2. 销毁底层的 struct pw_loop 对象。
  3. 清理监听者列表。
  4. 释放 struct pw_main_loop 对象。

主事件循环不起专门的线程来运行。

数据事件循环 data-loop

数据事件循环用 struct pw_data_loop 对象描述,这个类型定义 (位于 pipewire/src/pipewire/private.h) 如下:

#define pw_data_loop_emit(o,m,v,...) spa_hook_list_call(&o->listener_list, struct pw_data_loop_events, m, v, ##__VA_ARGS__)
#define pw_data_loop_emit_destroy(o) pw_data_loop_emit(o, destroy, 0)

struct pw_data_loop {
	struct pw_loop *loop;

	struct spa_hook_list listener_list;

	pthread_t thread;
	unsigned int cancel:1;
	unsigned int created:1;
	unsigned int running:1;
};

struct pw_data_loopstruct pw_main_loop 类似,但由于它设计为运行在专门创建的线程中,因而多了它所运行的线程的引用的字段 thread 以及指示是否可以取消的字段 cancel

PipeWire 的数据事件循环的有关接口声明 (位于 pipewire/src/pipewire/data-loop.h) 如下:

/** Loop events, use \ref pw_data_loop_add_listener to add a listener */
struct pw_data_loop_events {
#define PW_VERSION_DATA_LOOP_EVENTS		0
	uint32_t version;
	/** The loop is destroyed */
	void (*destroy) (void *data);
};

/** Make a new loop. */
struct pw_data_loop *
pw_data_loop_new(const struct spa_dict *props);

/** Add an event listener to loop */
void pw_data_loop_add_listener(struct pw_data_loop *loop,
			       struct spa_hook *listener,
			       const struct pw_data_loop_events *events,
			       void *data);

/** wait for activity on the loop up to \a timeout milliseconds.
 * Should be called from the loop function */
int pw_data_loop_wait(struct pw_data_loop *loop, int timeout);

/** make sure the thread will exit. Can be called from a loop callback */
void pw_data_loop_exit(struct pw_data_loop *loop);

/** Get the loop implementation of this data loop */
struct pw_loop *
pw_data_loop_get_loop(struct pw_data_loop *loop);

/** Destroy the loop */
void pw_data_loop_destroy(struct pw_data_loop *loop);

/** Start the processing thread */
int pw_data_loop_start(struct pw_data_loop *loop);

/** Stop the processing thread */
int pw_data_loop_stop(struct pw_data_loop *loop);

/** Check if the current thread is the processing thread */
bool pw_data_loop_in_thread(struct pw_data_loop *loop);
/** Get the thread object */
struct spa_thread *pw_data_loop_get_thread(struct pw_data_loop *loop);

/** invoke func in the context of the thread or in the caller thread when
 * the loop is not running. Since 0.3.3 */
int pw_data_loop_invoke(struct pw_data_loop *loop,
		spa_invoke_func_t func, uint32_t seq, const void *data, size_t size,
		bool block, void *user_data);

数据事件循环 data-loop 的声明周期大体为,pw_data_loop_new() 函数创建数据事件循环;pw_data_loop_start() 函数创建线程并启动运行事件循环;pw_data_loop_get_loop() 函数用来获得底层的 struct pw_loop,使用者可以借此向事件循环中抛入任务;pw_data_loop_invoke() 函数用来直接向事件循环中抛入任务;pw_data_loop_exit() 函数用来退出事件循环;pw_data_loop_stop() 函数用来停止处理线程;pw_data_loop_destroy() 函数用来销毁事件循环,结束事件循环的生命周期。

此外,pw_data_loop_add_listener() 函数用来向事件循环注册监听者;pw_data_loop_wait() 函数用来等待事件循环;pw_data_loop_in_thread() 函数用来检查特定事件循环是否运行在当前线程;pw_data_loop_get_thread() 函数用来获得事件循环所运行的线程的句柄。

数据事件循环的这些接口定义 (位于 pipewire/src/pipewire/data-loop.c) 如下:

PW_LOG_TOPIC_EXTERN(log_data_loop);
#define PW_LOG_TOPIC_DEFAULT log_data_loop

SPA_EXPORT
int pw_data_loop_wait(struct pw_data_loop *this, int timeout)
{
	int res;

	while (true) {
		if (!this->running) {
			res = -ECANCELED;
			break;
		}
		if ((res = pw_loop_iterate(this->loop, timeout)) < 0) {
			if (res == -EINTR)
				continue;
		}
		break;
	}
	return res;
}

SPA_EXPORT
void pw_data_loop_exit(struct pw_data_loop *this)
{
	this->running = false;
}

static void thread_cleanup(void *arg)
{
	struct pw_data_loop *this = arg;
	pw_log_debug("%p: leave thread", this);
	this->running = false;
	pw_loop_leave(this->loop);
}

static void *do_loop(void *user_data)
{
	struct pw_data_loop *this = user_data;
	int res;

	pw_log_debug("%p: enter thread", this);
	pw_loop_enter(this->loop);

	pthread_cleanup_push(thread_cleanup, this);

	while (this->running) {
		if ((res = pw_loop_iterate(this->loop, -1)) < 0) {
			if (res == -EINTR)
				continue;
			pw_log_error("%p: iterate error %d (%s)",
					this, res, spa_strerror(res));
		}
	}
	pthread_cleanup_pop(1);

	return NULL;
}

static int do_stop(struct spa_loop *loop, bool async, uint32_t seq,
		const void *data, size_t size, void *user_data)
{
	struct pw_data_loop *this = user_data;
	pw_log_debug("%p: stopping", this);
	this->running = false;
	return 0;
}

static struct pw_data_loop *loop_new(struct pw_loop *loop, const struct spa_dict *props)
{
	struct pw_data_loop *this;
	const char *str;
	int res;

	this = calloc(1, sizeof(struct pw_data_loop));
	if (this == NULL) {
		res = -errno;
		goto error_cleanup;
	}

	pw_log_debug("%p: new", this);

	if (loop == NULL) {
		loop = pw_loop_new(props);
		this->created = true;
	}
	if (loop == NULL) {
		res = -errno;
		pw_log_error("%p: can't create loop: %m", this);
		goto error_free;
	}
	this->loop = loop;

	if (props != NULL &&
	    (str = spa_dict_lookup(props, "loop.cancel")) != NULL)
		this->cancel = pw_properties_parse_bool(str);

	spa_hook_list_init(&this->listener_list);

	return this;

error_free:
	free(this);
error_cleanup:
	errno = -res;
	return NULL;
}

/** Create a new \ref pw_data_loop.
 * \return a newly allocated data loop
 *
 */
SPA_EXPORT
struct pw_data_loop *pw_data_loop_new(const struct spa_dict *props)
{
	return loop_new(NULL, props);
}


/** Destroy a data loop
 * \param loop the data loop to destroy
 */
SPA_EXPORT
void pw_data_loop_destroy(struct pw_data_loop *loop)
{
	pw_log_debug("%p: destroy", loop);

	pw_data_loop_emit_destroy(loop);

	pw_data_loop_stop(loop);

	if (loop->created)
		pw_loop_destroy(loop->loop);

	spa_hook_list_clean(&loop->listener_list);

	free(loop);
}

SPA_EXPORT
void pw_data_loop_add_listener(struct pw_data_loop *loop,
			       struct spa_hook *listener,
			       const struct pw_data_loop_events *events,
			       void *data)
{
	spa_hook_list_append(&loop->listener_list, listener, events, data);
}

SPA_EXPORT
struct pw_loop *
pw_data_loop_get_loop(struct pw_data_loop *loop)
{
	return loop->loop;
}

/** Start a data loop
 * \param loop the data loop to start
 * \return 0 if ok, -1 on error
 *
 * This will start the realtime thread that manages the loop.
 *
 */
SPA_EXPORT
int pw_data_loop_start(struct pw_data_loop *loop)
{
	if (!loop->running) {
		struct spa_thread *thr;

		loop->running = true;
		thr = pw_thread_utils_create(NULL, do_loop, loop);
		loop->thread = (pthread_t)thr;
		if (thr == NULL) {
			pw_log_error("%p: can't create thread: %m", loop);
			loop->running = false;
			return -errno;
		}
	}
	return 0;
}

/** Stop a data loop
 * \param loop the data loop to Stop
 * \return 0
 *
 * This will stop and join the realtime thread that manages the loop.
 *
 */
SPA_EXPORT
int pw_data_loop_stop(struct pw_data_loop *loop)
{
	pw_log_debug("%p stopping", loop);
	if (loop->running) {
		if (loop->cancel) {
			pw_log_debug("%p cancel", loop);
			pthread_cancel(loop->thread);
		} else {
			pw_log_debug("%p signal", loop);
			pw_loop_invoke(loop->loop, do_stop, 1, NULL, 0, false, loop);
		}
		pw_log_debug("%p join", loop);
		pw_thread_utils_join((struct spa_thread*)loop->thread, NULL);
		pw_log_debug("%p joined", loop);
	}
	pw_log_debug("%p stopped", loop);
	return 0;
}

/** Check if we are inside the data loop
 * \param loop the data loop to check
 * \return true is the current thread is the data loop thread
 *
 */
SPA_EXPORT
bool pw_data_loop_in_thread(struct pw_data_loop * loop)
{
	return pthread_equal(loop->thread, pthread_self());
}

/** Get the thread object.
 * \param loop the data loop to get the thread of
 * \return the thread object or NULL when the thread is not running
 *
 * On posix based systems this returns a pthread_t
 */
SPA_EXPORT
struct spa_thread *pw_data_loop_get_thread(struct pw_data_loop * loop)
{
	return loop->running ? (struct spa_thread*)loop->thread : NULL;
}

SPA_EXPORT
int pw_data_loop_invoke(struct pw_data_loop *loop,
		spa_invoke_func_t func, uint32_t seq, const void *data, size_t size,
		bool block, void *user_data)
{
	int res;
	if (loop->running)
		res = pw_loop_invoke(loop->loop, func, seq, data, size, block, user_data);
	else
		res = func(loop->loop->loop, false, seq, data, size, user_data);
	return res;
}

pw_data_loop_new() 函数创建数据事件循环,它的执行过程和主事件循环的 pw_main_loop_new() 函数的执行过程几乎完全一样,连内部函数多余的函数参数都一样,除了它会从参数中获取是否可以取消参数 loop.cancel 外。

pw_data_loop_start() 函数通过 pw_thread_utils_create() 调用 SPA thread_utils 组件创建线程并启动运行事件循环,事件循环由内部函数 do_loop() 运行。do_loop() 函数的执行过程和主事件循环的 pw_main_loop_run() 函数的执行过程几乎完全一样,仅有的区别在于事件循环运行结束后的资源清理部分。为了处理线程在正常退出和异常退出时都能正确清理资源,比如线程被取消,这里在进入事件循环之前使用 pthread_cleanup_push() 函数注册线程的退出清理函数 thread_cleanup(),在事件循环结束之后调用 pthread_cleanup_pop(1) 函数弹出清理函数并执行。thread_cleanup() 函数更新运行状态并调用 pw_loop_leave(),这与pw_main_loop_run() 函数的清理动作基本一致。

pw_data_loop_invoke() 函数直接向事件循环中抛入任务,事件循环没有运行时,在调用者线程运行任务。

pw_data_loop_exit() 函数用来退出事件循环,它更新事件循环的运行状态,后面事件循环检测到状态变化,平稳地退出运行。pw_data_loop_stop() 函数用来停止处理线程,如果参数设置为允许取消,则取消线程,否则在事件循环上下文执行 do_stop() 函数退出事件循环,并调用 SPA thread_utils 组件的 join() 等待线程最终退出。do_stop() 函数做的事情与 pw_data_loop_exit() 的完全一样。相对于 pw_data_loop_exit()pw_data_loop_stop() 函数会等线程最终结束。

看起来数据事件循环不是线程安全的。

线程化事件循环 thread-loop

线程化事件循环 thread-loop 也是 struct pw_loop 的包装器。与数据事件循环类似,它也起单独的线程运行包装的事件循环。它允许同步应用程序使用异步 API 而无需担心阻塞 PipeWire 库。

线程化事件循环用 struct pw_thread_loop 对象描述,这个类型定义 (位于 pipewire/src/pipewire/thread-loop.c) 如下:

PW_LOG_TOPIC_EXTERN(log_thread_loop);
#define PW_LOG_TOPIC_DEFAULT log_thread_loop


#define pw_thread_loop_events_emit(o,m,v,...) spa_hook_list_call(&o->listener_list, struct pw_thread_loop_events, m, v, ##__VA_ARGS__)
#define pw_thread_loop_events_destroy(o)	pw_thread_loop_events_emit(o, destroy, 0)

#ifdef __FreeBSD__
#include 
#if __FreeBSD_version < 1202000
int pthread_setname_np(pthread_t thread, const char *name)
{
	pthread_set_name_np(thread, name);
	return 0;
}
#endif
#endif

/** \cond */
struct pw_thread_loop {
	struct pw_loop *loop;
	char name[16];

	struct spa_hook_list listener_list;

	pthread_mutex_t lock;
	pthread_cond_t cond;
	pthread_cond_t accept_cond;

	pthread_t thread;

	struct spa_hook hook;

	struct spa_source *event;

	int n_waiting;
	int n_waiting_for_accept;
	unsigned int created:1;
	unsigned int running:1;
};

线程化事件循环对象使用 pw_thread_loop_new()pw_thread_loop_new_full() 创建,后者允许把一个已经创建的 struct pw_loop 包装为线程化事件循环,这两个接口都需要指定要派生的线程的名字。分配了对象之后,通过 pw_thread_loop_start() 创建并启动线程。

退出循环并停止它的线程使用 pw_thread_loop_stop(),它需要在不持有锁的情况下调用,会等待线程结束。当这个函数返回,线程已经停止,struct pw_thread_loop 对象可以使用 pw_thread_loop_destroy() 来释放。

由于 PipeWire API 不允许并发访问对象,则需要一个锁定方案来保证安全。线程化事件循环通过 pw_thread_loop_lock()pw_thread_loop_unlock() 提供了这样的方案。锁是递归的,因而相同线程可以锁定多次,但还是要确保 pw_thread_loop_unlock()pw_thread_loop_lock() 调用的次数相同。

调用任何使用了与循环关联的对象的 PipeWire 函数时,都需要持有锁。但不要超出限度地持有锁,线程化事件循环运行时也需要持有锁。

事件循环相关各个接口的实现

PipeWire 通过支持库插件实现事件循环相关的各个接口。support.system 插件的 Spa:Pointer:Interface:System 接口,是对操作系统 API 的包装,该接口的各个方法定义 (位于 pipewire/spa/plugins/support/system.c) 如下:

struct impl {
	struct spa_handle handle;
	struct spa_system system;
        struct spa_log *log;
};

static ssize_t impl_read(void *object, int fd, void *buf, size_t count)
{
	ssize_t res = read(fd, buf, count);
	return res < 0 ? -errno : res;
}

static ssize_t impl_write(void *object, int fd, const void *buf, size_t count)
{
	ssize_t res = write(fd, buf, count);
	return res < 0 ? -errno : res;
}

static int impl_ioctl(void *object, int fd, unsigned long request, ...)
{
	int res;
	va_list ap;
	long arg;

	va_start(ap, request);
	arg = va_arg(ap, long);
	res = ioctl(fd, request, arg);
	va_end(ap);

	return res < 0 ? -errno : res;
}

static int impl_close(void *object, int fd)
{
	struct impl *impl = object;
	int res = close(fd);
	spa_log_debug(impl->log, "%p: close fd:%d", impl, fd);
	return res < 0 ? -errno : res;
}

/* clock */
static int impl_clock_gettime(void *object,
			int clockid, struct timespec *value)
{
	int res = clock_gettime(clockid, value);
	return res < 0 ? -errno : res;
}

static int impl_clock_getres(void *object,
			int clockid, struct timespec *res)
{
	int r = clock_getres(clockid, res);
	return r < 0 ? -errno : r;
}

/* poll */
static int impl_pollfd_create(void *object, int flags)
{
	struct impl *impl = object;
	int fl = 0, res;
	if (flags & SPA_FD_CLOEXEC)
		fl |= EPOLL_CLOEXEC;
	res = epoll_create1(fl);
	spa_log_debug(impl->log, "%p: new fd:%d", impl, res);
	return res < 0 ? -errno : res;
}

static int impl_pollfd_add(void *object, int pfd, int fd, uint32_t events, void *data)
{
	struct epoll_event ep;
	int res;

	spa_zero(ep);
	ep.events = events;
	ep.data.ptr = data;

	res = epoll_ctl(pfd, EPOLL_CTL_ADD, fd, &ep);
	return res < 0 ? -errno : res;
}

static int impl_pollfd_mod(void *object, int pfd, int fd, uint32_t events, void *data)
{
	struct epoll_event ep;
	int res;

	spa_zero(ep);
	ep.events = events;
	ep.data.ptr = data;

	res = epoll_ctl(pfd, EPOLL_CTL_MOD, fd, &ep);
	return res < 0 ? -errno : res;
}

static int impl_pollfd_del(void *object, int pfd, int fd)
{
	int res = epoll_ctl(pfd, EPOLL_CTL_DEL, fd, NULL);
	return res < 0 ? -errno : res;
}

static int impl_pollfd_wait(void *object, int pfd,
		struct spa_poll_event *ev, int n_ev, int timeout)
{
	struct epoll_event ep[n_ev];
	int i, nfds;

	if (SPA_UNLIKELY((nfds = epoll_wait(pfd, ep, n_ev, timeout)) < 0))
		return -errno;

        for (i = 0; i < nfds; i++) {
                ev[i].events = ep[i].events;
                ev[i].data = ep[i].data.ptr;
        }
	return nfds;
}

/* timers */
static int impl_timerfd_create(void *object, int clockid, int flags)
{
	struct impl *impl = object;
	int fl = 0, res;
	if (flags & SPA_FD_CLOEXEC)
		fl |= TFD_CLOEXEC;
	if (flags & SPA_FD_NONBLOCK)
		fl |= TFD_NONBLOCK;
	res = timerfd_create(clockid, fl);
	spa_log_debug(impl->log, "%p: new fd:%d", impl, res);
	return res < 0 ? -errno : res;
}

static int impl_timerfd_settime(void *object,
			int fd, int flags,
			const struct itimerspec *new_value,
			struct itimerspec *old_value)
{
	int fl = 0, res;
	if (flags & SPA_FD_TIMER_ABSTIME)
		fl |= TFD_TIMER_ABSTIME;
	if (flags & SPA_FD_TIMER_CANCEL_ON_SET)
		fl |= TFD_TIMER_CANCEL_ON_SET;
	res = timerfd_settime(fd, fl, new_value, old_value);
	return res < 0 ? -errno : res;
}

static int impl_timerfd_gettime(void *object,
			int fd, struct itimerspec *curr_value)
{
	int res = timerfd_gettime(fd, curr_value);
	return res < 0 ? -errno : res;

}
static int impl_timerfd_read(void *object, int fd, uint64_t *expirations)
{
	if (read(fd, expirations, sizeof(uint64_t)) != sizeof(uint64_t))
		return -errno;
	return 0;
}

/* events */
static int impl_eventfd_create(void *object, int flags)
{
	struct impl *impl = object;
	int fl = 0, res;
	if (flags & SPA_FD_CLOEXEC)
		fl |= EFD_CLOEXEC;
	if (flags & SPA_FD_NONBLOCK)
		fl |= EFD_NONBLOCK;
	if (flags & SPA_FD_EVENT_SEMAPHORE)
		fl |= EFD_SEMAPHORE;
	res = eventfd(0, fl);
	spa_log_debug(impl->log, "%p: new fd:%d", impl, res);
	return res < 0 ? -errno : res;
}

static int impl_eventfd_write(void *object, int fd, uint64_t count)
{
	if (write(fd, &count, sizeof(uint64_t)) != sizeof(uint64_t))
		return -errno;
	return 0;
}

static int impl_eventfd_read(void *object, int fd, uint64_t *count)
{
	if (read(fd, count, sizeof(uint64_t)) != sizeof(uint64_t))
		return -errno;
	return 0;
}

/* signals */
static int impl_signalfd_create(void *object, int signal, int flags)
{
	struct impl *impl = object;
	sigset_t mask;
	int res, fl = 0;

	if (flags & SPA_FD_CLOEXEC)
		fl |= SFD_CLOEXEC;
	if (flags & SPA_FD_NONBLOCK)
		fl |= SFD_NONBLOCK;

	sigemptyset(&mask);
	sigaddset(&mask, signal);
	res = signalfd(-1, &mask, fl);
	sigprocmask(SIG_BLOCK, &mask, NULL);
	spa_log_debug(impl->log, "%p: new fd:%d", impl, res);

	return res < 0 ? -errno : res;
}

static int impl_signalfd_read(void *object, int fd, int *signal)
{
	struct signalfd_siginfo signal_info;
	int len;

	len = read(fd, &signal_info, sizeof signal_info);
	if (!(len == -1 && errno == EAGAIN) && len != sizeof signal_info)
		return -errno;

	*signal = signal_info.ssi_signo;

	return 0;
}

static const struct spa_system_methods impl_system = {
	SPA_VERSION_SYSTEM_METHODS,
	.read = impl_read,
	.write = impl_write,
	.ioctl = impl_ioctl,
	.close = impl_close,
	.clock_gettime = impl_clock_gettime,
	.clock_getres = impl_clock_getres,
	.pollfd_create = impl_pollfd_create,
	.pollfd_add = impl_pollfd_add,
	.pollfd_mod = impl_pollfd_mod,
	.pollfd_del = impl_pollfd_del,
	.pollfd_wait = impl_pollfd_wait,
	.timerfd_create = impl_timerfd_create,
	.timerfd_settime = impl_timerfd_settime,
	.timerfd_gettime = impl_timerfd_gettime,
	.timerfd_read = impl_timerfd_read,
	.eventfd_create = impl_eventfd_create,
	.eventfd_write = impl_eventfd_write,
	.eventfd_read = impl_eventfd_read,
	.signalfd_create = impl_signalfd_create,
	.signalfd_read = impl_signalfd_read,
};

PipeWire 事件循环的核心是 epoll 机制,support.system 插件的 Spa:Pointer:Interface:System 接口提供接口方法来创建 epoll fd (pollfd_create),向 epoll fd 添加事件 (pollfd_add),修改已经添加的事件 (pollfd_mod),删除已经添加的事件 (pollfd_del),以及在 epoll fd 上等待 (pollfd_wait)。

基于 epoll 机制,Spa:Pointer:Interface:System 接口为添加多元化的事件源提供支持,包括操作定时器的接口方法 (timerfd_createtimerfd_settimetimerfd_gettimetimerfd_read),操作事件的接口方法 (eventfd_createeventfd_writeeventfd_read) 和操作信号的接口方法 (signalfd_createsignalfd_read)。

Spa:Pointer:Interface:System 接口还支持一般的文件操作 (readwriteioctlclose) 和时钟操作 (clock_gettimeclock_getres) 。

support.loop 插件的实现中,事件循环用一个内部对象 struct impl 表示,事件循环中的事件源用 struct source_impl 对象表示,这两个类型定义 (位于 pipewire/spa/plugins/support/loop.c) 如下:

struct impl {
	struct spa_handle handle;
	struct spa_loop loop;
	struct spa_loop_control control;
	struct spa_loop_utils utils;

        struct spa_log *log;
        struct spa_system *system;

	struct spa_list source_list;
	struct spa_list destroy_list;
	struct spa_hook_list hooks_list;

	int poll_fd;
	pthread_t thread;
	int enter_count;

	struct spa_source *wakeup;
	int ack_fd;

	struct spa_ringbuffer buffer;
	uint8_t *buffer_data;
	uint8_t buffer_mem[DATAS_SIZE + MAX_ALIGN];

	unsigned int flushing:1;
};

struct source_impl {
	struct spa_source source;

	struct impl *impl;
	struct spa_list link;

	bool close;
	union {
		spa_source_io_func_t io;
		spa_source_idle_func_t idle;
		spa_source_event_func_t event;
		spa_source_timer_func_t timer;
		spa_source_signal_func_t signal;
	} func;
	bool enabled;
	struct spa_source *fallback;
};

support.loop 插件基于 Spa:Pointer:Interface:System 接口实现。support.loop 插件的 3 个接口中,Spa:Pointer:Interface:Loop 接口用来直接操作事件循环中的事件源以及在事件循环上下文中执行任务。Spa:Pointer:Interface:LoopUtils 接口用来操作高层事件源,如 IO、事件、定时器和信号事件源,它的各个接口方法基于 Spa:Pointer:Interface:Loop 接口实现。Spa:Pointer:Interface:LoopControl 接口主要用来执行事件循环的运行控制。

Done.

你可能感兴趣的:(音视频开发,音视频,java)