Projects
Essentials
pipewire-aptx
Sign Up
Log In
Username
Password
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
Expand all
Collapse all
Changes of Revision 34
View file
pipewire-aptx.changes
Changed
@@ -1,4 +1,9 @@ ------------------------------------------------------------------- +Fri Sep 22 17:21:32 UTC 2023 - Bjørn Lie <zaitor@opensuse.org> + +- Update to version 0.3.80 + +------------------------------------------------------------------- Wed Aug 30 13:49:39 UTC 2023 - Bjørn Lie <zaitor@opensuse.org> - Update to version 0.3.79
View file
pipewire-aptx.spec
Changed
@@ -7,7 +7,7 @@ %define soversion 0_2 Name: pipewire-aptx -Version: 0.3.79 +Version: 0.3.80 Release: 0 Summary: PipeWire Bluetooth aptX codec plugin License: MIT
View file
pipewire-0.3.79.tar.gz/.gitignore -> pipewire-0.3.80.tar.gz/.gitignore
Changed
@@ -18,6 +18,7 @@ subprojects/libyaml.wrap subprojects/libyaml subprojects/libcamera +subprojects/webrtc-audio-processing # Created by https://www.gitignore.io/api/vim
View file
pipewire-0.3.79.tar.gz/NEWS -> pipewire-0.3.80.tar.gz/NEWS
Changed
@@ -1,3 +1,71 @@ +# PipeWire 0.3.80 (2023-09-14) + +This is a bugfix release that is API and ABI compatible with previous +0.3.x releases. + +## Highlights + - A new Tag param was added that allows arbitrary metadata to be transported + out-of-band in the graph. + - Vulkan DMA buf support was merged. + - The echo-canceller was ported to webrtc-audio-processing-1. + - Fix a regression in locating monitor sources by id in pulse-server. + - Mixer io areas updates are now synchronized correctly with the data + thread to avoid potential crashes. + - Many more bugfixes and improvements. + + +## PipeWire + - Handle driver nodes that refuse to change the quantum or rate. + - A new Tag param was added that allows arbitrary metadata to be transported + out-of-band in the graph. + +## Modules + - The pipe-tunnel source has been reworked to use a ringbuffer and rate + adaption to keep the latency constant. It can now also function as a + driver to reduce resampling. (#3478) + +## Tools + - pw-cat will now place media properties in Tag params. + - pw-mon can now filter props and params. + +## SPA + - ALSA refuses to change quantum and rate when in IRQ mode. + - ALSA will now be smarter in selecting the period size for batch devices + and will make it depend on the samplerate. (#3444) + - Vulkan DMA buf support was merged. + - ALSA latency will now be reported in the time domain of the graph. + - Add udev based autodetection for compress-offload devices. + - The echo-canceller was ported to webrtc-audio-processing-1. + - The v4l2 inotify code was rewritten to avoid a use-after-free and by + using a separate watch (but same fd) for each device. (#3439) + - The tag and latency handling was improved in audioadpter. + - Don't use -Ofast on alpha because it can crash on denormalized + values. (#3489) + - The mixers now synchronize spa_io_buffers updates with the data + thread to avoid crashes. + - Handle NULL param updates. (#3504) + +## Pulse-server + - Fix a regression in locating monitor sources by id. (#3476) + - Add support for use_system_clock_for_timing in module-pipe-sink. + - Add support for checking module arguments. + - Avoid some useless change events. + +## Bluetooth + - Ports are now marked as physical, which makes the bluetooth devices show + up as hardware devices in Ardour and other JACK apps. (#3418) + - Some fixes for LE audio support (#3479) + +## JACK + - Also emit unregister notify even when supressed when creating the + client. + - The notify callbacks now match JACK2 behaviour more. + - The mixer io areas are updated and handled safely now to avoid + crashes. (#3506) + +Older versions: + + # PipeWire 0.3.79 (2023-08-29) This is a quick bugfix release that is API and ABI compatible with previous @@ -53,9 +121,6 @@ - Improve property handling, support lists and ranges in addition to fixed values. (#3451) -Older versions: - - # PipeWire 0.3.78 (2023-08-22) This is a small bugfix release that is API and ABI compatible with previous
View file
pipewire-0.3.79.tar.gz/man/pipewire-pulse.1.rst.in -> pipewire-0.3.80.tar.gz/man/pipewire-pulse.1.rst.in
Changed
@@ -17,8 +17,8 @@ =========== **pipewire-pulse** starts a PulseAudio-compatible daemon that integrates with -the PipeWire media server. This daemon is a drop-in replacement for the -PulseAudio daemon. +the PipeWire media server, by running a pipewire process through a systemd +service. This daemon is a drop-in replacement for the PulseAudio daemon. OPTIONS =======
View file
pipewire-0.3.79.tar.gz/man/pipewire.conf.5.rst.in -> pipewire-0.3.80.tar.gz/man/pipewire.conf.5.rst.in
Changed
@@ -69,7 +69,7 @@ of use, a relaxed format may be used where: * ``:`` to delimit keys and values can be substuted by ``=`` or a space. - * ``"`` around keys and string can be omited as long as no special characters are used in the strings. + * ``"`` around keys and string can be omitted as long as no special characters are used in the strings. * ``,`` to separate objects can be replaced with a whitespace character. * ``#`` can be used to start a comment until the line end
View file
pipewire-0.3.79.tar.gz/man/pw-top.1.rst.in -> pipewire-0.3.80.tar.gz/man/pw-top.1.rst.in
Changed
@@ -155,11 +155,17 @@ -h | --help Show help. +-b | --batch-mode + Run in non-interactive batch mode, similar to top's batch mode. + +-n | --iterations=NUMBER + Exit after NUMBER of batch iterations. Only used in batch mode. + -r | --remote=NAME The name the *remote* instance to monitor. If left unspecified, a connection is made to the default PipeWire instance. ---version +-V | --version Show version information.
View file
pipewire-0.3.79.tar.gz/meson.build -> pipewire-0.3.80.tar.gz/meson.build
Changed
@@ -1,5 +1,5 @@ project('pipewire', 'c' , - version : '0.3.79', + version : '0.3.80', license : 'MIT', 'LGPL-2.1-or-later', 'GPL-2.0-only' , meson_version : '>= 0.61.1', default_options : 'warning_level=3', @@ -49,7 +49,9 @@ pipewire_confdatadir = pipewire_datadir / 'pipewire' modules_install_dir = pipewire_libdir / pipewire_name -if host_machine.system() == 'linux' +cc = meson.get_compiler('c') + +if cc.has_header('features.h') and cc.get_define('__GLIBC__', prefix: '#include <features.h>') != '' # glibc ld.so interprets ${LIB} in a library loading path with an # appropriate value for the current architecture, typically something # like lib, lib64 or lib/x86_64-linux-gnu. @@ -72,8 +74,6 @@ pkgconfig = import('pkgconfig') -cc = meson.get_compiler('c') - common_flags = '-fvisibility=hidden', '-fno-strict-aliasing', @@ -375,8 +375,8 @@ cdata.set('HAVE_GSTREAMER_DEVICE_PROVIDER', get_option('gstreamer-device-provider').allowed()) -webrtc_dep = dependency('webrtc-audio-processing', - version : '>= 0.2', '< 1.0', +webrtc_dep = dependency('webrtc-audio-processing-1', + version : '>= 1.2' , required : get_option('echo-cancel-webrtc')) summary({'WebRTC Echo Canceling': webrtc_dep.found()}, bool_yn: true, section: 'Misc dependencies') cdata.set('HAVE_WEBRTC', webrtc_dep.found())
View file
pipewire-0.3.79.tar.gz/pipewire-jack/src/pipewire-jack.c -> pipewire-0.3.80.tar.gz/pipewire-jack/src/pipewire-jack.c
Changed
@@ -247,6 +247,7 @@ struct spa_io_buffers io; struct spa_list mix; + uint32_t n_mix; struct mix *global_mix; struct port *tied; @@ -526,6 +527,27 @@ } +struct io_info { + struct mix *mix; + void *data; +}; + +static int +do_mix_set_io(struct spa_loop *loop, bool async, uint32_t seq, + const void *data, size_t size, void *user_data) +{ + struct io_info *info = user_data; + info->mix->io = info->data; + return 0; +} + +static inline void mix_set_io(struct mix *mix, void *data) +{ + struct io_info info = { .mix = mix, .data = data }; + pw_data_loop_invoke(mix->port->client->loop, + do_mix_set_io, SPA_ID_INVALID, NULL, 0, true, &info); +} + static void init_mix(struct mix *mix, uint32_t mix_id, struct port *port, uint32_t peer_id) { pw_log_debug("create %p mix:%d peer:%d", port, mix_id, peer_id); @@ -538,6 +560,8 @@ spa_list_init(&mix->queue); if (mix_id == SPA_ID_INVALID) port->global_mix = mix; + else if (port->n_mix++ == 0 && port->global_mix != NULL) + mix_set_io(port->global_mix, &port->io); } static struct mix *find_mix_peer(struct client *c, uint32_t peer_id) { @@ -618,10 +642,14 @@ static void free_mix(struct client *c, struct mix *mix) { + struct port *port = mix->port; + clear_buffers(c, mix); spa_list_remove(&mix->port_link); if (mix->id == SPA_ID_INVALID) - mix->port->global_mix = NULL; + port->global_mix = NULL; + else if (--port->n_mix == 0 && port->global_mix != NULL) + mix_set_io(port->global_mix, NULL); spa_list_remove(&mix->link); spa_list_append(&c->free_mix, &mix->link); } @@ -1108,9 +1136,12 @@ } pw_log_debug("%p: skip notify %08x active:%d emit:%d", c, type, c->active, emit); - if (o != NULL && arg1 == 0 && o->removing) { - o->removing = false; - free_object(c, o); + if (o != NULL) { + o->registered = arg1; + if (arg1 == 0 && o->removing) { + o->removing = false; + free_object(c, o); + } } return res; } @@ -1395,6 +1426,7 @@ void *ptr = NULL; struct buffer *b; struct spa_data *d; + struct spa_io_buffers *io; if (frames == 0 || !p->valid) return NULL; @@ -1402,20 +1434,21 @@ if (SPA_UNLIKELY((mix = p->global_mix) == NULL)) return NULL; - pw_log_trace_fp("%p: port %s %d get buffer %d n_buffers:%d", - c, p->object->port.name, p->port_id, frames, mix->n_buffers); + pw_log_trace_fp("%p: port %s %d get buffer %d n_buffers:%d io:%p", + c, p->object->port.name, p->port_id, frames, + mix->n_buffers, mix->io); - if (SPA_UNLIKELY(mix->n_buffers == 0)) + if (SPA_UNLIKELY((io = mix->io) == NULL)) return NULL; - if (p->io.status == SPA_STATUS_HAVE_DATA && - p->io.buffer_id < mix->n_buffers) { - b = &mix->buffersp->io.buffer_id; + if (io->status == SPA_STATUS_HAVE_DATA && + io->buffer_id < mix->n_buffers) { + b = &mix->buffersio->buffer_id; d = &b->datas0; } else { - if (p->io.buffer_id < mix->n_buffers) { - reuse_buffer(c, mix, p->io.buffer_id); - p->io.buffer_id = SPA_ID_INVALID; + if (io->buffer_id < mix->n_buffers) { + reuse_buffer(c, mix, io->buffer_id); + io->buffer_id = SPA_ID_INVALID; } if (SPA_UNLIKELY((b = dequeue_buffer(c, mix)) == NULL)) { pw_log_warn("port %p: out of buffers", p); @@ -1426,8 +1459,8 @@ d->chunk->size = frames * sizeof(float); d->chunk->stride = stride; - p->io.status = SPA_STATUS_HAVE_DATA; - p->io.buffer_id = b->id; + io->status = SPA_STATUS_HAVE_DATA; + io->buffer_id = b->id; } ptr = d->data; if (buf) @@ -1469,14 +1502,19 @@ static void prepare_output(struct port *p, uint32_t frames) { struct mix *mix; + struct spa_io_buffers *io; if (SPA_UNLIKELY(p->empty_out || p->tied)) process_empty(p, frames); + if (p->global_mix == NULL || (io = p->global_mix->io) == NULL) + return; + spa_list_for_each(mix, &p->mix, port_link) { if (SPA_LIKELY(mix->io != NULL)) - *mix->io = p->io; + *mix->io = *io; } + io->status = SPA_STATUS_NEED_DATA; } static void complete_process(struct client *c, uint32_t frames) @@ -1492,7 +1530,6 @@ if (!p->valid) continue; prepare_output(p, frames); - p->io.status = SPA_STATUS_NEED_DATA; } pw_array_for_each(item, &c->portsSPA_DIRECTION_INPUT.items) { if (pw_map_item_is_free(item)) @@ -2405,7 +2442,6 @@ if (param == NULL) return 0; - if ((res = spa_latency_parse(param, &info)) < 0) return res; @@ -2685,7 +2721,7 @@ switch (id) { case SPA_IO_Buffers: - mix->io = ptr; + mix_set_io(mix, ptr); break; default: break; @@ -2812,6 +2848,9 @@ mix = find_mix(c, p, mix_id); + pw_log_debug("%p: port %p mix:%d peer_id:%u info:%p", c, p, mix_id, + peer_id, props); + if (peer_id == SPA_ID_INVALID) { if (mix == NULL) { res = -ENOENT; @@ -3110,15 +3149,23 @@ pw_node_state_as_string(info->state), n->node.is_running); if (info->change_mask & PW_NODE_CHANGE_MASK_STATE) { - struct object *p; + struct object *p, *l; spa_list_for_each(p, &c->context.objects, link) { if (p->type != INTERFACE_Port || p->removed || p->port.node_id != info->id) continue; if (n->node.is_running) queue_notify(c, NOTIFY_TYPE_PORTREGISTRATION, p, 1, NULL); - else + else { + spa_list_for_each(l, &c->context.objects, link) { + if (l->type != INTERFACE_Link || l->removed || + (l->port_link.src_serial != p->serial && + l->port_link.dst_serial != p->serial)) + continue; + queue_notify(c, NOTIFY_TYPE_CONNECT, l, 0, NULL); + } queue_notify(c, NOTIFY_TYPE_PORTREGISTRATION, p, 0, NULL); + } } } } @@ -4141,7 +4188,11 @@ pw_thread_loop_lock(c->context.loop); freeze_callbacks(c); + /* reemit buffer_frames */ + c->buffer_frames = 0; + pw_data_loop_start(c->loop); + c->active = true; if ((res = do_activate(c)) < 0) goto done; @@ -4149,17 +4200,18 @@ c->activation->pending_new_pos = true; c->activation->pending_sync = true; - c->active = true; - spa_list_for_each(o, &c->context.objects, link) { if (o->type != INTERFACE_Port || o->port.port == NULL || o->port.port->client != c || !o->port.port->valid) continue; + o->registered = 0; queue_notify(c, NOTIFY_TYPE_PORTREGISTRATION, o, 1, NULL); } done: - if (res < 0) + if (res < 0) { + c->active = false; pw_data_loop_stop(c->loop); + } pw_log_debug("%p: activate result:%d", c, res); thaw_callbacks(c);
View file
pipewire-0.3.79.tar.gz/spa/include/spa/debug/log.h -> pipewire-0.3.80.tar.gz/spa/include/spa/debug/log.h
Changed
@@ -53,25 +53,32 @@ #define spa_debug_log_pod(l,lev,indent,info,pod) \ ({ \ - struct spa_debug_log_ctx c = SPA_LOG_DEBUG_INIT(l,lev); \ + struct spa_debug_log_ctx c = SPA_LOG_DEBUG_INIT(l,lev); \ if (SPA_UNLIKELY(spa_log_level_topic_enabled(c.log, c.topic, c.level))) \ spa_debugc_pod(&c.ctx, indent, info, pod); \ }) #define spa_debug_log_format(l,lev,indent,info,format) \ ({ \ - struct spa_debug_log_ctx c = SPA_LOG_DEBUG_INIT(l,lev); \ + struct spa_debug_log_ctx c = SPA_LOG_DEBUG_INIT(l,lev); \ if (SPA_UNLIKELY(spa_log_level_topic_enabled(c.log, c.topic, c.level))) \ spa_debugc_format(&c.ctx, indent, info, format); \ }) #define spa_debug_log_mem(l,lev,indent,data,len) \ ({ \ - struct spa_debug_log_ctx c = SPA_LOG_DEBUG_INIT(l,lev); \ + struct spa_debug_log_ctx c = SPA_LOG_DEBUG_INIT(l,lev); \ if (SPA_UNLIKELY(spa_log_level_topic_enabled(c.log, c.topic, c.level))) \ spa_debugc_mem(&c.ctx, indent, data, len); \ }) +#define spa_debug_log_dict(l,lev,indent,dict) \ +({ \ + struct spa_debug_log_ctx c = SPA_LOG_DEBUG_INIT(l,lev); \ + if (SPA_UNLIKELY(spa_log_level_topic_enabled(c.log, c.topic, c.level))) \ + spa_debugc_dict(&c.ctx, indent, dict); \ +}) + /** * \} */
View file
pipewire-0.3.79.tar.gz/spa/include/spa/node/keys.h -> pipewire-0.3.80.tar.gz/spa/include/spa/node/keys.h
Changed
@@ -16,6 +16,8 @@ /** node keys */ #define SPA_KEY_NODE_NAME "node.name" /**< a node name */ +#define SPA_KEY_NODE_DESCRIPTION "node.description" /**< localized human readable node one-line + * description. Ex. "Foobar USB Headset" */ #define SPA_KEY_NODE_LATENCY "node.latency" /**< the requested node latency */ #define SPA_KEY_NODE_MAX_LATENCY "node.max-latency" /**< maximum supported latency */
View file
pipewire-0.3.79.tar.gz/spa/include/spa/node/node.h -> pipewire-0.3.80.tar.gz/spa/include/spa/node/node.h
Changed
@@ -485,7 +485,9 @@ * * When \a param is NULL, the parameter will be unset. * - * This function must be called from the main thread. + * This function must be called from the main thread. The node muse be paused + * or the port SPA_IO_Buffers area is NULL when this function is called with + * a param that changes the processing state (like a format change). * * \param node a struct \ref spa_node * \param direction a enum \ref spa_direction @@ -540,7 +542,8 @@ * When this function returns async, use the spa_node_sync operation to * wait for completion. * - * This function must be called from the main thread. + * This function must be called from the main thread. The node muse be paused + * or the port SPA_IO_Buffers area is NULL when this function is called. * * \param object an object implementing the interface * \param direction a port direction @@ -566,6 +569,11 @@ * * This function must be called from the main thread. * + * This function can be called when the node is running and the node + * must be prepared to handle changes in io areas while running. This + * is normally done by synchronizing the port io updates with the + * data processing loop. + * * \param direction a spa_direction * \param port_id a port id * \param id the id of the io area, the available ids can be
View file
pipewire-0.3.79.tar.gz/spa/include/spa/param/param-types.h -> pipewire-0.3.80.tar.gz/spa/include/spa/param/param-types.h
Changed
@@ -40,6 +40,7 @@ { SPA_PARAM_Control, SPA_TYPE_Sequence, SPA_TYPE_INFO_PARAM_ID_BASE "Control", NULL }, { SPA_PARAM_Latency, SPA_TYPE_OBJECT_ParamLatency, SPA_TYPE_INFO_PARAM_ID_BASE "Latency", NULL }, { SPA_PARAM_ProcessLatency, SPA_TYPE_OBJECT_ParamProcessLatency, SPA_TYPE_INFO_PARAM_ID_BASE "ProcessLatency", NULL }, + { SPA_PARAM_Tag, SPA_TYPE_OBJECT_ParamTag, SPA_TYPE_INFO_PARAM_ID_BASE "Tag", NULL }, { 0, 0, NULL, NULL }, };
View file
pipewire-0.3.79.tar.gz/spa/include/spa/param/param.h -> pipewire-0.3.80.tar.gz/spa/include/spa/param/param.h
Changed
@@ -39,6 +39,7 @@ SPA_PARAM_Control, /**< Control parameter, a SPA_TYPE_Sequence */ SPA_PARAM_Latency, /**< latency reporting, a SPA_TYPE_OBJECT_ParamLatency */ SPA_PARAM_ProcessLatency, /**< processing latency, a SPA_TYPE_OBJECT_ParamProcessLatency */ + SPA_PARAM_Tag, /**< tag reporting, a SPA_TYPE_OBJECT_ParamTag. Since 0.3.79 */ }; /** information about a parameter */
View file
pipewire-0.3.80.tar.gz/spa/include/spa/param/tag-types.h
Added
@@ -0,0 +1,39 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2018 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_PARAM_TAG_TYPES_H +#define SPA_PARAM_TAG_TYPES_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \addtogroup spa_param + * \{ + */ + +#include <spa/utils/enum-types.h> +#include <spa/param/param-types.h> +#include <spa/param/tag.h> + +#define SPA_TYPE_INFO_PARAM_Tag SPA_TYPE_INFO_PARAM_BASE "Tag" +#define SPA_TYPE_INFO_PARAM_TAG_BASE SPA_TYPE_INFO_PARAM_Tag ":" + +static const struct spa_type_info spa_type_param_tag = { + { SPA_PARAM_TAG_START, SPA_TYPE_Id, SPA_TYPE_INFO_PARAM_TAG_BASE, spa_type_param, }, + { SPA_PARAM_TAG_direction, SPA_TYPE_Id, SPA_TYPE_INFO_PARAM_TAG_BASE "direction", spa_type_direction, }, + { SPA_PARAM_TAG_info, SPA_TYPE_Struct, SPA_TYPE_INFO_PARAM_TAG_BASE "info", NULL, }, + { 0, 0, NULL, NULL }, +}; + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_PARAM_TAG_TYPES_H */
View file
pipewire-0.3.80.tar.gz/spa/include/spa/param/tag-utils.h
Added
@@ -0,0 +1,143 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2023 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_PARAM_TAG_UTILS_H +#define SPA_PARAM_TAG_UTILS_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \addtogroup spa_param + * \{ + */ + +#include <float.h> + +#include <spa/utils/dict.h> +#include <spa/pod/builder.h> +#include <spa/pod/parser.h> +#include <spa/param/tag.h> + +static inline int +spa_tag_compare(const struct spa_pod *a, const struct spa_pod *b) +{ + return ((a == b) || (a && b && SPA_POD_SIZE(a) == SPA_POD_SIZE(b) && + memcmp(a, b, SPA_POD_SIZE(b)) == 0)) ? 0 : 1; +} + +static inline int +spa_tag_parse(const struct spa_pod *tag, struct spa_tag_info *info, void **state) +{ + int res; + const struct spa_pod_object *obj = (const struct spa_pod_object*)tag; + const struct spa_pod_prop *first, *start, *cur; + + spa_zero(*info); + + if ((res = spa_pod_parse_object(tag, + SPA_TYPE_OBJECT_ParamTag, NULL, + SPA_PARAM_TAG_direction, SPA_POD_Id(&info->direction))) < 0) + return res; + + first = spa_pod_prop_first(&obj->body); + start = *state ? spa_pod_prop_next((struct spa_pod_prop*)*state) : first; + + res = 0; + for (cur = start; spa_pod_prop_is_inside(&obj->body, obj->pod.size, cur); + cur = spa_pod_prop_next(cur)) { + if (cur->key == SPA_PARAM_TAG_info) { + info->info = &cur->value; + *state = (void*)cur; + return 1; + } + } + return 0; +} + +static inline int +spa_tag_info_parse(const struct spa_tag_info *info, struct spa_dict *dict, struct spa_dict_item *items) +{ + struct spa_pod_parser prs; + uint32_t n, n_items; + const char *key, *value; + struct spa_pod_frame f1; + + spa_pod_parser_pod(&prs, info->info); + if (spa_pod_parser_push_struct(&prs, &f0) < 0 || + spa_pod_parser_get_int(&prs, (int32_t*)&n_items) < 0) + return -EINVAL; + + if (items == NULL) { + dict->n_items = n_items; + return 0; + } + n_items = SPA_MIN(dict->n_items, n_items); + + for (n = 0; n < n_items; n++) { + if (spa_pod_parser_get(&prs, + SPA_POD_String(&key), + SPA_POD_String(&value), + NULL) < 0) + break; + itemsn.key = key; + itemsn.value = value; + } + dict->items = items; + spa_pod_parser_pop(&prs, &f0); + return 0; +} + +static inline void +spa_tag_build_start(struct spa_pod_builder *builder, struct spa_pod_frame *f, + uint32_t id, enum spa_direction direction) +{ + spa_pod_builder_push_object(builder, f, SPA_TYPE_OBJECT_ParamTag, id); + spa_pod_builder_add(builder, + SPA_PARAM_TAG_direction, SPA_POD_Id(direction), + 0); +} + +static inline void +spa_tag_build_add_info(struct spa_pod_builder *builder, const struct spa_pod *info) +{ + spa_pod_builder_add(builder, + SPA_PARAM_TAG_info, SPA_POD_Pod(info), + 0); +} + +static inline void +spa_tag_build_add_dict(struct spa_pod_builder *builder, const struct spa_dict *dict) +{ + uint32_t i, n_items; + struct spa_pod_frame f; + + n_items = dict ? dict->n_items : 0; + + spa_pod_builder_prop(builder, SPA_PARAM_TAG_info, SPA_POD_PROP_FLAG_HINT_DICT); + spa_pod_builder_push_struct(builder, &f); + spa_pod_builder_int(builder, n_items); + for (i = 0; i < n_items; i++) { + spa_pod_builder_string(builder, dict->itemsi.key); + spa_pod_builder_string(builder, dict->itemsi.value); + } + spa_pod_builder_pop(builder, &f); +} + +static inline struct spa_pod * +spa_tag_build_end(struct spa_pod_builder *builder, struct spa_pod_frame *f) +{ + return (struct spa_pod*)spa_pod_builder_pop(builder, f); +} + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_PARAM_TAG_UTILS_H */
View file
pipewire-0.3.80.tar.gz/spa/include/spa/param/tag.h
Added
@@ -0,0 +1,46 @@ +/* Simple Plugin API */ +/* SPDX-FileCopyrightText: Copyright © 2023 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#ifndef SPA_PARAM_TAG_H +#define SPA_PARAM_TAG_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \addtogroup spa_param + * \{ + */ + +#include <spa/param/param.h> + +/** properties for SPA_TYPE_OBJECT_ParamTag */ +enum spa_param_tag { + SPA_PARAM_TAG_START, + SPA_PARAM_TAG_direction, /**< direction, input/output (Id enum spa_direction) */ + SPA_PARAM_TAG_info, /**< Struct( + * Int: n_items + * (String: key + * String: value)* + * ) */ +}; + +/** helper structure for managing tag objects */ +struct spa_tag_info { + enum spa_direction direction; + const struct spa_pod *info; +}; + +#define SPA_TAG_INFO(dir,...) ((struct spa_tag_info) { .direction = (dir), ## __VA_ARGS__ }) + +/** + * \} + */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_PARAM_TAG_H */
View file
pipewire-0.3.79.tar.gz/spa/include/spa/param/type-info.h -> pipewire-0.3.80.tar.gz/spa/include/spa/param/type-info.h
Changed
@@ -14,5 +14,6 @@ #include <spa/param/profiler-types.h> #include <spa/param/profile-types.h> #include <spa/param/route-types.h> +#include <spa/param/tag-types.h> #endif /* SPA_PARAM_TYPE_INFO_H */
View file
pipewire-0.3.79.tar.gz/spa/include/spa/utils/names.h -> pipewire-0.3.80.tar.gz/spa/include/spa/utils/names.h
Changed
@@ -90,8 +90,10 @@ #define SPA_NAME_API_ALSA_SEQ_BRIDGE "api.alsa.seq.bridge" /**< an alsa Node interface for * bridging midi ports */ #define SPA_NAME_API_ALSA_ACP_DEVICE "api.alsa.acp.device" /**< an alsa ACP Device interface */ -#define SPA_NAME_API_ALSA_COMPRESS_OFFLOAD_SINK "api.alsa.compress.offload.sink" /**< an alsa Node interface for - * compressed audio */ +#define SPA_NAME_API_ALSA_COMPRESS_OFFLOAD_DEVICE "api.alsa.compress.offload.device" /**< an alsa Device interface for + * compressed audio */ +#define SPA_NAME_API_ALSA_COMPRESS_OFFLOAD_SINK "api.alsa.compress.offload.sink" /**< an alsa Node interface for + * compressed audio */ /** keys for bluez5 factory names */ #define SPA_NAME_API_BLUEZ5_ENUM_DBUS "api.bluez5.enum.dbus" /**< a dbus Device interface */
View file
pipewire-0.3.79.tar.gz/spa/include/spa/utils/ratelimit.h -> pipewire-0.3.80.tar.gz/spa/include/spa/utils/ratelimit.h
Changed
@@ -17,23 +17,23 @@ uint64_t begin; unsigned burst; unsigned n_printed; - unsigned n_missed; + unsigned n_suppressed; }; static inline int spa_ratelimit_test(struct spa_ratelimit *r, uint64_t now) { - unsigned missed = 0; + unsigned suppressed = 0; if (r->begin + r->interval < now) { - missed = r->n_missed; + suppressed = r->n_suppressed; r->begin = now; r->n_printed = 0; - r->n_missed = 0; + r->n_suppressed = 0; } else if (r->n_printed >= r->burst) { - r->n_missed++; + r->n_suppressed++; return -1; } r->n_printed++; - return missed; + return suppressed; } #ifdef __cplusplus
View file
pipewire-0.3.79.tar.gz/spa/include/spa/utils/type-info.h -> pipewire-0.3.80.tar.gz/spa/include/spa/utils/type-info.h
Changed
@@ -83,6 +83,7 @@ { SPA_TYPE_OBJECT_Profiler, SPA_TYPE_Object, SPA_TYPE_INFO_Profiler, spa_type_profiler }, { SPA_TYPE_OBJECT_ParamLatency, SPA_TYPE_Object, SPA_TYPE_INFO_PARAM_Latency, spa_type_param_latency }, { SPA_TYPE_OBJECT_ParamProcessLatency, SPA_TYPE_Object, SPA_TYPE_INFO_PARAM_ProcessLatency, spa_type_param_process_latency }, + { SPA_TYPE_OBJECT_ParamTag, SPA_TYPE_Object, SPA_TYPE_INFO_PARAM_Tag, spa_type_param_tag }, { 0, 0, NULL, NULL } };
View file
pipewire-0.3.79.tar.gz/spa/include/spa/utils/type.h -> pipewire-0.3.80.tar.gz/spa/include/spa/utils/type.h
Changed
@@ -78,6 +78,7 @@ SPA_TYPE_OBJECT_Profiler, SPA_TYPE_OBJECT_ParamLatency, SPA_TYPE_OBJECT_ParamProcessLatency, + SPA_TYPE_OBJECT_ParamTag, _SPA_TYPE_OBJECT_LAST, /**< not part of ABI */ /* vendor extensions */
View file
pipewire-0.3.79.tar.gz/spa/plugins/aec/aec-webrtc.cpp -> pipewire-0.3.80.tar.gz/spa/plugins/aec/aec-webrtc.cpp
Changed
@@ -13,9 +13,7 @@ #include <spa/utils/json.h> #include <spa/support/plugin.h> -#include <webrtc/modules/audio_processing/include/audio_processing.h> -#include <webrtc/modules/interface/module_common_types.h> -#include <webrtc/system_wrappers/include/trace.h> +#include <modules/audio_processing/include/audio_processing.h> struct impl_data { struct spa_handle handle; @@ -41,53 +39,6 @@ return default_value; } - -/* f0 f1 f2 */ -static int parse_point(struct spa_json *it, float (&f)3) -{ - struct spa_json arr; - int i, res; - - if (spa_json_enter_array(it, &arr) <= 0) - return -EINVAL; - - for (i = 0; i < 3; i++) { - if ((res = spa_json_get_float(&arr, &fi)) <= 0) - return -EINVAL; - } - return 0; -} - -/* point1 point2 ... */ -static int parse_mic_geometry(struct impl_data *impl, const char *mic_geometry, - std::vector<webrtc::Point>& geometry) -{ - int res; - size_t i; - struct spa_json it2; - - spa_json_init(&it0, mic_geometry, strlen(mic_geometry)); - if (spa_json_enter_array(&it0, &it1) <= 0) { - spa_log_error(impl->log, "Error: webrtc.mic-geometry expects an array"); - return -EINVAL; - } - - for (i = 0; i < geometry.size(); i++) { - float f3; - - if ((res = parse_point(&it1, f)) < 0) { - spa_log_error(impl->log, "Error: can't parse webrtc.mic-geometry points: %d", res); - return res; - } - - spa_log_info(impl->log, "mic %zd position: (%g %g %g)", i, f0, f1, f2); - geometryi.c0 = f0; - geometryi.c1 = f1; - geometryi.c2 = f2; - } - return 0; -} - static int webrtc_init2(void *object, const struct spa_dict *args, struct spa_audio_info_raw *rec_info, struct spa_audio_info_raw *out_info, struct spa_audio_info_raw *play_info) @@ -95,69 +46,33 @@ auto impl = static_cast<struct impl_data*>(object); int res; - bool extended_filter = webrtc_get_spa_bool(args, "webrtc.extended_filter", true); - bool delay_agnostic = webrtc_get_spa_bool(args, "webrtc.delay_agnostic", true); bool high_pass_filter = webrtc_get_spa_bool(args, "webrtc.high_pass_filter", true); bool noise_suppression = webrtc_get_spa_bool(args, "webrtc.noise_suppression", true); + bool transient_suppression = webrtc_get_spa_bool(args, "webrtc.transient_suppression", true); bool voice_detection = webrtc_get_spa_bool(args, "webrtc.voice_detection", true); // Note: AGC seems to mess up with Agnostic Delay Detection, especially with speech, // result in very poor performance, disable by default bool gain_control = webrtc_get_spa_bool(args, "webrtc.gain_control", false); - // Disable experimental flags by default - bool experimental_agc = webrtc_get_spa_bool(args, "webrtc.experimental_agc", false); - bool experimental_ns = webrtc_get_spa_bool(args, "webrtc.experimental_ns", false); - - bool beamforming = webrtc_get_spa_bool(args, "webrtc.beamforming", false); - // FIXME: Intelligibility enhancer is not currently supported // This filter will modify playback buffer (when calling ProcessReverseStream), but now // playback buffer modifications are discarded. - webrtc::Config config; - config.Set<webrtc::ExtendedFilter>(new webrtc::ExtendedFilter(extended_filter)); - config.Set<webrtc::DelayAgnostic>(new webrtc::DelayAgnostic(delay_agnostic)); - config.Set<webrtc::ExperimentalAgc>(new webrtc::ExperimentalAgc(experimental_agc)); - config.Set<webrtc::ExperimentalNs>(new webrtc::ExperimentalNs(experimental_ns)); - - if (beamforming) { - std::vector<webrtc::Point> geometry(rec_info->channels); - const char *mic_geometry, *target_direction; - - /* The beamformer gives a single mono channel */ - out_info->channels = 1; - out_info->position0 = SPA_AUDIO_CHANNEL_MONO; - - if ((mic_geometry = spa_dict_lookup(args, "webrtc.mic-geometry")) == NULL) { - spa_log_error(impl->log, "Error: webrtc.beamforming requires webrtc.mic-geometry"); - return -EINVAL; - } - - if ((res = parse_mic_geometry(impl, mic_geometry, geometry)) < 0) - return res; - - if ((target_direction = spa_dict_lookup(args, "webrtc.target-direction")) != NULL) { - webrtc::SphericalPointf direction(0.0f, 0.0f, 0.0f); - struct spa_json it; - float f3; - - spa_json_init(&it, target_direction, strlen(target_direction)); - if (parse_point(&it, f) < 0) { - spa_log_error(impl->log, "Error: can't parse target-direction %s", - target_direction); - return -EINVAL; - } - - direction.s0 = f0; - direction.s1 = f1; - direction.s2 = f2; - - config.Set<webrtc::Beamforming>(new webrtc::Beamforming(true, geometry, direction)); - } else { - config.Set<webrtc::Beamforming>(new webrtc::Beamforming(true, geometry)); - } - } + webrtc::AudioProcessing::Config config; + config.echo_canceller.enabled = true; + // FIXME: Example code enables both gain controllers, but that seems sus + config.gain_controller1.enabled = gain_control; + config.gain_controller1.mode = webrtc::AudioProcessing::Config::GainController1::Mode::kAdaptiveDigital; + config.gain_controller1.analog_level_minimum = 0; + config.gain_controller1.analog_level_maximum = 255; + config.gain_controller2.enabled = gain_control; + config.high_pass_filter.enabled = high_pass_filter; + config.noise_suppression.enabled = noise_suppression; + config.noise_suppression.level = webrtc::AudioProcessing::Config::NoiseSuppression::kHigh; + // FIXME: expose pre/postamp gain + config.transient_suppression.enabled = transient_suppression; + config.voice_detection.enabled = voice_detection; webrtc::ProcessingConfig pconfig = {{ webrtc::StreamConfig(rec_info->rate, rec_info->channels, false), /* input stream */ @@ -166,26 +81,15 @@ webrtc::StreamConfig(play_info->rate, play_info->channels, false), /* reverse output stream */ }}; - auto apm = std::unique_ptr<webrtc::AudioProcessing>(webrtc::AudioProcessing::Create(config)); + auto apm = std::unique_ptr<webrtc::AudioProcessing>(webrtc::AudioProcessingBuilder().Create()); + + apm->ApplyConfig(config); + if ((res = apm->Initialize(pconfig)) != webrtc::AudioProcessing::kNoError) { spa_log_error(impl->log, "Error initialising webrtc audio processing module: %d", res); return -EINVAL; } - apm->high_pass_filter()->Enable(high_pass_filter); - // Always disable drift compensation since PipeWire will already do - // drift compensation on all sinks and sources linked to this echo-canceler - apm->echo_cancellation()->enable_drift_compensation(false); - apm->echo_cancellation()->Enable(true); - // TODO: wire up supression levels to args - apm->echo_cancellation()->set_suppression_level(webrtc::EchoCancellation::kHighSuppression); - apm->noise_suppression()->set_level(webrtc::NoiseSuppression::kHigh); - apm->noise_suppression()->Enable(noise_suppression); - apm->voice_detection()->Enable(voice_detection); - // TODO: wire up AGC parameters to args - apm->gain_control()->set_analog_level_limits(0, 255); - apm->gain_control()->set_mode(webrtc::GainControl::kAdaptiveDigital); - apm->gain_control()->Enable(gain_control); impl->apm = std::move(apm); impl->rec_info = *rec_info; impl->out_info = *out_info;
View file
pipewire-0.3.79.tar.gz/spa/plugins/alsa/acp/volume.h -> pipewire-0.3.80.tar.gz/spa/plugins/alsa/acp/volume.h
Changed
@@ -83,7 +83,7 @@ static inline pa_volume_t pa_sw_volume_from_dB(double dB) { - if (isinf(dB) < 0 || dB <= PA_DECIBEL_MININFTY) + if (dB == -INFINITY || dB <= PA_DECIBEL_MININFTY) return PA_VOLUME_MUTED; return pa_sw_volume_from_linear(pa_volume_dB_to_linear(dB)); }
View file
pipewire-0.3.80.tar.gz/spa/plugins/alsa/alsa-compress-offload-device.c
Added
@@ -0,0 +1,578 @@ +/* Spa ALSA Compress-Offload device */ +/* SPDX-FileCopyrightText: Copyright @ 2023 Carlos Rafael Giani */ +/* SPDX-License-Identifier: MIT */ + +#include <sys/types.h> +#include <limits.h> +#include <dirent.h> + +#include <alsa/asoundlib.h> + +#include <spa/debug/dict.h> +#include <spa/debug/log.h> +#include <spa/debug/pod.h> +#include <spa/node/node.h> +#include <spa/support/plugin.h> +#include <spa/monitor/device.h> +#include <spa/monitor/utils.h> +#include <spa/node/keys.h> +#include <spa/param/param.h> +#include <spa/pod/filter.h> +#include <spa/pod/parser.h> +#include <spa/utils/cleanup.h> +#include <spa/utils/dict.h> +#include <spa/utils/keys.h> +#include <spa/utils/names.h> +#include <spa/utils/string.h> + +#include "compress-offload-api-util.h" +#include "alsa.h" + +static const char default_device = "hw:0"; + +struct props { + char device64; + unsigned int card_nr; +}; + +static void reset_props(struct props *props) +{ + strncpy(props->device, default_device, 64); + props->card_nr = 0; +} + +struct impl { + struct spa_handle handle; + struct spa_device device; + + struct spa_log *log; + + struct spa_hook_list hooks; + + struct props props; + uint32_t n_nodes; + uint32_t n_capture; + uint32_t n_playback; + + uint32_t profile; +}; + +#define ADD_DICT_ITEM(key, value) do { itemsn_items++ = SPA_DICT_ITEM_INIT(key, value); } while (0) + +static void emit_node(struct impl *this, const char *device_node, unsigned int device_nr, + enum spa_compress_offload_direction direction, snd_ctl_card_info_t *cardinfo, + uint32_t id) +{ + struct spa_dict_item items5; + uint32_t n_items = 0; + char alsa_path128, path180; + char node_name200; + char node_desc200; + struct spa_device_object_info info; + const char *stream; + + spa_log_debug(this->log, "emitting node info for device %s (card nr %u device nr %u)", + device_node, this->props.card_nr, device_nr); + + info = SPA_DEVICE_OBJECT_INFO_INIT(); + info.type = SPA_TYPE_INTERFACE_Node; + + if (direction == SPA_COMPRESS_OFFLOAD_DIRECTION_PLAYBACK) { + stream = "playback"; + info.factory_name = SPA_NAME_API_ALSA_COMPRESS_OFFLOAD_SINK; + } else { + stream = "capture"; + /* TODO: This is not yet implemented, because getting Compress-Offload + * hardware that can capture audio is difficult to do. The only hardware + * known is the Wolfson ADSP; the only driver in the kernel that exposes + * Compress-Offload capture devices is the one for that hardware. */ + assert(false); + } + + info.change_mask = SPA_DEVICE_OBJECT_CHANGE_MASK_PROPS; + + snprintf(alsa_path, sizeof(alsa_path), "%s,%u", this->props.device, device_nr); + snprintf(path, sizeof(path), "alsa:compressed:%s:%u:%s", snd_ctl_card_info_get_id(cardinfo), device_nr, stream); + snprintf(node_name, sizeof(node_name), "comprC%uD%u", this->props.card_nr, device_nr); + snprintf(node_desc, sizeof(node_desc), "Compress-Offload sink node (ALSA card %u device %u)", this->props.card_nr, device_nr); + + ADD_DICT_ITEM(SPA_KEY_NODE_NAME, node_name); + ADD_DICT_ITEM(SPA_KEY_NODE_DESCRIPTION, node_desc); + ADD_DICT_ITEM(SPA_KEY_OBJECT_PATH, path); + ADD_DICT_ITEM(SPA_KEY_API_ALSA_PATH, alsa_path); + /* NOTE: Set alsa.name, since session managers look for this, or for + * SPA_KEY_API_ALSA_PCM_NAME, or other items. The best fit in this + * case seems to be alsa.name, since SPA_KEY_API_ALSA_PCM_NAME is + * PCM specific, as the name suggests. If none of these items are + * provided, session managers may not work properly. WirePlumber's + * alsa.lua script looks for these for example. + * And, since we have no good way of getting a name, just reuse + * the alsa_path here. */ + ADD_DICT_ITEM("alsa.name", alsa_path); + + info.props = &SPA_DICT_INIT(items, n_items); + + spa_log_debug(this->log, "node information:"); + spa_debug_dict(2, info.props); + + spa_device_emit_object_info(&this->hooks, id, &info); +} + +static int set_profile(struct impl *this, uint32_t id) +{ + int ret = 0; + uint32_t i, n_cap, n_play; + char prefix32; + int prefix_length; + struct dirent *entry; + DIR *snd_dir = NULL; + snd_ctl_t *ctl_handle = NULL; + snd_ctl_card_info_t *cardinfo; + + spa_log_debug(this->log, "enumerate Compress-Offload nodes for card %s; profile: %d", + this->props.device, id); + + if ((ret = snd_ctl_open(&ctl_handle, this->props.device, 0)) < 0) { + spa_log_error(this->log, "can't open control for card %s: %s", + this->props.device, snd_strerror(ret)); + goto finish; + } + + this->profile = id; + + snd_ctl_card_info_alloca(&cardinfo); + if ((ret = snd_ctl_card_info(ctl_handle, cardinfo)) < 0) { + spa_log_error(this->log, "error card info: %s", snd_strerror(ret)); + goto finish; + } + + /* Clear any previous node object info. */ + for (i = 0; i < this->n_nodes; i++) + spa_device_emit_object_info(&this->hooks, i, NULL); + + this->n_nodes = this->n_capture = this->n_playback = 0; + + /* Profile ID 0 is the "off" profile, that is, the profile where the device + * is "disabled". To implement such a disabled state, simply exit here without + * adding any nodes after we removed any existing one (see above). */ + if (id == 0) + { + spa_log_debug(this->log, "\"Off\" profile selected - exiting without " + "creating any nodes after all previous ones were removed"); + goto finish; + } + + spa_scnprintf(prefix, sizeof(prefix), "comprC%uD", this->props.card_nr); + prefix_length = strlen(prefix); + + /* There is no API to enumerate all Compress-Offload devices, so we have + * to stick to walking through the /dev/snd directory entries and looking + * for device nodes that match the comprC<card number>D prefix. */ + snd_dir = opendir("/dev/snd"); + if (snd_dir == NULL) + goto errno_error; + + i = 0; + i = n_cap = n_play = 0; + while ((errno = 0, entry = readdir(snd_dir)) != NULL) { + long long device_nr; + enum spa_compress_offload_direction direction; + + if (!(entry->d_type == DT_CHR && spa_strstartswith(entry->d_name, prefix))) + continue; + + /* Parse the device number from the device filename. We know that the filename + * is always structured like this: comprC<card number>D<device number> + * We consider "comprC<card number>D" to form the "prefix" here. Right after + * that prefix, the device number can be parsed, so skip the prefix. */ + device_nr = strtol(entry->d_name + prefix_length, NULL, 10); + if ((device_nr < 0) || (device_nr > UINT_MAX)) { + spa_log_warn(this->log, "device %s contains unusable device number; " + "skipping", entry->d_name); + continue; + } + + if (get_compress_offload_device_direction(this->props.card_nr, device_nr, + this->log, &direction) < 0) + goto finish; + + switch (direction) { + case SPA_COMPRESS_OFFLOAD_DIRECTION_PLAYBACK: + n_play++; + emit_node(this, entry->d_name, device_nr, direction, cardinfo, i++); + break; + case SPA_COMPRESS_OFFLOAD_DIRECTION_CAPTURE: + /* TODO: Disabled for now. See the TODO in emit_node() for details. */ +#if 0 + n_cap++; + emit_node(this, entry->d_name, device_nr, direction, cardinfo, i++); +#endif + break; + } + } + + this->n_capture = n_cap; + this->n_playback = n_play; + this->n_nodes = i; + +finish: + if (snd_dir != NULL) + closedir(snd_dir); + if (ctl_handle != NULL) + snd_ctl_close(ctl_handle); + return ret; + +errno_error: + ret = -errno; + goto finish; +} + +static int emit_info(struct impl *this, bool full) +{ + int err = 0; + struct spa_dict_item items20; + uint32_t n_items = 0; + snd_ctl_t *ctl_hndl; + snd_ctl_card_info_t *info; + struct spa_device_info dinfo; + struct spa_param_info params2; + char path128; + char device_name200; + char device_desc200; + + spa_log_debug(this->log, "open card %s", this->props.device); + if ((err = snd_ctl_open(&ctl_hndl, this->props.device, 0)) < 0) { + spa_log_error(this->log, "can't open control for card %s: %s", + this->props.device, snd_strerror(err)); + return err; + } + + snd_ctl_card_info_alloca(&info); + if ((err = snd_ctl_card_info(ctl_hndl, info)) < 0) { + spa_log_error(this->log, "error hardware info: %s", snd_strerror(err)); + goto finish; + } + + dinfo = SPA_DEVICE_INFO_INIT(); + + dinfo.change_mask = SPA_DEVICE_CHANGE_MASK_PROPS; + + snprintf(path, sizeof(path), "alsa:compressed:%s", snd_ctl_card_info_get_id(info)); + snprintf(device_name, sizeof(device_name), "comprC%u", this->props.card_nr); + snprintf(device_desc, sizeof(device_desc), "Compress-Offload device (ALSA card %u)", this->props.card_nr); + + ADD_DICT_ITEM(SPA_KEY_OBJECT_PATH, path); + ADD_DICT_ITEM(SPA_KEY_DEVICE_API, "alsa:compressed"); + ADD_DICT_ITEM(SPA_KEY_DEVICE_NICK, "alsa:compressed"); + ADD_DICT_ITEM(SPA_KEY_DEVICE_NAME, device_name); + ADD_DICT_ITEM(SPA_KEY_DEVICE_DESCRIPTION, device_desc); + ADD_DICT_ITEM(SPA_KEY_MEDIA_CLASS, "Audio/Device"); + ADD_DICT_ITEM(SPA_KEY_API_ALSA_PATH, (char *)this->props.device); + ADD_DICT_ITEM(SPA_KEY_API_ALSA_CARD_ID, snd_ctl_card_info_get_id(info)); + ADD_DICT_ITEM(SPA_KEY_API_ALSA_CARD_COMPONENTS, snd_ctl_card_info_get_components(info)); + ADD_DICT_ITEM(SPA_KEY_API_ALSA_CARD_DRIVER, snd_ctl_card_info_get_driver(info)); + ADD_DICT_ITEM(SPA_KEY_API_ALSA_CARD_NAME, snd_ctl_card_info_get_name(info)); + ADD_DICT_ITEM(SPA_KEY_API_ALSA_CARD_LONGNAME, snd_ctl_card_info_get_longname(info)); + ADD_DICT_ITEM(SPA_KEY_API_ALSA_CARD_MIXERNAME, snd_ctl_card_info_get_mixername(info)); + + dinfo.props = &SPA_DICT_INIT(items, n_items); + + dinfo.change_mask |= SPA_DEVICE_CHANGE_MASK_PARAMS; + params0 = SPA_PARAM_INFO(SPA_PARAM_EnumProfile, SPA_PARAM_INFO_READ); + params1 = SPA_PARAM_INFO(SPA_PARAM_Profile, SPA_PARAM_INFO_READWRITE); + dinfo.n_params = SPA_N_ELEMENTS(params); + dinfo.params = params; + + spa_device_emit_info(&this->hooks, &dinfo); + +finish: + spa_log_debug(this->log, "close card %s", this->props.device); + snd_ctl_close(ctl_hndl); + return err; +} + +static int impl_add_listener(void *object, + struct spa_hook *listener, + const struct spa_device_events *events, + void *data) +{ + struct impl *this = object; + struct spa_hook_list save; + + spa_return_val_if_fail(this != NULL, -EINVAL); + spa_return_val_if_fail(events != NULL, -EINVAL); + + spa_hook_list_isolate(&this->hooks, &save, listener, events, data); + + if (events->info || events->object_info) + emit_info(this, true); + + spa_hook_list_join(&this->hooks, &save); + + return 0; +} + +static int impl_sync(void *object, int seq) +{ + struct impl *this = object; + + spa_return_val_if_fail(this != NULL, -EINVAL); + + spa_device_emit_result(&this->hooks, seq, 0, 0, NULL); + + return 0; +} + +static struct spa_pod *build_profile(struct impl *this, struct spa_pod_builder *b, + uint32_t id, uint32_t index) +{ + struct spa_pod_frame f2; + const char *name, *desc; + + switch (index) { + case 0: + name = "off"; + desc = "Off"; + break; + case 1: + name = "on"; + desc = "On"; + break; + default: + errno = EINVAL; + return NULL; + } + + spa_pod_builder_push_object(b, &f0, SPA_TYPE_OBJECT_ParamProfile, id); + spa_pod_builder_add(b, + SPA_PARAM_PROFILE_index, SPA_POD_Int(index), + SPA_PARAM_PROFILE_name, SPA_POD_String(name), + SPA_PARAM_PROFILE_description, SPA_POD_String(desc), + 0); + if (index == 1) { + spa_pod_builder_prop(b, SPA_PARAM_PROFILE_classes, 0); + spa_pod_builder_push_struct(b, &f1); + if (this->n_capture) { + spa_pod_builder_add_struct(b, + SPA_POD_String("Audio/Source"), + SPA_POD_Int(this->n_capture)); + } + if (this->n_playback) { + spa_pod_builder_add_struct(b, + SPA_POD_String("Audio/Sink"), + SPA_POD_Int(this->n_playback)); + } + spa_pod_builder_pop(b, &f1); + } + return spa_pod_builder_pop(b, &f0); + +} + +static int impl_enum_params(void *object, int seq, + uint32_t id, uint32_t start, uint32_t num, + const struct spa_pod *filter) +{ + struct impl *this = object; + struct spa_pod *param; + struct spa_pod_builder b = { 0 }; + uint8_t buffer1024; + struct spa_result_device_params result; + uint32_t count = 0; + + spa_return_val_if_fail(this != NULL, -EINVAL); + spa_return_val_if_fail(num != 0, -EINVAL); + + result.id = id; + result.next = start; + next: + result.index = result.next++; + + spa_pod_builder_init(&b, buffer, sizeof(buffer)); + + switch (id) { + case SPA_PARAM_EnumProfile: + { + switch (result.index) { + case 0: + case 1: + param = build_profile(this, &b, id, result.index); + break; + default: + return 0; + } + break; + } + case SPA_PARAM_Profile: + { + switch (result.index) { + case 0: + param = build_profile(this, &b, id, this->profile); + break; + default: + return 0; + } + break; + } + default: + return -ENOENT; + } + + if (spa_pod_filter(&b, &result.param, param, filter) < 0) + goto next; + + spa_device_emit_result(&this->hooks, seq, 0, + SPA_RESULT_TYPE_DEVICE_PARAMS, &result); + + if (++count != num) + goto next; + + return 0; +} + +static int impl_set_param(void *object, + uint32_t id, uint32_t flags, + const struct spa_pod *param) +{ + struct impl *this = object; + int res; + + spa_return_val_if_fail(this != NULL, -EINVAL); + + switch (id) { + case SPA_PARAM_Profile: + { + uint32_t idx; + + if ((res = spa_pod_parse_object(param, + SPA_TYPE_OBJECT_ParamProfile, NULL, + SPA_PARAM_PROFILE_index, SPA_POD_Int(&idx))) < 0) { + spa_log_warn(this->log, "can't parse profile"); + spa_debug_log_pod(this->log, SPA_LOG_LEVEL_DEBUG, 0, NULL, param); + return res; + } + + set_profile(this, idx); + break; + } + default: + return -ENOENT; + } + return 0; +} + +static const struct spa_device_methods impl_device = { + SPA_VERSION_DEVICE_METHODS, + .add_listener = impl_add_listener, + .sync = impl_sync, + .enum_params = impl_enum_params, + .set_param = impl_set_param, +}; + +static int impl_get_interface(struct spa_handle *handle, const char *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 (spa_streq(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; + + 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; + + this->log = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log); + alsa_log_topic_init(this->log); + + this->device.iface = SPA_INTERFACE_INIT( + SPA_TYPE_INTERFACE_Device, + SPA_VERSION_DEVICE, + &impl_device, this); + spa_hook_list_init(&this->hooks); + + reset_props(&this->props); + + snd_config_update_free_global(); + + if (info) { + uint32_t i; + for (i = 0; info && i < info->n_items; i++) { + const char *k = info->itemsi.key; + const char *s = info->itemsi.value; + + if (spa_streq(k, SPA_KEY_API_ALSA_PATH)) { + snprintf(this->props.device, 64, "%s", s); + spa_log_debug(this->log, "using ALSA path \"%s\"", this->props.device); + } else if (spa_streq(k, SPA_KEY_API_ALSA_CARD)) { + long long card_nr = strtol(s, NULL, 10); + if ((card_nr >= 0) && (card_nr <= UINT_MAX)) { + this->props.card_nr = card_nr; + spa_log_debug(this->log, "using ALSA card number %u", this->props.card_nr); + } else + spa_log_warn(this->log, "invalid ALSA card number \"%s\"; using default", s); + } + } + } + + 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_alsa_compress_offload_device_factory = { + SPA_VERSION_HANDLE_FACTORY, + SPA_NAME_API_ALSA_COMPRESS_OFFLOAD_DEVICE, + NULL, + impl_get_size, + impl_init, + impl_enum_interface_info, +};
View file
pipewire-0.3.79.tar.gz/spa/plugins/alsa/alsa-pcm-device.c -> pipewire-0.3.80.tar.gz/spa/plugins/alsa/alsa-pcm-device.c
Changed
@@ -154,7 +154,6 @@ snd_pcm_info_t *pcminfo; snd_ctl_card_info_t *cardinfo; - spa_log_debug(this->log, "profile %d", id); this->profile = id; snd_ctl_card_info_alloca(&cardinfo); @@ -216,7 +215,9 @@ snd_ctl_t *ctl_hndl; int err; - spa_log_debug(this->log, "open card %s", this->props.device); + spa_log_debug(this->log, "enumerate PCM nodes for card %s; profile: %d", + this->props.device, id); + if ((err = snd_ctl_open(&ctl_hndl, this->props.device, 0)) < 0) { spa_log_error(this->log, "can't open control for card %s: %s", this->props.device, snd_strerror(err)); @@ -225,7 +226,7 @@ err = activate_profile(this, ctl_hndl, id); - spa_log_debug(this->log, "close card %s", this->props.device); + spa_log_debug(this->log, "done enumerating PCM nodes for card %s", this->props.device); snd_ctl_close(ctl_hndl); return err; @@ -551,7 +552,7 @@ return 1; } -const struct spa_handle_factory spa_alsa_device_factory = { +const struct spa_handle_factory spa_alsa_pcm_device_factory = { SPA_VERSION_HANDLE_FACTORY, SPA_NAME_API_ALSA_PCM_DEVICE, NULL,
View file
pipewire-0.3.79.tar.gz/spa/plugins/alsa/alsa-pcm-sink.c -> pipewire-0.3.80.tar.gz/spa/plugins/alsa/alsa-pcm-sink.c
Changed
@@ -15,6 +15,8 @@ #include <spa/utils/string.h> #include <spa/param/audio/format.h> #include <spa/pod/filter.h> +#include <spa/debug/log.h> +#include <spa/debug/pod.h> #include "alsa-pcm.h" @@ -669,7 +671,7 @@ const struct spa_pod *param) { struct state *this = object; - int res; + int res = 0; spa_return_val_if_fail(this != NULL, -EINVAL); @@ -693,9 +695,12 @@ this->port_info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS; this->port_paramsPORT_Latency.user++; emit_port_info(this, false); - res = 0; break; } + case SPA_PARAM_Tag: + if (param != NULL) + spa_debug_log_pod(this->log, SPA_LOG_LEVEL_DEBUG, 0, NULL, param); + break; default: res = -ENOENT; break; @@ -815,14 +820,9 @@ spa_list_append(&this->ready, &b->link); SPA_FLAG_CLEAR(b->flags, BUFFER_FLAG_OUT); io->buffer_id = SPA_ID_INVALID; - - spa_alsa_write(this); - - io->status = SPA_STATUS_OK; } - else if (!spa_list_is_empty(&this->ready)) { + if (!spa_list_is_empty(&this->ready)) { spa_alsa_write(this); - io->status = SPA_STATUS_OK; } return SPA_STATUS_HAVE_DATA;
View file
pipewire-0.3.79.tar.gz/spa/plugins/alsa/alsa-pcm.c -> pipewire-0.3.80.tar.gz/spa/plugins/alsa/alsa-pcm.c
Changed
@@ -920,7 +920,7 @@ min = max = rate; if (rate == 0) - rate = state->position ? state->position->clock.rate.denom : DEFAULT_RATE; + rate = state->position ? state->position->clock.target_rate.denom : DEFAULT_RATE; rate = SPA_CLAMP(rate, min, max); @@ -1349,6 +1349,17 @@ return 1; } +/* find smaller power of 2 */ +static uint32_t flp2(uint32_t x) +{ + x = x | (x >> 1); + x = x | (x >> 2); + x = x | (x >> 4); + x = x | (x >> 8); + x = x | (x >> 16); + return x - (x >> 1); +} + int spa_alsa_enum_format(struct state *state, int seq, uint32_t start, uint32_t num, const struct spa_pod *filter) @@ -1426,6 +1437,7 @@ unsigned int periods; bool match = true, planar = false, is_batch; char spdif_params128 = ""; + uint32_t default_period, latency; spa_log_debug(state->log, "opened:%d format:%d started:%d", state->opened, state->have_format, state->started); @@ -1651,12 +1663,15 @@ period_size = state->default_period_size; is_batch = snd_pcm_hw_params_is_batch(params) && !state->disable_batch; + default_period = SPA_SCALE32_UP(DEFAULT_PERIOD, state->rate, DEFAULT_RATE); + default_period = flp2(2 * default_period - 1); + /* no period size specified. If we are batch or not using timers, * use the graph duration as the period */ if (period_size == 0 && (is_batch || state->disable_tsched)) - period_size = state->position ? state->position->clock.target_duration : DEFAULT_PERIOD; + period_size = state->position ? state->position->clock.target_duration : default_period; if (period_size == 0) - period_size = DEFAULT_PERIOD; + period_size = default_period; if (!state->disable_tsched) { if (is_batch) { @@ -1664,7 +1679,7 @@ * the period smaller and add one period of headroom. Limit the * period size to our default so that we don't create too much * headroom. */ - period_size = SPA_MIN(period_size, DEFAULT_PERIOD) / 2; + period_size = SPA_MIN(period_size, default_period) / 2; } else { /* disable ALSA wakeups */ if (snd_pcm_hw_params_can_disable_period_wakeup(params)) @@ -1723,9 +1738,12 @@ state->headroom = SPA_MIN(state->headroom, state->buffer_frames); state->start_delay = state->default_start_delay; + latency = SPA_MAX(state->min_delay, SPA_MIN(state->max_delay, state->headroom)); + if (state->position != NULL) + latency = SPA_SCALE32_UP(latency, state->position->clock.target_rate.denom, state->rate); + state->latencystate->port_direction.min_rate = - state->latencystate->port_direction.max_rate = - SPA_MAX(state->min_delay, SPA_MIN(state->max_delay, state->headroom)); + state->latencystate->port_direction.max_rate = latency; spa_log_info(state->log, "%s (%s): format:%s access:%s-%s rate:%d channels:%d " "buffer frames %lu, period frames %lu, periods %u, frame_size %zd " @@ -1931,16 +1949,15 @@ delay = SPA_TIMEVAL_TO_USEC(&diff); missing = delay * state->rate / SPA_USEC_PER_SEC; - if (missing == 0) - missing = state->threshold; + missing += state->start_delay + state->threshold + state->headroom; spa_log_trace(state->log, "%p: xrun of %"PRIu64" usec %"PRIu64, state, delay, missing); - if (state->clock) - state->clock->xrun += missing; - state->sample_count += missing; - + if (state->clock) { + state->clock->xrun += SPA_SCALE32_UP(missing, + state->clock->rate.denom, state->rate); + } spa_node_call_xrun(&state->callbacks, SPA_TIMEVAL_TO_USEC(&trigger), delay, NULL); break; @@ -1977,16 +1994,16 @@ static int get_avail(struct state *state, uint64_t current_time, snd_pcm_uframes_t *delay) { - int res, missed; + int res, suppressed; snd_pcm_sframes_t avail; if (SPA_UNLIKELY((avail = snd_pcm_avail(state->hndl)) < 0)) { if ((res = alsa_recover(state, avail)) < 0) return res; if ((avail = snd_pcm_avail(state->hndl)) < 0) { - if ((missed = spa_ratelimit_test(&state->rate_limit, current_time)) >= 0) { - spa_log_warn(state->log, "%s: (%d missed) snd_pcm_avail after recover: %s", - state->props.device, missed, snd_strerror(avail)); + if ((suppressed = spa_ratelimit_test(&state->rate_limit, current_time)) >= 0) { + spa_log_warn(state->log, "%s: (%d suppressed) snd_pcm_avail after recover: %s", + state->props.device, suppressed, snd_strerror(avail)); } avail = state->threshold * 2; } @@ -2001,9 +2018,9 @@ uint64_t then; if ((res = snd_pcm_htimestamp(state->hndl, &havail, &tstamp)) < 0) { - if ((missed = spa_ratelimit_test(&state->rate_limit, current_time)) >= 0) { - spa_log_warn(state->log, "%s: (%d missed) snd_pcm_htimestamp error: %s", - state->props.device, missed, snd_strerror(res)); + if ((suppressed = spa_ratelimit_test(&state->rate_limit, current_time)) >= 0) { + spa_log_warn(state->log, "%s: (%d suppressed) snd_pcm_htimestamp error: %s", + state->props.device, suppressed, snd_strerror(res)); } return avail; } @@ -2029,9 +2046,9 @@ state->htimestamp_error = 0; state->htimestamp = false; } - else if ((missed = spa_ratelimit_test(&state->rate_limit, current_time)) >= 0) { - spa_log_warn(state->log, "%s: (%d missed) impossible htimestamp diff:%"PRIi64, - state->props.device, missed, diff); + else if ((suppressed = spa_ratelimit_test(&state->rate_limit, current_time)) >= 0) { + spa_log_warn(state->log, "%s: (%d suppressed) impossible htimestamp diff:%"PRIi64, + state->props.device, suppressed, diff); } } } @@ -2205,8 +2222,15 @@ if (SPA_UNLIKELY(state->position == NULL)) return 0; - target_duration = state->position->clock.target_duration; - target_rate = state->position->clock.target_rate; + if (state->disable_tsched && state->started && !state->following) { + target_duration = state->period_frames; + target_rate = SPA_FRACTION(1, state->rate); + state->position->clock.target_duration = target_duration; + state->position->clock.target_rate = target_rate; + } else { + target_duration = state->position->clock.target_duration; + target_rate = state->position->clock.target_rate; + } if (SPA_UNLIKELY((state->duration != target_duration) || (state->rate_denom != target_rate.denom))) { @@ -2229,7 +2253,7 @@ const snd_pcm_channel_area_t *my_areas; snd_pcm_uframes_t written, frames, offset, off, to_write, total_written, max_write; snd_pcm_sframes_t commitres; - int res, missed; + int res, suppressed; size_t frame_size = state->frame_size; if ((res = check_position_config(state)) < 0) @@ -2257,11 +2281,11 @@ else lev = SPA_LOG_LEVEL_INFO; - if ((missed = spa_ratelimit_test(&state->rate_limit, current_time)) >= 0) { + if ((suppressed = spa_ratelimit_test(&state->rate_limit, current_time)) >= 0) { spa_log_lev(state->log, lev, "%s: follower avail:%lu delay:%ld " - "target:%ld thr:%u, resync (%d missed)", + "target:%ld thr:%u, resync (%d suppressed)", state->props.device, avail, delay, - target, state->threshold, missed); + target, state->threshold, suppressed); } if (avail > target) @@ -2467,7 +2491,7 @@ const snd_pcm_channel_area_t *my_areas; snd_pcm_uframes_t read, frames, offset; snd_pcm_sframes_t commitres; - int res, missed; + int res, suppressed; if ((res = check_position_config(state)) < 0) return res; @@ -2494,10 +2518,10 @@ else lev = SPA_LOG_LEVEL_INFO; - if ((missed = spa_ratelimit_test(&state->rate_limit, current_time)) >= 0) { + if ((suppressed = spa_ratelimit_test(&state->rate_limit, current_time)) >= 0) { spa_log_lev(state->log, lev, "%s: follower delay:%ld target:%ld thr:%u, " - "resync (%d missed)", state->props.device, delay, - target, state->threshold, missed); + "resync (%d suppressed)", state->props.device, delay, + target, state->threshold, suppressed); } if (avail < target) @@ -2676,7 +2700,7 @@ struct state *state = source->data; snd_pcm_uframes_t avail, delay, target; uint64_t expire, current_time; - int res, missed; + int res, suppressed; if (SPA_UNLIKELY(state->disable_tsched)) { /* ALSA poll fds need to be "demangled" to know whether it's a real wakeup */ @@ -2748,12 +2772,12 @@ if (!state->disable_tsched && (state->next_time > current_time + SPA_NSEC_PER_SEC || current_time > state->next_time + SPA_NSEC_PER_SEC)) { - if ((missed = spa_ratelimit_test(&state->rate_limit, current_time)) >= 0) { + if ((suppressed = spa_ratelimit_test(&state->rate_limit, current_time)) >= 0) { spa_log_error(state->log, "%s: impossible timeout %lu %lu %lu %" - PRIu64" %"PRIu64" %"PRIi64" %d %"PRIi64" (%d missed)", + PRIu64" %"PRIu64" %"PRIi64" %d %"PRIi64" (%d suppressed)", state->props.device, avail, delay, target, current_time, state->next_time, state->next_time - current_time, - state->threshold, state->sample_count, missed); + state->threshold, state->sample_count, suppressed); } state->next_time = current_time + state->threshold * 1e9 / state->rate; }
View file
pipewire-0.3.79.tar.gz/spa/plugins/alsa/alsa-seq-bridge.c -> pipewire-0.3.80.tar.gz/spa/plugins/alsa/alsa-seq-bridge.c
Changed
@@ -691,7 +691,9 @@ case SPA_PARAM_Latency: { struct spa_latency_info info; - if ((res = spa_latency_parse(param, &info)) < 0) + if (param == NULL) + info = SPA_LATENCY_INFO(SPA_DIRECTION_REVERSE(direction)); + else if ((res = spa_latency_parse(param, &info)) < 0) return res; if (direction == info.direction) return -EINVAL;
View file
pipewire-0.3.79.tar.gz/spa/plugins/alsa/alsa-udev.c -> pipewire-0.3.80.tar.gz/spa/plugins/alsa/alsa-udev.c
Changed
@@ -24,24 +24,52 @@ #include <spa/support/plugin.h> #include <spa/monitor/device.h> #include <spa/monitor/utils.h> +#include <spa/debug/log.h> +#include <spa/debug/dict.h> #include "alsa.h" -#define MAX_DEVICES 64 +#define MAX_CARDS 64 #define ACTION_ADD 0 #define ACTION_REMOVE 1 #define ACTION_DISABLE 2 -struct device { - uint32_t id; - struct udev_device *dev; +/* Used for unavailable devices in the card structure. */ +#define ID_DEVICE_NOT_SUPPORTED 0 + +/* This represents an ALSA card. + * One card can have up to 1 PCM and 1 Compress-Offload device. */ +struct card { + unsigned int card_nr; + struct udev_device *udev_device; unsigned int unavailable:1; unsigned int accessible:1; unsigned int ignored:1; unsigned int emitted:1; + + /* Local SPA object IDs. (Global IDs are produced by PipeWire + * out of this using its registry.) Compress-Offload or PCM + * is not available, the corresponding ID is set to + * ID_DEVICE_NOT_SUPPORTED (= 0). + * PCM device IDs are (card nr + 1) * 2, and Compress-Offload + * device IDs are (card nr + 1) * 2 + 1. Assigning IDs like this + * makes it easy to deal with removed devices. (card nr + 1) + * is used because 0 is a valid ALSA card number. */ + uint32_t pcm_device_id; + uint32_t compress_offload_device_id; }; +static uint32_t calc_pcm_device_id(struct card *card) +{ + return (card->card_nr + 1) * 2 + 0; +} + +static uint32_t calc_compress_offload_device_id(struct card *card) +{ + return (card->card_nr + 1) * 2 + 1; +} + struct impl { struct spa_handle handle; struct spa_device device; @@ -58,8 +86,8 @@ struct udev *udev; struct udev_monitor *umonitor; - struct device devicesMAX_DEVICES; - uint32_t n_devices; + struct card cardsMAX_CARDS; + unsigned int n_cards; struct spa_source source; struct spa_source notify; @@ -84,58 +112,60 @@ return 0; } -static struct device *add_device(struct impl *this, uint32_t id, struct udev_device *dev) +static struct card *add_card(struct impl *this, unsigned int card_nr, struct udev_device *udev_device) { - struct device *device; + struct card *card; - if (this->n_devices >= MAX_DEVICES) + if (this->n_cards >= MAX_CARDS) return NULL; - device = &this->devicesthis->n_devices++; - spa_zero(*device); - device->id = id; - udev_device_ref(dev); - device->dev = dev; - return device; + + card = &this->cardsthis->n_cards++; + spa_zero(*card); + card->card_nr = card_nr; + udev_device_ref(udev_device); + card->udev_device = udev_device; + + return card; } -static struct device *find_device(struct impl *this, uint32_t id) +static struct card *find_card(struct impl *this, unsigned int card_nr) { - uint32_t i; - for (i = 0; i < this->n_devices; i++) { - if (this->devicesi.id == id) - return &this->devicesi; + unsigned int i; + for (i = 0; i < this->n_cards; i++) { + if (this->cardsi.card_nr == card_nr) + return &this->cardsi; } return NULL; } -static void remove_device(struct impl *this, struct device *device) +static void remove_card(struct impl *this, struct card *card) { - udev_device_unref(device->dev); - *device = this->devices--this->n_devices; + udev_device_unref(card->udev_device); + *card = this->cards--this->n_cards; } -static void clear_devices(struct impl *this) +static void clear_cards(struct impl *this) { - uint32_t i; - for (i = 0; i < this->n_devices; i++) - udev_device_unref(this->devicesi.dev); - this->n_devices = 0; + unsigned int i; + for (i = 0; i < this->n_cards; i++) + udev_device_unref(this->cardsi.udev_device); + this->n_cards = 0; } -static uint32_t get_card_id(struct impl *this, struct udev_device *dev) +static unsigned int get_card_nr(struct impl *this, struct udev_device *udev_device) { const char *e, *str; - if (udev_device_get_property_value(dev, "ACP_IGNORE")) + if (udev_device_get_property_value(udev_device, "ACP_IGNORE")) return SPA_ID_INVALID; - if ((str = udev_device_get_property_value(dev, "SOUND_CLASS")) && spa_streq(str, "modem")) + if ((str = udev_device_get_property_value(udev_device, "SOUND_CLASS")) && spa_streq(str, "modem")) return SPA_ID_INVALID; - if (udev_device_get_property_value(dev, "SOUND_INITIALIZED") == NULL) + if (udev_device_get_property_value(udev_device, "SOUND_INITIALIZED") == NULL) return SPA_ID_INVALID; - if ((str = udev_device_get_property_value(dev, "DEVPATH")) == NULL) + if ((str = udev_device_get_property_value(udev_device, "DEVPATH")) == NULL) return SPA_ID_INVALID; if ((e = strrchr(str, '/')) == NULL) @@ -244,7 +274,7 @@ return spa_strstartswith(buf, "modem") ? -ENXIO : 0; } -static int get_num_pcm_devices(unsigned int card_id) +static int get_num_pcm_devices(unsigned int card_nr) { char prefix32; struct dirent *entry; @@ -253,7 +283,7 @@ /* Check if card has PCM devices, without opening them */ - spa_scnprintf(prefix, sizeof(prefix), "pcmC%uD", card_id); + spa_scnprintf(prefix, sizeof(prefix), "pcmC%uD", card_nr); spa_autoptr(DIR) snd = opendir("/dev/snd"); if (snd == NULL) @@ -274,7 +304,33 @@ return errno != 0 ? -errno : num_dev; } -static int check_device_available(struct impl *this, struct device *device, int *num_pcm) +static int get_num_compress_offload_devices(unsigned int card_nr) +{ + char prefix32; + struct dirent *entry; + int num_dev = 0; + + /* Check if card has Compress-Offload devices, without opening them */ + + spa_scnprintf(prefix, sizeof(prefix), "comprC%uD", card_nr); + + spa_autoptr(DIR) snd = opendir("/dev/snd"); + if (snd == NULL) + return -errno; + + while ((errno = 0, entry = readdir(snd)) != NULL) { + if (!(entry->d_type == DT_CHR && + spa_strstartswith(entry->d_name, prefix))) + continue; + + ++num_dev; + } + + return errno != 0 ? -errno : num_dev; +} + +static int check_pcm_device_availability(struct impl *this, struct card *card, + int *num_pcm_devices) { char pathPATH_MAX; char buf16; @@ -282,15 +338,16 @@ struct dirent *entry, *entry_pcm; int res; - res = get_num_pcm_devices(device->id); + res = get_num_pcm_devices(card->card_nr); if (res < 0) { spa_log_error(this->log, "Error finding PCM devices for ALSA card %u: %s", - (unsigned int)device->id, spa_strerror(res)); + card->card_nr, spa_strerror(res)); return res; } - *num_pcm = res; + *num_pcm_devices = res; - spa_log_debug(this->log, "card %u has %d pcm device(s)", (unsigned int)device->id, *num_pcm); + spa_log_debug(this->log, "card %u has %d PCM device(s)", + card->card_nr, *num_pcm_devices); /* * Check if some pcm devices of the card are busy. Check it via /proc, as we @@ -304,25 +361,25 @@ res = 0; - spa_scnprintf(path, sizeof(path), "/proc/asound/card%u", (unsigned int)device->id); + spa_scnprintf(path, sizeof(path), "/proc/asound/card%u", card->card_nr); - spa_autoptr(DIR) card = opendir(path); - if (card == NULL) + spa_autoptr(DIR) card_dir = opendir(path); + if (card_dir == NULL) goto done; - while ((errno = 0, entry = readdir(card)) != NULL) { + while ((errno = 0, entry = readdir(card_dir)) != NULL) { if (!(entry->d_type == DT_DIR && spa_strstartswith(entry->d_name, "pcm"))) continue; spa_scnprintf(path, sizeof(path), "pcmC%uD%s", - (unsigned int)device->id, entry->d_name+3); + card->card_nr, entry->d_name+3); if (check_device_pcm_class(path) < 0) continue; /* Check busy status */ spa_scnprintf(path, sizeof(path), "/proc/asound/card%u/%s", - (unsigned int)device->id, entry->d_name); + card->card_nr, entry->d_name); spa_autoptr(DIR) pcm = opendir(path); if (pcm == NULL) @@ -334,7 +391,7 @@ continue; spa_scnprintf(path, sizeof(path), "/proc/asound/card%u/%s/%s/status", - (unsigned int)device->id, entry->d_name, entry_pcm->d_name); + card->card_nr, entry->d_name, entry_pcm->d_name); spa_autoptr(FILE) f = fopen(path, "re"); if (f == NULL) @@ -344,12 +401,12 @@ if (!spa_strstartswith(buf, "closed")) { spa_log_debug(this->log, "card %u pcm device %s busy", - (unsigned int)device->id, entry->d_name); + card->card_nr, entry->d_name); res = -EBUSY; goto done; } spa_log_debug(this->log, "card %u pcm device %s free", - (unsigned int)device->id, entry->d_name); + card->card_nr, entry->d_name); } if (errno != 0) goto done; @@ -360,158 +417,249 @@ done: if (errno != 0) { spa_log_info(this->log, "card %u: failed to find busy status (%s)", - (unsigned int)device->id, spa_strerror(-errno)); + card->card_nr, spa_strerror(-errno)); } return res; } -static int emit_object_info(struct impl *this, struct device *device) +static int check_compress_offload_device_availability(struct impl *this, struct card *card, + int *num_compress_offload_devices) +{ + int res; + + res = get_num_compress_offload_devices(card->card_nr); + if (res < 0) { + spa_log_error(this->log, "Error finding Compress-Offload devices for ALSA card %u: %s", + card->card_nr, spa_strerror(res)); + return res; + } + *num_compress_offload_devices = res; + + spa_log_debug(this->log, "card %u has %d Compress-Offload device(s)", + card->card_nr, *num_compress_offload_devices); + + return 0; +} + +static int emit_added_object_info(struct impl *this, struct card *card) { - struct spa_device_object_info info; - uint32_t id = device->id; - struct udev_device *dev = device->dev; + char path32; + int res, num_pcm_devices, num_compress_offload_devices; const char *str; - char path32, *cn = NULL, *cln = NULL; - struct spa_dict_item items25; - uint32_t n_items = 0; - int res, pcm; + struct udev_device *udev_device = card->udev_device; /* * inotify close events under /dev/snd must not be emitted, except after setting - * device->emitted to true. alsalib functions can be used after that. + * card->emitted to true. alsalib functions can be used after that. */ - snprintf(path, sizeof(path), "hw:%u", id); + snprintf(path, sizeof(path), "hw:%u", card->card_nr); - if ((res = check_device_available(this, device, &pcm)) < 0) + if ((res = check_pcm_device_availability(this, card, &num_pcm_devices)) < 0) + return res; + if ((res = check_compress_offload_device_availability(this, card, &num_compress_offload_devices)) < 0) return res; - if (pcm == 0) { - spa_log_debug(this->log, "no pcm devices for %s", path); - device->ignored = true; + + if ((num_pcm_devices == 0) && (num_compress_offload_devices == 0)) { + spa_log_debug(this->log, "no PCM and no Compress-Offload devices for %s", path); + card->ignored = true; return -ENODEV; } - spa_log_debug(this->log, "emitting card %s", path); - device->emitted = true; - - info = SPA_DEVICE_OBJECT_INFO_INIT(); - - info.type = SPA_TYPE_INTERFACE_Device; - info.factory_name = this->use_acp ? - SPA_NAME_API_ALSA_ACP_DEVICE : - SPA_NAME_API_ALSA_PCM_DEVICE; - info.change_mask = SPA_DEVICE_OBJECT_CHANGE_MASK_FLAGS | - SPA_DEVICE_OBJECT_CHANGE_MASK_PROPS; - info.flags = 0; - - itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_ENUM_API, "udev"); - itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_API, "alsa"); - itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_MEDIA_CLASS, "Audio/Device"); - itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_API_ALSA_PATH, path); - itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_API_ALSA_CARD, path+3); - if (snd_card_get_name(id, &cn) >= 0) - itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_API_ALSA_CARD_NAME, cn); - if (snd_card_get_longname(id, &cln) >= 0) - itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_API_ALSA_CARD_LONGNAME, cln); - - if ((str = udev_device_get_property_value(dev, "ACP_NAME")) && *str) - itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_NAME, str); - - if ((str = udev_device_get_property_value(dev, "ACP_PROFILE_SET")) && *str) - itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_PROFILE_SET, str); - - if ((str = udev_device_get_property_value(dev, "SOUND_CLASS")) && *str) - itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_CLASS, str); - - if ((str = udev_device_get_property_value(dev, "USEC_INITIALIZED")) && *str) - itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_PLUGGED_USEC, str); - - str = udev_device_get_property_value(dev, "ID_PATH"); - if (!(str && *str)) - str = udev_device_get_syspath(dev); - if (str && *str) { - itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_BUS_PATH, str); - } - if ((str = udev_device_get_devpath(dev)) && *str) { - itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_SYSFS_PATH, str); - } - if ((str = udev_device_get_property_value(dev, "ID_ID")) && *str) { - itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_BUS_ID, str); - } - if ((str = udev_device_get_property_value(dev, "ID_BUS")) && *str) { - itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_BUS, str); - } - if ((str = udev_device_get_property_value(dev, "SUBSYSTEM")) && *str) { - itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_SUBSYSTEM, str); - } - if ((str = udev_device_get_property_value(dev, "ID_VENDOR_ID")) && *str) { - int32_t val; - if (spa_atoi32(str, &val, 16)) { - char *dec = alloca(12); /* 0xffffffff is max */ - snprintf(dec, 12, "0x%04x", val); - itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_VENDOR_ID, dec); + card->emitted = true; + + if (num_pcm_devices > 0) { + struct spa_device_object_info info; + char *cn = NULL, *cln = NULL; + struct spa_dict_item items25; + unsigned int n_items = 0; + + card->pcm_device_id = calc_pcm_device_id(card); + + spa_log_debug(this->log, "emitting ACP/PCM device interface for card %s; " + "using local alsa-udev object ID %" PRIu32, path, card->pcm_device_id); + + info = SPA_DEVICE_OBJECT_INFO_INIT(); + + info.type = SPA_TYPE_INTERFACE_Device; + info.factory_name = this->use_acp ? + SPA_NAME_API_ALSA_ACP_DEVICE : + SPA_NAME_API_ALSA_PCM_DEVICE; + info.change_mask = SPA_DEVICE_OBJECT_CHANGE_MASK_FLAGS | + SPA_DEVICE_OBJECT_CHANGE_MASK_PROPS; + info.flags = 0; + + itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_ENUM_API, "udev"); + itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_API, "alsa"); + itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_MEDIA_CLASS, "Audio/Device"); + itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_API_ALSA_PATH, path); + itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_API_ALSA_CARD, path+3); + if (snd_card_get_name(card->card_nr, &cn) >= 0) + itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_API_ALSA_CARD_NAME, cn); + if (snd_card_get_longname(card->card_nr, &cln) >= 0) + itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_API_ALSA_CARD_LONGNAME, cln); + + if ((str = udev_device_get_property_value(udev_device, "ACP_NAME")) && *str) + itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_NAME, str); + + if ((str = udev_device_get_property_value(udev_device, "ACP_PROFILE_SET")) && *str) + itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_PROFILE_SET, str); + + if ((str = udev_device_get_property_value(udev_device, "SOUND_CLASS")) && *str) + itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_CLASS, str); + + if ((str = udev_device_get_property_value(udev_device, "USEC_INITIALIZED")) && *str) + itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_PLUGGED_USEC, str); + + str = udev_device_get_property_value(udev_device, "ID_PATH"); + if (!(str && *str)) + str = udev_device_get_syspath(udev_device); + if (str && *str) { + itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_BUS_PATH, str); } - } - str = udev_device_get_property_value(dev, "ID_VENDOR_FROM_DATABASE"); - if (!(str && *str)) { - str = udev_device_get_property_value(dev, "ID_VENDOR_ENC"); + if ((str = udev_device_get_devpath(udev_device)) && *str) { + itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_SYSFS_PATH, str); + } + if ((str = udev_device_get_property_value(udev_device, "ID_ID")) && *str) { + itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_BUS_ID, str); + } + if ((str = udev_device_get_property_value(udev_device, "ID_BUS")) && *str) { + itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_BUS, str); + } + if ((str = udev_device_get_property_value(udev_device, "SUBSYSTEM")) && *str) { + itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_SUBSYSTEM, str); + } + if ((str = udev_device_get_property_value(udev_device, "ID_VENDOR_ID")) && *str) { + int32_t val; + if (spa_atoi32(str, &val, 16)) { + char *dec = alloca(12); /* 0xffffffff is max */ + snprintf(dec, 12, "0x%04x", val); + itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_VENDOR_ID, dec); + } + } + str = udev_device_get_property_value(udev_device, "ID_VENDOR_FROM_DATABASE"); if (!(str && *str)) { - str = udev_device_get_property_value(dev, "ID_VENDOR"); - } else { - char *t = alloca(strlen(str) + 1); - unescape(str, t); - str = t; + str = udev_device_get_property_value(udev_device, "ID_VENDOR_ENC"); + if (!(str && *str)) { + str = udev_device_get_property_value(udev_device, "ID_VENDOR"); + } else { + char *t = alloca(strlen(str) + 1); + unescape(str, t); + str = t; + } } - } - if (str && *str) { - itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_VENDOR_NAME, str); - } - if ((str = udev_device_get_property_value(dev, "ID_MODEL_ID")) && *str) { - int32_t val; - if (spa_atoi32(str, &val, 16)) { - char *dec = alloca(12); /* 0xffffffff is max */ - snprintf(dec, 12, "0x%04x", val); - itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_PRODUCT_ID, dec); + if (str && *str) { + itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_VENDOR_NAME, str); } - } - str = udev_device_get_property_value(dev, "ID_MODEL_FROM_DATABASE"); - if (!(str && *str)) { - str = udev_device_get_property_value(dev, "ID_MODEL_ENC"); + if ((str = udev_device_get_property_value(udev_device, "ID_MODEL_ID")) && *str) { + int32_t val; + if (spa_atoi32(str, &val, 16)) { + char *dec = alloca(12); /* 0xffffffff is max */ + snprintf(dec, 12, "0x%04x", val); + itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_PRODUCT_ID, dec); + } + } + str = udev_device_get_property_value(udev_device, "ID_MODEL_FROM_DATABASE"); if (!(str && *str)) { - str = udev_device_get_property_value(dev, "ID_MODEL"); - } else { - char *t = alloca(strlen(str) + 1); - unescape(str, t); - str = t; + str = udev_device_get_property_value(udev_device, "ID_MODEL_ENC"); + if (!(str && *str)) { + str = udev_device_get_property_value(udev_device, "ID_MODEL"); + } else { + char *t = alloca(strlen(str) + 1); + unescape(str, t); + str = t; + } } - } - if (str && *str) - itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_PRODUCT_NAME, str); + if (str && *str) + itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_PRODUCT_NAME, str); - if ((str = udev_device_get_property_value(dev, "ID_SERIAL")) && *str) { - itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_SERIAL, str); - } - if ((str = udev_device_get_property_value(dev, "SOUND_FORM_FACTOR")) && *str) { - itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_FORM_FACTOR, str); + if ((str = udev_device_get_property_value(udev_device, "ID_SERIAL")) && *str) { + itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_SERIAL, str); + } + if ((str = udev_device_get_property_value(udev_device, "SOUND_FORM_FACTOR")) && *str) { + itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_FORM_FACTOR, str); + } + info.props = &SPA_DICT_INIT(items, n_items); + + spa_log_debug(this->log, "interface information:"); + spa_debug_log_dict(this->log, SPA_LOG_LEVEL_DEBUG, 2, info.props); + + spa_device_emit_object_info(&this->hooks, card->pcm_device_id, &info); + free(cn); + free(cln); + } else { + card->pcm_device_id = ID_DEVICE_NOT_SUPPORTED; } - info.props = &SPA_DICT_INIT(items, n_items); - spa_device_emit_object_info(&this->hooks, id, &info); - free(cn); - free(cln); + if (num_compress_offload_devices > 0) { + struct spa_device_object_info info; + struct spa_dict_item items11; + unsigned int n_items = 0; + char device_name200; + char device_desc200; + + card->compress_offload_device_id = calc_compress_offload_device_id(card); + + spa_log_debug(this->log, "emitting Compress-Offload device interface for card %s; " + "using local alsa-udev object ID %" PRIu32, path, card->compress_offload_device_id); + + info = SPA_DEVICE_OBJECT_INFO_INIT(); + + info.type = SPA_TYPE_INTERFACE_Device; + info.factory_name = SPA_NAME_API_ALSA_COMPRESS_OFFLOAD_DEVICE; + info.change_mask = SPA_DEVICE_OBJECT_CHANGE_MASK_FLAGS | + SPA_DEVICE_OBJECT_CHANGE_MASK_PROPS; + info.flags = 0; + + snprintf(device_name, sizeof(device_name), "comprC%u", card->card_nr); + snprintf(device_desc, sizeof(device_desc), "Compress-Offload device (ALSA card %u)", card->card_nr); + + itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_ENUM_API, "udev"); + itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_API, "alsa:compressed"); + itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_NAME, device_name); + itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_DESCRIPTION, device_desc); + itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_MEDIA_CLASS, "Audio/Device"); + itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_API_ALSA_PATH, path); + itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_API_ALSA_CARD, path+3); + + if ((str = udev_device_get_property_value(udev_device, "USEC_INITIALIZED")) && *str) + itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_PLUGGED_USEC, str); + + str = udev_device_get_property_value(udev_device, "ID_PATH"); + if (!(str && *str)) + str = udev_device_get_syspath(udev_device); + if (str && *str) { + itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_BUS_PATH, str); + } + if ((str = udev_device_get_devpath(udev_device)) && *str) { + itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_SYSFS_PATH, str); + } + if ((str = udev_device_get_property_value(udev_device, "SUBSYSTEM")) && *str) { + itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_SUBSYSTEM, str); + } + + info.props = &SPA_DICT_INIT(items, n_items); + + spa_log_debug(this->log, "interface information:"); + spa_debug_log_dict(this->log, SPA_LOG_LEVEL_DEBUG, 2, info.props); + + spa_device_emit_object_info(&this->hooks, card->compress_offload_device_id, &info); + } else { + card->compress_offload_device_id = ID_DEVICE_NOT_SUPPORTED; + } return 1; } -static bool check_access(struct impl *this, struct device *device) +static bool check_access(struct impl *this, struct card *card) { - char path128, prefix32; + char path128, pcm_prefix32, compr_prefix32;; spa_autoptr(DIR) snd = NULL; struct dirent *entry; bool accessible = false; - snprintf(path, sizeof(path), "/dev/snd/controlC%u", device->id); + snprintf(path, sizeof(path), "/dev/snd/controlC%u", card->card_nr); if (access(path, R_OK|W_OK) >= 0 && (snd = opendir("/dev/snd"))) { /* * It's possible that controlCX is accessible before pcmCX* or @@ -520,10 +668,12 @@ */ accessible = true; - spa_scnprintf(prefix, sizeof(prefix), "pcmC%uD", device->id); + spa_scnprintf(pcm_prefix, sizeof(pcm_prefix), "pcmC%uD", card->card_nr); + spa_scnprintf(compr_prefix, sizeof(compr_prefix), "comprC%uD", card->card_nr); while ((entry = readdir(snd)) != NULL) { if (!(entry->d_type == DT_CHR && - spa_strstartswith(entry->d_name, prefix))) + (spa_strstartswith(entry->d_name, pcm_prefix) || + spa_strstartswith(entry->d_name, compr_prefix)))) continue; snprintf(path, sizeof(path), "/dev/snd/%.32s", entry->d_name); @@ -534,70 +684,90 @@ } } - if (accessible != device->accessible) + if (accessible != card->accessible) spa_log_debug(this->log, "%s accessible:%u", path, accessible); - device->accessible = accessible; + card->accessible = accessible; - return device->accessible; + return card->accessible; } -static void process_device(struct impl *this, uint32_t action, struct udev_device *dev) +static void process_card(struct impl *this, uint32_t action, struct udev_device *udev_device) { - uint32_t id; - struct device *device; + unsigned int card_nr; + struct card *card; bool emitted; int res; - if ((id = get_card_id(this, dev)) == SPA_ID_INVALID) + if ((card_nr = get_card_nr(this, udev_device)) == SPA_ID_INVALID) return; - device = find_device(this, id); - if (device && device->ignored) + card = find_card(this, card_nr); + if (card && card->ignored) return; switch (action) { case ACTION_ADD: - if (device == NULL) - device = add_device(this, id, dev); - if (device == NULL) + if (card == NULL) + card = add_card(this, card_nr, udev_device); + if (card == NULL) return; - if (!check_access(this, device)) + if (!check_access(this, card)) return; - res = emit_object_info(this, device); + res = emit_added_object_info(this, card); if (res < 0) { - if (device->ignored) + if (card->ignored) spa_log_info(this->log, "ALSA card %u unavailable (%s): it is ignored", - device->id, spa_strerror(res)); - else if (!device->unavailable) + card->card_nr, spa_strerror(res)); + else if (!card->unavailable) spa_log_info(this->log, "ALSA card %u unavailable (%s): wait for it", - device->id, spa_strerror(res)); + card->card_nr, spa_strerror(res)); else spa_log_debug(this->log, "ALSA card %u still unavailable (%s)", - device->id, spa_strerror(res)); - device->unavailable = true; + card->card_nr, spa_strerror(res)); + card->unavailable = true; } else { - if (device->unavailable) + if (card->unavailable) spa_log_info(this->log, "ALSA card %u now available", - device->id); - device->unavailable = false; + card->card_nr); + card->unavailable = false; } break; - case ACTION_REMOVE: - if (device == NULL) + case ACTION_REMOVE: { + uint32_t pcm_device_id, compress_offload_device_id; + + if (card == NULL) return; - emitted = device->emitted; - remove_device(this, device); - if (emitted) - spa_device_emit_object_info(&this->hooks, id, NULL); + + emitted = card->emitted; + pcm_device_id = card->pcm_device_id; + compress_offload_device_id = card->compress_offload_device_id; + remove_card(this, card); + + if (emitted) { + if (pcm_device_id != ID_DEVICE_NOT_SUPPORTED) + spa_device_emit_object_info(&this->hooks, pcm_device_id, NULL); + if (compress_offload_device_id != ID_DEVICE_NOT_SUPPORTED) + spa_device_emit_object_info(&this->hooks, compress_offload_device_id, NULL); + } break; + } case ACTION_DISABLE: - if (device == NULL) + if (card == NULL) return; - if (device->emitted) { - device->emitted = false; - spa_device_emit_object_info(&this->hooks, id, NULL); + if (card->emitted) { + uint32_t pcm_device_id, compress_offload_device_id; + + pcm_device_id = card->pcm_device_id; + compress_offload_device_id = card->compress_offload_device_id; + + card->emitted = false; + + if (pcm_device_id != ID_DEVICE_NOT_SUPPORTED) + spa_device_emit_object_info(&this->hooks, pcm_device_id, NULL); + if (compress_offload_device_id != ID_DEVICE_NOT_SUPPORTED) + spa_device_emit_object_info(&this->hooks, compress_offload_device_id, NULL); } break; } @@ -638,28 +808,28 @@ for (p = &buf; p < e; p = SPA_PTROFF(p, sizeof(struct inotify_event) + event->len, void)) { - unsigned int id; - struct device *device; + unsigned int card_nr; + struct card *card; event = (const struct inotify_event *) p; spa_assert_se(SPA_PTRDIFF(e, p) >= (ptrdiff_t)sizeof(struct inotify_event) && SPA_PTRDIFF(e, p) - sizeof(struct inotify_event) >= event->len && "bad event from kernel"); - /* Device becomes accessible or not busy */ + /* card becomes accessible or not busy */ if ((event->mask & (IN_ATTRIB | IN_CLOSE_WRITE))) { bool access; - if (sscanf(event->name, "controlC%u", &id) != 1 && - sscanf(event->name, "pcmC%uD", &id) != 1) + if (sscanf(event->name, "controlC%u", &card_nr) != 1 && + sscanf(event->name, "pcmC%uD", &card_nr) != 1) continue; - if ((device = find_device(this, id)) == NULL) + if ((card = find_card(this, card_nr)) == NULL) continue; - access = check_access(this, device); - if (access && !device->emitted) - process_device(this, ACTION_ADD, device->dev); - else if (!access && device->emitted) - process_device(this, ACTION_DISABLE, device->dev); + access = check_access(this, card); + if (access && !card->emitted) + process_card(this, ACTION_ADD, card->udev_device); + else if (!access && card->emitted) + process_card(this, ACTION_DISABLE, card->udev_device); } /* /dev/snd/ might have been removed */ if ((event->mask & (IN_DELETE_SELF | IN_MOVE_SELF))) @@ -707,14 +877,14 @@ static void impl_on_fd_events(struct spa_source *source) { struct impl *this = source->data; - struct udev_device *dev; + struct udev_device *udev_device; const char *action; - dev = udev_monitor_receive_device(this->umonitor); - if (dev == NULL) + udev_device = udev_monitor_receive_device(this->umonitor); + if (udev_device == NULL) return; - if ((action = udev_device_get_action(dev)) == NULL) + if ((action = udev_device_get_action(udev_device)) == NULL) action = "change"; spa_log_debug(this->log, "action %s", action); @@ -722,11 +892,11 @@ start_inotify(this); if (spa_streq(action, "change")) { - process_device(this, ACTION_ADD, dev); + process_card(this, ACTION_ADD, udev_device); } else if (spa_streq(action, "remove")) { - process_device(this, ACTION_REMOVE, dev); + process_card(this, ACTION_REMOVE, udev_device); } - udev_device_unref(dev); + udev_device_unref(udev_device); } static int start_monitor(struct impl *this) @@ -763,7 +933,7 @@ if (this->umonitor == NULL) return 0; - clear_devices (this); + clear_cards (this); spa_loop_remove_source(this->main_loop, &this->source); udev_monitor_unref(this->umonitor); @@ -774,10 +944,10 @@ return 0; } -static int enum_devices(struct impl *this) +static int enum_cards(struct impl *this) { struct udev_enumerate *enumerate; - struct udev_list_entry *devices; + struct udev_list_entry *udev_devices; enumerate = udev_enumerate_new(this->udev); if (enumerate == NULL) @@ -786,17 +956,18 @@ udev_enumerate_add_match_subsystem(enumerate, "sound"); udev_enumerate_scan_devices(enumerate); - for (devices = udev_enumerate_get_list_entry(enumerate); devices; - devices = udev_list_entry_get_next(devices)) { - struct udev_device *dev; + for (udev_devices = udev_enumerate_get_list_entry(enumerate); udev_devices; + udev_devices = udev_list_entry_get_next(udev_devices)) { + struct udev_device *udev_device; - dev = udev_device_new_from_syspath(this->udev, udev_list_entry_get_name(devices)); - if (dev == NULL) + udev_device = udev_device_new_from_syspath(this->udev, + udev_list_entry_get_name(udev_devices)); + if (udev_device == NULL) continue; - process_device(this, ACTION_ADD, dev); + process_card(this, ACTION_ADD, udev_device); - udev_device_unref(dev); + udev_device_unref(udev_device); } udev_enumerate_unref(enumerate); @@ -851,7 +1022,7 @@ if ((res = start_monitor(this)) < 0) return res; - if ((res = enum_devices(this)) < 0) + if ((res = enum_cards(this)) < 0) return res; spa_hook_list_join(&this->hooks, &save);
View file
pipewire-0.3.79.tar.gz/spa/plugins/alsa/alsa.c -> pipewire-0.3.80.tar.gz/spa/plugins/alsa/alsa.c
Changed
@@ -12,11 +12,12 @@ 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_udev_factory; -extern const struct spa_handle_factory spa_alsa_device_factory; +extern const struct spa_handle_factory spa_alsa_pcm_device_factory; extern const struct spa_handle_factory spa_alsa_seq_bridge_factory; extern const struct spa_handle_factory spa_alsa_acp_device_factory; #ifdef HAVE_ALSA_COMPRESS_OFFLOAD extern const struct spa_handle_factory spa_alsa_compress_offload_sink_factory; +extern const struct spa_handle_factory spa_alsa_compress_offload_device_factory; #endif struct spa_log_topic log_topic = SPA_LOG_TOPIC(0, "spa.alsa"); @@ -39,7 +40,7 @@ *factory = &spa_alsa_udev_factory; break; case 3: - *factory = &spa_alsa_device_factory; + *factory = &spa_alsa_pcm_device_factory; break; case 4: *factory = &spa_alsa_seq_bridge_factory; @@ -51,6 +52,9 @@ case 6: *factory = &spa_alsa_compress_offload_sink_factory; break; + case 7: + *factory = &spa_alsa_compress_offload_device_factory; + break; #endif default: return 0;
View file
pipewire-0.3.80.tar.gz/spa/plugins/alsa/compress-offload-api-util.c
Added
@@ -0,0 +1,36 @@ +#include <errno.h> +#include <inttypes.h> +#include "compress-offload-api.h" +#include "compress-offload-api-util.h" + +int get_compress_offload_device_direction(int card_nr, int device_nr, + struct spa_log *log, + enum spa_compress_offload_direction *direction) +{ + int ret = 0; + struct compress_offload_api_context *device_context; + const struct snd_compr_caps *compr_caps; + + device_context = compress_offload_api_open(card_nr, device_nr, log); + if (device_context == NULL) + return -errno; + + compr_caps = compress_offload_api_get_caps(device_context); + + switch (compr_caps->direction) { + case SND_COMPRESS_PLAYBACK: + *direction = SPA_COMPRESS_OFFLOAD_DIRECTION_PLAYBACK; + break; + case SND_COMPRESS_CAPTURE: + *direction = SPA_COMPRESS_OFFLOAD_DIRECTION_CAPTURE; + break; + default: + spa_log_error(log, "card nr %d device nr %d: unknown direction %#" PRIx32, + card_nr, device_nr, (uint32_t)(compr_caps->direction)); + ret = -EINVAL; + } + + compress_offload_api_close(device_context); + + return ret; +}
View file
pipewire-0.3.80.tar.gz/spa/plugins/alsa/compress-offload-api-util.h
Added
@@ -0,0 +1,30 @@ +#ifndef SPA_ALSA_COMPRESS_OFFLOAD_DEVICE_UTIL_H +#define SPA_ALSA_COMPRESS_OFFLOAD_DEVICE_UTIL_H + +#include <spa/support/log.h> + +#if defined(__GNUC__) && __GNUC__ >= 4 +#define COMPR_API_PRIVATE __attribute__((visibility("hidden"))) +#elif defined(__SUNPRO_C) && (__SUNPRO_C >= 0x590) +#define COMPR_API_PRIVATE __attribute__((visibility("hidden"))) +#elif defined(__SUNPRO_C) && (__SUNPRO_C >= 0x550) +#define COMPR_API_PRIVATE __hidden +#else +#define COMPR_API_PRIVATE +#endif + +enum spa_compress_offload_direction { + SPA_COMPRESS_OFFLOAD_DIRECTION_PLAYBACK, + SPA_COMPRESS_OFFLOAD_DIRECTION_CAPTURE +}; + +/* This exists for situations where both the direction of the compress-offload + * device and the functions from asoundlib.h are needed. It is not possible to + * include asoundlib.h and the compress-offload headers in the same C file, + * since these headers contain conflicting declarations. Provide this direction + * check function to keep the compress-offload headers encapsulated. */ +COMPR_API_PRIVATE int get_compress_offload_device_direction(int card_nr, int device_nr, + struct spa_log *log, + enum spa_compress_offload_direction *direction); + +#endif /* SPA_ALSA_COMPRESS_OFFLOAD_DEVICE_UTIL_H */
View file
pipewire-0.3.79.tar.gz/spa/plugins/alsa/compress-offload-api.h -> pipewire-0.3.80.tar.gz/spa/plugins/alsa/compress-offload-api.h
Changed
@@ -6,22 +6,12 @@ #include <sound/compress_offload.h> #include <sound/compress_params.h> #include <spa/support/log.h> +#include "compress-offload-api-util.h" struct compress_offload_api_context; -#if defined(__GNUC__) && __GNUC__ >= 4 -#define COMPR_API_PRIVATE __attribute__((visibility("hidden"))) -#elif defined(__SUNPRO_C) && (__SUNPRO_C >= 0x590) -#define COMPR_API_PRIVATE __attribute__((visibility("hidden"))) -#elif defined(__SUNPRO_C) && (__SUNPRO_C >= 0x550) -#define COMPR_API_PRIVATE __hidden -#else -#define COMPR_API_PRIVATE -#endif - - /* This is a simple encapsulation of the ALSA Compress-Offload API * and its ioctl calls. It is intentionally not using any PipeWire * or SPA headers to allow for porting it or extracting it as its
View file
pipewire-0.3.79.tar.gz/spa/plugins/alsa/meson.build -> pipewire-0.3.80.tar.gz/spa/plugins/alsa/meson.build
Changed
@@ -15,7 +15,10 @@ 'alsa-seq.c' if compress_offload_option.allowed() - spa_alsa_sources += 'alsa-compress-offload-sink.c', 'compress-offload-api.c' + spa_alsa_sources += 'alsa-compress-offload-sink.c', + 'alsa-compress-offload-device.c', + 'compress-offload-api-util.c', + 'compress-offload-api.c' endif spa_alsa = shared_library(
View file
pipewire-0.3.79.tar.gz/spa/plugins/audioconvert/audioadapter.c -> pipewire-0.3.80.tar.gz/spa/plugins/audioconvert/audioadapter.c
Changed
@@ -20,6 +20,7 @@ #include <spa/param/param.h> #include <spa/param/audio/format-utils.h> #include <spa/param/latency-utils.h> +#include <spa/param/tag-utils.h> #include <spa/debug/format.h> #include <spa/debug/pod.h> #include <spa/debug/log.h> @@ -75,7 +76,8 @@ #define IDX_PortConfig 5 #define IDX_Latency 6 #define IDX_ProcessLatency 7 -#define N_NODE_PARAMS 8 +#define IDX_Tag 8 +#define N_NODE_PARAMS 9 struct spa_param_info paramsN_NODE_PARAMS; uint32_t convert_params_flagsN_NODE_PARAMS; uint32_t follower_params_flagsN_NODE_PARAMS; @@ -93,6 +95,7 @@ unsigned int async:1; unsigned int passthrough:1; unsigned int follower_removing:1; + unsigned int in_recalc; }; /** \endcond */ @@ -204,6 +207,7 @@ case SPA_PARAM_EnumFormat: case SPA_PARAM_Format: case SPA_PARAM_Latency: + case SPA_PARAM_Tag: res = spa_node_port_enum_params_sync(this->follower, this->direction, 0, id, &result.next, filter, &result.param, &b.b); @@ -541,6 +545,80 @@ static const struct spa_node_events follower_node_events; +static int recalc_latency(struct impl *this, struct spa_node *src, enum spa_direction direction, + uint32_t port_id, struct spa_node *dst) +{ + struct spa_pod_builder b = { 0 }; + uint8_t buffer1024; + struct spa_pod *param; + uint32_t index = 0; + struct spa_latency_info latency; + int res; + + spa_log_info(this->log, "%p: %d:%d", this, direction, port_id); + + if (this->target == this->follower) + return 0; + + while (true) { + spa_pod_builder_init(&b, buffer, sizeof(buffer)); + if ((res = spa_node_port_enum_params_sync(src, + direction, port_id, SPA_PARAM_Latency, + &index, NULL, ¶m, &b)) != 1) { + param = NULL; + break; + } + if ((res = spa_latency_parse(param, &latency)) < 0) + return res; + if (latency.direction == direction) + break; + } + if ((res = spa_node_port_set_param(dst, + SPA_DIRECTION_REVERSE(direction), 0, + SPA_PARAM_Latency, 0, param)) < 0) + return res; + + return 0; +} + +static int recalc_tag(struct impl *this, struct spa_node *src, enum spa_direction direction, + uint32_t port_id, struct spa_node *dst) +{ + struct spa_pod_builder b = { 0 }; + uint8_t buffer1024; + struct spa_pod *param; + uint32_t index = 0; + struct spa_tag_info info; + int res; + + spa_log_debug(this->log, "%p: %d:%d", this, direction, port_id); + + if (this->target == this->follower) + return 0; + + while (true) { + void *state = NULL; + spa_pod_builder_init(&b, buffer, sizeof(buffer)); + if ((res = spa_node_port_enum_params_sync(src, + direction, port_id, SPA_PARAM_Tag, + &index, NULL, ¶m, &b)) != 1) { + param = NULL; + break; + } + if ((res = spa_tag_parse(param, &info, &state)) < 0) + return res; + if (info.direction == direction) + break; + } + if ((res = spa_node_port_set_param(dst, + SPA_DIRECTION_REVERSE(direction), 0, + SPA_PARAM_Tag, 0, param)) < 0) + return res; + + return 0; +} + + static int reconfigure_mode(struct impl *this, bool passthrough, enum spa_direction direction, struct spa_pod *format) { @@ -590,6 +668,8 @@ emit_node_info(this, false); + spa_log_debug(this->log, "%p: passthrough mode %d", this, passthrough); + return 0; } @@ -673,6 +753,8 @@ if (this->target != this->follower) { if ((res = spa_node_set_param(this->target, id, flags, param)) < 0) return res; + + res = recalc_latency(this, this->follower, this->direction, 0, this->convert); } break; } @@ -953,6 +1035,61 @@ emit_node_info(this, false); } +static void follower_convert_port_info(void *data, + enum spa_direction direction, uint32_t port_id, + const struct spa_port_info *info) +{ + struct impl *this = data; + uint32_t i; + int res; + + spa_log_info(this->log, "%p: convert port info %s %p %08"PRIx64, this, + this->direction == SPA_DIRECTION_INPUT ? + "Input" : "Output", info, info->change_mask); + + if (info->change_mask & SPA_PORT_CHANGE_MASK_PARAMS) { + for (i = 0; i < info->n_params; i++) { + uint32_t idx; + + switch (info->paramsi.id) { + case SPA_PARAM_Latency: + idx = IDX_Latency; + break; + case SPA_PARAM_Tag: + idx = IDX_Tag; + break; + default: + continue; + } + + if (!this->add_listener && + this->convert_params_flagsidx == info->paramsi.flags) + continue; + + this->convert_params_flagsidx = info->paramsi.flags; + + if (this->add_listener) + continue; + + if (idx == IDX_Latency) { + this->in_recalc++; + res = recalc_latency(this, this->convert, direction, port_id, this->follower); + this->in_recalc--; + spa_log_debug(this->log, "latency: %d (%s)", res, + spa_strerror(res)); + } + if (idx == IDX_Tag) { + this->in_recalc++; + res = recalc_tag(this, this->convert, direction, port_id, this->follower); + this->in_recalc--; + spa_log_debug(this->log, "tag: %d (%s)", res, + spa_strerror(res)); + } + spa_log_debug(this->log, "param %d changed", info->paramsi.id); + } + } +} + static void convert_port_info(void *data, enum spa_direction direction, uint32_t port_id, const struct spa_port_info *info) @@ -961,10 +1098,11 @@ struct spa_port_info pi; if (direction != this->direction) { - /* skip the converter output port into the follower */ - if (port_id == 0) + if (port_id == 0) { + /* handle the converter output port into the follower separately */ + follower_convert_port_info(this, direction, port_id, info); return; - else + } else /* the monitor ports are exposed */ port_id--; } else if (info) { @@ -1072,40 +1210,6 @@ spa_zero(this->info.props); this->info.change_mask &= ~SPA_NODE_CHANGE_MASK_PROPS; - -} - -static int recalc_latency(struct impl *this, enum spa_direction direction, uint32_t port_id) -{ - struct spa_pod_builder b = { 0 }; - uint8_t buffer1024; - struct spa_pod *param; - uint32_t index = 0; - struct spa_latency_info latency; - int res; - - spa_log_debug(this->log, "%p: ", this); - - if (this->target == this->follower) - return 0; - - while (true) { - spa_pod_builder_init(&b, buffer, sizeof(buffer)); - if ((res = spa_node_port_enum_params_sync(this->follower, - direction, port_id, SPA_PARAM_Latency, - &index, NULL, ¶m, &b)) != 1) - return res; - if ((res = spa_latency_parse(param, &latency)) < 0) - return res; - if (latency.direction == direction) - break; - } - if ((res = spa_node_port_set_param(this->target, - SPA_DIRECTION_REVERSE(direction), 0, - SPA_PARAM_Latency, 0, param)) < 0) - return res; - - return 0; } static void follower_port_info(void *data, @@ -1126,9 +1230,10 @@ SPA_PORT_FLAG_PHYSICAL | SPA_PORT_FLAG_TERMINAL); - spa_log_debug(this->log, "%p: follower port info %s %p %08"PRIx64, this, + spa_log_debug(this->log, "%p: follower port info %s %p %08"PRIx64" recalc:%u", this, this->direction == SPA_DIRECTION_INPUT ? - "Input" : "Output", info, info->change_mask); + "Input" : "Output", info, info->change_mask, + this->in_recalc); if (info->change_mask & SPA_PORT_CHANGE_MASK_PARAMS) { for (i = 0; i < info->n_params; i++) { @@ -1144,6 +1249,9 @@ case SPA_PARAM_Latency: idx = IDX_Latency; break; + case SPA_PARAM_Tag: + idx = IDX_Tag; + break; default: continue; } @@ -1161,11 +1269,16 @@ if (this->add_listener) continue; - if (idx == IDX_Latency) { - res = recalc_latency(this, direction, port_id); + if (idx == IDX_Latency && this->in_recalc == 0) { + res = recalc_latency(this, this->follower, direction, port_id, this->target); spa_log_debug(this->log, "latency: %d (%s)", res, spa_strerror(res)); } + if (idx == IDX_Tag && this->in_recalc == 0) { + res = recalc_tag(this, this->follower, direction, port_id, this->target); + spa_log_debug(this->log, "tag: %d (%s)", res, + spa_strerror(res)); + } if (idx == IDX_EnumFormat) { spa_log_debug(this->log, "new formats"); configure_format(this, 0, NULL); @@ -1390,7 +1503,6 @@ const struct spa_pod *param) { struct impl *this = object; - int res; spa_return_val_if_fail(this != NULL, -EINVAL); @@ -1399,18 +1511,8 @@ if (direction != this->direction) port_id++; - if ((res = spa_node_port_set_param(this->target, direction, port_id, id, - flags, param)) < 0) - return res; - - if ((id == SPA_PARAM_Latency) && - direction == this->direction) { - if ((res = spa_node_port_set_param(this->follower, direction, 0, id, - flags, param)) < 0) - return res; - } - - return res; + return spa_node_port_set_param(this->target, direction, port_id, id, + flags, param); } static int @@ -1707,6 +1809,7 @@ this->paramsIDX_PortConfig = SPA_PARAM_INFO(SPA_PARAM_PortConfig, SPA_PARAM_INFO_READWRITE); this->paramsIDX_Latency = SPA_PARAM_INFO(SPA_PARAM_Latency, SPA_PARAM_INFO_READWRITE); this->paramsIDX_ProcessLatency = SPA_PARAM_INFO(SPA_PARAM_ProcessLatency, SPA_PARAM_INFO_READWRITE); + this->paramsIDX_Tag = SPA_PARAM_INFO(SPA_PARAM_Tag, SPA_PARAM_INFO_READWRITE); this->info.params = this->params; this->info.n_params = N_NODE_PARAMS;
View file
pipewire-0.3.79.tar.gz/spa/plugins/audioconvert/audioconvert.c -> pipewire-0.3.80.tar.gz/spa/plugins/audioconvert/audioconvert.c
Changed
@@ -24,6 +24,7 @@ #include <spa/param/audio/format-utils.h> #include <spa/param/param.h> #include <spa/param/latency-utils.h> +#include <spa/param/tag-utils.h> #include <spa/pod/filter.h> #include <spa/pod/dynamic.h> #include <spa/debug/types.h> @@ -138,13 +139,17 @@ #define IDX_Format 3 #define IDX_Buffers 4 #define IDX_Latency 5 -#define N_PORT_PARAMS 6 +#define IDX_Tag 6 +#define N_PORT_PARAMS 7 struct spa_param_info paramsN_PORT_PARAMS; char position16; struct buffer buffersMAX_BUFFERS; uint32_t n_buffers; + struct spa_latency_info latency2; + unsigned int have_latency:1; + struct spa_audio_info format; unsigned int have_format:1; unsigned int is_dsp:1; @@ -170,7 +175,7 @@ struct spa_audio_info format; unsigned int have_format:1; unsigned int have_profile:1; - struct spa_latency_info latency; + struct spa_pod *tag; uint32_t remapMAX_PORTS; @@ -323,6 +328,8 @@ } port->direction = direction; port->id = port_id; + port->latencySPA_DIRECTION_INPUT = SPA_LATENCY_INFO(SPA_DIRECTION_INPUT); + port->latencySPA_DIRECTION_OUTPUT = SPA_LATENCY_INFO(SPA_DIRECTION_OUTPUT); name = spa_debug_type_find_short_name(spa_type_audio_channel, position); snprintf(port->position, sizeof(port->position), "%s", name ? name : "UNK"); @@ -339,6 +346,7 @@ port->paramsIDX_Format = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE); port->paramsIDX_Buffers = SPA_PARAM_INFO(SPA_PARAM_Buffers, 0); port->paramsIDX_Latency = SPA_PARAM_INFO(SPA_PARAM_Latency, SPA_PARAM_INFO_READWRITE); + port->paramsIDX_Tag = SPA_PARAM_INFO(SPA_PARAM_Tag, SPA_PARAM_INFO_READWRITE); port->info.params = port->params; port->info.n_params = N_PORT_PARAMS; @@ -996,7 +1004,7 @@ spa_log_info(this->log, "generating ramp up sequence from %f to %f with a" " step value %f at scale %d", p->prev_volume, p->volume, volume_step, p->vrp.scale); do { - // spa_log_debug(this->log, "volume accum %f", get_volume_at_scale(this, volume_accum)); + spa_log_trace(this->log, "volume accum %f", get_volume_at_scale(this, volume_accum)); spa_pod_builder_control(&b.b, volume_offs, SPA_CONTROL_Properties); spa_pod_builder_add_object(&b.b, SPA_TYPE_OBJECT_Props, 0, @@ -1025,7 +1033,7 @@ spa_log_info(this->log, "generating ramp down sequence from %f to %f with a" " step value %f at scale %d", p->prev_volume, p->volume, volume_step, p->vrp.scale); do { - // spa_log_debug(this->log, "volume accum %f", get_volume_at_scale(this, volume_accum)); + spa_log_trace(this->log, "volume accum %f", get_volume_at_scale(this, volume_accum)); spa_pod_builder_control(&b.b, volume_offs, SPA_CONTROL_Properties); spa_pod_builder_add_object(&b.b, SPA_TYPE_OBJECT_Props, 0, @@ -2101,7 +2109,23 @@ uint32_t idx = result.index; if (port->is_monitor) idx = idx ^ 1; - param = spa_latency_build(&b, id, &this->diridx.latency); + param = spa_latency_build(&b, id, &port->latencyidx); + break; + } + default: + return 0; + } + break; + case SPA_PARAM_Tag: + switch (result.index) { + case 0: case 1: + { + uint32_t idx = result.index; + if (port->is_monitor) + idx = idx ^ 1; + param = this->diridx.tag; + if (param == NULL) + goto next; break; } default: @@ -2142,33 +2166,111 @@ struct impl *this = object; struct port *port, *oport; enum spa_direction other = SPA_DIRECTION_REVERSE(direction); + struct spa_latency_info info; + bool have_latency, emit = false;; uint32_t i; - spa_log_debug(this->log, "%p: set latency direction:%d id:%d", - this, direction, port_id); + spa_log_debug(this->log, "%p: set latency direction:%d id:%d %p", + this, direction, port_id, latency); port = GET_PORT(this, direction, port_id); if (port->is_monitor) return 0; if (latency == NULL) { - this->dirother.latency = SPA_LATENCY_INFO(other); + info = SPA_LATENCY_INFO(other); + have_latency = false; } else { - struct spa_latency_info info; if (spa_latency_parse(latency, &info) < 0 || info.direction != other) return -EINVAL; - this->dirother.latency = info; + have_latency = true; } + emit = spa_latency_info_compare(&info, &port->latencyother) != 0 || + port->have_latency == have_latency; + + port->latencyother = info; + port->have_latency = have_latency; + + spa_log_debug(this->log, "%p: set %s latency %f-%f %d-%d %"PRIu64"-%"PRIu64, this, + info.direction == SPA_DIRECTION_INPUT ? "input" : "output", + info.min_quantum, info.max_quantum, + info.min_rate, info.max_rate, + info.min_ns, info.max_ns); + + spa_latency_info_combine_start(&info, other); + for (i = 0; i < this->dirdirection.n_ports; i++) { + oport = GET_PORT(this, direction, i); + if (oport->is_monitor || !oport->have_latency) + continue; + spa_log_debug(this->log, "%p: combine %d", this, i); + spa_latency_info_combine(&info, &oport->latencyother); + } + spa_latency_info_combine_finish(&info); + + spa_log_debug(this->log, "%p: combined %s latency %f-%f %d-%d %"PRIu64"-%"PRIu64, this, + info.direction == SPA_DIRECTION_INPUT ? "input" : "output", + info.min_quantum, info.max_quantum, + info.min_rate, info.max_rate, + info.min_ns, info.max_ns); for (i = 0; i < this->dirother.n_ports; i++) { oport = GET_PORT(this, other, i); - oport->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS; - oport->paramsIDX_Latency.user++; - emit_port_info(this, oport, false); + + spa_log_debug(this->log, "%p: change %d", this, i); + if (spa_latency_info_compare(&info, &oport->latencyother) != 0) { + oport->latencyother = info; + oport->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS; + oport->paramsIDX_Latency.user++; + emit_port_info(this, oport, false); + } + } + if (emit) { + port->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS; + port->paramsIDX_Latency.user++; + emit_port_info(this, port, false); + } + return 0; +} + +static int port_set_tag(void *object, + enum spa_direction direction, + uint32_t port_id, + uint32_t flags, + const struct spa_pod *tag) +{ + struct impl *this = object; + struct port *port, *oport; + enum spa_direction other = SPA_DIRECTION_REVERSE(direction); + uint32_t i; + + spa_log_debug(this->log, "%p: set tag direction:%d id:%d %p", + this, direction, port_id, tag); + + port = GET_PORT(this, direction, port_id); + if (port->is_monitor) + return 0; + + if (tag != NULL) { + struct spa_tag_info info; + void *state = NULL; + if (spa_tag_parse(tag, &info, &state) < 0 || + info.direction != other) + return -EINVAL; + } + if (spa_tag_compare(tag, this->dirother.tag) != 0) { + free(this->dirother.tag); + this->dirother.tag = tag ? spa_pod_copy(tag) : NULL; + + for (i = 0; i < this->dirother.n_ports; i++) { + oport = GET_PORT(this, other, i); + oport->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS; + oport->paramsIDX_Tag.user++; + emit_port_info(this, oport, false); + } } port->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS; - port->paramsIDX_Latency.user++; + port->paramsIDX_Tag.user++; emit_port_info(this, port, false); return 0; } @@ -2296,6 +2398,8 @@ switch (id) { case SPA_PARAM_Latency: return port_set_latency(this, direction, port_id, flags, param); + case SPA_PARAM_Tag: + return port_set_tag(this, direction, port_id, flags, param); case SPA_PARAM_Format: return port_set_format(this, direction, port_id, flags, param); default: @@ -2636,7 +2740,7 @@ struct buffer *buf, *out_bufsMAX_PORTS; struct spa_data *bd; struct dir *dir; - int tmp = 0, res = 0, missed; + int tmp = 0, res = 0, suppressed; bool in_passthrough, mix_passthrough, resample_passthrough, out_passthrough; bool in_avail = false, flush_in = false, flush_out = false; bool draining = false, in_empty = this->out_offset == 0; @@ -2804,9 +2908,9 @@ buf = peek_buffer(this, port); if (buf == NULL && port->n_buffers > 0 && - (missed = spa_ratelimit_test(&this->rate_limit, current_time)) >= 0) { - spa_log_warn(this->log, "%p: (%d missed) out of buffers on port %d %d", - this, missed, port->id, port->n_buffers); + (suppressed = spa_ratelimit_test(&this->rate_limit, current_time)) >= 0) { + spa_log_warn(this->log, "%p: (%d suppressed) out of buffers on port %d %d", + this, suppressed, port->id, port->n_buffers); } } out_bufsi = buf; @@ -3123,19 +3227,27 @@ return 0; } +static void free_dir(struct dir *dir) +{ + uint32_t i; + for (i = 0; i < MAX_PORTS; i++) + free(dir->portsi); + if (dir->conv.free) + convert_free(&dir->conv); + free(dir->tag); +} + static int impl_clear(struct spa_handle *handle) { struct impl *this; - uint32_t i; spa_return_val_if_fail(handle != NULL, -EINVAL); this = (struct impl *) handle; - for (i = 0; i < MAX_PORTS; i++) - free(this->dirSPA_DIRECTION_INPUT.portsi); - for (i = 0; i < MAX_PORTS; i++) - free(this->dirSPA_DIRECTION_OUTPUT.portsi); + free_dir(&this->dirSPA_DIRECTION_INPUT); + free_dir(&this->dirSPA_DIRECTION_OUTPUT); + free(this->empty); free(this->scratch); free(this->tmp0); @@ -3143,10 +3255,6 @@ if (this->resample.free) resample_free(&this->resample); - if (this->dir0.conv.free) - convert_free(&this->dir0.conv); - if (this->dir1.conv.free) - convert_free(&this->dir1.conv); if (this->wav_file != NULL) wav_file_close(this->wav_file); free (this->vol_ramp_sequence); @@ -3258,9 +3366,7 @@ this->props.monitor.n_volumes = this->props.n_channels; this->dirSPA_DIRECTION_INPUT.direction = SPA_DIRECTION_INPUT; - this->dirSPA_DIRECTION_INPUT.latency = SPA_LATENCY_INFO(SPA_DIRECTION_INPUT); this->dirSPA_DIRECTION_OUTPUT.direction = SPA_DIRECTION_OUTPUT; - this->dirSPA_DIRECTION_OUTPUT.latency = SPA_LATENCY_INFO(SPA_DIRECTION_OUTPUT); this->node.iface = SPA_INTERFACE_INIT( SPA_TYPE_INTERFACE_Node,
View file
pipewire-0.3.79.tar.gz/spa/plugins/audioconvert/meson.build -> pipewire-0.3.80.tar.gz/spa/plugins/audioconvert/meson.build
Changed
@@ -7,6 +7,13 @@ simd_cargs = simd_dependencies = +opt_flags = +if host_machine.cpu_family() != 'alpha' + opt_flags += '-Ofast' +else + opt_flags += '-O3' +endif + audioconvert_c = static_library('audioconvert_c', 'channelmix-ops-c.c', 'biquad.c', @@ -15,7 +22,7 @@ 'peaks-ops-c.c', 'resample-native-c.c', 'fmt-ops-c.c' , - c_args : '-Ofast', '-ffast-math', + c_args : opt_flags , dependencies : spa_dep , install : false ) @@ -27,7 +34,7 @@ 'volume-ops-sse.c', 'peaks-ops-sse.c', 'channelmix-ops-sse.c' , - c_args : sse_args, '-Ofast', '-DHAVE_SSE', + c_args : sse_args, opt_flags, '-DHAVE_SSE', dependencies : spa_dep , install : false )
View file
pipewire-0.3.79.tar.gz/spa/plugins/audiomixer/audiomixer.c -> pipewire-0.3.80.tar.gz/spa/plugins/audiomixer/audiomixer.c
Changed
@@ -9,6 +9,7 @@ #include <spa/support/plugin.h> #include <spa/support/log.h> #include <spa/support/cpu.h> +#include <spa/support/loop.h> #include <spa/utils/list.h> #include <spa/utils/names.h> #include <spa/utils/string.h> @@ -88,6 +89,9 @@ struct spa_cpu *cpu; uint32_t cpu_flags; uint32_t max_align; + + struct spa_loop *data_loop; + uint32_t quantum_limit; struct mix_ops ops; @@ -525,6 +529,8 @@ port = GET_PORT(this, direction, port_id); + spa_return_val_if_fail(!this->started || port->io == NULL, -EIO); + if (format == NULL) { if (port->have_format) { port->have_format = false; @@ -632,6 +638,8 @@ port = GET_PORT(this, direction, port_id); + spa_return_val_if_fail(!this->started || port->io == NULL, -EIO); + clear_buffers(this, port); if (n_buffers > 0 && !port->have_format) @@ -670,6 +678,19 @@ return 0; } +struct io_info { + struct port *port; + void *data; +}; + +static int do_port_set_io(struct spa_loop *loop, bool async, uint32_t seq, + const void *data, size_t size, void *user_data) +{ + struct io_info *info = user_data; + info->port->io = info->data; + return 0; +} + static int impl_node_port_set_io(void *object, enum spa_direction direction, uint32_t port_id, @@ -677,6 +698,7 @@ { struct impl *this = object; struct port *port; + struct io_info info; spa_return_val_if_fail(this != NULL, -EINVAL); @@ -686,10 +708,13 @@ spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL); port = GET_PORT(this, direction, port_id); + info.port = port; + info.data = data; switch (id) { case SPA_IO_Buffers: - port->io = data; + spa_loop_invoke(this->data_loop, + do_port_set_io, SPA_ID_INVALID, NULL, 0, true, &info); break; default: return -ENOENT;
View file
pipewire-0.3.79.tar.gz/spa/plugins/audiomixer/mixer-dsp.c -> pipewire-0.3.80.tar.gz/spa/plugins/audiomixer/mixer-dsp.c
Changed
@@ -9,6 +9,7 @@ #include <spa/support/plugin.h> #include <spa/support/log.h> #include <spa/support/cpu.h> +#include <spa/support/loop.h> #include <spa/utils/list.h> #include <spa/utils/names.h> #include <spa/utils/string.h> @@ -85,6 +86,8 @@ uint32_t cpu_flags; uint32_t max_align; + struct spa_loop *data_loop; + uint32_t quantum_limit; struct mix_ops ops; @@ -474,6 +477,8 @@ port = GET_PORT(this, direction, port_id); + spa_return_val_if_fail(!this->started || port->io == NULL, -EIO); + if (format == NULL) { if (port->have_format) { port->have_format = false; @@ -569,6 +574,8 @@ port = GET_PORT(this, direction, port_id); + spa_return_val_if_fail(!this->started || port->io == NULL, -EIO); + clear_buffers(this, port); if (n_buffers > 0 && !port->have_format) @@ -606,6 +613,19 @@ return 0; } +struct io_info { + struct port *port; + void *data; +}; + +static int do_port_set_io(struct spa_loop *loop, bool async, uint32_t seq, + const void *data, size_t size, void *user_data) +{ + struct io_info *info = user_data; + info->port->io = info->data; + return 0; +} + static int impl_node_port_set_io(void *object, enum spa_direction direction, uint32_t port_id, @@ -613,6 +633,7 @@ { struct impl *this = object; struct port *port; + struct io_info info; spa_return_val_if_fail(this != NULL, -EINVAL); @@ -622,10 +643,13 @@ spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL); port = GET_PORT(this, direction, port_id); + info.port = port; + info.data = data; switch (id) { case SPA_IO_Buffers: - port->io = data; + spa_loop_invoke(this->data_loop, + do_port_set_io, SPA_ID_INVALID, NULL, 0, true, &info); break; default: return -ENOENT; @@ -833,6 +857,12 @@ this->log = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log); spa_log_topic_init(this->log, log_topic); + this->data_loop = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_DataLoop); + if (this->data_loop == NULL) { + spa_log_error(this->log, "a data loop is needed"); + return -EINVAL; + } + this->cpu = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_CPU); if (this->cpu) { this->cpu_flags = spa_cpu_get_flags(this->cpu);
View file
pipewire-0.3.79.tar.gz/spa/plugins/avb/avb-pcm-sink.c -> pipewire-0.3.80.tar.gz/spa/plugins/avb/avb-pcm-sink.c
Changed
@@ -572,7 +572,9 @@ case SPA_PARAM_Latency: { struct spa_latency_info info; - if ((res = spa_latency_parse(param, &info)) < 0) + if (param == NULL) + info = SPA_LATENCY_INFO(SPA_DIRECTION_REVERSE(direction)); + else if ((res = spa_latency_parse(param, &info)) < 0) return res; if (direction == info.direction) return -EINVAL;
View file
pipewire-0.3.79.tar.gz/spa/plugins/avb/avb-pcm-source.c -> pipewire-0.3.80.tar.gz/spa/plugins/avb/avb-pcm-source.c
Changed
@@ -572,7 +572,9 @@ case SPA_PARAM_Latency: { struct spa_latency_info info; - if ((res = spa_latency_parse(param, &info)) < 0) + if (param == NULL) + info = SPA_LATENCY_INFO(SPA_DIRECTION_REVERSE(direction)); + else if ((res = spa_latency_parse(param, &info)) < 0) return res; if (direction == info.direction) return -EINVAL;
View file
pipewire-0.3.79.tar.gz/spa/plugins/bluez5/bluez5-dbus.c -> pipewire-0.3.80.tar.gz/spa/plugins/bluez5/bluez5-dbus.c
Changed
@@ -526,6 +526,26 @@ } } +static enum spa_bt_profile swap_profile(enum spa_bt_profile profile) +{ + switch (profile) { + case SPA_BT_PROFILE_A2DP_SOURCE: + return SPA_BT_PROFILE_A2DP_SINK; + case SPA_BT_PROFILE_A2DP_SINK: + return SPA_BT_PROFILE_A2DP_SOURCE; + case SPA_BT_PROFILE_BAP_SOURCE: + return SPA_BT_PROFILE_BAP_SINK; + case SPA_BT_PROFILE_BAP_SINK: + return SPA_BT_PROFILE_BAP_SOURCE; + case SPA_BT_PROFILE_BAP_BROADCAST_SOURCE: + return SPA_BT_PROFILE_BAP_BROADCAST_SINK; + case SPA_BT_PROFILE_BAP_BROADCAST_SINK: + return SPA_BT_PROFILE_BAP_BROADCAST_SOURCE; + default: + return SPA_BT_PROFILE_NULL; + } +} + static bool endpoint_should_be_registered(struct spa_bt_monitor *monitor, const struct media_codec *codec, enum spa_bt_media_direction direction) @@ -2230,11 +2250,11 @@ return device->adapter && device->address; } -bool spa_bt_device_supports_media_codec(struct spa_bt_device *device, const struct media_codec *codec, bool sink) +bool spa_bt_device_supports_media_codec(struct spa_bt_device *device, const struct media_codec *codec, enum spa_bt_profile profile) { struct spa_bt_monitor *monitor = device->monitor; struct spa_bt_remote_endpoint *ep; - enum spa_bt_profile codec_profile; + enum spa_bt_profile codec_target_profile; struct spa_bt_transport *t; const struct { enum spa_bluetooth_audio_codec codec; uint32_t mask; } quirks = { { SPA_BLUETOOTH_AUDIO_CODEC_SBC_XQ, SPA_BT_FEATURE_SBC_XQ }, @@ -2270,17 +2290,14 @@ return false; } + for (i = 0, codec_target_profile = 0; i < (size_t)SPA_BT_MEDIA_DIRECTION_LAST; ++i) + if (codec_has_direction(codec, i)) + codec_target_profile |= swap_profile(get_codec_profile(codec, i)); + spa_list_for_each(ep, &device->remote_endpoint_list, device_link) { - enum spa_bt_profile profile = spa_bt_profile_from_uuid(ep->uuid); - if (codec->bap) { - if ((profile == SPA_BT_PROFILE_BAP_BROADCAST_SINK) || (profile == SPA_BT_PROFILE_BAP_BROADCAST_SOURCE)) - codec_profile = sink ? SPA_BT_PROFILE_BAP_BROADCAST_SINK : SPA_BT_PROFILE_BAP_BROADCAST_SOURCE; - else - codec_profile = sink ? SPA_BT_PROFILE_BAP_SINK : SPA_BT_PROFILE_BAP_SOURCE; - } else - codec_profile = sink ? SPA_BT_PROFILE_A2DP_SINK : SPA_BT_PROFILE_A2DP_SOURCE; + enum spa_bt_profile ep_profile = spa_bt_profile_from_uuid(ep->uuid); - if (profile != codec_profile) + if (!(ep_profile & codec_target_profile & profile)) continue; if (media_codec_check_caps(codec, ep->codec, ep->capabilities, ep->capabilities_len, @@ -2296,15 +2313,7 @@ * can only know that the currently configured codec is supported. */ spa_list_for_each(t, &device->transport_list, device_link) { - if (codec->bap) { - if((t->profile == SPA_BT_PROFILE_BAP_BROADCAST_SINK) || (t->profile == SPA_BT_PROFILE_BAP_BROADCAST_SOURCE)) - codec_profile = sink ? SPA_BT_PROFILE_BAP_BROADCAST_SINK : SPA_BT_PROFILE_BAP_BROADCAST_SOURCE; - else - codec_profile = sink ? SPA_BT_PROFILE_BAP_SINK : SPA_BT_PROFILE_BAP_SOURCE; - } else - codec_profile = sink ? SPA_BT_PROFILE_A2DP_SINK : SPA_BT_PROFILE_A2DP_SOURCE; - - if (t->profile != codec_profile) + if (!(t->profile & codec_target_profile & profile)) continue; if (codec == t->media_codec) @@ -2314,7 +2323,7 @@ return false; } -const struct media_codec **spa_bt_device_get_supported_media_codecs(struct spa_bt_device *device, size_t *count, bool sink) +const struct media_codec **spa_bt_device_get_supported_media_codecs(struct spa_bt_device *device, size_t *count) { struct spa_bt_monitor *monitor = device->monitor; const struct media_codec * const * const media_codecs = monitor->media_codecs; @@ -2329,7 +2338,7 @@ j = 0; for (i = 0; media_codecsi != NULL; ++i) { - if (spa_bt_device_supports_media_codec(device, media_codecsi, sink)) { + if (spa_bt_device_supports_media_codec(device, media_codecsi, device->connected_profiles)) { supported_codecsj = media_codecsi; ++j; } @@ -2373,10 +2382,17 @@ return NULL; } -static struct spa_bt_device* create_bcast_device(struct spa_bt_monitor *monitor, - const char *object_path) +static struct spa_bt_device *create_bcast_device(struct spa_bt_monitor *monitor, const char *object_path) { struct spa_bt_device *d; + struct spa_bt_adapter *adapter; + + adapter = adapter_find(monitor, object_path); + if (adapter == NULL) { + spa_log_warn(monitor->log, "unknown adapter %s", object_path); + return NULL; + } + d = device_create(monitor, object_path); if (d == NULL) { spa_log_warn(monitor->log, "can't create Bluetooth device %s: %m", @@ -2384,22 +2400,12 @@ return NULL; } - d->adapter = adapter_find(monitor, object_path); - if (d->adapter == NULL) { - spa_log_warn(monitor->log, "unknown adapter %s", d->adapter_path); - } - d->adapter_path = d->adapter->path; - d->alias = strdup("bcast_device"); - d->name = strdup("bcast_device"); + d->adapter = adapter; + d->adapter_path = strdup(adapter->path); + d->alias = strdup(adapter->alias); + d->name = strdup(adapter->name); d->address = strdup("00:00:00:00:00:00"); - - spa_bt_device_check_profiles(d, false); - d->reconnect_state = BT_DEVICE_RECONNECT_INIT; - - if (!device_props_ready(d)) - { - return NULL; - } + d->reconnect_state = BT_DEVICE_RECONNECT_STOP; device_update_hw_volume_profiles(d); @@ -2439,6 +2445,7 @@ } else if (spa_streq(key, "Device")) { struct spa_bt_device *device; + device = spa_bt_device_find(monitor, value); if (device == NULL) { /* @@ -2447,11 +2454,11 @@ * This is done because BlueZ sets the adapter as the device * that is connected to for a broadcast sink endpoint/transport. */ - if(spa_streq(remote_endpoint->uuid, SPA_BT_UUID_BAP_BROADCAST_SINK)) { + if (spa_streq(remote_endpoint->uuid, SPA_BT_UUID_BAP_BROADCAST_SINK)) { device = create_bcast_device(monitor, value); - if(device == NULL) { + if (device == NULL) goto next; - } + remote_endpoint->acceptor = true; device_set_connected(device, 1); } else { @@ -3048,29 +3055,9 @@ spa_log_debug(monitor->log, "transport %p: %s=%s", transport, key, value); if (spa_streq(key, "UUID")) { - switch (spa_bt_profile_from_uuid(value)) { - case SPA_BT_PROFILE_A2DP_SOURCE: - transport->profile = SPA_BT_PROFILE_A2DP_SINK; - break; - case SPA_BT_PROFILE_A2DP_SINK: - transport->profile = SPA_BT_PROFILE_A2DP_SOURCE; - break; - case SPA_BT_PROFILE_BAP_SOURCE: - transport->profile = SPA_BT_PROFILE_BAP_SINK; - break; - case SPA_BT_PROFILE_BAP_SINK: - transport->profile = SPA_BT_PROFILE_BAP_SOURCE; - break; - case SPA_BT_PROFILE_BAP_BROADCAST_SOURCE: - transport->profile = SPA_BT_PROFILE_BAP_BROADCAST_SINK; - break; - case SPA_BT_PROFILE_BAP_BROADCAST_SINK: - transport->profile = SPA_BT_PROFILE_BAP_BROADCAST_SOURCE; - break; - default: + transport->profile = swap_profile(spa_bt_profile_from_uuid(value)); + if (transport->profile == SPA_BT_PROFILE_NULL) spa_log_warn(monitor->log, "unknown profile %s", value); - break; - } } else if (spa_streq(key, "State")) { enum spa_bt_transport_state state = spa_bt_transport_state_from_string(value); @@ -4154,7 +4141,7 @@ } for (i = 0; codecsi != NULL; ++i) { - if (spa_bt_device_supports_media_codec(device, codecsi, true)) { + if (spa_bt_device_supports_media_codec(device, codecsi, device->connected_profiles)) { preferred_codec = codecsi; break; }
View file
pipewire-0.3.79.tar.gz/spa/plugins/bluez5/bluez5-device.c -> pipewire-0.3.80.tar.gz/spa/plugins/bluez5/bluez5-device.c
Changed
@@ -190,10 +190,12 @@ *codecs = NULL; } -static const struct media_codec *get_supported_media_codec(struct impl *this, enum spa_bluetooth_audio_codec id, size_t *idx) +static const struct media_codec *get_supported_media_codec(struct impl *this, enum spa_bluetooth_audio_codec id, + size_t *idx, enum spa_bt_profile profile) { const struct media_codec *media_codec = NULL; size_t i; + for (i = 0; i < this->supported_codec_count; ++i) { if (this->supported_codecsi->id == id) { media_codec = this->supported_codecsi; @@ -201,6 +203,13 @@ *idx = i; } } + + if (!media_codec) + return NULL; + + if (!spa_bt_device_supports_media_codec(this->bt_dev, media_codec, profile)) + return NULL; + return media_codec; } @@ -625,6 +634,8 @@ char transport32, str_id32, object_path512; bool is_dyn_node = SPA_FLAG_IS_SET(id, DYNAMIC_NODE_ID_FLAG); + spa_log_debug(this->log, "node, transport:%p id:%08x factory:%s", t, id, factory_name); + snprintf(transport, sizeof(transport), "pointer:%p", t); items0 = SPA_DICT_ITEM_INIT(SPA_KEY_API_BLUEZ5_TRANSPORT, transport); items1 = SPA_DICT_ITEM_INIT(SPA_KEY_API_BLUEZ5_PROFILE, spa_bt_profile_name(t->profile)); @@ -1016,9 +1027,6 @@ } } } - - if (get_supported_media_codec(this, this->props.codec, NULL) == NULL) - this->props.codec = 0; break; case DEVICE_PROFILE_BAP: if (this->bt_dev->connected_profiles & (SPA_BT_PROFILE_BAP_SOURCE)) { @@ -1070,9 +1078,6 @@ DEVICE_ID_SOURCE, SPA_NAME_API_BLUEZ5_MEDIA_SOURCE, false); } } - - if (get_supported_media_codec(this, this->props.codec, NULL) == NULL) - this->props.codec = 0; break; case DEVICE_PROFILE_HSP_HFP: if (this->bt_dev->connected_profiles & SPA_BT_PROFILE_HEADSET_HEAD_UNIT) { @@ -1119,6 +1124,8 @@ static void emit_remove_nodes(struct impl *this) { + spa_log_debug(this->log, "remove nodes"); + remove_dynamic_node (&this->dyn_media_source); remove_dynamic_node (&this->dyn_media_sink); remove_dynamic_node (&this->dyn_sco_source); @@ -1278,11 +1285,9 @@ if (this->switching_codec) return; - if (this->bt_dev->connected_profiles & SPA_BT_PROFILE_MEDIA_SINK) { - free(this->supported_codecs); - this->supported_codecs = spa_bt_device_get_supported_media_codecs( - this->bt_dev, &this->supported_codec_count, true); - } + free(this->supported_codecs); + this->supported_codecs = spa_bt_device_get_supported_media_codecs( + this->bt_dev, &this->supported_codec_count); switch (this->profile) { case DEVICE_PROFILE_OFF: @@ -1297,8 +1302,6 @@ break; case DEVICE_PROFILE_A2DP: case DEVICE_PROFILE_BAP: - if (get_supported_media_codec(this, this->props.codec, NULL) == NULL) - this->props.codec = 0; nodes_changed = (connected_change & (SPA_BT_PROFILE_MEDIA_SINK | SPA_BT_PROFILE_MEDIA_SOURCE)); spa_log_debug(this->log, "profiles changed: media nodes changed: %d", @@ -1419,7 +1422,7 @@ if (device->connected_profiles & SPA_BT_PROFILE_A2DP_SINK) have_output = true; - media_codec = get_supported_media_codec(this, codec, NULL); + media_codec = get_supported_media_codec(this, codec, NULL, device->connected_profiles); if (media_codec && media_codec->duplex_codec) have_input = true; break; @@ -1533,7 +1536,7 @@ if (this->supported_codecs) free(this->supported_codecs); this->supported_codecs = spa_bt_device_get_supported_media_codecs( - this->bt_dev, &this->supported_codec_count, true); + this->bt_dev, &this->supported_codec_count); /* Prefer BAP, then A2DP, then HFP, then null, but select AG if the device appears not to have BAP_SINK, A2DP_SINK or any HEAD_UNIT profile */ @@ -1625,7 +1628,7 @@ n_sink++; if (codec) { size_t idx; - const struct media_codec *media_codec = get_supported_media_codec(this, codec, &idx); + const struct media_codec *media_codec = get_supported_media_codec(this, codec, &idx, profile); if (media_codec == NULL) { errno = EINVAL; return NULL; @@ -1686,7 +1689,7 @@ name = spa_bt_profile_name(profile); if (codec) { - media_codec = get_supported_media_codec(this, codec, &idx); + media_codec = get_supported_media_codec(this, codec, &idx, profile); if (media_codec == NULL) { errno = EINVAL; return NULL;
View file
pipewire-0.3.79.tar.gz/spa/plugins/bluez5/defs.h -> pipewire-0.3.80.tar.gz/spa/plugins/bluez5/defs.h
Changed
@@ -169,6 +169,7 @@ SPA_BT_MEDIA_SINK, SPA_BT_MEDIA_SOURCE_BROADCAST, SPA_BT_MEDIA_SINK_BROADCAST, + SPA_BT_MEDIA_DIRECTION_LAST, }; enum spa_bt_profile { @@ -538,8 +539,8 @@ int spa_bt_device_connect_profile(struct spa_bt_device *device, enum spa_bt_profile profile); int spa_bt_device_check_profiles(struct spa_bt_device *device, bool force); int spa_bt_device_ensure_media_codec(struct spa_bt_device *device, const struct media_codec * const *codecs); -bool spa_bt_device_supports_media_codec(struct spa_bt_device *device, const struct media_codec *codec, bool sink); -const struct media_codec **spa_bt_device_get_supported_media_codecs(struct spa_bt_device *device, size_t *count, bool sink); +bool spa_bt_device_supports_media_codec(struct spa_bt_device *device, const struct media_codec *codec, enum spa_bt_profile profile); +const struct media_codec **spa_bt_device_get_supported_media_codecs(struct spa_bt_device *device, size_t *count); int spa_bt_device_ensure_hfp_codec(struct spa_bt_device *device, unsigned int codec); int spa_bt_device_supports_hfp_codec(struct spa_bt_device *device, unsigned int codec); int spa_bt_device_release_transports(struct spa_bt_device *device);
View file
pipewire-0.3.79.tar.gz/spa/plugins/bluez5/media-sink.c -> pipewire-0.3.80.tar.gz/spa/plugins/bluez5/media-sink.c
Changed
@@ -1694,8 +1694,6 @@ port->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS; if (port->have_format) { - port->info.change_mask |= SPA_PORT_CHANGE_MASK_FLAGS; - port->info.flags = SPA_PORT_FLAG_LIVE; port->info.change_mask |= SPA_PORT_CHANGE_MASK_RATE; port->info.rate = SPA_FRACTION(1, port->current_format.info.raw.rate); port->paramsIDX_Format = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_READWRITE); @@ -2078,7 +2076,9 @@ port->info_all = SPA_PORT_CHANGE_MASK_FLAGS | SPA_PORT_CHANGE_MASK_PARAMS; port->info = SPA_PORT_INFO_INIT(); - port->info.flags = 0; + port->info.flags = SPA_PORT_FLAG_LIVE | + SPA_PORT_FLAG_PHYSICAL | + SPA_PORT_FLAG_TERMINAL; port->paramsIDX_EnumFormat = SPA_PARAM_INFO(SPA_PARAM_EnumFormat, SPA_PARAM_INFO_READ); port->paramsIDX_Meta = SPA_PARAM_INFO(SPA_PARAM_Meta, SPA_PARAM_INFO_READ); port->paramsIDX_IO = SPA_PARAM_INFO(SPA_PARAM_IO, SPA_PARAM_INFO_READ);
View file
pipewire-0.3.79.tar.gz/spa/plugins/bluez5/media-source.c -> pipewire-0.3.80.tar.gz/spa/plugins/bluez5/media-source.c
Changed
@@ -1137,8 +1137,6 @@ port->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS; if (port->have_format) { - port->info.change_mask |= SPA_PORT_CHANGE_MASK_FLAGS; - port->info.flags = SPA_PORT_FLAG_LIVE; port->info.change_mask |= SPA_PORT_CHANGE_MASK_RATE; port->info.rate = SPA_FRACTION(1, port->current_format.info.raw.rate); port->paramsIDX_Format = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_READWRITE); @@ -1669,6 +1667,7 @@ port->info = SPA_PORT_INFO_INIT(); port->info.change_mask = SPA_PORT_CHANGE_MASK_FLAGS; port->info.flags = SPA_PORT_FLAG_LIVE | + SPA_PORT_FLAG_PHYSICAL | SPA_PORT_FLAG_TERMINAL; port->paramsIDX_EnumFormat = SPA_PARAM_INFO(SPA_PARAM_EnumFormat, SPA_PARAM_INFO_READ); port->paramsIDX_Meta = SPA_PARAM_INFO(SPA_PARAM_Meta, SPA_PARAM_INFO_READ);
View file
pipewire-0.3.79.tar.gz/spa/plugins/bluez5/midi-node.c -> pipewire-0.3.80.tar.gz/spa/plugins/bluez5/midi-node.c
Changed
@@ -2033,7 +2033,9 @@ SPA_PORT_CHANGE_MASK_PARAMS; port->info = SPA_PORT_INFO_INIT(); port->info.change_mask = SPA_PORT_CHANGE_MASK_FLAGS; - port->info.flags = SPA_PORT_FLAG_LIVE; + port->info.flags = SPA_PORT_FLAG_LIVE | + SPA_PORT_FLAG_PHYSICAL | + SPA_PORT_FLAG_TERMINAL; port->paramsIDX_EnumFormat = SPA_PARAM_INFO(SPA_PARAM_EnumFormat, SPA_PARAM_INFO_READ); port->paramsIDX_Meta = SPA_PARAM_INFO(SPA_PARAM_Meta, SPA_PARAM_INFO_READ); port->paramsIDX_IO = SPA_PARAM_INFO(SPA_PARAM_IO, SPA_PARAM_INFO_READ);
View file
pipewire-0.3.79.tar.gz/spa/plugins/bluez5/sco-sink.c -> pipewire-0.3.80.tar.gz/spa/plugins/bluez5/sco-sink.c
Changed
@@ -1157,8 +1157,6 @@ port->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS; if (port->have_format) { - port->info.change_mask |= SPA_PORT_CHANGE_MASK_FLAGS; - port->info.flags = SPA_PORT_FLAG_LIVE; port->info.change_mask |= SPA_PORT_CHANGE_MASK_RATE; port->info.rate = SPA_FRACTION(1, port->current_format.info.raw.rate); port->paramsIDX_Format = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_READWRITE); @@ -1499,7 +1497,9 @@ port->info_all = SPA_PORT_CHANGE_MASK_FLAGS | SPA_PORT_CHANGE_MASK_PARAMS; port->info = SPA_PORT_INFO_INIT(); - port->info.flags = 0; + port->info.flags = SPA_PORT_FLAG_LIVE | + SPA_PORT_FLAG_PHYSICAL | + SPA_PORT_FLAG_TERMINAL; port->paramsIDX_EnumFormat = SPA_PARAM_INFO(SPA_PARAM_EnumFormat, SPA_PARAM_INFO_READ); port->paramsIDX_Meta = SPA_PARAM_INFO(SPA_PARAM_Meta, SPA_PARAM_INFO_READ); port->paramsIDX_IO = SPA_PARAM_INFO(SPA_PARAM_IO, SPA_PARAM_INFO_READ);
View file
pipewire-0.3.79.tar.gz/spa/plugins/bluez5/sco-source.c -> pipewire-0.3.80.tar.gz/spa/plugins/bluez5/sco-source.c
Changed
@@ -1135,8 +1135,6 @@ port->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS; if (port->have_format) { - port->info.change_mask |= SPA_PORT_CHANGE_MASK_FLAGS; - port->info.flags = SPA_PORT_FLAG_LIVE; port->info.change_mask |= SPA_PORT_CHANGE_MASK_RATE; port->info.rate = SPA_FRACTION(1, port->current_format.info.raw.rate); port->paramsIDX_Format = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_READWRITE); @@ -1601,6 +1599,7 @@ port->info = SPA_PORT_INFO_INIT(); port->info.change_mask = SPA_PORT_CHANGE_MASK_FLAGS; port->info.flags = SPA_PORT_FLAG_LIVE | + SPA_PORT_FLAG_PHYSICAL | SPA_PORT_FLAG_TERMINAL; port->paramsIDX_EnumFormat = SPA_PARAM_INFO(SPA_PARAM_EnumFormat, SPA_PARAM_INFO_READ); port->paramsIDX_Meta = SPA_PARAM_INFO(SPA_PARAM_Meta, SPA_PARAM_INFO_READ);
View file
pipewire-0.3.79.tar.gz/spa/plugins/control/mixer.c -> pipewire-0.3.80.tar.gz/spa/plugins/control/mixer.c
Changed
@@ -6,9 +6,10 @@ #include <string.h> #include <stdio.h> -#include <spa/support/plugin.h> -#include <spa/support/log.h> #include <spa/support/cpu.h> +#include <spa/support/log.h> +#include <spa/support/loop.h> +#include <spa/support/plugin.h> #include <spa/utils/list.h> #include <spa/utils/names.h> #include <spa/utils/string.h> @@ -59,6 +60,8 @@ struct spa_log *log; + struct spa_loop *data_loop; + uint64_t info_all; struct spa_node_info info; struct spa_param_info params8; @@ -412,6 +415,8 @@ port = GET_PORT(this, direction, port_id); + spa_return_val_if_fail(!this->started || port->io == NULL, -EIO); + if (format == NULL) { if (port->have_format) { port->have_format = false; @@ -489,6 +494,8 @@ spa_log_debug(this->log, NAME " %p: use buffers %d on port %d:%d", this, n_buffers, direction, port_id); + spa_return_val_if_fail(!this->started || port->io == NULL, -EIO); + clear_buffers(this, port); if (n_buffers > 0 && !port->have_format) @@ -517,6 +524,19 @@ return 0; } +struct io_info { + struct port *port; + void *data; +}; + +static int do_port_set_io(struct spa_loop *loop, bool async, uint32_t seq, + const void *data, size_t size, void *user_data) +{ + struct io_info *info = user_data; + info->port->io = info->data; + return 0; +} + static int impl_node_port_set_io(void *object, enum spa_direction direction, uint32_t port_id, @@ -524,18 +544,23 @@ { struct impl *this = object; struct port *port; + struct io_info info; spa_return_val_if_fail(this != NULL, -EINVAL); - spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL); - - port = GET_PORT(this, direction, port_id); spa_log_debug(this->log, NAME " %p: port %d:%d io %d %p/%zd", this, direction, port_id, id, data, size); + spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL); + + port = GET_PORT(this, direction, port_id); + info.port = port; + info.data = data; + switch (id) { case SPA_IO_Buffers: - port->io = data; + spa_loop_invoke(this->data_loop, + do_port_set_io, SPA_ID_INVALID, NULL, 0, true, &info); break; default: return -ENOENT; @@ -793,6 +818,12 @@ this->log = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log); + this->data_loop = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_DataLoop); + if (this->data_loop == NULL) { + spa_log_error(this->log, "a data loop is needed"); + return -EINVAL; + } + spa_hook_list_init(&this->hooks); this->node.iface = SPA_INTERFACE_INIT(
View file
pipewire-0.3.79.tar.gz/spa/plugins/support/node-driver.c -> pipewire-0.3.80.tar.gz/spa/plugins/support/node-driver.c
Changed
@@ -241,7 +241,7 @@ if ((res = spa_system_timerfd_read(this->data_system, this->timer_source.fd, &expirations)) < 0) { - if (res != EAGAIN) + if (res != -EAGAIN) spa_log_error(this->log, NAME " %p: timerfd error: %s", this, spa_strerror(res)); return;
View file
pipewire-0.3.79.tar.gz/spa/plugins/support/null-audio-sink.c -> pipewire-0.3.80.tar.gz/spa/plugins/support/null-audio-sink.c
Changed
@@ -273,7 +273,7 @@ if ((res = spa_system_timerfd_read(this->data_system, this->timer_source.fd, &expirations)) < 0) { - if (res != EAGAIN) + if (res != -EAGAIN) spa_log_error(this->log, NAME " %p: timerfd error: %s", this, spa_strerror(res)); return;
View file
pipewire-0.3.79.tar.gz/spa/plugins/v4l2/v4l2-source.c -> pipewire-0.3.80.tar.gz/spa/plugins/v4l2/v4l2-source.c
Changed
@@ -729,7 +729,9 @@ case SPA_PARAM_Latency: { struct spa_latency_info info; - if ((res = spa_latency_parse(param, &info)) < 0) + if (param == NULL) + info = SPA_LATENCY_INFO(SPA_DIRECTION_REVERSE(direction)); + else if ((res = spa_latency_parse(param, &info)) < 0) return res; if (direction == info.direction) return -EINVAL;
View file
pipewire-0.3.79.tar.gz/spa/plugins/v4l2/v4l2-udev.c -> pipewire-0.3.80.tar.gz/spa/plugins/v4l2/v4l2-udev.c
Changed
@@ -33,10 +33,9 @@ #define ACTION_DISABLE 2 struct device { - struct impl *impl; uint32_t id; struct udev_device *dev; - struct spa_source notify; + int inotify_wd; unsigned int accessible:1; unsigned int ignored:1; unsigned int emitted:1; @@ -61,79 +60,91 @@ uint32_t n_devices; struct spa_source source; + struct spa_source notify; }; -static int stop_inotify(struct device *dev); -static int start_inotify(struct device *dev); - -static int impl_udev_open(struct impl *impl) +static int impl_udev_open(struct impl *this) { - if (impl->udev == NULL) { - impl->udev = udev_new(); - if (impl->udev == NULL) + if (this->udev == NULL) { + this->udev = udev_new(); + if (this->udev == NULL) return -ENOMEM; } return 0; } -static int impl_udev_close(struct impl *impl) +static int impl_udev_close(struct impl *this) { - if (impl->udev != NULL) - udev_unref(impl->udev); - impl->udev = NULL; + if (this->udev != NULL) + udev_unref(this->udev); + this->udev = NULL; return 0; } -static struct device *add_device(struct impl *impl, uint32_t id, struct udev_device *dev) +static void start_watching_device(struct impl *this, struct device *device) +{ + if (this->notify.fd < 0 || device->inotify_wd >= 0) + return; + + char path64; + snprintf(path, sizeof(path), "/dev/video%" PRIu32, device->id); + + device->inotify_wd = inotify_add_watch(this->notify.fd, path, IN_ATTRIB); +} + +static void stop_watching_device(struct impl *this, struct device *device) +{ + if (device->inotify_wd < 0) + return; + + spa_assert(this->notify.fd >= 0); + + inotify_rm_watch(this->notify.fd, device->inotify_wd); + device->inotify_wd = -1; +} + +static struct device *add_device(struct impl *this, uint32_t id, struct udev_device *dev) { struct device *device; - if (impl->n_devices >= MAX_DEVICES) + if (this->n_devices >= MAX_DEVICES) return NULL; - device = &impl->devicesimpl->n_devices++; + device = &this->devicesthis->n_devices++; spa_zero(*device); - device->impl = impl; - device->notify.fd = -1; device->id = id; udev_device_ref(dev); device->dev = dev; - start_inotify(device); + device->inotify_wd = -1; + + start_watching_device(this, device); + return device; } -static struct device *find_device(struct impl *impl, uint32_t id) +static struct device *find_device(struct impl *this, uint32_t id) { uint32_t i; - for (i = 0; i < impl->n_devices; i++) { - if (impl->devicesi.id == id) - return &impl->devicesi; + for (i = 0; i < this->n_devices; i++) { + if (this->devicesi.id == id) + return &this->devicesi; } return NULL; } -static void clear_device(struct device *device) +static void remove_device(struct impl *this, struct device *device) { - stop_inotify(device); - if (device->dev) - udev_device_unref(device->dev); + device->dev = udev_device_unref(device->dev); + stop_watching_device(this, device); + *device = this->devices--this->n_devices; } -static void remove_device(struct device *device) +static void clear_devices(struct impl *this) { - struct impl *impl = device->impl; - clear_device(device); - *device = impl->devices--impl->n_devices; + while (this->n_devices > 0) + remove_device(this, &this->devices0); } -static void clear_devices(struct impl *impl) -{ - uint32_t i; - for (i = 0; i < impl->n_devices; i++) - clear_device(&impl->devicesi); - impl->n_devices = 0; -} - -static uint32_t get_device_id(struct impl *impl, struct udev_device *dev) +static uint32_t get_device_id(struct impl *this, struct udev_device *dev) { const char *str; @@ -229,9 +240,8 @@ *d = 0; } -static int emit_object_info(struct device *device) +static int emit_object_info(struct impl *this, struct device *device) { - struct impl *impl = device->impl; struct spa_device_object_info info; uint32_t id = device->id; struct udev_device *dev = device->dev; @@ -331,55 +341,54 @@ itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_CAPABILITIES, str); } info.props = &SPA_DICT_INIT(items, n_items); - spa_device_emit_object_info(&impl->hooks, id, &info); + spa_device_emit_object_info(&this->hooks, id, &info); device->emitted = true; return 1; } -static bool check_access(struct device *device) +static bool check_access(struct impl *this, struct device *device) { char path128; snprintf(path, sizeof(path), "/dev/video%u", device->id); device->accessible = access(path, R_OK|W_OK) >= 0; - spa_log_debug(device->impl->log, "%s accessible:%u", path, device->accessible); + spa_log_debug(this->log, "%s accessible:%u", path, device->accessible); return device->accessible; } -static void process_device(struct impl *impl, uint32_t action, struct udev_device *dev) +static void process_device(struct impl *this, uint32_t action, struct udev_device *dev) { uint32_t id; struct device *device; bool emitted; - if ((id = get_device_id(impl, dev)) == SPA_ID_INVALID) + if ((id = get_device_id(this, dev)) == SPA_ID_INVALID) return; - device = find_device(impl, id); + device = find_device(this, id); if (device && device->ignored) return; switch (action) { case ACTION_ADD: if (device == NULL) - device = add_device(impl, id, dev); + device = add_device(this, id, dev); if (device == NULL) return; - if (!check_access(device)) + if (!check_access(this, device)) return; - else - emit_object_info(device); + emit_object_info(this, device); break; case ACTION_REMOVE: if (device == NULL) return; emitted = device->emitted; - remove_device(device); + remove_device(this, device); if (emitted) - spa_device_emit_object_info(&impl->hooks, id, NULL); + spa_device_emit_object_info(&this->hooks, id, NULL); break; case ACTION_DISABLE: @@ -387,40 +396,36 @@ return; if (device->emitted) { device->emitted = false; - spa_device_emit_object_info(&impl->hooks, id, NULL); + spa_device_emit_object_info(&this->hooks, id, NULL); } break; } } -static int stop_inotify(struct device *dev) +static int stop_inotify(struct impl *this) { - struct impl *impl = dev->impl; - if (dev->notify.fd == -1) + if (this->notify.fd == -1) return 0; - spa_log_info(impl->log, "stop inotify for /dev/video%u", dev->id); - spa_loop_remove_source(impl->main_loop, &dev->notify); - close(dev->notify.fd); - dev->notify.fd = -1; + spa_log_info(this->log, "stop inotify"); + + for (size_t i = 0; i < this->n_devices; i++) + stop_watching_device(this, &this->devicesi); + + spa_loop_remove_source(this->main_loop, &this->notify); + close(this->notify.fd); + this->notify.fd = -1; return 0; } - static void impl_on_notify_events(struct spa_source *source) { - struct device *dev = source->data; - struct impl *impl = dev->impl; + struct impl *this = source->data; union { unsigned char namesizeof(struct inotify_event) + NAME_MAX + 1; struct inotify_event e; /* for appropriate alignment */ } buf; - if (source->rmask & (SPA_IO_ERR | SPA_IO_HUP)) { - spa_log_warn(impl->log, "notify error on /dev/video%u", dev->id); - stop_inotify(dev); - return; - } - while (source->rmask & SPA_IO_IN) { + while (true) { ssize_t len; const struct inotify_event *event; void *p, *e; @@ -435,126 +440,131 @@ for (p = &buf; p < e; p = SPA_PTROFF(p, sizeof(struct inotify_event) + event->len, void)) { - event = (const struct inotify_event *) p; if ((event->mask & IN_ATTRIB)) { - bool access; - access = check_access(dev); - if (access && !dev->emitted) - process_device(impl, ACTION_ADD, dev->dev); - else if (!access && dev->emitted) - process_device(impl, ACTION_DISABLE, dev->dev); + struct device *device = NULL; + + for (size_t i = 0; i < this->n_devices; i++) { + if (this->devicesi.inotify_wd == event->wd) { + device = &this->devicesi; + break; + } + } + + spa_assert(device); + + bool access = check_access(this, device); + if (access && !device->emitted) + process_device(this, ACTION_ADD, device->dev); + else if (!access && device->emitted) + process_device(this, ACTION_DISABLE, device->dev); } } } } -static int start_inotify(struct device *dev) +static int start_inotify(struct impl *this) { - struct impl *impl = dev->impl; - int res, notify_fd; - char name32; + int notify_fd; - if (dev->notify.fd != -1) + if (this->notify.fd != -1) return 0; if ((notify_fd = inotify_init1(IN_CLOEXEC | IN_NONBLOCK)) < 0) return -errno; - snprintf(name, sizeof(name), "/dev/video%u", dev->id); + spa_log_info(this->log, "start inotify"); + this->notify.func = impl_on_notify_events; + this->notify.data = this; + this->notify.fd = notify_fd; + this->notify.mask = SPA_IO_IN | SPA_IO_ERR; - res = inotify_add_watch(notify_fd, name, IN_ATTRIB | IN_CLOSE_WRITE); - if (res < 0) { - res = -errno; - close(notify_fd); - - if (res == -ENOENT) { - spa_log_debug(impl->log, "%s does not exist yet", name); - return 0; - } - spa_log_error(impl->log, "inotify_add_watch() failed: %s", spa_strerror(res)); - return res; - } - spa_log_info(impl->log, "start inotify for %s", name); - dev->notify.func = impl_on_notify_events; - dev->notify.data = dev; - dev->notify.fd = notify_fd; - dev->notify.mask = SPA_IO_IN | SPA_IO_ERR; + spa_loop_add_source(this->main_loop, &this->notify); - spa_loop_add_source(impl->main_loop, &dev->notify); + for (size_t i = 0; i < this->n_devices; i++) + start_watching_device(this, &this->devicesi); return 0; } static void impl_on_fd_events(struct spa_source *source) { - struct impl *impl = source->data; + struct impl *this = source->data; struct udev_device *dev; const char *action; - dev = udev_monitor_receive_device(impl->umonitor); + dev = udev_monitor_receive_device(this->umonitor); if (dev == NULL) return; if ((action = udev_device_get_action(dev)) == NULL) action = "change"; - spa_log_debug(impl->log, "action %s", action); + spa_log_debug(this->log, "action %s", action); + + start_inotify(this); if (spa_streq(action, "add") || spa_streq(action, "change")) { - process_device(impl, ACTION_ADD, dev); + process_device(this, ACTION_ADD, dev); } else if (spa_streq(action, "remove")) { - process_device(impl, ACTION_REMOVE, dev); + process_device(this, ACTION_REMOVE, dev); } udev_device_unref(dev); } -static int start_monitor(struct impl *impl) +static int start_monitor(struct impl *this) { - if (impl->umonitor != NULL) + int res; + + if (this->umonitor != NULL) return 0; - impl->umonitor = udev_monitor_new_from_netlink(impl->udev, "udev"); - if (impl->umonitor == NULL) + this->umonitor = udev_monitor_new_from_netlink(this->udev, "udev"); + if (this->umonitor == NULL) return -ENOMEM; - udev_monitor_filter_add_match_subsystem_devtype(impl->umonitor, + udev_monitor_filter_add_match_subsystem_devtype(this->umonitor, "video4linux", NULL); - udev_monitor_enable_receiving(impl->umonitor); + udev_monitor_enable_receiving(this->umonitor); - impl->source.func = impl_on_fd_events; - impl->source.data = impl; - impl->source.fd = udev_monitor_get_fd(impl->umonitor); - impl->source.mask = SPA_IO_IN | SPA_IO_ERR; + this->source.func = impl_on_fd_events; + this->source.data = this; + this->source.fd = udev_monitor_get_fd(this->umonitor); + this->source.mask = SPA_IO_IN | SPA_IO_ERR; - spa_log_debug(impl->log, "monitor %p", impl->umonitor); - spa_loop_add_source(impl->main_loop, &impl->source); + spa_log_debug(this->log, "monitor %p", this->umonitor); + spa_loop_add_source(this->main_loop, &this->source); + + if ((res = start_inotify(this)) < 0) + return res; return 0; } -static int stop_monitor(struct impl *impl) +static int stop_monitor(struct impl *this) { - if (impl->umonitor == NULL) + if (this->umonitor == NULL) return 0; - clear_devices(impl); + clear_devices (this); - spa_loop_remove_source(impl->main_loop, &impl->source); - udev_monitor_unref(impl->umonitor); - impl->umonitor = NULL; + spa_loop_remove_source(this->main_loop, &this->source); + udev_monitor_unref(this->umonitor); + this->umonitor = NULL; + + stop_inotify(this); return 0; } -static int enum_devices(struct impl *impl) +static int enum_devices(struct impl *this) { struct udev_enumerate *enumerate; struct udev_list_entry *devices; - enumerate = udev_enumerate_new(impl->udev); + enumerate = udev_enumerate_new(this->udev); if (enumerate == NULL) return -ENOMEM; @@ -565,11 +575,11 @@ devices = udev_list_entry_get_next(devices)) { struct udev_device *dev; - dev = udev_device_new_from_syspath(impl->udev, udev_list_entry_get_name(devices)); + dev = udev_device_new_from_syspath(this->udev, udev_list_entry_get_name(devices)); if (dev == NULL) continue; - process_device(impl, ACTION_ADD, dev); + process_device(this, ACTION_ADD, dev); udev_device_unref(dev); } @@ -584,24 +594,24 @@ { SPA_KEY_API_UDEV_MATCH, "video4linux" }, }; -static void emit_device_info(struct impl *impl, bool full) +static void emit_device_info(struct impl *this, bool full) { - uint64_t old = full ? impl->info.change_mask : 0; + uint64_t old = full ? this->info.change_mask : 0; if (full) - impl->info.change_mask = impl->info_all; - if (impl->info.change_mask) { - impl->info.props = &SPA_DICT_INIT_ARRAY(device_info_items); - spa_device_emit_info(&impl->hooks, &impl->info); - impl->info.change_mask = old; + this->info.change_mask = this->info_all; + if (this->info.change_mask) { + this->info.props = &SPA_DICT_INIT_ARRAY(device_info_items); + spa_device_emit_info(&this->hooks, &this->info); + this->info.change_mask = old; } } static void impl_hook_removed(struct spa_hook *hook) { - struct impl *impl = hook->priv; - if (spa_hook_list_is_empty(&impl->hooks)) { - stop_monitor(impl); - impl_udev_close(impl); + struct impl *this = hook->priv; + if (spa_hook_list_is_empty(&this->hooks)) { + stop_monitor(this); + impl_udev_close(this); } } @@ -610,29 +620,29 @@ const struct spa_device_events *events, void *data) { int res; - struct impl *impl = object; + struct impl *this = object; struct spa_hook_list save; - spa_return_val_if_fail(impl != NULL, -EINVAL); + spa_return_val_if_fail(this != NULL, -EINVAL); spa_return_val_if_fail(events != NULL, -EINVAL); - if ((res = impl_udev_open(impl)) < 0) + if ((res = impl_udev_open(this)) < 0) return res; - spa_hook_list_isolate(&impl->hooks, &save, listener, events, data); + spa_hook_list_isolate(&this->hooks, &save, listener, events, data); - emit_device_info(impl, true); + emit_device_info(this, true); - if ((res = enum_devices(impl)) < 0) + if ((res = enum_devices(this)) < 0) return res; - if ((res = start_monitor(impl)) < 0) + if ((res = start_monitor(this)) < 0) return res; - spa_hook_list_join(&impl->hooks, &save); + spa_hook_list_join(&this->hooks, &save); listener->removed = impl_hook_removed; - listener->priv = impl; + listener->priv = this; return 0; } @@ -644,15 +654,15 @@ static int impl_get_interface(struct spa_handle *handle, const char *type, void **interface) { - struct impl *impl; + struct impl *this; spa_return_val_if_fail(handle != NULL, -EINVAL); spa_return_val_if_fail(interface != NULL, -EINVAL); - impl = (struct impl *) handle; + this = (struct impl *) handle; if (spa_streq(type, SPA_TYPE_INTERFACE_Device)) - *interface = &impl->device; + *interface = &this->device; else return -ENOENT; @@ -661,9 +671,9 @@ static int impl_clear(struct spa_handle *handle) { - struct impl *impl = (struct impl *) handle; - stop_monitor(impl); - impl_udev_close(impl); + struct impl *this = (struct impl *) handle; + stop_monitor(this); + impl_udev_close(this); return 0; } @@ -681,7 +691,7 @@ const struct spa_support *support, uint32_t n_support) { - struct impl *impl; + struct impl *this; spa_return_val_if_fail(factory != NULL, -EINVAL); spa_return_val_if_fail(handle != NULL, -EINVAL); @@ -689,25 +699,27 @@ handle->get_interface = impl_get_interface; handle->clear = impl_clear; - impl = (struct impl *) handle; - impl->log = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log); - impl->main_loop = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Loop); + this = (struct impl *) handle; + this->notify.fd = -1; + + this->log = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log); + this->main_loop = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Loop); - if (impl->main_loop == NULL) { - spa_log_error(impl->log, "a main-loop is needed"); + if (this->main_loop == NULL) { + spa_log_error(this->log, "a main-loop is needed"); return -EINVAL; } - spa_hook_list_init(&impl->hooks); + spa_hook_list_init(&this->hooks); - impl->device.iface = SPA_INTERFACE_INIT( + this->device.iface = SPA_INTERFACE_INIT( SPA_TYPE_INTERFACE_Device, SPA_VERSION_DEVICE, - &impl_device, impl); + &impl_device, this); - impl->info = SPA_DEVICE_INFO_INIT(); - impl->info_all = SPA_DEVICE_CHANGE_MASK_FLAGS | + this->info = SPA_DEVICE_INFO_INIT(); + this->info_all = SPA_DEVICE_CHANGE_MASK_FLAGS | SPA_DEVICE_CHANGE_MASK_PROPS; - impl->info.flags = 0; + this->info.flags = 0; return 0; }
View file
pipewire-0.3.80.tar.gz/spa/plugins/vulkan/dmabuf.h
Added
@@ -0,0 +1,62 @@ +// Copyright (c) 2023 The wlroots contributors +// +// 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 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. +// +// Obtained from https://gitlab.freedesktop.org/wlroots/wlroots/ + +/* SPDX-FileCopyrightText: Copyright © 2023 The wlroots contributors */ +/* SPDX-License-Identifier: MIT */ + +#ifndef RENDER_DMABUF_H +#define RENDER_DMABUF_H + +#include <stdbool.h> +#include <stdint.h> + +#include "spa/support/log.h" + +// Copied from <linux/dma-buf.h> to avoid #ifdef soup +#define DMA_BUF_SYNC_READ (1 << 0) +#define DMA_BUF_SYNC_WRITE (2 << 0) +#define DMA_BUF_SYNC_RW (DMA_BUF_SYNC_READ | DMA_BUF_SYNC_WRITE) + +/** + * Check whether DMA-BUF import/export from/to sync_file is available. + * + * If this function returns true, dmabuf_import_sync_file() is supported. + */ +bool dmabuf_check_sync_file_import_export(struct spa_log *log); + +/** + * Import a sync_file into a DMA-BUF with DMA_BUF_IOCTL_IMPORT_SYNC_FILE. + * + * This can be used to make explicit sync interoperate with implicit sync. + */ +bool dmabuf_import_sync_file(struct spa_log *log, int dmabuf_fd, uint32_t flags, int sync_file_fd); + +/** + * Export a sync_file from a DMA-BUF with DMA_BUF_IOCTL_EXPORT_SYNC_FILE. + * + * The sync_file FD is returned on success, -1 is returned on error. + * + * This can be used to make explicit sync interoperate with implicit sync. + */ +int dmabuf_export_sync_file(struct spa_log *log, int dmabuf_fd, uint32_t flags); + +#endif
View file
pipewire-0.3.80.tar.gz/spa/plugins/vulkan/dmabuf_fallback.c
Added
@@ -0,0 +1,42 @@ +// Copyright (c) 2023 The wlroots contributors +// +// 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 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. +// +// Obtained from https://gitlab.freedesktop.org/wlroots/wlroots/ + +/* SPDX-FileCopyrightText: Copyright © 2023 The wlroots contributors */ +/* SPDX-License-Identifier: MIT */ + +#include <spa/support/log.h> +#include <spa/utils/result.h> + + +bool dmabuf_check_sync_file_import_export(struct spa_log *log) { + return false; +} + +bool dmabuf_import_sync_file(struct spa_log *log, int dmabuf_fd, uint32_t flags, int sync_file_fd) { + spa_log_error("DMA-BUF sync_file import IOCTL not available on this system"); + return false; +} + +int dmabuf_export_sync_file(struct spa_log *log, int dmabuf_fd, uint32_t flags) { + spa_log_error("DMA-BUF sync_file export IOCTL not available on this system"); + return false; +}
View file
pipewire-0.3.80.tar.gz/spa/plugins/vulkan/dmabuf_linux.c
Added
@@ -0,0 +1,127 @@ +// Copyright (c) 2023 The wlroots contributors +// +// 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 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. +// +// Obtained from https://gitlab.freedesktop.org/wlroots/wlroots/ + +/* SPDX-FileCopyrightText: Copyright © 2023 The wlroots contributors */ +/* SPDX-License-Identifier: MIT */ + +#include <linux/dma-buf.h> +#include <linux/version.h> +#include <stdlib.h> +#include <sys/utsname.h> +#include <xf86drm.h> +#include <errno.h> + +#include <spa/support/log.h> +#include <spa/utils/result.h> + +#include "dmabuf.h" + +bool dmabuf_check_sync_file_import_export(struct spa_log *log) { + /* Unfortunately there's no better way to check the availability of the + * IOCTL than to check the kernel version. See the discussion at: + * https://lore.kernel.org/dri-devel/20220601161303.64797-1-contact@emersion.fr/ + */ + + struct utsname utsname = {0}; + if (uname(&utsname) != 0) { + spa_log_warn(log, "uname failed"); + return false; + } + + if (strcmp(utsname.sysname, "Linux") != 0) { + return false; + } + + // Trim release suffix if any, e.g. "-arch1-1" + for (size_t i = 0; utsname.releasei != '\0'; i++) { + char ch = utsname.releasei; + if ((ch < '0' || ch > '9') && ch != '.') { + utsname.releasei = '\0'; + break; + } + } + + char *rel = strtok(utsname.release, "."); + int major = atoi(rel); + + int minor = 0; + rel = strtok(NULL, "."); + if (rel != NULL) { + minor = atoi(rel); + } + + int patch = 0; + rel = strtok(NULL, "."); + if (rel != NULL) { + patch = atoi(rel); + } + + return KERNEL_VERSION(major, minor, patch) >= KERNEL_VERSION(5, 20, 0); +} + +// TODO: drop these definitions once widespread + +#if !defined(DMA_BUF_IOCTL_IMPORT_SYNC_FILE) + +struct dma_buf_import_sync_file { + __u32 flags; + __s32 fd; +}; + +#define DMA_BUF_IOCTL_IMPORT_SYNC_FILE _IOW(DMA_BUF_BASE, 3, struct dma_buf_import_sync_file) + +#endif + +#if !defined(DMA_BUF_IOCTL_EXPORT_SYNC_FILE) + +struct dma_buf_export_sync_file { + __u32 flags; + __s32 fd; +}; + +#define DMA_BUF_IOCTL_EXPORT_SYNC_FILE _IOWR(DMA_BUF_BASE, 2, struct dma_buf_export_sync_file) + +#endif + +bool dmabuf_import_sync_file(struct spa_log *log, int dmabuf_fd, uint32_t flags, int sync_file_fd) { + struct dma_buf_import_sync_file data = { + .flags = flags, + .fd = sync_file_fd, + }; + if (drmIoctl(dmabuf_fd, DMA_BUF_IOCTL_IMPORT_SYNC_FILE, &data) != 0) { + spa_log_error(log, "drmIoctl(IMPORT_SYNC_FILE) failed with %d (%s)", errno, spa_strerror(-errno)); + return false; + } + return true; +} + +int dmabuf_export_sync_file(struct spa_log *log, int dmabuf_fd, uint32_t flags) { + struct dma_buf_export_sync_file data = { + .flags = flags, + .fd = -1, + }; + if (drmIoctl(dmabuf_fd, DMA_BUF_IOCTL_EXPORT_SYNC_FILE, &data) != 0) { + spa_log_error(log, "drmIoctl(EXPORT_SYNC_FILE) failed with %d (%s)", errno, spa_strerror(-errno)); + return -1; + } + return data.fd; +}
View file
pipewire-0.3.79.tar.gz/spa/plugins/vulkan/meson.build -> pipewire-0.3.80.tar.gz/spa/plugins/vulkan/meson.build
Changed
@@ -2,11 +2,20 @@ 'plugin.c', 'vulkan-compute-filter.c', 'vulkan-compute-source.c', + 'vulkan-compute-utils.c', 'vulkan-utils.c' +drm = dependency('libdrm') + +if cc.has_header('linux/dma-buf.h') and target_machine.system() == 'linux' + spa_vulkan_sources += files('dmabuf_linux.c') +else + spa_vulkan_sources += files('dmabuf_fallback.c') +endif + spa_vulkan = shared_library('spa-vulkan', spa_vulkan_sources, - dependencies : spa_dep, vulkan_dep, mathlib , + dependencies : spa_dep, vulkan_dep, mathlib, drm , install : true, install_dir : spa_plugindir / 'vulkan')
View file
pipewire-0.3.79.tar.gz/spa/plugins/vulkan/vulkan-compute-filter.c -> pipewire-0.3.80.tar.gz/spa/plugins/vulkan/vulkan-compute-filter.c
Changed
@@ -23,7 +23,7 @@ #include <spa/param/param.h> #include <spa/pod/filter.h> -#include "vulkan-utils.h" +#include "vulkan-compute-utils.h" #define NAME "vulkan-compute-filter" @@ -73,7 +73,7 @@ bool started; - struct vulkan_state state; + struct vulkan_compute_state state; struct port port2; }; @@ -269,6 +269,64 @@ return -ENOTSUP; } +static struct spa_pod *build_EnumFormat(uint32_t fmt, const struct vulkan_format_info *fmtInfo, struct spa_pod_builder *builder) { + struct spa_pod_frame f2; + uint32_t i, c; + + spa_pod_builder_push_object(builder, &f0, SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat); + spa_pod_builder_add(builder, SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_video), 0); + spa_pod_builder_add(builder, SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_dsp), 0); + spa_pod_builder_add(builder, SPA_FORMAT_VIDEO_format, SPA_POD_Id(fmt), 0); + if (fmtInfo && fmtInfo->modifierCount > 0) { + spa_pod_builder_prop(builder, SPA_FORMAT_VIDEO_modifier, SPA_POD_PROP_FLAG_MANDATORY | SPA_POD_PROP_FLAG_DONT_FIXATE); + spa_pod_builder_push_choice(builder, &f1, SPA_CHOICE_Enum, 0); + for (i = 0, c = 0; i < fmtInfo->modifierCount; i++) { + spa_pod_builder_long(builder, fmtInfo->infosi.props.drmFormatModifier); + if (c++ == 0) + spa_pod_builder_long(builder, fmtInfo->infosi.props.drmFormatModifier); + } + spa_pod_builder_pop(builder, &f1); + } + return spa_pod_builder_pop(builder, &f0); +} + +// This function enumerates the available formats in vulkan_state::formats, announcing all formats capable to support DmaBufs +// first and then falling back to those supported with SHM buffers. +static bool find_EnumFormatInfo(struct vulkan_base *s, uint32_t index, uint32_t caps, uint32_t *fmt_idx, bool *has_modifier) { + int64_t fmtIterator = 0; + int64_t maxIterator = 0; + if (caps & VULKAN_BUFFER_TYPE_CAP_SHM) + maxIterator += s->formatInfoCount; + if (caps & VULKAN_BUFFER_TYPE_CAP_DMABUF) + maxIterator += s->formatInfoCount; + // Count available formats until index underflows, while fmtIterator indexes the current format. + // Iterate twice over formats first time with modifiers, second time without if both caps are supported. + while (index < (uint32_t)-1 && fmtIterator < maxIterator) { + const struct vulkan_format_info *f_info = &s->formatInfosfmtIterator%s->formatInfoCount; + if (caps & VULKAN_BUFFER_TYPE_CAP_DMABUF && fmtIterator < s->formatInfoCount) { + // First round, check for modifiers + if (f_info->modifierCount > 0) { + index--; + } + } else if (caps & VULKAN_BUFFER_TYPE_CAP_SHM) { + // Second round, every format should be supported. + index--; + } + fmtIterator++; + } + + if (index != (uint32_t)-1) { + // No more formats available + return false; + } + // Undo end of loop increment + fmtIterator--; + *fmt_idx = fmtIterator%s->formatInfoCount; + // Loop finished in first round + *has_modifier = caps & VULKAN_BUFFER_TYPE_CAP_DMABUF && fmtIterator < s->formatInfoCount; + return true; +} + static int port_enum_formats(void *object, enum spa_direction direction, uint32_t port_id, uint32_t index, @@ -276,17 +334,30 @@ struct spa_pod **param, struct spa_pod_builder *builder) { - switch (index) { - case 0: - *param = spa_pod_builder_add_object(builder, - SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat, - SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_video), - SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_dsp), - SPA_FORMAT_VIDEO_format, SPA_POD_Id(SPA_VIDEO_FORMAT_DSP_F32)); - break; - default: - return 0; + struct impl *this = object; + + uint32_t fmt_index; + bool has_modifier; + if (this->portport_id.have_format + && this->portport_id.current_format.info.dsp.flags & SPA_VIDEO_FLAG_MODIFIER + && this->portport_id.current_format.info.dsp.flags ^ SPA_VIDEO_FLAG_MODIFIER_FIXATION_REQUIRED) { + if (index == 0) { + spa_log_info(this->log, "vulkan-compute-filter: enum_formats fixated format idx: %d, format %d, has_modifier 1", + index, this->portport_id.current_format.info.dsp.format); + *param = spa_format_video_dsp_build(builder, SPA_PARAM_EnumFormat, &this->portport_id.current_format.info.dsp); + return 1; + } + if (!find_EnumFormatInfo(&this->state.base, index-1, spa_vulkan_get_buffer_caps(&this->state, direction), &fmt_index, &has_modifier)) + return 0; + } else { + if (!find_EnumFormatInfo(&this->state.base, index, spa_vulkan_get_buffer_caps(&this->state, direction), &fmt_index, &has_modifier)) + return 0; } + + const struct vulkan_format_info *f_info = &this->state.base.formatInfosfmt_index; + spa_log_info(this->log, "vulkan-compute-filter: enum_formats idx: %d, format %d, has_modifier %d", index, f_info->spa_format, has_modifier); + *param = build_EnumFormat(f_info->spa_format, has_modifier ? f_info : NULL, builder); + return 1; } @@ -348,13 +419,25 @@ this->position->video.size.height, this->position->video.stride); - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_ParamBuffers, id, - SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(2, 1, MAX_BUFFERS), - SPA_PARAM_BUFFERS_blocks, SPA_POD_Int(1), - SPA_PARAM_BUFFERS_size, SPA_POD_Int(this->position->video.stride * - this->position->video.size.height), - SPA_PARAM_BUFFERS_stride, SPA_POD_Int(this->position->video.stride)); + if (port->current_format.info.dsp.flags & SPA_VIDEO_FLAG_MODIFIER) { + struct vulkan_modifier_info *mod_info = spa_vulkan_get_modifier_info(&this->state, + &port->current_format.info.dsp); + param = spa_pod_builder_add_object(&b, + SPA_TYPE_OBJECT_ParamBuffers, id, + SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(2, 1, MAX_BUFFERS), + SPA_PARAM_BUFFERS_blocks, SPA_POD_Int(mod_info->props.drmFormatModifierPlaneCount), + SPA_PARAM_BUFFERS_dataType, SPA_POD_CHOICE_FLAGS_Int(1<<SPA_DATA_DmaBuf)); + } else { + param = spa_pod_builder_add_object(&b, + SPA_TYPE_OBJECT_ParamBuffers, id, + SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(2, 1, MAX_BUFFERS), + SPA_PARAM_BUFFERS_blocks, SPA_POD_Int(1), + SPA_PARAM_BUFFERS_size, SPA_POD_Int(this->position->video.stride * + this->position->video.size.height), + SPA_PARAM_BUFFERS_stride, SPA_POD_Int(this->position->video.stride), + SPA_PARAM_BUFFERS_dataType, SPA_POD_CHOICE_FLAGS_Int(1<<SPA_DATA_MemPtr)); + } + break; } case SPA_PARAM_Meta: @@ -390,7 +473,7 @@ if (port->n_buffers > 0) { spa_log_debug(this->log, NAME " %p: clear buffers", this); spa_vulkan_stop(&this->state); - spa_vulkan_use_buffers(&this->state, &this->state.streamsport->stream_id, 0, 0, NULL); + spa_vulkan_use_buffers(&this->state, &this->state.streamsport->stream_id, 0, &port->current_format.info.dsp, 0, NULL); port->n_buffers = 0; spa_list_init(&port->empty); spa_list_init(&port->ready); @@ -428,8 +511,49 @@ this->state.constants.width = this->position->video.size.width; this->state.constants.height = this->position->video.size.height; + bool modifier_fixed = false; + if (port->direction == SPA_DIRECTION_OUTPUT + && info.info.dsp.flags & SPA_VIDEO_FLAG_MODIFIER + && info.info.dsp.flags & SPA_VIDEO_FLAG_MODIFIER_FIXATION_REQUIRED) { + const struct spa_pod_prop *mod_prop; + if ((mod_prop = spa_pod_find_prop(format, NULL, SPA_FORMAT_VIDEO_modifier)) == NULL) + return -EINVAL; + + const struct spa_pod *mod_pod = &mod_prop->value; + uint32_t modifierCount = SPA_POD_CHOICE_N_VALUES(mod_pod); + uint64_t *modifiers = SPA_POD_CHOICE_VALUES(mod_pod); + if (modifierCount <= 1) + return -EINVAL; + // SPA_POD_CHOICE carries the "preferred" value at position 0 + modifierCount -= 1; + modifiers++; + uint64_t fixed_modifier; + if (spa_vulkan_fixate_modifier(&this->state, &this->state.streamsport->stream_id, &info.info.dsp, modifierCount, modifiers, &fixed_modifier) != 0) + return -EINVAL; + + spa_log_info(this->log, NAME ": modifier fixated %"PRIu64, fixed_modifier); + + info.info.dsp.modifier = fixed_modifier; + info.info.dsp.flags &= ~SPA_VIDEO_FLAG_MODIFIER_FIXATION_REQUIRED; + modifier_fixed = true; + } + + if (info.info.dsp.flags & SPA_VIDEO_FLAG_MODIFIER) { + port->info.flags |= SPA_PORT_FLAG_CAN_ALLOC_BUFFERS; + } else { + port->info.flags &= ~SPA_PORT_FLAG_CAN_ALLOC_BUFFERS; + } + port->info.change_mask |= SPA_PORT_CHANGE_MASK_FLAGS; + port->current_format = info; port->have_format = true; + + if (modifier_fixed) { + port->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS; + port->params0.flags ^= SPA_PARAM_INFO_SERIAL; + emit_port_info(this, port, false); + return 0; + } } port->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS; @@ -504,7 +628,7 @@ spa_log_info(this->log, "%p: %d:%d add buffer %p", port, direction, port_id, b); spa_list_append(&port->empty, &b->link); } - spa_vulkan_use_buffers(&this->state, &this->state.streamsport->stream_id, flags, n_buffers, buffers); + spa_vulkan_use_buffers(&this->state, &this->state.streamsport->stream_id, flags, &port->current_format.info.dsp, n_buffers, buffers); port->n_buffers = n_buffers; return 0; @@ -650,6 +774,13 @@ static int impl_clear(struct spa_handle *handle) { + struct impl *this; + + spa_return_val_if_fail(handle != NULL, -EINVAL); + + this = (struct impl *) handle; + + spa_vulkan_deinit(&this->state); return 0; } @@ -708,7 +839,7 @@ SPA_PORT_CHANGE_MASK_PARAMS | SPA_PORT_CHANGE_MASK_PROPS; port->info = SPA_PORT_INFO_INIT(); - port->info.flags = SPA_PORT_FLAG_NO_REF | SPA_PORT_FLAG_CAN_ALLOC_BUFFERS; + port->info.flags = SPA_PORT_FLAG_NO_REF; port->params0 = SPA_PARAM_INFO(SPA_PARAM_EnumFormat, SPA_PARAM_INFO_READ); port->params1 = SPA_PARAM_INFO(SPA_PARAM_Meta, SPA_PARAM_INFO_READ); port->params2 = SPA_PARAM_INFO(SPA_PARAM_IO, SPA_PARAM_INFO_READ); @@ -742,6 +873,7 @@ SPA_DIRECTION_OUTPUT, NULL); this->state.n_streams = 2; + spa_vulkan_init(&this->state); spa_vulkan_prepare(&this->state); return 0;
View file
pipewire-0.3.79.tar.gz/spa/plugins/vulkan/vulkan-compute-source.c -> pipewire-0.3.80.tar.gz/spa/plugins/vulkan/vulkan-compute-source.c
Changed
@@ -24,7 +24,7 @@ #include <spa/param/param.h> #include <spa/pod/filter.h> -#include "vulkan-utils.h" +#include "vulkan-compute-utils.h" #define NAME "vulkan-compute-source" @@ -97,7 +97,7 @@ uint64_t frame_count; - struct vulkan_state state; + struct vulkan_compute_state state; struct port port; }; @@ -503,6 +503,64 @@ return -ENOTSUP; } +static struct spa_pod *build_EnumFormat(uint32_t fmt, const struct vulkan_format_info *fmtInfo, struct spa_pod_builder *builder) { + struct spa_pod_frame f2; + uint32_t i, c; + + spa_pod_builder_push_object(builder, &f0, SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat); + spa_pod_builder_add(builder, SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_video), 0); + spa_pod_builder_add(builder, SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_dsp), 0); + spa_pod_builder_add(builder, SPA_FORMAT_VIDEO_format, SPA_POD_Id(fmt), 0); + if (fmtInfo && fmtInfo->modifierCount > 0) { + spa_pod_builder_prop(builder, SPA_FORMAT_VIDEO_modifier, SPA_POD_PROP_FLAG_MANDATORY | SPA_POD_PROP_FLAG_DONT_FIXATE); + spa_pod_builder_push_choice(builder, &f1, SPA_CHOICE_Enum, 0); + for (i = 0, c = 0; i < fmtInfo->modifierCount; i++) { + spa_pod_builder_long(builder, fmtInfo->infosi.props.drmFormatModifier); + if (c++ == 0) + spa_pod_builder_long(builder, fmtInfo->infosi.props.drmFormatModifier); + } + spa_pod_builder_pop(builder, &f1); + } + return spa_pod_builder_pop(builder, &f0); +} + +// This function enumerates the available formats in vulkan_state::formats, announcing all formats capable to support DmaBufs +// first and then falling back to those supported with SHM buffers. +static bool find_EnumFormatInfo(struct vulkan_base *s, uint32_t index, uint32_t caps, uint32_t *fmt_idx, bool *has_modifier) { + int64_t fmtIterator = 0; + int64_t maxIterator = 0; + if (caps & VULKAN_BUFFER_TYPE_CAP_SHM) + maxIterator += s->formatInfoCount; + if (caps & VULKAN_BUFFER_TYPE_CAP_DMABUF) + maxIterator += s->formatInfoCount; + // Count available formats until index underflows, while fmtIterator indexes the current format. + // Iterate twice over formats first time with modifiers, second time without if both caps are supported. + while (index < (uint32_t)-1 && fmtIterator < maxIterator) { + const struct vulkan_format_info *f_info = &s->formatInfosfmtIterator%s->formatInfoCount; + if (caps & VULKAN_BUFFER_TYPE_CAP_DMABUF && fmtIterator < s->formatInfoCount) { + // First round, check for modifiers + if (f_info->modifierCount > 0) { + index--; + } + } else if (caps & VULKAN_BUFFER_TYPE_CAP_SHM) { + // Second round, every format should be supported. + index--; + } + fmtIterator++; + } + + if (index != (uint32_t)-1) { + // No more formats available + return false; + } + // Undo end of loop increment + fmtIterator--; + *fmt_idx = fmtIterator%s->formatInfoCount; + // Loop finished in first round + *has_modifier = caps & VULKAN_BUFFER_TYPE_CAP_DMABUF && fmtIterator < s->formatInfoCount; + return true; +} + static int port_enum_formats(void *object, enum spa_direction direction, uint32_t port_id, uint32_t index, @@ -510,17 +568,30 @@ struct spa_pod **param, struct spa_pod_builder *builder) { - switch (index) { - case 0: - *param = spa_pod_builder_add_object(builder, - SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat, - SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_video), - SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_dsp), - SPA_FORMAT_VIDEO_format, SPA_POD_Id(SPA_VIDEO_FORMAT_DSP_F32)); - break; - default: - return 0; + struct impl *this = object; + + uint32_t fmt_index; + bool has_modifier; + if (this->port.have_format + && this->port.current_format.info.dsp.flags & SPA_VIDEO_FLAG_MODIFIER + && this->port.current_format.info.dsp.flags ^ SPA_VIDEO_FLAG_MODIFIER_FIXATION_REQUIRED) { + if (index == 0) { + spa_log_info(this->log, "vulkan-compute-source: enum_formats fixated format idx: %d, format %d, has_modifier 1", + index, this->port.current_format.info.dsp.format); + *param = spa_format_video_dsp_build(builder, SPA_PARAM_EnumFormat, &this->port.current_format.info.dsp); + return 1; + } + if (!find_EnumFormatInfo(&this->state.base, index-1, spa_vulkan_get_buffer_caps(&this->state, direction), &fmt_index, &has_modifier)) + return 0; + } else { + if (!find_EnumFormatInfo(&this->state.base, index, spa_vulkan_get_buffer_caps(&this->state, direction), &fmt_index, &has_modifier)) + return 0; } + + const struct vulkan_format_info *f_info = &this->state.base.formatInfosfmt_index; + spa_log_info(this->log, "vulkan-compute-source: enum_formats idx: %d, format %d, has_modifier %d", index, f_info->spa_format, has_modifier); + *param = build_EnumFormat(f_info->spa_format, has_modifier ? f_info : NULL, builder); + return 1; } @@ -582,13 +653,26 @@ this->position->video.size.height, this->position->video.stride); - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_ParamBuffers, id, - SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(2, 1, MAX_BUFFERS), - SPA_PARAM_BUFFERS_blocks, SPA_POD_Int(1), - SPA_PARAM_BUFFERS_size, SPA_POD_Int(this->position->video.stride * - this->position->video.size.height), - SPA_PARAM_BUFFERS_stride, SPA_POD_Int(this->position->video.stride)); + + if (port->current_format.info.dsp.flags & SPA_VIDEO_FLAG_MODIFIER) { + struct vulkan_modifier_info *mod_info = spa_vulkan_get_modifier_info(&this->state, + &port->current_format.info.dsp); + param = spa_pod_builder_add_object(&b, + SPA_TYPE_OBJECT_ParamBuffers, id, + SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(2, 1, MAX_BUFFERS), + SPA_PARAM_BUFFERS_blocks, SPA_POD_Int(mod_info->props.drmFormatModifierPlaneCount), + SPA_PARAM_BUFFERS_dataType, SPA_POD_CHOICE_FLAGS_Int(1<<SPA_DATA_DmaBuf)); + } else { + param = spa_pod_builder_add_object(&b, + SPA_TYPE_OBJECT_ParamBuffers, id, + SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(2, 1, MAX_BUFFERS), + SPA_PARAM_BUFFERS_blocks, SPA_POD_Int(1), + SPA_PARAM_BUFFERS_size, SPA_POD_Int(this->position->video.stride * + this->position->video.size.height), + SPA_PARAM_BUFFERS_stride, SPA_POD_Int(this->position->video.stride), + SPA_PARAM_BUFFERS_dataType, SPA_POD_CHOICE_FLAGS_Int(1<<SPA_DATA_MemPtr)); + } + break; } case SPA_PARAM_Meta: @@ -623,7 +707,7 @@ { if (port->n_buffers > 0) { spa_log_debug(this->log, NAME " %p: clear buffers", this); - spa_vulkan_use_buffers(&this->state, &this->state.streams0, 0, 0, NULL); + spa_vulkan_use_buffers(&this->state, &this->state.streams0, 0, &port->current_format.info.dsp, 0, NULL); port->n_buffers = 0; spa_list_init(&port->empty); spa_list_init(&port->ready); @@ -662,9 +746,50 @@ this->state.constants.width = this->position->video.size.width; this->state.constants.height = this->position->video.size.height; + bool modifier_fixed = false; + if (info.info.dsp.flags & SPA_VIDEO_FLAG_MODIFIER + && info.info.dsp.flags & SPA_VIDEO_FLAG_MODIFIER_FIXATION_REQUIRED) { + const struct spa_pod_prop *mod_prop; + if ((mod_prop = spa_pod_find_prop(format, NULL, SPA_FORMAT_VIDEO_modifier)) == NULL) + return -EINVAL; + + const struct spa_pod *mod_pod = &mod_prop->value; + uint32_t modifierCount = SPA_POD_CHOICE_N_VALUES(mod_pod); + uint64_t *modifiers = SPA_POD_CHOICE_VALUES(mod_pod); + if (modifierCount <= 1) + return -EINVAL; + // SPA_POD_CHOICE carries the "preferred" value at position 0 + modifierCount -= 1; + modifiers++; + + uint64_t fixed_modifier; + if (spa_vulkan_fixate_modifier(&this->state, &this->state.streams0, &info.info.dsp, modifierCount, modifiers, &fixed_modifier) != 0) + return -EINVAL; + + spa_log_info(this->log, NAME ": modifier fixated %"PRIu64, fixed_modifier); + + info.info.dsp.modifier = fixed_modifier; + info.info.dsp.flags &= ~SPA_VIDEO_FLAG_MODIFIER_FIXATION_REQUIRED; + modifier_fixed = true; + } + + if (info.info.dsp.flags & SPA_VIDEO_FLAG_MODIFIER) { + port->info.flags |= SPA_PORT_FLAG_CAN_ALLOC_BUFFERS; + } else { + port->info.flags &= ~SPA_PORT_FLAG_CAN_ALLOC_BUFFERS; + } + port->info.change_mask |= SPA_PORT_CHANGE_MASK_FLAGS; + port->current_format = info; port->have_format = true; spa_vulkan_prepare(&this->state); + + if (modifier_fixed) { + port->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS; + port->params0.flags ^= SPA_PARAM_INFO_SERIAL; + emit_port_info(this, port, false); + return 0; + } } port->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS; @@ -736,9 +861,10 @@ b->flags = 0; b->h = spa_buffer_find_meta_data(buffersi, SPA_META_Header, sizeof(*b->h)); + spa_log_info(this->log, "%p: %d:%d add buffer %p", port, direction, port_id, b); spa_list_append(&port->empty, &b->link); } - spa_vulkan_use_buffers(&this->state, &this->state.streams0, flags, n_buffers, buffers); + spa_vulkan_use_buffers(&this->state, &this->state.streams0, flags, &port->current_format.info.dsp, n_buffers, buffers); port->n_buffers = n_buffers; return 0; @@ -859,6 +985,8 @@ this = (struct impl *) handle; + spa_vulkan_deinit(&this->state); + if (this->data_loop) spa_loop_invoke(this->data_loop, do_remove_timer, 0, NULL, 0, true, this); spa_system_close(this->data_system, this->timer_source.fd); @@ -933,7 +1061,7 @@ SPA_PORT_CHANGE_MASK_PARAMS | SPA_PORT_CHANGE_MASK_PROPS; port->info = SPA_PORT_INFO_INIT(); - port->info.flags = SPA_PORT_FLAG_NO_REF | SPA_PORT_FLAG_CAN_ALLOC_BUFFERS; + port->info.flags = SPA_PORT_FLAG_NO_REF; if (this->props.live) port->info.flags |= SPA_PORT_FLAG_LIVE; port->params0 = SPA_PARAM_INFO(SPA_PARAM_EnumFormat, SPA_PARAM_INFO_READ); @@ -951,6 +1079,7 @@ SPA_DIRECTION_OUTPUT, NULL); this->state.shaderName = "spa/plugins/vulkan/shaders/main.spv"; this->state.n_streams = 1; + spa_vulkan_init(&this->state); return 0; }
View file
pipewire-0.3.80.tar.gz/spa/plugins/vulkan/vulkan-compute-utils.c
Added
@@ -0,0 +1,675 @@ +/* Spa */ +/* SPDX-FileCopyrightText: Copyright © 2019 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#include <vulkan/vulkan.h> + +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <fcntl.h> +#include <string.h> +#include <vulkan/vulkan_core.h> +#if !defined(__FreeBSD__) && !defined(__MidnightBSD__) +#include <alloca.h> +#endif +#include <errno.h> +#include <stdio.h> +#include <assert.h> +#include <math.h> +#include <time.h> + +#include <spa/utils/result.h> +#include <spa/utils/string.h> +#include <spa/support/log.h> +#include <spa/debug/mem.h> + +#include "vulkan-compute-utils.h" +#include "vulkan-utils.h" + +#define VULKAN_INSTANCE_FUNCTION(name) \ + PFN_##name name = (PFN_##name)vkGetInstanceProcAddr(s->base.instance, #name) + +static int createFence(struct vulkan_compute_state *s) { + VkFenceCreateInfo createInfo = { + .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, + .flags = 0, + }; + VK_CHECK_RESULT(vkCreateFence(s->base.device, &createInfo, NULL, &s->fence)); + + return 0; +}; + +static int createDescriptors(struct vulkan_compute_state *s) +{ + uint32_t i; + + VkDescriptorPoolSize descriptorPoolSizes2 = { + { + .type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, + .descriptorCount = 1, + }, + { + .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .descriptorCount = s->n_streams - 1, + }, + }; + const VkDescriptorPoolCreateInfo descriptorPoolCreateInfo = { + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, + .maxSets = s->n_streams, + .poolSizeCount = s->n_streams > 1 ? 2 : 1, + .pPoolSizes = descriptorPoolSizes, + }; + + VK_CHECK_RESULT(vkCreateDescriptorPool(s->base.device, + &descriptorPoolCreateInfo, NULL, + &s->descriptorPool)); + + VkDescriptorSetLayoutBinding descriptorSetLayoutBindings->n_streams; + descriptorSetLayoutBinding0 = (VkDescriptorSetLayoutBinding) { + .binding = 0, + .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, + .descriptorCount = 1, + .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT + }; + for (i = 1; i < s->n_streams; i++) { + descriptorSetLayoutBindingi = (VkDescriptorSetLayoutBinding) { + .binding = i, + .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .descriptorCount = 1, + .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT + }; + }; + const VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo = { + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, + .bindingCount = s->n_streams, + .pBindings = descriptorSetLayoutBinding + }; + VK_CHECK_RESULT(vkCreateDescriptorSetLayout(s->base.device, + &descriptorSetLayoutCreateInfo, NULL, + &s->descriptorSetLayout)); + + const VkDescriptorSetAllocateInfo descriptorSetAllocateInfo = { + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, + .descriptorPool = s->descriptorPool, + .descriptorSetCount = 1, + .pSetLayouts = &s->descriptorSetLayout + }; + + VK_CHECK_RESULT(vkAllocateDescriptorSets(s->base.device, + &descriptorSetAllocateInfo, + &s->descriptorSet)); + + const VkSamplerCreateInfo samplerInfo = { + .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, + .magFilter = VK_FILTER_LINEAR, + .minFilter = VK_FILTER_LINEAR, + .mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR, + .addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, + .addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, + .addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, + .borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK, + .unnormalizedCoordinates = VK_FALSE, + .compareEnable = VK_FALSE, + .compareOp = VK_COMPARE_OP_ALWAYS, + .mipLodBias = 0.0f, + .minLod = 0, + .maxLod = 5, + }; + VK_CHECK_RESULT(vkCreateSampler(s->base.device, &samplerInfo, NULL, &s->sampler)); + + return 0; +} + +static int updateDescriptors(struct vulkan_compute_state *s) +{ + uint32_t i; + VkDescriptorImageInfo descriptorImageInfos->n_streams; + VkWriteDescriptorSet writeDescriptorSets->n_streams; + uint32_t descriptorSetLen = 0; + + for (i = 0; i < s->n_streams; i++) { + struct vulkan_stream *p = &s->streamsi; + + if (p->current_buffer_id == p->pending_buffer_id || + p->pending_buffer_id == SPA_ID_INVALID) + continue; + + p->current_buffer_id = p->pending_buffer_id; + p->busy_buffer_id = p->current_buffer_id; + p->pending_buffer_id = SPA_ID_INVALID; + + descriptorImageInfodescriptorSetLen = (VkDescriptorImageInfo) { + .sampler = s->sampler, + .imageView = p->buffersp->current_buffer_id.view, + .imageLayout = VK_IMAGE_LAYOUT_GENERAL, + }; + writeDescriptorSetdescriptorSetLen = (VkWriteDescriptorSet) { + .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + .dstSet = s->descriptorSet, + .dstBinding = i, + .descriptorCount = 1, + .descriptorType = i == 0 ? + VK_DESCRIPTOR_TYPE_STORAGE_IMAGE : + VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .pImageInfo = &descriptorImageInfoi, + }; + descriptorSetLen++; + } + vkUpdateDescriptorSets(s->base.device, descriptorSetLen, + writeDescriptorSet, 0, NULL); + + return 0; +} + +static VkShaderModule createShaderModule(struct vulkan_compute_state *s, const char* shaderFile) +{ + VkShaderModule shaderModule = VK_NULL_HANDLE; + VkResult result; + void *data; + int fd; + struct stat stat; + + if ((fd = open(shaderFile, 0, O_RDONLY)) == -1) { + spa_log_error(s->log, "can't open %s: %m", shaderFile); + return VK_NULL_HANDLE; + } + if (fstat(fd, &stat) < 0) { + spa_log_error(s->log, "can't stat %s: %m", shaderFile); + close(fd); + return VK_NULL_HANDLE; + } + + data = mmap(NULL, stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + + const VkShaderModuleCreateInfo shaderModuleCreateInfo = { + .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, + .codeSize = stat.st_size, + .pCode = data, + }; + result = vkCreateShaderModule(s->base.device, + &shaderModuleCreateInfo, 0, &shaderModule); + + munmap(data, stat.st_size); + close(fd); + + if (result != VK_SUCCESS) { + spa_log_error(s->log, "can't create shader %s: %m", shaderFile); + return VK_NULL_HANDLE; + } + return shaderModule; +} + +static int createComputePipeline(struct vulkan_compute_state *s, const char *shader_file) +{ + static const VkPushConstantRange range = { + .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT, + .offset = 0, + .size = sizeof(struct push_constants) + }; + + const VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = { + .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, + .setLayoutCount = 1, + .pSetLayouts = &s->descriptorSetLayout, + .pushConstantRangeCount = 1, + .pPushConstantRanges = &range, + }; + VK_CHECK_RESULT(vkCreatePipelineLayout(s->base.device, + &pipelineLayoutCreateInfo, NULL, + &s->pipelineLayout)); + + s->computeShaderModule = createShaderModule(s, shader_file); + if (s->computeShaderModule == VK_NULL_HANDLE) + return -ENOENT; + + const VkPipelineShaderStageCreateInfo shaderStageCreateInfo = { + .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, + .stage = VK_SHADER_STAGE_COMPUTE_BIT, + .module = s->computeShaderModule, + .pName = "main", + }; + const VkComputePipelineCreateInfo pipelineCreateInfo = { + .sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO, + .stage = shaderStageCreateInfo, + .layout = s->pipelineLayout, + }; + VK_CHECK_RESULT(vkCreateComputePipelines(s->base.device, VK_NULL_HANDLE, + 1, &pipelineCreateInfo, NULL, + &s->pipeline)); + return 0; +} + +static int createCommandBuffer(struct vulkan_compute_state *s) +{ + CHECK(vulkan_commandPool_create(&s->base, &s->commandPool)); + CHECK(vulkan_commandBuffer_create(&s->base, s->commandPool, &s->commandBuffer)); + + return 0; +} + +static int runExportSHMBuffers(struct vulkan_compute_state *s) { + for (uint32_t i = 0; i < s->n_streams; i++) { + struct vulkan_stream *p = &s->streamsi; + + if (p->direction == SPA_DIRECTION_INPUT) + continue; + + if (p->spa_buffersp->current_buffer_id->datas0.type == SPA_DATA_MemPtr) { + struct spa_buffer *spa_buf = p->spa_buffersp->current_buffer_id; + struct vulkan_read_pixels_info readInfo = { + .data = spa_buf->datas0.data, + .offset = spa_buf->datas0.chunk->offset, + .stride = spa_buf->datas0.chunk->stride, + .bytes_per_pixel = 16, + .size.width = s->constants.width, + .size.height = s->constants.height, + }; + CHECK(vulkan_read_pixels(&s->base, &readInfo, &p->buffersp->current_buffer_id)); + } + } + + return 0; +} + +/** runCommandBuffer + * The return value of this functions means the following: + * ret < 0: Error + * ret = 0: queueSubmit was succsessful, but manual synchronization is required + * ret = 1: queueSubmit was succsessful and buffers can be released without synchronization + */ +static int runCommandBuffer(struct vulkan_compute_state *s) +{ + VULKAN_INSTANCE_FUNCTION(vkQueueSubmit2KHR); + VULKAN_INSTANCE_FUNCTION(vkGetSemaphoreFdKHR); + + static const VkCommandBufferBeginInfo beginInfo = { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, + }; + VK_CHECK_RESULT(vkBeginCommandBuffer(s->commandBuffer, &beginInfo)); + + vkCmdBindPipeline(s->commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, s->pipeline); + vkCmdPushConstants (s->commandBuffer, + s->pipelineLayout, VK_SHADER_STAGE_COMPUTE_BIT, + 0, sizeof(struct push_constants), (const void *) &s->constants); + vkCmdBindDescriptorSets(s->commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, + s->pipelineLayout, 0, 1, &s->descriptorSet, 0, NULL); + + vkCmdDispatch(s->commandBuffer, + (uint32_t)ceil(s->constants.width / (float)WORKGROUP_SIZE), + (uint32_t)ceil(s->constants.height / (float)WORKGROUP_SIZE), 1); + + VkImageMemoryBarrier acquire_barriers->n_streams; + VkImageMemoryBarrier release_barriers->n_streams; + VkSemaphoreSubmitInfo semaphore_wait_infos->n_streams; + uint32_t semaphore_wait_info_len = 0; + VkSemaphoreSubmitInfo semaphore_signal_info1; + uint32_t semaphore_signal_info_len = 0; + + uint32_t i; + for (i = 0; i < s->n_streams; i++) { + struct vulkan_stream *p = &s->streamsi; + struct vulkan_buffer *current_buffer = &p->buffersp->current_buffer_id; + struct spa_buffer *current_spa_buffer = p->spa_buffersp->current_buffer_id; + + VkAccessFlags access_flags; + if (p->direction == SPA_DIRECTION_INPUT) { + access_flags = VK_ACCESS_SHADER_READ_BIT; + } else { + access_flags = VK_ACCESS_SHADER_WRITE_BIT; + } + + acquire_barrieri= (VkImageMemoryBarrier) { + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_FOREIGN_EXT, + .dstQueueFamilyIndex = s->base.queueFamilyIndex, + .image = current_buffer->image, + .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED, + .newLayout = VK_IMAGE_LAYOUT_GENERAL, + .srcAccessMask = 0, + .dstAccessMask = access_flags, + .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .subresourceRange.levelCount = 1, + .subresourceRange.layerCount = 1, + }; + + release_barrieri= (VkImageMemoryBarrier) { + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .srcQueueFamilyIndex = s->base.queueFamilyIndex, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_FOREIGN_EXT, + .image = current_buffer->image, + .oldLayout = VK_IMAGE_LAYOUT_GENERAL, + .newLayout = VK_IMAGE_LAYOUT_GENERAL, + .srcAccessMask = access_flags, + .dstAccessMask = 0, + .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .subresourceRange.levelCount = 1, + .subresourceRange.layerCount = 1, + }; + + if (current_spa_buffer->datas0.type != SPA_DATA_DmaBuf) + continue; + + if (vulkan_sync_foreign_dmabuf(&s->base, current_buffer) < 0) { + spa_log_warn(s->log, "Failed to wait for foreign buffer DMA-BUF fence"); + } else { + if (current_buffer->foreign_semaphore != VK_NULL_HANDLE) { + semaphore_wait_infosemaphore_wait_info_len++ = (VkSemaphoreSubmitInfo) { + .sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO, + .semaphore = current_buffer->foreign_semaphore, + .stageMask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + }; + } + } + } + + vkCmdPipelineBarrier(s->commandBuffer, + VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + 0, 0, NULL, 0, NULL, + s->n_streams, acquire_barrier); + + vkCmdPipelineBarrier(s->commandBuffer, + VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, + 0, 0, NULL, 0, NULL, + s->n_streams, release_barrier); + + VK_CHECK_RESULT(vkEndCommandBuffer(s->commandBuffer)); + + VK_CHECK_RESULT(vkResetFences(s->base.device, 1, &s->fence)); + + if (s->pipelineSemaphore == VK_NULL_HANDLE) { + VkExportSemaphoreCreateInfo export_info = { + .sType = VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO, + .handleTypes = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT, + }; + VkSemaphoreCreateInfo semaphore_info = { + .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, + .pNext = &export_info, + }; + VK_CHECK_RESULT(vkCreateSemaphore(s->base.device, &semaphore_info, NULL, &s->pipelineSemaphore)); + } + + semaphore_signal_infosemaphore_signal_info_len++ = (VkSemaphoreSubmitInfo) { + .sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO, + .semaphore = s->pipelineSemaphore, + }; + + VkCommandBufferSubmitInfoKHR commandBufferInfo = { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_SUBMIT_INFO, + .commandBuffer = s->commandBuffer, + }; + + const VkSubmitInfo2KHR submitInfo = { + .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO_2_KHR, + .commandBufferInfoCount = 1, + .pCommandBufferInfos = &commandBufferInfo, + .waitSemaphoreInfoCount = semaphore_wait_info_len, + .pWaitSemaphoreInfos = semaphore_wait_info, + .signalSemaphoreInfoCount = semaphore_signal_info_len, + .pSignalSemaphoreInfos = semaphore_signal_info, + }; + VK_CHECK_RESULT(vkQueueSubmit2KHR(s->base.queue, 1, &submitInfo, s->fence)); + s->started = true; + + VkSemaphoreGetFdInfoKHR get_fence_fd_info = { + .sType = VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR, + .semaphore = s->pipelineSemaphore, + .handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT, + }; + int sync_file_fd = -1; + VK_CHECK_RESULT(vkGetSemaphoreFdKHR(s->base.device, &get_fence_fd_info, &sync_file_fd)); + + int ret = 1; + for (uint32_t i = 0; i < s->n_streams; i++) { + struct vulkan_stream *p = &s->streamsi; + struct spa_buffer *current_spa_buffer = p->spa_buffersp->current_buffer_id; + + if (current_spa_buffer->datas0.type != SPA_DATA_DmaBuf) + continue; + + if (!vulkan_sync_export_dmabuf(&s->base, &p->buffersp->current_buffer_id, sync_file_fd)) { + ret = 0; + } + } + close(sync_file_fd); + + return ret; +} + +static void clear_buffers(struct vulkan_compute_state *s, struct vulkan_stream *p) +{ + uint32_t i; + + for (i = 0; i < p->n_buffers; i++) { + vulkan_buffer_clear(&s->base, &p->buffersi); + p->spa_buffersi = NULL; + } + p->n_buffers = 0; +} + +static void clear_streams(struct vulkan_compute_state *s) +{ + uint32_t i; + for (i = 0; i < s->n_streams; i++) { + struct vulkan_stream *p = &s->streamsi; + clear_buffers(s, p); + } +} + +int spa_vulkan_fixate_modifier(struct vulkan_compute_state *s, struct vulkan_stream *p, struct spa_video_info_dsp *dsp_info, + uint32_t modifierCount, uint64_t *modifiers, uint64_t *modifier) +{ + VkFormat format = vulkan_id_to_vkformat(dsp_info->format); + if (format == VK_FORMAT_UNDEFINED) { + return -1; + } + + struct dmabuf_fixation_info fixation_info = { + .format = format, + .modifierCount = modifierCount, + .modifiers = modifiers, + .size.width = s->constants.width, + .size.height = s->constants.height, + .usage = VK_IMAGE_USAGE_STORAGE_BIT, + }; + return vulkan_fixate_modifier(&s->base, &fixation_info, modifier); +} + +int spa_vulkan_use_buffers(struct vulkan_compute_state *s, struct vulkan_stream *p, uint32_t flags, + struct spa_video_info_dsp *dsp_info, uint32_t n_buffers, struct spa_buffer **buffers) +{ + VkFormat format = vulkan_id_to_vkformat(dsp_info->format); + if (format == VK_FORMAT_UNDEFINED) + return -1; + + vulkan_wait_idle(&s->base); + clear_buffers(s, p); + + bool alloc = flags & SPA_NODE_BUFFERS_FLAG_ALLOC; + int ret; + p->n_buffers = 0; + for (uint32_t i = 0; i < n_buffers; i++) { + if (alloc) { + if (SPA_FLAG_IS_SET(buffersi->datas0.type, 1<<SPA_DATA_DmaBuf)) { + struct external_buffer_info dmabufInfo = { + .format = format, + .modifier = dsp_info->modifier, + .size.width = s->constants.width, + .size.height = s->constants.height, + .usage = p->direction == SPA_DIRECTION_OUTPUT + ? VK_IMAGE_USAGE_STORAGE_BIT + : VK_IMAGE_USAGE_SAMPLED_BIT, + .spa_buf = buffersi, + }; + ret = vulkan_create_dmabuf(&s->base, &dmabufInfo, &p->buffersi); + } else { + spa_log_error(s->log, "Unsupported buffer type mask %d", buffersi->datas0.type); + return -1; + } + } else { + switch (buffersi->datas0.type) { + case SPA_DATA_DmaBuf:; + struct external_buffer_info dmabufInfo = { + .format = format, + .modifier = dsp_info->modifier, + .size.width = s->constants.width, + .size.height = s->constants.height, + .usage = p->direction == SPA_DIRECTION_OUTPUT + ? VK_IMAGE_USAGE_STORAGE_BIT + : VK_IMAGE_USAGE_SAMPLED_BIT, + .spa_buf = buffersi, + }; + ret = vulkan_import_dmabuf(&s->base, &dmabufInfo, &p->buffersi); + break; + case SPA_DATA_MemPtr:; + struct external_buffer_info memptrInfo = { + .format = format, + .size.width = s->constants.width, + .size.height = s->constants.height, + .usage = p->direction == SPA_DIRECTION_OUTPUT + ? VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT + : VK_IMAGE_USAGE_SAMPLED_BIT, + .spa_buf = buffersi, + }; + ret = vulkan_import_memptr(&s->base, &memptrInfo, &p->buffersi); + break; + default: + spa_log_error(s->log, "Unsupported buffer type %d", buffersi->datas0.type); + return -1; + } + } + if (ret != 0) { + spa_log_error(s->log, "Failed to use buffer %d", i); + return ret; + } + p->spa_buffersi = buffersi; + p->n_buffers++; + } + + return 0; +} + +int spa_vulkan_init_stream(struct vulkan_compute_state *s, struct vulkan_stream *stream, + enum spa_direction direction, struct spa_dict *props) +{ + return vulkan_stream_init(stream, direction, props); +} + +int spa_vulkan_prepare(struct vulkan_compute_state *s) +{ + if (!s->prepared) { + CHECK(createFence(s)); + CHECK(createDescriptors(s)); + CHECK(createComputePipeline(s, s->shaderName)); + CHECK(createCommandBuffer(s)); + s->prepared = true; + } + return 0; +} + +int spa_vulkan_unprepare(struct vulkan_compute_state *s) +{ + if (s->prepared) { + vkDestroyShaderModule(s->base.device, s->computeShaderModule, NULL); + vkDestroySampler(s->base.device, s->sampler, NULL); + vkDestroyDescriptorPool(s->base.device, s->descriptorPool, NULL); + vkDestroyDescriptorSetLayout(s->base.device, s->descriptorSetLayout, NULL); + vkDestroyPipelineLayout(s->base.device, s->pipelineLayout, NULL); + vkDestroyPipeline(s->base.device, s->pipeline, NULL); + vkDestroyCommandPool(s->base.device, s->commandPool, NULL); + vkDestroyFence(s->base.device, s->fence, NULL); + s->prepared = false; + } + return 0; +} + +int spa_vulkan_start(struct vulkan_compute_state *s) +{ + uint32_t i; + + for (i = 0; i < s->n_streams; i++) { + struct vulkan_stream *p = &s->streamsi; + p->current_buffer_id = SPA_ID_INVALID; + p->busy_buffer_id = SPA_ID_INVALID; + p->ready_buffer_id = SPA_ID_INVALID; + } + return 0; +} + +int spa_vulkan_stop(struct vulkan_compute_state *s) +{ + VK_CHECK_RESULT(vkDeviceWaitIdle(s->base.device)); + clear_streams(s); + s->started = false; + return 0; +} + +int spa_vulkan_ready(struct vulkan_compute_state *s) +{ + uint32_t i; + VkResult result; + + if (!s->started) + return 0; + + result = vkGetFenceStatus(s->base.device, s->fence); + if (result == VK_NOT_READY) + return -EBUSY; + VK_CHECK_RESULT(result); + + s->started = false; + + for (i = 0; i < s->n_streams; i++) { + struct vulkan_stream *p = &s->streamsi; + p->ready_buffer_id = p->busy_buffer_id; + p->busy_buffer_id = SPA_ID_INVALID; + } + return 0; +} + +int spa_vulkan_process(struct vulkan_compute_state *s) +{ + CHECK(updateDescriptors(s)); + CHECK(runCommandBuffer(s)); + VK_CHECK_RESULT(vkDeviceWaitIdle(s->base.device)); + CHECK(runExportSHMBuffers(s)); + + return 0; +} + +int spa_vulkan_get_buffer_caps(struct vulkan_compute_state *s, enum spa_direction direction) +{ + switch (direction) { + case SPA_DIRECTION_INPUT: + return VULKAN_BUFFER_TYPE_CAP_DMABUF; + case SPA_DIRECTION_OUTPUT: + return VULKAN_BUFFER_TYPE_CAP_DMABUF | VULKAN_BUFFER_TYPE_CAP_SHM; + } + return 0; +} + +struct vulkan_modifier_info *spa_vulkan_get_modifier_info(struct vulkan_compute_state *s, struct spa_video_info_dsp *info) { + VkFormat vk_format = vulkan_id_to_vkformat(info->format); + return vulkan_modifierInfo_find(&s->base, vk_format, info->modifier); +} + +int spa_vulkan_init(struct vulkan_compute_state *s) +{ + s->base.log = s->log; + uint32_t dsp_format = SPA_VIDEO_FORMAT_DSP_F32; + struct vulkan_base_info baseInfo = { + .queueFlags = VK_QUEUE_COMPUTE_BIT, + .formatInfo.formatCount = 1, + .formatInfo.formats = &dsp_format, + }; + return vulkan_base_init(&s->base, &baseInfo); +} + +void spa_vulkan_deinit(struct vulkan_compute_state *s) +{ + vulkan_base_deinit(&s->base); +}
View file
pipewire-0.3.80.tar.gz/spa/plugins/vulkan/vulkan-compute-utils.h
Added
@@ -0,0 +1,80 @@ +/* Spa */ +/* SPDX-FileCopyrightText: Copyright © 2019 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#include <vulkan/vulkan.h> + +#include <spa/buffer/buffer.h> +#include <spa/param/video/format.h> +#include <spa/node/node.h> + +#include "vulkan-utils.h" + +#define MAX_STREAMS 2 +#define WORKGROUP_SIZE 32 + +struct pixel { + float r, g, b, a; +}; + +struct push_constants { + float time; + int frame; + int width; + int height; +}; + + +struct vulkan_compute_state { + struct spa_log *log; + + struct push_constants constants; + + struct vulkan_base base; + + VkPipeline pipeline; + VkPipelineLayout pipelineLayout; + const char *shaderName; + VkShaderModule computeShaderModule; + + VkCommandPool commandPool; + VkCommandBuffer commandBuffer; + + VkFence fence; + VkSemaphore pipelineSemaphore; + unsigned int initialized:1; + unsigned int prepared:1; + unsigned int started:1; + + VkDescriptorPool descriptorPool; + VkDescriptorSetLayout descriptorSetLayout; + + VkSampler sampler; + + uint32_t n_streams; + VkDescriptorSet descriptorSet; + struct vulkan_stream streamsMAX_STREAMS; +}; + +int spa_vulkan_init_stream(struct vulkan_compute_state *s, struct vulkan_stream *stream, enum spa_direction, + struct spa_dict *props); + +int spa_vulkan_fixate_modifier(struct vulkan_compute_state *s, struct vulkan_stream *p, struct spa_video_info_dsp *dsp_info, + uint32_t modifierCount, uint64_t *modifiers, uint64_t *modifier); +int spa_vulkan_prepare(struct vulkan_compute_state *s); +int spa_vulkan_use_buffers(struct vulkan_compute_state *s, struct vulkan_stream *stream, uint32_t flags, + struct spa_video_info_dsp *dsp_info, uint32_t n_buffers, struct spa_buffer **buffers); +int spa_vulkan_unprepare(struct vulkan_compute_state *s); + +int spa_vulkan_start(struct vulkan_compute_state *s); +int spa_vulkan_stop(struct vulkan_compute_state *s); +int spa_vulkan_ready(struct vulkan_compute_state *s); +int spa_vulkan_process(struct vulkan_compute_state *s); +int spa_vulkan_cleanup(struct vulkan_compute_state *s); + +int spa_vulkan_get_buffer_caps(struct vulkan_compute_state *s, enum spa_direction direction); +struct vulkan_modifier_info *spa_vulkan_get_modifier_info(struct vulkan_compute_state *s, + struct spa_video_info_dsp *dsp_info); + +int spa_vulkan_init(struct vulkan_compute_state *s); +void spa_vulkan_deinit(struct vulkan_compute_state *s);
View file
pipewire-0.3.80.tar.gz/spa/plugins/vulkan/vulkan-types.h
Added
@@ -0,0 +1,75 @@ +#pragma once + +#include <vulkan/vulkan.h> + +#include <spa/buffer/buffer.h> +#include <spa/node/node.h> + +#define MAX_BUFFERS 16 +#define DMABUF_MAX_PLANES 1 + +enum buffer_type_caps { + VULKAN_BUFFER_TYPE_CAP_SHM = 1<<0, + VULKAN_BUFFER_TYPE_CAP_DMABUF = 1<<1, +}; + +struct vulkan_modifier_info { + VkDrmFormatModifierPropertiesEXT props; + VkExtent2D max_extent; +}; + +struct vulkan_format_info { + uint32_t spa_format; + VkFormat vk_format; + uint32_t modifierCount; + struct vulkan_modifier_info *infos; +}; + +struct vulkan_buffer { + int fd; + VkImage image; + VkImageView view; + VkDeviceMemory memory; + VkSemaphore foreign_semaphore; +}; + +struct vulkan_stream { + enum spa_direction direction; + + uint32_t pending_buffer_id; + uint32_t current_buffer_id; + uint32_t busy_buffer_id; + uint32_t ready_buffer_id; + + struct vulkan_buffer buffersMAX_BUFFERS; + struct spa_buffer *spa_buffersMAX_BUFFERS; + uint32_t n_buffers; +}; + +struct vulkan_base_info { + uint32_t queueFlags; + + struct { + uint32_t formatCount; + uint32_t *formats; + } formatInfo; +}; + +struct vulkan_base { + struct spa_log *log; + + VkInstance instance; + + VkPhysicalDevice physicalDevice; + + VkQueue queue; + uint32_t queueFamilyIndex; + VkDevice device; + + uint32_t formatInfoCount; + struct vulkan_format_info *formatInfos; + + bool implicit_sync_interop; + + unsigned int initialized:1; +};
View file
pipewire-0.3.79.tar.gz/spa/plugins/vulkan/vulkan-utils.c -> pipewire-0.3.80.tar.gz/spa/plugins/vulkan/vulkan-utils.c
Changed
@@ -1,3 +1,7 @@ +/* Spa */ +/* SPDX-FileCopyrightText: Copyright © 2019 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + #include <vulkan/vulkan.h> #include <unistd.h> @@ -6,6 +10,7 @@ #include <sys/mman.h> #include <fcntl.h> #include <string.h> +#include <poll.h> #if !defined(__FreeBSD__) && !defined(__MidnightBSD__) #include <alloca.h> #endif @@ -17,10 +22,12 @@ #include <spa/utils/result.h> #include <spa/utils/string.h> +#include <spa/param/video/format.h> #include <spa/support/log.h> #include <spa/debug/mem.h> #include "vulkan-utils.h" +#include "dmabuf.h" //#define ENABLE_VALIDATION @@ -91,23 +98,14 @@ } } -#define VK_CHECK_RESULT(f) \ -{ \ - VkResult _result = (f); \ - int _r = -vkresult_to_errno(_result); \ - if (_result != VK_SUCCESS) { \ - spa_log_error(s->log, "error: %d (%d %s)", _result, _r, spa_strerror(_r)); \ - return _r; \ - } \ -} -#define CHECK(f) \ -{ \ - int _res = (f); \ - if (_res < 0) \ - return _res; \ -} +static struct { + VkFormat format; + uint32_t id; +} vk_video_format_convs = { + { VK_FORMAT_R32G32B32A32_SFLOAT, SPA_VIDEO_FORMAT_RGBA_F32 }, +}; -static int createInstance(struct vulkan_state *s) +static int createInstance(struct vulkan_base *s) { static const VkApplicationInfo applicationInfo = { .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, @@ -154,7 +152,24 @@ return 0; } -static uint32_t getComputeQueueFamilyIndex(struct vulkan_state *s) +static int findPhysicalDevice(struct vulkan_base *s) +{ + uint32_t deviceCount; + VkPhysicalDevice *devices; + + vkEnumeratePhysicalDevices(s->instance, &deviceCount, NULL); + if (deviceCount == 0) + return -ENODEV; + + devices = alloca(deviceCount * sizeof(VkPhysicalDevice)); + vkEnumeratePhysicalDevices(s->instance, &deviceCount, devices); + + s->physicalDevice = devices0; + + return 0; +} + +static int getComputeQueueFamilyIndex(struct vulkan_base *s, uint32_t queueFlags, uint32_t *queueFamilyIndex) { uint32_t i, queueFamilyCount; VkQueueFamilyProperties *queueFamilies; @@ -167,283 +182,245 @@ for (i = 0; i < queueFamilyCount; i++) { VkQueueFamilyProperties props = queueFamiliesi; - if (props.queueCount > 0 && (props.queueFlags & VK_QUEUE_COMPUTE_BIT)) + if (props.queueCount > 0 && ((props.queueFlags & queueFlags) == queueFlags)) break; } if (i == queueFamilyCount) return -ENODEV; - return i; -} - -static int findPhysicalDevice(struct vulkan_state *s) -{ - uint32_t deviceCount; - VkPhysicalDevice *devices; - - vkEnumeratePhysicalDevices(s->instance, &deviceCount, NULL); - if (deviceCount == 0) - return -ENODEV; - - devices = alloca(deviceCount * sizeof(VkPhysicalDevice)); - vkEnumeratePhysicalDevices(s->instance, &deviceCount, devices); - - s->physicalDevice = devices0; - - s->queueFamilyIndex = getComputeQueueFamilyIndex(s); - + *queueFamilyIndex = i; return 0; } -static int createDevice(struct vulkan_state *s) +static int createDevice(struct vulkan_base *s, struct vulkan_base_info *info) { + CHECK(getComputeQueueFamilyIndex(s, info->queueFlags, &s->queueFamilyIndex)); + const VkDeviceQueueCreateInfo queueCreateInfo = { .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, .queueFamilyIndex = s->queueFamilyIndex, .queueCount = 1, .pQueuePriorities = (const float) { 1.0f } }; + const VkPhysicalDeviceSynchronization2FeaturesKHR sync2_features = { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SYNCHRONIZATION_2_FEATURES_KHR, + .synchronization2 = VK_TRUE, + }; static const char * const extensions = { VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME, - VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME + VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME, + VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME, + VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME, + VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME, + VK_EXT_IMAGE_DRM_FORMAT_MODIFIER_EXTENSION_NAME }; const VkDeviceCreateInfo deviceCreateInfo = { .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, .queueCreateInfoCount = 1, .pQueueCreateInfos = &queueCreateInfo, - .enabledExtensionCount = 2, + .enabledExtensionCount = SPA_N_ELEMENTS(extensions), .ppEnabledExtensionNames = extensions, + .pNext = &sync2_features, }; VK_CHECK_RESULT(vkCreateDevice(s->physicalDevice, &deviceCreateInfo, NULL, &s->device)); vkGetDeviceQueue(s->device, s->queueFamilyIndex, 0, &s->queue); - static const VkFenceCreateInfo fenceCreateInfo = { - .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, - .flags = 0, - }; - VK_CHECK_RESULT(vkCreateFence(s->device, &fenceCreateInfo, NULL, &s->fence)); - return 0; } -static uint32_t findMemoryType(struct vulkan_state *s, - uint32_t memoryTypeBits, VkMemoryPropertyFlags properties) +static int queryFormatInfo(struct vulkan_base *s, struct vulkan_base_info *info) { - uint32_t i; - VkPhysicalDeviceMemoryProperties memoryProperties; + if (s->formatInfos) + return 0; - vkGetPhysicalDeviceMemoryProperties(s->physicalDevice, &memoryProperties); + s->formatInfos = calloc(info->formatInfo.formatCount, sizeof(struct vulkan_format_info)); + if (!s->formatInfos) + return -ENOMEM; - for (i = 0; i < memoryProperties.memoryTypeCount; i++) { - if ((memoryTypeBits & (1 << i)) && - ((memoryProperties.memoryTypesi.propertyFlags & properties) == properties)) - return i; - } - return -1; -} + for (uint32_t i = 0; i < info->formatInfo.formatCount; i++) { + VkFormat format = vulkan_id_to_vkformat(info->formatInfo.formatsi); + if (format == VK_FORMAT_UNDEFINED) + continue; + struct vulkan_format_info *f_info = &s->formatInfoss->formatInfoCount++; + f_info->spa_format = info->formatInfo.formatsi; + f_info->vk_format = format; -static int createDescriptors(struct vulkan_state *s) -{ - uint32_t i; + VkDrmFormatModifierPropertiesListEXT modPropsList = { + .sType = VK_STRUCTURE_TYPE_DRM_FORMAT_MODIFIER_PROPERTIES_LIST_EXT, + }; + VkFormatProperties2 fmtProps = { + .sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2, + .pNext = &modPropsList, + }; + vkGetPhysicalDeviceFormatProperties2(s->physicalDevice, format, &fmtProps); - VkDescriptorPoolSize descriptorPoolSizes2 = { - { - .type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, - .descriptorCount = 1, - }, - { - .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, - .descriptorCount = s->n_streams - 1, - }, - }; - const VkDescriptorPoolCreateInfo descriptorPoolCreateInfo = { - .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, - .maxSets = s->n_streams, - .poolSizeCount = s->n_streams > 1 ? 2 : 1, - .pPoolSizes = descriptorPoolSizes, - }; + if (!modPropsList.drmFormatModifierCount) + continue; - VK_CHECK_RESULT(vkCreateDescriptorPool(s->device, - &descriptorPoolCreateInfo, NULL, - &s->descriptorPool)); + modPropsList.pDrmFormatModifierProperties = calloc(modPropsList.drmFormatModifierCount, + sizeof(modPropsList.pDrmFormatModifierProperties0)); + if (!modPropsList.pDrmFormatModifierProperties) + continue; + vkGetPhysicalDeviceFormatProperties2(s->physicalDevice, format, &fmtProps); - VkDescriptorSetLayoutBinding descriptorSetLayoutBindings->n_streams; - descriptorSetLayoutBinding0 = (VkDescriptorSetLayoutBinding) { - .binding = 0, - .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, - .descriptorCount = 1, - .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT - }; - for (i = 1; i < s->n_streams; i++) { - descriptorSetLayoutBindingi = (VkDescriptorSetLayoutBinding) { - .binding = i, - .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, - .descriptorCount = 1, - .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT - }; - }; - const VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo = { - .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, - .bindingCount = s->n_streams, - .pBindings = descriptorSetLayoutBinding - }; - VK_CHECK_RESULT(vkCreateDescriptorSetLayout(s->device, - &descriptorSetLayoutCreateInfo, NULL, - &s->descriptorSetLayout)); - - const VkDescriptorSetAllocateInfo descriptorSetAllocateInfo = { - .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, - .descriptorPool = s->descriptorPool, - .descriptorSetCount = 1, - .pSetLayouts = &s->descriptorSetLayout - }; + f_info->infos = calloc(modPropsList.drmFormatModifierCount, sizeof(f_info->infos0)); + if (!f_info->infos) { + free(modPropsList.pDrmFormatModifierProperties); + continue; + } - VK_CHECK_RESULT(vkAllocateDescriptorSets(s->device, - &descriptorSetAllocateInfo, - &s->descriptorSet)); - - const VkSamplerCreateInfo samplerInfo = { - .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, - .magFilter = VK_FILTER_LINEAR, - .minFilter = VK_FILTER_LINEAR, - .mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR, - .addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, - .addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, - .addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, - .borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK, - .unnormalizedCoordinates = VK_FALSE, - .compareEnable = VK_FALSE, - .compareOp = VK_COMPARE_OP_ALWAYS, - .mipLodBias = 0.0f, - .minLod = 0, - .maxLod = 5, - }; - VK_CHECK_RESULT(vkCreateSampler(s->device, &samplerInfo, NULL, &s->sampler)); + for (uint32_t j = 0; j < modPropsList.drmFormatModifierCount; j++) { + VkDrmFormatModifierPropertiesEXT props = modPropsList.pDrmFormatModifierPropertiesj; - return 0; -} + if (!(props.drmFormatModifierTilingFeatures & VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT)) + continue; -static int updateDescriptors(struct vulkan_state *s) -{ - uint32_t i; - VkDescriptorImageInfo descriptorImageInfos->n_streams; - VkWriteDescriptorSet writeDescriptorSets->n_streams; + if (props.drmFormatModifierPlaneCount > DMABUF_MAX_PLANES) + continue; - for (i = 0; i < s->n_streams; i++) { - struct vulkan_stream *p = &s->streamsi; + VkPhysicalDeviceImageDrmFormatModifierInfoEXT modInfo = { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_DRM_FORMAT_MODIFIER_INFO_EXT, + .drmFormatModifier = props.drmFormatModifier, + .sharingMode = VK_SHARING_MODE_EXCLUSIVE, + }; + VkPhysicalDeviceExternalImageFormatInfo extImgFmtInfo = { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO, + .pNext = &modInfo, + .handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT, + }; + VkPhysicalDeviceImageFormatInfo2 imgFmtInfo = { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2, + .pNext = &extImgFmtInfo, + .type = VK_IMAGE_TYPE_2D, + .format = format, + .usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, + .tiling = VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT, + }; - if (p->current_buffer_id == p->pending_buffer_id || - p->pending_buffer_id == SPA_ID_INVALID) - continue; + VkExternalImageFormatProperties extImgFmtProps = { + .sType = VK_STRUCTURE_TYPE_EXTERNAL_IMAGE_FORMAT_PROPERTIES, + }; + VkImageFormatProperties2 imgFmtProps = { + .sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2, + .pNext = &extImgFmtProps, + }; - p->current_buffer_id = p->pending_buffer_id; - p->busy_buffer_id = p->current_buffer_id; - p->pending_buffer_id = SPA_ID_INVALID; + VK_CHECK_RESULT_LOOP(vkGetPhysicalDeviceImageFormatProperties2(s->physicalDevice, &imgFmtInfo, &imgFmtProps)) - descriptorImageInfoi = (VkDescriptorImageInfo) { - .sampler = s->sampler, - .imageView = p->buffersp->current_buffer_id.view, - .imageLayout = VK_IMAGE_LAYOUT_GENERAL, - }; - writeDescriptorSeti = (VkWriteDescriptorSet) { - .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, - .dstSet = s->descriptorSet, - .dstBinding = i, - .descriptorCount = 1, - .descriptorType = i == 0 ? - VK_DESCRIPTOR_TYPE_STORAGE_IMAGE : - VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, - .pImageInfo = &descriptorImageInfoi, - }; - } - vkUpdateDescriptorSets(s->device, s->n_streams, - writeDescriptorSet, 0, NULL); + VkExternalMemoryFeatureFlags extMemFeatures = + extImgFmtProps.externalMemoryProperties.externalMemoryFeatures; + if (!(extMemFeatures & VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT)) { + continue; + } + + VkExtent3D max_extent = imgFmtProps.imageFormatProperties.maxExtent; + f_info->infosf_info->modifierCount++ = (struct vulkan_modifier_info){ + .props = props, + .max_extent = { .width = max_extent.width, .height = max_extent.height }, + }; + } + free(modPropsList.pDrmFormatModifierProperties); + } return 0; } -static VkShaderModule createShaderModule(struct vulkan_state *s, const char* shaderFile) +static void destroyFormatInfo(struct vulkan_base *s) { - VkShaderModule shaderModule = VK_NULL_HANDLE; - VkResult result; - void *data; - int fd; - struct stat stat; - - if ((fd = open(shaderFile, 0, O_RDONLY)) == -1) { - spa_log_error(s->log, "can't open %s: %m", shaderFile); - return VK_NULL_HANDLE; + for (uint32_t i = 0; i < s->formatInfoCount; i++) { + free(s->formatInfosi.infos); } - if (fstat(fd, &stat) < 0) { - spa_log_error(s->log, "can't stat %s: %m", shaderFile); - close(fd); - return VK_NULL_HANDLE; + free(s->formatInfos); + s->formatInfos = NULL; + s->formatInfoCount = 0; +} + +int vulkan_read_pixels(struct vulkan_base *s, struct vulkan_read_pixels_info *info, struct vulkan_buffer *vk_buf) +{ + VkImageSubresource img_sub_res = { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .arrayLayer = 0, + .mipLevel = 0, + }; + VkSubresourceLayout img_sub_layout; + vkGetImageSubresourceLayout(s->device, vk_buf->image, &img_sub_res, &img_sub_layout); + + void *v; + VK_CHECK_RESULT(vkMapMemory(s->device, vk_buf->memory, 0, VK_WHOLE_SIZE, 0, &v)); + + const char *d = (const char *)v + img_sub_layout.offset; + unsigned char *p = (unsigned char *)info->data + info->offset; + uint32_t bytes_per_pixel = 16; + uint32_t pack_stride = img_sub_layout.rowPitch; + if (pack_stride == info->stride) { + memcpy(p, d, info->stride * info->size.height); + } else { + for (uint32_t i = 0; i < info->size.height; i++) { + memcpy(p + i * info->stride, d + i * pack_stride, info->size.width * bytes_per_pixel); + } } + vkUnmapMemory(s->device, vk_buf->memory); + return 0; +} - data = mmap(NULL, stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0); +int vulkan_sync_foreign_dmabuf(struct vulkan_base *s, struct vulkan_buffer *vk_buf) +{ + VULKAN_INSTANCE_FUNCTION(vkImportSemaphoreFdKHR); - const VkShaderModuleCreateInfo shaderModuleCreateInfo = { - .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, - .codeSize = stat.st_size, - .pCode = data, - }; - result = vkCreateShaderModule(s->device, - &shaderModuleCreateInfo, 0, &shaderModule); + if (!s->implicit_sync_interop) { + struct pollfd pollfd = { + .fd = vk_buf->fd, + .events = POLLIN, + }; + int timeout_ms = 1000; + int ret = poll(&pollfd, 1, timeout_ms); + if (ret < 0) { + spa_log_error(s->log, "Failed to wait for DMA-BUF fence"); + return -1; + } else if (ret == 0) { + spa_log_error(s->log, "Timed out waiting for DMA-BUF fence"); + return -1; + } + return 0; + } - munmap(data, stat.st_size); - close(fd); + int sync_file_fd = dmabuf_export_sync_file(s->log, vk_buf->fd, DMA_BUF_SYNC_READ); + if (sync_file_fd < 0) { + spa_log_error(s->log, "Failed to extract for DMA-BUF fence"); + return -1; + } - if (result != VK_SUCCESS) { - spa_log_error(s->log, "can't create shader %s: %m", shaderFile); - return VK_NULL_HANDLE; + if (vk_buf->foreign_semaphore == VK_NULL_HANDLE) { + VkSemaphoreCreateInfo semaphore_info = { + .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, + }; + VK_CHECK_RESULT_WITH_CLEANUP(vkCreateSemaphore(s->device, &semaphore_info, NULL, &vk_buf->foreign_semaphore), close(sync_file_fd)); } - return shaderModule; -} -static int createComputePipeline(struct vulkan_state *s, const char *shader_file) -{ - static const VkPushConstantRange range = { - .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT, - .offset = 0, - .size = sizeof(struct push_constants) + VkImportSemaphoreFdInfoKHR import_info = { + .sType = VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHR, + .handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT, + .flags = VK_SEMAPHORE_IMPORT_TEMPORARY_BIT_KHR, + .semaphore = vk_buf->foreign_semaphore, + .fd = sync_file_fd, }; + VK_CHECK_RESULT_WITH_CLEANUP(vkImportSemaphoreFdKHR(s->device, &import_info), close(sync_file_fd)); - const VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = { - .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, - .setLayoutCount = 1, - .pSetLayouts = &s->descriptorSetLayout, - .pushConstantRangeCount = 1, - .pPushConstantRanges = &range, - }; - VK_CHECK_RESULT(vkCreatePipelineLayout(s->device, - &pipelineLayoutCreateInfo, NULL, - &s->pipelineLayout)); - - s->computeShaderModule = createShaderModule(s, shader_file); - if (s->computeShaderModule == VK_NULL_HANDLE) - return -ENOENT; - - const VkPipelineShaderStageCreateInfo shaderStageCreateInfo = { - .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, - .stage = VK_SHADER_STAGE_COMPUTE_BIT, - .module = s->computeShaderModule, - .pName = "main", - }; - const VkComputePipelineCreateInfo pipelineCreateInfo = { - .sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO, - .stage = shaderStageCreateInfo, - .layout = s->pipelineLayout, - }; - VK_CHECK_RESULT(vkCreateComputePipelines(s->device, VK_NULL_HANDLE, - 1, &pipelineCreateInfo, NULL, - &s->pipeline)); return 0; } -static int createCommandBuffer(struct vulkan_state *s) +bool vulkan_sync_export_dmabuf(struct vulkan_base *s, struct vulkan_buffer *vk_buf, int sync_file_fd) +{ + if (!s->implicit_sync_interop) + return false; + + return dmabuf_import_sync_file(s->log, vk_buf->fd, DMA_BUF_SYNC_WRITE, sync_file_fd); +} + +int vulkan_commandPool_create(struct vulkan_base *s, VkCommandPool *commandPool) { const VkCommandPoolCreateInfo commandPoolCreateInfo = { .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, @@ -452,217 +429,396 @@ }; VK_CHECK_RESULT(vkCreateCommandPool(s->device, &commandPoolCreateInfo, NULL, - &s->commandPool)); + commandPool)); + return 0; +} + +int vulkan_commandBuffer_create(struct vulkan_base *s, VkCommandPool commandPool, VkCommandBuffer *commandBuffer) +{ const VkCommandBufferAllocateInfo commandBufferAllocateInfo = { .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, - .commandPool = s->commandPool, + .commandPool = commandPool, .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, .commandBufferCount = 1, }; VK_CHECK_RESULT(vkAllocateCommandBuffers(s->device, &commandBufferAllocateInfo, - &s->commandBuffer)); + commandBuffer)); return 0; } -static int runCommandBuffer(struct vulkan_state *s) +uint32_t vulkan_memoryType_find(struct vulkan_base *s, + uint32_t memoryTypeBits, VkMemoryPropertyFlags properties) { - static const VkCommandBufferBeginInfo beginInfo = { - .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, - .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, - }; - VK_CHECK_RESULT(vkBeginCommandBuffer(s->commandBuffer, &beginInfo)); - - VkImageMemoryBarrier barriers->n_streams; uint32_t i; + VkPhysicalDeviceMemoryProperties memoryProperties; - for (i = 0; i < s->n_streams; i++) { - struct vulkan_stream *p = &s->streamsi; - - barrieri= (VkImageMemoryBarrier) { - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .subresourceRange.levelCount = 1, - .subresourceRange.layerCount = 1, - .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED, - .newLayout = VK_IMAGE_LAYOUT_GENERAL, - .srcAccessMask = 0, - .dstAccessMask = 0, - .image = p->buffersp->current_buffer_id.image, - }; + vkGetPhysicalDeviceMemoryProperties(s->physicalDevice, &memoryProperties); + + for (i = 0; i < memoryProperties.memoryTypeCount; i++) { + if ((memoryTypeBits & (1 << i)) && + ((memoryProperties.memoryTypesi.propertyFlags & properties) == properties)) + return i; } + return -1; +} - vkCmdPipelineBarrier(s->commandBuffer, - VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, - VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, - 0, 0, NULL, 0, NULL, - s->n_streams, barrier); +struct vulkan_format_info *vulkan_formatInfo_find(struct vulkan_base *s, VkFormat format) +{ + for (uint32_t i = 0; i < s->formatInfoCount; i++) { + if (s->formatInfosi.vk_format == format) + return &s->formatInfosi; + } + return NULL; +} - vkCmdBindPipeline(s->commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, s->pipeline); - vkCmdPushConstants (s->commandBuffer, - s->pipelineLayout, VK_SHADER_STAGE_COMPUTE_BIT, - 0, sizeof(struct push_constants), (const void *) &s->constants); - vkCmdBindDescriptorSets(s->commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, - s->pipelineLayout, 0, 1, &s->descriptorSet, 0, NULL); +struct vulkan_modifier_info *vulkan_modifierInfo_find(struct vulkan_base *s, VkFormat format, uint64_t mod) +{ + struct vulkan_format_info *f_info = vulkan_formatInfo_find(s, format); + if (!f_info) + return NULL; + for (uint32_t i = 0; i < f_info->modifierCount; i++) { + if (f_info->infosi.props.drmFormatModifier == mod) + return &f_info->infosi; + } + return NULL; +} + +void vulkan_buffer_clear(struct vulkan_base *s, struct vulkan_buffer *buffer) +{ + if (buffer->fd != -1) + close(buffer->fd); + vkFreeMemory(s->device, buffer->memory, NULL); + vkDestroyImage(s->device, buffer->image, NULL); + vkDestroyImageView(s->device, buffer->view, NULL); +} + +static VkImageAspectFlagBits mem_plane_aspect(uint32_t i) +{ + switch (i) { + case 0: return VK_IMAGE_ASPECT_MEMORY_PLANE_0_BIT_EXT; + case 1: return VK_IMAGE_ASPECT_MEMORY_PLANE_1_BIT_EXT; + case 2: return VK_IMAGE_ASPECT_MEMORY_PLANE_2_BIT_EXT; + case 3: return VK_IMAGE_ASPECT_MEMORY_PLANE_3_BIT_EXT; + default: abort(); // unreachable + } +} - vkCmdDispatch(s->commandBuffer, - (uint32_t)ceil(s->constants.width / (float)WORKGROUP_SIZE), - (uint32_t)ceil(s->constants.height / (float)WORKGROUP_SIZE), 1); +static int allocate_dmabuf(struct vulkan_base *s, VkFormat format, uint32_t modifierCount, uint64_t *modifiers, VkImageUsageFlags usage, struct spa_rectangle *size, struct vulkan_buffer *vk_buf) +{ + VkImageDrmFormatModifierListCreateInfoEXT imageDrmFormatModifierListCreateInfo = { + .sType = VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_LIST_CREATE_INFO_EXT, + .drmFormatModifierCount = modifierCount, + .pDrmFormatModifiers = modifiers, + }; - VK_CHECK_RESULT(vkEndCommandBuffer(s->commandBuffer)); + VkExternalMemoryImageCreateInfo extMemoryImageCreateInfo = { + .sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO, + .handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT, + }; + extMemoryImageCreateInfo.pNext = &imageDrmFormatModifierListCreateInfo; + + VkImageCreateInfo imageCreateInfo = { + .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, + .imageType = VK_IMAGE_TYPE_2D, + .format = format, + .extent.width = size->width, + .extent.height = size->height, + .extent.depth = 1, + .mipLevels = 1, + .arrayLayers = 1, + .samples = VK_SAMPLE_COUNT_1_BIT, + .tiling = VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT, + .usage = usage, + .sharingMode = VK_SHARING_MODE_EXCLUSIVE, + .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, + }; + imageCreateInfo.pNext = &extMemoryImageCreateInfo; - VK_CHECK_RESULT(vkResetFences(s->device, 1, &s->fence)); + VK_CHECK_RESULT(vkCreateImage(s->device, + &imageCreateInfo, NULL, &vk_buf->image)); - const VkSubmitInfo submitInfo = { - .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, - .commandBufferCount = 1, - .pCommandBuffers = &s->commandBuffer, + VkMemoryRequirements memoryRequirements = {0}; + vkGetImageMemoryRequirements(s->device, + vk_buf->image, &memoryRequirements); + + VkExportMemoryAllocateInfo exportAllocateInfo = { + .sType = VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO, + .handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT, }; - VK_CHECK_RESULT(vkQueueSubmit(s->queue, 1, &submitInfo, s->fence)); - s->started = true; + VkMemoryAllocateInfo allocateInfo = { + .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, + .allocationSize = memoryRequirements.size, + .memoryTypeIndex = vulkan_memoryType_find(s, + memoryRequirements.memoryTypeBits, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT), + }; + allocateInfo.pNext = &exportAllocateInfo; + + VK_CHECK_RESULT(vkAllocateMemory(s->device, + &allocateInfo, NULL, &vk_buf->memory)); + + VK_CHECK_RESULT(vkBindImageMemory(s->device, + vk_buf->image, vk_buf->memory, 0)); return 0; } -static void clear_buffers(struct vulkan_state *s, struct vulkan_stream *p) +int vulkan_fixate_modifier(struct vulkan_base *s, struct dmabuf_fixation_info *info, uint64_t *modifier) { - uint32_t i; + VULKAN_INSTANCE_FUNCTION(vkGetImageDrmFormatModifierPropertiesEXT); - for (i = 0; i < p->n_buffers; i++) { - if (p->buffersi.fd != -1) - close(p->buffersi.fd); - vkFreeMemory(s->device, p->buffersi.memory, NULL); - vkDestroyImage(s->device, p->buffersi.image, NULL); - vkDestroyImageView(s->device, p->buffersi.view, NULL); - } - p->n_buffers = 0; -} + struct vulkan_buffer vk_buf; + vk_buf.fd = -1; + vk_buf.view = VK_NULL_HANDLE; + VK_CHECK_RESULT(allocate_dmabuf(s, info->format, info->modifierCount, info->modifiers, info->usage, &info->size, &vk_buf)); -static void clear_streams(struct vulkan_state *s) -{ - uint32_t i; - for (i = 0; i < s->n_streams; i++) { - struct vulkan_stream *p = &s->streamsi; - clear_buffers(s, p); - } + VkImageDrmFormatModifierPropertiesEXT mod_prop = { + .sType = VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_PROPERTIES_EXT, + }; + VK_CHECK_RESULT(vkGetImageDrmFormatModifierPropertiesEXT(s->device, vk_buf.image, &mod_prop)); + + *modifier = mod_prop.drmFormatModifier; + + vulkan_buffer_clear(s, &vk_buf); + + return 0; } -int spa_vulkan_use_buffers(struct vulkan_state *s, struct vulkan_stream *p, uint32_t flags, - uint32_t n_buffers, struct spa_buffer **buffers) +int vulkan_create_dmabuf(struct vulkan_base *s, struct external_buffer_info *info, struct vulkan_buffer *vk_buf) { - uint32_t i; VULKAN_INSTANCE_FUNCTION(vkGetMemoryFdKHR); - clear_buffers(s, p); - - for (i = 0; i < n_buffers; i++) { - VkExternalMemoryImageCreateInfo extInfo; - VkImageCreateInfo imageCreateInfo = { - .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, - .imageType = VK_IMAGE_TYPE_2D, - .format = VK_FORMAT_R32G32B32A32_SFLOAT, - .extent.width = s->constants.width, - .extent.height = s->constants.height, - .extent.depth = 1, - .mipLevels = 1, - .arrayLayers = 1, - .samples = VK_SAMPLE_COUNT_1_BIT, - .tiling = VK_IMAGE_TILING_LINEAR, - .usage = p->direction == SPA_DIRECTION_OUTPUT ? - VK_IMAGE_USAGE_STORAGE_BIT: - VK_IMAGE_USAGE_SAMPLED_BIT, - .sharingMode = VK_SHARING_MODE_EXCLUSIVE, - .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, - }; + if (info->spa_buf->n_datas != 1) + return -1; - if (!(flags & SPA_NODE_BUFFERS_FLAG_ALLOC)) { - extInfo = (VkExternalMemoryImageCreateInfo) { - .sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO, - .handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT, - }; - imageCreateInfo.pNext = &extInfo; - } + VK_CHECK_RESULT(allocate_dmabuf(s, info->format, 1, &info->modifier, info->usage, &info->size, vk_buf)); - VK_CHECK_RESULT(vkCreateImage(s->device, - &imageCreateInfo, NULL, &p->buffersi.image)); + const VkMemoryGetFdInfoKHR getFdInfo = { + .sType = VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR, + .memory = vk_buf->memory, + .handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT + }; + int fd = -1; + VK_CHECK_RESULT(vkGetMemoryFdKHR(s->device, &getFdInfo, &fd)); - VkMemoryRequirements memoryRequirements; - vkGetImageMemoryRequirements(s->device, - p->buffersi.image, &memoryRequirements); + const struct vulkan_modifier_info *modInfo = vulkan_modifierInfo_find(s, info->format, info->modifier); - VkMemoryAllocateInfo allocateInfo = { - .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, - .allocationSize = memoryRequirements.size, - .memoryTypeIndex = findMemoryType(s, - memoryRequirements.memoryTypeBits, - VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT), - }; + if (info->spa_buf->n_datas != modInfo->props.drmFormatModifierPlaneCount) + return -1; - if (flags & SPA_NODE_BUFFERS_FLAG_ALLOC) { - VK_CHECK_RESULT(vkAllocateMemory(s->device, - &allocateInfo, NULL, &p->buffersi.memory)); + VkMemoryRequirements memoryRequirements = {0}; + vkGetImageMemoryRequirements(s->device, + vk_buf->image, &memoryRequirements); - const VkMemoryGetFdInfoKHR getFdInfo = { - .sType = VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR, - .memory = p->buffersi.memory, - .handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT - }; - int fd; - - VK_CHECK_RESULT(vkGetMemoryFdKHR(s->device, &getFdInfo, &fd)); - - spa_log_info(s->log, "export DMABUF %zd", memoryRequirements.size); - -// buffersi->datas0.type = SPA_DATA_DmaBuf; - buffersi->datas0.type = SPA_DATA_MemFd; - buffersi->datas0.fd = fd; - buffersi->datas0.flags = SPA_DATA_FLAG_READABLE; - buffersi->datas0.mapoffset = 0; - buffersi->datas0.maxsize = memoryRequirements.size; - p->buffersi.fd = fd; - } else { - VkImportMemoryFdInfoKHR importInfo = { - .sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR, - .handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT, - .fd = fcntl(buffersi->datas0.fd, F_DUPFD_CLOEXEC, 0), - }; - allocateInfo.pNext = &importInfo; - p->buffersi.fd = -1; - spa_log_info(s->log, "import DMABUF"); + spa_log_info(s->log, "export DMABUF %zd", memoryRequirements.size); - VK_CHECK_RESULT(vkAllocateMemory(s->device, - &allocateInfo, NULL, &p->buffersi.memory)); - } - VK_CHECK_RESULT(vkBindImageMemory(s->device, - p->buffersi.image, p->buffersi.memory, 0)); - - VkImageViewCreateInfo viewInfo = { - .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, - .image = p->buffersi.image, - .viewType = VK_IMAGE_VIEW_TYPE_2D, - .format = VK_FORMAT_R32G32B32A32_SFLOAT, - .components.r = VK_COMPONENT_SWIZZLE_R, - .components.g = VK_COMPONENT_SWIZZLE_G, - .components.b = VK_COMPONENT_SWIZZLE_B, - .components.a = VK_COMPONENT_SWIZZLE_A, - .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .subresourceRange.levelCount = 1, - .subresourceRange.layerCount = 1, + for (uint32_t i = 0; i < info->spa_buf->n_datas; i++) { + VkImageSubresource subresource = { + .aspectMask = mem_plane_aspect(i), }; + VkSubresourceLayout subresLayout = {0}; + vkGetImageSubresourceLayout(s->device, vk_buf->image, &subresource, &subresLayout); + + info->spa_buf->datasi.type = SPA_DATA_DmaBuf; + info->spa_buf->datasi.fd = fd; + info->spa_buf->datasi.flags = SPA_DATA_FLAG_READABLE; + info->spa_buf->datasi.mapoffset = 0; + info->spa_buf->datasi.chunk->offset = subresLayout.offset; + info->spa_buf->datasi.chunk->stride = subresLayout.rowPitch; + info->spa_buf->datasi.chunk->size = subresLayout.size; + info->spa_buf->datasi.maxsize = memoryRequirements.size; + } + vk_buf->fd = fd; + + VkImageViewCreateInfo viewInfo = { + .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, + .image = vk_buf->image, + .viewType = VK_IMAGE_VIEW_TYPE_2D, + .format = info->format, + .components.r = VK_COMPONENT_SWIZZLE_R, + .components.g = VK_COMPONENT_SWIZZLE_G, + .components.b = VK_COMPONENT_SWIZZLE_B, + .components.a = VK_COMPONENT_SWIZZLE_A, + .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .subresourceRange.levelCount = 1, + .subresourceRange.layerCount = 1, + }; + + VK_CHECK_RESULT(vkCreateImageView(s->device, + &viewInfo, NULL, &vk_buf->view)); + return 0; +} - VK_CHECK_RESULT(vkCreateImageView(s->device, - &viewInfo, NULL, &p->buffersi.view)); +int vulkan_import_dmabuf(struct vulkan_base *s, struct external_buffer_info *info, struct vulkan_buffer *vk_buf) +{ + + if (info->spa_buf->n_datas == 0 || info->spa_buf->n_datas > DMABUF_MAX_PLANES) + return -1; + + struct vulkan_modifier_info *modProps = vulkan_modifierInfo_find(s, info->format, info->modifier); + if (!modProps) + return -1; + + uint32_t planeCount = info->spa_buf->n_datas; + + if (planeCount != modProps->props.drmFormatModifierPlaneCount) + return -1; + + if (info->size.width > modProps->max_extent.width || info->size.height > modProps->max_extent.height) + return -1; + + VkSubresourceLayout planeLayoutsDMABUF_MAX_PLANES = {0}; + for (uint32_t i = 0; i < planeCount; i++) { + planeLayoutsi.offset = info->spa_buf->datasi.chunk->offset; + planeLayoutsi.rowPitch = info->spa_buf->datasi.chunk->stride; + planeLayoutsi.size = 0; } - p->n_buffers = n_buffers; + VkImageDrmFormatModifierExplicitCreateInfoEXT modInfo = { + .sType = VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_EXPLICIT_CREATE_INFO_EXT, + .drmFormatModifierPlaneCount = planeCount, + .drmFormatModifier = info->modifier, + .pPlaneLayouts = planeLayouts, + }; + + VkExternalMemoryImageCreateInfo extInfo = { + .sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO, + .handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT, + .pNext = &modInfo, + }; + + VkImageCreateInfo imageCreateInfo = { + .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, + .imageType = VK_IMAGE_TYPE_2D, + .format = info->format, + .extent.width = info->size.width, + .extent.height = info->size.height, + .extent.depth = 1, + .mipLevels = 1, + .arrayLayers = 1, + .samples = VK_SAMPLE_COUNT_1_BIT, + .tiling = VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT, + .usage = info->usage, + .sharingMode = VK_SHARING_MODE_EXCLUSIVE, + .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, + .pNext = &extInfo, + }; + + VK_CHECK_RESULT(vkCreateImage(s->device, + &imageCreateInfo, NULL, &vk_buf->image)); + + VkMemoryRequirements memoryRequirements; + vkGetImageMemoryRequirements(s->device, + vk_buf->image, &memoryRequirements); + + vk_buf->fd = fcntl(info->spa_buf->datas0.fd, F_DUPFD_CLOEXEC, 0); + VkImportMemoryFdInfoKHR importInfo = { + .sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR, + .handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT, + .fd = fcntl(info->spa_buf->datas0.fd, F_DUPFD_CLOEXEC, 0), + }; + + VkMemoryAllocateInfo allocateInfo = { + .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, + .allocationSize = memoryRequirements.size, + .memoryTypeIndex = vulkan_memoryType_find(s, + memoryRequirements.memoryTypeBits, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT), + }; + allocateInfo.pNext = &importInfo; + + spa_log_info(s->log, "import DMABUF"); + + VK_CHECK_RESULT(vkAllocateMemory(s->device, + &allocateInfo, NULL, &vk_buf->memory)); + VK_CHECK_RESULT(vkBindImageMemory(s->device, + vk_buf->image, vk_buf->memory, 0)); + + VkImageViewCreateInfo viewInfo = { + .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, + .image = vk_buf->image, + .viewType = VK_IMAGE_VIEW_TYPE_2D, + .format = info->format, + .components.r = VK_COMPONENT_SWIZZLE_R, + .components.g = VK_COMPONENT_SWIZZLE_G, + .components.b = VK_COMPONENT_SWIZZLE_B, + .components.a = VK_COMPONENT_SWIZZLE_A, + .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .subresourceRange.levelCount = 1, + .subresourceRange.layerCount = 1, + }; + + VK_CHECK_RESULT(vkCreateImageView(s->device, + &viewInfo, NULL, &vk_buf->view)); + return 0; +} + +int vulkan_import_memptr(struct vulkan_base *s, struct external_buffer_info *info, struct vulkan_buffer *vk_buf) +{ + VkImageCreateInfo imageCreateInfo = { + .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, + .imageType = VK_IMAGE_TYPE_2D, + .format = info->format, + .extent.width = info->size.width, + .extent.height = info->size.height, + .extent.depth = 1, + .mipLevels = 1, + .arrayLayers = 1, + .samples = VK_SAMPLE_COUNT_1_BIT, + .tiling = VK_IMAGE_TILING_LINEAR, + .usage = info->usage, + .sharingMode = VK_SHARING_MODE_EXCLUSIVE, + .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, + }; + + VK_CHECK_RESULT(vkCreateImage(s->device, + &imageCreateInfo, NULL, &vk_buf->image)); + + VkMemoryRequirements memoryRequirements; + vkGetImageMemoryRequirements(s->device, + vk_buf->image, &memoryRequirements); + + VkMemoryAllocateInfo allocateInfo = { + .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, + .allocationSize = memoryRequirements.size, + .memoryTypeIndex = vulkan_memoryType_find(s, + memoryRequirements.memoryTypeBits, + VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT), + }; + + vk_buf->fd = -1; + spa_log_info(s->log, "import MemPtr"); + + VK_CHECK_RESULT(vkAllocateMemory(s->device, + &allocateInfo, NULL, &vk_buf->memory)); + VK_CHECK_RESULT(vkBindImageMemory(s->device, + vk_buf->image, vk_buf->memory, 0)); + + VkImageViewCreateInfo viewInfo = { + .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, + .image = vk_buf->image, + .viewType = VK_IMAGE_VIEW_TYPE_2D, + .format = info->format, + .components.r = VK_COMPONENT_SWIZZLE_R, + .components.g = VK_COMPONENT_SWIZZLE_G, + .components.b = VK_COMPONENT_SWIZZLE_B, + .components.a = VK_COMPONENT_SWIZZLE_A, + .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .subresourceRange.levelCount = 1, + .subresourceRange.layerCount = 1, + }; + + VK_CHECK_RESULT(vkCreateImageView(s->device, + &viewInfo, NULL, &vk_buf->view)); return 0; } -int spa_vulkan_init_stream(struct vulkan_state *s, struct vulkan_stream *stream, - enum spa_direction direction, struct spa_dict *props) +int vulkan_stream_init(struct vulkan_stream *stream, enum spa_direction direction, + struct spa_dict *props) { spa_zero(*stream); stream->direction = direction; @@ -672,87 +828,62 @@ return 0; } -int spa_vulkan_prepare(struct vulkan_state *s) +uint32_t vulkan_vkformat_to_id(VkFormat format) { - if (!s->prepared) { - CHECK(createInstance(s)); - CHECK(findPhysicalDevice(s)); - CHECK(createDevice(s)); - CHECK(createDescriptors(s)); - CHECK(createComputePipeline(s, s->shaderName)); - CHECK(createCommandBuffer(s)); - s->prepared = true; + SPA_FOR_EACH_ELEMENT_VAR(vk_video_format_convs, f) { + if (f->format == format) + return f->id; } - return 0; + return SPA_VIDEO_FORMAT_UNKNOWN; } -int spa_vulkan_unprepare(struct vulkan_state *s) +VkFormat vulkan_id_to_vkformat(uint32_t id) { - if (s->prepared) { - vkDestroyShaderModule(s->device, s->computeShaderModule, NULL); - vkDestroySampler(s->device, s->sampler, NULL); - vkDestroyDescriptorPool(s->device, s->descriptorPool, NULL); - vkDestroyDescriptorSetLayout(s->device, s->descriptorSetLayout, NULL); - vkDestroyPipelineLayout(s->device, s->pipelineLayout, NULL); - vkDestroyPipeline(s->device, s->pipeline, NULL); - vkDestroyCommandPool(s->device, s->commandPool, NULL); - vkDestroyFence(s->device, s->fence, NULL); - vkDestroyDevice(s->device, NULL); - vkDestroyInstance(s->instance, NULL); - s->prepared = false; + SPA_FOR_EACH_ELEMENT_VAR(vk_video_format_convs, f) { + if (f->id == id) + return f->format; } - return 0; + return VK_FORMAT_UNDEFINED; } -int spa_vulkan_start(struct vulkan_state *s) +int vulkan_vkresult_to_errno(VkResult result) { - uint32_t i; + return vkresult_to_errno(result); +} + +int vulkan_wait_fence(struct vulkan_base *s, VkFence fence) +{ + VK_CHECK_RESULT(vkWaitForFences(s->device, 1, &fence, VK_TRUE, UINT64_MAX)); - for (i = 0; i < s->n_streams; i++) { - struct vulkan_stream *p = &s->streamsi; - p->current_buffer_id = SPA_ID_INVALID; - p->busy_buffer_id = SPA_ID_INVALID; - p->ready_buffer_id = SPA_ID_INVALID; - } return 0; } -int spa_vulkan_stop(struct vulkan_state *s) +int vulkan_wait_idle(struct vulkan_base *s) { VK_CHECK_RESULT(vkDeviceWaitIdle(s->device)); - clear_streams(s); - s->started = false; + return 0; } -int spa_vulkan_ready(struct vulkan_state *s) +int vulkan_base_init(struct vulkan_base *s, struct vulkan_base_info *info) { - uint32_t i; - VkResult result; - - if (!s->started) - return 0; - - result = vkGetFenceStatus(s->device, s->fence); - if (result == VK_NOT_READY) - return -EBUSY; - VK_CHECK_RESULT(result); - - s->started = false; - - for (i = 0; i < s->n_streams; i++) { - struct vulkan_stream *p = &s->streamsi; - p->ready_buffer_id = p->busy_buffer_id; - p->busy_buffer_id = SPA_ID_INVALID; + if (!s->initialized) { + CHECK(createInstance(s)); + CHECK(findPhysicalDevice(s)); + CHECK(createDevice(s, info)); + CHECK(queryFormatInfo(s, info)); + s->implicit_sync_interop = dmabuf_check_sync_file_import_export(s->log); + s->initialized = true; } return 0; } -int spa_vulkan_process(struct vulkan_state *s) +void vulkan_base_deinit(struct vulkan_base *s) { - CHECK(updateDescriptors(s)); - CHECK(runCommandBuffer(s)); - VK_CHECK_RESULT(vkDeviceWaitIdle(s->device)); - - return 0; + if (s->initialized) { + destroyFormatInfo(s); + vkDestroyDevice(s->device, NULL); + vkDestroyInstance(s->instance, NULL); + s->initialized = false; + } }
View file
pipewire-0.3.79.tar.gz/spa/plugins/vulkan/vulkan-utils.h -> pipewire-0.3.80.tar.gz/spa/plugins/vulkan/vulkan-utils.h
Changed
@@ -1,86 +1,105 @@ +/* Spa */ +/* SPDX-FileCopyrightText: Copyright © 2019 Wim Taymans */ +/* SPDX-License-Identifier: MIT */ + +#pragma once + #include <vulkan/vulkan.h> #include <spa/buffer/buffer.h> #include <spa/node/node.h> -#define MAX_STREAMS 2 -#define MAX_BUFFERS 16 -#define WORKGROUP_SIZE 32 - -struct pixel { - float r, g, b, a; +#include "vulkan-types.h" + +#define VK_CHECK_RESULT(f) \ +{ \ + VkResult _result = (f); \ + int _r = -vulkan_vkresult_to_errno(_result); \ + if (_result != VK_SUCCESS) { \ + spa_log_error(s->log, "error: %d (%d %s)", _result, _r, spa_strerror(_r)); \ + return _r; \ + } \ +} +#define VK_CHECK_RESULT_WITH_CLEANUP(f, c) \ +{ \ + VkResult _result = (f); \ + int _r = -vkresult_to_errno(_result); \ + if (_result != VK_SUCCESS) { \ + spa_log_error(s->log, "error: %d (%d %s)", _result, _r, spa_strerror(_r)); \ + (c); \ + return _r; \ + } \ +} +#define VK_CHECK_RESULT_LOOP(f) \ +{ \ + VkResult _result = (f); \ + int _r = -vkresult_to_errno(_result); \ + if (_result != VK_SUCCESS) { \ + spa_log_error(s->log, "error: %d (%d %s)", _result, _r, spa_strerror(_r)); \ + continue; \ + } \ +} +#define CHECK(f) \ +{ \ + int _res = (f); \ + if (_res < 0) \ + return _res; \ +} + +struct vulkan_read_pixels_info { + struct spa_rectangle size; + void *data; + uint32_t offset; + uint32_t stride; + uint32_t bytes_per_pixel; }; -struct push_constants { - float time; - int frame; - int width; - int height; +struct dmabuf_fixation_info { + VkFormat format; + uint64_t modifierCount; + uint64_t *modifiers; + struct spa_rectangle size; + VkImageUsageFlags usage; }; -struct vulkan_buffer { - int fd; - VkImage image; - VkImageView view; - VkDeviceMemory memory; -}; - -struct vulkan_stream { - enum spa_direction direction; - - uint32_t pending_buffer_id; - uint32_t current_buffer_id; - uint32_t busy_buffer_id; - uint32_t ready_buffer_id; - - struct vulkan_buffer buffersMAX_BUFFERS; - uint32_t n_buffers; +struct external_buffer_info { + VkFormat format; + uint64_t modifier; + struct spa_rectangle size; + VkImageUsageFlags usage; + struct spa_buffer *spa_buf; }; -struct vulkan_state { - struct spa_log *log; - - struct push_constants constants; - - VkInstance instance; +int vulkan_read_pixels(struct vulkan_base *s, struct vulkan_read_pixels_info *info, struct vulkan_buffer *vk_buf); - VkPhysicalDevice physicalDevice; - VkDevice device; +int vulkan_sync_foreign_dmabuf(struct vulkan_base *s, struct vulkan_buffer *vk_buf); +bool vulkan_sync_export_dmabuf(struct vulkan_base *s, struct vulkan_buffer *vk_buf, int sync_file_fd); - VkPipeline pipeline; - VkPipelineLayout pipelineLayout; - const char *shaderName; - VkShaderModule computeShaderModule; +int vulkan_fixate_modifier(struct vulkan_base *s, struct dmabuf_fixation_info *info, uint64_t *modifier); +int vulkan_create_dmabuf(struct vulkan_base *s, struct external_buffer_info *info, struct vulkan_buffer *vk_buf); +int vulkan_import_dmabuf(struct vulkan_base *s, struct external_buffer_info *info, struct vulkan_buffer *vk_buf); +int vulkan_import_memptr(struct vulkan_base *s, struct external_buffer_info *info, struct vulkan_buffer *vk_buf); - VkCommandPool commandPool; - VkCommandBuffer commandBuffer; +int vulkan_commandPool_create(struct vulkan_base *s, VkCommandPool *commandPool); +int vulkan_commandBuffer_create(struct vulkan_base *s, VkCommandPool commandPool, VkCommandBuffer *commandBuffer); - VkQueue queue; - uint32_t queueFamilyIndex; - VkFence fence; - unsigned int prepared:1; - unsigned int started:1; +uint32_t vulkan_memoryType_find(struct vulkan_base *s, + uint32_t memoryTypeBits, VkMemoryPropertyFlags properties); +struct vulkan_format_info *vulkan_formatInfo_find(struct vulkan_base *s, VkFormat format); +struct vulkan_modifier_info *vulkan_modifierInfo_find(struct vulkan_base *s, VkFormat format, uint64_t modifier); - VkDescriptorPool descriptorPool; - VkDescriptorSetLayout descriptorSetLayout; +void vulkan_buffer_clear(struct vulkan_base *s, struct vulkan_buffer *buffer); - VkSampler sampler; +int vulkan_stream_init(struct vulkan_stream *stream, enum spa_direction direction, + struct spa_dict *props); - uint32_t n_streams; - VkDescriptorSet descriptorSet; - struct vulkan_stream streamsMAX_STREAMS; -}; +uint32_t vulkan_vkformat_to_id(VkFormat vkFormat); +VkFormat vulkan_id_to_vkformat(uint32_t id); -int spa_vulkan_init_stream(struct vulkan_state *s, struct vulkan_stream *stream, enum spa_direction, - struct spa_dict *props); +int vulkan_vkresult_to_errno(VkResult result); -int spa_vulkan_prepare(struct vulkan_state *s); -int spa_vulkan_use_buffers(struct vulkan_state *s, struct vulkan_stream *stream, uint32_t flags, - uint32_t n_buffers, struct spa_buffer **buffers); -int spa_vulkan_unprepare(struct vulkan_state *s); +int vulkan_wait_fence(struct vulkan_base *s, VkFence fence); +int vulkan_wait_idle(struct vulkan_base *s); -int spa_vulkan_start(struct vulkan_state *s); -int spa_vulkan_stop(struct vulkan_state *s); -int spa_vulkan_ready(struct vulkan_state *s); -int spa_vulkan_process(struct vulkan_state *s); -int spa_vulkan_cleanup(struct vulkan_state *s); +int vulkan_base_init(struct vulkan_base *s, struct vulkan_base_info *info); +void vulkan_base_deinit(struct vulkan_base *s);
View file
pipewire-0.3.79.tar.gz/src/daemon/meson.build -> pipewire-0.3.80.tar.gz/src/daemon/meson.build
Changed
@@ -27,6 +27,8 @@ build_wp = 'wireplumber' in get_option('session-managers') default_sm = get_option('session-managers').get(0, '') +build_vk = get_option('vulkan').enabled() + summary({'Build media-session': build_ms, 'Build wireplumber': build_wp, 'Default session-manager': default_sm}, @@ -75,6 +77,10 @@ 'pipewire-aes67.conf', +if build_vk + conf_files += 'pipewire-vulkan.conf' +endif + foreach c : conf_files configure_file(input : '@0@.in'.format(c), output : c, @@ -107,7 +113,17 @@ ln = find_program('ln') -foreach alias : 'pipewire-pulse', 'pipewire-avb', 'pipewire-aes67' +pipewire_aliases = + 'pipewire-pulse', + 'pipewire-avb', + 'pipewire-aes67', + + +if build_vk + pipewire_aliases += 'pipewire-vulkan' +endif + +foreach alias : pipewire_aliases custom_target( alias, build_by_default: true,
View file
pipewire-0.3.79.tar.gz/src/daemon/minimal.conf.in -> pipewire-0.3.80.tar.gz/src/daemon/minimal.conf.in
Changed
@@ -144,11 +144,11 @@ # Creates an object from a PipeWire factory with the given parameters. # If nofail is given, errors are ignored (and no object is created). # - #{ factory = spa-node-factory args = { factory.name = videotestsrc node.name = videotestsrc Spa:Pod:Object:Param:Props:patternType = 1 } } + #{ factory = spa-node-factory args = { factory.name = videotestsrc node.name = videotestsrc node.description = videotestsrc Spa:Pod:Object:Param:Props:patternType = 1 } } #{ factory = spa-device-factory args = { factory.name = api.jack.device foo=bar } flags = nofail } #{ factory = spa-device-factory args = { factory.name = api.alsa.enum.udev } } #{ factory = spa-node-factory args = { factory.name = api.alsa.seq.bridge node.name = Internal-MIDI-Bridge } } - #{ factory = adapter args = { factory.name = audiotestsrc node.name = my-test } } + #{ factory = adapter args = { factory.name = audiotestsrc node.name = my-test node.description = audiotestsrc } } #{ factory = spa-node-factory args = { factory.name = api.vulkan.compute.source node.name = my-compute-source } } # Make a default metadata store
View file
pipewire-0.3.80.tar.gz/src/daemon/pipewire-vulkan.conf.in
Added
@@ -0,0 +1,98 @@ +# Config file for PipeWire version "0.3.77" # +# +# This config file should start the vulkan-compute-source/filter as proxied +# clients +# + +context.properties = { + ## Configure properties in the system. + #library.name.system = support/libspa-support + #context.data-loop.library.name.system = support/libspa-support + #support.dbus = true + #link.max-buffers = 64 + #link.max-buffers = 16 # version < 3 clients can't handle more + #mem.warn-mlock = false + #mem.allow-mlock = true + #mem.mlock-all = false + #clock.power-of-two-quantum = true + #log.level = 4 + #cpu.zero.denormals = false + + #default.clock.quantum-limit = 8192 +} + +context.spa-libs = { + #<factory-name regex> = <library-name> + # + # Used to find spa factory names. It maps an spa factory name + # regular expression to a library name that should contain + # that factory. + # + api.vulkan.* = vulkan/libspa-vulkan + support.* = support/libspa-support +} + +context.modules = + #{ name = <module-name> + # ( args = { <key> = <value> ... } ) + # ( flags = ( ifexists ) ( nofail ) ) + # ( condition = { <key> = <value> ... } ... ) + #} + # + # Loads a module with the given parameters. + # If ifexists is given, the module is ignored when it is not found. + # If nofail is given, module initialization failures are ignored. + # If condition is given, the module is loaded only when the context + # properties all match the match rules. + # + + # Uses realtime scheduling to boost the audio thread priorities. This uses + # RTKit if the user doesn't have permission to use regular realtime + # scheduling. + { name = libpipewire-module-rt + args = { + nice.level = -11 + #rt.prio = 88 + #rt.time.soft = -1 + #rt.time.hard = -1 + } + flags = ifexists nofail + } + + # The native communication protocol. + { name = libpipewire-module-protocol-native } + + # Creates a factory for making nodes that run in the + # context of the PipeWire server. + { name = libpipewire-module-spa-node-factory } + + # Allows creating nodes that run in the context of the + # client. Is used by all clients that want to provide + # data to PipeWire. + { name = libpipewire-module-client-node } + + # Makes a factory for wrapping nodes in an adapter with a + # converter and resampler. + { name = libpipewire-module-adapter } + + +context.objects = + #{ factory = <factory-name> + # ( args = { <key> = <value> ... } ) + # ( flags = ( nofail ) ) + # ( condition = { <key> = <value> ... } ... ) + #} + # + # Creates an object from a PipeWire factory with the given parameters. + # If nofail is given, errors are ignored (and no object is created). + # If condition is given, the object is created only when the context properties + # all match the match rules. + # + #{ factory = spa-node-factory args = { factory.name = videotestsrc node.name = videotestsrc Spa:Pod:Object:Param:Props:patternType = 1 } } + #{ factory = spa-device-factory args = { factory.name = api.jack.device foo=bar } flags = nofail } + #{ factory = spa-device-factory args = { factory.name = api.alsa.enum.udev } } + #{ factory = spa-node-factory args = { factory.name = api.alsa.seq.bridge node.name = Internal-MIDI-Bridge } } + #{ factory = adapter args = { factory.name = audiotestsrc node.name = my-test } } + { factory = spa-node-factory args = { factory.name = api.vulkan.compute.source node.name = vulkan-compute-source object.export = true } } + { factory = spa-node-factory args = { factory.name = api.vulkan.compute.filter node.name = vulkan-compute-filter object.export = true } } +
View file
pipewire-0.3.79.tar.gz/src/daemon/pipewire.conf.in -> pipewire-0.3.80.tar.gz/src/daemon/pipewire.conf.in
Changed
@@ -193,11 +193,11 @@ # If condition is given, the object is created only when the context properties # all match the match rules. # - #{ factory = spa-node-factory args = { factory.name = videotestsrc node.name = videotestsrc Spa:Pod:Object:Param:Props:patternType = 1 } } + #{ factory = spa-node-factory args = { factory.name = videotestsrc node.name = videotestsrc node.description = videotestsrc Spa:Pod:Object:Param:Props:patternType = 1 } } #{ factory = spa-device-factory args = { factory.name = api.jack.device foo=bar } flags = nofail } #{ factory = spa-device-factory args = { factory.name = api.alsa.enum.udev } } #{ factory = spa-node-factory args = { factory.name = api.alsa.seq.bridge node.name = Internal-MIDI-Bridge } } - #{ factory = adapter args = { factory.name = audiotestsrc node.name = my-test } } + #{ factory = adapter args = { factory.name = audiotestsrc node.name = my-test node.description = audiotestsrc } } #{ factory = spa-node-factory args = { factory.name = api.vulkan.compute.source node.name = my-compute-source } } # A default dummy driver. This handles nodes marked with the "node.always-driver"
View file
pipewire-0.3.79.tar.gz/src/examples/video-play.c -> pipewire-0.3.80.tar.gz/src/examples/video-play.c
Changed
@@ -14,8 +14,10 @@ #include <spa/utils/result.h> #include <spa/param/video/format-utils.h> +#include <spa/param/tag-utils.h> #include <spa/param/props.h> #include <spa/debug/format.h> +#include <spa/debug/pod.h> #include <pipewire/pipewire.h> @@ -278,6 +280,10 @@ void *d; int32_t mult, size; + if (param != NULL && id == SPA_PARAM_Tag) { + spa_debug_pod(0, NULL, param); + return; + } /* NULL means to clear the format */ if (param == NULL || id != SPA_PARAM_Format) return; @@ -422,7 +428,7 @@ int main(int argc, char *argv) { struct data data = { 0, }; - const struct spa_pod *params2; + const struct spa_pod *params3; uint8_t buffer1024; struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer)); struct pw_properties *props; @@ -478,6 +484,16 @@ * object to the stack. */ n_params = build_format(&data, &b, params); + { + struct spa_pod_frame f; + struct spa_dict_item items1; + /* send a tag, input tags travel upstream */ + spa_tag_build_start(&b, &f, SPA_PARAM_Tag, SPA_DIRECTION_INPUT); + items0 = SPA_DICT_ITEM_INIT("my-tag-other-key", "my-special-other-tag-value"); + spa_tag_build_add_dict(&b, &SPA_DICT_INIT(items, 1)); + paramsn_params++ = spa_tag_build_end(&b, &f); + } + /* now connect the stream, we need a direction (input/output), * an optional target node to connect to, some flags and parameters */
View file
pipewire-0.3.79.tar.gz/src/examples/video-src.c -> pipewire-0.3.80.tar.gz/src/examples/video-src.c
Changed
@@ -14,6 +14,8 @@ #include <math.h> #include <spa/param/video/format-utils.h> +#include <spa/param/tag-utils.h> +#include <spa/debug/pod.h> #include <pipewire/pipewire.h> @@ -212,6 +214,10 @@ struct spa_pod_builder b = SPA_POD_BUILDER_INIT(params_buffer, sizeof(params_buffer)); const struct spa_pod *params5; + if (param != NULL && id == SPA_PARAM_Tag) { + spa_debug_pod(0, NULL, param); + return; + } if (param == NULL || id != SPA_PARAM_Format) return; @@ -276,7 +282,7 @@ int main(int argc, char *argv) { struct data data = { 0, }; - const struct spa_pod *params1; + const struct spa_pod *params2; uint8_t buffer1024; struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer)); @@ -314,6 +320,16 @@ &SPA_RECTANGLE(4096, 4096)), SPA_FORMAT_VIDEO_framerate, SPA_POD_Fraction(&SPA_FRACTION(25, 1))); + { + struct spa_pod_frame f; + struct spa_dict_item items1; + /* send a tag, output tags travel downstream */ + spa_tag_build_start(&b, &f, SPA_PARAM_Tag, SPA_DIRECTION_OUTPUT); + items0 = SPA_DICT_ITEM_INIT("my-tag-key", "my-special-tag-value"); + spa_tag_build_add_dict(&b, &SPA_DICT_INIT(items, 1)); + params1 = spa_tag_build_end(&b, &f); + } + pw_stream_add_listener(data.stream, &data.stream_listener, &stream_events, @@ -324,7 +340,7 @@ PW_ID_ANY, PW_STREAM_FLAG_DRIVER | PW_STREAM_FLAG_MAP_BUFFERS, - params, 1); + params, 2); pw_main_loop_run(data.loop);
View file
pipewire-0.3.79.tar.gz/src/modules/module-combine-stream.c -> pipewire-0.3.80.tar.gz/src/modules/module-combine-stream.c
Changed
@@ -697,7 +697,7 @@ update_delay(s->impl); break; case SPA_PARAM_Latency: - if (!param) { + if (param == NULL) { s->have_latency = false; } else if (spa_latency_parse(param, &latency) == 0 && latency.direction == get_combine_direction(s->impl)) {
View file
pipewire-0.3.79.tar.gz/src/modules/module-echo-cancel.c -> pipewire-0.3.80.tar.gz/src/modules/module-echo-cancel.c
Changed
@@ -604,7 +604,7 @@ struct spa_pod_builder b; const struct spa_pod *params1; - if (spa_latency_parse(param, &latency) < 0) + if (param == NULL || spa_latency_parse(param, &latency) < 0) return; spa_pod_builder_init(&b, buffer, sizeof(buffer)); @@ -767,7 +767,7 @@ struct spa_pod_builder b; const struct spa_pod *params1; - if (spa_latency_parse(param, &latency) < 0) + if (param == NULL || spa_latency_parse(param, &latency) < 0) return; spa_pod_builder_init(&b, buffer, sizeof(buffer));
View file
pipewire-0.3.79.tar.gz/src/modules/module-example-filter.c -> pipewire-0.3.80.tar.gz/src/modules/module-example-filter.c
Changed
@@ -227,7 +227,7 @@ struct spa_pod_builder b; const struct spa_pod *params1; - if (spa_latency_parse(param, &latency) < 0) + if (param == NULL || spa_latency_parse(param, &latency) < 0) return; *info = latency;
View file
pipewire-0.3.79.tar.gz/src/modules/module-ffado-driver.c -> pipewire-0.3.80.tar.gz/src/modules/module-ffado-driver.c
Changed
@@ -417,7 +417,7 @@ bool update = false; enum spa_direction direction = port->direction; - if (spa_latency_parse(param, &latency) < 0) + if (param == NULL || spa_latency_parse(param, &latency) < 0) return; if (spa_latency_info_compare(&port->latencydirection, &latency)) {
View file
pipewire-0.3.79.tar.gz/src/modules/module-filter-chain.c -> pipewire-0.3.80.tar.gz/src/modules/module-filter-chain.c
Changed
@@ -20,6 +20,7 @@ #include <spa/utils/json.h> #include <spa/support/cpu.h> #include <spa/param/latency-utils.h> +#include <spa/param/tag-utils.h> #include <spa/pod/dynamic.h> #include <spa/debug/types.h> @@ -1109,7 +1110,7 @@ struct spa_pod_builder b; const struct spa_pod *params1; - if (spa_latency_parse(param, &latency) < 0) + if (param == NULL || spa_latency_parse(param, &latency) < 0) return; spa_pod_builder_init(&b, buffer, sizeof(buffer)); @@ -1121,6 +1122,21 @@ pw_stream_update_params(impl->playback, params, 1); } +static void param_tag_changed(struct impl *impl, const struct spa_pod *param) +{ + struct spa_tag_info tag; + const struct spa_pod *params1 = { param }; + void *state = NULL; + + if (param == 0 || spa_tag_parse(param, &tag, &state) < 0) + return; + + if (tag.direction == SPA_DIRECTION_INPUT) + pw_stream_update_params(impl->capture, params, 1); + else + pw_stream_update_params(impl->playback, params, 1); +} + static void state_changed(void *data, enum pw_stream_state old, enum pw_stream_state state, const char *error) { @@ -1208,6 +1224,9 @@ case SPA_PARAM_Latency: param_latency_changed(impl, param); break; + case SPA_PARAM_Tag: + param_tag_changed(impl, param); + break; } return;
View file
pipewire-0.3.79.tar.gz/src/modules/module-filter-chain/builtin_plugin.c -> pipewire-0.3.80.tar.gz/src/modules/module-filter-chain/builtin_plugin.c
Changed
@@ -921,7 +921,7 @@ pw_log_error("convolver:filename requires a string or an array"); return NULL; } else { - filenamesi = strdup(v); + filenames0 = strdup(v); } } else if (spa_streq(key, "offset")) {
View file
pipewire-0.3.79.tar.gz/src/modules/module-jack-tunnel.c -> pipewire-0.3.80.tar.gz/src/modules/module-jack-tunnel.c
Changed
@@ -410,7 +410,7 @@ bool update = false; enum spa_direction direction = port->direction; - if (spa_latency_parse(param, &latency) < 0) + if (param == NULL || spa_latency_parse(param, &latency) < 0) return; if (spa_latency_info_compare(&port->latencydirection, &latency)) {
View file
pipewire-0.3.79.tar.gz/src/modules/module-loopback.c -> pipewire-0.3.80.tar.gz/src/modules/module-loopback.c
Changed
@@ -171,13 +171,11 @@ struct pw_stream *capture; struct spa_hook capture_listener; struct spa_audio_info_raw capture_info; - struct spa_latency_info capture_latency; struct pw_properties *playback_props; struct pw_stream *playback; struct spa_hook playback_listener; struct spa_audio_info_raw playback_info; - struct spa_latency_info playback_latency; unsigned int do_disconnect:1; unsigned int recalc_delay:1; @@ -317,18 +315,16 @@ } static void param_latency_changed(struct impl *impl, const struct spa_pod *param, - struct spa_latency_info *info, struct pw_stream *other) + struct pw_stream *other) { struct spa_latency_info latency; uint8_t buffer1024; struct spa_pod_builder b; const struct spa_pod *params1; - if (spa_latency_parse(param, &latency) < 0) + if (param == NULL || spa_latency_parse(param, &latency) < 0) return; - *info = latency; - spa_pod_builder_init(&b, buffer, sizeof(buffer)); params0 = spa_latency_build(&b, SPA_PARAM_Latency, &latency); pw_stream_update_params(other, params, 1); @@ -336,6 +332,15 @@ impl->recalc_delay = true; } +static void param_tag_changed(struct impl *impl, const struct spa_pod *param, + struct pw_stream *other) +{ + const struct spa_pod *params1 = { param }; + if (param == NULL) + return; + pw_stream_update_params(other, params, 1); +} + static void recalculate_buffer(struct impl *impl) { if (impl->target_delay > 0.0f) { @@ -414,7 +419,10 @@ break; } case SPA_PARAM_Latency: - param_latency_changed(impl, param, &impl->capture_latency, impl->playback); + param_latency_changed(impl, param, impl->playback); + break; + case SPA_PARAM_Tag: + param_tag_changed(impl, param, impl->playback); break; } } @@ -453,7 +461,10 @@ switch (id) { case SPA_PARAM_Latency: - param_latency_changed(impl, param, &impl->playback_latency, impl->capture); + param_latency_changed(impl, param, impl->capture); + break; + case SPA_PARAM_Tag: + param_tag_changed(impl, param, impl->capture); break; } }
View file
pipewire-0.3.79.tar.gz/src/modules/module-netjack2-driver.c -> pipewire-0.3.80.tar.gz/src/modules/module-netjack2-driver.c
Changed
@@ -371,7 +371,7 @@ bool update = false; enum spa_direction direction = port->direction; - if (spa_latency_parse(param, &latency) < 0) + if (param == NULL || spa_latency_parse(param, &latency) < 0) return; if (spa_latency_info_compare(&port->latencydirection, &latency)) {
View file
pipewire-0.3.79.tar.gz/src/modules/module-netjack2-manager.c -> pipewire-0.3.80.tar.gz/src/modules/module-netjack2-manager.c
Changed
@@ -493,7 +493,7 @@ bool update = false; enum spa_direction direction = port->direction; - if (spa_latency_parse(param, &latency) < 0) + if (param == NULL || spa_latency_parse(param, &latency) < 0) return; if (spa_latency_info_compare(&port->latencydirection, &latency)) {
View file
pipewire-0.3.79.tar.gz/src/modules/module-pipe-tunnel.c -> pipewire-0.3.80.tar.gz/src/modules/module-pipe-tunnel.c
Changed
@@ -111,6 +111,9 @@ #define DEFAULT_CHANNELS 2 #define DEFAULT_POSITION " FL FR " +#define RINGBUFFER_SIZE (1u << 22) +#define RINGBUFFER_MASK (RINGBUFFER_SIZE-1) + PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME); #define PW_LOG_TOPIC_DEFAULT mod_topic @@ -137,6 +140,7 @@ struct impl { struct pw_context *context; + struct pw_loop *data_loop; #define MODE_PLAYBACK 0 #define MODE_CAPTURE 1 @@ -156,6 +160,8 @@ char *filename; unsigned int unlink_fifo; int fd; + struct spa_source *socket; + struct spa_source *timer; struct pw_properties *stream_props; enum pw_direction direction; @@ -165,10 +171,78 @@ uint32_t frame_size; unsigned int do_disconnect:1; - uint32_t leftover_count; - uint8_t *leftover; + unsigned int driving:1; + unsigned int have_sync:1; + + struct spa_ringbuffer ring; + void *buffer; + uint32_t target_buffer; + + struct spa_io_rate_match *rate_match; + struct spa_io_position *position; + + struct spa_dll dll; + float max_error; + float corr; + + uint64_t next_time; }; +static uint64_t get_time_ns(struct impl *impl) +{ + struct timespec now; + if (spa_system_clock_gettime(impl->data_loop->system, CLOCK_MONOTONIC, &now) < 0) + return 0; + return SPA_TIMESPEC_TO_NSEC(&now); +} + +static int set_timeout(struct impl *impl, uint64_t time) +{ + struct timespec timeout, interval; + timeout.tv_sec = time / SPA_NSEC_PER_SEC; + timeout.tv_nsec = time % SPA_NSEC_PER_SEC; + interval.tv_sec = 0; + interval.tv_nsec = 0; + pw_loop_update_timer(impl->data_loop, + impl->timer, &timeout, &interval, true); + return 0; +} + +static void on_timeout(void *d, uint64_t expirations) +{ + struct impl *impl = d; + uint64_t duration, current_time; + uint32_t rate, index; + int32_t avail; + struct spa_io_position *pos = impl->position; + + if (SPA_LIKELY(pos)) { + duration = pos->clock.target_duration; + rate = pos->clock.target_rate.denom; + } else { + duration = 1024; + rate = 48000; + } + pw_log_debug("timeout %"PRIu64, duration); + + current_time = impl->next_time; + impl->next_time += duration / impl->corr * 1e9 / rate; + avail = spa_ringbuffer_get_read_index(&impl->ring, &index); + + if (SPA_LIKELY(pos)) { + pos->clock.nsec = current_time; + pos->clock.rate = pos->clock.target_rate; + pos->clock.position += pos->clock.duration; + pos->clock.duration = pos->clock.target_duration; + pos->clock.delay = SPA_SCALE32_UP(avail, rate, impl->info.rate); + pos->clock.rate_diff = impl->corr; + pos->clock.next_nsec = impl->next_time; + } + set_timeout(impl, impl->next_time); + + pw_stream_trigger_process(impl->stream); +} + static void stream_destroy(void *d) { struct impl *impl = d; @@ -186,8 +260,20 @@ pw_impl_module_schedule_destroy(impl->module); break; case PW_STREAM_STATE_PAUSED: + if (impl->direction == PW_DIRECTION_OUTPUT) { + pw_loop_update_io(impl->data_loop, impl->socket, 0); + set_timeout(impl, 0); + } break; case PW_STREAM_STATE_STREAMING: + if (impl->direction == PW_DIRECTION_OUTPUT) { + pw_loop_update_io(impl->data_loop, impl->socket, SPA_IO_IN); + impl->driving = pw_stream_is_driving(impl->stream); + if (impl->driving) { + impl->next_time = get_time_ns(impl); + set_timeout(impl, impl->next_time); + } + } break; default: break; @@ -221,9 +307,12 @@ continue; } else if (errno == EAGAIN || errno == EWOULDBLOCK) { /* Don't continue writing */ + pw_log_debug("pipe (%s) overrun: %m", + impl->filename); break; } else { - pw_log_warn("Failed to write to pipe sink"); + pw_log_warn("Failed to write to pipe (%s): %m", + impl->filename); } } offs += written; @@ -233,53 +322,95 @@ pw_stream_queue_buffer(impl->stream, buf); } +static void update_rate(struct impl *impl, uint32_t filled) +{ + float error; + + if (impl->rate_match == NULL) + return; + + error = (float)impl->target_buffer - (float)(filled); + error = SPA_CLAMP(error, -impl->max_error, impl->max_error); + + impl->corr = spa_dll_update(&impl->dll, error); + pw_log_debug("error:%f corr:%f current:%u target:%u", + error, impl->corr, filled, impl->target_buffer); + + if (!impl->driving) { + SPA_FLAG_SET(impl->rate_match->flags, SPA_IO_RATE_MATCH_FLAG_ACTIVE); + impl->rate_match->rate = 1.0f / impl->corr; + } +} + static void capture_stream_process(void *data) { struct impl *impl = data; struct pw_buffer *buf; - struct spa_data *d; - uint32_t req; - ssize_t nread; + struct spa_data *bd; + uint32_t req, index, size; + int32_t avail; if ((buf = pw_stream_dequeue_buffer(impl->stream)) == NULL) { - pw_log_debug("out of buffers: %m"); + pw_log_warn("out of buffers: %m"); return; } - d = &buf->buffer->datas0; + bd = &buf->buffer->datas0; if ((req = buf->requested * impl->frame_size) == 0) req = 4096 * impl->frame_size; - req = SPA_MIN(req, d->maxsize); + size = SPA_MIN(req, bd->maxsize); + size = SPA_ROUND_DOWN(size, impl->frame_size); - d->chunk->offset = 0; - d->chunk->stride = impl->frame_size; - d->chunk->size = SPA_MIN(req, impl->leftover_count); - memcpy(d->data, impl->leftover, d->chunk->size); - req -= d->chunk->size; + avail = spa_ringbuffer_get_read_index(&impl->ring, &index); - nread = read(impl->fd, SPA_PTROFF(d->data, d->chunk->size, void), req); - if (nread < 0) { - const bool important = !(errno == EINTR - || errno == EAGAIN - || errno == EWOULDBLOCK); + pw_log_debug("avail %d %u %u", avail, index, size); - if (important) - pw_log_warn("failed to read from pipe (%s): %s", - impl->filename, strerror(errno)); + if (avail < (int32_t)size) { + memset(bd->data, 0, size); + if (avail > 0) + pw_log_warn("underrun %d < %u", avail, size); + impl->have_sync = false; } - else { - d->chunk->size += nread; + if (avail > (int32_t)RINGBUFFER_SIZE) { + index += avail - impl->target_buffer; + avail = impl->target_buffer; + pw_log_warn("overrun %d > %u", avail, RINGBUFFER_SIZE); } - - impl->leftover_count = d->chunk->size % impl->frame_size; - d->chunk->size -= impl->leftover_count; - memcpy(impl->leftover, SPA_PTROFF(d->data, d->chunk->size, void), impl->leftover_count); + if (avail > 0) { + avail = SPA_ROUND_DOWN(avail, impl->frame_size); + update_rate(impl, avail); + + avail = SPA_MIN(size, (uint32_t)avail); + spa_ringbuffer_read_data(&impl->ring, + impl->buffer, RINGBUFFER_SIZE, + index & RINGBUFFER_MASK, + bd->data, avail); + + index += avail; + spa_ringbuffer_read_update(&impl->ring, index); + } + bd->chunk->offset = 0; + bd->chunk->size = size; + bd->chunk->stride = impl->frame_size; pw_stream_queue_buffer(impl->stream, buf); } +static void stream_io_changed(void *data, uint32_t id, void *area, uint32_t size) +{ + struct impl *impl = data; + switch (id) { + case SPA_IO_RateMatch: + impl->rate_match = area; + break; + case SPA_IO_Position: + impl->position = area; + break; + } +} + static const struct pw_stream_events playback_stream_events = { PW_VERSION_STREAM_EVENTS, .destroy = stream_destroy, @@ -290,8 +421,9 @@ static const struct pw_stream_events capture_stream_events = { PW_VERSION_STREAM_EVENTS, .destroy = stream_destroy, + .io_changed = stream_io_changed, .state_changed = stream_state_changed, - .process = capture_stream_process + .process = capture_stream_process, }; static int create_stream(struct impl *impl) @@ -335,6 +467,92 @@ return 0; } +static inline void +set_iovec(struct spa_ringbuffer *rbuf, void *buffer, uint32_t size, + uint32_t offset, struct iovec *iov, uint32_t len) +{ + iov0.iov_len = SPA_MIN(len, size - offset); + iov0.iov_base = SPA_PTROFF(buffer, offset, void); + iov1.iov_len = len - iov0.iov_len; + iov1.iov_base = buffer; +} + +static int handle_pipe_read(struct impl *impl) +{ + ssize_t nread; + int32_t filled; + uint32_t index; + struct iovec iov2; + + filled = spa_ringbuffer_get_write_index(&impl->ring, &index); + if (!impl->have_sync) { + memset(impl->buffer, 0, RINGBUFFER_SIZE); + } + + if (filled < 0) { + pw_log_warn("%p: underrun write:%u filled:%d", + impl, index, filled); + } + + set_iovec(&impl->ring, + impl->buffer, RINGBUFFER_SIZE, + index & RINGBUFFER_MASK, + iov, RINGBUFFER_SIZE); + + nread = read(impl->fd, iov0.iov_base, iov0.iov_len); + if (nread > 0) { + index += nread; + filled += nread; + if (nread == (ssize_t)iov0.iov_len) { + nread = read(impl->fd, iov1.iov_base, iov1.iov_len); + if (nread > 0) { + index += nread; + filled += nread; + } + } + } + if (!impl->have_sync) { + impl->ring.readindex = index - impl->target_buffer; + + spa_dll_init(&impl->dll); + spa_dll_set_bw(&impl->dll, SPA_DLL_BW_MIN, 256.f, impl->info.rate); + impl->corr = 1.0f; + + pw_log_info("resync"); + impl->have_sync = true; + } + spa_ringbuffer_write_update(&impl->ring, index); + + if (nread < 0) { + const bool important = !(errno == EINTR + || errno == EAGAIN + || errno == EWOULDBLOCK); + + if (important) + pw_log_warn("failed to read from pipe (%s): %m", + impl->filename); + else + pw_log_debug("pipe (%s) underrun: %m", impl->filename); + } + pw_log_debug("filled %d %u %d", filled, index, impl->target_buffer); + + return 0; +} + + +static void on_pipe_io(void *data, int fd, uint32_t mask) +{ + struct impl *impl = data; + + if (mask & (SPA_IO_ERR | SPA_IO_HUP)) { + pw_log_warn("error:%08x", mask); + pw_loop_update_io(impl->data_loop, impl->socket, 0); + return; + } + if (mask & SPA_IO_IN) + handle_pipe_read(impl); +} + static int create_fifo(struct impl *impl) { struct stat st; @@ -363,7 +581,6 @@ do_unlink_fifo = true; } - if ((fd = open(filename, O_RDWR | O_CLOEXEC | O_NONBLOCK, 0)) < 0) { res = -errno; pw_log_error("open('%s'): %s", filename, spa_strerror(res)); @@ -381,6 +598,20 @@ pw_log_error("'%s' is not a FIFO.", filename); goto error; } + impl->socket = pw_loop_add_io(impl->data_loop, fd, + 0, false, on_pipe_io, impl); + if (impl->socket == NULL) { + res = -errno; + pw_log_error("can't create socket"); + goto error; + } + impl->timer = pw_loop_add_timer(impl->data_loop, on_timeout, impl); + if (impl->timer == NULL) { + res = -errno; + pw_log_error("can't create timer"); + goto error; + } + pw_log_info("%s fifo '%s' with format:%s channels:%d rate:%d", impl->direction == PW_DIRECTION_OUTPUT ? "reading from" : "writing to", filename, @@ -390,6 +621,7 @@ impl->filename = strdup(filename); impl->unlink_fifo = do_unlink_fifo; impl->fd = fd; + return 0; error: @@ -440,13 +672,17 @@ unlink(impl->filename); free(impl->filename); } + if (impl->socket) + pw_loop_destroy_source(impl->data_loop, impl->socket); + if (impl->timer) + pw_loop_destroy_source(impl->data_loop, impl->timer); if (impl->fd >= 0) close(impl->fd); pw_properties_free(impl->stream_props); pw_properties_free(impl->props); - free(impl->leftover); + free(impl->buffer); free(impl); } @@ -569,6 +805,7 @@ struct pw_properties *props = NULL; struct impl *impl; const char *str, *media_class = NULL; + struct pw_data_loop *data_loop; int res; PW_LOG_TOPIC_INIT(mod_topic); @@ -601,6 +838,8 @@ impl->module = module; impl->context = context; + data_loop = pw_context_get_data_loop(context); + impl->data_loop = pw_data_loop_get_loop(data_loop); if ((str = pw_properties_get(props, "tunnel.mode")) == NULL) str = "playback"; @@ -662,12 +901,18 @@ copy_props(impl, props, PW_KEY_NODE_RATE); - impl->leftover = calloc(1, impl->frame_size); - if (impl->leftover == NULL) { + impl->buffer = calloc(1, RINGBUFFER_SIZE); + if (impl->buffer == NULL) { res = -errno; - pw_log_error("can't alloc leftover buffer: %m"); + pw_log_error("can't alloc ringbuffer: %m"); goto error; } + spa_ringbuffer_init(&impl->ring); + impl->target_buffer = 8192 * impl->frame_size; + spa_dll_init(&impl->dll); + spa_dll_set_bw(&impl->dll, SPA_DLL_BW_MIN, 256.f, impl->info.rate); + impl->max_error = 256.0f * impl->frame_size; + impl->corr = 1.0f; impl->core = pw_context_get_object(impl->context, PW_TYPE_INTERFACE_Core); if (impl->core == NULL) {
View file
pipewire-0.3.79.tar.gz/src/modules/module-protocol-pulse/manager.c -> pipewire-0.3.80.tar.gz/src/modules/module-protocol-pulse/manager.c
Changed
@@ -318,7 +318,7 @@ case SPA_PARAM_EnumRoute: changed++; break; - case SPA_PARAM_Route: + default: break; } add_param(&o->pending_list, info->paramsi.seq, id, NULL); @@ -435,7 +435,16 @@ continue; info->paramsi.user = 0; - changed++; + switch (id) { + case SPA_PARAM_Props: + case SPA_PARAM_PropInfo: + case SPA_PARAM_Format: + case SPA_PARAM_EnumFormat: + changed++; + break; + default: + break; + } add_param(&o->pending_list, info->paramsi.seq, id, NULL); if (!(info->paramsi.flags & SPA_PARAM_INFO_READ)) continue;
View file
pipewire-0.3.79.tar.gz/src/modules/module-protocol-pulse/module.c -> pipewire-0.3.80.tar.gz/src/modules/module-protocol-pulse/module.c
Changed
@@ -164,6 +164,28 @@ } } +static bool find_key(const char * const keys, const char *key) +{ + for (int i = 0; keysi != NULL; i++) + if (spa_streq(keysi, key)) + return true; + return false; +} + +static int module_args_check(struct pw_properties *props, const char * const valid_args) +{ + if (valid_args != NULL) { + const struct spa_dict_item *it; + spa_dict_for_each(it, &props->dict) { + if (!find_key(valid_args, it->key)) { + pw_log_warn("'%s' is not a valid module argument key", it->key); + return -EINVAL; + } + } + } + return 0; +} + int module_args_to_audioinfo_keys(struct impl *impl, struct pw_properties *props, const char *key_format, const char *key_rate, const char *key_channels, const char *key_channel_map, @@ -299,6 +321,7 @@ { const struct module_info *info; struct module *module; + int res; info = find_module_info(name); if (info == NULL) { @@ -321,19 +344,19 @@ return NULL; module->props = pw_properties_new(NULL, NULL); - if (module->props == NULL) { - module_free(module); - return NULL; - } + if (module->props == NULL) + goto error_free; if (args) module_args_add_props(module->props, args); + if ((res = module_args_check(module->props, info->valid_args)) < 0) { + errno = -res; + goto error_free; + } - int res = module->info->prepare(module); - if (res < 0) { - module_free(module); + if ((res = module->info->prepare(module)) < 0) { errno = -res; - return NULL; + goto error_free; } module->index = pw_map_insert_new(&impl->modules, module); @@ -346,4 +369,9 @@ module->index |= MODULE_FLAG; return module; + +error_free: + module_free(module); + return NULL; + }
View file
pipewire-0.3.79.tar.gz/src/modules/module-protocol-pulse/module.h -> pipewire-0.3.80.tar.gz/src/modules/module-protocol-pulse/module.h
Changed
@@ -23,6 +23,7 @@ int (*load) (struct module *module); int (*unload) (struct module *module); + const char* const *valid_args; const struct spa_dict *properties; size_t data_size; };
View file
pipewire-0.3.79.tar.gz/src/modules/module-protocol-pulse/modules/module-pipe-sink.c -> pipewire-0.3.80.tar.gz/src/modules/module-protocol-pulse/modules/module-pipe-sink.c
Changed
@@ -26,7 +26,7 @@ struct pw_impl_module *mod; struct pw_properties *global_props; - struct pw_properties *capture_props; + struct pw_properties *stream_props; }; static void module_destroy(void *data) @@ -49,7 +49,7 @@ char *args; size_t size; - pw_properties_setf(data->capture_props, "pulse.module.id", + pw_properties_setf(data->stream_props, "pulse.module.id", "%u", module->index); if ((f = open_memstream(&args, &size)) == NULL) @@ -58,7 +58,7 @@ fprintf(f, "{"); pw_properties_serialize_dict(f, &data->global_props->dict, 0); fprintf(f, " \"stream.props\": {"); - pw_properties_serialize_dict(f, &data->capture_props->dict, 0); + pw_properties_serialize_dict(f, &data->stream_props->dict, 0); fprintf(f, " } }"); fclose(f); @@ -86,7 +86,7 @@ pw_impl_module_destroy(d->mod); d->mod = NULL; } - pw_properties_free(d->capture_props); + pw_properties_free(d->stream_props); pw_properties_free(d->global_props); return 0; } @@ -100,7 +100,8 @@ "format=<sample format> " "rate=<sample rate> " "channels=<number of channels> " - "channel_map=<channel map> " }, + "channel_map=<channel map> " + "use_system_clock_for_timing=<yes or no> " }, { PW_KEY_MODULE_VERSION, PACKAGE_VERSION }, }; @@ -108,16 +109,17 @@ { struct module_pipesink_data * const d = module->user_data; struct pw_properties * const props = module->props; - struct pw_properties *global_props = NULL, *capture_props = NULL; + struct pw_properties *global_props = NULL, *stream_props = NULL; struct spa_audio_info_raw info = { 0 }; const char *str; + bool use_system_clock; int res = 0; PW_LOG_TOPIC_INIT(mod_topic); global_props = pw_properties_new(NULL, NULL); - capture_props = pw_properties_new(NULL, NULL); - if (!global_props || !capture_props) { + stream_props = pw_properties_new(NULL, NULL); + if (!global_props || !stream_props) { res = -EINVAL; goto out; } @@ -133,31 +135,39 @@ audioinfo_to_properties(&info, global_props); if ((str = pw_properties_get(props, "sink_name")) != NULL) { - pw_properties_set(capture_props, PW_KEY_NODE_NAME, str); + pw_properties_set(stream_props, PW_KEY_NODE_NAME, str); pw_properties_set(props, "sink_name", NULL); } if ((str = pw_properties_get(props, "sink_properties")) != NULL) - module_args_add_props(capture_props, str); + module_args_add_props(stream_props, str); if ((str = pw_properties_get(props, "file")) != NULL) { pw_properties_set(global_props, "pipe.filename", str); pw_properties_set(props, "file", NULL); } - if ((str = pw_properties_get(capture_props, PW_KEY_DEVICE_ICON_NAME)) == NULL) - pw_properties_set(capture_props, PW_KEY_DEVICE_ICON_NAME, + str = pw_properties_get(props, "use_system_clock_for_timing"); + use_system_clock = str ? module_args_parse_bool(str) : false; + + if ((str = pw_properties_get(stream_props, PW_KEY_NODE_GROUP)) == NULL) { + if (use_system_clock) + pw_properties_set(stream_props, PW_KEY_NODE_GROUP, + "pipewire.dummy"); + } + if ((str = pw_properties_get(stream_props, PW_KEY_DEVICE_ICON_NAME)) == NULL) + pw_properties_set(stream_props, PW_KEY_DEVICE_ICON_NAME, "audio-card"); - if ((str = pw_properties_get(capture_props, PW_KEY_NODE_NAME)) == NULL) - pw_properties_set(capture_props, PW_KEY_NODE_NAME, + if ((str = pw_properties_get(stream_props, PW_KEY_NODE_NAME)) == NULL) + pw_properties_set(stream_props, PW_KEY_NODE_NAME, "fifo_output"); d->module = module; d->global_props = global_props; - d->capture_props = capture_props; + d->stream_props = stream_props; return 0; out: pw_properties_free(global_props); - pw_properties_free(capture_props); + pw_properties_free(stream_props); return res; }
View file
pipewire-0.3.79.tar.gz/src/modules/module-protocol-pulse/modules/module-pipe-source.c -> pipewire-0.3.80.tar.gz/src/modules/module-protocol-pulse/modules/module-pipe-source.c
Changed
@@ -26,7 +26,7 @@ struct pw_impl_module *mod; struct pw_properties *global_props; - struct pw_properties *playback_props; + struct pw_properties *stream_props; }; static void module_destroy(void *data) @@ -49,7 +49,7 @@ char *args; size_t size; - pw_properties_setf(data->playback_props, "pulse.module.id", + pw_properties_setf(data->stream_props, "pulse.module.id", "%u", module->index); if ((f = open_memstream(&args, &size)) == NULL) @@ -58,7 +58,7 @@ fprintf(f, "{"); pw_properties_serialize_dict(f, &data->global_props->dict, 0); fprintf(f, " \"stream.props\": {"); - pw_properties_serialize_dict(f, &data->playback_props->dict, 0); + pw_properties_serialize_dict(f, &data->stream_props->dict, 0); fprintf(f, " } }"); fclose(f); @@ -86,7 +86,7 @@ pw_impl_module_destroy(d->mod); d->mod = NULL; } - pw_properties_free(d->playback_props); + pw_properties_free(d->stream_props); pw_properties_free(d->global_props); return 0; } @@ -108,7 +108,7 @@ { struct module_pipesrc_data * const d = module->user_data; struct pw_properties * const props = module->props; - struct pw_properties *global_props = NULL, *playback_props = NULL; + struct pw_properties *global_props = NULL, *stream_props = NULL; struct spa_audio_info_raw info = { 0 }; const char *str; int res = 0; @@ -116,8 +116,8 @@ PW_LOG_TOPIC_INIT(mod_topic); global_props = pw_properties_new(NULL, NULL); - playback_props = pw_properties_new(NULL, NULL); - if (!global_props || !playback_props) { + stream_props = pw_properties_new(NULL, NULL); + if (!global_props || !stream_props) { res = -errno; goto out; } @@ -133,31 +133,36 @@ audioinfo_to_properties(&info, global_props); if ((str = pw_properties_get(props, "source_name")) != NULL) { - pw_properties_set(playback_props, PW_KEY_NODE_NAME, str); + pw_properties_set(stream_props, PW_KEY_NODE_NAME, str); pw_properties_set(props, "source_name", NULL); } if ((str = pw_properties_get(props, "source_properties")) != NULL) - module_args_add_props(playback_props, str); + module_args_add_props(stream_props, str); if ((str = pw_properties_get(props, "file")) != NULL) { pw_properties_set(global_props, "pipe.filename", str); pw_properties_set(props, "file", NULL); } - if ((str = pw_properties_get(playback_props, PW_KEY_DEVICE_ICON_NAME)) == NULL) - pw_properties_set(playback_props, PW_KEY_DEVICE_ICON_NAME, + if ((str = pw_properties_get(stream_props, PW_KEY_DEVICE_ICON_NAME)) == NULL) + pw_properties_set(stream_props, PW_KEY_DEVICE_ICON_NAME, "audio-input-microphone"); - if ((str = pw_properties_get(playback_props, PW_KEY_NODE_NAME)) == NULL) - pw_properties_set(playback_props, PW_KEY_NODE_NAME, + if ((str = pw_properties_get(stream_props, PW_KEY_NODE_NAME)) == NULL) + pw_properties_set(stream_props, PW_KEY_NODE_NAME, "fifo_input"); + if ((str = pw_properties_get(stream_props, PW_KEY_NODE_DRIVER)) == NULL) + pw_properties_set(stream_props, PW_KEY_NODE_DRIVER, "true"); + if ((str = pw_properties_get(stream_props, PW_KEY_PRIORITY_DRIVER)) == NULL) + pw_properties_set(stream_props, PW_KEY_PRIORITY_DRIVER, "50000"); + d->module = module; - d->playback_props = playback_props; + d->stream_props = stream_props; d->global_props = global_props; return 0; out: pw_properties_free(global_props); - pw_properties_free(playback_props); + pw_properties_free(stream_props); return res; }
View file
pipewire-0.3.79.tar.gz/src/modules/module-protocol-pulse/modules/module-roc-sink.c -> pipewire-0.3.80.tar.gz/src/modules/module-protocol-pulse/modules/module-roc-sink.c
Changed
@@ -89,6 +89,15 @@ return 0; } +static const char* const valid_args = { + "sink_name", + "sink_properties", + "fec_code", + "remote_ip", + "remote_source_port", + "remote_repair_port", + NULL +}; static const struct spa_dict_item module_roc_sink_info = { { PW_KEY_MODULE_AUTHOR, "Sanchayan Maity <sanchayan@asymptotic.io>" }, { PW_KEY_MODULE_DESCRIPTION, "roc sink" }, @@ -169,6 +178,7 @@ DEFINE_MODULE_INFO(module_roc_sink) = { .name = "module-roc-sink", + .valid_args = valid_args, .prepare = module_roc_sink_prepare, .load = module_roc_sink_load, .unload = module_roc_sink_unload,
View file
pipewire-0.3.79.tar.gz/src/modules/module-protocol-pulse/modules/module-roc-source.c -> pipewire-0.3.80.tar.gz/src/modules/module-protocol-pulse/modules/module-roc-source.c
Changed
@@ -89,6 +89,18 @@ return 0; } +static const char* const valid_args = { + "source_name", + "source_properties", + "resampler_profile", + "fec_code", + "sess_latency_msec", + "local_ip", + "local_source_port", + "local_repair_port", + NULL +}; + static const struct spa_dict_item module_roc_source_info = { { PW_KEY_MODULE_AUTHOR, "Sanchayan Maity <sanchayan@asymptotic.io>" }, { PW_KEY_MODULE_DESCRIPTION, "roc source" }, @@ -178,6 +190,7 @@ DEFINE_MODULE_INFO(module_roc_source) = { .name = "module-roc-source", + .valid_args = valid_args, .prepare = module_roc_source_prepare, .load = module_roc_source_load, .unload = module_roc_source_unload,
View file
pipewire-0.3.79.tar.gz/src/modules/module-protocol-pulse/pulse-server.c -> pipewire-0.3.80.tar.gz/src/modules/module-protocol-pulse/pulse-server.c
Changed
@@ -2505,8 +2505,12 @@ allow_monitor = true; } } - } else if (index == SPA_ID_INVALID) + } else if (index != SPA_ID_INVALID) { + if (!sink) + allow_monitor = true; + } else { return NULL; + } spa_zero(sel);
View file
pipewire-0.3.79.tar.gz/src/modules/module-protocol-pulse/stream.c -> pipewire-0.3.80.tar.gz/src/modules/module-protocol-pulse/stream.c
Changed
@@ -216,11 +216,11 @@ struct client *client = stream->client; struct impl *impl = client->impl; struct message *reply; - int missed; + int suppressed; - if ((missed = spa_ratelimit_test(&impl->rate_limit, stream->timestamp)) >= 0) { - pw_log_info("%s: UNDERFLOW channel:%u offset:%" PRIi64" (%d missed)", - client->name, stream->channel, offset, missed); + if ((suppressed = spa_ratelimit_test(&impl->rate_limit, stream->timestamp)) >= 0) { + pw_log_info("%s: UNDERFLOW channel:%u offset:%" PRIi64" (%d suppressed)", + client->name, stream->channel, offset, suppressed); } reply = message_alloc(impl, -1, 0);
View file
pipewire-0.3.79.tar.gz/src/modules/module-pulse-tunnel.c -> pipewire-0.3.80.tar.gz/src/modules/module-pulse-tunnel.c
Changed
@@ -393,23 +393,26 @@ req = 4096 * impl->frame_size; size = SPA_MIN(bd->maxsize, req); + size = SPA_ROUND_DOWN(size, impl->frame_size); avail = spa_ringbuffer_get_read_index(&impl->ring, &index); - if (avail < (int32_t)size) { + if (avail < (int32_t)size) memset(bd->data, 0, size); - } else { - if (avail > (int32_t)RINGBUFFER_SIZE) { - avail = impl->target_buffer; - index += avail - impl->target_buffer; - } else { - update_rate(impl, avail / impl->frame_size); - } + if (avail > (int32_t)RINGBUFFER_SIZE) { + index += avail - impl->target_buffer; + avail = impl->target_buffer; + } + if (avail > 0) { + avail = SPA_ROUND_DOWN(avail, impl->frame_size); + update_rate(impl, avail / impl->frame_size); + + avail = SPA_MIN(size, (uint32_t)avail); spa_ringbuffer_read_data(&impl->ring, impl->buffer, RINGBUFFER_SIZE, index & RINGBUFFER_MASK, - bd->data, size); + bd->data, avail); - index += size; + index += avail; spa_ringbuffer_read_update(&impl->ring, index); } bd->chunk->offset = 0; @@ -669,22 +672,22 @@ { struct impl *impl = userdata; struct timespec ts; - int missed; + int suppressed; clock_gettime(CLOCK_MONOTONIC, &ts); - if ((missed = spa_ratelimit_test(&impl->rate_limit, SPA_TIMESPEC_TO_NSEC(&ts))) >= 0) - pw_log_warn("underflow (%d missed)", missed); + if ((suppressed = spa_ratelimit_test(&impl->rate_limit, SPA_TIMESPEC_TO_NSEC(&ts))) >= 0) + pw_log_warn("underflow (%d suppressed)", suppressed); impl->resync = true; } static void stream_overflow_cb(pa_stream *s, void *userdata) { struct impl *impl = userdata; struct timespec ts; - int missed; + int suppressed; clock_gettime(CLOCK_MONOTONIC, &ts); - if ((missed = spa_ratelimit_test(&impl->rate_limit, SPA_TIMESPEC_TO_NSEC(&ts))) >= 0) - pw_log_warn("overflow (%d missed)", missed); + if ((suppressed = spa_ratelimit_test(&impl->rate_limit, SPA_TIMESPEC_TO_NSEC(&ts))) >= 0) + pw_log_warn("overflow (%d suppressed)", suppressed); impl->resync = true; }
View file
pipewire-0.3.79.tar.gz/src/modules/module-rtp-source.c -> pipewire-0.3.80.tar.gz/src/modules/module-rtp-source.c
Changed
@@ -40,7 +40,7 @@ * The `rtp-source` module creates a PipeWire source that receives audio * and midi RTP packets. * - * This module is usually loaded from the \page page_module_rtp_sap so that the + * This module is usually loaded from the \ref page_module_rtp_sap so that the * source.ip and source.port and format parameters matches that of the sender. * * ## Module Options
View file
pipewire-0.3.79.tar.gz/src/pipewire/context.c -> pipewire-0.3.80.tar.gz/src/pipewire/context.c
Changed
@@ -807,7 +807,7 @@ spa_list_for_each(l, &p->links, input_link) { t = l->output->node; - if (!t->active || !l->prepared || (!t->driving && t->runnable)) + if (!t->active || !l->prepared || (!t->driving && SPA_FLAG_IS_SET(t->checked, 1u<<direction))) continue; pw_log_debug(" peer %p: '%s'", t, t->name); @@ -820,7 +820,7 @@ spa_list_for_each(l, &p->links, output_link) { t = l->input->node; - if (!t->active || !l->prepared || (!t->driving && t->runnable)) + if (!t->active || !l->prepared || (!t->driving && SPA_FLAG_IS_SET(t->checked, 1u<<direction))) continue; pw_log_debug(" peer %p: '%s'", t, t->name); @@ -837,7 +837,7 @@ if (node->link_group != NULL) { spa_list_for_each(t, nodes, sort_link) { if (t->exported || !t->active || - SPA_FLAG_IS_SET(t->checked, 1u<<direction)) + SPA_FLAG_IS_SET(t->checked, 1u<<direction)) continue; if (!spa_streq(t->link_group, node->link_group)) continue; @@ -1496,6 +1496,9 @@ n->rt.position->clock.rate = n->target_rate; } n->target_pending = false; + } else { + n->target_quantum = n->rt.position->clock.target_duration; + n->target_rate = n->rt.position->clock.target_rate; } pw_log_debug("%p: driver %p running:%d runnable:%d quantum:%u '%s'",
View file
pipewire-0.3.79.tar.gz/src/pipewire/filter.c -> pipewire-0.3.80.tar.gz/src/pipewire/filter.c
Changed
@@ -83,7 +83,8 @@ #define PORT_Format 3 #define PORT_Buffers 4 #define PORT_Latency 5 -#define N_PORT_PARAMS 6 +#define PORT_Tag 6 +#define N_PORT_PARAMS 7 struct spa_param_info paramsN_PORT_PARAMS; struct spa_io_buffers *io; @@ -189,15 +190,17 @@ return PORT_Buffers; case SPA_PARAM_Latency: return PORT_Latency; + case SPA_PARAM_Tag: + return PORT_Tag; default: return -1; } } -static void fix_datatype(const struct spa_pod *param) +static void fix_datatype(struct spa_pod *param) { const struct spa_pod_prop *pod_param; - const struct spa_pod *vals; + struct spa_pod *vals; uint32_t dataType, n_vals, choice; pod_param = spa_pod_find_prop(param, NULL, SPA_PARAM_BUFFERS_dataType); @@ -237,11 +240,6 @@ if (p == NULL) return NULL; - if (id == SPA_PARAM_Buffers && port != NULL && - SPA_FLAG_IS_SET(port->flags, PW_FILTER_PORT_FLAG_MAP_BUFFERS) && - port->direction == SPA_DIRECTION_INPUT) - fix_datatype(param); - if (id == SPA_PARAM_ProcessLatency && port == NULL) spa_process_latency_parse(param, &impl->process_latency); @@ -251,6 +249,11 @@ memcpy(p->param, param, SPA_POD_SIZE(param)); SPA_POD_OBJECT_ID(p->param) = id; + if (id == SPA_PARAM_Buffers && port != NULL && + SPA_FLAG_IS_SET(port->flags, PW_FILTER_PORT_FLAG_MAP_BUFFERS) && + port->direction == SPA_DIRECTION_INPUT) + fix_datatype(p->param); + pw_log_debug("%p: port %p param id %d (%s)", impl, p, id, spa_debug_type_find_name(spa_type_param, id)); @@ -1849,6 +1852,7 @@ p->paramsPORT_Format = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE); p->paramsPORT_Buffers = SPA_PARAM_INFO(SPA_PARAM_Buffers, 0); p->paramsPORT_Latency = SPA_PARAM_INFO(SPA_PARAM_Latency, SPA_PARAM_INFO_WRITE); + p->paramsPORT_Tag = SPA_PARAM_INFO(SPA_PARAM_Tag, SPA_PARAM_INFO_WRITE); p->info.params = p->params; p->info.n_params = N_PORT_PARAMS;
View file
pipewire-0.3.79.tar.gz/src/pipewire/impl-link.c -> pipewire-0.3.80.tar.gz/src/pipewire/impl-link.c
Changed
@@ -774,6 +774,7 @@ pw_impl_port_emit_link_removed(this->input, this); pw_impl_port_recalc_latency(this->input); + pw_impl_port_recalc_tag(this->input); if ((res = pw_impl_port_use_buffers(port, mix, 0, NULL, 0)) < 0) { pw_log_warn("%p: port %p clear error %s", this, port, spa_strerror(res)); @@ -803,6 +804,7 @@ pw_impl_port_emit_link_removed(this->output, this); pw_impl_port_recalc_latency(this->output); + pw_impl_port_recalc_tag(this->output); /* we don't clear output buffers when the link goes away. They will get * cleared when the node goes to suspend */ @@ -988,6 +990,14 @@ pw_impl_port_recalc_latency(this->output); } +static void input_port_tag_changed(void *data) +{ + struct impl *impl = data; + struct pw_impl_link *this = &impl->this; + if (!this->feedback) + pw_impl_port_recalc_tag(this->output); +} + static void output_port_latency_changed(void *data) { struct impl *impl = data; @@ -996,11 +1006,20 @@ pw_impl_port_recalc_latency(this->input); } +static void output_port_tag_changed(void *data) +{ + struct impl *impl = data; + struct pw_impl_link *this = &impl->this; + if (!this->feedback) + pw_impl_port_recalc_tag(this->input); +} + static const struct pw_impl_port_events input_port_events = { PW_VERSION_IMPL_PORT_EVENTS, .param_changed = input_port_param_changed, .state_changed = input_port_state_changed, .latency_changed = input_port_latency_changed, + .tag_changed = input_port_tag_changed, }; static const struct pw_impl_port_events output_port_events = { @@ -1008,6 +1027,7 @@ .param_changed = output_port_param_changed, .state_changed = output_port_state_changed, .latency_changed = output_port_latency_changed, + .tag_changed = output_port_tag_changed, }; static void node_result(struct impl *impl, void *obj, @@ -1395,6 +1415,8 @@ pw_impl_port_recalc_latency(output); pw_impl_port_recalc_latency(input); + pw_impl_port_recalc_tag(output); + pw_impl_port_recalc_tag(input); if (impl->onode != impl->inode) this->peer = pw_node_peer_ref(impl->onode, impl->inode);
View file
pipewire-0.3.79.tar.gz/src/pipewire/impl-node.c -> pipewire-0.3.80.tar.gz/src/pipewire/impl-node.c
Changed
@@ -1105,9 +1105,9 @@ struct pw_node_activation *na = driver->rt.target.activation; struct spa_io_clock *cl = &na->position.clock; enum spa_log_level level = SPA_LOG_LEVEL_DEBUG; - int missed; + int suppressed; - if ((missed = spa_ratelimit_test(&driver->rt.rate_limit, nsec)) >= 0) + if ((suppressed = spa_ratelimit_test(&driver->rt.rate_limit, nsec)) >= 0) level = SPA_LOG_LEVEL_INFO; spa_list_for_each(t, &driver->rt.target_list, link) { @@ -1121,11 +1121,11 @@ a->status == PW_NODE_ACTIVATION_AWAKE) { update_xrun_stats(a, nsec / 1000, 0); - pw_log(level, "(%s-%u) client too slow! rate:%u/%u pos:%"PRIu64" status:%s (%u missed)", + pw_log(level, "(%s-%u) client too slow! rate:%u/%u pos:%"PRIu64" status:%s (%u suppressed)", t->name, t->id, (uint32_t)(cl->rate.num * cl->duration), cl->rate.denom, cl->position, str_status(a->status), - missed); + suppressed); } pw_log_debug("(%s-%u) state:%p pending:%d/%d s:%"PRIu64" a:%"PRIu64" f:%"PRIu64 " waiting:%"PRIu64" process:%"PRIu64" status:%s sync:%d", @@ -1900,11 +1900,11 @@ struct pw_node_activation *da = this->rt.driver_target.activation; struct spa_system *data_system = this->data_system; uint64_t nsec = get_time_ns(data_system); - int missed; + int suppressed; update_xrun_stats(a, trigger, delay); - if ((missed = spa_ratelimit_test(&this->rt.rate_limit, nsec)) >= 0) { + if ((suppressed = spa_ratelimit_test(&this->rt.rate_limit, nsec)) >= 0) { struct spa_fraction rate; if (da) { struct spa_io_clock *cl = &da->position.clock; @@ -1914,11 +1914,11 @@ rate = SPA_FRACTION(0,0); } pw_log_info("(%s-%d) XRun! rate:%u/%u count:%u time:%"PRIu64 - " delay:%"PRIu64" max:%"PRIu64" (%d missed)", + " delay:%"PRIu64" max:%"PRIu64" (%d suppressed)", this->name, this->info.id, rate.num, rate.denom, a->xrun_count, trigger, delay, a->max_delay, - missed); + suppressed); } pw_impl_node_rt_emit_xrun(this); @@ -2133,7 +2133,6 @@ uint32_t id, uint32_t index, uint32_t next, struct spa_pod *param); int seq; - uint32_t count; unsigned int cache:1; }; @@ -2147,11 +2146,8 @@ const struct spa_result_node_params *r = result; if (d->seq == seq) { d->callback(d->data, seq, r->id, r->index, r->next, r->param); - if (d->cache) { - if (d->count++ == 0) - pw_param_add(&impl->pending_list, seq, r->id, NULL); + if (d->cache) pw_param_add(&impl->pending_list, seq, r->id, r->param); - } } break; } @@ -2172,7 +2168,7 @@ { int res; struct impl *impl = SPA_CONTAINER_OF(node, struct impl, this); - struct result_node_params_data user_data = { impl, data, callback, seq, 0, false }; + struct result_node_params_data user_data = { impl, data, callback, seq, false }; struct spa_hook listener; struct spa_param_info *pi; static const struct spa_node_events node_events = { @@ -2226,6 +2222,9 @@ user_data.cache = impl->cache_params && (filter == NULL && index == 0 && max == UINT32_MAX); + if (user_data.cache) + pw_param_add(&impl->pending_list, seq, param_id, NULL); + spa_zero(listener); spa_node_add_listener(node->node, &listener, &node_events, &user_data); res = spa_node_enum_params(node->node, seq,
View file
pipewire-0.3.79.tar.gz/src/pipewire/impl-port.c -> pipewire-0.3.80.tar.gz/src/pipewire/impl-port.c
Changed
@@ -9,6 +9,7 @@ #include <spa/pod/parser.h> #include <spa/param/audio/format-utils.h> +#include <spa/param/tag-utils.h> #include <spa/node/utils.h> #include <spa/utils/names.h> #include <spa/utils/string.h> @@ -16,6 +17,7 @@ #include <spa/debug/types.h> #include <spa/pod/filter.h> #include <spa/pod/dynamic.h> +#include <spa/debug/pod.h> #include "pipewire/impl.h" #include "pipewire/private.h" @@ -444,7 +446,7 @@ struct pw_impl_port *this = data; struct spa_latency_info latency; - if (id != SPA_PARAM_Latency) + if (id != SPA_PARAM_Latency || param == NULL) return -EINVAL; if (spa_latency_parse(param, &latency) < 0) @@ -464,6 +466,37 @@ return 0; } +static int process_tag_param(void *data, int seq, + uint32_t id, uint32_t index, uint32_t next, struct spa_pod *param) +{ + struct pw_impl_port *this = data; + struct spa_tag_info info; + struct spa_pod *old; + void *state = NULL; + + if (id != SPA_PARAM_Tag || param == NULL) + return -EINVAL; + if (spa_tag_parse(param, &info, &state) < 0) + return 0; + + old = this->taginfo.direction; + + if (spa_tag_compare(old, param) == 0) + return 0; + + pw_log_debug("port %p: got %s tag %p", this, + pw_direction_as_string(info.direction), param); + if (param) + pw_log_pod(SPA_LOG_LEVEL_DEBUG, param); + + free(old); + this->taginfo.direction = spa_pod_copy(param); + + if (info.direction == this->direction) + pw_impl_port_emit_tag_changed(this); + + return 0; +} static void update_info(struct pw_impl_port *port, const struct spa_port_info *info) { @@ -514,6 +547,13 @@ pw_impl_port_for_each_param(port, 0, id, 0, UINT32_MAX, NULL, process_latency_param, port); break; + case SPA_PARAM_Tag: + port->have_tag_param = + SPA_FLAG_IS_SET(info->paramsi.flags, SPA_PARAM_INFO_WRITE); + if (port->node != NULL) + pw_impl_port_for_each_param(port, 0, id, 0, UINT32_MAX, + NULL, process_tag_param, port); + break; default: break; } @@ -1061,6 +1101,7 @@ pw_impl_port_for_each_param(port, 0, SPA_PARAM_IO, 0, 0, NULL, check_param_io, port); pw_impl_port_for_each_param(port, 0, SPA_PARAM_Latency, 0, 0, NULL, process_latency_param, port); + pw_impl_port_for_each_param(port, 0, SPA_PARAM_Tag, 0, 0, NULL, process_tag_param, port); nprops = pw_impl_node_get_properties(node); media_class = pw_properties_get(nprops, PW_KEY_MEDIA_CLASS); @@ -1310,6 +1351,8 @@ pw_param_clear(&impl->param_list, SPA_ID_INVALID); pw_param_clear(&impl->pending_list, SPA_ID_INVALID); + free(port->tagSPA_DIRECTION_INPUT); + free(port->tagSPA_DIRECTION_OUTPUT); pw_map_clear(&port->mix_port_map); @@ -1325,7 +1368,6 @@ uint32_t id, uint32_t index, uint32_t next, struct spa_pod *param); int seq; - uint32_t count; unsigned int cache:1; }; @@ -1339,11 +1381,8 @@ const struct spa_result_node_params *r = result; if (d->seq == seq) { d->callback(d->data, seq, r->id, r->index, r->next, r->param); - if (d->cache) { - if (d->count++ == 0) - pw_param_add(&impl->pending_list, seq, r->id, NULL); + if (d->cache) pw_param_add(&impl->pending_list, seq, r->id, r->param); - } } break; } @@ -1365,7 +1404,7 @@ int res; struct impl *impl = SPA_CONTAINER_OF(port, struct impl, this); struct pw_impl_node *node = port->node; - struct result_port_params_data user_data = { impl, data, callback, seq, 0, false }; + struct result_port_params_data user_data = { impl, data, callback, seq, false }; struct spa_hook listener; struct spa_param_info *pi; static const struct spa_node_events node_events = { @@ -1419,6 +1458,9 @@ user_data.cache = impl->cache_params && (filter == NULL && index == 0 && max == UINT32_MAX); + if (user_data.cache) + pw_param_add(&impl->pending_list, seq, param_id, NULL); + spa_zero(listener); spa_node_add_listener(node->node, &listener, &node_events, &user_data); res = spa_node_port_enum_params(node->node, seq, @@ -1507,6 +1549,7 @@ struct spa_pod_builder b = { 0 }; uint8_t buffer1024; bool changed; + int count = 0; if (port->destroying) return 0; @@ -1529,6 +1572,7 @@ latency.min_quantum, latency.max_quantum, latency.min_rate, latency.max_rate, latency.min_ns, latency.max_ns); + count++; } } else { spa_list_for_each(l, &port->links, input_link) { @@ -1544,13 +1588,16 @@ latency.min_quantum, latency.max_quantum, latency.min_rate, latency.max_rate, latency.min_ns, latency.max_ns); + count++; } } spa_latency_info_combine_finish(&latency); - current = &port->latencylatency.direction; - - changed = spa_latency_info_compare(current, &latency) != 0; + current = port->have_latency ? &port->latencylatency.direction : NULL; + if (current == NULL) + changed = count > 0; + else + changed = spa_latency_info_compare(current, &latency) != 0; pw_log_info("port %d: %s %s latency %f-%f %d-%d %"PRIu64"-%"PRIu64, port->info.id, changed ? "set" : "keep", @@ -1562,16 +1609,90 @@ if (!changed) return 0; - *current = latency; + port->latencylatency.direction = latency; + port->have_latency = count > 0; if (!port->have_latency_param) return 0; spa_pod_builder_init(&b, buffer, sizeof(buffer)); - param = spa_latency_build(&b, SPA_PARAM_Latency, &latency); + param = port->have_latency ? spa_latency_build(&b, SPA_PARAM_Latency, &latency) : NULL; return pw_impl_port_set_param(port, SPA_PARAM_Latency, 0, param); } +int pw_impl_port_recalc_tag(struct pw_impl_port *port) +{ + struct pw_impl_link *l; + struct pw_impl_port *other; + struct spa_pod *param, *tag, *old; + struct spa_pod_dynamic_builder b = { 0 }; + struct spa_pod_frame f; + struct spa_tag_info info; + enum spa_direction direction; + uint8_t buffer1024; + int count = 0; + bool changed; + + if (port->destroying) + return 0; + + direction = SPA_DIRECTION_REVERSE(port->direction); + + spa_pod_dynamic_builder_init(&b, buffer, sizeof(buffer), 4096); + spa_tag_build_start(&b.b, &f, SPA_PARAM_Tag, direction); + + if (port->direction == PW_DIRECTION_OUTPUT) { + spa_list_for_each(l, &port->links, output_link) { + other = l->input; + tag = other->tagother->direction; + if (tag) { + void *state = NULL; + while (spa_tag_parse(tag, &info, &state) == 1) { + spa_tag_build_add_info(&b.b, info.info); + count++; + } + } + } + } else { + spa_list_for_each(l, &port->links, input_link) { + other = l->output; + tag = other->tagother->direction; + if (tag) { + void *state = NULL; + while (spa_tag_parse(tag, &info, &state) == 1) { + spa_tag_build_add_info(&b.b, info.info); + count++; + } + } + } + } + param = count == 0 ? NULL : spa_tag_build_end(&b.b, &f); + + old = port->tagdirection; + + changed = spa_tag_compare(old, param); + + pw_log_info("port %d: %p %s %s tag %p", + port->info.id, port, changed ? "set" : "keep", + pw_direction_as_string(direction), param); + + if (changed) { + free(old); + port->tagdirection = param ? spa_pod_copy(param) : NULL; + if (param) + pw_log_pod(SPA_LOG_LEVEL_INFO, param); + } + spa_pod_dynamic_builder_clean(&b); + + if (!changed) + return 0; + + if (!port->have_tag_param) + return 0; + + return pw_impl_port_set_param(port, SPA_PARAM_Tag, 0, port->tagdirection); +} + SPA_EXPORT int pw_impl_port_is_linked(struct pw_impl_port *port) {
View file
pipewire-0.3.79.tar.gz/src/pipewire/impl-port.h -> pipewire-0.3.80.tar.gz/src/pipewire/impl-port.h
Changed
@@ -36,7 +36,7 @@ /** Port events, use \ref pw_impl_port_add_listener */ struct pw_impl_port_events { -#define PW_VERSION_IMPL_PORT_EVENTS 2 +#define PW_VERSION_IMPL_PORT_EVENTS 3 uint32_t version; /** The port is destroyed */ @@ -72,6 +72,8 @@ /** latency changed. Since version 2 */ void (*latency_changed) (void *data); + /** tag changed. Since version 3 */ + void (*tag_changed) (void *data); }; /** Create a new port
View file
pipewire-0.3.79.tar.gz/src/pipewire/private.h -> pipewire-0.3.80.tar.gz/src/pipewire/private.h
Changed
@@ -782,6 +782,7 @@ #define pw_impl_port_emit_control_removed(p,c) pw_impl_port_emit(p, control_removed, 0, c) #define pw_impl_port_emit_param_changed(p,i) pw_impl_port_emit(p, param_changed, 1, i) #define pw_impl_port_emit_latency_changed(p) pw_impl_port_emit(p, latency_changed, 2) +#define pw_impl_port_emit_tag_changed(p) pw_impl_port_emit(p, tag_changed, 3) #define PW_IMPL_PORT_IS_CONTROL(port) SPA_FLAG_MASK((port)->flags, \ PW_IMPL_PORT_FLAG_BUFFERS|PW_IMPL_PORT_FLAG_CONTROL,\ @@ -846,6 +847,10 @@ struct spa_latency_info latency2; /**< latencies */ unsigned int have_latency_param:1; unsigned int ignore_latency:1; + unsigned int have_latency:1; + + unsigned int have_tag_param:1; + struct spa_pod *tag2; /**< tags */ void *owner_data; /**< extra owner data */ void *user_data; /**< extra user data */ @@ -1221,6 +1226,7 @@ struct spa_buffer **buffers, uint32_t n_buffers); int pw_impl_port_recalc_latency(struct pw_impl_port *port); +int pw_impl_port_recalc_tag(struct pw_impl_port *port); /** Change the state of the node */ int pw_impl_node_set_state(struct pw_impl_node *node, enum pw_node_state state);
View file
pipewire-0.3.79.tar.gz/src/pipewire/stream.c -> pipewire-0.3.80.tar.gz/src/pipewire/stream.c
Changed
@@ -113,7 +113,8 @@ #define PORT_Format 3 #define PORT_Buffers 4 #define PORT_Latency 5 -#define N_PORT_PARAMS 6 +#define PORT_Tag 6 +#define N_PORT_PARAMS 7 struct spa_param_info port_paramsN_PORT_PARAMS; struct spa_list param_list; @@ -191,15 +192,17 @@ return PORT_Buffers; case SPA_PARAM_Latency: return PORT_Latency; + case SPA_PARAM_Tag: + return PORT_Tag; default: return -1; } } -static void fix_datatype(const struct spa_pod *param) +static void fix_datatype(struct spa_pod *param) { const struct spa_pod_prop *pod_param; - const struct spa_pod *vals; + struct spa_pod *vals; uint32_t dataType, n_vals, choice; pod_param = spa_pod_find_prop(param, NULL, SPA_PARAM_BUFFERS_dataType); @@ -239,17 +242,17 @@ if (p == NULL) return NULL; - if (id == SPA_PARAM_Buffers && - SPA_FLAG_IS_SET(impl->flags, PW_STREAM_FLAG_MAP_BUFFERS) && - impl->direction == SPA_DIRECTION_INPUT) - fix_datatype(param); - p->id = id; p->flags = flags; p->param = SPA_PTROFF(p, sizeof(struct param), struct spa_pod); memcpy(p->param, param, SPA_POD_SIZE(param)); SPA_POD_OBJECT_ID(p->param) = id; + if (id == SPA_PARAM_Buffers && + SPA_FLAG_IS_SET(impl->flags, PW_STREAM_FLAG_MAP_BUFFERS) && + impl->direction == SPA_DIRECTION_INPUT) + fix_datatype(p->param); + spa_list_append(&impl->param_list, &p->link); if ((idx = get_param_index(id)) != -1) { @@ -1960,6 +1963,7 @@ impl->port_paramsPORT_Format = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE); impl->port_paramsPORT_Buffers = SPA_PARAM_INFO(SPA_PARAM_Buffers, 0); impl->port_paramsPORT_Latency = SPA_PARAM_INFO(SPA_PARAM_Latency, SPA_PARAM_INFO_WRITE); + impl->port_paramsPORT_Tag = SPA_PARAM_INFO(SPA_PARAM_Tag, SPA_PARAM_INFO_WRITE); impl->port_info.props = &impl->port_props->dict; impl->port_info.params = impl->port_params; impl->port_info.n_params = N_PORT_PARAMS;
View file
pipewire-0.3.79.tar.gz/src/pipewire/stream.h -> pipewire-0.3.80.tar.gz/src/pipewire/stream.h
Changed
@@ -469,9 +469,7 @@ /** Update the param exposed on the stream. */ int pw_stream_update_params(struct pw_stream *stream, /**< a \ref pw_stream */ - const struct spa_pod **params, /**< an array of params. The params should - * ideally contain parameters for doing - * buffer allocation. */ + const struct spa_pod **params, /**< an array of params. */ uint32_t n_params /**< number of elements in \a params */); /**
View file
pipewire-0.3.79.tar.gz/src/tools/pw-cat.c -> pipewire-0.3.80.tar.gz/src/tools/pw-cat.c
Changed
@@ -20,6 +20,7 @@ #include <spa/param/audio/layout.h> #include <spa/param/audio/format-utils.h> #include <spa/param/audio/type-info.h> +#include <spa/param/tag-utils.h> #include <spa/param/props.h> #include <spa/utils/result.h> #include <spa/utils/string.h> @@ -1569,7 +1570,8 @@ { struct data data = { 0, }; struct pw_loop *l; - const struct spa_pod *params1; + const struct spa_pod *params2; + uint32_t n_params = 0; uint8_t buffer1024; struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer)); const char *prog; @@ -1793,19 +1795,6 @@ } data.filename = argvoptind++; - if (pw_properties_get(data.props, PW_KEY_MEDIA_TYPE) == NULL) - pw_properties_set(data.props, PW_KEY_MEDIA_TYPE, data.media_type); - if (pw_properties_get(data.props, PW_KEY_MEDIA_CATEGORY) == NULL) - pw_properties_set(data.props, PW_KEY_MEDIA_CATEGORY, data.media_category); - if (pw_properties_get(data.props, PW_KEY_MEDIA_ROLE) == NULL) - pw_properties_set(data.props, PW_KEY_MEDIA_ROLE, data.media_role); - if (pw_properties_get(data.props, PW_KEY_MEDIA_FILENAME) == NULL) - pw_properties_set(data.props, PW_KEY_MEDIA_FILENAME, data.filename); - if (pw_properties_get(data.props, PW_KEY_MEDIA_NAME) == NULL) - pw_properties_set(data.props, PW_KEY_MEDIA_NAME, data.filename); - if (pw_properties_get(data.props, PW_KEY_TARGET_OBJECT) == NULL) - pw_properties_set(data.props, PW_KEY_TARGET_OBJECT, data.target); - /* make a main loop. If you already have another main loop, you can add * the fd of this pipewire mainloop to it. */ data.loop = pw_main_loop_new(NULL); @@ -1874,6 +1863,19 @@ } ret = setup_properties(&data); + if (pw_properties_get(data.props, PW_KEY_MEDIA_TYPE) == NULL) + pw_properties_set(data.props, PW_KEY_MEDIA_TYPE, data.media_type); + if (pw_properties_get(data.props, PW_KEY_MEDIA_CATEGORY) == NULL) + pw_properties_set(data.props, PW_KEY_MEDIA_CATEGORY, data.media_category); + if (pw_properties_get(data.props, PW_KEY_MEDIA_ROLE) == NULL) + pw_properties_set(data.props, PW_KEY_MEDIA_ROLE, data.media_role); + if (pw_properties_get(data.props, PW_KEY_MEDIA_FILENAME) == NULL) + pw_properties_set(data.props, PW_KEY_MEDIA_FILENAME, data.filename); + if (pw_properties_get(data.props, PW_KEY_MEDIA_NAME) == NULL) + pw_properties_set(data.props, PW_KEY_MEDIA_NAME, data.filename); + if (pw_properties_get(data.props, PW_KEY_TARGET_OBJECT) == NULL) + pw_properties_set(data.props, PW_KEY_TARGET_OBJECT, data.target); + switch (data.data_type) { #ifdef HAVE_PW_CAT_FFMPEG_INTEGRATION case TYPE_ENCODED: @@ -1886,7 +1888,7 @@ ret = av_codec_params_to_audio_info(&data, data.encoded.audio_stream->codecpar, &info); if (ret < 0) goto error_bad_file; - params0 = spa_format_audio_build(&b, SPA_PARAM_EnumFormat, &info); + paramsn_params++ = spa_format_audio_build(&b, SPA_PARAM_EnumFormat, &info); break; } #endif @@ -1902,11 +1904,11 @@ if (data.channelmap.n_channels) memcpy(info.position, data.channelmap.channels, data.channels * sizeof(int)); - params0 = spa_format_audio_raw_build(&b, SPA_PARAM_EnumFormat, &info); + paramsn_params++ = spa_format_audio_raw_build(&b, SPA_PARAM_EnumFormat, &info); break; } case TYPE_MIDI: - params0 = spa_pod_builder_add_object(&b, + paramsn_params++ = spa_pod_builder_add_object(&b, SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat, SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_application), SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_control)); @@ -1928,10 +1930,25 @@ memcpy(info.position, i->info.position, info.channels * sizeof(uint32_t)); } - params0 = spa_format_audio_dsd_build(&b, SPA_PARAM_EnumFormat, &info); + paramsn_params++ = spa_format_audio_dsd_build(&b, SPA_PARAM_EnumFormat, &info); break; } } + if (data.mode == mode_playback) { + struct spa_dict_item items64; + uint32_t i, n_items = 0; + + for (i = 0; i < data.props->dict.n_items; i++) { + if (spa_strstartswith(data.props->dict.itemsi.key, "media.")) + itemsn_items++ = data.props->dict.itemsi; + } + if (n_items > 0) { + struct spa_pod_frame f; + spa_tag_build_start(&b, &f, SPA_PARAM_Tag, SPA_DIRECTION_OUTPUT); + spa_tag_build_add_dict(&b, &SPA_DICT_INIT(items, n_items)); + paramsn_params++ = spa_tag_build_end(&b, &f); + } + } data.stream = pw_stream_new(data.core, prog, data.props); data.props = NULL; @@ -1955,7 +1972,7 @@ PW_ID_ANY, flags | PW_STREAM_FLAG_MAP_BUFFERS, - params, 1); + params, n_params); if (ret < 0) { fprintf(stderr, "error: failed connect: %s\n", spa_strerror(ret)); goto error_connect_fail;
View file
pipewire-0.3.79.tar.gz/src/tools/pw-mon.c -> pipewire-0.3.80.tar.gz/src/tools/pw-mon.c
Changed
@@ -55,6 +55,9 @@ struct spa_list pending_list; struct spa_list global_list; + + bool hide_params; + bool hide_props; }; struct proxy_data { @@ -152,7 +155,7 @@ spa_list_append(&data->param_list, &p->link); } -static void print_params(struct proxy_data *data, bool use_prefix) +static void print_parameters(struct proxy_data *data, bool use_prefix) { struct param *p; @@ -198,9 +201,12 @@ #define MARK_CHANGE(f) (!!(print_mark && ((info)->change_mask & (f)))) -static void on_core_info(void *data, const struct pw_core_info *info) +static void on_core_info(void *_data, const struct pw_core_info *info) { - bool print_all = true, print_mark = true; + struct proxy_data *data = _data; + bool hide_props, print_mark = true; + + hide_props = data->data->hide_props; printf("\ttype: %s\n", PW_TYPE_INTERFACE_Core); printf("\tcookie: %u\n", info->cookie); @@ -208,22 +214,22 @@ printf("\thost-name: \"%s\"\n", info->host_name); printf("\tversion: \"%s\"\n", info->version); printf("\tname: \"%s\"\n", info->name); - if (print_all) { + if (!hide_props) { print_properties(info->props, MARK_CHANGE(PW_CORE_CHANGE_MASK_PROPS)); } } static void module_event_info(void *_data, const struct pw_module_info *info) { - struct proxy_data *data = _data; - bool print_all, print_mark; + struct proxy_data *data = _data; + bool hide_props, print_mark; - print_all = true; - if (data->info == NULL) { + hide_props = data->data->hide_props; + if (data->info == NULL) { printf("added:\n"); print_mark = false; } - else { + else { printf("changed:\n"); print_mark = true; } @@ -237,7 +243,7 @@ printf("\tname: \"%s\"\n", info->name); printf("\tfilename: \"%s\"\n", info->filename); printf("\targs: \"%s\"\n", info->args); - if (print_all) { + if (!hide_props) { print_properties(info->props, MARK_CHANGE(PW_MODULE_CHANGE_MASK_PROPS)); } } @@ -250,15 +256,17 @@ static void print_node(struct proxy_data *data) { struct pw_node_info *info = data->info; - bool print_all, print_mark; + bool hide_params, hide_props, print_mark; - print_all = true; - if (data->first) { + hide_params = data->data->hide_params; + hide_props = data->data->hide_props; + + if (data->first) { printf("added:\n"); print_mark = false; data->first = false; } - else { + else { printf("changed:\n"); print_mark = true; } @@ -267,8 +275,8 @@ printf("\tpermissions: "PW_PERMISSION_FORMAT"\n", PW_PERMISSION_ARGS(data->permissions)); printf("\ttype: %s (version %d)\n", data->type, data->version); - if (print_all) { - print_params(data, MARK_CHANGE(PW_NODE_CHANGE_MASK_PARAMS)); + if (!hide_params) { + print_parameters(data, MARK_CHANGE(PW_NODE_CHANGE_MASK_PARAMS)); with_prefix(MARK_CHANGE(PW_NODE_CHANGE_MASK_INPUT_PORTS)) { printf("\tinput ports: %u/%u\n", info->n_input_ports, info->max_input_ports); @@ -285,6 +293,9 @@ printf(" \"%s\"\n", info->error); else printf("\n"); + } + + if (!hide_props) { print_properties(info->props, MARK_CHANGE(PW_NODE_CHANGE_MASK_PROPS)); } } @@ -323,15 +334,17 @@ static void print_port(struct proxy_data *data) { struct pw_port_info *info = data->info; - bool print_all, print_mark; + bool hide_params, hide_props, print_mark; + + hide_params = data->data->hide_params; + hide_props = data->data->hide_props; - print_all = true; - if (data->first) { + if (data->first) { printf("added:\n"); print_mark = false; data->first = false; } - else { + else { printf("changed:\n"); print_mark = true; } @@ -342,8 +355,12 @@ printf("\ttype: %s (version %d)\n", data->type, data->version); printf("\tdirection: \"%s\"\n", pw_direction_as_string(info->direction)); - if (print_all) { - print_params(data, MARK_CHANGE(PW_PORT_CHANGE_MASK_PARAMS)); + + if (!hide_params) { + print_parameters(data, MARK_CHANGE(PW_PORT_CHANGE_MASK_PARAMS)); + } + + if (!hide_props) { print_properties(info->props, MARK_CHANGE(PW_PORT_CHANGE_MASK_PROPS)); } } @@ -382,14 +399,15 @@ static void factory_event_info(void *_data, const struct pw_factory_info *info) { struct proxy_data *data = _data; - bool print_all, print_mark; + bool hide_props, print_mark; + + hide_props = data->data->hide_props; - print_all = true; - if (data->info == NULL) { + if (data->info == NULL) { printf("added:\n"); print_mark = false; } - else { + else { printf("changed:\n"); print_mark = true; } @@ -403,7 +421,7 @@ printf("\tname: \"%s\"\n", info->name); printf("\tobject-type: %s/%d\n", info->type, info->version); - if (print_all) { + if (!hide_props) { print_properties(info->props, MARK_CHANGE(PW_FACTORY_CHANGE_MASK_PROPS)); } } @@ -416,14 +434,15 @@ static void client_event_info(void *_data, const struct pw_client_info *info) { struct proxy_data *data = _data; - bool print_all, print_mark; + bool hide_props, print_mark; + + hide_props = data->data->hide_props; - print_all = true; - if (data->info == NULL) { + if (data->info == NULL) { printf("added:\n"); print_mark = false; } - else { + else { printf("changed:\n"); print_mark = true; } @@ -435,7 +454,7 @@ PW_PERMISSION_ARGS(data->permissions)); printf("\ttype: %s (version %d)\n", data->type, data->version); - if (print_all) { + if (!hide_props) { print_properties(info->props, MARK_CHANGE(PW_CLIENT_CHANGE_MASK_PROPS)); } } @@ -448,14 +467,15 @@ static void link_event_info(void *_data, const struct pw_link_info *info) { struct proxy_data *data = _data; - bool print_all, print_mark; + bool hide_props, print_mark; - print_all = true; - if (data->info == NULL) { + hide_props = data->data->hide_props; + + if (data->info == NULL) { printf("added:\n"); print_mark = false; } - else { + else { printf("changed:\n"); print_mark = true; } @@ -471,7 +491,7 @@ printf("\toutput-port-id: %u\n", info->output_port_id); printf("\tinput-node-id: %u\n", info->input_node_id); printf("\tinput-port-id: %u\n", info->input_port_id); - if (print_all) { + if (!hide_props) { with_prefix(MARK_CHANGE(PW_LINK_CHANGE_MASK_STATE)) { printf("\tstate: \"%s\"", pw_link_state_as_string(info->state)); @@ -499,15 +519,17 @@ static void print_device(struct proxy_data *data) { struct pw_device_info *info = data->info; - bool print_all, print_mark; + bool hide_params, hide_props, print_mark; + + hide_params = data->data->hide_params; + hide_props = data->data->hide_props; - print_all = true; - if (data->first) { + if (data->first) { printf("added:\n"); print_mark = false; data->first = false; } - else { + else { printf("changed:\n"); print_mark = true; } @@ -517,8 +539,11 @@ PW_PERMISSION_ARGS(data->permissions)); printf("\ttype: %s (version %d)\n", data->type, data->version); - if (print_all) { - print_params(data, MARK_CHANGE(PW_DEVICE_CHANGE_MASK_PARAMS)); + if (!hide_params) { + print_parameters(data, MARK_CHANGE(PW_DEVICE_CHANGE_MASK_PARAMS)); + } + + if (!hide_props) { print_properties(info->props, MARK_CHANGE(PW_DEVICE_CHANGE_MASK_PROPS)); } } @@ -732,7 +757,9 @@ " --version Show version\n" " -r, --remote Remote daemon name\n" " -N, --no-colors disable color output\n" - " -C, --color=WHEN whether to enable color support. WHEN is `never`, `always`, or `auto`\n", + " -C, --color=WHEN whether to enable color support. WHEN is `never`, `always`, or `auto`\n" + " -o, --hide-props hide node properties\n" + " -a, --hide-params hide node properties\n", name); } @@ -742,11 +769,13 @@ struct pw_loop *l; const char *opt_remote = NULL; static const struct option long_options = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, 'V' }, - { "remote", required_argument, NULL, 'r' }, - { "no-colors", no_argument, NULL, 'N' }, - { "color", optional_argument, NULL, 'C' }, + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'V' }, + { "remote", required_argument, NULL, 'r' }, + { "no-colors", no_argument, NULL, 'N' }, + { "color", optional_argument, NULL, 'C' }, + { "hide-props", no_argument, NULL, 'o' }, + { "hide-params", no_argument, NULL, 'a' }, { NULL, 0, NULL, 0} }; int c; @@ -760,7 +789,7 @@ if (isatty(STDOUT_FILENO) && getenv("NO_COLOR") == NULL) colors = true; - while ((c = getopt_long(argc, argv, "hVr:NC", long_options, NULL)) != -1) { + while ((c = getopt_long(argc, argv, "hVr:NCoa", long_options, NULL)) != -1) { switch (c) { case 'h': show_help(argv0, false); @@ -793,6 +822,12 @@ return -1; } break; + case 'o': + data.hide_props = true; + break; + case 'a': + data.hide_params = true; + break; default: show_help(argv0, true); return -1;
View file
pipewire-0.3.79.tar.gz/src/tools/pw-top.c -> pipewire-0.3.80.tar.gz/src/tools/pw-top.c
Changed
@@ -721,8 +721,8 @@ { fprintf(error ? stderr : stdout, "Usage:\n%s options\n\n" "Options:\n" - " -b, --batch-mode run in non-interactive batch_mode mode\n" - " -n, --iterations = NUMBER exit on maximum iterations NUMBER\n" + " -b, --batch-mode run in non-interactive batch mode\n" + " -n, --iterations = NUMBER exit after NUMBER batch iterations\n" " -r, --remote Remote daemon name\n" "\n" " -h, --help Show this help\n"
View file
pipewire-0.3.80.tar.gz/subprojects/webrtc-audio-processing.wrap
Added
@@ -0,0 +1,8 @@ +wrap-git +directory = webrtc-audio-processing +url = https://gitlab.freedesktop.org/pulseaudio/webrtc-audio-processing.git +push-url = git@gitlab.freedesktop.org:pulseaudio/webrtc-audio-processing.git +revision = v1.3 + +provide +dependency_names = webrtc-audio-coding-1, webrtc-audio-processing-1
View file
pipewire-0.3.79.tar.gz/test/test-spa-utils.c -> pipewire-0.3.80.tar.gz/test/test-spa-utils.c
Changed
@@ -125,7 +125,8 @@ pwtest_int_eq(SPA_TYPE_OBJECT_Profiler, 0x4000a); pwtest_int_eq(SPA_TYPE_OBJECT_ParamLatency, 0x4000b); pwtest_int_eq(SPA_TYPE_OBJECT_ParamProcessLatency, 0x4000c); - pwtest_int_eq(_SPA_TYPE_OBJECT_LAST, 0x4000d); + pwtest_int_eq(SPA_TYPE_OBJECT_ParamTag, 0x4000d); + pwtest_int_eq(_SPA_TYPE_OBJECT_LAST, 0x4000e); pwtest_int_eq(SPA_TYPE_VENDOR_PipeWire, 0x02000000); pwtest_int_eq(SPA_TYPE_VENDOR_Other, 0x7f000000);
Locations
Projects
Search
Status Monitor
Help
Open Build Service
OBS Manuals
API Documentation
OBS Portal
Reporting a Bug
Contact
Mailing List
Forums
Chat (IRC)
Twitter
Open Build Service (OBS)
is an
openSUSE project
.