Projects
Essentials
pipewire-aptx
Sign Up
Log In
Username
Password
We truncated the diff of some files because they were too big. If you want to see the full diff for every file,
click here
.
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
Expand all
Collapse all
Changes of Revision 13
View file
pipewire-0.3.54.tar.gz/src/daemon/filter-chain/duplicate-FL.conf
Deleted
@@ -1,49 +0,0 @@ -# An example filter chain for duplicating the FL channel -# to FL and FR. -# -# Copy this file into a conf.d/ directory -# -context.modules = - { name = libpipewire-module-filter-chain - args = { - node.description = "Remap example" - media.name = "Remap example" - filter.graph = { - nodes = - { - name = copyIL - type = builtin - label = copy - } - { - name = copyOL - type = builtin - label = copy - } - { - name = copyOR - type = builtin - label = copy - } - - links = - # we can only tee from nodes, not inputs so we need - # to copy the inputs and then tee. - { output = "copyIL:Out" input = "copyOL:In" } - { output = "copyIL:Out" input = "copyOR:In" } - - inputs = "copyIL:In" - outputs = "copyOL:Out" "copyOR:Out" - } - capture.props = { - node.name = "remap_input.remap-FL-to-FL-FR" - audio.position = FL - stream.dont-remix = true - } - playback.props = { - node.name = "remap_output.remap-FL-to-FL-FR" - audio.position = FL FR - } - } - } -
View file
pipewire-0.3.54.tar.gz/NEWS -> pipewire-0.3.56.tar.gz/NEWS
Changed
@@ -1,3 +1,113 @@ +# PipeWire 0.3.56 (2022-07-19) + +This is a quick bugfix release that is API and ABI compatible with previous +0.3.x releases. + +## Highlights + - A critical bug that could crash JACK apps was fixed. + - Some more regressions in audiomixer were fixed. This should fix crackling + and stuttering in some cases as well as some channel mapping regressions. + - A bug in the alsa plugin was fixed that could cause stuttering in VMs. + - Bluetooth sources should have improved latency and rate control. + - Many more bugfixes and improvements. + + +## Modules + - An experimental AVB module was added. It can expose PipeWire as an AVB + entity and initiate (broken) streaming between entities. + - module-loopback now handles the cases where the input and output channels + are different without crashing or producing silence. + - The filter-chain module now correctly calculates the output size without + crashing in some cases. It also skips invalid ports instead of crashing. + - Handle and report pthread errors better. + +## SPA + - The resampler qualities were tweaked a little. + - A bug that would sometimes cut off the last part of a buffer was fixed in + the alsa plugin. This could cause broken audio in VMs. (#2536) + - Access to the alsa mixer and devices is now checked more thoroughly. + (#2534) + - The spa-resample tool can now also handle large downsampling rates without + crashing. + - Audioconverter now uses rounding for float to int conversions, which + reduces distortions. Compilation of the c functions was separated and uses + its own optimization flags now. Unit tests were added. (#2543) + - Noise shaping was improved in audioconvert. A new Wannamaker 3 tap shaper + was added. + - Audioconvert now uses a pattern for generating keep alive noise. This + should have much less energy and be even more inaudible. (#2540) + - A channel mapping bug was fixed in audioconvert. Unit tests were added. + - The dsp audio mixer would sometimes not mix enough and cause dropouts. + (#2525) + +## JACK + - A critical bug in the mixer was fixed. It would cause most JACK apps to + segfault at startup. + +## Bluetooth + - A new rate control algorithm was implemented for the sources. + - The media role on HSP/HFP streams is now fixed. + +## Pulse Server + - Add the resampler delay to delay reporting as well. + + +Older versions: + +# PipeWire 0.3.55 (2022-07-12) + +This is a quick bugfix release that is API and ABI compatible with previous +0.3.x releases. + +## Highlights + - Fix some more critical bugs in the new audioconvert and the queueing + in pw-stream that causes stuttering and hickups. + - HFP hardware volumes are now saved and restored. + - Format conversions and mixing was improved. + - Small bug fixes and improvements. + +## PipeWire + - The queueing in pw-stream was improved with support for buffer prefetch + in async mode. + - Add a pw-filter unit test. + +## tools + - pw-midiplay should now work again after improvements in pw-stream. + +## modules + - The RAOP module was improved to support auth_setup. + - The RAOP module should now handle timing packets better. + - Add some more filter-chain examples. + - The filter-chain now has a separate config file with the boilerplate + settings. The examples are now just config snippets that can be dropped + in .conf.d/ directories, such as the filter-chain.conf.d/ one. + - Start suggesting to use target.object instead of node.target in docs + and examples. + +## SPA + - Use the cosh window again for the resampler. It should now + give better resampler quality. (#2483) + - Rework the mixer functions. They were rewritten for higher precision and + better performance. Add unit tests and benchmarks. + - Improve format conversion for 32bits for avoid errors in clang because + of undefined behaviour at extreme ranges. + - Fix a bug in audioconvert where it would not consume the right + amount of samples when the resampler was disabled. This could cause + skipping and hickups. (#2519) + - Fix bug in audioconvert where it would try to convert the input samples + multiple times, causing strange artifacts when upmixing. + - Be more strict about valid JSON floats. + - device.vendor.id and device.product.id should now always show up in + 0xXXXX format and should not be converted to floats in pw-dump anymore. + - Add triangular dither, add unit tests for noise generation, add some + more optimizations. + +## Bluetooth + - HFP and A2DP now expose different routes and thus can have different + volumes. + - HW Volumes for HFP are now synced better. Volume changes from HW buttons + are now also saved. + # PipeWire 0.3.54 (2022-07-07) This is a quick bugfix release that is API and ABI compatible with previous @@ -47,9 +157,6 @@ - The source was rewritten to use a ringbuffer. This avoids regressions caused by audioconvert. -Older versions: - - # PipeWire 0.3.53 (2022-06-30) This is a bugfix release that is API and ABI compatible with previous
View file
pipewire-0.3.54.tar.gz/doc/pipewire-modules.dox -> pipewire-0.3.56.tar.gz/doc/pipewire-modules.dox
Changed
@@ -51,6 +51,7 @@ - \subpage page_module_access - \subpage page_module_adapter +- \subpage page_module_avb - \subpage page_module_client_device - \subpage page_module_client_node - \subpage page_module_echo_cancel
View file
pipewire-0.3.54.tar.gz/doc/tutorial4.c -> pipewire-0.3.56.tar.gz/doc/tutorial4.c
Changed
@@ -96,7 +96,7 @@ pw_stream_connect(data.stream, PW_DIRECTION_OUTPUT, - argc > 1 ? (uint32_t)atoi(argv1) : PW_ID_ANY, + PW_ID_ANY, PW_STREAM_FLAG_AUTOCONNECT | PW_STREAM_FLAG_MAP_BUFFERS | PW_STREAM_FLAG_RT_PROCESS,
View file
pipewire-0.3.54.tar.gz/doc/tutorial4.dox -> pipewire-0.3.56.tar.gz/doc/tutorial4.dox
Changed
@@ -118,8 +118,8 @@ pw_main_loop_run(data.loop); \endcode -To connect we specify that we have a `PW_DIRECTION_OUTPUT` stream. `PW_ID_ANY` -means that we are ok with connecting to any consumer. Next we set some flags: +To connect we specify that we have a `PW_DIRECTION_OUTPUT` stream. The third argument +is always `PW_ID_ANY`. Next we set some flags: - `PW_STREAM_FLAG_AUTOCONNECT`: Automatically connect this stream. This instructs the session manager to link us to some consumer.
View file
pipewire-0.3.54.tar.gz/doc/tutorial5.c -> pipewire-0.3.56.tar.gz/doc/tutorial5.c
Changed
@@ -83,19 +83,23 @@ const struct spa_pod *params1; uint8_t buffer1024; struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer)); + struct pw_properties *props; pw_init(&argc, &argv); data.loop = pw_main_loop_new(NULL); + props = pw_properties_new(PW_KEY_MEDIA_TYPE, "Video", + PW_KEY_MEDIA_CATEGORY, "Capture", + PW_KEY_MEDIA_ROLE, "Camera", + NULL); + if (argc > 1) + pw_properties_set(props, PW_KEY_TARGET_OBJECT, argv1); + data.stream = pw_stream_new_simple( pw_main_loop_get_loop(data.loop), "video-capture", - pw_properties_new( - PW_KEY_MEDIA_TYPE, "Video", - PW_KEY_MEDIA_CATEGORY, "Capture", - PW_KEY_MEDIA_ROLE, "Camera", - NULL), + props, &stream_events, &data); @@ -122,7 +126,7 @@ pw_stream_connect(data.stream, PW_DIRECTION_INPUT, - argc > 1 ? (uint32_t)atoi(argv1) : PW_ID_ANY, + PW_ID_ANY, PW_STREAM_FLAG_AUTOCONNECT | PW_STREAM_FLAG_MAP_BUFFERS, params, 1);
View file
pipewire-0.3.54.tar.gz/doc/tutorial5.dox -> pipewire-0.3.56.tar.gz/doc/tutorial5.dox
Changed
@@ -23,18 +23,25 @@ Video Capture stream. \code{.c} + props = pw_properties_new(PW_KEY_MEDIA_TYPE, "Video", + PW_KEY_MEDIA_CATEGORY, "Capture", + PW_KEY_MEDIA_ROLE, "Camera", + NULL); + if (argc > 1) + pw_properties_set(props, PW_KEY_TARGET_OBJECT, argv1); + data.stream = pw_stream_new_simple( pw_main_loop_get_loop(data.loop), "video-capture", - pw_properties_new( - PW_KEY_MEDIA_TYPE, "Video", - PW_KEY_MEDIA_CATEGORY, "Capture", - PW_KEY_MEDIA_ROLE, "Camera", - NULL), + props, &stream_events, &data); \endcode +We also optionally allow the user to pass the name of the target node where the session +manager is supposed to connect the node. The user may also give the value of the +unique target node serial (`PW_KEY_OBJECT_SERIAL`) as the value. + In addition to the `process` event, we are also going to listen to a new event, `param_changed`: @@ -122,7 +129,7 @@ \code{.c} pw_stream_connect(data.stream, PW_DIRECTION_INPUT, - argc > 1 ? (uint32_t)atoi(argv1) : PW_ID_ANY, + PW_ID_ANY, PW_STREAM_FLAG_AUTOCONNECT | PW_STREAM_FLAG_MAP_BUFFERS, params, 1); @@ -130,9 +137,8 @@ pw_main_loop_run(data.loop); \endcode -To connect we specify that we have a `PW_DIRECTION_INPUT` stream. `PW_ID_ANY` -means that we are ok with connecting to any producer. We also allow the user -to pass an optional target id. +To connect we specify that we have a `PW_DIRECTION_INPUT` stream. The third +argument is always `PW_ID_ANY`. We're setting the `PW_STREAM_FLAG_AUTOCONNECT` flag to make an automatic connection to a suitable camera and `PW_STREAM_FLAG_MAP_BUFFERS` to let the
View file
pipewire-0.3.54.tar.gz/meson.build -> pipewire-0.3.56.tar.gz/meson.build
Changed
@@ -1,5 +1,5 @@ project('pipewire', 'c' , - version : '0.3.54', + version : '0.3.56', license : 'MIT', 'LGPL-2.1-or-later', 'GPL-2.0-only' , meson_version : '>= 0.59.0', default_options : 'warning_level=3',
View file
pipewire-0.3.54.tar.gz/meson_options.txt -> pipewire-0.3.56.tar.gz/meson_options.txt
Changed
@@ -245,3 +245,7 @@ description: 'Build legacy rtkit module', type: 'boolean', value: 'true') +option('avb', + description: 'Enable AVB code', + type: 'feature', + value: 'auto')
View file
pipewire-0.3.54.tar.gz/pipewire-jack/src/meson.build -> pipewire-0.3.56.tar.gz/pipewire-jack/src/meson.build
Changed
@@ -78,7 +78,7 @@ endif pkgconfig.generate(filebase : 'jack', - libraries : pipewire_jack, pipewire_jackserver, + libraries : pipewire_jack, name : 'jack', description : 'PipeWire JACK API', version : '1.9.17',
View file
pipewire-0.3.54.tar.gz/pipewire-jack/src/metadata.c -> pipewire-0.3.56.tar.gz/pipewire-jack/src/metadata.c
Changed
@@ -210,7 +210,7 @@ pthread_mutex_unlock(&globals.lock); if (c->property_callback && changed > 0) { - pw_log_info("emit %lu %s", subject, key); + pw_log_info("emit %"PRIu64" %s", (uint64_t)subject, key); c->property_callback(subject, key, change, c->property_arg); } return changed;
View file
pipewire-0.3.54.tar.gz/pipewire-jack/src/pipewire-jack.c -> pipewire-0.3.56.tar.gz/pipewire-jack/src/pipewire-jack.c
Changed
@@ -67,7 +67,7 @@ #define JACK_PORT_TYPE_SIZE 32 #define MONITOR_EXT " Monitor" -#define MAX_MIDI_MIX 1024 +#define MAX_MIX 1024 #define MAX_BUFFER_FRAMES 8192 #define MAX_ALIGN 16 @@ -106,9 +106,9 @@ #define OBJECT_CHUNK 8 #define RECYCLE_THRESHOLD 128 -typedef void (*mix2_func) (float *dst, float *src1, float *src2, int n_samples); +typedef void (*mix_func) (float *dst, float *src, uint32_t n_src, bool aligned, uint32_t n_samples); -static mix2_func mix2; +static mix_func mix_function; struct object { struct spa_list link; @@ -736,38 +736,40 @@ #if defined (__SSE__) #include <xmmintrin.h> -static void mix2_sse(float *dst, float *src1, float *src2, int n_samples) +static void mix_sse(float *dst, float *src, uint32_t n_src, bool aligned, uint32_t n_samples) { - int n, unrolled; - __m128 in2; + uint32_t i, n, unrolled; + __m128 in1; - if (SPA_IS_ALIGNED(src1, 16) && - SPA_IS_ALIGNED(src2, 16) && - SPA_IS_ALIGNED(dst, 16)) - unrolled = n_samples / 4; + if (SPA_IS_ALIGNED(dst, 16) && aligned) + unrolled = n_samples & ~3; else unrolled = 0; - for (n = 0; unrolled--; n += 4) { - in0 = _mm_load_ps(&src1n), - in1 = _mm_load_ps(&src2n), - in0 = _mm_add_ps(in0, in1); + for (n = 0; n < unrolled; n += 4) { + in0 = _mm_load_ps(&src0n); + for (i = 1; i < n_src; i++) + in0 = _mm_add_ps(in0, _mm_load_ps(&srcin)); _mm_store_ps(&dstn, in0); } for (; n < n_samples; n++) { - in0 = _mm_load_ss(&src1n), - in1 = _mm_load_ss(&src2n), - in0 = _mm_add_ss(in0, in1); + in0 = _mm_load_ss(&src0n); + for (i = 1; i < n_src; i++) + in0 = _mm_add_ss(in0, _mm_load_ss(&srcin)); _mm_store_ss(&dstn, in0); } } #endif -static void mix2_c(float *dst, float *src1, float *src2, int n_samples) +static void mix_c(float *dst, float *src, uint32_t n_src, bool aligned, uint32_t n_samples) { - int i; - for (i = 0; i < n_samples; i++) - dsti = src1i + src2i; + uint32_t n, i; + for (n = 0; n < n_samples; n++) { + float t = src0n; + for (i = 1; i < n_src; i++) + t += srcin; + dstn = t; + } } SPA_EXPORT @@ -3281,13 +3283,13 @@ support = pw_context_get_support(client->context.context, &n_support); - mix2 = mix2_c; + mix_function = mix_c; cpu_iface = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_CPU); if (cpu_iface) { #if defined (__SSE__) uint32_t flags = spa_cpu_get_flags(cpu_iface); if (flags & SPA_CPU_FLAG_SSE) - mix2 = mix2_sse; + mix_function = mix_sse; #endif } client->context.old_thread_utils = @@ -4416,13 +4418,14 @@ { struct mix *mix; struct buffer *b; - int layer = 0; void *ptr = NULL; + float *mix_ptrMAX_MIX, *np; + uint32_t n_ptr = 0; + bool ptr_aligned = true; spa_list_for_each(mix, &p->mix, port_link) { struct spa_data *d; uint32_t offset, size; - void *np; pw_log_trace_fp("%p: port %s mix %d.%d get buffer %d", p->client, p->object->port.name, p->port_id, mix->id, frames); @@ -4436,14 +4439,20 @@ if (size / sizeof(float) < frames) continue; - np = SPA_PTROFF(d->data, offset, void); - if (layer++ == 0) { - ptr = np; - } else { - mix2(p->emptyptr, ptr, np, frames); - ptr = p->emptyptr; - p->zeroed = false; - } + np = SPA_PTROFF(d->data, offset, float); + if (!SPA_IS_ALIGNED(np, 16)) + ptr_aligned = false; + + mix_ptrn_ptr++ = np; + if (n_ptr == MAX_MIX) + break; + } + if (n_ptr == 1) { + ptr = mix_ptr0; + } else if (n_ptr > 1) { + ptr = p->emptyptr; + mix_function(ptr, mix_ptr, n_ptr, ptr_aligned, frames); + p->zeroed = false; } if (ptr == NULL) ptr = init_buffer(p); @@ -4454,7 +4463,7 @@ { struct mix *mix; void *ptr = p->emptyptr; - struct spa_pod_sequence *seqMAX_MIDI_MIX; + struct spa_pod_sequence *seqMAX_MIX; uint32_t n_seq = 0; jack_midi_clear_buffer(ptr); @@ -4478,7 +4487,7 @@ continue; seqn_seq++ = pod; - if (n_seq == MAX_MIDI_MIX) + if (n_seq == MAX_MIX) break; } convert_to_midi(seq, n_seq, ptr, p->client->fix_midi_events);
View file
pipewire-0.3.54.tar.gz/pipewire-v4l2/src/v4l2-func.c -> pipewire-0.3.56.tar.gz/pipewire-v4l2/src/v4l2-func.c
Changed
@@ -22,6 +22,16 @@ * DEALINGS IN THE SOFTWARE. */ + +/* + * We need to export open* etc., but _FORTIFY_SOURCE defines conflicting + * always_inline versions. Disable _FORTIFY_SOURCE for this file, so we + * can define our overrides. + */ +#ifdef _FORTIFY_SOURCE +#undef _FORTIFY_SOURCE +#endif + #include <stdio.h> #include <errno.h> #include <fcntl.h>
View file
pipewire-0.3.54.tar.gz/spa/include/spa/node/node.h -> pipewire-0.3.56.tar.gz/spa/include/spa/node/node.h
Changed
@@ -632,6 +632,12 @@ * * When the node can accept new input in the next cycle, the * SPA_STATUS_NEED_DATA bit will be set. + * + * Note that the node might return SPA_STATUS_NEED_DATA even when + * no input ports have this status. This means that the amount of + * data still available on the input ports is likely not going to + * be enough for the next cycle and the host might need to prefetch + * data for the next cycle. */ int (*process) (void *object); };
View file
pipewire-0.3.54.tar.gz/spa/include/spa/utils/defs.h -> pipewire-0.3.56.tar.gz/spa/include/spa/utils/defs.h
Changed
@@ -131,13 +131,13 @@ ({ \ __typeof__(a) _min_a = (a); \ __typeof__(b) _min_b = (b); \ - SPA_LIKELY(_min_a < _min_b) ? _min_a : _min_b; \ + SPA_LIKELY(_min_a <= _min_b) ? _min_a : _min_b; \ }) #define SPA_MAX(a,b) \ ({ \ __typeof__(a) _max_a = (a); \ __typeof__(b) _max_b = (b); \ - SPA_LIKELY(_max_a > _max_b) ? _max_a : _max_b; \ + SPA_LIKELY(_max_a >= _max_b) ? _max_a : _max_b; \ }) #define SPA_CLAMP(v,low,high) \ ({ \
View file
pipewire-0.3.54.tar.gz/spa/include/spa/utils/json.h -> pipewire-0.3.56.tar.gz/spa/include/spa/utils/json.h
Changed
@@ -240,6 +240,8 @@ static inline int spa_json_parse_float(const char *val, int len, float *result) { char *end; + if (strspn(val, "+-0123456789.Ee") < (size_t)len) + return 0; *result = spa_strtof(val, &end); return len > 0 && end == val + len; }
View file
pipewire-0.3.54.tar.gz/spa/plugins/alsa/alsa-pcm.c -> pipewire-0.3.56.tar.gz/spa/plugins/alsa/alsa-pcm.c
Changed
@@ -1971,13 +1971,14 @@ size_t n_bytes, n_frames; struct buffer *b; struct spa_data *d; - uint32_t i, offs, size; + uint32_t i, offs, size, last_offset; b = spa_list_first(&state->ready, struct buffer, link); d = b->buf->datas; offs = d0.chunk->offset + state->ready_offset; - size = d0.chunk->size - state->ready_offset; + last_offset = d0.chunk->size; + size = last_offset - state->ready_offset; offs = SPA_MIN(offs, d0.maxsize); size = SPA_MIN(d0.maxsize - offs, size); @@ -2003,7 +2004,7 @@ state->ready_offset += n_bytes; - if (state->ready_offset >= size) { + if (state->ready_offset >= last_offset) { spa_list_remove(&b->link); SPA_FLAG_SET(b->flags, BUFFER_FLAG_OUT); state->io->buffer_id = b->id;
View file
pipewire-0.3.54.tar.gz/spa/plugins/alsa/alsa-udev.c -> pipewire-0.3.56.tar.gz/spa/plugins/alsa/alsa-udev.c
Changed
@@ -477,9 +477,14 @@ 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) - itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_VENDOR_ID, 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); + } + } str = udev_device_get_property_value(dev, "ID_VENDOR_FROM_DATABASE"); if (!(str && *str)) { str = udev_device_get_property_value(dev, "ID_VENDOR_ENC"); @@ -494,9 +499,14 @@ 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) - itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_PRODUCT_ID, 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); + } + } str = udev_device_get_property_value(dev, "ID_MODEL_FROM_DATABASE"); if (!(str && *str)) { str = udev_device_get_property_value(dev, "ID_MODEL_ENC"); @@ -528,11 +538,35 @@ static bool check_access(struct impl *this, struct device *device) { - char path128; - bool accessible; + char path128, prefix32; + DIR *snd = NULL; + struct dirent *entry; + bool accessible = false; snprintf(path, sizeof(path), "/dev/snd/controlC%u", device->id); - accessible = access(path, R_OK|W_OK) >= 0; + if (access(path, R_OK|W_OK) >= 0 && (snd = opendir("/dev/snd"))) { + /* + * It's possible that controlCX is accessible before pcmCX* or + * the other way around. Return true only if all devices are + * accessible. + */ + + accessible = true; + spa_scnprintf(prefix, sizeof(prefix), "pcmC%uD", device->id); + while ((entry = readdir(snd)) != NULL) { + if (!(entry->d_type == DT_CHR && + spa_strstartswith(entry->d_name, prefix))) + continue; + + snprintf(path, sizeof(path), "/dev/snd/%.32s", entry->d_name); + if (access(path, R_OK|W_OK) < 0) { + accessible = false; + break; + } + } + closedir(snd); + } + if (accessible != device->accessible) spa_log_debug(this->log, "%s accessible:%u", path, accessible); device->accessible = accessible; @@ -645,10 +679,6 @@ /* Device becomes accessible or not busy */ if ((event->mask & (IN_ATTRIB | IN_CLOSE_WRITE))) { bool access; - - if ((event->mask & IN_ATTRIB) && - spa_strstartswith(event->name, "pcm")) - continue; if (sscanf(event->name, "controlC%u", &id) != 1 && sscanf(event->name, "pcmC%uD", &id) != 1) continue;
View file
pipewire-0.3.54.tar.gz/spa/plugins/audioconvert/audioconvert.c -> pipewire-0.3.56.tar.gz/spa/plugins/audioconvert/audioconvert.c
Changed
@@ -628,8 +628,8 @@ param = spa_pod_builder_add_object(&b, SPA_TYPE_OBJECT_PropInfo, id, SPA_PROP_INFO_name, SPA_POD_String("dither.noise"), - SPA_PROP_INFO_description, SPA_POD_String("Add dithering noise"), - SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Int(this->dir1.conv.noise, 0, 16), + SPA_PROP_INFO_description, SPA_POD_String("Add noise bits"), + SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Int(this->dir1.conv.noise_bits, 0, 16), SPA_PROP_INFO_params, SPA_POD_Bool(true)); break; case 23: @@ -643,7 +643,7 @@ 0); spa_pod_builder_prop(&b, SPA_PROP_INFO_labels, 0); spa_pod_builder_push_struct(&b, &f1); - for (i = 0; i < SPA_N_ELEMENTS(channelmix_upmix_info); i++) { + for (i = 0; i < SPA_N_ELEMENTS(dither_method_info); i++) { spa_pod_builder_string(&b, dither_method_infoi.label); spa_pod_builder_string(&b, dither_method_infoi.description); } @@ -719,7 +719,7 @@ spa_pod_builder_string(&b, "resample.disable"); spa_pod_builder_bool(&b, p->resample_disabled); spa_pod_builder_string(&b, "dither.noise"); - spa_pod_builder_int(&b, this->dir1.conv.noise); + spa_pod_builder_int(&b, this->dir1.conv.noise_bits); spa_pod_builder_string(&b, "dither.method"); spa_pod_builder_string(&b, dither_method_infothis->dir1.conv.method.label); spa_pod_builder_pop(&b, &f1); @@ -792,7 +792,7 @@ else if (spa_streq(k, "resample.disable")) this->props.resample_disabled = spa_atob(s); else if (spa_streq(k, "dither.noise")) - spa_atou32(s, &this->dir1.conv.noise, 0); + spa_atou32(s, &this->dir1.conv.noise_bits, 0); else if (spa_streq(k, "dither.method")) this->dir1.conv.method = dither_method_from_label(s); else @@ -1452,7 +1452,7 @@ spa_log_debug(this->log, "%p: got converter features %08x:%08x quant:%d:%d" " passthrough:%d remap:%d %s", this, this->cpu_flags, out->conv.cpu_flags, out->conv.method, - out->conv.noise, out->conv.is_passthrough, remap, out->conv.func_name); + out->conv.noise_bits, out->conv.is_passthrough, remap, out->conv.func_name); return 0; } @@ -2246,6 +2246,33 @@ struct spa_io_buffers *io, *ctrlio = NULL; const struct spa_pod_sequence *ctrl = NULL; + /* calculate quantum scale, this is how many samples we need to produce or + * consume. Also update the rate scale, this is sent to the resampler to adjust + * the rate, either when the graph clock changed or when the user adjusted the + * rate. */ + if (SPA_LIKELY(this->io_position)) { + double r = this->rate_scale; + + quant_samples = this->io_position->clock.duration; + if (this->direction == SPA_DIRECTION_INPUT) { + if (this->io_position->clock.rate.denom != this->resample.o_rate) + r = (double) this->io_position->clock.rate.denom / this->resample.o_rate; + else + r = 1.0; + } else { + if (this->io_position->clock.rate.denom != this->resample.i_rate) + r = (double) this->resample.i_rate / this->io_position->clock.rate.denom; + else + r = 1.0; + } + if (this->rate_scale != r) { + spa_log_info(this->log, "scale %f->%f", this->rate_scale, r); + this->rate_scale = r; + } + } + else + quant_samples = this->quantum_limit; + dir = &this->dirSPA_DIRECTION_INPUT; in_passthrough = dir->conv.is_passthrough; max_in = UINT32_MAX; @@ -2335,57 +2362,17 @@ } } - /* calculate quantum scale */ - if (SPA_LIKELY(this->io_position)) { - double r = this->rate_scale; - - quant_samples = this->io_position->clock.duration; - if (this->direction == SPA_DIRECTION_INPUT) { - if (this->io_position->clock.rate.denom != this->resample.o_rate) - r = (double) this->io_position->clock.rate.denom / this->resample.o_rate; - else - r = 1.0; - } else { - if (this->io_position->clock.rate.denom != this->resample.i_rate) - r = (double) this->resample.i_rate / this->io_position->clock.rate.denom; - else - r = 1.0; - } - if (this->rate_scale != r) { - spa_log_info(this->log, "scale %f->%f", this->rate_scale, r); - this->rate_scale = r; - } - } - else - quant_samples = this->quantum_limit; - - if (SPA_UNLIKELY(draining)) - n_samples = SPA_MIN(max_in, this->quantum_limit); - else { - n_samples = max_in - SPA_MIN(max_in, this->in_offset); - } - - resample_passthrough = resample_is_passthrough(this); - + /* calculate how many samples we are going to produce. */ if (this->direction == SPA_DIRECTION_INPUT) { - uint32_t out = resample_update_rate_match(this, resample_passthrough, quant_samples, 0); - if (!in_avail || this->drained) { - spa_log_trace_fp(this->log, "%p: no input drained:%d", this, this->drained); - /* no input, ask for more */ - res |= this->drained ? SPA_STATUS_DRAINED : SPA_STATUS_NEED_DATA; - return res; - } /* in split mode we need to output exactly the size of the * duration so we don't try to flush early */ - n_samples = SPA_MIN(n_samples, out); max_out = quant_samples; flush_out = false; } else { /* in merge mode we consume one duration of samples and * always output the resulting data */ - n_samples = SPA_MIN(n_samples, quant_samples); max_out = this->quantum_limit; - flush_out = flush_in = true; + flush_out = true; } dir = &this->dirSPA_DIRECTION_OUTPUT; @@ -2418,6 +2405,7 @@ dst_datasremap = SPA_PTR_ALIGN(this->scratch, MAX_ALIGN, void); spa_log_trace_fp(this->log, "%p: empty output %d->%d", this, i * port->blocks + j, remap); + max_out = SPA_MIN(max_out, this->empty_size / port->stride); } } } else { @@ -2436,7 +2424,7 @@ volume *= this->props.channel.mute ? 0.0f : this->props.channel.volumesremap; - mon_max = SPA_MIN(bd->maxsize / port->stride, n_samples); + mon_max = SPA_MIN(bd->maxsize / port->stride, max_in); volume_process(&this->volume, bd->data, src_datasremap, volume, mon_max); @@ -2465,6 +2453,39 @@ } } } + + + /* calculate how many samples at most we are going to consume. If we're + * draining, we consume as much as we can. Otherwise we consume what is + * left. */ + if (SPA_UNLIKELY(draining)) + n_samples = SPA_MIN(max_in, this->quantum_limit); + else { + n_samples = max_in - SPA_MIN(max_in, this->in_offset); + } + /* we only need to output the remaining samples */ + n_out = max_out - SPA_MIN(max_out, this->out_offset); + + resample_passthrough = resample_is_passthrough(this); + + /* calculate how many samples we are going to consume. */ + if (this->direction == SPA_DIRECTION_INPUT) { + uint32_t n_in; + /* then figure out how much input samples we need to consume */ + n_in = resample_update_rate_match(this, resample_passthrough, n_out, 0); + if (!in_avail || this->drained) { + spa_log_trace_fp(this->log, "%p: no input drained:%d", this, this->drained); + /* no input, ask for more */ + res |= this->drained ? SPA_STATUS_DRAINED : SPA_STATUS_NEED_DATA; + return res; + } + n_samples = SPA_MIN(n_samples, n_in); + } else { + /* in merge mode we consume one duration of samples */ + n_samples = SPA_MIN(n_samples, quant_samples); + flush_in = true; + } + mix_passthrough = SPA_FLAG_IS_SET(this->mix.flags, CHANNELMIX_FLAG_IDENTITY) && (ctrlport == NULL || ctrlport->ctrl == NULL); @@ -2482,27 +2503,35 @@ dst_remap = (void **)dst_datas;
View file
pipewire-0.3.54.tar.gz/spa/plugins/audioconvert/biquad.c -> pipewire-0.3.56.tar.gz/spa/plugins/audioconvert/biquad.c
Changed
@@ -9,8 +9,6 @@ */ -#include "config.h" - #include <spa/utils/defs.h> #include <math.h>
View file
pipewire-0.3.54.tar.gz/spa/plugins/audioconvert/crossover.c -> pipewire-0.3.56.tar.gz/spa/plugins/audioconvert/crossover.c
Changed
@@ -3,8 +3,6 @@ * found in the LICENSE file. */ -#include "config.h" - #include <float.h> #include <string.h>
View file
pipewire-0.3.54.tar.gz/spa/plugins/audioconvert/fmt-ops-avx2.c -> pipewire-0.3.56.tar.gz/spa/plugins/audioconvert/fmt-ops-avx2.c
Changed
@@ -34,6 +34,15 @@ # define _mm256_setr_m128i(v0, v1) _mm256_set_m128i((v1), (v0)) #endif +#define _MM_CLAMP_PS(r,min,max) \ + _mm_min_ps(_mm_max_ps(r, min), max) + +#define _MM256_CLAMP_PS(r,min,max) \ + _mm256_min_ps(_mm256_max_ps(r, min), max) + +#define _MM_CLAMP_SS(r,min,max) \ + _mm_min_ss(_mm_max_ss(r, min), max) + static void conv_s16_to_f32d_1s_avx2(void *data, void * SPA_RESTRICT dst, const void * SPA_RESTRICT src, uint32_t n_channels, uint32_t n_samples) @@ -538,8 +547,9 @@ uint32_t n, unrolled; __m128 in1; __m128i out4; - __m128 scale = _mm_set1_ps(S32_SCALE); - __m128 int_max = _mm_set1_ps(S32_MAX); + __m128 scale = _mm_set1_ps(S24_SCALE); + __m128 int_max = _mm_set1_ps(S24_MAX); + __m128 int_min = _mm_set1_ps(S24_MIN); if (SPA_IS_ALIGNED(s0, 16)) unrolled = n_samples & ~3; @@ -548,8 +558,9 @@ for(n = 0; n < unrolled; n += 4) { in0 = _mm_mul_ps(_mm_load_ps(&s0n), scale); - in0 = _mm_min_ps(in0, int_max); - out0 = _mm_cvttps_epi32(in0); + in0 = _MM_CLAMP_PS(in0, int_min, int_max); + out0 = _mm_cvtps_epi32(in0); + out0 = _mm_slli_epi32(out0, 8); out1 = _mm_shuffle_epi32(out0, _MM_SHUFFLE(0, 3, 2, 1)); out2 = _mm_shuffle_epi32(out0, _MM_SHUFFLE(1, 0, 3, 2)); out3 = _mm_shuffle_epi32(out0, _MM_SHUFFLE(2, 1, 0, 3)); @@ -563,8 +574,8 @@ for(; n < n_samples; n++) { in0 = _mm_load_ss(&s0n); in0 = _mm_mul_ss(in0, scale); - in0 = _mm_min_ss(in0, int_max); - *d = _mm_cvtss_si32(in0); + in0 = _MM_CLAMP_SS(in0, int_min, int_max); + *d = _mm_cvtss_si32(in0) << 8; d += n_channels; } } @@ -578,8 +589,9 @@ uint32_t n, unrolled; __m256 in2; __m256i out2, t2; - __m256 scale = _mm256_set1_ps(S32_SCALE); - __m256 int_max = _mm256_set1_ps(S32_MAX); + __m256 scale = _mm256_set1_ps(S24_SCALE); + __m256 int_min = _mm256_set1_ps(S24_MIN); + __m256 int_max = _mm256_set1_ps(S24_MAX); if (SPA_IS_ALIGNED(s0, 32) && SPA_IS_ALIGNED(s1, 32)) @@ -591,11 +603,13 @@ in0 = _mm256_mul_ps(_mm256_load_ps(&s0n), scale); in1 = _mm256_mul_ps(_mm256_load_ps(&s1n), scale); - in0 = _mm256_min_ps(in0, int_max); - in1 = _mm256_min_ps(in1, int_max); + in0 = _MM256_CLAMP_PS(in0, int_min, int_max); + in1 = _MM256_CLAMP_PS(in1, int_min, int_max); - out0 = _mm256_cvttps_epi32(in0); /* a0 a1 a2 a3 a4 a5 a6 a7 */ - out1 = _mm256_cvttps_epi32(in1); /* b0 b1 b2 b3 b4 b5 b6 b7 */ + out0 = _mm256_cvtps_epi32(in0); /* a0 a1 a2 a3 a4 a5 a6 a7 */ + out1 = _mm256_cvtps_epi32(in1); /* b0 b1 b2 b3 b4 b5 b6 b7 */ + out0 = _mm256_slli_epi32(out0, 8); + out1 = _mm256_slli_epi32(out1, 8); t0 = _mm256_unpacklo_epi32(out0, out1); /* a0 b0 a1 b1 a4 b4 a5 b5 */ t1 = _mm256_unpackhi_epi32(out0, out1); /* a2 b2 a3 b3 a6 b6 a7 b7 */ @@ -624,8 +638,9 @@ for(; n < n_samples; n++) { __m128 in2; __m128i out2; - __m128 scale = _mm_set1_ps(S32_SCALE); - __m128 int_max = _mm_set1_ps(S32_MAX); + __m128 scale = _mm_set1_ps(S24_SCALE); + __m128 int_min = _mm_set1_ps(S24_MIN); + __m128 int_max = _mm_set1_ps(S24_MAX); in0 = _mm_load_ss(&s0n); in1 = _mm_load_ss(&s1n); @@ -633,8 +648,9 @@ in0 = _mm_unpacklo_ps(in0, in1); in0 = _mm_mul_ps(in0, scale); - in0 = _mm_min_ps(in0, int_max); - out0 = _mm_cvttps_epi32(in0); + in0 = _MM_CLAMP_PS(in0, int_min, int_max); + out0 = _mm_cvtps_epi32(in0); + out0 = _mm_slli_epi32(out0, 8); _mm_storel_epi64((__m128i*)d, out0); d += n_channels; } @@ -649,8 +665,9 @@ uint32_t n, unrolled; __m256 in4; __m256i out4, t4; - __m256 scale = _mm256_set1_ps(S32_SCALE); - __m256 int_max = _mm256_set1_ps(S32_MAX); + __m256 scale = _mm256_set1_ps(S24_SCALE); + __m256 int_min = _mm256_set1_ps(S24_MIN); + __m256 int_max = _mm256_set1_ps(S24_MAX); if (SPA_IS_ALIGNED(s0, 32) && SPA_IS_ALIGNED(s1, 32) && @@ -666,15 +683,19 @@ in2 = _mm256_mul_ps(_mm256_load_ps(&s2n), scale); in3 = _mm256_mul_ps(_mm256_load_ps(&s3n), scale); - in0 = _mm256_min_ps(in0, int_max); - in1 = _mm256_min_ps(in1, int_max); - in2 = _mm256_min_ps(in2, int_max); - in3 = _mm256_min_ps(in3, int_max); + in0 = _MM256_CLAMP_PS(in0, int_min, int_max); + in1 = _MM256_CLAMP_PS(in1, int_min, int_max); + in2 = _MM256_CLAMP_PS(in2, int_min, int_max); + in3 = _MM256_CLAMP_PS(in3, int_min, int_max); - out0 = _mm256_cvttps_epi32(in0); /* a0 a1 a2 a3 a4 a5 a6 a7 */ - out1 = _mm256_cvttps_epi32(in1); /* b0 b1 b2 b3 b4 b5 b6 b7 */ - out2 = _mm256_cvttps_epi32(in2); /* c0 c1 c2 c3 c4 c5 c6 c7 */ - out3 = _mm256_cvttps_epi32(in3); /* d0 d1 d2 d3 d4 d5 d6 d7 */ + out0 = _mm256_cvtps_epi32(in0); /* a0 a1 a2 a3 a4 a5 a6 a7 */ + out1 = _mm256_cvtps_epi32(in1); /* b0 b1 b2 b3 b4 b5 b6 b7 */ + out2 = _mm256_cvtps_epi32(in2); /* c0 c1 c2 c3 c4 c5 c6 c7 */ + out3 = _mm256_cvtps_epi32(in3); /* d0 d1 d2 d3 d4 d5 d6 d7 */ + out0 = _mm256_slli_epi32(out0, 8); + out1 = _mm256_slli_epi32(out1, 8); + out2 = _mm256_slli_epi32(out2, 8); + out3 = _mm256_slli_epi32(out3, 8); t0 = _mm256_unpacklo_epi32(out0, out1); /* a0 b0 a1 b1 a4 b4 a5 b5 */ t1 = _mm256_unpackhi_epi32(out0, out1); /* a2 b2 a3 b3 a6 b6 a7 b7 */ @@ -699,8 +720,9 @@ for(; n < n_samples; n++) { __m128 in4; __m128i out4; - __m128 scale = _mm_set1_ps(S32_SCALE); - __m128 int_max = _mm_set1_ps(S32_MAX); + __m128 scale = _mm_set1_ps(S24_SCALE); + __m128 int_min = _mm_set1_ps(S24_MIN); + __m128 int_max = _mm_set1_ps(S24_MAX); in0 = _mm_load_ss(&s0n); in1 = _mm_load_ss(&s1n); @@ -712,8 +734,9 @@ in0 = _mm_unpacklo_ps(in0, in1); in0 = _mm_mul_ps(in0, scale); - in0 = _mm_min_ps(in0, int_max); - out0 = _mm_cvttps_epi32(in0); + in0 = _MM_CLAMP_PS(in0, int_min, int_max); + out0 = _mm_cvtps_epi32(in0); + out0 = _mm_slli_epi32(out0, 8); _mm_storeu_si128((__m128i*)d, out0); d += n_channels; } @@ -755,8 +778,8 @@ for(n = 0; n < unrolled; n += 8) { in0 = _mm_mul_ps(_mm_load_ps(&s0n), int_scale); in1 = _mm_mul_ps(_mm_load_ps(&s0n+4), int_scale); - out0 = _mm_cvttps_epi32(in0); - out1 = _mm_cvttps_epi32(in1); + out0 = _mm_cvtps_epi32(in0); + out1 = _mm_cvtps_epi32(in1); out0 = _mm_packs_epi32(out0, out1); d0*n_channels = _mm_extract_epi16(out0, 0); @@ -771,7 +794,7 @@ } for(; n < n_samples; n++) { in0 = _mm_mul_ss(_mm_load_ss(&s0n), int_scale); - in0 = _mm_min_ss(int_max, _mm_max_ss(in0, int_min)); + in0 = _MM_CLAMP_SS(in0, int_min, int_max); *d = _mm_cvtss_si32(in0); d += n_channels; } @@ -798,8 +821,8 @@ in0 = _mm256_mul_ps(_mm256_load_ps(&s0n+0), int_scale); in1 = _mm256_mul_ps(_mm256_load_ps(&s1n+0), int_scale); - out0 = _mm256_cvttps_epi32(in0); /* a0 a1 a2 a3 a4 a5 a6 a7 */ - out1 = _mm256_cvttps_epi32(in1); /* b0 b1 b2 b3 b4 b5 b6 b7 */ + out0 = _mm256_cvtps_epi32(in0); /* a0 a1 a2 a3 a4 a5 a6 a7 */ + out1 = _mm256_cvtps_epi32(in1); /* b0 b1 b2 b3 b4 b5 b6 b7 */ t0 = _mm256_unpacklo_epi32(out0, out1); /* a0 b0 a1 b1 a4 b4 a5 b5 */ t1 = _mm256_unpackhi_epi32(out0, out1); /* a2 b2 a3 b3 a6 b6 a7 b7 */
View file
pipewire-0.3.54.tar.gz/spa/plugins/audioconvert/fmt-ops-c.c -> pipewire-0.3.56.tar.gz/spa/plugins/audioconvert/fmt-ops-c.c
Changed
@@ -33,7 +33,7 @@ #include "fmt-ops.h" #include "law.h" -#define MAKE_COPY(size) \ +#define MAKE_COPY(size) \ void conv_copy ##size## d_c(struct convert *conv, \ void * SPA_RESTRICT dst, const void * SPA_RESTRICT src, \ uint32_t n_samples) \ @@ -55,7 +55,7 @@ MAKE_COPY(32); MAKE_COPY(64); -#define MAKE_D_TO_D(sname,stype,dname,dtype,func) \ +#define MAKE_D_TO_D(sname,stype,dname,dtype,func) \ void conv_ ##sname## d_to_ ##dname## d_c(struct convert *conv, \ void * SPA_RESTRICT dst, const void * SPA_RESTRICT src, \ uint32_t n_samples) \ @@ -65,11 +65,11 @@ const stype *s = srci; \ dtype *d = dsti; \ for (j = 0; j < n_samples; j++) \ - dj = func (sj); \ + dj = func (sj); \ } \ } -#define MAKE_I_TO_I(sname,stype,dname,dtype,func) \ +#define MAKE_I_TO_I(sname,stype,dname,dtype,func) \ void conv_ ##sname## _to_ ##dname## _c(struct convert *conv, \ void * SPA_RESTRICT dst, const void * SPA_RESTRICT src, \ uint32_t n_samples) \ @@ -79,10 +79,10 @@ dtype *d = dst0; \ n_samples *= conv->n_channels; \ for (j = 0; j < n_samples; j++) \ - dj = func (sj); \ + dj = func (sj); \ } -#define MAKE_I_TO_D(sname,stype,dname,dtype,func) \ +#define MAKE_I_TO_D(sname,stype,dname,dtype,func) \ void conv_ ##sname## _to_ ##dname## d_c(struct convert *conv, \ void * SPA_RESTRICT dst, const void * SPA_RESTRICT src, \ uint32_t n_samples) \ @@ -92,11 +92,11 @@ uint32_t i, j, n_channels = conv->n_channels; \ for (j = 0; j < n_samples; j++) { \ for (i = 0; i < n_channels; i++) \ - dij = func (*s++); \ + dij = func (*s++); \ } \ } -#define MAKE_D_TO_I(sname,stype,dname,dtype,func) \ +#define MAKE_D_TO_I(sname,stype,dname,dtype,func) \ void conv_ ##sname## d_to_ ##dname## _c(struct convert *conv, \ void * SPA_RESTRICT dst, const void * SPA_RESTRICT src, \ uint32_t n_samples) \ @@ -106,7 +106,7 @@ uint32_t i, j, n_channels = conv->n_channels; \ for (j = 0; j < n_samples; j++) { \ for (i = 0; i < n_channels; i++) \ - *d++ = func (sij); \ + *d++ = func (sij); \ } \ } @@ -164,8 +164,7 @@ MAKE_I_TO_I(f64, double, f32, float, (float)); MAKE_I_TO_D(f64, double, f32, float, (float)); MAKE_D_TO_I(f64, double, f32, float, (float)); -MAKE_I_TO_D(f64s, double, f32, float, bswap_64); /* FIXME */ - +MAKE_I_TO_D(f64s, uint64_t, f32, float, (float)F64S_TO_F64); /* from f32 */ MAKE_D_TO_D(f32, float, u8, uint8_t, F32_TO_U8); @@ -221,7 +220,7 @@ MAKE_I_TO_I(f32, float, f64, double, (double)); MAKE_I_TO_D(f32, float, f64, double, (double)); MAKE_D_TO_I(f32, float, f64, double, (double)); -MAKE_D_TO_I(f32, float, f64s, double, bswap_32); /* FIXME */ +MAKE_D_TO_I(f32, float, f64s, uint64_t, F64_TO_F64S); static inline int32_t @@ -231,132 +230,154 @@ return (int32_t)(*state); } -static inline void update_dither_c(struct convert *conv, uint32_t n_samples) +static inline void update_noise_c(struct convert *conv, uint32_t n_samples) { uint32_t n; - float *dither = conv->dither, scale = conv->scale; + float *noise = conv->noise, scale = conv->scale; uint32_t *state = &conv->random0; - - for (n = 0; n < n_samples; n++) - dithern = lcnoise(state) * scale; + int32_t *prev = &conv->prev0, old, new; + + switch (conv->noise_method) { + case NOISE_METHOD_RECTANGULAR: + for (n = 0; n < n_samples; n++) + noisen = lcnoise(state) * scale; + break; + case NOISE_METHOD_TRIANGULAR: + for (n = 0; n < n_samples; n++) + noisen = (lcnoise(state) - lcnoise(state)) * scale; + break; + case NOISE_METHOD_TRIANGULAR_HF: + old = *prev; + for (n = 0; n < n_samples; n++) { + new = lcnoise(state); + noisen = (new - old) * scale; + old = new; + } + *prev = old; + break; + case NOISE_METHOD_PATTERN: + old = *prev; + for (n = 0; n < n_samples; n++) + noisen = conv->scale * (1-((old++>>10)&1)); + *prev = old; + break; + } } -#define MAKE_D_dither(dname,dtype,func) \ -void conv_f32d_to_ ##dname## d_dither_c(struct convert *conv, \ +#define MAKE_D_noise(dname,dtype,func) \ +void conv_f32d_to_ ##dname## d_noise_c(struct convert *conv, \ void * SPA_RESTRICT dst, const void * SPA_RESTRICT src, \ uint32_t n_samples) \ { \ - uint32_t i, j, k, chunk, n_channels = conv->n_channels, dither_size = conv->dither_size; \ - float *dither = conv->dither; \ - update_dither_c(conv, SPA_MIN(n_samples, dither_size)); \ + uint32_t i, j, k, chunk, n_channels = conv->n_channels, noise_size = conv->noise_size; \ + float *noise = conv->noise; \ + update_noise_c(conv, SPA_MIN(n_samples, noise_size)); \ for (i = 0; i < n_channels; i++) { \ const float *s = srci; \ dtype *d = dsti; \ for (j = 0; j < n_samples;) { \ - chunk = SPA_MIN(n_samples - j, dither_size); \ + chunk = SPA_MIN(n_samples - j, noise_size); \ for (k = 0; k < chunk; k++, j++) \ - dj = func (sj, ditherk); \ + dj = func (sj, noisek); \ } \ } \ } -#define MAKE_I_dither(dname,dtype,func) \ -void conv_f32d_to_ ##dname## _dither_c(struct convert *conv, \ +#define MAKE_I_noise(dname,dtype,func) \ +void conv_f32d_to_ ##dname## _noise_c(struct convert *conv, \ void * SPA_RESTRICT dst, const void * SPA_RESTRICT src, \ uint32_t n_samples) \ { \ const float **s = (const float **) src; \ dtype *d = dst0; \ - uint32_t i, j, k, chunk, n_channels = conv->n_channels, dither_size = conv->dither_size; \ - float *dither = conv->dither; \ - update_dither_c(conv, SPA_MIN(n_samples, dither_size)); \ + uint32_t i, j, k, chunk, n_channels = conv->n_channels, noise_size = conv->noise_size; \ + float *noise = conv->noise; \ + update_noise_c(conv, SPA_MIN(n_samples, noise_size)); \ for (j = 0; j < n_samples;) { \ - chunk = SPA_MIN(n_samples - j, dither_size); \ + chunk = SPA_MIN(n_samples - j, noise_size); \ for (k = 0; k < chunk; k++, j++) { \ for (i = 0; i < n_channels; i++) \ - *d++ = func (sij, ditherk); \ + *d++ = func (sij, noisek); \ } \ } \ } -MAKE_D_dither(u8, uint8_t, F32_TO_U8_D); -MAKE_I_dither(u8, uint8_t, F32_TO_U8_D); -MAKE_D_dither(s8, int8_t, F32_TO_S8_D); -MAKE_I_dither(s8, int8_t, F32_TO_S8_D); -MAKE_D_dither(s16, int16_t, F32_TO_S16_D); -MAKE_I_dither(s16, int16_t, F32_TO_S16_D); -MAKE_I_dither(s16s, uint16_t, F32_TO_S16S_D); -MAKE_D_dither(s32, int32_t, F32_TO_S32_D); -MAKE_I_dither(s32, int32_t, F32_TO_S32_D); -MAKE_I_dither(s32s, uint32_t, F32_TO_S32S_D); -MAKE_D_dither(s24, int24_t, F32_TO_S24_D); -MAKE_I_dither(s24, int24_t, F32_TO_S24_D); -MAKE_I_dither(s24s, int24_t, F32_TO_S24_D); -MAKE_D_dither(s24_32, int32_t, F32_TO_S24_32_D); -MAKE_I_dither(s24_32, int32_t, F32_TO_S24_32_D); -MAKE_I_dither(s24_32s, int32_t, F32_TO_S24_32S_D); - -
View file
pipewire-0.3.54.tar.gz/spa/plugins/audioconvert/fmt-ops-sse2.c -> pipewire-0.3.56.tar.gz/spa/plugins/audioconvert/fmt-ops-sse2.c
Changed
@@ -26,6 +26,12 @@ #include <emmintrin.h> +#define _MM_CLAMP_PS(r,min,max) \ + _mm_min_ps(_mm_max_ps(r, min), max) + +#define _MM_CLAMP_SS(r,min,max) \ + _mm_min_ss(_mm_max_ss(r, min), max) + static void conv_s16_to_f32d_1s_sse2(void *data, void * SPA_RESTRICT dst, const void * SPA_RESTRICT src, uint32_t n_channels, uint32_t n_samples) @@ -338,7 +344,7 @@ float *d0 = dst0; uint32_t n, unrolled; __m128i in; - __m128 out, factor = _mm_set1_ps(1.0f / S32_SCALE); + __m128 out, factor = _mm_set1_ps(1.0f / S24_SCALE); if (SPA_IS_ALIGNED(d0, 16)) unrolled = n_samples & ~3; @@ -350,13 +356,14 @@ s1*n_channels, s2*n_channels, s3*n_channels); + in = _mm_srai_epi32(in, 8); out = _mm_cvtepi32_ps(in); out = _mm_mul_ps(out, factor); _mm_store_ps(&d0n, out); s += 4*n_channels; } for(; n < n_samples; n++) { - out = _mm_cvtsi32_ss(factor, s0); + out = _mm_cvtsi32_ss(factor, s0>>8); out = _mm_mul_ss(out, factor); _mm_store_ss(&d0n, out); s += n_channels; @@ -383,8 +390,9 @@ uint32_t n, unrolled; __m128 in1; __m128i out4; - __m128 scale = _mm_set1_ps(S32_SCALE); - __m128 int_max = _mm_set1_ps(S32_MAX); + __m128 scale = _mm_set1_ps(S24_SCALE); + __m128 int_min = _mm_set1_ps(S24_MIN); + __m128 int_max = _mm_set1_ps(S24_MAX); if (SPA_IS_ALIGNED(s0, 16)) unrolled = n_samples & ~3; @@ -393,8 +401,9 @@ for(n = 0; n < unrolled; n += 4) { in0 = _mm_mul_ps(_mm_load_ps(&s0n), scale); - in0 = _mm_min_ps(in0, int_max); - out0 = _mm_cvttps_epi32(in0); + in0 = _MM_CLAMP_PS(in0, int_min, int_max); + out0 = _mm_cvtps_epi32(in0); + out0 = _mm_slli_epi32(out0, 8); out1 = _mm_shuffle_epi32(out0, _MM_SHUFFLE(0, 3, 2, 1)); out2 = _mm_shuffle_epi32(out0, _MM_SHUFFLE(1, 0, 3, 2)); out3 = _mm_shuffle_epi32(out0, _MM_SHUFFLE(2, 1, 0, 3)); @@ -408,8 +417,8 @@ for(; n < n_samples; n++) { in0 = _mm_load_ss(&s0n); in0 = _mm_mul_ss(in0, scale); - in0 = _mm_min_ss(in0, int_max); - *d = _mm_cvtss_si32(in0); + in0 = _MM_CLAMP_SS(in0, int_min, int_max); + *d = _mm_cvtss_si32(in0) << 8; d += n_channels; } } @@ -423,8 +432,9 @@ uint32_t n, unrolled; __m128 in2; __m128i out2, t2; - __m128 scale = _mm_set1_ps(S32_SCALE); - __m128 int_max = _mm_set1_ps(S32_MAX); + __m128 scale = _mm_set1_ps(S24_SCALE); + __m128 int_min = _mm_set1_ps(S24_MIN); + __m128 int_max = _mm_set1_ps(S24_MAX); if (SPA_IS_ALIGNED(s0, 16) && SPA_IS_ALIGNED(s1, 16)) @@ -436,11 +446,13 @@ in0 = _mm_mul_ps(_mm_load_ps(&s0n), scale); in1 = _mm_mul_ps(_mm_load_ps(&s1n), scale); - in0 = _mm_min_ps(in0, int_max); - in1 = _mm_min_ps(in1, int_max); + in0 = _MM_CLAMP_PS(in0, int_min, int_max); + in1 = _MM_CLAMP_PS(in1, int_min, int_max); - out0 = _mm_cvttps_epi32(in0); - out1 = _mm_cvttps_epi32(in1); + out0 = _mm_cvtps_epi32(in0); + out1 = _mm_cvtps_epi32(in1); + out0 = _mm_slli_epi32(out0, 8); + out1 = _mm_slli_epi32(out1, 8); t0 = _mm_unpacklo_epi32(out0, out1); t1 = _mm_unpackhi_epi32(out0, out1); @@ -458,8 +470,9 @@ in0 = _mm_unpacklo_ps(in0, in1); in0 = _mm_mul_ps(in0, scale); - in0 = _mm_min_ps(in0, int_max); - out0 = _mm_cvttps_epi32(in0); + in0 = _MM_CLAMP_PS(in0, int_min, int_max); + out0 = _mm_cvtps_epi32(in0); + out0 = _mm_slli_epi32(out0, 8); _mm_storel_epi64((__m128i*)d, out0); d += n_channels; } @@ -474,8 +487,9 @@ uint32_t n, unrolled; __m128 in4; __m128i out4; - __m128 scale = _mm_set1_ps(S32_SCALE); - __m128 int_max = _mm_set1_ps(S32_MAX); + __m128 scale = _mm_set1_ps(S24_SCALE); + __m128 int_min = _mm_set1_ps(S24_MIN); + __m128 int_max = _mm_set1_ps(S24_MAX); if (SPA_IS_ALIGNED(s0, 16) && SPA_IS_ALIGNED(s1, 16) && @@ -491,17 +505,21 @@ in2 = _mm_mul_ps(_mm_load_ps(&s2n), scale); in3 = _mm_mul_ps(_mm_load_ps(&s3n), scale); - in0 = _mm_min_ps(in0, int_max); - in1 = _mm_min_ps(in1, int_max); - in2 = _mm_min_ps(in2, int_max); - in3 = _mm_min_ps(in3, int_max); + in0 = _MM_CLAMP_PS(in0, int_min, int_max); + in1 = _MM_CLAMP_PS(in1, int_min, int_max); + in2 = _MM_CLAMP_PS(in2, int_min, int_max); + in3 = _MM_CLAMP_PS(in3, int_min, int_max); _MM_TRANSPOSE4_PS(in0, in1, in2, in3); - out0 = _mm_cvttps_epi32(in0); - out1 = _mm_cvttps_epi32(in1); - out2 = _mm_cvttps_epi32(in2); - out3 = _mm_cvttps_epi32(in3); + out0 = _mm_cvtps_epi32(in0); + out1 = _mm_cvtps_epi32(in1); + out2 = _mm_cvtps_epi32(in2); + out3 = _mm_cvtps_epi32(in3); + out0 = _mm_slli_epi32(out0, 8); + out1 = _mm_slli_epi32(out1, 8); + out2 = _mm_slli_epi32(out2, 8); + out3 = _mm_slli_epi32(out3, 8); _mm_storeu_si128((__m128i*)(d + 0*n_channels), out0); _mm_storeu_si128((__m128i*)(d + 1*n_channels), out1); @@ -520,8 +538,9 @@ in0 = _mm_unpacklo_ps(in0, in1); in0 = _mm_mul_ps(in0, scale); - in0 = _mm_min_ps(in0, int_max); - out0 = _mm_cvttps_epi32(in0); + in0 = _MM_CLAMP_PS(in0, int_min, int_max); + out0 = _mm_cvtps_epi32(in0); + out0 = _mm_slli_epi32(out0, 8); _mm_storeu_si128((__m128i*)d, out0); d += n_channels; } @@ -542,43 +561,83 @@ conv_f32d_to_s32_1s_sse2(conv, &di, &srci, n_channels, n_samples); } -static inline void update_dither_sse2(struct convert *conv, uint32_t n_samples) +/* 32 bit xorshift PRNG, see https://en.wikipedia.org/wiki/Xorshift */ +#define _MM_XORSHIFT_EPI32(r) \ +({ \ + __m128i i, t; \ + i = _mm_load_si128((__m128i*)r); \ + t = _mm_slli_epi32(i, 13); \ + i = _mm_xor_si128(i, t); \ + t = _mm_srli_epi32(i, 17); \ + i = _mm_xor_si128(i, t); \ + t = _mm_slli_epi32(i, 5); \ + i = _mm_xor_si128(i, t); \ + _mm_store_si128((__m128i*)r, i); \ + i; \ +}) + + +static inline void update_noise_sse2(struct convert *conv, uint32_t n_samples) { uint32_t n; const uint32_t *r = SPA_PTR_ALIGN(conv->random, 16, uint32_t); - float *dither = SPA_PTR_ALIGN(conv->dither, 16, float); - __m128 scale = _mm_set1_ps(conv->scale), out1; - __m128i in1, t1; - - for (n = 0; n < n_samples; n += 4) { - /* 32 bit xorshift PRNG, see https://en.wikipedia.org/wiki/Xorshift */
View file
pipewire-0.3.54.tar.gz/spa/plugins/audioconvert/fmt-ops.c -> pipewire-0.3.56.tar.gz/spa/plugins/audioconvert/fmt-ops.c
Changed
@@ -46,9 +46,9 @@ const char *name; uint32_t cpu_flags; -#define CONV_DITHER (1<<0) +#define CONV_NOISE (1<<0) #define CONV_SHAPE (1<<1) - uint32_t dither_flags; + uint32_t conv_flags; }; #define MAKE(fmt1,fmt2,chan,func,...) \ @@ -105,13 +105,13 @@ MAKE(F32P, F32, 0, conv_32d_to_32_c), #if defined (HAVE_SSE2) - MAKE(F32_OE, F32P, 0, conv_32s_to_32sd_sse2, SPA_CPU_FLAG_SSE2), + MAKE(F32_OE, F32P, 0, conv_32s_to_32d_sse2, SPA_CPU_FLAG_SSE2), #endif - MAKE(F32_OE, F32P, 0, conv_32s_to_32sd_c), + MAKE(F32_OE, F32P, 0, conv_32s_to_32d_c), #if defined (HAVE_SSE2) - MAKE(F32P, F32_OE, 0, conv_32sd_to_32s_sse2, SPA_CPU_FLAG_SSE2), + MAKE(F32P, F32_OE, 0, conv_32d_to_32s_sse2, SPA_CPU_FLAG_SSE2), #endif - MAKE(F32P, F32_OE, 0, conv_32sd_to_32s_c), + MAKE(F32P, F32_OE, 0, conv_32d_to_32s_c), MAKE(U32, F32, 0, conv_u32_to_f32_c), MAKE(U32, F32P, 0, conv_u32_to_f32d_c), @@ -171,20 +171,20 @@ /* from f32 */ MAKE(F32, U8, 0, conv_f32_to_u8_c), MAKE(F32P, U8P, 0, conv_f32d_to_u8d_shaped_c, 0, CONV_SHAPE), - MAKE(F32P, U8P, 0, conv_f32d_to_u8d_dither_c, 0, CONV_DITHER), + MAKE(F32P, U8P, 0, conv_f32d_to_u8d_noise_c, 0, CONV_NOISE), MAKE(F32P, U8P, 0, conv_f32d_to_u8d_c), MAKE(F32, U8P, 0, conv_f32_to_u8d_c), MAKE(F32P, U8, 0, conv_f32d_to_u8_shaped_c, 0, CONV_SHAPE), - MAKE(F32P, U8, 0, conv_f32d_to_u8_dither_c, 0, CONV_DITHER), + MAKE(F32P, U8, 0, conv_f32d_to_u8_noise_c, 0, CONV_NOISE), MAKE(F32P, U8, 0, conv_f32d_to_u8_c), MAKE(F32, S8, 0, conv_f32_to_s8_c), MAKE(F32P, S8P, 0, conv_f32d_to_s8d_shaped_c, 0, CONV_SHAPE), - MAKE(F32P, S8P, 0, conv_f32d_to_s8d_dither_c, 0, CONV_DITHER), + MAKE(F32P, S8P, 0, conv_f32d_to_s8d_noise_c, 0, CONV_NOISE), MAKE(F32P, S8P, 0, conv_f32d_to_s8d_c), MAKE(F32, S8P, 0, conv_f32_to_s8d_c), MAKE(F32P, S8, 0, conv_f32d_to_s8_shaped_c, 0, CONV_SHAPE), - MAKE(F32P, S8, 0, conv_f32d_to_s8_dither_c, 0, CONV_DITHER), + MAKE(F32P, S8, 0, conv_f32d_to_s8_noise_c, 0, CONV_NOISE), MAKE(F32P, S8, 0, conv_f32d_to_s8_c), MAKE(F32P, ALAW, 0, conv_f32d_to_alaw_c), @@ -199,7 +199,10 @@ MAKE(F32, S16, 0, conv_f32_to_s16_c), MAKE(F32P, S16P, 0, conv_f32d_to_s16d_shaped_c, 0, CONV_SHAPE), - MAKE(F32P, S16P, 0, conv_f32d_to_s16d_dither_c, 0, CONV_DITHER), +#if defined (HAVE_SSE2) + MAKE(F32P, S16P, 0, conv_f32d_to_s16d_noise_sse2, SPA_CPU_FLAG_SSE2, CONV_NOISE), +#endif + MAKE(F32P, S16P, 0, conv_f32d_to_s16d_noise_c, 0, CONV_NOISE), #if defined (HAVE_SSE2) MAKE(F32P, S16P, 0, conv_f32d_to_s16d_sse2, SPA_CPU_FLAG_SSE2), #endif @@ -208,7 +211,10 @@ MAKE(F32, S16P, 0, conv_f32_to_s16d_c), MAKE(F32P, S16, 0, conv_f32d_to_s16_shaped_c, 0, CONV_SHAPE), - MAKE(F32P, S16, 0, conv_f32d_to_s16_dither_c, 0, CONV_DITHER), +#if defined (HAVE_SSE2) + MAKE(F32P, S16, 0, conv_f32d_to_s16_noise_sse2, SPA_CPU_FLAG_SSE2, CONV_NOISE), +#endif + MAKE(F32P, S16, 0, conv_f32d_to_s16_noise_c, 0, CONV_NOISE), #if defined (HAVE_NEON) MAKE(F32P, S16, 0, conv_f32d_to_s16_neon, SPA_CPU_FLAG_NEON), #endif @@ -224,21 +230,21 @@ MAKE(F32P, S16, 0, conv_f32d_to_s16_c), MAKE(F32P, S16_OE, 0, conv_f32d_to_s16s_shaped_c, 0, CONV_SHAPE), - MAKE(F32P, S16_OE, 0, conv_f32d_to_s16s_dither_c, 0, CONV_DITHER), + MAKE(F32P, S16_OE, 0, conv_f32d_to_s16s_noise_c, 0, CONV_NOISE), MAKE(F32P, S16_OE, 0, conv_f32d_to_s16s_c), MAKE(F32, U32, 0, conv_f32_to_u32_c), MAKE(F32P, U32, 0, conv_f32d_to_u32_c), MAKE(F32, S32, 0, conv_f32_to_s32_c), - MAKE(F32P, S32P, 0, conv_f32d_to_s32d_dither_c, 0, CONV_DITHER), + MAKE(F32P, S32P, 0, conv_f32d_to_s32d_noise_c, 0, CONV_NOISE), MAKE(F32P, S32P, 0, conv_f32d_to_s32d_c), MAKE(F32, S32P, 0, conv_f32_to_s32d_c), #if defined (HAVE_SSE2) - MAKE(F32P, S32, 0, conv_f32d_to_s32_dither_sse2, SPA_CPU_FLAG_SSE2, CONV_DITHER), + MAKE(F32P, S32, 0, conv_f32d_to_s32_noise_sse2, SPA_CPU_FLAG_SSE2, CONV_NOISE), #endif - MAKE(F32P, S32, 0, conv_f32d_to_s32_dither_c, 0, CONV_DITHER), + MAKE(F32P, S32, 0, conv_f32d_to_s32_noise_c, 0, CONV_NOISE), #if defined (HAVE_AVX2) MAKE(F32P, S32, 0, conv_f32d_to_s32_avx2, SPA_CPU_FLAG_AVX2), @@ -248,33 +254,33 @@ #endif MAKE(F32P, S32, 0, conv_f32d_to_s32_c), - MAKE(F32P, S32_OE, 0, conv_f32d_to_s32s_dither_c, 0, CONV_DITHER), + MAKE(F32P, S32_OE, 0, conv_f32d_to_s32s_noise_c, 0, CONV_NOISE), MAKE(F32P, S32_OE, 0, conv_f32d_to_s32s_c), MAKE(F32, U24, 0, conv_f32_to_u24_c), MAKE(F32P, U24, 0, conv_f32d_to_u24_c), MAKE(F32, S24, 0, conv_f32_to_s24_c), - MAKE(F32P, S24P, 0, conv_f32d_to_s24d_dither_c, 0, CONV_DITHER), + MAKE(F32P, S24P, 0, conv_f32d_to_s24d_noise_c, 0, CONV_NOISE), MAKE(F32P, S24P, 0, conv_f32d_to_s24d_c), MAKE(F32, S24P, 0, conv_f32_to_s24d_c), - MAKE(F32P, S24, 0, conv_f32d_to_s24_dither_c, 0, CONV_DITHER), + MAKE(F32P, S24, 0, conv_f32d_to_s24_noise_c, 0, CONV_NOISE), MAKE(F32P, S24, 0, conv_f32d_to_s24_c), - MAKE(F32P, S24_OE, 0, conv_f32d_to_s24s_dither_c, 0, CONV_DITHER), + MAKE(F32P, S24_OE, 0, conv_f32d_to_s24s_noise_c, 0, CONV_NOISE), MAKE(F32P, S24_OE, 0, conv_f32d_to_s24s_c), MAKE(F32, U24_32, 0, conv_f32_to_u24_32_c), MAKE(F32P, U24_32, 0, conv_f32d_to_u24_32_c), MAKE(F32, S24_32, 0, conv_f32_to_s24_32_c), - MAKE(F32P, S24_32P, 0, conv_f32d_to_s24_32d_dither_c, 0, CONV_DITHER), + MAKE(F32P, S24_32P, 0, conv_f32d_to_s24_32d_noise_c, 0, CONV_NOISE), MAKE(F32P, S24_32P, 0, conv_f32d_to_s24_32d_c), MAKE(F32, S24_32P, 0, conv_f32_to_s24_32d_c), - MAKE(F32P, S24_32, 0, conv_f32d_to_s24_32_dither_c, 0, CONV_DITHER), + MAKE(F32P, S24_32, 0, conv_f32d_to_s24_32_noise_c, 0, CONV_NOISE), MAKE(F32P, S24_32, 0, conv_f32d_to_s24_32_c), - MAKE(F32P, S24_32_OE, 0, conv_f32d_to_s24_32s_dither_c, 0, CONV_DITHER), + MAKE(F32P, S24_32_OE, 0, conv_f32d_to_s24_32s_noise_c, 0, CONV_NOISE), MAKE(F32P, S24_32_OE, 0, conv_f32d_to_s24_32s_c), MAKE(F32, F64, 0, conv_f32_to_f64_c), @@ -350,7 +356,7 @@ #define MATCH_DITHER(a,b) ((a) == 0 || ((a) & (b)) == a) static const struct conv_info *find_conv_info(uint32_t src_fmt, uint32_t dst_fmt, - uint32_t n_channels, uint32_t cpu_flags, uint32_t dither_flags) + uint32_t n_channels, uint32_t cpu_flags, uint32_t conv_flags) { size_t i; @@ -359,7 +365,7 @@ conv_tablei.dst_fmt == dst_fmt && MATCH_CHAN(conv_tablei.n_channels, n_channels) && MATCH_CPU_FLAGS(conv_tablei.cpu_flags, cpu_flags) && - MATCH_DITHER(conv_tablei.dither_flags, dither_flags)) + MATCH_DITHER(conv_tablei.conv_flags, conv_flags)) return &conv_tablei; } return NULL; @@ -368,8 +374,8 @@ static void impl_convert_free(struct convert *conv) { conv->process = NULL; - free(conv->dither); - conv->dither = NULL; + free(conv->noise); + conv->noise = NULL; } static bool need_dither(uint32_t format) @@ -389,37 +395,109 @@ return false; } +/* filters based on F-weighted curves + * from 'Psychoacoustically Optimal Noise Shaping' (**) + * this filter is the "F-Weighted" noise filter described by Wannamaker + * It is designed to produce minimum audibility: */ +static const float wan3 = { /* Table 3; 3 Coefficients */ + 1.623f, -0.982f, 0.109f +}; +/* Noise shaping coefficients from1, moves most power of the + * error noise into inaudible frequency ranges. + * + * 1 + * "Minimally Audible Noise Shaping", Stanley P. Lipshitz, + * John Vanderkooy, and Robert A. Wannamaker, + * J. Audio Eng. Soc., Vol. 39, No. 11, November 1991. */ +static const float lips44 = { /* improved E-weighted (appendix: 5) */ + 2.033f, -2.165f, 1.959f, -1.590f, 0.6149f +}; + +static const struct dither_info { + uint32_t method; + uint32_t noise_method;
View file
pipewire-0.3.54.tar.gz/spa/plugins/audioconvert/fmt-ops.h -> pipewire-0.3.56.tar.gz/spa/plugins/audioconvert/fmt-ops.h
Changed
@@ -35,93 +35,100 @@ #include <spa/utils/defs.h> #include <spa/utils/string.h> +#define f32_round(a) lrintf(a) + +#define ITOF(type,v,scale,offs) \ + (((type)(v)) * (1.0f / (scale)) - (offs)) +#define FTOI(type,v,scale,offs,noise,min,max) \ + (type)f32_round(SPA_CLAMP((v) * (scale) + (offs) + (noise), min, max)) + #define FMT_OPS_MAX_ALIGN 32 #define U8_MIN 0u #define U8_MAX 255u #define U8_SCALE 128.f #define U8_OFFS 128.f -#define U8_TO_F32(v) ((((uint8_t)(v)) * (1.0f / U8_SCALE)) - 1.0f) -#define F32_TO_U8(v) (uint8_t)SPA_CLAMP((v) * U8_SCALE + U8_OFFS, U8_MIN, U8_MAX) -#define F32_TO_U8_D(v,d) (uint8_t)SPA_CLAMP((v) * U8_SCALE + U8_OFFS + (d), U8_MIN, U8_MAX) +#define U8_TO_F32(v) ITOF(uint8_t, v, U8_SCALE, 1.0f) +#define F32_TO_U8_D(v,d) FTOI(uint8_t, v, U8_SCALE, U8_OFFS, d, U8_MIN, U8_MAX) +#define F32_TO_U8(v) F32_TO_U8_D(v, 0.0f) #define S8_MIN -128 #define S8_MAX 127 #define S8_SCALE 128.0f -#define S8_TO_F32(v) (((int8_t)(v)) * (1.0f / S8_SCALE)) -#define F32_TO_S8(v) (int8_t)SPA_CLAMP((v) * S8_SCALE, S8_MIN, S8_MAX) -#define F32_TO_S8_D(v,d) (int8_t)SPA_CLAMP((v) * S8_SCALE + (d), S8_MIN, S8_MAX) +#define S8_TO_F32(v) ITOF(int8_t, v, S8_SCALE, 0.0f) +#define F32_TO_S8_D(v,d) FTOI(int8_t, v, S8_SCALE, 0.0f, d, S8_MIN, S8_MAX) +#define F32_TO_S8(v) F32_TO_S8_D(v, 0.0f); #define U16_MIN 0u #define U16_MAX 65535u #define U16_SCALE 32768.f #define U16_OFFS 32768.f -#define U16_TO_F32(v) ((((uint16_t)(v)) * (1.0f / U16_SCALE)) - 1.0f) -#define U16S_TO_F32(v) (((uint16_t)bswap_16((uint16_t)(v)) * (1.0f / U16_OFFS)) - 1.0f) -#define F32_TO_U16(v) (uint16_t)SPA_CLAMP((v) * U16_SCALE + U16_OFFS, U16_MIN, U16_MAX) -#define F32_TO_U16_D(v,d) (uint16_t)SPA_CLAMP((v) * U16_SCALE + U16_OFFS + (d), U16_MIN, U16_MAX) -#define F32_TO_U16S(v) bswap_16(F32_TO_U16(v)) +#define U16_TO_F32(v) ITOF(uint16_t, v, U16_SCALE, 1.0f) +#define U16S_TO_F32(v) U16_TO_F32(bswap_16(v)) +#define F32_TO_U16_D(v,d) FTOI(uint16_t, v, U16_SCALE, U16_OFFS, d, U16_MIN, U16_MAX) +#define F32_TO_U16(v) F32_TO_U16_D(v, 0.0f); #define F32_TO_U16S_D(v,d) bswap_16(F32_TO_U16_D(v,d)) +#define F32_TO_U16S(v) bswap_16(F32_TO_U16(v)) #define S16_MIN -32768 #define S16_MAX 32767 #define S16_SCALE 32768.0f -#define S16_TO_F32(v) (((int16_t)(v)) * (1.0f / S16_SCALE)) -#define S16S_TO_F32(v) (((int16_t)bswap_16(v)) * (1.0f / S16_SCALE)) -#define F32_TO_S16(v) (int16_t)SPA_CLAMP((v) * S16_SCALE, S16_MIN, S16_MAX) -#define F32_TO_S16_D(v,d) (int16_t)SPA_CLAMP((v) * S16_SCALE + (d), S16_MIN, S16_MAX) -#define F32_TO_S16S(v) bswap_16(F32_TO_S16(v)) +#define S16_TO_F32(v) ITOF(int16_t, v, S16_SCALE, 0.0f) +#define S16S_TO_F32(v) S16_TO_F32(bswap_16(v)) +#define F32_TO_S16_D(v,d) FTOI(int16_t, v, S16_SCALE, 0.0f, d, S16_MIN, S16_MAX) +#define F32_TO_S16(v) F32_TO_S16_D(v, 0.0f) #define F32_TO_S16S_D(v,d) bswap_16(F32_TO_S16_D(v,d)) +#define F32_TO_S16S(v) bswap_16(F32_TO_S16(v)) #define U24_MIN 0u #define U24_MAX 16777215u #define U24_SCALE 8388608.f #define U24_OFFS 8388608.f -#define U24_TO_F32(v) ((u24_to_u32(v) * (1.0f / U24_SCALE)) - 1.0f) -#define F32_TO_U24(v) u32_to_u24(SPA_CLAMP((v) * U24_SCALE + U24_OFFS, U24_MIN, U24_MAX)) -#define F32_TO_U24_D(v,d) u32_to_u24(SPA_CLAMP((v) * U24_SCALE + U24_OFFS + (d), U24_MIN, U24_MAX)) +#define U24_TO_F32(v) ITOF(uint32_t, u24_to_u32(v), U24_SCALE, 1.0f) +#define F32_TO_U24_D(v,d) u32_to_u24(FTOI(uint32_t, v, U24_SCALE, U24_OFFS, d, U24_MIN, U24_MAX)) +#define F32_TO_U24(v) F32_TO_U24_D(v, 0.0f) #define S24_MIN -8388608 #define S24_MAX 8388607 #define S24_SCALE 8388608.0f -#define S24_TO_F32(v) (s24_to_s32(v) * (1.0f / S24_SCALE)) -#define S24S_TO_F32(v) (s24_to_s32(bswap_s24(v)) * (1.0f / S24_SCALE)) -#define F32_TO_S24(v) s32_to_s24(SPA_CLAMP((v) * S24_SCALE, S24_MIN, S24_MAX)) +#define S24_TO_F32(v) ITOF(int32_t, s24_to_s32(v), S24_SCALE, 0.0f) +#define S24S_TO_F32(v) S24_TO_F32(bswap_s24(v)) +#define F32_TO_S24_D(v,d) s32_to_s24(FTOI(int32_t, v, S24_SCALE, 0.0f, d, S24_MIN, S24_MAX)) +#define F32_TO_S24(v) F32_TO_S24_D(v, 0.0f) #define F32_TO_S24S(v) bswap_s24(F32_TO_S24(v)) -#define F32_TO_S24_D(v,d) s32_to_s24(SPA_CLAMP((v) * S24_SCALE + (d), S24_MIN, S24_MAX)) + +#define U24_32_TO_F32(v) U32_TO_F32((v)<<8) +#define U24_32S_TO_F32(v) U24_32_TO_F32(bswap_32(v)) +#define F32_TO_U24_32_D(v,d) FTOI(uint32_t, v, U24_SCALE, U24_OFFS, d, U24_MIN, U24_MAX) +#define F32_TO_U24_32(v) F32_TO_U24_32_D(v, 0.0f) +#define F32_TO_U24_32S(v) bswap_32(F32_TO_U24_32(v)) +#define F32_TO_U24_32S_D(v,d) bswap_32(F32_TO_U24_32_D(v,d)) #define U32_MIN 0u -#define U32_MAX 4294967295 +#define U32_MAX 4294967295u #define U32_SCALE 2147483648.f #define U32_OFFS 2147483648.f -#define U32_TO_F32(v) ((((uint32_t)(v)) * (1.0f / U32_SCALE)) - 1.0f) -#define F32_TO_U32(v) (uint32_t)SPA_CLAMP((v) * U32_SCALE + U32_OFFS, U32_MIN, U32_MAX) -#define F32_TO_U32_D(v,d) (uint32_t)SPA_CLAMP((v) * U32_SCALE + U32_OFFS + (d), U32_MIN, U32_MAX) +#define U32_TO_F32(v) ITOF(uint32_t, (v) >> 8, U24_SCALE, 1.0f) +#define F32_TO_U32(v) (F32_TO_U24_32(v) << 8) +#define F32_TO_U32_D(v,d) (F32_TO_U24_32_D(v,d) << 8) + +#define S24_32_TO_F32(v) S32_TO_F32((v)<<8) +#define S24_32S_TO_F32(v) S24_32_TO_F32(bswap_32(v)) +#define F32_TO_S24_32_D(v,d) FTOI(int32_t, v, S24_SCALE, 0.0f, d, S24_MIN, S24_MAX) +#define F32_TO_S24_32(v) F32_TO_S24_32_D(v, 0.0f) +#define F32_TO_S24_32S(v) bswap_32(F32_TO_S24_32(v)) +#define F32_TO_S24_32S_D(v,d) bswap_32(F32_TO_S24_32_D(v,d)) #define S32_MIN -2147483648 -#define S32_MAX 2147483520 +#define S32_MAX 2147483647 #define S32_SCALE 2147483648.f -#define S32_TO_F32(v) (((int32_t)(v)) * (1.0f / S32_SCALE)) -#define S32S_TO_F32(v) (((int32_t)bswap_32(v)) * (1.0f / S32_SCALE)) -#define F32_TO_S32(v) (int32_t)SPA_CLAMP((v) * S32_SCALE, S32_MIN, S32_MAX) -#define F32_TO_S32_D(v,d) (int32_t)SPA_CLAMP((v) * S32_SCALE + (d), S32_MIN, S32_MAX) +#define S32_TO_F32(v) ITOF(int32_t, (v) >> 8, S24_SCALE, 0.0f) +#define S32S_TO_F32(v) S32_TO_F32(bswap_32(v)) +#define F32_TO_S32(v) (F32_TO_S24_32(v) << 8) +#define F32_TO_S32_D(v,d) (F32_TO_S24_32_D(v,d) << 8) #define F32_TO_S32S(v) bswap_32(F32_TO_S32(v)) #define F32_TO_S32S_D(v,d) bswap_32(F32_TO_S32_D(v,d)) -#define U24_32_TO_F32(v) U32_TO_F32((v)<<8) -#define U24_32S_TO_F32(v) U32_TO_F32(((int32_t)bswap_32(v))<<8) -#define F32_TO_U24_32(v) (uint32_t)SPA_CLAMP((v) * U24_SCALE + U24_OFFS, U24_MIN, U24_MAX) -#define F32_TO_U24_32S(v) bswap_32(F32_TO_U24_32(v)) -#define F32_TO_U24_32_D(v,d) (uint32_t)SPA_CLAMP((v) * U24_SCALE + U24_OFFS + (d), U24_MIN, U24_MAX) -#define F32_TO_U24_32S_D(v,d) bswap_32(F32_TO_U24_32_D(v,d)) - -#define S24_32_TO_F32(v) S32_TO_F32((v)<<8) -#define S24_32S_TO_F32(v) S32_TO_F32(((int32_t)bswap_32(v))<<8) -#define F32_TO_S24_32(v) (int32_t)SPA_CLAMP((v) * S24_SCALE, S24_MIN, S24_MAX) -#define F32_TO_S24_32S(v) bswap_32(F32_TO_S24_32(v)) -#define F32_TO_S24_32_D(v,d) (int32_t)SPA_CLAMP((v) * S24_SCALE + (d), S24_MIN, S24_MAX) -#define F32_TO_S24_32S_D(v,d) bswap_32(F32_TO_S24_32_D(v,d)) - typedef struct { #if __BYTE_ORDER == __LITTLE_ENDIAN uint8_t v3; @@ -181,21 +188,33 @@ return (int24_t) { .v1 = src.v3, .v2 = src.v2, .v3 = src.v1 }; } +#define F32_TO_F32S(v) \ + bswap_32((union { uint32_t i; float f; }){ .f = (v) }.i) +#define F32S_TO_F32(v) \ + ((union { uint32_t i; float f; }){ .i = bswap_32(v) }.f) + +#define F64_TO_F64S(v) \ + bswap_32((union { uint64_t i; double d; }){ .d = (v) }.i) +#define F64S_TO_F64(v) \ + ((union { uint64_t i; double d; }){ .i = bswap_32(v) }.d) + #define NS_MAX 8 #define NS_MASK (NS_MAX-1) struct shaper { - float eNS_MAX; + float eNS_MAX * 2; uint32_t idx; float r; }; struct convert { - uint32_t noise; + uint32_t noise_bits; #define DITHER_METHOD_NONE 0 #define DITHER_METHOD_RECTANGULAR 1 #define DITHER_METHOD_TRIANGULAR 2 -#define DITHER_METHOD_SHAPED_5 3 +#define DITHER_METHOD_TRIANGULAR_HF 3 +#define DITHER_METHOD_WANNAMAKER_3 4 +#define DITHER_METHOD_LIPSHITZ 5 uint32_t method; uint32_t src_fmt; @@ -209,8 +228,17 @@ float scale; uint32_t random16 + FMT_OPS_MAX_ALIGN/4; - float *dither; - uint32_t dither_size; + int32_t prev16 + FMT_OPS_MAX_ALIGN/4; +#define NOISE_METHOD_NONE 0 +#define NOISE_METHOD_RECTANGULAR 1 +#define NOISE_METHOD_TRIANGULAR 2 +#define NOISE_METHOD_TRIANGULAR_HF 3 +#define NOISE_METHOD_PATTERN 4 + uint32_t noise_method; + float *noise; + uint32_t noise_size; + const float *ns;
View file
pipewire-0.3.54.tar.gz/spa/plugins/audioconvert/meson.build -> pipewire-0.3.56.tar.gz/spa/plugins/audioconvert/meson.build
Changed
@@ -7,6 +7,20 @@ simd_cargs = simd_dependencies = +audioconvert_c = static_library('audioconvert_c', + 'channelmix-ops-c.c', + 'biquad.c', + 'crossover.c', + 'volume-ops-c.c', + 'resample-native-c.c', + 'resample-peaks-c.c', + 'fmt-ops-c.c' , + c_args : '-Ofast', '-ffast-math', + dependencies : spa_dep , + install : false + ) +simd_dependencies += audioconvert_c + if have_sse audioconvert_sse = static_library('audioconvert_sse', 'resample-native-sse.c', @@ -86,15 +100,10 @@ audioconvert_lib = static_library('audioconvert', 'fmt-ops.c', - 'biquad.c', - 'crossover.c', 'channelmix-ops.c', - 'channelmix-ops-c.c', 'resample-native.c', 'resample-peaks.c', - 'fmt-ops-c.c', - 'volume-ops.c', - 'volume-ops-c.c' , + 'volume-ops.c' , c_args : simd_cargs, '-O3', link_with : simd_dependencies, include_directories : configinc,
View file
pipewire-0.3.56.tar.gz/spa/plugins/audioconvert/resample-native-c.c
Added
@@ -0,0 +1,65 @@ +/* Spa + * + * Copyright © 2019 Wim Taymans + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "resample-native-impl.h" + +static void inner_product_c(float *d, const float * SPA_RESTRICT s, + const float * SPA_RESTRICT taps, uint32_t n_taps) +{ + float sum = 0.0f; +#if 1 + uint32_t i, j, nt2 = n_taps/2; + for (i = 0, j = n_taps-1; i < nt2; i++, j--) + sum += si * tapsi + sj * tapsj; +#else + uint32_t i; + for (i = 0; i < n_taps; i++) + sum += si * tapsi; +#endif + *d = sum; +} + +static void inner_product_ip_c(float *d, const float * SPA_RESTRICT s, + const float * SPA_RESTRICT t0, const float * SPA_RESTRICT t1, float x, + uint32_t n_taps) +{ + float sum2 = { 0.0f, 0.0f }; + uint32_t i; +#if 1 + uint32_t j, nt2 = n_taps/2; + for (i = 0, j = n_taps-1; i < nt2; i++, j--) { + sum0 += si * t0i + sj * t0j; + sum1 += si * t1i + sj * t1j; + } +#else + for (i = 0; i < n_taps; i++) { + sum0 += si * t0i; + sum1 += si * t1i; + } +#endif + *d = (sum1 - sum0) * x + sum0; +} + +MAKE_RESAMPLER_FULL(c); +MAKE_RESAMPLER_INTER(c);
View file
pipewire-0.3.54.tar.gz/spa/plugins/audioconvert/resample-native.c -> pipewire-0.3.56.tar.gz/spa/plugins/audioconvert/resample-native.c
Changed
@@ -33,22 +33,22 @@ double cutoff; }; -static const struct quality blackman_qualities = { - { 8, 0.5, }, - { 16, 0.70, }, - { 24, 0.76, }, - { 32, 0.8, }, +static const struct quality window_qualities = { + { 8, 0.53, }, + { 16, 0.67, }, + { 24, 0.75, }, + { 32, 0.80, }, { 48, 0.85, }, /* default */ - { 64, 0.90, }, - { 80, 0.92, }, - { 96, 0.933, }, - { 128, 0.950, }, - { 144, 0.955, }, - { 160, 0.958, }, - { 192, 0.965, }, - { 256, 0.975, }, - { 896, 0.997, }, - { 1024, 0.998, }, + { 64, 0.88, }, + { 80, 0.895, }, + { 96, 0.910, }, + { 128, 0.936, }, + { 144, 0.945, }, + { 160, 0.950, }, + { 192, 0.960, }, + { 256, 0.970, }, + { 896, 0.990, }, + { 1024, 0.995, }, }; static inline double sinc(double x) @@ -69,13 +69,17 @@ static inline double window_cosh(double x, double n_taps) { double R = 190.0, r; - double A = -325.1E-6 * (R * R) + 0.1677 * R - 3.149; + double A = (-325.1E-6 * R + 0.1677) * R - 3.149; + double x2; x = 2.0 * x / n_taps; - r = cosh(A * sqrt(1 - pow(x, 2))) / cosh(A); + x2 = x * x; + if (x2 >= 1.0) + return 0.0; + r = cosh(A * sqrt(1 - x2)) / cosh(A); return r; } -#define window window_blackman +#define window window_cosh static int build_filter(float *taps, uint32_t stride, uint32_t n_taps, uint32_t n_phases, double cutoff) { @@ -93,46 +97,7 @@ return 0; } -static void inner_product_c(float *d, const float * SPA_RESTRICT s, - const float * SPA_RESTRICT taps, uint32_t n_taps) -{ - float sum = 0.0f; -#if 1 - uint32_t i, j, nt2 = n_taps/2; - for (i = 0, j = n_taps-1; i < nt2; i++, j--) - sum += si * tapsi + sj * tapsj; -#else - uint32_t i; - for (i = 0; i < n_taps; i++) - sum += si * tapsi; -#endif - *d = sum; -} - -static void inner_product_ip_c(float *d, const float * SPA_RESTRICT s, - const float * SPA_RESTRICT t0, const float * SPA_RESTRICT t1, float x, - uint32_t n_taps) -{ - float sum2 = { 0.0f, 0.0f }; - uint32_t i; -#if 1 - uint32_t j, nt2 = n_taps/2; - for (i = 0, j = n_taps-1; i < nt2; i++, j--) { - sum0 += si * t0i + sj * t0j; - sum1 += si * t1i + sj * t1j; - } -#else - for (i = 0; i < n_taps; i++) { - sum0 += si * t0i; - sum1 += si * t1i; - } -#endif - *d = (sum1 - sum0) * x + sum0; -} - MAKE_RESAMPLER_COPY(c); -MAKE_RESAMPLER_FULL(c); -MAKE_RESAMPLER_INTER(c); #define MAKE(fmt,copy,full,inter,...) \ { SPA_AUDIO_FORMAT_ ##fmt, do_resample_ ##copy, #copy, \ @@ -356,7 +321,7 @@ uint32_t c, n_taps, n_phases, filter_size, in_rate, out_rate, gcd, filter_stride; uint32_t history_stride, history_size, oversample; - r->quality = SPA_CLAMP(r->quality, 0, (int) SPA_N_ELEMENTS(blackman_qualities) - 1); + r->quality = SPA_CLAMP(r->quality, 0, (int) SPA_N_ELEMENTS(window_qualities) - 1); r->free = impl_native_free; r->update_rate = impl_native_update_rate; r->in_len = impl_native_in_len; @@ -364,14 +329,15 @@ r->reset = impl_native_reset; r->delay = impl_native_delay; - q = &blackman_qualitiesr->quality; + q = &window_qualitiesr->quality; gcd = calc_gcd(r->i_rate, r->o_rate); in_rate = r->i_rate / gcd; out_rate = r->o_rate / gcd; - scale = SPA_MIN(q->cutoff * out_rate / in_rate, 1.0); + scale = SPA_MIN(q->cutoff * out_rate / in_rate, q->cutoff); + /* multiple of 8 taps to ease simd optimizations */ n_taps = SPA_ROUND_UP_N((uint32_t)ceil(q->n_taps / scale), 8); n_taps = SPA_MIN(n_taps, 1u << 18);
View file
pipewire-0.3.56.tar.gz/spa/plugins/audioconvert/resample-peaks-c.c
Added
@@ -0,0 +1,73 @@ +/* Spa + * + * Copyright © 2018 Wim Taymans + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include <math.h> + +#include "resample-peaks-impl.h" + +void resample_peaks_process_c(struct resample *r, + const void * SPA_RESTRICT src, uint32_t *in_len, + void * SPA_RESTRICT dst, uint32_t *out_len) +{ + struct peaks_data *pd = r->data; + uint32_t c, i, o, end, chunk, o_count, i_count; + + if (SPA_UNLIKELY(r->channels == 0)) + return; + + for (c = 0; c < r->channels; c++) { + const float *s = srcc; + float *d = dstc, m = pd->max_fc; + + o_count = pd->o_count; + i_count = pd->i_count; + o = i = 0; + + while (i < *in_len && o < *out_len) { + end = ((uint64_t) (o_count + 1) * r->i_rate) / r->o_rate; + end = end > i_count ? end - i_count : 0; + chunk = SPA_MIN(end, *in_len); + + for (; i < chunk; i++) + m = SPA_MAX(fabsf(si), m); + + if (i == end) { + do++ = m; + m = 0.0f; + o_count++; + } + } + pd->max_fc = m; + } + + *out_len = o; + *in_len = i; + pd->o_count = o_count; + pd->i_count = i_count + i; + + while (pd->i_count >= r->i_rate) { + pd->i_count -= r->i_rate; + pd->o_count -= r->o_rate; + } +}
View file
pipewire-0.3.54.tar.gz/spa/plugins/audioconvert/resample-peaks-impl.h -> pipewire-0.3.56.tar.gz/spa/plugins/audioconvert/resample-peaks-impl.h
Changed
@@ -34,6 +34,9 @@ float max_f; }; +void resample_peaks_process_c(struct resample *r, + const void * SPA_RESTRICT src, uint32_t *in_len, + void * SPA_RESTRICT dst, uint32_t *out_len); #if defined (HAVE_SSE) void resample_peaks_process_sse(struct resample *r, const void * SPA_RESTRICT src, uint32_t *in_len,
View file
pipewire-0.3.54.tar.gz/spa/plugins/audioconvert/resample-peaks.c -> pipewire-0.3.56.tar.gz/spa/plugins/audioconvert/resample-peaks.c
Changed
@@ -29,52 +29,6 @@ #include "resample-peaks-impl.h" -static void resample_peaks_process_c(struct resample *r, - const void * SPA_RESTRICT src, uint32_t *in_len, - void * SPA_RESTRICT dst, uint32_t *out_len) -{ - struct peaks_data *pd = r->data; - uint32_t c, i, o, end, chunk, o_count, i_count; - - if (SPA_UNLIKELY(r->channels == 0)) - return; - - for (c = 0; c < r->channels; c++) { - const float *s = srcc; - float *d = dstc, m = pd->max_fc; - - o_count = pd->o_count; - i_count = pd->i_count; - o = i = 0; - - while (i < *in_len && o < *out_len) { - end = ((uint64_t) (o_count + 1) * r->i_rate) / r->o_rate; - end = end > i_count ? end - i_count : 0; - chunk = SPA_MIN(end, *in_len); - - for (; i < chunk; i++) - m = SPA_MAX(fabsf(si), m); - - if (i == end) { - do++ = m; - m = 0.0f; - o_count++; - } - } - pd->max_fc = m; - } - - *out_len = o; - *in_len = i; - pd->o_count = o_count; - pd->i_count = i_count + i; - - while (pd->i_count >= r->i_rate) { - pd->i_count -= r->i_rate; - pd->o_count -= r->o_rate; - } -} - struct resample_info { uint32_t format; uint32_t cpu_flags;
View file
pipewire-0.3.54.tar.gz/spa/plugins/audioconvert/spa-resample.c -> pipewire-0.3.56.tar.gz/spa/plugins/audioconvert/spa-resample.c
Changed
@@ -33,6 +33,7 @@ #include <spa/support/log-impl.h> #include <spa/debug/mem.h> #include <spa/utils/string.h> +#include <spa/utils/result.h> #include <sndfile.h> @@ -184,14 +185,14 @@ float outMAX_SAMPLES * channels; float ibufMAX_SAMPLES * channels; float obufMAX_SAMPLES * channels; - uint32_t in_len, out_len; - uint32_t pin_len, pout_len; + uint32_t in_len, out_len, queued; + uint32_t pin_len, pout_len; size_t read, written; const void *srcchannels; void *dstchannels; uint32_t i; - int j, k, queued; - bool flushing = false; + int res, j, k; + uint32_t flushing = UINT32_MAX; spa_zero(r); r.cpu_flags = d->cpu_flags; @@ -200,7 +201,10 @@ r.i_rate = d->iinfo.samplerate; r.o_rate = d->oinfo.samplerate; r.quality = d->quality < 0 ? DEFAULT_QUALITY : d->quality; - resample_native_init(&r); + if ((res = resample_native_init(&r)) < 0) { + fprintf(stderr, "can't init converter: %s\n", spa_strerror(res)); + return res; + } for (j = 0; j < channels; j++) srcj = &inMAX_SAMPLES * j; @@ -210,25 +214,29 @@ read = written = queued = 0; while (true) { pout_len = out_len = MAX_SAMPLES; - in_len = SPA_MIN(MAX_SAMPLES, resample_in_len(&r, out_len)) - queued; + in_len = SPA_MIN(MAX_SAMPLES, resample_in_len(&r, out_len)); + in_len -= SPA_MIN(queued, in_len); - pin_len = in_len = sf_readf_float(d->ifile, &ibufqueued * channels, in_len); + if (in_len > 0) { + pin_len = in_len = sf_readf_float(d->ifile, &ibufqueued * channels, in_len); - read += pin_len; + read += pin_len; - if (pin_len == 0) { - if (flushing) - break; + if (pin_len == 0) { + if (flushing == 0) + break; + if (flushing == UINT32_MAX) + flushing = resample_delay(&r); - flushing = true; - pin_len = in_len = resample_delay(&r); + pin_len = in_len = SPA_MIN(MAX_SAMPLES, flushing); + flushing -= in_len; - for (k = 0, i = 0; i < pin_len; i++) { - for (j = 0; j < channels; j++) - ibufk++ = 0.0; + for (k = 0, i = 0; i < pin_len; i++) { + for (j = 0; j < channels; j++) + ibufk++ = 0.0; + } } } - in_len += queued; pin_len = in_len; @@ -243,18 +251,20 @@ if (queued) memmove(ibuf, &ibufpin_len * channels, queued * channels * sizeof(float)); - for (k = 0, i = 0; i < pout_len; i++) { - for (j = 0; j < channels; j++) { - obufk++ = outMAX_SAMPLES * j + i; + if (pout_len > 0) { + for (k = 0, i = 0; i < pout_len; i++) { + for (j = 0; j < channels; j++) { + obufk++ = outMAX_SAMPLES * j + i; + } } - } - pout_len = sf_writef_float(d->ofile, obuf, pout_len); + pout_len = sf_writef_float(d->ofile, obuf, pout_len); - written += pout_len; + written += pout_len; + } } - if (d->verbose) { + if (d->verbose) fprintf(stdout, "read %zu samples, wrote %zu samples\n", read, written); - } + return 0; }
View file
pipewire-0.3.54.tar.gz/spa/plugins/audioconvert/test-audioconvert.c -> pipewire-0.3.56.tar.gz/spa/plugins/audioconvert/test-audioconvert.c
Changed
@@ -703,11 +703,18 @@ static const float data_f32p_4 = { 0.4f, 0.4f, 0.4f, 0.4f }; static const float data_f32p_5 = { 0.5f, 0.5f, 0.5f, 0.5f }; static const float data_f32p_6 = { 0.6f, 0.6f, 0.6f, 0.6f }; +static const float data_f32p_7 = { 0.7f, 0.7f, 0.7f, 0.7f }; +static const float data_f32p_8 = { 0.8f, 0.8f, 0.8f, 0.8f }; static const float data_f32_5p1 = { 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f }; + +static const float data_f32_7p1_remapped = { 0.1f, 0.2f, 0.5f, 0.6f, 0.7f, 0.8f, 0.3f, 0.4f, + 0.1f, 0.2f, 0.5f, 0.6f, 0.7f, 0.8f, 0.3f, 0.4f, + 0.1f, 0.2f, 0.5f, 0.6f, 0.7f, 0.8f, 0.3f, 0.4f, + 0.1f, 0.2f, 0.5f, 0.6f, 0.7f, 0.8f, 0.3f, 0.4f }; static const float data_f32_5p1_remapped = { 0.1f, 0.2f, 0.5f, 0.6f, 0.3f, 0.4f, 0.1f, 0.2f, 0.5f, 0.6f, 0.3f, 0.4f, 0.1f, 0.2f, 0.5f, 0.6f, 0.3f, 0.4f, @@ -753,6 +760,48 @@ .size = sizeof(float) * 4 }; +struct data dsp_7p1_remapped = { + .mode = SPA_PARAM_PORT_CONFIG_MODE_dsp, + .info = SPA_AUDIO_INFO_RAW_INIT( + .format = SPA_AUDIO_FORMAT_F32, + .rate = 48000, + .channels = 8, + .position = { + SPA_AUDIO_CHANNEL_FL, + SPA_AUDIO_CHANNEL_FR, + SPA_AUDIO_CHANNEL_FC, + SPA_AUDIO_CHANNEL_LFE, + SPA_AUDIO_CHANNEL_RL, + SPA_AUDIO_CHANNEL_RR, + SPA_AUDIO_CHANNEL_SL, + SPA_AUDIO_CHANNEL_SR, + }), + .ports = 8, + .planes = 1, + .data = { data_f32p_1, data_f32p_2, data_f32p_3, data_f32p_4, data_f32p_7, data_f32p_8, data_f32p_5, data_f32p_6 }, + .size = sizeof(data_f32p_1) +}; + +struct data dsp_5p1_remapped_2 = { + .mode = SPA_PARAM_PORT_CONFIG_MODE_dsp, + .info = SPA_AUDIO_INFO_RAW_INIT( + .format = SPA_AUDIO_FORMAT_F32, + .rate = 48000, + .channels = 6, + .position = { + SPA_AUDIO_CHANNEL_FC, + SPA_AUDIO_CHANNEL_LFE, + SPA_AUDIO_CHANNEL_RL, + SPA_AUDIO_CHANNEL_RR, + SPA_AUDIO_CHANNEL_FR, + SPA_AUDIO_CHANNEL_FL, + }), + .ports = 6, + .planes = 1, + .data = { data_f32p_3, data_f32p_4, data_f32p_5, data_f32p_6, data_f32p_2, data_f32p_1, }, + .size = sizeof(float) * 4 +}; + struct data conv_f32_48000_5p1 = { .mode = SPA_PARAM_PORT_CONFIG_MODE_convert, .info = SPA_AUDIO_INFO_RAW_INIT( @@ -833,6 +882,28 @@ .size = sizeof(float) * 4 }; +struct data conv_f32_48000_7p1_remapped = { + .mode = SPA_PARAM_PORT_CONFIG_MODE_convert, + .info = SPA_AUDIO_INFO_RAW_INIT( + .format = SPA_AUDIO_FORMAT_F32, + .rate = 48000, + .channels = 8, + .position = { + SPA_AUDIO_CHANNEL_FL, + SPA_AUDIO_CHANNEL_FR, + SPA_AUDIO_CHANNEL_SL, + SPA_AUDIO_CHANNEL_SR, + SPA_AUDIO_CHANNEL_RL, + SPA_AUDIO_CHANNEL_RR, + SPA_AUDIO_CHANNEL_FC, + SPA_AUDIO_CHANNEL_LFE, + }), + .ports = 1, + .planes = 1, + .data = { data_f32_7p1_remapped, }, + .size = sizeof(data_f32_7p1_remapped) +}; + static int test_convert_remap_dsp(struct context *ctx) { run_convert(ctx, &dsp_5p1, &conv_f32_48000_5p1); @@ -843,6 +914,10 @@ run_convert(ctx, &dsp_5p1_remapped, &conv_f32p_48000_5p1); run_convert(ctx, &dsp_5p1_remapped, &conv_f32_48000_5p1_remapped); run_convert(ctx, &dsp_5p1_remapped, &conv_f32p_48000_5p1_remapped); + run_convert(ctx, &dsp_5p1_remapped_2, &conv_f32_48000_5p1); + run_convert(ctx, &dsp_5p1_remapped_2, &conv_f32p_48000_5p1); + run_convert(ctx, &dsp_5p1_remapped_2, &conv_f32_48000_5p1_remapped); + run_convert(ctx, &dsp_5p1_remapped_2, &conv_f32p_48000_5p1_remapped); return 0; } @@ -850,12 +925,17 @@ { run_convert(ctx, &conv_f32_48000_5p1, &dsp_5p1); run_convert(ctx, &conv_f32_48000_5p1, &dsp_5p1_remapped); + run_convert(ctx, &conv_f32_48000_5p1, &dsp_5p1_remapped_2); run_convert(ctx, &conv_f32p_48000_5p1, &dsp_5p1); run_convert(ctx, &conv_f32p_48000_5p1, &dsp_5p1_remapped); + run_convert(ctx, &conv_f32p_48000_5p1, &dsp_5p1_remapped_2); run_convert(ctx, &conv_f32_48000_5p1_remapped, &dsp_5p1); run_convert(ctx, &conv_f32_48000_5p1_remapped, &dsp_5p1_remapped); + run_convert(ctx, &conv_f32_48000_5p1_remapped, &dsp_5p1_remapped_2); run_convert(ctx, &conv_f32p_48000_5p1_remapped, &dsp_5p1); run_convert(ctx, &conv_f32p_48000_5p1_remapped, &dsp_5p1_remapped); + run_convert(ctx, &conv_f32_48000_7p1_remapped, &dsp_7p1_remapped); + run_convert(ctx, &conv_f32p_48000_5p1_remapped, &dsp_5p1_remapped_2); return 0; }
View file
pipewire-0.3.54.tar.gz/spa/plugins/audioconvert/test-fmt-ops.c -> pipewire-0.3.56.tar.gz/spa/plugins/audioconvert/test-fmt-ops.c
Changed
@@ -54,7 +54,7 @@ spa_debug_mem(0, m1, size); spa_debug_mem(0, m2, size); } -// spa_assert_se(res == 0); + spa_assert_se(res == 0); } static void run_test(const char *name, @@ -127,8 +127,9 @@ static void test_f32_s8(void) { - static const float in = { 0.0f, 1.0f, -1.0f, 0.5f, -0.5f, 1.1f, -1.1f }; - static const int8_t out = { 0, 127, -128, 64, 192, 127, -128 }; + static const float in = { 0.0f, 1.0f, -1.0f, 0.5f, -0.5f, 1.1f, -1.1f, + 1.0f/160.f, 1.0f/256.f, -1.0f/160.f, -1.0f/256.f }; + static const int8_t out = { 0, 127, -128, 64, 192, 127, -128, 1, 0, -1, 0 }; run_test("test_f32_s8", in, sizeof(in0), out, sizeof(out0), SPA_N_ELEMENTS(out), true, true, conv_f32_to_s8_c); @@ -157,8 +158,9 @@ static void test_f32_u8(void) { - static const float in = { 0.0f, 1.0f, -1.0f, 0.5f, -0.5f, 1.1f, -1.1f }; - static const uint8_t out = { 128, 255, 0, 192, 64, 255, 0, }; + static const float in = { 0.0f, 1.0f, -1.0f, 0.5f, -0.5f, 1.1f, -1.1f, + 1.0f/160.f, 1.0f/256.f, -1.0f/160.f, -1.0f/256.f }; + static const uint8_t out = { 128, 255, 0, 192, 64, 255, 0, 129, 128, 127, 128 }; run_test("test_f32_u8", in, sizeof(in0), out, sizeof(out0), SPA_N_ELEMENTS(out), true, true, conv_f32_to_u8_c); @@ -187,8 +189,10 @@ static void test_f32_u16(void) { - static const float in = { 0.0f, 1.0f, -1.0f, 0.5f, -0.5f, 1.1f, -1.1f }; - static const uint16_t out = { 32768, 65535, 0, 49152, 16384, 65535, 0 }; + static const float in = { 0.0f, 1.0f, -1.0f, 0.5f, -0.5f, 1.1f, -1.1f, + 1.0f/49152.f, 1.0f/65536.f, -1.0f/49152.f, -1.0f/65536.f }; + static const uint16_t out = { 32768, 65535, 0, 49152, 16384, 65535, 0, + 32769, 32768, 32767, 32768 }; run_test("test_f32_u16", in, sizeof(in0), out, sizeof(out0), SPA_N_ELEMENTS(out), true, true, conv_f32_to_u16_c); @@ -209,8 +213,10 @@ static void test_f32_s16(void) { - static const float in = { 0.0f, 1.0f, -1.0f, 0.5f, -0.5f, 1.1f, -1.1f }; - static const int16_t out = { 0, 32767, -32768, 16384, -16384, 32767, -32768 }; + static const float in = { 0.0f, 1.0f, -1.0f, 0.5f, -0.5f, 1.1f, -1.1f, + 1.0f/49152.f, 1.0f/65536.f, -1.0f/49152.f, -1.0f/65536.f }; + static const int16_t out = { 0, 32767, -32768, 16384, -16384, 32767, -32768, + 1, 0, -1, 0 }; run_test("test_f32_s16", in, sizeof(in0), out, sizeof(out0), SPA_N_ELEMENTS(out), true, true, conv_f32_to_s16_c); @@ -267,9 +273,11 @@ static void test_f32_u32(void) { - static const float in = { 0.0f, 1.0f, -1.0f, 0.5f, -0.5f, 1.1f, -1.1f }; - static const uint32_t out = { 0x80000000, 0xffffffff, 0x0, 0xc0000000, 0x40000000, - 0xffffffff, 0x0 }; + static const float in = { 0.0f, 1.0f, -1.0f, 0.5f, -0.5f, 1.1f, -1.1f, + 1.0f/0xa00000, 1.0f/0x1000000, -1.0f/0xa00000, -1.0f/0x1000000 }; + static const uint32_t out = { 0x80000000, 0xffffff00, 0x0, 0xc0000000, 0x40000000, + 0xffffff00, 0x0, + 0x80000100, 0x80000000, 0x7fffff00, 0x80000000 }; run_test("test_f32_u32", in, sizeof(in0), out, sizeof(out0), SPA_N_ELEMENTS(out), true, true, conv_f32_to_u32_c); @@ -279,8 +287,8 @@ static void test_u32_f32(void) { - static const uint32_t in = { 0x80000000, 0xffffffff, 0x0, 0xc0000000, 0x40000000 }; - static const float out = { 0.0f, 1.0f, -1.0f, 0.5f, -0.5f, }; + static const uint32_t in = { 0x80000000, 0xffffff00, 0x0, 0xc0000000, 0x40000000 }; + static const float out = { 0.0f, 0.999999880791f, -1.0f, 0.5f, -0.5f, }; run_test("test_u32_f32d", in, sizeof(in0), out, sizeof(out0), SPA_N_ELEMENTS(out), true, false, conv_u32_to_f32d_c); @@ -290,9 +298,11 @@ static void test_f32_s32(void) { - static const float in = { 0.0f, 1.0f, -1.0f, 0.5f, -0.5f, 1.1f, -1.1f }; - static const int32_t out = { 0, 0x7fffff80, 0x80000000, 0x40000000, 0xc0000000, - 0x7fffff80, 0x80000000 }; + static const float in = { 0.0f, 1.0f, -1.0f, 0.5f, -0.5f, 1.1f, -1.1f, + 1.0f/0xa00000, 1.0f/0x1000000, -1.0f/0xa00000, -1.0f/0x1000000 }; + static const int32_t out = { 0, 0x7fffff00, 0x80000000, 0x40000000, 0xc0000000, + 0x7fffff00, 0x80000000, + 0x00000100, 0x00000000, 0xffffff00, 0x00000000 }; run_test("test_f32_s32", in, sizeof(in0), out, sizeof(out0), SPA_N_ELEMENTS(out), true, true, conv_f32_to_s32_c); @@ -318,8 +328,8 @@ static void test_s32_f32(void) { - static const int32_t in = { 0, 0x7fffff80, 0x80000000, 0x40000000, 0xc0000000 }; - static const float out = { 0.0f, 0.999999940395f, -1.0f, 0.5, -0.5, }; + static const int32_t in = { 0, 0x7fffff00, 0x80000000, 0x40000000, 0xc0000000 }; + static const float out = { 0.0f, 0.999999880791, -1.0f, 0.5, -0.5, }; run_test("test_s32_f32d", in, sizeof(in0), out, sizeof(out0), SPA_N_ELEMENTS(out), true, false, conv_s32_to_f32d_c); @@ -345,10 +355,13 @@ static void test_f32_u24(void) { - static const float in = { 0.0f, 1.0f, -1.0f, 0.5f, -0.5f, 1.1f, -1.1f }; + static const float in = { 0.0f, 1.0f, -1.0f, 0.5f, -0.5f, 1.1f, -1.1f, + 1.0f/0xa00000, 1.0f/0x1000000, -1.0f/0xa00000, -1.0f/0x1000000 }; static const uint24_t out = { U32_TO_U24(0x00800000), U32_TO_U24(0xffffff), U32_TO_U24(0x000000), U32_TO_U24(0xc00000), U32_TO_U24(0x400000), - U32_TO_U24(0xffffff), U32_TO_U24(0x000000) }; + U32_TO_U24(0xffffff), U32_TO_U24(0x000000), + U32_TO_U24(0x800001), U32_TO_U24(0x800000), U32_TO_U24(0x7fffff), + U32_TO_U24(0x800000) }; run_test("test_f32_u24", in, sizeof(in0), out, 3, SPA_N_ELEMENTS(in), true, true, conv_f32_to_u24_c); @@ -370,10 +383,13 @@ static void test_f32_s24(void) { - static const float in = { 0.0f, 1.0f, -1.0f, 0.5f, -0.5f, 1.1f, -1.1f }; + static const float in = { 0.0f, 1.0f, -1.0f, 0.5f, -0.5f, 1.1f, -1.1f, + 1.0f/0xa00000, 1.0f/0x1000000, -1.0f/0xa00000, -1.0f/0x1000000 }; static const int24_t out = { S32_TO_S24(0), S32_TO_S24(0x7fffff), S32_TO_S24(0xff800000), S32_TO_S24(0x400000), S32_TO_S24(0xc00000), - S32_TO_S24(0x7fffff), S32_TO_S24(0xff800000) }; + S32_TO_S24(0x7fffff), S32_TO_S24(0xff800000), + S32_TO_S24(0x000001), S32_TO_S24(0x000000), S32_TO_S24(0xffffffff), + S32_TO_S24(0x000000) }; run_test("test_f32_s24", in, sizeof(in0), out, 3, SPA_N_ELEMENTS(in), true, true, conv_f32_to_s24_c); @@ -427,9 +443,11 @@ static void test_f32_u24_32(void) { - static const float in = { 0.0f, 1.0f, -1.0f, 0.5f, -0.5f, 1.1f, -1.1f }; + static const float in = { 0.0f, 1.0f, -1.0f, 0.5f, -0.5f, 1.1f, -1.1f, + 1.0f/0xa00000, 1.0f/0x1000000, -1.0f/0xa00000, -1.0f/0x1000000 }; static const uint32_t out = { 0x800000, 0xffffff, 0x0, 0xc00000, 0x400000, - 0xffffff, 0x000000 }; + 0xffffff, 0x000000, + 0x800001, 0x800000, 0x7fffff, 0x800000 }; run_test("test_f32_u24_32", in, sizeof(in0), out, sizeof(out0), SPA_N_ELEMENTS(out), true, true, conv_f32_to_u24_32_c); @@ -439,8 +457,8 @@ static void test_u24_32_f32(void) { - static const uint32_t in = { 0x800000, 0xffffff, 0x0, 0xc00000, 0x400000 }; - static const float out = { 0.0f, 0.999999880791f, -1.0f, 0.5f, -0.5f, }; + static const uint32_t in = { 0x800000, 0xffffff, 0x0, 0xc00000, 0x400000, 0x11000000 }; + static const float out = { 0.0f, 0.999999880791f, -1.0f, 0.5f, -0.5f, -1.0f }; run_test("test_u24_32_f32d", in, sizeof(in0), out, sizeof(out0), SPA_N_ELEMENTS(out), true, false, conv_u24_32_to_f32d_c); @@ -450,9 +468,11 @@ static void test_f32_s24_32(void) { - static const float in = { 0.0f, 1.0f, -1.0f, 0.5f, -0.5f, 1.1f, -1.1f }; + static const float in = { 0.0f, 1.0f, -1.0f, 0.5f, -0.5f, 1.1f, -1.1f, + 1.0f/0xa00000, 1.0f/0x1000000, -1.0f/0xa00000, -1.0f/0x1000000 }; static const int32_t out = { 0, 0x7fffff, 0xff800000, 0x400000, 0xffc00000, - 0x7fffff, 0xff800000 }; + 0x7fffff, 0xff800000, + 0x000001, 0x000000, 0xffffffff, 0x000000 }; run_test("test_f32_s24_32", in, sizeof(in0), out, sizeof(out0), SPA_N_ELEMENTS(out), true, true, conv_f32_to_s24_32_c); @@ -466,8 +486,8 @@ static void test_s24_32_f32(void) { - static const int32_t in = { 0, 0x7fffff, 0xff800000, 0x400000, 0xffc00000 }; - static const float out = { 0.0f, 0.999999880791f, -1.0f, 0.5f, -0.5f, }; + static const int32_t in = { 0, 0x7fffff, 0xff800000, 0x400000, 0xffc00000, 0x66800000 }; + static const float out = { 0.0f, 0.999999880791f, -1.0f, 0.5f, -0.5f, -1.0f }; run_test("test_s24_32_f32d", in, sizeof(in0), out, sizeof(out0), SPA_N_ELEMENTS(out), true, false, conv_s24_32_to_f32d_c); @@ -588,7 +608,7 @@ for (i = S32_MIN; i < S32_MAX; i+=255) { float v = S32_TO_F32(i); int32_t t = F32_TO_S32(v); - spa_assert_se(SPA_ABS(i - t) <= 128); + spa_assert_se(SPA_ABS(i - t) <= 256); }
View file
pipewire-0.3.56.tar.gz/spa/plugins/audiomixer/benchmark-mix-ops.c
Added
@@ -0,0 +1,222 @@ +/* Spa + * + * Copyright © 2019 Wim Taymans + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "config.h" + +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <time.h> + +#include "test-helper.h" +#include "mix-ops.h" + +static uint32_t cpu_flags; + +typedef void (*mix_func_t) (struct mix_ops *ops, void * SPA_RESTRICT dst, + const void * SPA_RESTRICT src, uint32_t n_src, uint32_t n_samples); +struct stats { + uint32_t n_samples; + uint32_t n_src; + uint64_t perf; + const char *name; + const char *impl; +}; + +#define MAX_SAMPLES 4096 +#define MAX_SRC 11 + +#define MAX_COUNT 100 + +static uint8_t samp_inMAX_SAMPLES * MAX_SRC * 8; +static uint8_t samp_outMAX_SAMPLES * 8; + +static const int sample_sizes = { 0, 1, 128, 513, 4096 }; +static const int src_counts = { 1, 2, 4, 6, 8, 11 }; + +#define MAX_RESULTS SPA_N_ELEMENTS(sample_sizes) * SPA_N_ELEMENTS(src_counts) * 70 + +static uint32_t n_results = 0; +static struct stats resultsMAX_RESULTS; + +static void run_test1(const char *name, const char *impl, mix_func_t func, int n_src, int n_samples) +{ + int i, j; + const void *ipn_src; + void *op; + struct timespec ts; + uint64_t count, t1, t2; + struct mix_ops mix; + + mix.n_channels = 1; + + for (j = 0; j < n_src; j++) + ipj = SPA_PTR_ALIGN(&samp_inj * n_samples * 4, 32, void); + op = SPA_PTR_ALIGN(samp_out, 32, void); + + clock_gettime(CLOCK_MONOTONIC, &ts); + t1 = SPA_TIMESPEC_TO_NSEC(&ts); + + count = 0; + for (i = 0; i < MAX_COUNT; i++) { + func(&mix, op, ip, n_src, n_samples); + count++; + } + clock_gettime(CLOCK_MONOTONIC, &ts); + t2 = SPA_TIMESPEC_TO_NSEC(&ts); + + spa_assert(n_results < MAX_RESULTS); + + resultsn_results++ = (struct stats) { + .n_samples = n_samples, + .n_src = n_src, + .perf = count * (uint64_t)SPA_NSEC_PER_SEC / (t2 - t1), + .name = name, + .impl = impl + }; +} + +static void run_test(const char *name, const char *impl, mix_func_t func) +{ + size_t i, j; + + for (i = 0; i < SPA_N_ELEMENTS(sample_sizes); i++) { + for (j = 0; j < SPA_N_ELEMENTS(src_counts); j++) { + run_test1(name, impl, func, src_countsj, + (sample_sizesi + (src_countsj -1)) / src_countsj); + } + } +} + +static void test_s8(void) +{ + run_test("test_s8", "c", mix_s8_c); +} +static void test_u8(void) +{ + run_test("test_u8", "c", mix_u8_c); +} + +static void test_s16(void) +{ + run_test("test_s16", "c", mix_s16_c); +} +static void test_u16(void) +{ + run_test("test_u8", "c", mix_u16_c); +} + +static void test_s24(void) +{ + run_test("test_s24", "c", mix_s24_c); +} +static void test_u24(void) +{ + run_test("test_u24", "c", mix_u24_c); +} +static void test_s24_32(void) +{ + run_test("test_s24_32", "c", mix_s24_32_c); +} +static void test_u24_32(void) +{ + run_test("test_u24_32", "c", mix_u24_32_c); +} + +static void test_s32(void) +{ + run_test("test_s32", "c", mix_s32_c); +} +static void test_u32(void) +{ + run_test("test_u32", "c", mix_u32_c); +} + +static void test_f32(void) +{ + run_test("test_f32", "c", mix_f32_c); +#if defined (HAVE_SSE) + if (cpu_flags & SPA_CPU_FLAG_SSE) { + run_test("test_f32", "sse", mix_f32_sse); + } +#endif +#if defined (HAVE_AVX) + if (cpu_flags & SPA_CPU_FLAG_AVX) { + run_test("test_f32", "avx", mix_f32_avx); + } +#endif +} + +static void test_f64(void) +{ + run_test("test_f64", "c", mix_f64_c); +#if defined (HAVE_SSE2) + if (cpu_flags & SPA_CPU_FLAG_SSE2) { + run_test("test_f64", "sse2", mix_f64_sse2); + } +#endif +} + +static int compare_func(const void *_a, const void *_b) +{ + const struct stats *a = _a, *b = _b; + int diff; + if ((diff = strcmp(a->name, b->name)) != 0) return diff; + if ((diff = a->n_samples - b->n_samples) != 0) return diff; + if ((diff = a->n_src - b->n_src) != 0) return diff; + if ((diff = b->perf - a->perf) != 0) return diff; + return 0; +} + +int main(int argc, char *argv) +{ + uint32_t i; + + cpu_flags = get_cpu_flags(); + printf("got get CPU flags %d\n", cpu_flags);
View file
pipewire-0.3.54.tar.gz/spa/plugins/audiomixer/meson.build -> pipewire-0.3.56.tar.gz/spa/plugins/audiomixer/meson.build
Changed
@@ -1,6 +1,5 @@ audiomixer_sources = 'audiomixer.c', - 'mix-ops.c', 'mixer-dsp.c', 'plugin.c' @@ -47,11 +46,81 @@ simd_dependencies += audiomixer_avx endif -audiomixerlib = shared_library('spa-audiomixer', +audiomixer_lib = static_library('audiomixer', + 'mix-ops.c' , + c_args : simd_cargs, '-O3', + link_with : simd_dependencies, + include_directories : configinc, + dependencies : spa_dep , + install : false + ) +audiomixer_dep = declare_dependency(link_with: audiomixer_lib) + +spa_audiomixer_lib = shared_library('spa-audiomixer', audiomixer_sources, c_args : simd_cargs, link_with : simd_dependencies, - dependencies : spa_dep, mathlib , + dependencies : spa_dep, mathlib, audiomixer_dep , install : true, install_dir : spa_plugindir / 'audiomixer' ) +spa_audiomixer_dep = declare_dependency(link_with: spa_audiomixer_lib) + +test_apps = + 'test-mix-ops', + + +foreach a : test_apps + test(a, + executable(a, a + '.c', + dependencies : spa_dep, dl_lib, pthread_lib, mathlib, audiomixer_dep , + include_directories : configinc , + link_with : test_lib , + install_rpath : spa_plugindir / 'audiomixer', + c_args : simd_cargs , + install : installed_tests_enabled, + install_dir : installed_tests_execdir / 'audiomixer'), + env : + 'SPA_PLUGIN_DIR=@0@'.format(spa_dep.get_variable('plugindir')), + ) + + if installed_tests_enabled + test_conf = configuration_data() + test_conf.set('exec', installed_tests_execdir / 'audiomixer' / a) + configure_file( + input: installed_tests_template, + output: a + '.test', + install_dir: installed_tests_metadir / 'audiomixer', + configuration: test_conf + ) + endif +endforeach + +benchmark_apps = + 'benchmark-mix-ops', + + +foreach a : benchmark_apps + benchmark(a, + executable(a, a + '.c', + dependencies : spa_dep, dl_lib, pthread_lib, mathlib, audiomixer_dep , + include_directories : configinc , + c_args : simd_cargs , + install_rpath : spa_plugindir / 'audiomixer', + install : installed_tests_enabled, + install_dir : installed_tests_execdir / 'audiomixer'), + env : + 'SPA_PLUGIN_DIR=@0@'.format(spa_dep.get_variable('plugindir')), + ) + + if installed_tests_enabled + test_conf = configuration_data() + test_conf.set('exec', installed_tests_execdir / 'audiomixer' / a) + configure_file( + input: installed_tests_template, + output: a + '.test', + install_dir: installed_tests_metadir / 'audiomixer', + configuration: test_conf + ) + endif +endforeach
View file
pipewire-0.3.54.tar.gz/spa/plugins/audiomixer/mix-ops-avx.c -> pipewire-0.3.56.tar.gz/spa/plugins/audiomixer/mix-ops-avx.c
Changed
@@ -86,50 +86,59 @@ static inline void mix_2(float * dst, const float * SPA_RESTRICT src, uint32_t n_samples) { - uint32_t n, unrolled; - - if (SPA_IS_ALIGNED(src, 32) && - SPA_IS_ALIGNED(dst, 32)) - unrolled = n_samples & ~15; - else - unrolled = 0; - - for (n = 0; n < unrolled; n += 16) { - __m256 in12, in22; - - in10 = _mm256_load_ps(&dstn + 0); - in11 = _mm256_load_ps(&dstn + 8); - in20 = _mm256_load_ps(&srcn + 0); - in21 = _mm256_load_ps(&srcn + 8); - - in10 = _mm256_add_ps(in10, in20); - in11 = _mm256_add_ps(in11, in21); - - _mm256_store_ps(&dstn + 0, in10); - _mm256_store_ps(&dstn + 8, in11); - } - for (; n < n_samples; n++) { - __m128 in11, in21; - in10 = _mm_load_ss(&dstn), - in20 = _mm_load_ss(&srcn), - in10 = _mm_add_ss(in10, in20); - _mm_store_ss(&dstn, in10); - } } void mix_f32_avx(struct mix_ops *ops, void * SPA_RESTRICT dst, const void * SPA_RESTRICT src, uint32_t n_src, uint32_t n_samples) { - uint32_t i; + n_samples *= ops->n_channels; if (n_src == 0) memset(dst, 0, n_samples * ops->n_channels * sizeof(float)); - else if (dst != src0) - spa_memcpy(dst, src0, n_samples * ops->n_channels * sizeof(float)); - - for (i = 1; i + 2 < n_src; i += 3) - mix_4(dst, srci, srci + 1, srci + 2, n_samples); - for (; i < n_src; i++) - mix_2(dst, srci, n_samples * ops->n_channels); + else if (n_src == 1) { + if (dst != src0) + spa_memcpy(dst, src0, n_samples * sizeof(float)); + } else { + uint32_t i, n, unrolled; + const float **s = (const float **)src; + float *d = dst; + + if (SPA_LIKELY(SPA_IS_ALIGNED(dst, 32))) { + unrolled = n_samples & ~31; + for (i = 0; i < n_src; i++) { + if (SPA_UNLIKELY(!SPA_IS_ALIGNED(srci, 32))) { + unrolled = 0; + break; + } + } + } else + unrolled = 0; + + for (n = 0; n < unrolled; n += 32) { + __m256 in4; + + in0 = _mm256_load_ps(&s0n + 0); + in1 = _mm256_load_ps(&s0n + 8); + in2 = _mm256_load_ps(&s0n + 16); + in3 = _mm256_load_ps(&s0n + 24); + for (i = 1; i < n_src; i++) { + in0 = _mm256_add_ps(in0, _mm256_load_ps(&sin + 0)); + in1 = _mm256_add_ps(in1, _mm256_load_ps(&sin + 8)); + in2 = _mm256_add_ps(in2, _mm256_load_ps(&sin + 16)); + in3 = _mm256_add_ps(in3, _mm256_load_ps(&sin + 24)); + } + _mm256_store_ps(&dn + 0, in0); + _mm256_store_ps(&dn + 8, in1); + _mm256_store_ps(&dn + 16, in2); + _mm256_store_ps(&dn + 24, in3); + } + for (; n < n_samples; n++) { + __m128 in1; + in0 = _mm_load_ss(&s0n); + for (i = 1; i < n_src; i++) + in0 = _mm_add_ss(in0, _mm_load_ss(&sin)); + _mm_store_ss(&dn, in0); + } + } }
View file
pipewire-0.3.54.tar.gz/spa/plugins/audiomixer/mix-ops-c.c -> pipewire-0.3.56.tar.gz/spa/plugins/audiomixer/mix-ops-c.c
Changed
@@ -30,236 +30,39 @@ #include "mix-ops.h" -void -mix_s8_c(struct mix_ops *ops, void * SPA_RESTRICT dst, const void * SPA_RESTRICT src, - uint32_t n_src, uint32_t n_samples) -{ - uint32_t i, n; - int8_t *d = dst; - - if (n_src == 0) - memset(dst, 0, n_samples * ops->n_channels * sizeof(int8_t)); - else if (dst != src0) - spa_memcpy(dst, src0, n_samples * ops->n_channels * sizeof(int8_t)); - - for (i = 1; i < n_src; i++) { - const int8_t *s = srci; - for (n = 0; n < n_samples * ops->n_channels; n++) - dn = S8_MIX(dn, sn); - } -} - -void -mix_u8_c(struct mix_ops *ops, void * SPA_RESTRICT dst, const void * SPA_RESTRICT src, - uint32_t n_src, uint32_t n_samples) -{ - uint32_t i, n; - uint8_t *d = dst; - - if (n_src == 0) - memset(dst, 0, n_samples * ops->n_channels * sizeof(uint8_t)); - else if (dst != src0) - spa_memcpy(dst, src0, n_samples * ops->n_channels * sizeof(uint8_t)); - - for (i = 1; i < n_src; i++) { - const uint8_t *s = srci; - for (n = 0; n < n_samples * ops->n_channels; n++) - dn = U8_MIX(dn, sn); - } +#define MAKE_FUNC(name,type,atype,accum,clamp,zero) \ +void mix_ ##name## _c(struct mix_ops *ops, \ + void * SPA_RESTRICT dst, const void * SPA_RESTRICT src, \ + uint32_t n_src, uint32_t n_samples) \ +{ \ + uint32_t i, n; \ + type *d = dst; \ + const type **s = (const type **)src; \ + n_samples *= ops->n_channels; \ + if (n_src == 0 && zero) \ + memset(dst, 0, n_samples * sizeof(type)); \ + else if (n_src == 1) { \ + if (dst != src0) \ + spa_memcpy(dst, src0, n_samples * sizeof(type)); \ + } else { \ + for (n = 0; n < n_samples; n++) { \ + atype ac = 0; \ + for (i = 0; i < n_src; i++) \ + ac = accum (ac, sin); \ + dn = clamp (ac); \ + } \ + } \ } -void -mix_s16_c(struct mix_ops *ops, void * SPA_RESTRICT dst, const void * SPA_RESTRICT src, - uint32_t n_src, uint32_t n_samples) -{ - uint32_t i, n; - int16_t *d = dst; - - if (n_src == 0) - memset(dst, 0, n_samples * ops->n_channels * sizeof(int16_t)); - else if (dst != src0) - spa_memcpy(dst, src0, n_samples * ops->n_channels * sizeof(int16_t)); - - for (i = 1; i < n_src; i++) { - const int16_t *s = srci; - for (n = 0; n < n_samples * ops->n_channels; n++) - dn = S16_MIX(dn, sn); - } -} - -void -mix_u16_c(struct mix_ops *ops, void * SPA_RESTRICT dst, const void * SPA_RESTRICT src, - uint32_t n_src, uint32_t n_samples) -{ - uint32_t i, n; - uint16_t *d = dst; - - if (n_src == 0) - memset(dst, 0, n_samples * ops->n_channels * sizeof(uint16_t)); - else if (dst != src0) - spa_memcpy(dst, src0, n_samples * ops->n_channels * sizeof(uint16_t)); - - for (i = 1; i < n_src; i++) { - const uint16_t *s = srci; - for (n = 0; n < n_samples * ops->n_channels; n++) - dn = U16_MIX(dn, sn); - } -} - -void -mix_s24_c(struct mix_ops *ops, void * SPA_RESTRICT dst, const void * SPA_RESTRICT src, - uint32_t n_src, uint32_t n_samples) -{ - uint32_t i, n; - uint8_t *d = dst; - - if (n_src == 0) - memset(dst, 0, n_samples * ops->n_channels * sizeof(uint8_t) * 3); - else if (dst != src0) - spa_memcpy(dst, src0, n_samples * ops->n_channels * sizeof(uint8_t) * 3); - - for (i = 1; i < n_src; i++) { - const uint8_t *s = srci; - for (n = 0; n < n_samples * ops->n_channels; n++) { - write_s24(d, S24_MIX(read_s24(d), read_s24(s))); - d += 3; - s += 3; - } - } -} - -void -mix_u24_c(struct mix_ops *ops, void * SPA_RESTRICT dst, const void * SPA_RESTRICT src, - uint32_t n_src, uint32_t n_samples) -{ - uint32_t i, n; - uint8_t *d = dst; - - if (n_src == 0) - memset(dst, 0, n_samples * ops->n_channels * sizeof(uint8_t) * 3); - else if (dst != src0) - spa_memcpy(dst, src0, n_samples * ops->n_channels * sizeof(uint8_t) * 3); - - for (i = 1; i < n_src; i++) { - const uint8_t *s = srci; - for (n = 0; n < n_samples * ops->n_channels; n++) { - write_u24(d, U24_MIX(read_u24(d), read_u24(s))); - d += 3; - s += 3; - } - } -} - -void -mix_s32_c(struct mix_ops *ops, void * SPA_RESTRICT dst, const void * SPA_RESTRICT src, - uint32_t n_src, uint32_t n_samples) -{ - uint32_t i, n; - int32_t *d = dst; - - if (n_src == 0) - memset(dst, 0, n_samples * ops->n_channels * sizeof(int32_t)); - else if (dst != src0) - spa_memcpy(dst, src0, n_samples * ops->n_channels * sizeof(int32_t)); - - for (i = 1; i < n_src; i++) { - const int32_t *s = srci; - for (n = 0; n < n_samples * ops->n_channels; n++) - dn = S32_MIX(dn, sn); - } -} - -void -mix_u32_c(struct mix_ops *ops, void * SPA_RESTRICT dst, const void * SPA_RESTRICT src, - uint32_t n_src, uint32_t n_samples) -{ - uint32_t i, n; - uint32_t *d = dst; - - if (n_src == 0) - memset(dst, 0, n_samples * ops->n_channels * sizeof(uint32_t)); - else if (dst != src0) - spa_memcpy(dst, src0, n_samples * ops->n_channels * sizeof(uint32_t)); - - for (i = 1; i < n_src; i++) { - const uint32_t *s = srci; - for (n = 0; n < n_samples * ops->n_channels; n++) - dn = U32_MIX(dn, sn); - } -} - -void -mix_s24_32_c(struct mix_ops *ops, void * SPA_RESTRICT dst, const void * SPA_RESTRICT src, - uint32_t n_src, uint32_t n_samples) -{ - uint32_t i, n; - int32_t *d = dst; - - if (n_src == 0) - memset(dst, 0, n_samples * ops->n_channels * sizeof(int32_t)); - else if (dst != src0) - spa_memcpy(dst, src0, n_samples * ops->n_channels * sizeof(int32_t)); - - for (i = 1; i < n_src; i++) { - const int32_t *s = srci; - for (n = 0; n < n_samples * ops->n_channels; n++) - dn = S24_32_MIX(dn, sn);
View file
pipewire-0.3.54.tar.gz/spa/plugins/audiomixer/mix-ops-sse.c -> pipewire-0.3.56.tar.gz/spa/plugins/audiomixer/mix-ops-sse.c
Changed
@@ -32,58 +32,56 @@ #include <xmmintrin.h> -static inline void mix_2(float * dst, const float * SPA_RESTRICT src, uint32_t n_samples) -{ - uint32_t n, unrolled; - __m128 in14, in24; - - if (SPA_LIKELY(SPA_IS_ALIGNED(src, 16) && - SPA_IS_ALIGNED(dst, 16))) - unrolled = n_samples & ~15; - else - unrolled = 0; - - for (n = 0; n < unrolled; n += 16) { - in10 = _mm_load_ps(&dstn+ 0); - in11 = _mm_load_ps(&dstn+ 4); - in12 = _mm_load_ps(&dstn+ 8); - in13 = _mm_load_ps(&dstn+12); - - in20 = _mm_load_ps(&srcn+ 0); - in21 = _mm_load_ps(&srcn+ 4); - in22 = _mm_load_ps(&srcn+ 8); - in23 = _mm_load_ps(&srcn+12); - - in10 = _mm_add_ps(in10, in20); - in11 = _mm_add_ps(in11, in21); - in12 = _mm_add_ps(in12, in22); - in13 = _mm_add_ps(in13, in23); - - _mm_store_ps(&dstn+ 0, in10); - _mm_store_ps(&dstn+ 4, in11); - _mm_store_ps(&dstn+ 8, in12); - _mm_store_ps(&dstn+12, in13); - } - for (; n < n_samples; n++) { - in10 = _mm_load_ss(&dstn), - in20 = _mm_load_ss(&srcn), - in10 = _mm_add_ss(in10, in20); - _mm_store_ss(&dstn, in10); - } -} - void mix_f32_sse(struct mix_ops *ops, void * SPA_RESTRICT dst, const void * SPA_RESTRICT src, uint32_t n_src, uint32_t n_samples) { - uint32_t i; + n_samples *= ops->n_channels; + + if (n_src == 0) { + memset(dst, 0, n_samples * sizeof(float)); + } else if (n_src == 1) { + if (dst != src0) + spa_memcpy(dst, src0, n_samples * sizeof(float)); + } else { + uint32_t n, i, unrolled; + __m128 in4; + const float **s = (const float **)src; + float *d = dst; + + if (SPA_LIKELY(SPA_IS_ALIGNED(dst, 16))) { + unrolled = n_samples & ~15; + for (i = 0; i < n_src; i++) { + if (SPA_UNLIKELY(!SPA_IS_ALIGNED(srci, 16))) { + unrolled = 0; + break; + } + } + } else + unrolled = 0; - if (n_src == 0) - memset(dst, 0, n_samples * ops->n_channels * sizeof(float)); - else if (dst != src0) - spa_memcpy(dst, src0, n_samples * ops->n_channels * sizeof(float)); + for (n = 0; n < unrolled; n += 16) { + in0 = _mm_load_ps(&s0n+ 0); + in1 = _mm_load_ps(&s0n+ 4); + in2 = _mm_load_ps(&s0n+ 8); + in3 = _mm_load_ps(&s0n+12); - for (i = 1; i < n_src; i++) { - mix_2(dst, srci, n_samples * ops->n_channels); + for (i = 1; i < n_src; i++) { + in0 = _mm_add_ps(in0, _mm_load_ps(&sin+ 0)); + in1 = _mm_add_ps(in1, _mm_load_ps(&sin+ 4)); + in2 = _mm_add_ps(in2, _mm_load_ps(&sin+ 8)); + in3 = _mm_add_ps(in3, _mm_load_ps(&sin+12)); + } + _mm_store_ps(&dn+ 0, in0); + _mm_store_ps(&dn+ 4, in1); + _mm_store_ps(&dn+ 8, in2); + _mm_store_ps(&dn+12, in3); + } + for (; n < n_samples; n++) { + in0 = _mm_load_ss(&s0n); + for (i = 1; i < n_src; i++) + in0 = _mm_add_ss(in0, _mm_load_ss(&sin)); + _mm_store_ss(&dn, in0); + } } }
View file
pipewire-0.3.54.tar.gz/spa/plugins/audiomixer/mix-ops-sse2.c -> pipewire-0.3.56.tar.gz/spa/plugins/audiomixer/mix-ops-sse2.c
Changed
@@ -32,58 +32,56 @@ #include <emmintrin.h> -static inline void mix_2(double * dst, const double * SPA_RESTRICT src, uint32_t n_samples) -{ - uint32_t n, unrolled; - __m128d in14, in24; - - if (SPA_IS_ALIGNED(src, 16) && - SPA_IS_ALIGNED(dst, 16)) - unrolled = n_samples & ~7; - else - unrolled = 0; - - for (n = 0; n < unrolled; n += 8) { - in10 = _mm_load_pd(&dstn+ 0); - in11 = _mm_load_pd(&dstn+ 2); - in12 = _mm_load_pd(&dstn+ 4); - in13 = _mm_load_pd(&dstn+ 6); - - in20 = _mm_load_pd(&srcn+ 0); - in21 = _mm_load_pd(&srcn+ 2); - in22 = _mm_load_pd(&srcn+ 4); - in23 = _mm_load_pd(&srcn+ 6); - - in10 = _mm_add_pd(in10, in20); - in11 = _mm_add_pd(in11, in21); - in12 = _mm_add_pd(in12, in22); - in13 = _mm_add_pd(in13, in23); - - _mm_store_pd(&dstn+ 0, in10); - _mm_store_pd(&dstn+ 2, in11); - _mm_store_pd(&dstn+ 4, in12); - _mm_store_pd(&dstn+ 6, in13); - } - for (; n < n_samples; n++) { - in10 = _mm_load_sd(&dstn), - in20 = _mm_load_sd(&srcn), - in10 = _mm_add_sd(in10, in20); - _mm_store_sd(&dstn, in10); - } -} - void mix_f64_sse2(struct mix_ops *ops, void * SPA_RESTRICT dst, const void * SPA_RESTRICT src, uint32_t n_src, uint32_t n_samples) { - uint32_t i; + n_samples *= ops->n_channels; + + if (n_src == 0) { + memset(dst, 0, n_samples * sizeof(double)); + } else if (n_src == 1) { + if (dst != src0) + spa_memcpy(dst, src0, n_samples * sizeof(double)); + } else { + uint32_t n, i, unrolled; + __m128d in4; + const double **s = (const double **)src; + double *d = dst; + + if (SPA_LIKELY(SPA_IS_ALIGNED(dst, 16))) { + unrolled = n_samples & ~15; + for (i = 0; i < n_src; i++) { + if (SPA_UNLIKELY(!SPA_IS_ALIGNED(srci, 16))) { + unrolled = 0; + break; + } + } + } else + unrolled = 0; - if (n_src == 0) - memset(dst, 0, n_samples * ops->n_channels * sizeof(double)); - else if (dst != src0) - spa_memcpy(dst, src0, n_samples * ops->n_channels * sizeof(double)); + for (n = 0; n < unrolled; n += 8) { + in0 = _mm_load_pd(&s0n+0); + in1 = _mm_load_pd(&s0n+2); + in2 = _mm_load_pd(&s0n+4); + in3 = _mm_load_pd(&s0n+6); - for (i = 1; i < n_src; i++) { - mix_2(dst, srci, n_samples * ops->n_channels); + for (i = 1; i < n_src; i++) { + in0 = _mm_add_pd(in0, _mm_load_pd(&sin+0)); + in1 = _mm_add_pd(in1, _mm_load_pd(&sin+2)); + in2 = _mm_add_pd(in2, _mm_load_pd(&sin+4)); + in3 = _mm_add_pd(in3, _mm_load_pd(&sin+6)); + } + _mm_store_pd(&dn+0, in0); + _mm_store_pd(&dn+2, in1); + _mm_store_pd(&dn+4, in2); + _mm_store_pd(&dn+6, in3); + } + for (; n < n_samples; n++) { + in0 = _mm_load_sd(&s0n); + for (i = 1; i < n_src; i++) + in0 = _mm_add_sd(in0, _mm_load_sd(&sin)); + _mm_store_sd(&dn, in0); + } } }
View file
pipewire-0.3.54.tar.gz/spa/plugins/audiomixer/mix-ops.h -> pipewire-0.3.56.tar.gz/spa/plugins/audiomixer/mix-ops.h
Changed
@@ -24,80 +24,98 @@ #include <spa/utils/defs.h> -static inline uint32_t read_u24(const void *src) -{ - const uint8_t *s = src; +typedef struct { #if __BYTE_ORDER == __LITTLE_ENDIAN - return (((uint32_t)s2 << 16) | ((uint32_t)(uint8_t)s1 << 8) | (uint32_t)(uint8_t)s0); + uint8_t v3; + uint8_t v2; + uint8_t v1; #else - return (((uint32_t)s0 << 16) | ((uint32_t)(uint8_t)s1 << 8) | (uint32_t)(uint8_t)s2); + uint8_t v1; + uint8_t v2; + uint8_t v3; #endif -} +} __attribute__ ((packed)) uint24_t; -static inline int32_t read_s24(const void *src) -{ - const int8_t *s = src; +typedef struct { #if __BYTE_ORDER == __LITTLE_ENDIAN - return (((int32_t)s2 << 16) | ((uint32_t)(uint8_t)s1 << 8) | (uint32_t)(uint8_t)s0); + uint8_t v3; + uint8_t v2; + int8_t v1; #else - return (((int32_t)s0 << 16) | ((uint32_t)(uint8_t)s1 << 8) | (uint32_t)(uint8_t)s2); + int8_t v1; + uint8_t v2; + uint8_t v3; #endif -} +} __attribute__ ((packed)) int24_t; -static inline void write_u24(void *dst, uint32_t val) +static inline uint32_t u24_to_u32(uint24_t src) { - uint8_t *d = dst; -#if __BYTE_ORDER == __LITTLE_ENDIAN - d0 = (uint8_t) (val); - d1 = (uint8_t) (val >> 8); - d2 = (uint8_t) (val >> 16); -#else - d0 = (uint8_t) (val >> 16); - d1 = (uint8_t) (val >> 8); - d2 = (uint8_t) (val); -#endif + return ((uint32_t)src.v1 << 16) | ((uint32_t)src.v2 << 8) | (uint32_t)src.v3; } -static inline void write_s24(void *dst, int32_t val) +#define U32_TO_U24(s) (uint24_t) { .v1 = (uint8_t)(((uint32_t)s) >> 16), \ + .v2 = (uint8_t)(((uint32_t)s) >> 8), .v3 = (uint8_t)((uint32_t)s) } + +static inline uint24_t u32_to_u24(uint32_t src) { - uint8_t *d = dst; -#if __BYTE_ORDER == __LITTLE_ENDIAN - d0 = (uint8_t) (val); - d1 = (uint8_t) (val >> 8); - d2 = (uint8_t) (val >> 16); -#else - d0 = (uint8_t) (val >> 16); - d1 = (uint8_t) (val >> 8); - d2 = (uint8_t) (val); -#endif + return U32_TO_U24(src); } -#define S8_MIN -127 -#define S8_MAX 127 -#define S8_MIX(a, b) (int8_t)(SPA_CLAMP((int16_t)(a) + (int16_t)(b), S8_MIN, S8_MAX)) -#define U8_MIX(a, b) (uint8_t)((int16_t)S8_MIX((int16_t)(a) - S8_MAX, (int16_t)(b) - S8_MAX) + S8_MAX) - -#define S16_MIN -32767 -#define S16_MAX 32767 -#define S16_MIX(a, b) (int16_t)(SPA_CLAMP((int32_t)(a) + (int32_t)(b), S16_MIN, S16_MAX)) -#define U16_MIX(a, b) (uint16_t)((int32_t)S16_MIX((int32_t)(a) - S16_MAX, (int32_t)(b) - S16_MAX) + S16_MAX) - -#define S24_MIN -8388607 -#define S24_MAX 8388607 -#define S24_MIX(a, b) (int32_t)(SPA_CLAMP((int32_t)(a) + (int32_t)(b), S24_MIN, S24_MAX)) -#define U24_MIX(a, b) (uint32_t)((int32_t)S24_MIX((int32_t)(a) - S24_MAX, (int32_t)(b) - S24_MAX) + S24_MAX) +static inline int32_t s24_to_s32(int24_t src) +{ + return ((int32_t)src.v1 << 16) | ((uint32_t)src.v2 << 8) | (uint32_t)src.v3; +} -#define S32_MIN -2147483647 -#define S32_MAX 2147483647 -#define S32_MIX(a, b) (int32_t)(SPA_CLAMP((int64_t)(a) + (int64_t)(b), S32_MIN, S32_MAX)) -#define U32_MIX(a, b) (uint32_t)((int64_t)S32_MIX((int64_t)(a) - S32_MAX, (int64_t)(b) - S32_MAX) + S32_MAX) +#define S32_TO_S24(s) (int24_t) { .v1 = (int8_t)(((int32_t)s) >> 16), \ + .v2 = (uint8_t)(((uint32_t)s) >> 8), .v3 = (uint8_t)((uint32_t)s) } -#define S24_32_MIX(a, b) S24_MIX (a, b) -#define U24_32_MIX(a, b) U24_MIX (a, b) +static inline int24_t s32_to_s24(int32_t src) +{ + return S32_TO_S24(src); +} -#define F32_MIX(a, b) (float)((float)(a) + (float)(b)) -#define F64_MIX(a, b) (double)((double)(a) + (double)(b)) +#define S8_MIN -128 +#define S8_MAX 127 +#define S8_ACCUM(a,b) ((a) + (int16_t)(b)) +#define S8_CLAMP(a) (int8_t)(SPA_CLAMP((a), S8_MIN, S8_MAX)) +#define U8_OFFS 128 +#define U8_ACCUM(a,b) ((a) + ((int16_t)(b) - U8_OFFS)) +#define U8_CLAMP(a) (uint8_t)(SPA_CLAMP((a), S8_MIN, S8_MAX) + U8_OFFS) + +#define S16_MIN -32768 +#define S16_MAX 32767 +#define S16_ACCUM(a,b) ((a) + (int32_t)(b)) +#define S16_CLAMP(a) (int16_t)(SPA_CLAMP((a), S16_MIN, S16_MAX)) +#define U16_OFFS 32768 +#define U16_ACCUM(a,b) ((a) + ((int32_t)(b) - U16_OFFS)) +#define U16_CLAMP(a) (uint16_t)(SPA_CLAMP((a), S16_MIN, S16_MAX) + U16_OFFS) + +#define S24_32_MIN -8388608 +#define S24_32_MAX 8388607 +#define S24_32_ACCUM(a,b) ((a) + (int32_t)(b)) +#define S24_32_CLAMP(a) (int32_t)(SPA_CLAMP((a), S24_32_MIN, S24_32_MAX)) +#define U24_32_OFFS 8388608 +#define U24_32_ACCUM(a,b) ((a) + ((int32_t)(b) - U24_32_OFFS)) +#define U24_32_CLAMP(a) (uint32_t)(SPA_CLAMP((a), S24_32_MIN, S24_32_MAX) + U24_32_OFFS) + +#define S24_ACCUM(a,b) S24_32_ACCUM(a, s24_to_s32(b)) +#define S24_CLAMP(a) s32_to_s24(S24_32_CLAMP(a)) +#define U24_ACCUM(a,b) U24_32_ACCUM(a, u24_to_u32(b)) +#define U24_CLAMP(a) u32_to_u24(U24_32_CLAMP(a)) + +#define S32_MIN -2147483648 +#define S32_MAX 2147483647 +#define S32_ACCUM(a,b) ((a) + (int64_t)(b)) +#define S32_CLAMP(a) (int32_t)(SPA_CLAMP((a), S32_MIN, S32_MAX)) +#define U32_OFFS 2147483648 +#define U32_ACCUM(a,b) ((a) + ((int64_t)(b) - U32_OFFS)) +#define U32_CLAMP(a) (uint32_t)(SPA_CLAMP((a), S32_MIN, S32_MAX) + U32_OFFS) + +#define F32_ACCUM(a,b) ((a) + (b)) +#define F32_CLAMP(a) (a) +#define F64_ACCUM(a,b) ((a) + (b)) +#define F64_CLAMP(a) (a) struct mix_ops { uint32_t fmt;
View file
pipewire-0.3.54.tar.gz/spa/plugins/audiomixer/mixer-dsp.c -> pipewire-0.3.56.tar.gz/spa/plugins/audiomixer/mixer-dsp.c
Changed
@@ -105,7 +105,6 @@ uint32_t cpu_flags; uint32_t max_align; - struct spa_io_position *io_position; uint32_t quantum_limit; struct mix_ops ops; @@ -153,20 +152,7 @@ static int impl_node_set_io(void *object, uint32_t id, void *data, size_t size) { - struct impl *this = object; - - spa_return_val_if_fail(this != NULL, -EINVAL); - - spa_log_debug(this->log, "%p: io %d %p/%zd", this, id, data, size); - - switch (id) { - case SPA_IO_Position: - this->io_position = data; - break; - default: - return -ENOENT; - } - return 0; + return -ENOTSUP; } static int impl_node_send_command(void *object, const struct spa_command *command) @@ -708,10 +694,7 @@ datas = alloca(MAX_PORTS * sizeof(void *)); n_buffers = 0; - if (SPA_LIKELY(this->io_position)) - maxsize = this->io_position->clock.duration * sizeof(float); - else - maxsize = this->quantum_limit; + maxsize = UINT32_MAX; for (i = 0; i < this->last_port; i++) { struct port *inport = GET_IN_PORT(this, i); @@ -867,7 +850,6 @@ this->max_align = SPA_MIN(MAX_ALIGN, spa_cpu_get_max_align(this->cpu)); } - this->quantum_limit = 8192; for (i = 0; info && i < info->n_items; i++) { const char *k = info->itemsi.key; const char *s = info->itemsi.value;
View file
pipewire-0.3.56.tar.gz/spa/plugins/audiomixer/test-helper.h
Added
@@ -0,0 +1,97 @@ +#include <dlfcn.h> + +#include <spa/support/plugin.h> +#include <spa/utils/type.h> +#include <spa/utils/result.h> +#include <spa/support/cpu.h> +#include <spa/utils/names.h> + +static inline const struct spa_handle_factory *get_factory(spa_handle_factory_enum_func_t enum_func, + const char *name, uint32_t version) +{ + uint32_t i; + int res; + const struct spa_handle_factory *factory; + + for (i = 0;;) { + if ((res = enum_func(&factory, &i)) <= 0) { + if (res < 0) + errno = -res; + break; + } + if (factory->version >= version && + !strcmp(factory->name, name)) + return factory; + } + return NULL; +} + +static inline struct spa_handle *load_handle(const struct spa_support *support, + uint32_t n_support, const char *lib, const char *name) +{ + int res, len; + void *hnd; + spa_handle_factory_enum_func_t enum_func; + const struct spa_handle_factory *factory; + struct spa_handle *handle; + const char *str; + char *path; + + if ((str = getenv("SPA_PLUGIN_DIR")) == NULL) + str = PLUGINDIR; + + len = strlen(str) + strlen(lib) + 2; + path = alloca(len); + snprintf(path, len, "%s/%s", str, lib); + + if ((hnd = dlopen(path, RTLD_NOW)) == NULL) { + fprintf(stderr, "can't load %s: %s\n", lib, dlerror()); + res = -ENOENT; + goto error; + } + if ((enum_func = dlsym(hnd, SPA_HANDLE_FACTORY_ENUM_FUNC_NAME)) == NULL) { + fprintf(stderr, "can't find enum function\n"); + res = -ENXIO; + goto error_close; + } + + if ((factory = get_factory(enum_func, name, SPA_VERSION_HANDLE_FACTORY)) == NULL) { + fprintf(stderr, "can't find factory\n"); + res = -ENOENT; + goto error_close; + } + handle = calloc(1, spa_handle_factory_get_size(factory, NULL)); + if ((res = spa_handle_factory_init(factory, handle, + NULL, support, n_support)) < 0) { + fprintf(stderr, "can't make factory instance: %d\n", res); + goto error_close; + } + return handle; + +error_close: + dlclose(hnd); +error: + errno = -res; + return NULL; +} + +static inline uint32_t get_cpu_flags(void) +{ + struct spa_handle *handle; + uint32_t flags; + void *iface; + int res; + + handle = load_handle(NULL, 0, "support/libspa-support.so", SPA_NAME_SUPPORT_CPU); + if (handle == NULL) + return 0; + if ((res = spa_handle_get_interface(handle, SPA_TYPE_INTERFACE_CPU, &iface)) < 0) { + fprintf(stderr, "can't get CPU interface %s\n", spa_strerror(res)); + return 0; + } + flags = spa_cpu_get_flags((struct spa_cpu*)iface); + + free(handle); + + return flags; +}
View file
pipewire-0.3.56.tar.gz/spa/plugins/audiomixer/test-mix-ops.c
Added
@@ -0,0 +1,293 @@ +/* Spa + * + * Copyright © 2022 Wim Taymans + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "config.h" + +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <time.h> + +#include <spa/debug/mem.h> + +#include "test-helper.h" +#include "mix-ops.c" + +static uint32_t cpu_flags; + +#define N_SAMPLES 1024 + +static uint8_t samp_outN_SAMPLES * 8; + +static void compare_mem(int i, int j, const void *m1, const void *m2, size_t size) +{ + int res = memcmp(m1, m2, size); + if (res != 0) { + fprintf(stderr, "%d %d %zd:\n", i, j, size); + spa_debug_mem(0, m1, size); + spa_debug_mem(0, m2, size); + } + spa_assert_se(res == 0); +} + +static int run_test(const char *name, const void *src, uint32_t n_src, const void *dst, + size_t dst_size, uint32_t n_samples, mix_func_t mix) +{ + struct mix_ops ops; + + ops.fmt = SPA_AUDIO_FORMAT_F32; + ops.n_channels = 1; + ops.cpu_flags = cpu_flags; + mix_ops_init(&ops); + + fprintf(stderr, "%s\n", name); + + mix(&ops, (void *)samp_out, src, n_src, n_samples); + compare_mem(0, 0, samp_out, dst, dst_size); + return 0; +} + +static void test_s8(void) +{ + int8_t out = { 0x00, 0x00, 0x00, 0x00 }; + int8_t in_1 = { 0x00, 0x00, 0x00, 0x00 }; + int8_t in_2 = { 0x7f, 0x80, 0x40, 0xc0 }; + int8_t in_3 = { 0x40, 0xc0, 0xc0, 0x40 }; + int8_t in_4 = { 0xc0, 0x40, 0x40, 0xc0 }; + int8_t out_4 = { 0x7f, 0x80, 0x40, 0xc0 }; + const void *src6 = { in_1, in_2, in_3, in_4 }; + + run_test("test_s8_0", NULL, 0, out, sizeof(out), SPA_N_ELEMENTS(out), mix_s8_c); + run_test("test_s8_1", src, 1, in_1, sizeof(in_1), SPA_N_ELEMENTS(in_1), mix_s8_c); + run_test("test_s8_4", src, 4, out_4, sizeof(out_4), SPA_N_ELEMENTS(out_4), mix_s8_c); +} + +static void test_u8(void) +{ + uint8_t out = { 0x80, 0x80, 0x80, 0x80 }; + uint8_t in_1 = { 0x80, 0x80, 0x80, 0x80 }; + uint8_t in_2 = { 0xff, 0x00, 0xc0, 0x40 }; + uint8_t in_3 = { 0xc0, 0x40, 0x40, 0xc0 }; + uint8_t in_4 = { 0x40, 0xc0, 0xc0, 0x40 }; + uint8_t out_4 = { 0xff, 0x00, 0xc0, 0x40 }; + const void *src6 = { in_1, in_2, in_3, in_4 }; + + run_test("test_u8_0", NULL, 0, out, sizeof(out), SPA_N_ELEMENTS(out), mix_u8_c); + run_test("test_u8_1", src, 1, in_1, sizeof(in_1), SPA_N_ELEMENTS(in_1), mix_u8_c); + run_test("test_u8_4", src, 4, out_4, sizeof(out_4), SPA_N_ELEMENTS(out_4), mix_u8_c); +} + +static void test_s16(void) +{ + int16_t out = { 0x0000, 0x0000, 0x0000, 0x0000 }; + int16_t in_1 = { 0x0000, 0x0000, 0x0000, 0x0000 }; + int16_t in_2 = { 0x7fff, 0x8000, 0x4000, 0xc000 }; + int16_t in_3 = { 0x4000, 0xc000, 0xc000, 0x4000 }; + int16_t in_4 = { 0xc000, 0x4000, 0x4000, 0xc000 }; + int16_t out_4 = { 0x7fff, 0x8000, 0x4000, 0xc000 }; + const void *src6 = { in_1, in_2, in_3, in_4 }; + + run_test("test_s16_0", NULL, 0, out, sizeof(out), SPA_N_ELEMENTS(out), mix_s16_c); + run_test("test_s16_1", src, 1, in_1, sizeof(in_1), SPA_N_ELEMENTS(in_1), mix_s16_c); + run_test("test_s16_4", src, 4, out_4, sizeof(out_4), SPA_N_ELEMENTS(out_4), mix_s16_c); +} + +static void test_u16(void) +{ + uint16_t out = { 0x8000, 0x8000, 0x8000, 0x8000 }; + uint16_t in_1 = { 0x8000, 0x8000, 0x8000 , 0x8000}; + uint16_t in_2 = { 0xffff, 0x0000, 0xc000, 0x4000 }; + uint16_t in_3 = { 0xc000, 0x4000, 0x4000, 0xc000 }; + uint16_t in_4 = { 0x4000, 0xc000, 0xc000, 0x4000 }; + uint16_t out_4 = { 0xffff, 0x0000, 0xc000, 0x4000 }; + const void *src6 = { in_1, in_2, in_3, in_4 }; + + run_test("test_u16_0", NULL, 0, out, sizeof(out), SPA_N_ELEMENTS(out), mix_u16_c); + run_test("test_u16_1", src, 1, in_1, sizeof(in_1), SPA_N_ELEMENTS(in_1), mix_u16_c); + run_test("test_u16_4", src, 4, out_4, sizeof(out_4), SPA_N_ELEMENTS(out_4), mix_u16_c); +} + +static void test_s24(void) +{ + int24_t out = { S32_TO_S24(0x000000), S32_TO_S24(0x000000), S32_TO_S24(0x000000) }; + int24_t in_1 = { S32_TO_S24(0x000000), S32_TO_S24(0x000000), S32_TO_S24(0x000000) }; + int24_t in_2 = { S32_TO_S24(0x7fffff), S32_TO_S24(0xff800000), S32_TO_S24(0x400000) }; + int24_t in_3 = { S32_TO_S24(0x400000), S32_TO_S24(0xffc00000), S32_TO_S24(0xffc00000) }; + int24_t in_4 = { S32_TO_S24(0xffc00000), S32_TO_S24(0x400000), S32_TO_S24(0x400000) }; + int24_t out_4 = { S32_TO_S24(0x7fffff), S32_TO_S24(0xff800000), S32_TO_S24(0x400000) }; + const void *src6 = { in_1, in_2, in_3, in_4 }; + + run_test("test_s24_0", NULL, 0, out, sizeof(out), SPA_N_ELEMENTS(out), mix_s24_c); + run_test("test_s24_1", src, 1, in_1, sizeof(in_1), SPA_N_ELEMENTS(in_1), mix_s24_c); + run_test("test_s24_4", src, 4, out_4, sizeof(out_4), SPA_N_ELEMENTS(out_4), mix_s24_c); +} + +static void test_u24(void) +{ + uint24_t out = { U32_TO_U24(0x800000), U32_TO_U24(0x800000), U32_TO_U24(0x800000) }; + uint24_t in_1 = { U32_TO_U24(0x800000), U32_TO_U24(0x800000), U32_TO_U24(0x800000) }; + uint24_t in_2 = { U32_TO_U24(0xffffffff), U32_TO_U24(0x000000), U32_TO_U24(0xffc00000) }; + uint24_t in_3 = { U32_TO_U24(0xffc00000), U32_TO_U24(0x400000), U32_TO_U24(0x400000) }; + uint24_t in_4 = { U32_TO_U24(0x400000), U32_TO_U24(0xffc00000), U32_TO_U24(0xffc00000) }; + uint24_t out_4 = { U32_TO_U24(0xffffffff), U32_TO_U24(0x000000), U32_TO_U24(0xffc00000) }; + const void *src6 = { in_1, in_2, in_3, in_4 }; + + run_test("test_u24_0", NULL, 0, out, sizeof(out), SPA_N_ELEMENTS(out), mix_u24_c); + run_test("test_u24_1", src, 1, in_1, sizeof(in_1), SPA_N_ELEMENTS(in_1), mix_u24_c); + run_test("test_u24_4", src, 4, out_4, sizeof(out_4), SPA_N_ELEMENTS(out_4), mix_u24_c); +} + +static void test_s32(void) +{ + int32_t out = { 0x00000000, 0x00000000, 0x00000000, 0x00000000 }; + int32_t in_1 = { 0x00000000, 0x00000000, 0x00000000, 0x00000000 }; + int32_t in_2 = { 0x7fffffff, 0x80000000, 0x40000000, 0xc0000000 }; + int32_t in_3 = { 0x40000000, 0xc0000000, 0xc0000000, 0x40000000 }; + int32_t in_4 = { 0xc0000000, 0x40000000, 0x40000000, 0xc0000000 }; + int32_t out_4 = { 0x7fffffff, 0x80000000, 0x40000000, 0xc0000000 }; + const void *src6 = { in_1, in_2, in_3, in_4 }; + + run_test("test_s32_0", NULL, 0, out, sizeof(out), SPA_N_ELEMENTS(out), mix_s32_c); + run_test("test_s32_1", src, 1, in_1, sizeof(in_1), SPA_N_ELEMENTS(in_1), mix_s32_c); + run_test("test_s32_4", src, 4, out_4, sizeof(out_4), SPA_N_ELEMENTS(out_4), mix_s32_c); +} + +static void test_u32(void) +{ + uint32_t out = { 0x80000000, 0x80000000, 0x80000000, 0x80000000 }; + uint32_t in_1 = { 0x80000000, 0x80000000, 0x80000000, 0x80000000 }; + uint32_t in_2 = { 0xffffffff, 0x00000000, 0xc0000000, 0x40000000 }; + uint32_t in_3 = { 0xc0000000, 0x40000000, 0x40000000, 0xc0000000 }; + uint32_t in_4 = { 0x40000000, 0xc0000000, 0xc0000000, 0x40000000 }; + uint32_t out_4 = { 0xffffffff, 0x00000000, 0xc0000000, 0x40000000 }; + const void *src6 = { in_1, in_2, in_3, in_4 }; + + run_test("test_u32_0", NULL, 0, out, sizeof(out), SPA_N_ELEMENTS(out), mix_u32_c); + run_test("test_u32_1", src, 1, in_1, sizeof(in_1), SPA_N_ELEMENTS(in_1), mix_u32_c); + run_test("test_u32_4", src, 4, out_4, sizeof(out_4), SPA_N_ELEMENTS(out_4), mix_u32_c); +} + +static void test_s24_32(void) +{ + int32_t out = { 0x000000, 0x000000, 0x000000, 0x000000 }; + int32_t in_1 = { 0x000000, 0x000000, 0x000000, 0x000000 }; + int32_t in_2 = { 0x7fffff, 0xff800000, 0x400000, 0xffc00000 }; + int32_t in_3 = { 0x400000, 0xffc00000, 0xffc00000, 0x400000 }; + int32_t in_4 = { 0xffc00000, 0x400000, 0x400000, 0xffc00000 };
View file
pipewire-0.3.56.tar.gz/spa/plugins/avb
Added
+(directory)
View file
pipewire-0.3.56.tar.gz/spa/plugins/avb/avb-pcm-sink.c
Added
@@ -0,0 +1,912 @@ +/* Spa AVB PCM Sink + * + * Copyright © 2022 Wim Taymans + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include <stddef.h> + +#include <spa/node/node.h> +#include <spa/node/utils.h> +#include <spa/node/keys.h> +#include <spa/monitor/device.h> +#include <spa/utils/keys.h> +#include <spa/utils/names.h> +#include <spa/utils/string.h> +#include <spa/param/audio/format.h> +#include <spa/pod/filter.h> +#include <spa/debug/pod.h> + +#include "avb-pcm.h" + +#define CHECK_PORT(this,d,p) ((d) == SPA_DIRECTION_INPUT && (p) == 0) +#define GET_PORT(this,d,p) (&this->portsp) + +static void reset_props(struct props *props) +{ + snprintf(props->ifname, sizeof(props->ifname), "%s", DEFAULT_IFNAME); + parse_addr(props->addr, DEFAULT_ADDR); + props->prio = DEFAULT_PRIO; + parse_streamid(&props->streamid, DEFAULT_STREAMID); + props->mtt = DEFAULT_MTT; + props->t_uncertainty = DEFAULT_TU; + props->frames_per_pdu = DEFAULT_FRAMES_PER_PDU; +} + +static void emit_node_info(struct state *this, bool full) +{ + uint64_t old = full ? this->info.change_mask : 0; + + if (full) + this->info.change_mask = this->info_all; + if (this->info.change_mask) { + struct spa_dict_item items4; + uint32_t i, n_items = 0; + + itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_API, "avb"); + itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_MEDIA_CLASS, "Audio/Sink"); + itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_NODE_DRIVER, "true"); + this->info.props = &SPA_DICT_INIT(items, n_items); + + if (this->info.change_mask & SPA_NODE_CHANGE_MASK_PARAMS) { + for (i = 0; i < this->info.n_params; i++) { + if (this->paramsi.user > 0) { + this->paramsi.flags ^= SPA_PARAM_INFO_SERIAL; + this->paramsi.user = 0; + } + } + } + spa_node_emit_info(&this->hooks, &this->info); + + this->info.change_mask = old; + } +} + +static void emit_port_info(struct state *this, struct port *port, bool full) +{ + uint64_t old = full ? port->info.change_mask : 0; + + if (full) + port->info.change_mask = port->info_all; + if (port->info.change_mask) { + uint32_t i; + + if (port->info.change_mask & SPA_PORT_CHANGE_MASK_PARAMS) { + for (i = 0; i < port->info.n_params; i++) { + if (port->paramsi.user > 0) { + port->paramsi.flags ^= SPA_PARAM_INFO_SERIAL; + port->paramsi.user = 0; + } + } + } + spa_node_emit_port_info(&this->hooks, + port->direction, port->id, &port->info); + port->info.change_mask = old; + } +} + +static int impl_node_enum_params(void *object, int seq, + uint32_t id, uint32_t start, uint32_t num, + const struct spa_pod *filter) +{ + struct state *this = object; + struct spa_pod *param; + struct spa_pod_builder b = { 0 }; + uint8_t buffer4096; + struct spa_result_node_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_PropInfo: + { + switch (result.index) { + default: + param = spa_avb_enum_propinfo(this, result.index, &b); + if (param == NULL) + return 0; + } + break; + } + case SPA_PARAM_Props: + { + struct spa_pod_frame f; + + switch (result.index) { + case 0: + spa_pod_builder_push_object(&b, &f, + SPA_TYPE_OBJECT_Props, id); + spa_pod_builder_add(&b, + SPA_PROP_latencyOffsetNsec, SPA_POD_Long(this->process_latency.ns), + 0); + spa_avb_add_prop_params(this, &b); + param = spa_pod_builder_pop(&b, &f); + break; + default: + return 0; + } + break; + } + case SPA_PARAM_IO: + switch (result.index) { + case 0: + param = spa_pod_builder_add_object(&b, + SPA_TYPE_OBJECT_ParamIO, id, + SPA_PARAM_IO_id, SPA_POD_Id(SPA_IO_Clock), + SPA_PARAM_IO_size, SPA_POD_Int(sizeof(struct spa_io_clock))); + break; + case 1: + param = spa_pod_builder_add_object(&b, + SPA_TYPE_OBJECT_ParamIO, id, + SPA_PARAM_IO_id, SPA_POD_Id(SPA_IO_Position), + SPA_PARAM_IO_size, SPA_POD_Int(sizeof(struct spa_io_position))); + break; + default: + return 0; + } + break; + + case SPA_PARAM_ProcessLatency: + switch (result.index) { + case 0: + param = spa_process_latency_build(&b, id, &this->process_latency); + break; + default: + return 0; + } + break; + + default: + return -ENOENT; + } + + if (spa_pod_filter(&b, &result.param, param, filter) < 0) + goto next; + + spa_node_emit_result(&this->hooks, seq, 0, SPA_RESULT_TYPE_NODE_PARAMS, &result); + + if (++count != num) + goto next; + + return 0; +}
View file
pipewire-0.3.56.tar.gz/spa/plugins/avb/avb-pcm-source.c
Added
@@ -0,0 +1,912 @@ +/* Spa AVB PCM Source + * + * Copyright © 2022 Wim Taymans + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include <stddef.h> + +#include <spa/node/node.h> +#include <spa/node/utils.h> +#include <spa/node/keys.h> +#include <spa/monitor/device.h> +#include <spa/utils/keys.h> +#include <spa/utils/names.h> +#include <spa/utils/string.h> +#include <spa/param/audio/format.h> +#include <spa/pod/filter.h> +#include <spa/debug/pod.h> + +#include "avb-pcm.h" + +#define CHECK_PORT(this,d,p) ((d) == SPA_DIRECTION_OUTPUT && (p) == 0) +#define GET_PORT(this,d,p) (&this->portsp) + +static void reset_props(struct props *props) +{ + snprintf(props->ifname, sizeof(props->ifname), "%s", DEFAULT_IFNAME); + parse_addr(props->addr, DEFAULT_ADDR); + props->prio = DEFAULT_PRIO; + parse_streamid(&props->streamid, DEFAULT_STREAMID); + props->mtt = DEFAULT_MTT; + props->t_uncertainty = DEFAULT_TU; + props->frames_per_pdu = DEFAULT_FRAMES_PER_PDU; +} + +static void emit_node_info(struct state *this, bool full) +{ + uint64_t old = full ? this->info.change_mask : 0; + + if (full) + this->info.change_mask = this->info_all; + if (this->info.change_mask) { + struct spa_dict_item items4; + uint32_t i, n_items = 0; + + itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_API, "avb"); + itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_MEDIA_CLASS, "Audio/Source"); + itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_NODE_DRIVER, "true"); + this->info.props = &SPA_DICT_INIT(items, n_items); + + if (this->info.change_mask & SPA_NODE_CHANGE_MASK_PARAMS) { + for (i = 0; i < this->info.n_params; i++) { + if (this->paramsi.user > 0) { + this->paramsi.flags ^= SPA_PARAM_INFO_SERIAL; + this->paramsi.user = 0; + } + } + } + spa_node_emit_info(&this->hooks, &this->info); + + this->info.change_mask = old; + } +} + +static void emit_port_info(struct state *this, struct port *port, bool full) +{ + uint64_t old = full ? port->info.change_mask : 0; + + if (full) + port->info.change_mask = port->info_all; + if (port->info.change_mask) { + uint32_t i; + + if (port->info.change_mask & SPA_PORT_CHANGE_MASK_PARAMS) { + for (i = 0; i < port->info.n_params; i++) { + if (port->paramsi.user > 0) { + port->paramsi.flags ^= SPA_PARAM_INFO_SERIAL; + port->paramsi.user = 0; + } + } + } + spa_node_emit_port_info(&this->hooks, + port->direction, port->id, &port->info); + port->info.change_mask = old; + } +} + +static int impl_node_enum_params(void *object, int seq, + uint32_t id, uint32_t start, uint32_t num, + const struct spa_pod *filter) +{ + struct state *this = object; + struct spa_pod *param; + struct spa_pod_builder b = { 0 }; + uint8_t buffer4096; + struct spa_result_node_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_PropInfo: + { + switch (result.index) { + default: + param = spa_avb_enum_propinfo(this, result.index, &b); + if (param == NULL) + return 0; + } + break; + } + case SPA_PARAM_Props: + { + struct spa_pod_frame f; + + switch (result.index) { + case 0: + spa_pod_builder_push_object(&b, &f, + SPA_TYPE_OBJECT_Props, id); + spa_pod_builder_add(&b, + SPA_PROP_latencyOffsetNsec, SPA_POD_Long(this->process_latency.ns), + 0); + spa_avb_add_prop_params(this, &b); + param = spa_pod_builder_pop(&b, &f); + break; + default: + return 0; + } + break; + } + case SPA_PARAM_IO: + switch (result.index) { + case 0: + param = spa_pod_builder_add_object(&b, + SPA_TYPE_OBJECT_ParamIO, id, + SPA_PARAM_IO_id, SPA_POD_Id(SPA_IO_Clock), + SPA_PARAM_IO_size, SPA_POD_Int(sizeof(struct spa_io_clock))); + break; + case 1: + param = spa_pod_builder_add_object(&b, + SPA_TYPE_OBJECT_ParamIO, id, + SPA_PARAM_IO_id, SPA_POD_Id(SPA_IO_Position), + SPA_PARAM_IO_size, SPA_POD_Int(sizeof(struct spa_io_position))); + break; + default: + return 0; + } + break; + + case SPA_PARAM_ProcessLatency: + switch (result.index) { + case 0: + param = spa_process_latency_build(&b, id, &this->process_latency); + break; + default: + return 0; + } + break; + + default: + return -ENOENT; + } + + if (spa_pod_filter(&b, &result.param, param, filter) < 0) + goto next; + + spa_node_emit_result(&this->hooks, seq, 0, SPA_RESULT_TYPE_NODE_PARAMS, &result); + + if (++count != num) + goto next; + + return 0; +}
View file
pipewire-0.3.56.tar.gz/spa/plugins/avb/avb-pcm.c
Added
@@ -0,0 +1,1217 @@ +/* Spa AVB PCM + * + * Copyright © 2022 Wim Taymans + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sched.h> +#include <errno.h> +#include <getopt.h> +#include <sys/time.h> +#include <math.h> +#include <limits.h> +#include <unistd.h> +#include <sys/ioctl.h> +#include <arpa/inet.h> + +#include <spa/pod/filter.h> +#include <spa/utils/string.h> +#include <spa/support/system.h> +#include <spa/utils/keys.h> + +#include "avb-pcm.h" + +#define TAI_OFFSET (37ULL * SPA_NSEC_PER_SEC) +#define TAI_TO_UTC(t) (t - TAI_OFFSET) + +static int avb_set_param(struct state *state, const char *k, const char *s) +{ + struct props *p = &state->props; + int fmt_change = 0; + if (spa_streq(k, SPA_KEY_AUDIO_CHANNELS)) { + state->default_channels = atoi(s); + fmt_change++; + } else if (spa_streq(k, SPA_KEY_AUDIO_RATE)) { + state->default_rate = atoi(s); + fmt_change++; + } else if (spa_streq(k, SPA_KEY_AUDIO_FORMAT)) { + state->default_format = spa_avb_format_from_name(s, strlen(s)); + fmt_change++; + } else if (spa_streq(k, SPA_KEY_AUDIO_POSITION)) { + spa_avb_parse_position(&state->default_pos, s, strlen(s)); + fmt_change++; + } else if (spa_streq(k, SPA_KEY_AUDIO_ALLOWED_RATES)) { + state->n_allowed_rates = spa_avb_parse_rates(state->allowed_rates, + MAX_RATES, s, strlen(s)); + fmt_change++; + } else if (spa_streq(k, "avb.ifname")) { + snprintf(p->ifname, sizeof(p->ifname), "%s", s); + } else if (spa_streq(k, "avb.macaddr")) { + parse_addr(p->addr, s); + } else if (spa_streq(k, "avb.prio")) { + p->prio = atoi(s); + } else if (spa_streq(k, "avb.streamid")) { + parse_streamid(&p->streamid, s); + } else if (spa_streq(k, "avb.mtt")) { + p->mtt = atoi(s); + } else if (spa_streq(k, "avb.time-uncertainty")) { + p->t_uncertainty = atoi(s); + } else if (spa_streq(k, "avb.frames-per-pdu")) { + p->frames_per_pdu = atoi(s); + } else if (spa_streq(k, "avb.ptime-tolerance")) { + p->ptime_tolerance = atoi(s); + } else if (spa_streq(k, "latency.internal.rate")) { + state->process_latency.rate = atoi(s); + } else if (spa_streq(k, "latency.internal.ns")) { + state->process_latency.ns = atoi(s); + } else if (spa_streq(k, "clock.name")) { + spa_scnprintf(state->clock_name, + sizeof(state->clock_name), "%s", s); + } else + return 0; + + if (fmt_change > 0) { + struct port *port = &state->ports0; + port->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS; + port->paramsPORT_EnumFormat.user++; + } + return 1; +} + +static int position_to_string(struct channel_map *map, char *val, size_t len) +{ + uint32_t i, o = 0; + int r; + o += snprintf(val, len, " "); + for (i = 0; i < map->channels; i++) { + r = snprintf(val+o, len-o, "%s%s", i == 0 ? "" : ", ", + spa_debug_type_find_short_name(spa_type_audio_channel, + map->posi)); + if (r < 0 || o + r >= len) + return -ENOSPC; + o += r; + } + if (len > o) + o += snprintf(val+o, len-o, " "); + return 0; +} + +static int uint32_array_to_string(uint32_t *vals, uint32_t n_vals, char *val, size_t len) +{ + uint32_t i, o = 0; + int r; + o += snprintf(val, len, " "); + for (i = 0; i < n_vals; i++) { + r = snprintf(val+o, len-o, "%s%d", i == 0 ? "" : ", ", valsi); + if (r < 0 || o + r >= len) + return -ENOSPC; + o += r; + } + if (len > o) + o += snprintf(val+o, len-o, " "); + return 0; +} + +struct spa_pod *spa_avb_enum_propinfo(struct state *state, + uint32_t idx, struct spa_pod_builder *b) +{ + struct spa_pod *param; + struct props *p = &state->props; + char tmp128; + + switch (idx) { + case 0: + param = spa_pod_builder_add_object(b, + SPA_TYPE_OBJECT_PropInfo, SPA_PARAM_PropInfo, + SPA_PROP_INFO_name, SPA_POD_String(SPA_KEY_AUDIO_CHANNELS), + SPA_PROP_INFO_description, SPA_POD_String("Audio Channels"), + SPA_PROP_INFO_type, SPA_POD_Int(state->default_channels), + SPA_PROP_INFO_params, SPA_POD_Bool(true)); + break; + case 1: + param = spa_pod_builder_add_object(b, + SPA_TYPE_OBJECT_PropInfo, SPA_PARAM_PropInfo, + SPA_PROP_INFO_name, SPA_POD_String(SPA_KEY_AUDIO_RATE), + SPA_PROP_INFO_description, SPA_POD_String("Audio Rate"), + SPA_PROP_INFO_type, SPA_POD_Int(state->default_rate), + SPA_PROP_INFO_params, SPA_POD_Bool(true)); + break; + case 2: + param = spa_pod_builder_add_object(b, + SPA_TYPE_OBJECT_PropInfo, SPA_PARAM_PropInfo, + SPA_PROP_INFO_name, SPA_POD_String(SPA_KEY_AUDIO_FORMAT), + SPA_PROP_INFO_description, SPA_POD_String("Audio Format"), + SPA_PROP_INFO_type, SPA_POD_String( + spa_debug_type_find_short_name(spa_type_audio_format, + state->default_format)), + SPA_PROP_INFO_params, SPA_POD_Bool(true)); + break; + case 3: + { + char buf1024; + position_to_string(&state->default_pos, buf, sizeof(buf)); + param = spa_pod_builder_add_object(b, + SPA_TYPE_OBJECT_PropInfo, SPA_PARAM_PropInfo, + SPA_PROP_INFO_name, SPA_POD_String(SPA_KEY_AUDIO_POSITION), + SPA_PROP_INFO_description, SPA_POD_String("Audio Position"), + SPA_PROP_INFO_type, SPA_POD_String(buf), + SPA_PROP_INFO_params, SPA_POD_Bool(true)); + break; + } + case 4: + { + char buf1024; + uint32_array_to_string(state->allowed_rates, state->n_allowed_rates, buf, sizeof(buf)); + param = spa_pod_builder_add_object(b, + SPA_TYPE_OBJECT_PropInfo, SPA_PARAM_PropInfo, + SPA_PROP_INFO_name, SPA_POD_String(SPA_KEY_AUDIO_ALLOWED_RATES), + SPA_PROP_INFO_description, SPA_POD_String("Audio Allowed Rates"), + SPA_PROP_INFO_type, SPA_POD_String(buf), + SPA_PROP_INFO_params, SPA_POD_Bool(true)); + break; + } + case 5: + param = spa_pod_builder_add_object(b, + SPA_TYPE_OBJECT_PropInfo, SPA_PARAM_PropInfo, + SPA_PROP_INFO_name, SPA_POD_String("avb.ifname"), + SPA_PROP_INFO_description, SPA_POD_String("The AVB interface name"), + SPA_PROP_INFO_type, SPA_POD_Stringn(p->ifname, sizeof(p->ifname)),
View file
pipewire-0.3.56.tar.gz/spa/plugins/avb/avb-pcm.h
Added
@@ -0,0 +1,343 @@ +/* Spa AVB PCM + * + * Copyright © 2022 Wim Taymans + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef SPA_AVB_PCM_H +#define SPA_AVB_PCM_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stddef.h> +#include <math.h> +#include <linux/if_ether.h> +#include <linux/if_packet.h> +#include <linux/net_tstamp.h> +#include <limits.h> +#include <net/if.h> + +#include <avbtp/packets.h> + +#include <spa/support/plugin.h> +#include <spa/support/loop.h> +#include <spa/utils/list.h> +#include <spa/utils/json.h> +#include <spa/utils/dll.h> + +#include <spa/node/node.h> +#include <spa/node/utils.h> +#include <spa/node/io.h> +#include <spa/debug/types.h> +#include <spa/utils/ringbuffer.h> +#include <spa/param/param.h> +#include <spa/param/latency-utils.h> +#include <spa/param/audio/format-utils.h> + +#include "avb.h" + +#define MAX_RATES 16 + +#define DEFAULT_IFNAME "eth0" +#define DEFAULT_ADDR "01:AA:AA:AA:AA:AA" +#define DEFAULT_PRIO 0 +#define DEFAULT_STREAMID "AA:BB:CC:DD:EE:FF:0000" +#define DEFAULT_MTT 5000000 +#define DEFAULT_TU 1000000 +#define DEFAULT_FRAMES_PER_PDU 8 + +#define DEFAULT_PERIOD 1024u +#define DEFAULT_RATE 48000u +#define DEFAULT_CHANNELS 8u + +struct props { + char ifnameIFNAMSIZ; + unsigned char addrETH_ALEN; + int prio; + uint64_t streamid; + int mtt; + int t_uncertainty; + uint32_t frames_per_pdu; + int ptime_tolerance; +}; + +static inline int parse_addr(unsigned char addrETH_ALEN, const char *str) +{ + unsigned char adETH_ALEN; + if (sscanf(str, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", + &ad0, &ad1, &ad2, &ad3, &ad4, &ad5) != 6) + return -EINVAL; + memcpy(addr, ad, sizeof(ad)); + return 0; +} +static inline char *format_addr(char *str, size_t size, const unsigned char addrETH_ALEN) +{ + snprintf(str, size, "%02x:%02x:%02x:%02x:%02x:%02x", + addr0, addr1, addr2, + addr3, addr4, addr5); + return str; +} + +static inline int parse_streamid(uint64_t *streamid, const char *str) +{ + unsigned char addr6; + unsigned short unique_id; + if (sscanf(str, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx:%hx", + &addr0, &addr1, &addr2, &addr3, + &addr4, &addr5, &unique_id) != 7) + return -EINVAL; + *streamid = (uint64_t) addr0 << 56 | + (uint64_t) addr1 << 48 | + (uint64_t) addr2 << 40 | + (uint64_t) addr3 << 32 | + (uint64_t) addr4 << 24 | + (uint64_t) addr5 << 16 | + unique_id; + return 0; +} +static inline char *format_streamid(char *str, size_t size, const uint64_t streamid) +{ + snprintf(str, size, "%02x:%02x:%02x:%02x:%02x:%02x:%04x", + (uint8_t)(streamid >> 56), + (uint8_t)(streamid >> 48), + (uint8_t)(streamid >> 40), + (uint8_t)(streamid >> 32), + (uint8_t)(streamid >> 24), + (uint8_t)(streamid >> 16), + (uint16_t)(streamid)); + return str; +} + +#define MAX_BUFFERS 32 + +struct buffer { + uint32_t id; +#define BUFFER_FLAG_OUT (1<<0) + uint32_t flags; + struct spa_buffer *buf; + struct spa_meta_header *h; + struct spa_list link; +}; + +#define BW_MAX 0.128 +#define BW_MED 0.064 +#define BW_MIN 0.016 +#define BW_PERIOD (3 * SPA_NSEC_PER_SEC) + +struct channel_map { + uint32_t channels; + uint32_t posSPA_AUDIO_MAX_CHANNELS; +}; + +struct port { + enum spa_direction direction; + uint32_t id; + + uint64_t info_all; + struct spa_port_info info; +#define PORT_EnumFormat 0 +#define PORT_Meta 1 +#define PORT_IO 2 +#define PORT_Format 3 +#define PORT_Buffers 4 +#define PORT_Latency 5 +#define N_PORT_PARAMS 6 + struct spa_param_info paramsN_PORT_PARAMS; + + bool have_format; + struct spa_audio_info current_format; + + struct spa_io_buffers *io; + struct spa_io_rate_match *rate_match; + struct buffer buffersMAX_BUFFERS; + unsigned int n_buffers; + + struct spa_list free; + struct spa_list ready; + uint32_t ready_offset; +}; + +struct state { + struct spa_handle handle; + struct spa_node node; + + struct spa_log *log; + struct spa_system *data_system; + struct spa_loop *data_loop; + + struct spa_hook_list hooks; + struct spa_callbacks callbacks; + + uint64_t info_all; + struct spa_node_info info; +#define NODE_PropInfo 0 +#define NODE_Props 1 +#define NODE_IO 2 +#define NODE_ProcessLatency 3 +#define N_NODE_PARAMS 4 + struct spa_param_info paramsN_NODE_PARAMS; + struct props props;
View file
pipewire-0.3.56.tar.gz/spa/plugins/avb/avb.c
Added
@@ -0,0 +1,54 @@ +/* Spa AVB support + * + * Copyright © 2022 Wim Taymans + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include <errno.h> + +#include <spa/support/plugin.h> +#include <spa/support/log.h> + +extern const struct spa_handle_factory spa_avb_sink_factory; +extern const struct spa_handle_factory spa_avb_source_factory; + +struct spa_log_topic log_topic = SPA_LOG_TOPIC(0, "spa.avb"); +struct spa_log_topic *avb_log_topic = &log_topic; + +SPA_EXPORT +int spa_handle_factory_enum(const struct spa_handle_factory **factory, uint32_t *index) +{ + spa_return_val_if_fail(factory != NULL, -EINVAL); + spa_return_val_if_fail(index != NULL, -EINVAL); + + switch (*index) { + case 0: + *factory = &spa_avb_sink_factory; + break; + case 1: + *factory = &spa_avb_source_factory; + break; + default: + return 0; + } + (*index)++; + return 1; +}
View file
pipewire-0.3.56.tar.gz/spa/plugins/avb/avb.h
Added
@@ -0,0 +1,39 @@ +/* Spa AVB + * + * Copyright © 2022 Wim Taymans + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef SPA_AVB_H +#define SPA_AVB_H + +#include <spa/support/log.h> + +#undef SPA_LOG_TOPIC_DEFAULT +#define SPA_LOG_TOPIC_DEFAULT avb_log_topic +extern struct spa_log_topic *avb_log_topic; + +static inline void avb_log_topic_init(struct spa_log *log) +{ + spa_log_topic_init(log, avb_log_topic); +} + +#endif /* SPA_AVB_H */
View file
pipewire-0.3.56.tar.gz/spa/plugins/avb/avbtp
Added
+(directory)
View file
pipewire-0.3.56.tar.gz/spa/plugins/avb/avbtp/packets.h
Added
@@ -0,0 +1,220 @@ +/* Spa AVB support + * + * Copyright © 2022 Wim Taymans + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef SPA_AVB_PACKETS_H +#define SPA_AVB_PACKETS_H + +#define SPA_AVBTP_SUBTYPE_61883_IIDC 0x00 +#define SPA_AVBTP_SUBTYPE_MMA_STREAM 0x01 +#define SPA_AVBTP_SUBTYPE_AAF 0x02 +#define SPA_AVBTP_SUBTYPE_CVF 0x03 +#define SPA_AVBTP_SUBTYPE_CRF 0x04 +#define SPA_AVBTP_SUBTYPE_TSCF 0x05 +#define SPA_AVBTP_SUBTYPE_SVF 0x06 +#define SPA_AVBTP_SUBTYPE_RVF 0x07 +#define SPA_AVBTP_SUBTYPE_AEF_CONTINUOUS 0x6E +#define SPA_AVBTP_SUBTYPE_VSF_STREAM 0x6F +#define SPA_AVBTP_SUBTYPE_EF_STREAM 0x7F +#define SPA_AVBTP_SUBTYPE_NTSCF 0x82 +#define SPA_AVBTP_SUBTYPE_ESCF 0xEC +#define SPA_AVBTP_SUBTYPE_EECF 0xED +#define SPA_AVBTP_SUBTYPE_AEF_DISCRETE 0xEE +#define SPA_AVBTP_SUBTYPE_ADP 0xFA +#define SPA_AVBTP_SUBTYPE_AECP 0xFB +#define SPA_AVBTP_SUBTYPE_ACMP 0xFC +#define SPA_AVBTP_SUBTYPE_MAAP 0xFE +#define SPA_AVBTP_SUBTYPE_EF_CONTROL 0xFF + +struct spa_avbtp_packet_common { + uint8_t subtype; +#if __BYTE_ORDER == __BIG_ENDIAN + unsigned sv:1; /* stream_id valid */ + unsigned version:3; + unsigned subtype_data1:4; +#elif __BYTE_ORDER == __LITTLE_ENDIAN + unsigned subtype_data1:4; + unsigned version:3; + unsigned sv:1; +#elif +#error "Unknown byte order" +#endif + uint16_t subtype_data2; + uint64_t stream_id; + uint8_t payload0; +} __attribute__ ((__packed__)); + +#define SPA_AVBTP_PACKET_SET_SUBTYPE(p,v) ((p)->subtype = (v)) +#define SPA_AVBTP_PACKET_SET_SV(p,v) ((p)->sv = (v)) +#define SPA_AVBTP_PACKET_SET_VERSION(p,v) ((p)->version = (v)) +#define SPA_AVBTP_PACKET_SET_STREAM_ID(p,v) ((p)->stream_id = htobe64(v)) + +#define SPA_AVBTP_PACKET_GET_SUBTYPE(p) ((p)->subtype) +#define SPA_AVBTP_PACKET_GET_SV(p) ((p)->sv) +#define SPA_AVBTP_PACKET_GET_VERSION(p) ((p)->version) +#define SPA_AVBTP_PACKET_GET_STREAM_ID(p) be64toh((p)->stream_id) + +struct spa_avbtp_packet_cc { + uint8_t subtype; +#if __BYTE_ORDER == __BIG_ENDIAN + unsigned sv:1; + unsigned version:3; + unsigned control_data1:4; +#elif __BYTE_ORDER == __LITTLE_ENDIAN + unsigned control_data1:4; + unsigned version:3; + unsigned sv:1; +#endif + uint8_t status; + uint16_t control_frame_length; + uint64_t stream_id; + uint8_t payload0; +} __attribute__ ((__packed__)); + +#define SPA_AVBTP_PACKET_CC_SET_SUBTYPE(p,v) ((p)->subtype = (v)) +#define SPA_AVBTP_PACKET_CC_SET_SV(p,v) ((p)->sv = (v)) +#define SPA_AVBTP_PACKET_CC_SET_VERSION(p,v) ((p)->version = (v)) +#define SPA_AVBTP_PACKET_CC_SET_STREAM_ID(p,v) ((p)->stream_id = htobe64(v)) +#define SPA_AVBTP_PACKET_CC_SET_STATUS(p,v) ((p)->status = (v)) +#define SPA_AVBTP_PACKET_CC_SET_LENGTH(p,v) ((p)->control_frame_length = htons(v)) + +#define SPA_AVBTP_PACKET_CC_GET_SUBTYPE(p) ((p)->subtype) +#define SPA_AVBTP_PACKET_CC_GET_SV(p) ((p)->sv) +#define SPA_AVBTP_PACKET_CC_GET_VERSION(p) ((p)->version) +#define SPA_AVBTP_PACKET_CC_GET_STREAM_ID(p) be64toh((p)->stream_id) +#define SPA_AVBTP_PACKET_CC_GET_STATUS(p) ((p)->status) +#define SPA_AVBTP_PACKET_CC_GET_LENGTH(p) ntohs((p)->control_frame_length) + +/* AAF */ +struct spa_avbtp_packet_aaf { + uint8_t subtype; +#if __BYTE_ORDER == __BIG_ENDIAN + unsigned sv:1; + unsigned version:3; + unsigned mr:1; + unsigned _r1:1; + unsigned gv:1; + unsigned tv:1; + + uint8_t seq_number; + + unsigned _r2:7; + unsigned tu:1; +#elif __BYTE_ORDER == __LITTLE_ENDIAN + unsigned tv:1; + unsigned gv:1; + unsigned _r1:1; + unsigned mr:1; + unsigned version:3; + unsigned sv:1; + + uint8_t seq_num; + + unsigned tu:1; + unsigned _r2:7; +#endif + uint64_t stream_id; + uint32_t timestamp; +#define SPA_AVBTP_AAF_FORMAT_USER 0x00 +#define SPA_AVBTP_AAF_FORMAT_FLOAT_32BIT 0x01 +#define SPA_AVBTP_AAF_FORMAT_INT_32BIT 0x02 +#define SPA_AVBTP_AAF_FORMAT_INT_24BIT 0x03 +#define SPA_AVBTP_AAF_FORMAT_INT_16BIT 0x04 +#define SPA_AVBTP_AAF_FORMAT_AES3_32BIT 0x05 + uint8_t format; + +#define SPA_AVBTP_AAF_PCM_NSR_USER 0x00 +#define SPA_AVBTP_AAF_PCM_NSR_8KHZ 0x01 +#define SPA_AVBTP_AAF_PCM_NSR_16KHZ 0x02 +#define SPA_AVBTP_AAF_PCM_NSR_32KHZ 0x03 +#define SPA_AVBTP_AAF_PCM_NSR_44_1KHZ 0x04 +#define SPA_AVBTP_AAF_PCM_NSR_48KHZ 0x05 +#define SPA_AVBTP_AAF_PCM_NSR_88_2KHZ 0x06 +#define SPA_AVBTP_AAF_PCM_NSR_96KHZ 0x07 +#define SPA_AVBTP_AAF_PCM_NSR_176_4KHZ 0x08 +#define SPA_AVBTP_AAF_PCM_NSR_192KHZ 0x09 +#define SPA_AVBTP_AAF_PCM_NSR_24KHZ 0x0A +#if __BYTE_ORDER == __BIG_ENDIAN + unsigned nsr:4; + unsigned _r3:4; +#elif __BYTE_ORDER == __LITTLE_ENDIAN + unsigned _r3:4; + unsigned nsr:4; +#endif + uint8_t chan_per_frame; + uint8_t bit_depth; + uint16_t data_len; + +#define SPA_AVBTP_AAF_PCM_SP_NORMAL 0x00 +#define SPA_AVBTP_AAF_PCM_SP_SPARSE 0x01 +#if __BYTE_ORDER == __BIG_ENDIAN + unsigned _r4:3; + unsigned sp:1; + unsigned event:4; +#elif __BYTE_ORDER == __LITTLE_ENDIAN + unsigned event:4; + unsigned sp:1; + unsigned _r4:3; +#endif + uint8_t _r5; + uint8_t payload0; +} __attribute__ ((__packed__)); + +#define SPA_AVBTP_PACKET_AAF_SET_SUBTYPE(p,v) ((p)->subtype = (v)) +#define SPA_AVBTP_PACKET_AAF_SET_SV(p,v) ((p)->sv = (v)) +#define SPA_AVBTP_PACKET_AAF_SET_VERSION(p,v) ((p)->version = (v)) +#define SPA_AVBTP_PACKET_AAF_SET_MR(p,v) ((p)->mr = (v)) +#define SPA_AVBTP_PACKET_AAF_SET_GV(p,v) ((p)->gv = (v)) +#define SPA_AVBTP_PACKET_AAF_SET_TV(p,v) ((p)->tv = (v)) +#define SPA_AVBTP_PACKET_AAF_SET_SEQ_NUM(p,v) ((p)->seq_num = (v)) +#define SPA_AVBTP_PACKET_AAF_SET_TU(p,v) ((p)->tu = (v)) +#define SPA_AVBTP_PACKET_AAF_SET_STREAM_ID(p,v) ((p)->stream_id = htobe64(v)) +#define SPA_AVBTP_PACKET_AAF_SET_TIMESTAMP(p,v) ((p)->timestamp = htonl(v)) +#define SPA_AVBTP_PACKET_AAF_SET_DATA_LEN(p,v) ((p)->data_len = htons(v)) +#define SPA_AVBTP_PACKET_AAF_SET_FORMAT(p,v) ((p)->format = (v)) +#define SPA_AVBTP_PACKET_AAF_SET_NSR(p,v) ((p)->nsr = (v)) +#define SPA_AVBTP_PACKET_AAF_SET_CHAN_PER_FRAME(p,v) ((p)->chan_per_frame = (v)) +#define SPA_AVBTP_PACKET_AAF_SET_BIT_DEPTH(p,v) ((p)->bit_depth = (v)) +#define SPA_AVBTP_PACKET_AAF_SET_SP(p,v) ((p)->sp = (v)) +#define SPA_AVBTP_PACKET_AAF_SET_EVENT(p,v) ((p)->event = (v))
View file
pipewire-0.3.56.tar.gz/spa/plugins/avb/meson.build
Added
@@ -0,0 +1,14 @@ +spa_avb_sources = 'avb.c', + 'avb.h', + 'avb-pcm-sink.c', + 'avb-pcm-source.c', + 'avb-pcm.c' + +spa_avb = shared_library( + 'spa-avb', + spa_avb_sources , + include_directories : configinc, + dependencies : spa_dep, mathlib, epoll_shim_dep , + install : true, + install_dir : spa_plugindir / 'avb' +)
View file
pipewire-0.3.54.tar.gz/spa/plugins/bluez5/backend-native.c -> pipewire-0.3.56.tar.gz/spa/plugins/bluez5/backend-native.c
Changed
@@ -29,6 +29,7 @@ #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> +#include <poll.h> #include <bluetooth/bluetooth.h> #include <bluetooth/sco.h> @@ -73,6 +74,7 @@ struct spa_log *log; struct spa_loop *main_loop; struct spa_system *main_system; + struct spa_loop_utils *loop_utils; struct spa_dbus *dbus; DBusConnection *conn; @@ -127,6 +129,7 @@ struct spa_hook transport_listener; enum spa_bt_profile profile; struct spa_source timer; + struct spa_source *volume_sync_timer; char* path; bool has_volume; struct rfcomm_volume volumesSPA_BT_VOLUME_ID_TERM; @@ -225,6 +228,8 @@ static int codec_switch_stop_timer(struct rfcomm *rfcomm); +static void volume_sync_stop_timer(struct rfcomm *rfcomm); + static void rfcomm_free(struct rfcomm *rfcomm) { codec_switch_stop_timer(rfcomm); @@ -247,6 +252,8 @@ close (rfcomm->source.fd); rfcomm->source.fd = -1; } + if (rfcomm->volume_sync_timer) + spa_loop_utils_destroy_source(rfcomm->backend->loop_utils, rfcomm->volume_sync_timer); free(rfcomm); } @@ -809,6 +816,7 @@ rfcomm->hfp_ag_switching_codec = false; rfcomm->hfp_ag_initial_codec_setup = HFP_AG_INITIAL_CODEC_SETUP_NONE; codec_switch_stop_timer(rfcomm); + volume_sync_stop_timer(rfcomm); if (selected_codec != HFP_AUDIO_CODEC_CVSD && selected_codec != HFP_AUDIO_CODEC_MSBC) { spa_log_warn(backend->log, "unsupported codec negotiation: %d", selected_codec); @@ -1203,6 +1211,18 @@ return -1; } +static int rfcomm_ag_sync_volume(struct rfcomm *rfcomm, bool later); + +static void wait_for_socket(int fd) +{ + struct pollfd fds1; + const int timeout_ms = 500; + + fds0.fd = fd; + fds0.events = POLLIN | POLLERR | POLLHUP; + poll(fds, 1, timeout_ms); +} + static int sco_acquire_cb(void *data, bool optional) { struct spa_bt_transport *t = data; @@ -1225,6 +1245,25 @@ rfcomm_hfp_ag_set_cind(td->rfcomm, true); #endif + /* + * Send RFCOMM volume after connection is ready, and also after + * a timeout. + * + * Some headsets adjust their HFP volume when in A2DP mode + * without reporting via RFCOMM to us, so the volume level can + * be out of sync, and we can't know what it is. Moreover, they may + * take the first +VGS command after connection only partially + * into account, and need a long enough timeout. + * + * E.g. with Sennheiser HD-250BT, the first +VGS changes the + * actual volume, but does not update the level in the hardware + * volume buttons, which is updated by an +VGS event only after + * sufficient time is elapsed from the connection. + */ + wait_for_socket(sock); + rfcomm_ag_sync_volume(td->rfcomm, false); + rfcomm_ag_sync_volume(td->rfcomm, true); + t->fd = sock; /* Fallback value */ @@ -1471,10 +1510,8 @@ return -1; } -static int sco_set_volume_cb(void *data, int id, float volume) +static int rfcomm_ag_set_volume(struct spa_bt_transport *t, int id) { - struct spa_bt_transport *t = data; - struct spa_bt_transport_volume *t_volume = &t->volumesid; struct transport_data *td = t->user_data; struct rfcomm *rfcomm = td->rfcomm; const char *format; @@ -1485,12 +1522,7 @@ || !(rfcomm->has_volume && rfcomm->volumesid.active)) return -ENOTSUP; - value = spa_bt_volume_linear_to_hw(volume, t_volume->hw_volume_max); - t_volume->volume = volume; - - if (rfcomm->volumesid.hw_volume == value) - return 0; - rfcomm->volumesid.hw_volume = value; + value = rfcomm->volumesid.hw_volume; if (id == SPA_BT_VOLUME_ID_RX) if (rfcomm->profile & SPA_BT_PROFILE_HFP_HF) @@ -1511,6 +1543,29 @@ return 0; } +static int sco_set_volume_cb(void *data, int id, float volume) +{ + struct spa_bt_transport *t = data; + struct spa_bt_transport_volume *t_volume = &t->volumesid; + struct transport_data *td = t->user_data; + struct rfcomm *rfcomm = td->rfcomm; + int value; + + if (!rfcomm_volume_enabled(rfcomm) + || !(rfcomm->profile & SPA_BT_PROFILE_HEADSET_HEAD_UNIT) + || !(rfcomm->has_volume && rfcomm->volumesid.active)) + return -ENOTSUP; + + value = spa_bt_volume_linear_to_hw(volume, t_volume->hw_volume_max); + t_volume->volume = volume; + + if (rfcomm->volumesid.hw_volume == value) + return 0; + rfcomm->volumesid.hw_volume = value; + + return rfcomm_ag_set_volume(t, id); +} + static const struct spa_bt_transport_implementation sco_transport_impl = { SPA_VERSION_BT_TRANSPORT_IMPLEMENTATION, .acquire = sco_acquire_cb, @@ -1570,6 +1625,60 @@ return 0; } +static void volume_sync_stop_timer(struct rfcomm *rfcomm) +{ + if (rfcomm->volume_sync_timer) + spa_loop_utils_update_timer(rfcomm->backend->loop_utils, rfcomm->volume_sync_timer, + NULL, NULL, false); +} + +static void volume_sync_timer_event(void *data, uint64_t expirations) +{ + struct rfcomm *rfcomm = data; + + volume_sync_stop_timer(rfcomm); + + if (rfcomm->transport) { + rfcomm_ag_set_volume(rfcomm->transport, SPA_BT_VOLUME_ID_TX); + rfcomm_ag_set_volume(rfcomm->transport, SPA_BT_VOLUME_ID_RX); + } +} + +static int volume_sync_start_timer(struct rfcomm *rfcomm) +{ + struct timespec ts; + const uint64_t timeout = 1500 * SPA_NSEC_PER_MSEC; + + if (rfcomm->volume_sync_timer == NULL) + rfcomm->volume_sync_timer = spa_loop_utils_add_timer(rfcomm->backend->loop_utils, + volume_sync_timer_event, rfcomm); + + if (rfcomm->volume_sync_timer == NULL) + return -EIO; + + ts.tv_sec = timeout / SPA_NSEC_PER_SEC; + ts.tv_nsec = timeout % SPA_NSEC_PER_SEC; + spa_loop_utils_update_timer(rfcomm->backend->loop_utils, rfcomm->volume_sync_timer, + &ts, NULL, false); + + return 0; +} + +static int rfcomm_ag_sync_volume(struct rfcomm *rfcomm, bool later) +{ + if (rfcomm->transport == NULL) + return -ENOENT; + + if (!later) {
View file
pipewire-0.3.54.tar.gz/spa/plugins/bluez5/bluez5-device.c -> pipewire-0.3.56.tar.gz/spa/plugins/bluez5/bluez5-device.c
Changed
@@ -337,35 +337,57 @@ } } -static void volume_changed(void *userdata) +static bool node_update_volume_from_transport(struct node *node, bool reset) { - struct node *node = userdata; struct impl *impl = node->impl; struct spa_bt_transport_volume *t_volume; float prev_hw_volume; if (!node->transport || !spa_bt_transport_volume_enabled(node->transport)) - return; + return false; /* PW is the controller for remote device. */ if (impl->profile != DEVICE_PROFILE_A2DP && impl->profile != DEVICE_PROFILE_HSP_HFP) - return; + return false; t_volume = &node->transport->volumesnode->id; if (!t_volume->active) - return; + return false; prev_hw_volume = node_get_hw_volume(node); - for (uint32_t i = 0; i < node->n_channels; ++i) { - node->volumesi = prev_hw_volume > 0.0f - ? node->volumesi * t_volume->volume / prev_hw_volume - : t_volume->volume; + + if (!reset) { + for (uint32_t i = 0; i < node->n_channels; ++i) { + node->volumesi = prev_hw_volume > 0.0f + ? node->volumesi * t_volume->volume / prev_hw_volume + : t_volume->volume; + } + } else { + for (uint32_t i = 0; i < node->n_channels; ++i) + node->volumesi = t_volume->volume; } node_update_soft_volumes(node, t_volume->volume); + /* + * Consider volume changes from the headset as requested + * by the user, and to be saved by the SM. + */ + node->save = true; + + return true; +} + +static void volume_changed(void *userdata) +{ + struct node *node = userdata; + struct impl *impl = node->impl; + + if (!node_update_volume_from_transport(node, false)) + return; + emit_volume(impl, node); impl->info.change_mask |= SPA_DEVICE_CHANGE_MASK_PARAMS; @@ -469,6 +491,8 @@ this->nodesid.volumesi = this->nodesid.volumesi % prev_channels; } + node_update_volume_from_transport(&this->nodesid, true); + boost = get_soft_volume_boost(&this->nodesid); if (boost != 1.0f) { size_t i; @@ -1301,16 +1325,17 @@ } static struct spa_pod *build_route(struct impl *this, struct spa_pod_builder *b, - uint32_t id, uint32_t port, uint32_t dev, uint32_t profile) + uint32_t id, uint32_t port, uint32_t profile) { struct spa_bt_device *device = this->bt_dev; struct spa_pod_frame f2; enum spa_direction direction; - const char *name_prefix, *description, *port_type; + const char *name_prefix, *description, *hfp_description, *port_type; enum spa_bt_form_factor ff; enum spa_bluetooth_audio_codec codec; char name128; uint32_t i, j, mask, next; + uint32_t dev = SPA_ID_INVALID, enum_dev; ff = spa_bt_form_factor_from_class(device->bluetooth_class); @@ -1318,52 +1343,62 @@ case SPA_BT_FORM_FACTOR_HEADSET: name_prefix = "headset"; description = _("Headset"); + hfp_description = _("Handsfree"); port_type = "headset"; break; case SPA_BT_FORM_FACTOR_HANDSFREE: name_prefix = "handsfree"; description = _("Handsfree"); + hfp_description = _("Handsfree (HFP)"); port_type = "handsfree"; break; case SPA_BT_FORM_FACTOR_MICROPHONE: name_prefix = "microphone"; description = _("Microphone"); + hfp_description = _("Handsfree"); port_type = "mic"; break; case SPA_BT_FORM_FACTOR_SPEAKER: name_prefix = "speaker"; description = _("Speaker"); + hfp_description = _("Handsfree"); port_type = "speaker"; break; case SPA_BT_FORM_FACTOR_HEADPHONE: name_prefix = "headphone"; description = _("Headphone"); + hfp_description = _("Handsfree"); port_type = "headphones"; break; case SPA_BT_FORM_FACTOR_PORTABLE: name_prefix = "portable"; description = _("Portable"); + hfp_description = _("Handsfree"); port_type = "portable"; break; case SPA_BT_FORM_FACTOR_CAR: name_prefix = "car"; description = _("Car"); + hfp_description = _("Handsfree"); port_type = "car"; break; case SPA_BT_FORM_FACTOR_HIFI: name_prefix = "hifi"; description = _("HiFi"); + hfp_description = _("Handsfree"); port_type = "hifi"; break; case SPA_BT_FORM_FACTOR_PHONE: name_prefix = "phone"; description = _("Phone"); + hfp_description = _("Handsfree"); port_type = "phone"; break; case SPA_BT_FORM_FACTOR_UNKNOWN: default: name_prefix = "bluetooth"; description = _("Bluetooth"); + hfp_description = _("Bluetooth (HFP)"); port_type = "bluetooth"; break; } @@ -1372,16 +1407,51 @@ case 0: direction = SPA_DIRECTION_INPUT; snprintf(name, sizeof(name), "%s-input", name_prefix); + enum_dev = DEVICE_ID_SOURCE; + if (profile == DEVICE_PROFILE_A2DP) + dev = enum_dev; + else if (profile != SPA_ID_INVALID) + enum_dev = SPA_ID_INVALID; break; case 1: direction = SPA_DIRECTION_OUTPUT; snprintf(name, sizeof(name), "%s-output", name_prefix); + enum_dev = DEVICE_ID_SINK; + if (profile == DEVICE_PROFILE_A2DP) + dev = enum_dev; + else if (profile != SPA_ID_INVALID) + enum_dev = SPA_ID_INVALID; + break; + case 2: + direction = SPA_DIRECTION_INPUT; + snprintf(name, sizeof(name), "%s-hf-input", name_prefix); + description = hfp_description; + enum_dev = DEVICE_ID_SOURCE; + if (profile == DEVICE_PROFILE_HSP_HFP) + dev = enum_dev; + else if (profile != SPA_ID_INVALID) + enum_dev = SPA_ID_INVALID; + break; + case 3: + direction = SPA_DIRECTION_OUTPUT; + snprintf(name, sizeof(name), "%s-hf-output", name_prefix); + description = hfp_description; + enum_dev = DEVICE_ID_SINK; + if (profile == DEVICE_PROFILE_HSP_HFP) + dev = enum_dev; + else if (profile != SPA_ID_INVALID) + enum_dev = SPA_ID_INVALID; break; default: errno = EINVAL;
View file
pipewire-0.3.54.tar.gz/spa/plugins/bluez5/decode-buffer.h -> pipewire-0.3.56.tar.gz/spa/plugins/bluez5/decode-buffer.h
Changed
@@ -56,12 +56,10 @@ #include <stdlib.h> #include <spa/utils/defs.h> -#include <spa/utils/dll.h> #include <spa/support/log.h> -#define BUFFERING_LONG_MSEC 60000 +#define BUFFERING_LONG_MSEC (2*60000) #define BUFFERING_SHORT_MSEC 1000 -#define BUFFERING_DLL_BW 0.03 #define BUFFERING_RATE_DIFF_MAX 0.005 /** @@ -73,6 +71,132 @@ #define BUFFERING_TARGET(spike,packet_size) \ SPA_CLAMP((spike)*3/2, (packet_size), 6*(packet_size)) +/** + * Rate controller. + * + * It's here in a form, where it operates on the running average + * so it's compatible with the level spike determination, and + * clamping the rate to a range is easy. The impulse response + * is similar to spa_dll, and step response does not have sign changes. + * + * The controller iterates as + * + * avg(j+1) = (1 - beta) avg(j) + beta level(j) + * corr(j+1) = corr(j) + a avg(j+1) - avg(j) / duration + * + b avg(j) - target / duration + * + * with beta = duration/avg_period < 0.5 is the moving average parameter, + * and a = beta/3 + ..., b = beta^2/27 + .... + * + * This choice results to c(j) being low-pass filtered, and buffer level(j) + * converging towards target with stable damped evolution with eigenvalues + * real and close to each other around (1 - beta)^(1/3). + * + * Derivation: + * + * The deviation from the buffer level target evolves as + * + * delta(j) = level(j) - target + * delta(j+1) = delta(j) + r(j) - c(j+1) + * + * where r is samples received in one duration, and c corrected rate + * (samples per duration). + * + * The rate correction is in general determined by linear filter f + * + * c(j+1) = c(j) + \sum_{k=0}^\infty delta(j - k) f(k) + * + * If \sum_k f(k) is not zero, the only fixed point is c=r, delta=0, + * so this structure (if the filter is stable) rate matches and + * drives buffer level to target. + * + * The z-transform then is + * + * delta(z) = G(z) r(z) + * c(z) = F(z) delta(z) + * G(z) = (z - 1) / (z - 1)^2 + z f(z) + * F(z) = f(z) / (z - 1) + * + * We now want: poles of G(z) must be in |z|<1 for stability, F(z) + * should damp high frequencies, and f(z) is causal. + * + * To satisfy the conditions, take + * + * (z - 1)^2 + z f(z) = p(z) / q(z) + * + * where p(z) is polynomial with leading term z^n with wanted root + * structure, and q(z) is any polynomial with leading term z^{n-2}. + * This guarantees f(z) is causal, and G(z) = (z-1) q(z) / p(z). + * We can choose p(z) and q(z) to improve low-pass properties of F(z). + * + * Simplest choice is p(z)=(z-x)^2 and q(z)=1, but that gives flat + * high frequency response in F(z). Better choice is p(z) = (z-u)*(z-v)*(z-w) + * and q(z) = z - r. To make F(z) better lowpass, one can cancel + * a resulting 1/z pole in F(z) by setting r=u*v*w. Then, + * + * G(z) = (z - u*v*w)*(z - 1) / (z - u)*(z - v)*(z - w) + * F(z) = (a z + b - a) / (z - 1) * H(z) + * H(z) = beta / (z - 1 + beta) + * beta = 1 - u*v*w + * a = (1-u) + (1-v) + (1-w) - beta / beta + * b = (1-u)*(1-v)*(1-w) / beta + * + * which corresponds to iteration for c(j): + * + * avg(j+1) = (1 - beta) avg(j) + beta delta(j) + * c(j+1) = c(j) + a avg(j+1) - avg(j) + b avg(j) + * + * So the controller operates on the running average, + * which gives the low-pass property for c(j). + * + * The simplest filter is obtained by putting the poles at + * u=v=w=(1-beta)**(1/3). Since beta << 1, computing the root + * can be avoided by expanding in series. + * + * Overshoot in impulse response could be reduced by moving one of the + * poles closer to z=1, but this increases the step response time. + */ +struct spa_bt_rate_control +{ + double avg; + double corr; +}; + +static void spa_bt_rate_control_init(struct spa_bt_rate_control *this, double level) +{ + this->avg = level; + this->corr = 1.0; +} + +static double spa_bt_rate_control_update(struct spa_bt_rate_control *this, double level, + double target, double duration, double period) +{ + /* + * u = (1 - beta)^(1/3) + * x = a / beta + * y = b / beta + * a = (2 + u) * (1 - u)^2 / beta + * b = (1 - u)^3 / beta + * beta -> 0 + */ + const double beta = SPA_CLAMP(duration / period, 0, 0.5); + const double x = 1.0/3; + const double y = beta/27; + double avg; + + avg = beta * level + (1 - beta) * this->avg; + this->corr += x * (avg - this->avg) / period + + y * (this->avg - target) / period; + this->avg = avg; + + this->corr = SPA_CLAMP(this->corr, + 1 - BUFFERING_RATE_DIFF_MAX, + 1 + BUFFERING_RATE_DIFF_MAX); + + return this->corr; +} + + /** Windowed min/max */ struct spa_bt_ptp { @@ -104,11 +228,7 @@ struct spa_bt_ptp spike; /**< spikes (long window) */ struct spa_bt_ptp packet_size; /**< packet size (short window) */ - int32_t target; - int32_t level; - double level_avg; - - struct spa_dll dll; + struct spa_bt_rate_control ctl; double corr; uint32_t prev_consumed; @@ -168,7 +288,7 @@ this->corr = 1.0; this->buffering = true; - spa_dll_init(&this->dll); + spa_bt_rate_control_init(&this->ctl, 0); spa_bt_ptp_init(&this->spike, (uint64_t)this->rate * BUFFERING_LONG_MSEC / 1000); spa_bt_ptp_init(&this->packet_size, (uint64_t)this->rate * BUFFERING_SHORT_MSEC / 1000); @@ -254,16 +374,16 @@ static void spa_bt_decode_buffer_recover(struct spa_bt_decode_buffer *this) { int32_t size = (this->write_index - this->read_index) / this->frame_size; + int32_t level; this->prev_avail = size * this->frame_size; this->prev_consumed = this->prev_duration; - this->level = (int32_t)this->prev_avail/this->frame_size + + level = (int32_t)this->prev_avail/this->frame_size - (int32_t)this->prev_duration; - this->level_avg = this->level; - this->target = this->level; this->corr = 1.0; - spa_dll_init(&this->dll); + spa_bt_rate_control_init(&this->ctl, level); } static void spa_bt_decode_buffer_process(struct spa_bt_decode_buffer *this, uint32_t samples, uint32_t duration) @@ -294,12 +414,6 @@ spa_bt_decode_buffer_recover(this); } - if (SPA_UNLIKELY(this->dll.bw == 0.0)) { - spa_log_trace(this->log, "%p dll reset duration:%d rate:%d", this, - (int)duration, (int)this->rate); - spa_dll_set_bw(&this->dll, BUFFERING_DLL_BW, duration, (uint64_t)this->rate); - } -
View file
pipewire-0.3.54.tar.gz/spa/plugins/bluez5/sco-sink.c -> pipewire-0.3.56.tar.gz/spa/plugins/bluez5/sco-sink.c
Changed
@@ -759,6 +759,7 @@ { SPA_KEY_MEDIA_CLASS, "Stream/Input/Audio" }, { "media.name", ((this->transport && this->transport->device->name) ? this->transport->device->name : "HSP/HFP") }, + { SPA_KEY_MEDIA_ROLE, "Communication" }, }; bool is_ag = this->transport && (this->transport->profile & SPA_BT_PROFILE_HEADSET_AUDIO_GATEWAY);
View file
pipewire-0.3.54.tar.gz/spa/plugins/bluez5/sco-source.c -> pipewire-0.3.56.tar.gz/spa/plugins/bluez5/sco-source.c
Changed
@@ -805,6 +805,7 @@ { SPA_KEY_MEDIA_CLASS, "Stream/Output/Audio" }, { "media.name", ((this->transport && this->transport->device->name) ? this->transport->device->name : "HSP/HFP") }, + { SPA_KEY_MEDIA_ROLE, "Communication" }, }; bool is_ag = this->transport && (this->transport->profile & SPA_BT_PROFILE_HEADSET_AUDIO_GATEWAY); uint64_t old = full ? this->info.change_mask : 0;
View file
pipewire-0.3.54.tar.gz/spa/plugins/meson.build -> pipewire-0.3.56.tar.gz/spa/plugins/meson.build
Changed
@@ -1,6 +1,9 @@ if alsa_dep.found() subdir('alsa') endif +if get_option('avb').allowed() + subdir('avb') +endif if get_option('audioconvert').allowed() subdir('audioconvert') endif
View file
pipewire-0.3.54.tar.gz/spa/plugins/v4l2/v4l2-udev.c -> pipewire-0.3.56.tar.gz/spa/plugins/v4l2/v4l2-udev.c
Changed
@@ -278,9 +278,14 @@ 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) - itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_VENDOR_ID, 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); /* 0xffff 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(dev, "ID_VENDOR_FROM_DATABASE"); if (!(str && *str)) { str = udev_device_get_property_value(dev, "ID_VENDOR_ENC"); @@ -295,8 +300,14 @@ 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) - itemsn_items++ = SPA_DICT_ITEM_INIT(SPA_KEY_DEVICE_PRODUCT_ID, 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); /* 0xffff 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(dev, "ID_MODEL_FROM_DATABASE"); if (!(str && *str)) {
View file
pipewire-0.3.54.tar.gz/spa/plugins/vulkan/vulkan-utils.c -> pipewire-0.3.56.tar.gz/spa/plugins/vulkan/vulkan-utils.c
Changed
@@ -6,7 +6,7 @@ #include <sys/mman.h> #include <fcntl.h> #include <string.h> -#if defined(__FreeBSD__) || defined(__MidnightBSD__) +#if !defined(__FreeBSD__) && !defined(__MidnightBSD__) #include <alloca.h> #endif #include <errno.h>
View file
pipewire-0.3.54.tar.gz/src/daemon/client.conf.in -> pipewire-0.3.56.tar.gz/src/daemon/client.conf.in
Changed
@@ -7,6 +7,7 @@ # @PIPEWIRE_CONFIG_DIR@/client.conf.d/ for system-wide changes or in # ~/.config/pipewire/client.conf.d/ for local changes. # + context.properties = { ## Configure properties in the system. #mem.warn-mlock = false
View file
pipewire-0.3.56.tar.gz/src/daemon/filter-chain.conf.in
Added
@@ -0,0 +1,62 @@ +# Filter-chain config file for PipeWire version @VERSION@ # +# +# This is a base config file for running filters. +# +# Place filter fragments in @PIPEWIRE_CONFIG_DIR@/filter-chain.conf.d/ +# for system-wide changes or in ~/.config/pipewire/filter-chain.conf.d/ +# for local changes. +# +# Run the filters with pipewire -c filter-chain.conf +# + +context.properties = { + ## Configure properties in the system. + #mem.warn-mlock = false + #mem.allow-mlock = true + #mem.mlock-all = false + log.level = 0 +} + +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. + # + audio.convert.* = audioconvert/libspa-audioconvert + support.* = support/libspa-support +} + +context.modules = + #{ name = <module-name> + # args = { <key> = <value> ... } + # flags = ifexists nofail + #} + # + # 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. + # + # Uses realtime scheduling to boost the audio thread priorities + { name = libpipewire-module-rt + args = { + #rt.prio = 88 + #rt.time.soft = -1 + #rt.time.hard = -1 + } + flags = ifexists nofail + } + + # The native communication protocol. + { name = libpipewire-module-protocol-native } + + # 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 } +
View file
pipewire-0.3.54.tar.gz/src/daemon/filter-chain/demonic.conf -> pipewire-0.3.56.tar.gz/src/daemon/filter-chain/demonic.conf
Changed
@@ -1,46 +1,9 @@ # filter-chain example config file for PipeWire version @VERSION@ # -context.properties = { - ## Configure properties in the system. - #mem.warn-mlock = false - #mem.allow-mlock = true - #mem.mlock-all = false - log.level = 0 -} - -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. - # - audio.convert.* = audioconvert/libspa-audioconvert - support.* = support/libspa-support -} - +# +# Copy this file into a conf.d/ directory such as +# ~/.config/pipewire/filter-chain.conf.d/ +# context.modules = - # Uses realtime scheduling to boost the audio thread priorities - { name = libpipewire-module-rt - args = { - #rt.prio = 88 - #rt.time.soft = -1 - #rt.time.hard = -1 - } - flags = ifexists nofail - } - - # The native communication protocol. - { name = libpipewire-module-protocol-native } - - # 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 } - { name = libpipewire-module-filter-chain args = { #audio.format = F32
View file
pipewire-0.3.54.tar.gz/src/daemon/filter-chain/meson.build -> pipewire-0.3.56.tar.gz/src/daemon/filter-chain/meson.build
Changed
@@ -1,6 +1,8 @@ conf_files = 'demonic.conf', 'demonic.conf' , - 'duplicate-FL.conf', 'duplicate-FL.conf' , + 'source-duplicate-FL.conf', 'source-duplicate-FL.conf' , + 'sink-mix-FL-FR.conf', 'sink-mix-FL-FR.conf' , + 'sink-make-LFE.conf', 'sink-make-LFE.conf' , 'sink-virtual-surround-5.1-kemar.conf', 'sink-virtual-surround-5.1-kemar.conf' , 'sink-virtual-surround-7.1-hesuvi.conf', 'sink-virtual-surround-7.1-hesuvi.conf' , 'sink-dolby-surround.conf', 'sink-dolby-surround.conf' ,
View file
pipewire-0.3.54.tar.gz/src/daemon/filter-chain/sink-dolby-surround.conf -> pipewire-0.3.56.tar.gz/src/daemon/filter-chain/sink-dolby-surround.conf
Changed
@@ -1,29 +1,9 @@ # Dolby Surround encoder sink # -# start with pipewire -c filter-chain/sink-dolby-surround.conf +# Copy this file into a conf.d/ directory such as +# ~/.config/pipewire/filter-chain.conf.d/ # -context.properties = { - log.level = 0 -} - -context.spa-libs = { - audio.convert.* = audioconvert/libspa-audioconvert - support.* = support/libspa-support -} - context.modules = - { name = libpipewire-module-rt - args = { - #rt.prio = 88 - #rt.time.soft = -1 - #rt.time.hard = -1 - } - flags = ifexists nofail - } - { name = libpipewire-module-protocol-native } - { name = libpipewire-module-client-node } - { name = libpipewire-module-adapter } - { name = libpipewire-module-filter-chain args = { node.description = "Dolby Surround Sink"
View file
pipewire-0.3.54.tar.gz/src/daemon/filter-chain/sink-eq6.conf -> pipewire-0.3.56.tar.gz/src/daemon/filter-chain/sink-eq6.conf
Changed
@@ -1,29 +1,9 @@ # 6 band sink equalizer # -# start with pipewire -c filter-chain/sink-eq6.conf +# Copy this file into a conf.d/ directory such as +# ~/.config/pipewire/filter-chain.conf.d/ # -context.properties = { - log.level = 0 -} - -context.spa-libs = { - audio.convert.* = audioconvert/libspa-audioconvert - support.* = support/libspa-support -} - context.modules = - { name = libpipewire-module-rt - args = { - #rt.prio = 88 - #rt.time.soft = -1 - #rt.time.hard = -1 - } - flags = ifexists nofail - } - { name = libpipewire-module-protocol-native } - { name = libpipewire-module-client-node } - { name = libpipewire-module-adapter } - { name = libpipewire-module-filter-chain args = { node.description = "Equalizer Sink"
View file
pipewire-0.3.56.tar.gz/src/daemon/filter-chain/sink-make-LFE.conf
Added
@@ -0,0 +1,56 @@ +# An example filter chain that makes a stereo sink that mixes +# the FL and FR channels to FL, FR, LFE +# +# Copy this file into a conf.d/ directory +# +context.modules = + { name = libpipewire-module-filter-chain + args = { + node.description = "LFE example" + media.name = "LFE example" + filter.graph = { + nodes = + { name = copyIL type = builtin label = copy } + { name = copyOL type = builtin label = copy } + { name = copyIR type = builtin label = copy } + { name = copyOR type = builtin label = copy } + { + name = mix + type = builtin + label = mixer + control = { + "Gain 1" = 0.5 + "Gain 2" = 0.5 + } + } + { + type = builtin + name = lpLFE + label = bq_lowpass + control = { "Freq" = 150.0 } + } + + links = + { output = "copyIL:Out" input = "copyOL:In" } + { output = "copyIR:Out" input = "copyOR:In" } + { output = "copyIL:Out" input = "mix:In 1" } + { output = "copyIR:Out" input = "mix:In 2" } + { output = "mix:Out" input = "lpLFE:In" } + + inputs = "copyIL:In" "copyIR:In" + outputs = "copyOL:Out" "copyOR:Out" "lpLFE:Out" + } + capture.props = { + node.name = "input_lfe" + audio.position = FL FR + media.class = "Audio/Sink" + } + playback.props = { + node.name = "output_lfe" + audio.position = FL FR LFE + stream.dont-remix = true + node.passive = true + } + } + } +
View file
pipewire-0.3.54.tar.gz/src/daemon/filter-chain/sink-matrix-spatialiser.conf -> pipewire-0.3.56.tar.gz/src/daemon/filter-chain/sink-matrix-spatialiser.conf
Changed
@@ -1,30 +1,12 @@ # Matrix Spatialiser sink # -# start with pipewire -c filter-chain/sink-matrix-spatialiser.conf +# Copy this file into a conf.d/ directory such as +# ~/.config/pipewire/filter-chain.conf.d/ +# # ( Jean-Philippe Guillemin <hyp3ri0n@sfr.fr> ) - -context.properties = { - log.level = 0 -} - -context.spa-libs = { - audio.convert.* = audioconvert/libspa-audioconvert - support.* = support/libspa-support -} +# context.modules = - { name = libpipewire-module-rt - args = { - #rt.prio = 88 - #rt.time.soft = -1 - #rt.time.hard = -1 - } - flags = ifexists nofail - } - { name = libpipewire-module-protocol-native } - { name = libpipewire-module-client-node } - { name = libpipewire-module-adapter } - { name = libpipewire-module-filter-chain args = { node.description = "Matrix Spatialiser"
View file
pipewire-0.3.56.tar.gz/src/daemon/filter-chain/sink-mix-FL-FR.conf
Added
@@ -0,0 +1,40 @@ +# An example filter chain that makes a stereo sink that mixes +# the FL and FR channels to a single FL channel +# +# Copy this file into a conf.d/ directory such as +# ~/.config/pipewire/filter-chain.conf.d/ +# +context.modules = + { name = libpipewire-module-filter-chain + args = { + node.description = "Mix example" + media.name = "Mix example" + filter.graph = { + nodes = + { + name = mix + type = builtin + label = mixer + control = { + "Gain 1" = 0.5 + "Gain 2" = 0.5 + } + } + + inputs = "mix:In 1" "mix:In 2" + outputs = "mix:Out" + } + capture.props = { + node.name = "mix_input.mix-FL-FR-to-FL" + audio.position = FL FR + media.class = "Audio/Sink" + } + playback.props = { + node.name = "mix_output.mix-FL-FR-to-FL" + audio.position = FL + stream.dont-remix = true + node.passive = true + } + } + } +
View file
pipewire-0.3.54.tar.gz/src/daemon/filter-chain/sink-virtual-surround-5.1-kemar.conf -> pipewire-0.3.56.tar.gz/src/daemon/filter-chain/sink-virtual-surround-5.1-kemar.conf
Changed
@@ -1,29 +1,9 @@ # Convolver sink # -# start with pipewire -c filter-chain/sink-virtual-surround-5.1-kemar.conf +# Copy this file into a conf.d/ directory such as +# ~/.config/pipewire/filter-chain.conf.d/ # -context.properties = { - log.level = 0 -} - -context.spa-libs = { - audio.convert.* = audioconvert/libspa-audioconvert - support.* = support/libspa-support -} - context.modules = - { name = libpipewire-module-rt - args = { - #rt.prio = 88 - #rt.time.soft = -1 - #rt.time.hard = -1 - } - flags = ifexists nofail - } - { name = libpipewire-module-protocol-native } - { name = libpipewire-module-client-node } - { name = libpipewire-module-adapter } - { name = libpipewire-module-filter-chain args = { node.description = "Virtual Surround Sink"
View file
pipewire-0.3.54.tar.gz/src/daemon/filter-chain/sink-virtual-surround-7.1-hesuvi.conf -> pipewire-0.3.56.tar.gz/src/daemon/filter-chain/sink-virtual-surround-7.1-hesuvi.conf
Changed
@@ -1,29 +1,9 @@ # Convolver sink # -# start with pipewire -c filter-chain/sink-virtual-surround-7.1-hesuvi.conf +# Copy this file into a conf.d/ directory such as +# ~/.config/pipewire/filter-chain.conf.d/ # -context.properties = { - log.level = 0 -} - -context.spa-libs = { - audio.convert.* = audioconvert/libspa-audioconvert - support.* = support/libspa-support -} - context.modules = - { name = libpipewire-module-rt - args = { - #rt.prio = 88 - #rt.time.soft = -1 - #rt.time.hard = -1 - } - flags = ifexists nofail - } - { name = libpipewire-module-protocol-native } - { name = libpipewire-module-client-node } - { name = libpipewire-module-adapter } - { name = libpipewire-module-filter-chain args = { node.description = "Virtual Surround Sink"
View file
pipewire-0.3.56.tar.gz/src/daemon/filter-chain/source-duplicate-FL.conf
Added
@@ -0,0 +1,52 @@ +# An example filter chain that makes a source that duplicates the FL channel +# to FL and FR. +# +# Copy this file into a conf.d/ directory such as +# ~/.config/pipewire/filter-chain.conf.d/ +# +context.modules = + { name = libpipewire-module-filter-chain + args = { + node.description = "Remap example" + media.name = "Remap example" + filter.graph = { + nodes = + { + name = copyIL + type = builtin + label = copy + } + { + name = copyOL + type = builtin + label = copy + } + { + name = copyOR + type = builtin + label = copy + } + + links = + # we can only tee from nodes, not inputs so we need + # to copy the inputs and then tee. + { output = "copyIL:Out" input = "copyOL:In" } + { output = "copyIL:Out" input = "copyOR:In" } + + inputs = "copyIL:In" + outputs = "copyOL:Out" "copyOR:Out" + } + capture.props = { + node.name = "remap_input.remap-FL-to-FL-FR" + audio.position = FL + stream.dont-remix = true + node.passive = true + } + playback.props = { + node.name = "remap_output.remap-FL-to-FL-FR" + audio.position = FL FR + media.class = "Audio/Source" + } + } + } +
View file
pipewire-0.3.54.tar.gz/src/daemon/filter-chain/source-rnnoise.conf -> pipewire-0.3.56.tar.gz/src/daemon/filter-chain/source-rnnoise.conf
Changed
@@ -1,29 +1,9 @@ # Noise canceling source # -# start with pipewire -c filter-chain/source-rnnoise.conf +# Copy this file into a conf.d/ directory such as +# ~/.config/pipewire/filter-chain.conf.d/ # -context.properties = { - log.level = 0 -} - -context.spa-libs = { - audio.convert.* = audioconvert/libspa-audioconvert - support.* = support/libspa-support -} - context.modules = - { name = libpipewire-module-rt - args = { - #rt.prio = 88 - #rt.time.soft = -1 - #rt.time.hard = -1 - } - flags = ifexists nofail - } - { name = libpipewire-module-protocol-native } - { name = libpipewire-module-client-node } - { name = libpipewire-module-adapter } - { name = libpipewire-module-filter-chain args = { node.description = "Noise Canceling source"
View file
pipewire-0.3.54.tar.gz/src/daemon/meson.build -> pipewire-0.3.56.tar.gz/src/daemon/meson.build
Changed
@@ -67,9 +67,11 @@ 'pipewire.conf', 'client.conf', 'client-rt.conf', + 'filter-chain.conf', 'jack.conf', 'minimal.conf', 'pipewire-pulse.conf', + 'pipewire-avb.conf', foreach c : conf_files @@ -99,6 +101,14 @@ dependencies : spa_dep, pipewire_dep, , ) +executable('pipewire-avb', + pipewire_daemon_sources, + install: true, + c_args : pipewire_c_args, + include_directories : configinc , + dependencies : spa_dep, pipewire_dep, , +) + ln = find_program('ln') custom_target('pipewire-uninstalled',
View file
pipewire-0.3.56.tar.gz/src/daemon/pipewire-avb.conf.in
Added
@@ -0,0 +1,73 @@ +# PulseAudio config file for PipeWire version @VERSION@ # +# +# Copy and edit this file in @PIPEWIRE_CONFIG_DIR@ for system-wide changes +# or in ~/.config/pipewire for local changes. +# +# It is also possible to place a file with an updated section in +# @PIPEWIRE_CONFIG_DIR@/pipewire-pulse.conf.d/ for system-wide changes or in +# ~/.config/pipewire/pipewire-pulse.conf.d/ for local changes. +# + +context.properties = { + ## Configure properties in the system. + #mem.warn-mlock = false + #mem.allow-mlock = true + #mem.mlock-all = false + #log.level = 2 + + #default.clock.quantum-limit = 8192 +} + +context.spa-libs = { + audio.convert.* = audioconvert/libspa-audioconvert + support.* = support/libspa-support +} + +context.modules = + { name = libpipewire-module-rt + args = { + nice.level = -11 + #rt.prio = 88 + #rt.time.soft = -1 + #rt.time.hard = -1 + } + flags = ifexists nofail + } + { name = libpipewire-module-protocol-native } + { name = libpipewire-module-client-node } + { name = libpipewire-module-adapter } + { name = libpipewire-module-avb + args = { + # contents of avb.properties can also be placed here + # to have config per server. + } + } + + +# Extra modules can be loaded here. Setup in default.pa can be moved here +context.exec = + #{ path = "pactl" args = "load-module module-always-sink" } + + +stream.properties = { + #node.latency = 1024/48000 + #node.autoconnect = true + #resample.quality = 4 + #channelmix.normalize = false + #channelmix.mix-lfe = false + #channelmix.upmix = true + #channelmix.lfe-cutoff = 120 + #channelmix.fc-cutoff = 6000 + #channelmix.rear-delay = 12.0 + #channelmix.stereo-widen = 0.1 + #channelmix.hilbert-taps = 0 +} + +avb.properties = { + # the addresses this server listens on + #ifname = "eth0.2" + ifname = "enp3s0" + # These overrides are only applied when running in a vm. + vm.overrides = { + } +}
View file
pipewire-0.3.54.tar.gz/src/daemon/pipewire.conf.in -> pipewire-0.3.56.tar.gz/src/daemon/pipewire.conf.in
Changed
@@ -54,6 +54,7 @@ # that factory. # audio.convert.* = audioconvert/libspa-audioconvert + avb.* = avb/libspa-avb api.alsa.* = alsa/libspa-alsa api.v4l2.* = v4l2/libspa-v4l2 api.libcamera.* = libcamera/libspa-libcamera
View file
pipewire-0.3.54.tar.gz/src/examples/audio-src.c -> pipewire-0.3.56.tar.gz/src/examples/audio-src.c
Changed
@@ -120,6 +120,7 @@ struct data data = { 0, }; const struct spa_pod *params1; uint8_t buffer1024; + struct pw_properties *props; struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer)); pw_init(&argc, &argv); @@ -142,14 +143,17 @@ * you need to listen to is the process event where you need to produce * the data. */ + props = pw_properties_new(PW_KEY_MEDIA_TYPE, "Audio", + PW_KEY_MEDIA_CATEGORY, "Playback", + PW_KEY_MEDIA_ROLE, "Music", + NULL); + if (argc > 1) + /* Set stream target if given on command line */ + pw_properties_set(props, PW_KEY_TARGET_OBJECT, argv1); data.stream = pw_stream_new_simple( pw_main_loop_get_loop(data.loop), "audio-src", - pw_properties_new( - PW_KEY_MEDIA_TYPE, "Audio", - PW_KEY_MEDIA_CATEGORY, "Playback", - PW_KEY_MEDIA_ROLE, "Music", - NULL), + props, &stream_events, &data); @@ -165,7 +169,7 @@ * called in a realtime thread. */ pw_stream_connect(data.stream, PW_DIRECTION_OUTPUT, - argc > 1 ? (uint32_t)atoi(argv1) : PW_ID_ANY, + PW_ID_ANY, PW_STREAM_FLAG_AUTOCONNECT | PW_STREAM_FLAG_MAP_BUFFERS | PW_STREAM_FLAG_RT_PROCESS,
View file
pipewire-0.3.54.tar.gz/src/examples/export-sink.c -> pipewire-0.3.56.tar.gz/src/examples/export-sink.c
Changed
@@ -472,7 +472,7 @@ props = pw_properties_new(PW_KEY_NODE_AUTOCONNECT, "true", NULL); if (data->path) - pw_properties_set(props, PW_KEY_NODE_TARGET, data->path); + pw_properties_set(props, PW_KEY_TARGET_OBJECT, data->path); pw_properties_set(props, PW_KEY_MEDIA_CLASS, "Stream/Input/Video"); pw_properties_set(props, PW_KEY_MEDIA_TYPE, "Video"); pw_properties_set(props, PW_KEY_MEDIA_CATEGORY, "Capture");
View file
pipewire-0.3.54.tar.gz/src/examples/export-source.c -> pipewire-0.3.56.tar.gz/src/examples/export-source.c
Changed
@@ -483,7 +483,7 @@ PW_KEY_MEDIA_ROLE, "Music", NULL); if (data->path) - pw_properties_set(props, PW_KEY_NODE_TARGET, data->path); + pw_properties_set(props, PW_KEY_TARGET_OBJECT, data->path); data->impl_node.iface = SPA_INTERFACE_INIT( SPA_TYPE_INTERFACE_Node,
View file
pipewire-0.3.54.tar.gz/src/examples/export-spa.c -> pipewire-0.3.56.tar.gz/src/examples/export-spa.c
Changed
@@ -93,7 +93,7 @@ if (data->path) { pw_properties_set(props, PW_KEY_NODE_AUTOCONNECT, "true"); - pw_properties_set(props, PW_KEY_NODE_TARGET, data->path); + pw_properties_set(props, PW_KEY_TARGET_OBJECT, data->path); } data->proxy = pw_core_export(data->core,
View file
pipewire-0.3.54.tar.gz/src/examples/video-dsp-play.c -> pipewire-0.3.56.tar.gz/src/examples/video-dsp-play.c
Changed
@@ -263,7 +263,7 @@ PW_KEY_MEDIA_CATEGORY, "Capture", PW_KEY_MEDIA_ROLE, "DSP", PW_KEY_NODE_AUTOCONNECT, data.target ? "true" : "false", - PW_KEY_NODE_TARGET, data.target, + PW_KEY_TARGET_OBJECT, data.target, PW_KEY_MEDIA_CLASS, "Stream/Input/Video", NULL), &filter_events,
View file
pipewire-0.3.54.tar.gz/src/examples/video-play-fixate.c -> pipewire-0.3.56.tar.gz/src/examples/video-play-fixate.c
Changed
@@ -419,6 +419,7 @@ const struct spa_pod *params2; uint8_t buffer1024; struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer)); + struct pw_properties *props; int res, n_params; pw_init(&argc, &argv); @@ -440,19 +441,22 @@ * you need to listen to is the process event where you need to consume * the data provided to you. */ + props = pw_properties_new(PW_KEY_MEDIA_TYPE, "Video", + PW_KEY_MEDIA_CATEGORY, "Capture", + PW_KEY_MEDIA_ROLE, "Camera", + NULL), + data.path = argc > 1 ? argv1 : NULL; + if (data.path) + /* Set stream target if given on command line */ + pw_properties_set(props, PW_KEY_TARGET_OBJECT, data.path); + data.stream = pw_stream_new_simple( pw_main_loop_get_loop(data.loop), - "video-play-reneg", - pw_properties_new( - PW_KEY_MEDIA_TYPE, "Video", - PW_KEY_MEDIA_CATEGORY, "Capture", - PW_KEY_MEDIA_ROLE, "Camera", - NULL), + "video-play-fixate", + props, &stream_events, &data); - data.path = argc > 1 ? argv1 : NULL; - if (SDL_Init(SDL_INIT_VIDEO) < 0) { fprintf(stderr, "can't initialize SDL: %s\n", SDL_GetError()); return -1; @@ -477,7 +481,7 @@ */ if ((res = pw_stream_connect(data.stream, PW_DIRECTION_INPUT, - data.path ? (uint32_t)atoi(data.path) : PW_ID_ANY, + PW_ID_ANY, PW_STREAM_FLAG_AUTOCONNECT | /* try to automatically connect this stream */ PW_STREAM_FLAG_MAP_BUFFERS, /* mmap the buffer data for us */ params, n_params)) /* extra parameters, see above */ < 0) {
View file
pipewire-0.3.54.tar.gz/src/examples/video-play-pull.c -> pipewire-0.3.56.tar.gz/src/examples/video-play-pull.c
Changed
@@ -493,6 +493,7 @@ const struct spa_pod *params2; uint8_t buffer1024; struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer)); + struct pw_properties *props; int res, n_params; pw_init(&argc, &argv); @@ -517,20 +518,23 @@ * you need to listen to is the process event where you need to consume * the data provided to you. */ + props = pw_properties_new(PW_KEY_MEDIA_TYPE, "Video", + PW_KEY_MEDIA_CATEGORY, "Capture", + PW_KEY_MEDIA_ROLE, "Camera", + PW_KEY_PRIORITY_DRIVER, "10000", + NULL), + data.path = argc > 1 ? argv1 : NULL; + if (data.path) + /* Set stream target if given on command line */ + pw_properties_set(props, PW_KEY_TARGET_OBJECT, data.path); + data.stream = pw_stream_new_simple( pw_main_loop_get_loop(data.loop), "video-play", - pw_properties_new( - PW_KEY_MEDIA_TYPE, "Video", - PW_KEY_MEDIA_CATEGORY, "Capture", - PW_KEY_MEDIA_ROLE, "Camera", - PW_KEY_PRIORITY_DRIVER, "10000", - NULL), + props, &stream_events, &data); - data.path = argc > 1 ? argv1 : NULL; - if (SDL_Init(SDL_INIT_VIDEO) < 0) { fprintf(stderr, "can't initialize SDL: %s\n", SDL_GetError()); return -1; @@ -552,7 +556,7 @@ */ if ((res = pw_stream_connect(data.stream, PW_DIRECTION_INPUT, - data.path ? (uint32_t)atoi(data.path) : PW_ID_ANY, + PW_ID_ANY, PW_STREAM_FLAG_DRIVER | /* we're driver, we pull */ PW_STREAM_FLAG_AUTOCONNECT | /* try to automatically connect this stream */ PW_STREAM_FLAG_MAP_BUFFERS, /* mmap the buffer data for us */
View file
pipewire-0.3.54.tar.gz/src/examples/video-play-reneg.c -> pipewire-0.3.56.tar.gz/src/examples/video-play-reneg.c
Changed
@@ -346,6 +346,7 @@ const struct spa_pod *params2; uint8_t buffer1024; struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer)); + struct pw_properties *props; int res, n_params; pw_init(&argc, &argv); @@ -367,19 +368,22 @@ * you need to listen to is the process event where you need to consume * the data provided to you. */ + props = pw_properties_new(PW_KEY_MEDIA_TYPE, "Video", + PW_KEY_MEDIA_CATEGORY, "Capture", + PW_KEY_MEDIA_ROLE, "Camera", + NULL), + data.path = argc > 1 ? argv1 : NULL; + if (data.path) + /* Set stream target if given on command line */ + pw_properties_set(props, PW_KEY_TARGET_OBJECT, data.path); + data.stream = pw_stream_new_simple( pw_main_loop_get_loop(data.loop), "video-play-reneg", - pw_properties_new( - PW_KEY_MEDIA_TYPE, "Video", - PW_KEY_MEDIA_CATEGORY, "Capture", - PW_KEY_MEDIA_ROLE, "Camera", - NULL), + props, &stream_events, &data); - data.path = argc > 1 ? argv1 : NULL; - if (SDL_Init(SDL_INIT_VIDEO) < 0) { fprintf(stderr, "can't initialize SDL: %s\n", SDL_GetError()); return -1; @@ -401,7 +405,7 @@ */ if ((res = pw_stream_connect(data.stream, PW_DIRECTION_INPUT, - data.path ? (uint32_t)atoi(data.path) : PW_ID_ANY, + PW_ID_ANY, PW_STREAM_FLAG_AUTOCONNECT | /* try to automatically connect this stream */ PW_STREAM_FLAG_MAP_BUFFERS, /* mmap the buffer data for us */ params, n_params)) /* extra parameters, see above */ < 0) {
View file
pipewire-0.3.54.tar.gz/src/examples/video-play.c -> pipewire-0.3.56.tar.gz/src/examples/video-play.c
Changed
@@ -439,6 +439,7 @@ const struct spa_pod *params2; uint8_t buffer1024; struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer)); + struct pw_properties *props; int res, n_params; pw_init(&argc, &argv); @@ -460,19 +461,21 @@ * you need to listen to is the process event where you need to consume * the data provided to you. */ + props = pw_properties_new(PW_KEY_MEDIA_TYPE, "Video", + PW_KEY_MEDIA_CATEGORY, "Capture", + PW_KEY_MEDIA_ROLE, "Camera", + NULL), + data.path = argc > 1 ? argv1 : NULL; + if (data.path) + pw_properties_set(props, PW_KEY_TARGET_OBJECT, data.path); + data.stream = pw_stream_new_simple( pw_main_loop_get_loop(data.loop), "video-play", - pw_properties_new( - PW_KEY_MEDIA_TYPE, "Video", - PW_KEY_MEDIA_CATEGORY, "Capture", - PW_KEY_MEDIA_ROLE, "Camera", - NULL), + props, &stream_events, &data); - data.path = argc > 1 ? argv1 : NULL; - if (SDL_Init(SDL_INIT_VIDEO) < 0) { fprintf(stderr, "can't initialize SDL: %s\n", SDL_GetError()); return -1; @@ -494,7 +497,7 @@ */ if ((res = pw_stream_connect(data.stream, PW_DIRECTION_INPUT, - data.path ? (uint32_t)atoi(data.path) : PW_ID_ANY, + PW_ID_ANY, PW_STREAM_FLAG_AUTOCONNECT | /* try to automatically connect this stream */ PW_STREAM_FLAG_INACTIVE | /* we will activate ourselves */ PW_STREAM_FLAG_MAP_BUFFERS, /* mmap the buffer data for us */
View file
pipewire-0.3.54.tar.gz/src/examples/video-src-alloc.c -> pipewire-0.3.56.tar.gz/src/examples/video-src-alloc.c
Changed
@@ -442,7 +442,7 @@ * the server. */ pw_stream_connect(data.stream, PW_DIRECTION_OUTPUT, - PW_ID_ANY, /* link to any node */ + PW_ID_ANY, PW_STREAM_FLAG_DRIVER | PW_STREAM_FLAG_ALLOC_BUFFERS, params, 1);
View file
pipewire-0.3.54.tar.gz/src/examples/video-src-fixate.c -> pipewire-0.3.56.tar.gz/src/examples/video-src-fixate.c
Changed
@@ -578,7 +578,7 @@ * the server. */ pw_stream_connect(data.stream, PW_DIRECTION_OUTPUT, - PW_ID_ANY, /* link to any node */ + PW_ID_ANY, PW_STREAM_FLAG_DRIVER | PW_STREAM_FLAG_ALLOC_BUFFERS, params, 2);
View file
pipewire-0.3.54.tar.gz/src/examples/video-src-reneg.c -> pipewire-0.3.56.tar.gz/src/examples/video-src-reneg.c
Changed
@@ -487,7 +487,7 @@ * the server. */ pw_stream_connect(data.stream, PW_DIRECTION_OUTPUT, - PW_ID_ANY, /* link to any node */ + PW_ID_ANY, PW_STREAM_FLAG_DRIVER | PW_STREAM_FLAG_ALLOC_BUFFERS, params, 1);
View file
pipewire-0.3.54.tar.gz/src/modules/meson.build -> pipewire-0.3.56.tar.gz/src/modules/meson.build
Changed
@@ -5,6 +5,7 @@ module_sources = 'module-access.c', 'module-adapter.c', + 'module-avb.c', 'module-client-device.c', 'module-client-node.c', 'module-echo-cancel.c', @@ -516,3 +517,30 @@ install_rpath: modules_install_dir, dependencies : mathlib, dl_lib, rt_lib, pipewire_dep, ) + +build_module_avb = get_option('avb').allowed() +if build_module_avb +pipewire_module_avb = shared_library('pipewire-module-avb', + 'module-avb.c', + 'module-avb/avb.c', + 'module-avb/adp.c', + 'module-avb/acmp.c', + 'module-avb/aecp.c', + 'module-avb/aecp-aem.c', + 'module-avb/avdecc.c', + 'module-avb/maap.c', + 'module-avb/mmrp.c', + 'module-avb/mrp.c', + 'module-avb/msrp.c', + 'module-avb/mvrp.c', + 'module-avb/srp.c', + 'module-avb/stream.c' + , + include_directories : configinc, + install : true, + install_dir : modules_install_dir, + install_rpath: modules_install_dir, + dependencies : mathlib, dl_lib, rt_lib, pipewire_dep, +) +endif +summary({'avb': build_module_avb}, bool_yn: true, section: 'Optional Modules')
View file
pipewire-0.3.56.tar.gz/src/modules/module-avb
Added
+(directory)
View file
pipewire-0.3.56.tar.gz/src/modules/module-avb.c
Added
@@ -0,0 +1,130 @@ +/* PipeWire + * + * Copyright © 2022 Wim Taymans + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include <string.h> +#include <stdio.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> + +#include "config.h" + +#include <spa/utils/result.h> +#include <spa/utils/string.h> +#include <spa/utils/json.h> + +#include <pipewire/impl.h> +#include <pipewire/private.h> +#include <pipewire/i18n.h> + +#include "module-avb/avb.h" + +/** \page page_module_avb PipeWire Module: AVB + */ + +#define NAME "avb" + +PW_LOG_TOPIC_STATIC(mod_topic, "mod." NAME); +#define PW_LOG_TOPIC_DEFAULT mod_topic + +#define MODULE_USAGE " " + +static const struct spa_dict_item module_props = { + { PW_KEY_MODULE_AUTHOR, "Wim Taymans <wim.taymans@gmail.com>" }, + { PW_KEY_MODULE_DESCRIPTION, "Manage an AVB network" }, + { PW_KEY_MODULE_USAGE, MODULE_USAGE }, + { PW_KEY_MODULE_VERSION, PACKAGE_VERSION }, +}; + + +struct impl { + struct pw_context *context; + + struct pw_impl_module *module; + struct spa_hook module_listener; + + struct pw_avb *avb; +}; + +static void impl_free(struct impl *impl) +{ + free(impl); +} + +static void module_destroy(void *data) +{ + struct impl *impl = data; + spa_hook_remove(&impl->module_listener); + impl_free(impl); +} + +static const struct pw_impl_module_events module_events = { + PW_VERSION_IMPL_MODULE_EVENTS, + .destroy = module_destroy, +}; + +SPA_EXPORT +int pipewire__module_init(struct pw_impl_module *module, const char *args) +{ + struct pw_context *context = pw_impl_module_get_context(module); + struct pw_properties *props; + struct impl *impl; + int res; + + PW_LOG_TOPIC_INIT(mod_topic); + + impl = calloc(1, sizeof(struct impl)); + if (impl == NULL) + goto error_errno; + + pw_log_debug("module %p: new %s", impl, args); + + if (args == NULL) + args = ""; + + props = pw_properties_new_string(args); + if (props == NULL) + goto error_errno; + + impl->module = module; + impl->context = context; + + impl->avb = pw_avb_new(context, props, 0); + if (impl->avb == NULL) + goto error_errno; + + pw_impl_module_add_listener(module, &impl->module_listener, &module_events, impl); + + pw_impl_module_update_properties(module, &SPA_DICT_INIT_ARRAY(module_props)); + + return 0; + +error_errno: + res = -errno; + if (impl) + impl_free(impl); + return res; +}
View file
pipewire-0.3.56.tar.gz/src/modules/module-avb/aaf.h
Added
@@ -0,0 +1,102 @@ +/* AVB support + * + * Copyright © 2022 Wim Taymans + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef AVB_AAF_H +#define AVB_AAF_H + +struct avb_packet_aaf { + uint8_t subtype; +#if __BYTE_ORDER == __BIG_ENDIAN + unsigned sv:1; + unsigned version:3; + unsigned mr:1; + unsigned _r1:1; + unsigned gv:1; + unsigned tv:1; + + uint8_t seq_number; + + unsigned _r2:7; + unsigned tu:1; +#elif __BYTE_ORDER == __LITTLE_ENDIAN + unsigned tv:1; + unsigned gv:1; + unsigned _r1:1; + unsigned mr:1; + unsigned version:3; + unsigned sv:1; + + uint8_t seq_num; + + unsigned tu:1; + unsigned _r2:7; +#endif + uint64_t stream_id; + uint32_t timestamp; +#define AVB_AAF_FORMAT_USER 0x00 +#define AVB_AAF_FORMAT_FLOAT_32BIT 0x01 +#define AVB_AAF_FORMAT_INT_32BIT 0x02 +#define AVB_AAF_FORMAT_INT_24BIT 0x03 +#define AVB_AAF_FORMAT_INT_16BIT 0x04 +#define AVB_AAF_FORMAT_AES3_32BIT 0x05 + uint8_t format; + +#define AVB_AAF_PCM_NSR_USER 0x00 +#define AVB_AAF_PCM_NSR_8KHZ 0x01 +#define AVB_AAF_PCM_NSR_16KHZ 0x02 +#define AVB_AAF_PCM_NSR_32KHZ 0x03 +#define AVB_AAF_PCM_NSR_44_1KHZ 0x04 +#define AVB_AAF_PCM_NSR_48KHZ 0x05 +#define AVB_AAF_PCM_NSR_88_2KHZ 0x06 +#define AVB_AAF_PCM_NSR_96KHZ 0x07 +#define AVB_AAF_PCM_NSR_176_4KHZ 0x08 +#define AVB_AAF_PCM_NSR_192KHZ 0x09 +#define AVB_AAF_PCM_NSR_24KHZ 0x0A +#if __BYTE_ORDER == __BIG_ENDIAN + unsigned nsr:4; + unsigned _r3:4; +#elif __BYTE_ORDER == __LITTLE_ENDIAN + unsigned _r3:4; + unsigned nsr:4; +#endif + uint8_t chan_per_frame; + uint8_t bit_depth; + uint16_t data_len; + +#define AVB_AAF_PCM_SP_NORMAL 0x00 +#define AVB_AAF_PCM_SP_SPARSE 0x01 +#if __BYTE_ORDER == __BIG_ENDIAN + unsigned _r4:3; + unsigned sp:1; + unsigned event:4; +#elif __BYTE_ORDER == __LITTLE_ENDIAN + unsigned event:4; + unsigned sp:1; + unsigned _r4:3; +#endif + uint8_t _r5; + uint8_t payload0; +} __attribute__ ((__packed__)); + +#endif /* AVB_AAF_H */
View file
pipewire-0.3.56.tar.gz/src/modules/module-avb/acmp.c
Added
@@ -0,0 +1,477 @@ +/* AVB support + * + * Copyright © 2022 Wim Taymans + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include <spa/utils/json.h> +#include <spa/debug/mem.h> + +#include <pipewire/pipewire.h> + +#include "acmp.h" +#include "msrp.h" +#include "internal.h" +#include "stream.h" + +static const uint8_t mac6 = AVB_BROADCAST_MAC; + +struct pending { + struct spa_list link; + uint64_t last_time; + uint64_t timeout; + uint16_t old_sequence_id; + uint16_t sequence_id; + uint16_t retry; + size_t size; + void *ptr; +}; + +struct acmp { + struct server *server; + struct spa_hook server_listener; + +#define PENDING_TALKER 0 +#define PENDING_LISTENER 1 +#define PENDING_CONTROLLER 2 + struct spa_list pending3; + uint16_t sequence_id3; +}; + +static void *pending_new(struct acmp *acmp, uint32_t type, uint64_t now, uint32_t timeout_ms, + const void *m, size_t size) +{ + struct pending *p; + struct avb_ethernet_header *h; + struct avb_packet_acmp *pm; + + p = calloc(1, sizeof(*p) + size); + if (p == NULL) + return NULL; + p->last_time = now; + p->timeout = timeout_ms * SPA_NSEC_PER_MSEC; + p->sequence_id = acmp->sequence_idtype++; + p->size = size; + p->ptr = SPA_PTROFF(p, sizeof(*p), void); + memcpy(p->ptr, m, size); + + h = p->ptr; + pm = SPA_PTROFF(h, sizeof(*h), void); + p->old_sequence_id = ntohs(pm->sequence_id); + pm->sequence_id = htons(p->sequence_id); + spa_list_append(&acmp->pendingtype, &p->link); + + return p->ptr; +} + +static struct pending *pending_find(struct acmp *acmp, uint32_t type, uint16_t sequence_id) +{ + struct pending *p; + spa_list_for_each(p, &acmp->pendingtype, link) + if (p->sequence_id == sequence_id) + return p; + return NULL; +} + +static void pending_free(struct acmp *acmp, struct pending *p) +{ + spa_list_remove(&p->link); + free(p); +} + +struct msg_info { + uint16_t type; + const char *name; + int (*handle) (struct acmp *acmp, uint64_t now, const void *m, int len); +}; + +static int reply_not_supported(struct acmp *acmp, uint8_t type, const void *m, int len) +{ + struct server *server = acmp->server; + uint8_t buflen; + struct avb_ethernet_header *h = (void*)buf; + struct avb_packet_acmp *reply = SPA_PTROFF(h, sizeof(*h), void); + + memcpy(h, m, len); + AVB_PACKET_ACMP_SET_MESSAGE_TYPE(reply, type); + AVB_PACKET_ACMP_SET_STATUS(reply, AVB_ACMP_STATUS_NOT_SUPPORTED); + + return avb_server_send_packet(server, h->src, AVB_TSN_ETH, buf, len); +} + +static int retry_pending(struct acmp *acmp, uint64_t now, struct pending *p) +{ + struct server *server = acmp->server; + struct avb_ethernet_header *h = p->ptr; + p->retry++; + p->last_time = now; + return avb_server_send_packet(server, h->dest, AVB_TSN_ETH, p->ptr, p->size); +} + +static int handle_connect_tx_command(struct acmp *acmp, uint64_t now, const void *m, int len) +{ + struct server *server = acmp->server; + uint8_t buflen; + struct avb_ethernet_header *h = (void*)buf; + struct avb_packet_acmp *reply = SPA_PTROFF(h, sizeof(*h), void); + const struct avb_packet_acmp *p = SPA_PTROFF(m, sizeof(*h), void); + int status = AVB_ACMP_STATUS_SUCCESS; + struct stream *stream; + + if (be64toh(p->talker_guid) != server->entity_id) + return 0; + + memcpy(buf, m, len); + stream = server_find_stream(server, SPA_DIRECTION_OUTPUT, + reply->talker_unique_id); + if (stream == NULL) { + status = AVB_ACMP_STATUS_TALKER_NO_STREAM_INDEX; + goto done; + } + + AVB_PACKET_ACMP_SET_MESSAGE_TYPE(reply, AVB_ACMP_MESSAGE_TYPE_CONNECT_TX_RESPONSE); + reply->stream_id = htobe64(stream->id); + + stream_activate(stream, now); + + memcpy(reply->stream_dest_mac, stream->addr, 6); + reply->connection_count = htons(1); + reply->stream_vlan_id = htons(stream->vlan_id); + +done: + AVB_PACKET_ACMP_SET_STATUS(reply, status); + return avb_server_send_packet(server, h->dest, AVB_TSN_ETH, buf, len); +} + +static int handle_connect_tx_response(struct acmp *acmp, uint64_t now, const void *m, int len) +{ + struct server *server = acmp->server; + struct avb_ethernet_header *h; + const struct avb_packet_acmp *resp = SPA_PTROFF(m, sizeof(*h), void); + struct avb_packet_acmp *reply; + struct pending *pending; + uint16_t sequence_id; + struct stream *stream; + int res; + + if (be64toh(resp->listener_guid) != server->entity_id) + return 0; + + sequence_id = ntohs(resp->sequence_id); + + pending = pending_find(acmp, PENDING_TALKER, sequence_id); + if (pending == NULL) + return 0; + + h = pending->ptr; + pending->size = SPA_MIN((int)pending->size, len); + memcpy(h, m, pending->size); + + reply = SPA_PTROFF(h, sizeof(*h), void); + reply->sequence_id = htons(pending->old_sequence_id); + AVB_PACKET_ACMP_SET_MESSAGE_TYPE(reply, AVB_ACMP_MESSAGE_TYPE_CONNECT_RX_RESPONSE); + + stream = server_find_stream(server, SPA_DIRECTION_INPUT, + ntohs(reply->listener_unique_id)); + if (stream == NULL) + return 0; + + stream->peer_id = be64toh(reply->stream_id); + memcpy(stream->addr, reply->stream_dest_mac, 6); + stream_activate(stream, now);
View file
pipewire-0.3.56.tar.gz/src/modules/module-avb/acmp.h
Added
@@ -0,0 +1,99 @@ +/* AVB support + * + * Copyright © 2022 Wim Taymans + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef AVB_ACMP_H +#define AVB_ACMP_H + +#include "packets.h" +#include "internal.h" + +#define AVB_ACMP_MESSAGE_TYPE_CONNECT_TX_COMMAND 0 +#define AVB_ACMP_MESSAGE_TYPE_CONNECT_TX_RESPONSE 1 +#define AVB_ACMP_MESSAGE_TYPE_DISCONNECT_TX_COMMAND 2 +#define AVB_ACMP_MESSAGE_TYPE_DISCONNECT_TX_RESPONSE 3 +#define AVB_ACMP_MESSAGE_TYPE_GET_TX_STATE_COMMAND 4 +#define AVB_ACMP_MESSAGE_TYPE_GET_TX_STATE_RESPONSE 5 +#define AVB_ACMP_MESSAGE_TYPE_CONNECT_RX_COMMAND 6 +#define AVB_ACMP_MESSAGE_TYPE_CONNECT_RX_RESPONSE 7 +#define AVB_ACMP_MESSAGE_TYPE_DISCONNECT_RX_COMMAND 8 +#define AVB_ACMP_MESSAGE_TYPE_DISCONNECT_RX_RESPONSE 9 +#define AVB_ACMP_MESSAGE_TYPE_GET_RX_STATE_COMMAND 10 +#define AVB_ACMP_MESSAGE_TYPE_GET_RX_STATE_RESPONSE 11 +#define AVB_ACMP_MESSAGE_TYPE_GET_TX_CONNECTION_COMMAND 12 +#define AVB_ACMP_MESSAGE_TYPE_GET_TX_CONNECTION_RESPONSE 13 + +#define AVB_ACMP_STATUS_SUCCESS 0 +#define AVB_ACMP_STATUS_LISTENER_UNKNOWN_ID 1 +#define AVB_ACMP_STATUS_TALKER_UNKNOWN_ID 2 +#define AVB_ACMP_STATUS_TALKER_DEST_MAC_FAIL 3 +#define AVB_ACMP_STATUS_TALKER_NO_STREAM_INDEX 4 +#define AVB_ACMP_STATUS_TALKER_NO_BANDWIDTH 5 +#define AVB_ACMP_STATUS_TALKER_EXCLUSIVE 6 +#define AVB_ACMP_STATUS_LISTENER_TALKER_TIMEOUT 7 +#define AVB_ACMP_STATUS_LISTENER_EXCLUSIVE 8 +#define AVB_ACMP_STATUS_STATE_UNAVAILABLE 9 +#define AVB_ACMP_STATUS_NOT_CONNECTED 10 +#define AVB_ACMP_STATUS_NO_SUCH_CONNECTION 11 +#define AVB_ACMP_STATUS_COULD_NOT_SEND_MESSAGE 12 +#define AVB_ACMP_STATUS_TALKER_MISBEHAVING 13 +#define AVB_ACMP_STATUS_LISTENER_MISBEHAVING 14 +#define AVB_ACMP_STATUS_RESERVED 15 +#define AVB_ACMP_STATUS_CONTROLLER_NOT_AUTHORIZED 16 +#define AVB_ACMP_STATUS_INCOMPATIBLE_REQUEST 17 +#define AVB_ACMP_STATUS_LISTENER_INVALID_CONNECTION 18 +#define AVB_ACMP_STATUS_NOT_SUPPORTED 31 + +#define AVB_ACMP_TIMEOUT_CONNECT_TX_COMMAND_MS 2000 +#define AVB_ACMP_TIMEOUT_DISCONNECT_TX_COMMAND_MS 200 +#define AVB_ACMP_TIMEOUT_GET_TX_STATE_COMMAND 200 +#define AVB_ACMP_TIMEOUT_CONNECT_RX_COMMAND_MS 4500 +#define AVB_ACMP_TIMEOUT_DISCONNECT_RX_COMMAND_MS 500 +#define AVB_ACMP_TIMEOUT_GET_RX_STATE_COMMAND_MS 200 +#define AVB_ACMP_TIMEOUT_GET_TX_CONNECTION_COMMAND 200 + +struct avb_packet_acmp { + struct avb_packet_header hdr; + uint64_t stream_id; + uint64_t controller_guid; + uint64_t talker_guid; + uint64_t listener_guid; + uint16_t talker_unique_id; + uint16_t listener_unique_id; + char stream_dest_mac6; + uint16_t connection_count; + uint16_t sequence_id; + uint16_t flags; + uint16_t stream_vlan_id; + uint16_t reserved; +} __attribute__ ((__packed__)); + +#define AVB_PACKET_ACMP_SET_MESSAGE_TYPE(p,v) AVB_PACKET_SET_SUB1(&(p)->hdr, v) +#define AVB_PACKET_ACMP_SET_STATUS(p,v) AVB_PACKET_SET_SUB2(&(p)->hdr, v) + +#define AVB_PACKET_ACMP_GET_MESSAGE_TYPE(p) AVB_PACKET_GET_SUB1(&(p)->hdr) +#define AVB_PACKET_ACMP_GET_STATUS(p) AVB_PACKET_GET_SUB2(&(p)->hdr) + +struct avb_acmp *avb_acmp_register(struct server *server); + +#endif /* AVB_ACMP_H */
View file
pipewire-0.3.56.tar.gz/src/modules/module-avb/adp.c
Added
@@ -0,0 +1,381 @@ +/* AVB support + * + * Copyright © 2022 Wim Taymans + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include <spa/utils/json.h> + +#include <pipewire/pipewire.h> + +#include "adp.h" +#include "aecp-aem-descriptors.h" +#include "internal.h" +#include "utils.h" + +static const uint8_t mac6 = AVB_BROADCAST_MAC; + +struct entity { + struct spa_list link; + uint64_t entity_id; + uint64_t last_time; + int valid_time; + unsigned advertise:1; + size_t len; + uint8_t buf128; +}; + +struct adp { + struct server *server; + struct spa_hook server_listener; + + struct spa_list entities; + uint32_t available_index; +}; + +static struct entity *find_entity_by_id(struct adp *adp, uint64_t id) +{ + struct entity *e; + spa_list_for_each(e, &adp->entities, link) + if (e->entity_id == id) + return e; + return NULL; +} +static void entity_free(struct entity *e) +{ + spa_list_remove(&e->link); + free(e); +} + +static int send_departing(struct adp *adp, uint64_t now, struct entity *e) +{ + struct avb_ethernet_header *h = (void*)e->buf; + struct avb_packet_adp *p = SPA_PTROFF(h, sizeof(*h), void); + + AVB_PACKET_ADP_SET_MESSAGE_TYPE(p, AVB_ADP_MESSAGE_TYPE_ENTITY_DEPARTING); + p->available_index = htonl(adp->available_index++); + avb_server_send_packet(adp->server, mac, AVB_TSN_ETH, e->buf, e->len); + e->last_time = now; + return 0; +} + +static int send_advertise(struct adp *adp, uint64_t now, struct entity *e) +{ + struct avb_ethernet_header *h = (void*)e->buf; + struct avb_packet_adp *p = SPA_PTROFF(h, sizeof(*h), void); + + AVB_PACKET_ADP_SET_MESSAGE_TYPE(p, AVB_ADP_MESSAGE_TYPE_ENTITY_AVAILABLE); + p->available_index = htonl(adp->available_index++); + avb_server_send_packet(adp->server, mac, AVB_TSN_ETH, e->buf, e->len); + e->last_time = now; + return 0; +} + +static int send_discover(struct adp *adp, uint64_t entity_id) +{ + uint8_t buf128; + struct avb_ethernet_header *h = (void*)buf; + struct avb_packet_adp *p = SPA_PTROFF(h, sizeof(*h), void); + size_t len = sizeof(*h) + sizeof(*p); + + spa_memzero(buf, sizeof(buf)); + AVB_PACKET_SET_SUBTYPE(&p->hdr, AVB_SUBTYPE_ADP); + AVB_PACKET_SET_LENGTH(&p->hdr, AVB_ADP_CONTROL_DATA_LENGTH); + AVB_PACKET_ADP_SET_MESSAGE_TYPE(p, AVB_ADP_MESSAGE_TYPE_ENTITY_DISCOVER); + p->entity_id = htonl(entity_id); + avb_server_send_packet(adp->server, mac, AVB_TSN_ETH, buf, len); + return 0; +} + +static int adp_message(void *data, uint64_t now, const void *message, int len) +{ + struct adp *adp = data; + struct server *server = adp->server; + const struct avb_ethernet_header *h = message; + const struct avb_packet_adp *p = SPA_PTROFF(h, sizeof(*h), void); + struct entity *e; + int message_type; + char buf128; + uint64_t entity_id; + + if (ntohs(h->type) != AVB_TSN_ETH) + return 0; + if (memcmp(h->dest, mac, 6) != 0 && + memcmp(h->dest, server->mac_addr, 6) != 0) + return 0; + + if (AVB_PACKET_GET_SUBTYPE(&p->hdr) != AVB_SUBTYPE_ADP || + AVB_PACKET_GET_LENGTH(&p->hdr) < AVB_ADP_CONTROL_DATA_LENGTH) + return 0; + + message_type = AVB_PACKET_ADP_GET_MESSAGE_TYPE(p); + entity_id = be64toh(p->entity_id); + + e = find_entity_by_id(adp, entity_id); + + switch (message_type) { + case AVB_ADP_MESSAGE_TYPE_ENTITY_AVAILABLE: + if (e == NULL) { + e = calloc(1, sizeof(*e)); + if (e == NULL) + return -errno; + + memcpy(e->buf, message, len); + e->len = len; + e->valid_time = AVB_PACKET_ADP_GET_VALID_TIME(p); + e->entity_id = entity_id; + spa_list_append(&adp->entities, &e->link); + pw_log_info("entity %s available", + avb_utils_format_id(buf, sizeof(buf), entity_id)); + } + e->last_time = now; + break; + case AVB_ADP_MESSAGE_TYPE_ENTITY_DEPARTING: + if (e != NULL) { + pw_log_info("entity %s departing", + avb_utils_format_id(buf, sizeof(buf), entity_id)); + entity_free(e); + } + break; + case AVB_ADP_MESSAGE_TYPE_ENTITY_DISCOVER: + pw_log_info("entity %s advertise", + avb_utils_format_id(buf, sizeof(buf), entity_id)); + if (entity_id == 0UL) { + spa_list_for_each(e, &adp->entities, link) + if (e->advertise) + send_advertise(adp, now, e); + } else if (e != NULL && + e->advertise && e->entity_id == entity_id) { + send_advertise(adp, now, e); + } + break; + default: + return -EINVAL; + } + return 0; +} + +static void adp_destroy(void *data) +{ + struct adp *adp = data; + spa_hook_remove(&adp->server_listener); + free(adp); +} + +static void check_timeout(struct adp *adp, uint64_t now) +{ + struct entity *e, *t; + char buf128; + + spa_list_for_each_safe(e, t, &adp->entities, link) { + if (e->last_time + (e->valid_time + 2) * SPA_NSEC_PER_SEC > now) + continue; + + pw_log_info("entity %s timeout", + avb_utils_format_id(buf, sizeof(buf), e->entity_id)); + + if (e->advertise) + send_departing(adp, now, e); + + entity_free(e); + }
View file
pipewire-0.3.56.tar.gz/src/modules/module-avb/adp.h
Added
@@ -0,0 +1,105 @@ +/* AVB support + * + * Copyright © 2022 Wim Taymans + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef AVB_ADP_H +#define AVB_ADP_H + +#include "packets.h" +#include "internal.h" + +#define AVB_ADP_MESSAGE_TYPE_ENTITY_AVAILABLE 0 +#define AVB_ADP_MESSAGE_TYPE_ENTITY_DEPARTING 1 +#define AVB_ADP_MESSAGE_TYPE_ENTITY_DISCOVER 2 + +#define AVB_ADP_ENTITY_CAPABILITY_EFU_MODE (1u<<0) +#define AVB_ADP_ENTITY_CAPABILITY_ADDRESS_ACCESS_SUPPORTED (1u<<1) +#define AVB_ADP_ENTITY_CAPABILITY_GATEWAY_ENTITY (1u<<2) +#define AVB_ADP_ENTITY_CAPABILITY_AEM_SUPPORTED (1u<<3) +#define AVB_ADP_ENTITY_CAPABILITY_LEGACY_AVC (1u<<4) +#define AVB_ADP_ENTITY_CAPABILITY_ASSOCIATION_ID_SUPPORTED (1u<<5) +#define AVB_ADP_ENTITY_CAPABILITY_ASSOCIATION_ID_VALID (1u<<6) +#define AVB_ADP_ENTITY_CAPABILITY_VENDOR_UNIQUE_SUPPORTED (1u<<7) +#define AVB_ADP_ENTITY_CAPABILITY_CLASS_A_SUPPORTED (1u<<8) +#define AVB_ADP_ENTITY_CAPABILITY_CLASS_B_SUPPORTED (1u<<9) +#define AVB_ADP_ENTITY_CAPABILITY_GPTP_SUPPORTED (1u<<10) +#define AVB_ADP_ENTITY_CAPABILITY_AEM_AUTHENTICATION_SUPPORTED (1u<<11) +#define AVB_ADP_ENTITY_CAPABILITY_AEM_AUTHENTICATION_REQUIRED (1u<<12) +#define AVB_ADP_ENTITY_CAPABILITY_AEM_PERSISTENT_ACQUIRE_SUPPORTED (1u<<13) +#define AVB_ADP_ENTITY_CAPABILITY_AEM_IDENTIFY_CONTROL_INDEX_VALID (1u<<14) +#define AVB_ADP_ENTITY_CAPABILITY_AEM_INTERFACE_INDEX_VALID (1u<<15) +#define AVB_ADP_ENTITY_CAPABILITY_GENERAL_CONTROLLER_IGNORE (1u<<16) +#define AVB_ADP_ENTITY_CAPABILITY_ENTITY_NOT_READY (1u<<17) + +#define AVB_ADP_TALKER_CAPABILITY_IMPLEMENTED (1u<<0) +#define AVB_ADP_TALKER_CAPABILITY_OTHER_SOURCE (1u<<9) +#define AVB_ADP_TALKER_CAPABILITY_CONTROL_SOURCE (1u<<10) +#define AVB_ADP_TALKER_CAPABILITY_MEDIA_CLOCK_SOURCE (1u<<11) +#define AVB_ADP_TALKER_CAPABILITY_SMPTE_SOURCE (1u<<12) +#define AVB_ADP_TALKER_CAPABILITY_MIDI_SOURCE (1u<<13) +#define AVB_ADP_TALKER_CAPABILITY_AUDIO_SOURCE (1u<<14) +#define AVB_ADP_TALKER_CAPABILITY_VIDEO_SOURCE (1u<<15) + +#define AVB_ADP_LISTENER_CAPABILITY_IMPLEMENTED (1u<<0) +#define AVB_ADP_LISTENER_CAPABILITY_OTHER_SINK (1u<<9) +#define AVB_ADP_LISTENER_CAPABILITY_CONTROL_SINK (1u<<10) +#define AVB_ADP_LISTENER_CAPABILITY_MEDIA_CLOCK_SINK (1u<<11) +#define AVB_ADP_LISTENER_CAPABILITY_SMPTE_SINK (1u<<12) +#define AVB_ADP_LISTENER_CAPABILITY_MIDI_SINK (1u<<13) +#define AVB_ADP_LISTENER_CAPABILITY_AUDIO_SINK (1u<<14) +#define AVB_ADP_LISTENER_CAPABILITY_VIDEO_SINK (1u<<15) + +#define AVB_ADP_CONTROLLER_CAPABILITY_IMPLEMENTED (1u<<0) +#define AVB_ADP_CONTROLLER_CAPABILITY_LAYER3_PROXY (1u<<1) + +#define AVB_ADP_CONTROL_DATA_LENGTH 56 + +struct avb_packet_adp { + struct avb_packet_header hdr; + uint64_t entity_id; + uint64_t entity_model_id; + uint32_t entity_capabilities; + uint16_t talker_stream_sources; + uint16_t talker_capabilities; + uint16_t listener_stream_sinks; + uint16_t listener_capabilities; + uint32_t controller_capabilities; + uint32_t available_index; + uint64_t gptp_grandmaster_id; + uint8_t gptp_domain_number; + uint8_t reserved03; + uint16_t identify_control_index; + uint16_t interface_index; + uint64_t association_id; + uint32_t reserved1; +} __attribute__ ((__packed__)); + +#define AVB_PACKET_ADP_SET_MESSAGE_TYPE(p,v) AVB_PACKET_SET_SUB1(&(p)->hdr, v) +#define AVB_PACKET_ADP_SET_VALID_TIME(p,v) AVB_PACKET_SET_SUB2(&(p)->hdr, v) + +#define AVB_PACKET_ADP_GET_MESSAGE_TYPE(p) AVB_PACKET_GET_SUB1(&(p)->hdr) +#define AVB_PACKET_ADP_GET_VALID_TIME(p) AVB_PACKET_GET_SUB2(&(p)->hdr) + +struct avb_adp *avb_adp_register(struct server *server); + +#endif /* AVB_ADP_H */
View file
pipewire-0.3.56.tar.gz/src/modules/module-avb/aecp-aem-descriptors.h
Added
@@ -0,0 +1,247 @@ +/* AVB support + * + * Copyright © 2022 Wim Taymans + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef AVB_AECP_AEM_DESCRIPTORS_H +#define AVB_AECP_AEM_DESCRIPTORS_H + +#include "internal.h" + +#define AVB_AEM_DESC_ENTITY 0x0000 +#define AVB_AEM_DESC_CONFIGURATION 0x0001 +#define AVB_AEM_DESC_AUDIO_UNIT 0x0002 +#define AVB_AEM_DESC_VIDEO_UNIT 0x0003 +#define AVB_AEM_DESC_SENSOR_UNIT 0x0004 +#define AVB_AEM_DESC_STREAM_INPUT 0x0005 +#define AVB_AEM_DESC_STREAM_OUTPUT 0x0006 +#define AVB_AEM_DESC_JACK_INPUT 0x0007 +#define AVB_AEM_DESC_JACK_OUTPUT 0x0008 +#define AVB_AEM_DESC_AVB_INTERFACE 0x0009 +#define AVB_AEM_DESC_CLOCK_SOURCE 0x000a +#define AVB_AEM_DESC_MEMORY_OBJECT 0x000b +#define AVB_AEM_DESC_LOCALE 0x000c +#define AVB_AEM_DESC_STRINGS 0x000d +#define AVB_AEM_DESC_STREAM_PORT_INPUT 0x000e +#define AVB_AEM_DESC_STREAM_PORT_OUTPUT 0x000f +#define AVB_AEM_DESC_EXTERNAL_PORT_INPUT 0x0010 +#define AVB_AEM_DESC_EXTERNAL_PORT_OUTPUT 0x0011 +#define AVB_AEM_DESC_INTERNAL_PORT_INPUT 0x0012 +#define AVB_AEM_DESC_INTERNAL_PORT_OUTPUT 0x0013 +#define AVB_AEM_DESC_AUDIO_CLUSTER 0x0014 +#define AVB_AEM_DESC_VIDEO_CLUSTER 0x0015 +#define AVB_AEM_DESC_SENSOR_CLUSTER 0x0016 +#define AVB_AEM_DESC_AUDIO_MAP 0x0017 +#define AVB_AEM_DESC_VIDEO_MAP 0x0018 +#define AVB_AEM_DESC_SENSOR_MAP 0x0019 +#define AVB_AEM_DESC_CONTROL 0x001a +#define AVB_AEM_DESC_SIGNAL_SELECTOR 0x001b +#define AVB_AEM_DESC_MIXER 0x001c +#define AVB_AEM_DESC_MATRIX 0x001d +#define AVB_AEM_DESC_MATRIX_SIGNAL 0x001e +#define AVB_AEM_DESC_SIGNAL_SPLITTER 0x001f +#define AVB_AEM_DESC_SIGNAL_COMBINER 0x0020 +#define AVB_AEM_DESC_SIGNAL_DEMULTIPLEXER 0x0021 +#define AVB_AEM_DESC_SIGNAL_MULTIPLEXER 0x0022 +#define AVB_AEM_DESC_SIGNAL_TRANSCODER 0x0023 +#define AVB_AEM_DESC_CLOCK_DOMAIN 0x0024 +#define AVB_AEM_DESC_CONTROL_BLOCK 0x0025 +#define AVB_AEM_DESC_INVALID 0xffff + +struct avb_aem_desc_entity { + uint64_t entity_id; + uint64_t entity_model_id; + uint32_t entity_capabilities; + uint16_t talker_stream_sources; + uint16_t talker_capabilities; + uint16_t listener_stream_sinks; + uint16_t listener_capabilities; + uint32_t controller_capabilities; + uint32_t available_index; + uint64_t association_id; + char entity_name64; + uint16_t vendor_name_string; + uint16_t model_name_string; + char firmware_version64; + char group_name64; + char serial_number64; + uint16_t configurations_count; + uint16_t current_configuration; +} __attribute__ ((__packed__)); + +struct avb_aem_desc_descriptor_count { + uint16_t descriptor_type; + uint16_t descriptor_count; +} __attribute__ ((__packed__)); + +struct avb_aem_desc_configuration { + char object_name64; + uint16_t localized_description; + uint16_t descriptor_counts_count; + uint16_t descriptor_counts_offset; + struct avb_aem_desc_descriptor_count descriptor_counts0; +} __attribute__ ((__packed__)); + +struct avb_aem_desc_sampling_rate { + uint32_t pull_frequency; +} __attribute__ ((__packed__)); + +struct avb_aem_desc_audio_unit { + char object_name64; + uint16_t localized_description; + uint16_t clock_domain_index; + uint16_t number_of_stream_input_ports; + uint16_t base_stream_input_port; + uint16_t number_of_stream_output_ports; + uint16_t base_stream_output_port; + uint16_t number_of_external_input_ports; + uint16_t base_external_input_port; + uint16_t number_of_external_output_ports; + uint16_t base_external_output_port; + uint16_t number_of_internal_input_ports; + uint16_t base_internal_input_port; + uint16_t number_of_internal_output_ports; + uint16_t base_internal_output_port; + uint16_t number_of_controls; + uint16_t base_control; + uint16_t number_of_signal_selectors; + uint16_t base_signal_selector; + uint16_t number_of_mixers; + uint16_t base_mixer; + uint16_t number_of_matrices; + uint16_t base_matrix; + uint16_t number_of_splitters; + uint16_t base_splitter; + uint16_t number_of_combiners; + uint16_t base_combiner; + uint16_t number_of_demultiplexers; + uint16_t base_demultiplexer; + uint16_t number_of_multiplexers; + uint16_t base_multiplexer; + uint16_t number_of_transcoders; + uint16_t base_transcoder; + uint16_t number_of_control_blocks; + uint16_t base_control_block; + uint32_t current_sampling_rate; + uint16_t sampling_rates_offset; + uint16_t sampling_rates_count; + struct avb_aem_desc_sampling_rate sampling_rates0; +} __attribute__ ((__packed__)); + +#define AVB_AEM_DESC_STREAM_FLAG_SYNC_SOURCE (1u<<0) +#define AVB_AEM_DESC_STREAM_FLAG_CLASS_A (1u<<1) +#define AVB_AEM_DESC_STREAM_FLAG_CLASS_B (1u<<2) +#define AVB_AEM_DESC_STREAM_FLAG_SUPPORTS_ENCRYPTED (1u<<3) +#define AVB_AEM_DESC_STREAM_FLAG_PRIMARY_BACKUP_SUPPORTED (1u<<4) +#define AVB_AEM_DESC_STREAM_FLAG_PRIMARY_BACKUP_VALID (1u<<5) +#define AVB_AEM_DESC_STREAM_FLAG_SECONDARY_BACKUP_SUPPORTED (1u<<6) +#define AVB_AEM_DESC_STREAM_FLAG_SECONDARY_BACKUP_VALID (1u<<7) +#define AVB_AEM_DESC_STREAM_FLAG_TERTIARY_BACKUP_SUPPORTED (1u<<8) +#define AVB_AEM_DESC_STREAM_FLAG_TERTIARY_BACKUP_VALID (1u<<9) + +struct avb_aem_desc_stream { + char object_name64; + uint16_t localized_description; + uint16_t clock_domain_index; + uint16_t stream_flags; + uint64_t current_format; + uint16_t formats_offset; + uint16_t number_of_formats; + uint64_t backup_talker_entity_id_0; + uint16_t backup_talker_unique_id_0; + uint64_t backup_talker_entity_id_1; + uint16_t backup_talker_unique_id_1; + uint64_t backup_talker_entity_id_2; + uint16_t backup_talker_unique_id_2; + uint64_t backedup_talker_entity_id; + uint16_t backedup_talker_unique; + uint16_t avb_interface_index; + uint32_t buffer_length; + uint64_t stream_formats0; +} __attribute__ ((__packed__)); + +#define AVB_AEM_DESC_AVB_INTERFACE_FLAG_GPTP_GRANDMASTER_SUPPORTED (1<<0) +#define AVB_AEM_DESC_AVB_INTERFACE_FLAG_GPTP_SUPPORTED (1<<1) +#define AVB_AEM_DESC_AVB_INTERFACE_FLAG_SRP_SUPPORTED (1<<2) + +struct avb_aem_desc_avb_interface { + char object_name64; + uint16_t localized_description; + uint8_t mac_address6; + uint16_t interface_flags; + uint64_t clock_identity; + uint8_t priority1; + uint8_t clock_class; + uint16_t offset_scaled_log_variance; + uint8_t clock_accuracy; + uint8_t priority2; + uint8_t domain_number; + int8_t log_sync_interval; + int8_t log_announce_interval;
View file
pipewire-0.3.56.tar.gz/src/modules/module-avb/aecp-aem.c
Added
@@ -0,0 +1,286 @@ +/* AVB support + * + * Copyright © 2022 Wim Taymans + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "aecp-aem.h" +#include "aecp-aem-descriptors.h" + +static int reply_status(struct aecp *aecp, int status, const void *m, int len) +{ + struct server *server = aecp->server; + uint8_t buflen; + struct avb_ethernet_header *h = (void*)buf; + struct avb_packet_aecp_header *reply = SPA_PTROFF(h, sizeof(*h), void); + + memcpy(buf, m, len); + AVB_PACKET_AECP_SET_MESSAGE_TYPE(reply, AVB_AECP_MESSAGE_TYPE_AEM_RESPONSE); + AVB_PACKET_AECP_SET_STATUS(reply, status); + + return avb_server_send_packet(server, h->src, AVB_TSN_ETH, buf, len); +} + +static int reply_not_implemented(struct aecp *aecp, const void *m, int len) +{ + return reply_status(aecp, AVB_AECP_AEM_STATUS_NOT_IMPLEMENTED, m, len); +} + +static int reply_success(struct aecp *aecp, const void *m, int len) +{ + return reply_status(aecp, AVB_AECP_AEM_STATUS_SUCCESS, m, len); +} + +/* ACQUIRE_ENTITY */ +static int handle_acquire_entity(struct aecp *aecp, const void *m, int len) +{ + struct server *server = aecp->server; + const struct avb_packet_aecp_aem *p = m; + const struct avb_packet_aecp_aem_acquire *ae; + const struct descriptor *desc; + uint16_t desc_type, desc_id; + + ae = (const struct avb_packet_aecp_aem_acquire*)p->payload; + + desc_type = ntohs(ae->descriptor_type); + desc_id = ntohs(ae->descriptor_id); + + desc = server_find_descriptor(server, desc_type, desc_id); + if (desc == NULL) + return reply_status(aecp, AVB_AECP_AEM_STATUS_NO_SUCH_DESCRIPTOR, p, len); + + if (desc_type != AVB_AEM_DESC_ENTITY || desc_id != 0) + return reply_not_implemented(aecp, m, len); + + return reply_success(aecp, m, len); +} + +/* LOCK_ENTITY */ +static int handle_lock_entity(struct aecp *aecp, const void *m, int len) +{ + struct server *server = aecp->server; + const struct avb_packet_aecp_aem *p = m; + const struct avb_packet_aecp_aem_acquire *ae; + const struct descriptor *desc; + uint16_t desc_type, desc_id; + + ae = (const struct avb_packet_aecp_aem_acquire*)p->payload; + + desc_type = ntohs(ae->descriptor_type); + desc_id = ntohs(ae->descriptor_id); + + desc = server_find_descriptor(server, desc_type, desc_id); + if (desc == NULL) + return reply_status(aecp, AVB_AECP_AEM_STATUS_NO_SUCH_DESCRIPTOR, p, len); + + if (desc_type != AVB_AEM_DESC_ENTITY || desc_id != 0) + return reply_not_implemented(aecp, m, len); + + return reply_success(aecp, m, len); +} + +/* READ_DESCRIPTOR */ +static int handle_read_descriptor(struct aecp *aecp, const void *m, int len) +{ + struct server *server = aecp->server; + const struct avb_ethernet_header *h = m; + const struct avb_packet_aecp_aem *p = SPA_PTROFF(h, sizeof(*h), void); + struct avb_packet_aecp_aem *reply; + const struct avb_packet_aecp_aem_read_descriptor *rd; + uint16_t desc_type, desc_id; + const struct descriptor *desc; + uint8_t buf2048; + size_t size, psize; + + rd = (struct avb_packet_aecp_aem_read_descriptor*)p->payload; + + desc_type = ntohs(rd->descriptor_type); + desc_id = ntohs(rd->descriptor_id); + + pw_log_info("descriptor type:%04x index:%d", desc_type, desc_id); + + desc = server_find_descriptor(server, desc_type, desc_id); + if (desc == NULL) + return reply_status(aecp, AVB_AECP_AEM_STATUS_NO_SUCH_DESCRIPTOR, m, len); + + memcpy(buf, m, len); + + psize = sizeof(*rd); + size = sizeof(*h) + sizeof(*reply) + psize; + + memcpy(buf + size, desc->ptr, desc->size); + size += desc->size; + psize += desc->size; + + h = (void*)buf; + reply = SPA_PTROFF(h, sizeof(*h), void); + AVB_PACKET_AECP_SET_MESSAGE_TYPE(&reply->aecp, AVB_AECP_MESSAGE_TYPE_AEM_RESPONSE); + AVB_PACKET_AECP_SET_STATUS(&reply->aecp, AVB_AECP_AEM_STATUS_SUCCESS); + AVB_PACKET_SET_LENGTH(&reply->aecp.hdr, psize + 12); + + return avb_server_send_packet(server, h->src, AVB_TSN_ETH, buf, size); +} + +/* GET_AVB_INFO */ +static int handle_get_avb_info(struct aecp *aecp, const void *m, int len) +{ + struct server *server = aecp->server; + const struct avb_ethernet_header *h = m; + const struct avb_packet_aecp_aem *p = SPA_PTROFF(h, sizeof(*h), void); + struct avb_packet_aecp_aem *reply; + struct avb_packet_aecp_aem_get_avb_info *i; + struct avb_aem_desc_avb_interface *avb_interface; + uint16_t desc_type, desc_id; + const struct descriptor *desc; + uint8_t buf2048; + size_t size, psize; + + i = (struct avb_packet_aecp_aem_get_avb_info*)p->payload; + + desc_type = ntohs(i->descriptor_type); + desc_id = ntohs(i->descriptor_id); + + desc = server_find_descriptor(server, desc_type, desc_id); + if (desc == NULL) + return reply_status(aecp, AVB_AECP_AEM_STATUS_NO_SUCH_DESCRIPTOR, m, len); + + if (desc_type != AVB_AEM_DESC_AVB_INTERFACE || desc_id != 0) + return reply_not_implemented(aecp, m, len); + + avb_interface = desc->ptr; + + memcpy(buf, m, len); + + psize = sizeof(*i); + size = sizeof(*h) + sizeof(*reply) + psize; + + h = (void*)buf; + reply = SPA_PTROFF(h, sizeof(*h), void); + AVB_PACKET_AECP_SET_MESSAGE_TYPE(&reply->aecp, AVB_AECP_MESSAGE_TYPE_AEM_RESPONSE); + AVB_PACKET_AECP_SET_STATUS(&reply->aecp, AVB_AECP_AEM_STATUS_SUCCESS); + AVB_PACKET_SET_LENGTH(&reply->aecp.hdr, psize + 12); + + i = (struct avb_packet_aecp_aem_get_avb_info*)reply->payload; + i->gptp_grandmaster_id = avb_interface->clock_identity; + i->propagation_delay = htonl(0); + i->gptp_domain_number = avb_interface->domain_number; + i->flags = 0; + i->msrp_mappings_count = htons(0); + + return avb_server_send_packet(server, h->src, AVB_TSN_ETH, buf, size); +} + +/* AEM_COMMAND */ +struct cmd_info { + uint16_t type; + const char *name; + int (*handle) (struct aecp *aecp, const void *p, int len); +}; + +static const struct cmd_info cmd_info = { + { AVB_AECP_AEM_CMD_ACQUIRE_ENTITY, "acquire-entity", handle_acquire_entity, },
View file
pipewire-0.3.56.tar.gz/src/modules/module-avb/aecp-aem.h
Added
@@ -0,0 +1,345 @@ +/* AVB support + * + * Copyright © 2022 Wim Taymans + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef AVB_AEM_H +#define AVB_AEM_H + +#include "aecp.h" + +#define AVB_AECP_AEM_STATUS_SUCCESS 0 +#define AVB_AECP_AEM_STATUS_NOT_IMPLEMENTED 1 +#define AVB_AECP_AEM_STATUS_NO_SUCH_DESCRIPTOR 2 +#define AVB_AECP_AEM_STATUS_ENTITY_LOCKED 3 +#define AVB_AECP_AEM_STATUS_ENTITY_ACQUIRED 4 +#define AVB_AECP_AEM_STATUS_NOT_AUTHENTICATED 5 +#define AVB_AECP_AEM_STATUS_AUTHENTICATION_DISABLED 6 +#define AVB_AECP_AEM_STATUS_BAD_ARGUMENTS 7 +#define AVB_AECP_AEM_STATUS_NO_RESOURCES 8 +#define AVB_AECP_AEM_STATUS_IN_PROGRESS 9 +#define AVB_AECP_AEM_STATUS_ENTITY_MISBEHAVING 10 +#define AVB_AECP_AEM_STATUS_NOT_SUPPORTED 11 +#define AVB_AECP_AEM_STATUS_STREAM_IS_RUNNING 12 + +#define AVB_AECP_AEM_CMD_ACQUIRE_ENTITY 0x0000 +#define AVB_AECP_AEM_CMD_LOCK_ENTITY 0x0001 +#define AVB_AECP_AEM_CMD_ENTITY_AVAILABLE 0x0002 +#define AVB_AECP_AEM_CMD_CONTROLLER_AVAILABLE 0x0003 +#define AVB_AECP_AEM_CMD_READ_DESCRIPTOR 0x0004 +#define AVB_AECP_AEM_CMD_WRITE_DESCRIPTOR 0x0005 +#define AVB_AECP_AEM_CMD_SET_CONFIGURATION 0x0006 +#define AVB_AECP_AEM_CMD_GET_CONFIGURATION 0x0007 +#define AVB_AECP_AEM_CMD_SET_STREAM_FORMAT 0x0008 +#define AVB_AECP_AEM_CMD_GET_STREAM_FORMAT 0x0009 +#define AVB_AECP_AEM_CMD_SET_VIDEO_FORMAT 0x000a +#define AVB_AECP_AEM_CMD_GET_VIDEO_FORMAT 0x000b +#define AVB_AECP_AEM_CMD_SET_SENSOR_FORMAT 0x000c +#define AVB_AECP_AEM_CMD_GET_SENSOR_FORMAT 0x000d +#define AVB_AECP_AEM_CMD_SET_STREAM_INFO 0x000e +#define AVB_AECP_AEM_CMD_GET_STREAM_INFO 0x000f +#define AVB_AECP_AEM_CMD_SET_NAME 0x0010 +#define AVB_AECP_AEM_CMD_GET_NAME 0x0011 +#define AVB_AECP_AEM_CMD_SET_ASSOCIATION_ID 0x0012 +#define AVB_AECP_AEM_CMD_GET_ASSOCIATION_ID 0x0013 +#define AVB_AECP_AEM_CMD_SET_SAMPLING_RATE 0x0014 +#define AVB_AECP_AEM_CMD_GET_SAMPLING_RATE 0x0015 +#define AVB_AECP_AEM_CMD_SET_CLOCK_SOURCE 0x0016 +#define AVB_AECP_AEM_CMD_GET_CLOCK_SOURCE 0x0017 +#define AVB_AECP_AEM_CMD_SET_CONTROL 0x0018 +#define AVB_AECP_AEM_CMD_GET_CONTROL 0x0019 +#define AVB_AECP_AEM_CMD_INCREMENT_CONTROL 0x001a +#define AVB_AECP_AEM_CMD_DECREMENT_CONTROL 0x001b +#define AVB_AECP_AEM_CMD_SET_SIGNAL_SELECTOR 0x001c +#define AVB_AECP_AEM_CMD_GET_SIGNAL_SELECTOR 0x001d +#define AVB_AECP_AEM_CMD_SET_MIXER 0x001e +#define AVB_AECP_AEM_CMD_GET_MIXER 0x001f +#define AVB_AECP_AEM_CMD_SET_MATRIX 0x0020 +#define AVB_AECP_AEM_CMD_GET_MATRIX 0x0021 +#define AVB_AECP_AEM_CMD_START_STREAMING 0x0022 +#define AVB_AECP_AEM_CMD_STOP_STREAMING 0x0023 +#define AVB_AECP_AEM_CMD_REGISTER_UNSOLICITED_NOTIFICATION 0x0024 +#define AVB_AECP_AEM_CMD_DEREGISTER_UNSOLICITED_NOTIFICATION 0x0025 +#define AVB_AECP_AEM_CMD_IDENTIFY_NOTIFICATION 0x0026 +#define AVB_AECP_AEM_CMD_GET_AVB_INFO 0x0027 +#define AVB_AECP_AEM_CMD_GET_AS_PATH 0x0028 +#define AVB_AECP_AEM_CMD_GET_COUNTERS 0x0029 +#define AVB_AECP_AEM_CMD_REBOOT 0x002a +#define AVB_AECP_AEM_CMD_GET_AUDIO_MAP 0x002b +#define AVB_AECP_AEM_CMD_ADD_AUDIO_MAPPINGS 0x002c +#define AVB_AECP_AEM_CMD_REMOVE_AUDIO_MAPPINGS 0x002d +#define AVB_AECP_AEM_CMD_GET_VIDEO_MAP 0x002e +#define AVB_AECP_AEM_CMD_ADD_VIDEO_MAPPINGS 0x002f +#define AVB_AECP_AEM_CMD_REMOVE_VIDEO_MAPPINGS 0x0030 +#define AVB_AECP_AEM_CMD_GET_SENSOR_MAP 0x0031 +#define AVB_AECP_AEM_CMD_ADD_SENSOR_MAPPINGS 0x0032 +#define AVB_AECP_AEM_CMD_REMOVE_SENSOR_MAPPINGS 0x0033 +#define AVB_AECP_AEM_CMD_START_OPERATION 0x0034 +#define AVB_AECP_AEM_CMD_ABORT_OPERATION 0x0035 +#define AVB_AECP_AEM_CMD_OPERATION_STATUS 0x0036 +#define AVB_AECP_AEM_CMD_AUTH_ADD_KEY 0x0037 +#define AVB_AECP_AEM_CMD_AUTH_DELETE_KEY 0x0038 +#define AVB_AECP_AEM_CMD_AUTH_GET_KEY_LIST 0x0039 +#define AVB_AECP_AEM_CMD_AUTH_GET_KEY 0x003a +#define AVB_AECP_AEM_CMD_AUTH_ADD_KEY_TO_CHAIN 0x003b +#define AVB_AECP_AEM_CMD_AUTH_DELETE_KEY_FROM_CHAIN 0x003c +#define AVB_AECP_AEM_CMD_AUTH_GET_KEYCHAIN_LIST 0x003d +#define AVB_AECP_AEM_CMD_AUTH_GET_IDENTITY 0x003e +#define AVB_AECP_AEM_CMD_AUTH_ADD_TOKEN 0x003f +#define AVB_AECP_AEM_CMD_AUTH_DELETE_TOKEN 0x0040 +#define AVB_AECP_AEM_CMD_AUTHENTICATE 0x0041 +#define AVB_AECP_AEM_CMD_DEAUTHENTICATE 0x0042 +#define AVB_AECP_AEM_CMD_ENABLE_TRANSPORT_SECURITY 0x0043 +#define AVB_AECP_AEM_CMD_DISABLE_TRANSPORT_SECURITY 0x0044 +#define AVB_AECP_AEM_CMD_ENABLE_STREAM_ENCRYPTION 0x0045 +#define AVB_AECP_AEM_CMD_DISABLE_STREAM_ENCRYPTION 0x0046 +#define AVB_AECP_AEM_CMD_SET_MEMORY_OBJECT_LENGTH 0x0047 +#define AVB_AECP_AEM_CMD_GET_MEMORY_OBJECT_LENGTH 0x0048 +#define AVB_AECP_AEM_CMD_SET_STREAM_BACKUP 0x0049 +#define AVB_AECP_AEM_CMD_GET_STREAM_BACKUP 0x004a +#define AVB_AECP_AEM_CMD_EXPANSION 0x7fff + +#define AVB_AEM_ACQUIRE_ENTITY_PERSISTENT_FLAG (1<<0) + +struct avb_packet_aecp_aem_acquire { + uint32_t flags; + uint64_t owner_guid; + uint16_t descriptor_type; + uint16_t descriptor_id; +} __attribute__ ((__packed__)); + +struct avb_packet_aecp_aem_lock { + uint32_t flags; + uint64_t locked_guid; + uint16_t descriptor_type; + uint16_t descriptor_id; +} __attribute__ ((__packed__)); + +struct avb_packet_aecp_aem_read_descriptor { + uint16_t configuration; + uint8_t reserved2; + uint16_t descriptor_type; + uint16_t descriptor_id; +} __attribute__ ((__packed__)); + +struct avb_packet_aecp_aem_setget_configuration { + uint16_t reserved; + uint16_t configuration_index; +} __attribute__ ((__packed__)); + +struct avb_packet_aecp_aem_setget_stream_format { + uint16_t descriptor_type; + uint16_t descriptor_id; + uint64_t stream_format; +} __attribute__ ((__packed__)); + +struct avb_packet_aecp_aem_setget_video_format { + uint16_t descriptor_type; + uint16_t descriptor_id; + uint32_t format_specific; + uint16_t aspect_ratio; + uint16_t color_space; + uint32_t frame_size; +} __attribute__ ((__packed__)); + +struct avb_packet_aecp_aem_setget_sensor_format { + uint16_t descriptor_type; + uint16_t descriptor_id; + uint64_t sensor_format; +} __attribute__ ((__packed__)); + + +#define AVB_AEM_STREAM_INFO_FLAG_CLASS_B (1u<<0) +#define AVB_AEM_STREAM_INFO_FLAG_FAST_CONNECT (1u<<1) +#define AVB_AEM_STREAM_INFO_FLAG_SAVED_STATE (1u<<2) +#define AVB_AEM_STREAM_INFO_FLAG_STREAMING_WAIT (1u<<3) +#define AVB_AEM_STREAM_INFO_FLAG_ENCRYPTED_PDU (1u<<4) +#define AVB_AEM_STREAM_INFO_FLAG_STREAM_VLAN_ID_VALID (1u<<25) +#define AVB_AEM_STREAM_INFO_FLAG_CONNECTED (1u<<26) +#define AVB_AEM_STREAM_INFO_FLAG_MSRP_FAILURE_VALID (1u<<27) +#define AVB_AEM_STREAM_INFO_FLAG_STREAM_DEST_MAC_VALID (1u<<28) +#define AVB_AEM_STREAM_INFO_FLAG_MSRP_ACC_LAT_VALID (1u<<29) +#define AVB_AEM_STREAM_INFO_FLAG_STREAM_ID_VALID (1u<<30) +#define AVB_AEM_STREAM_INFO_FLAG_STREAM_FORMAT_VALID (1u<<31) + +struct avb_packet_aecp_aem_setget_stream_info { + uint16_t descriptor_type; + uint16_t descriptor_index; + uint32_t aem_stream_info_flags; + uint64_t stream_format; + uint64_t stream_id; + uint32_t msrp_accumulated_latency; + uint8_t stream_dest_mac6; + uint8_t msrp_failure_code; + uint8_t reserved; + uint64_t msrp_failure_bridge_id; + uint16_t stream_vlan_id; + uint16_t reserved2; +} __attribute__ ((__packed__)); + +struct avb_packet_aecp_aem_setget_name {
View file
pipewire-0.3.56.tar.gz/src/modules/module-avb/aecp.c
Added
@@ -0,0 +1,169 @@ +/* AVB support + * + * Copyright © 2022 Wim Taymans + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include <spa/utils/json.h> +#include <spa/debug/mem.h> + +#include <pipewire/pipewire.h> + +#include "aecp.h" +#include "aecp-aem.h" +#include "internal.h" + +static const uint8_t mac6 = AVB_BROADCAST_MAC; + +struct msg_info { + uint16_t type; + const char *name; + int (*handle) (struct aecp *aecp, const void *p, int len); +}; + +static int reply_not_implemented(struct aecp *aecp, const void *p, int len) +{ + struct server *server = aecp->server; + uint8_t buflen; + struct avb_ethernet_header *h = (void*)buf; + struct avb_packet_aecp_header *reply = SPA_PTROFF(h, sizeof(*h), void); + + memcpy(h, p, len); + AVB_PACKET_AECP_SET_STATUS(reply, AVB_AECP_STATUS_NOT_IMPLEMENTED); + + return avb_server_send_packet(server, h->src, AVB_TSN_ETH, buf, len); +} + +static const struct msg_info msg_info = { + { AVB_AECP_MESSAGE_TYPE_AEM_COMMAND, "aem-command", avb_aecp_aem_handle_command, }, + { AVB_AECP_MESSAGE_TYPE_AEM_RESPONSE, "aem-response", avb_aecp_aem_handle_response, }, + { AVB_AECP_MESSAGE_TYPE_ADDRESS_ACCESS_COMMAND, "address-access-command", NULL, }, + { AVB_AECP_MESSAGE_TYPE_ADDRESS_ACCESS_RESPONSE, "address-access-response", NULL, }, + { AVB_AECP_MESSAGE_TYPE_AVC_COMMAND, "avc-command", NULL, }, + { AVB_AECP_MESSAGE_TYPE_AVC_RESPONSE, "avc-response", NULL, }, + { AVB_AECP_MESSAGE_TYPE_VENDOR_UNIQUE_COMMAND, "vendor-unique-command", NULL, }, + { AVB_AECP_MESSAGE_TYPE_VENDOR_UNIQUE_RESPONSE, "vendor-unique-response", NULL, }, + { AVB_AECP_MESSAGE_TYPE_EXTENDED_COMMAND, "extended-command", NULL, }, + { AVB_AECP_MESSAGE_TYPE_EXTENDED_RESPONSE, "extended-response", NULL, }, +}; + +static inline const struct msg_info *find_msg_info(uint16_t type, const char *name) +{ + uint32_t i; + for (i = 0; i < SPA_N_ELEMENTS(msg_info); i++) { + if ((name == NULL && type == msg_infoi.type) || + (name != NULL && spa_streq(name, msg_infoi.name))) + return &msg_infoi; + } + return NULL; +} + +static int aecp_message(void *data, uint64_t now, const void *message, int len) +{ + struct aecp *aecp = data; + struct server *server = aecp->server; + const struct avb_ethernet_header *h = message; + const struct avb_packet_aecp_header *p = SPA_PTROFF(h, sizeof(*h), void); + const struct msg_info *info; + int message_type; + + if (ntohs(h->type) != AVB_TSN_ETH) + return 0; + if (memcmp(h->dest, mac, 6) != 0 && + memcmp(h->dest, server->mac_addr, 6) != 0) + return 0; + if (AVB_PACKET_GET_SUBTYPE(&p->hdr) != AVB_SUBTYPE_AECP) + return 0; + + message_type = AVB_PACKET_AECP_GET_MESSAGE_TYPE(p); + + info = find_msg_info(message_type, NULL); + if (info == NULL) + return reply_not_implemented(aecp, message, len); + + pw_log_debug("got AECP message %s", info->name); + + if (info->handle == NULL) + return reply_not_implemented(aecp, message, len); + + return info->handle(aecp, message, len); +} + +static void aecp_destroy(void *data) +{ + struct aecp *aecp = data; + spa_hook_remove(&aecp->server_listener); + free(aecp); +} + +static int do_help(struct aecp *aecp, const char *args, FILE *out) +{ + fprintf(out, "{ \"type\": \"help\"," + "\"text\": \"" + "/adp/help: this help \\n" + "\" }"); + return 0; +} + +static int aecp_command(void *data, uint64_t now, const char *command, const char *args, FILE *out) +{ + struct aecp *aecp = data; + int res; + + if (!spa_strstartswith(command, "/aecp/")) + return 0; + + command += strlen("/aecp/"); + + if (spa_streq(command, "help")) + res = do_help(aecp, args, out); + else + res = -ENOTSUP; + + return res; +} + +static const struct server_events server_events = { + AVB_VERSION_SERVER_EVENTS, + .destroy = aecp_destroy, + .message = aecp_message, + .command = aecp_command +}; + +struct avb_aecp *avb_aecp_register(struct server *server) +{ + struct aecp *aecp; + + aecp = calloc(1, sizeof(*aecp)); + if (aecp == NULL) + return NULL; + + aecp->server = server; + + avdecc_server_add_listener(server, &aecp->server_listener, &server_events, aecp); + + return (struct avb_aecp*)aecp; +} + +void avb_aecp_unregister(struct avb_aecp *aecp) +{ + aecp_destroy(aecp); +}
View file
pipewire-0.3.56.tar.gz/src/modules/module-avb/aecp.h
Added
@@ -0,0 +1,60 @@ +/* AVB support + * + * Copyright © 2022 Wim Taymans + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef AVB_AECP_H +#define AVB_AECP_H + +#include "packets.h" +#include "internal.h" + +#define AVB_AECP_MESSAGE_TYPE_AEM_COMMAND 0 +#define AVB_AECP_MESSAGE_TYPE_AEM_RESPONSE 1 +#define AVB_AECP_MESSAGE_TYPE_ADDRESS_ACCESS_COMMAND 2 +#define AVB_AECP_MESSAGE_TYPE_ADDRESS_ACCESS_RESPONSE 3 +#define AVB_AECP_MESSAGE_TYPE_AVC_COMMAND 4 +#define AVB_AECP_MESSAGE_TYPE_AVC_RESPONSE 5 +#define AVB_AECP_MESSAGE_TYPE_VENDOR_UNIQUE_COMMAND 6 +#define AVB_AECP_MESSAGE_TYPE_VENDOR_UNIQUE_RESPONSE 7 +#define AVB_AECP_MESSAGE_TYPE_EXTENDED_COMMAND 14 +#define AVB_AECP_MESSAGE_TYPE_EXTENDED_RESPONSE 15 + +#define AVB_AECP_STATUS_SUCCESS 0 +#define AVB_AECP_STATUS_NOT_IMPLEMENTED 1 + +struct avb_packet_aecp_header { + struct avb_packet_header hdr; + uint64_t target_guid; + uint64_t controller_guid; + uint16_t sequence_id; +} __attribute__ ((__packed__)); + +#define AVB_PACKET_AECP_SET_MESSAGE_TYPE(p,v) AVB_PACKET_SET_SUB1(&(p)->hdr, v) +#define AVB_PACKET_AECP_SET_STATUS(p,v) AVB_PACKET_SET_SUB2(&(p)->hdr, v) + +#define AVB_PACKET_AECP_GET_MESSAGE_TYPE(p) AVB_PACKET_GET_SUB1(&(p)->hdr) +#define AVB_PACKET_AECP_GET_STATUS(p) AVB_PACKET_GET_SUB2(&(p)->hdr) + +struct avb_aecp *avb_aecp_register(struct server *server); + +#endif /* AVB_AECP_H */
View file
pipewire-0.3.56.tar.gz/src/modules/module-avb/avb.c
Added
@@ -0,0 +1,108 @@ +/* PipeWire + * + * Copyright © 2022 Wim Taymans + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "internal.h" + +#include <spa/support/cpu.h> + +struct pw_avb *pw_avb_new(struct pw_context *context, + struct pw_properties *props, size_t user_data_size) +{ + struct impl *impl; + const struct spa_support *support; + uint32_t n_support; + struct spa_cpu *cpu; + const char *str; + int res = 0; + + impl = calloc(1, sizeof(*impl) + user_data_size); + if (impl == NULL) + goto error_exit; + + if (props == NULL) + props = pw_properties_new(NULL, NULL); + if (props == NULL) + goto error_free; + + support = pw_context_get_support(context, &n_support); + cpu = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_CPU); + + pw_context_conf_update_props(context, "avb.properties", props); + + if ((str = pw_properties_get(props, "vm.overrides")) != NULL) { + if (cpu != NULL && spa_cpu_get_vm_type(cpu) != SPA_CPU_VM_NONE) + pw_properties_update_string(props, str, strlen(str)); + pw_properties_set(props, "vm.overrides", NULL); + } + + impl->context = context; + impl->loop = pw_context_get_main_loop(context); + impl->props = props; + impl->core = pw_context_get_object(context, PW_TYPE_INTERFACE_Core); + if (impl->core == NULL) { + str = pw_properties_get(props, PW_KEY_REMOTE_NAME); + impl->core = pw_context_connect(context, + pw_properties_new( + PW_KEY_REMOTE_NAME, str, + NULL), + 0); + impl->do_disconnect = true; + } + if (impl->core == NULL) { + res = -errno; + pw_log_error("can't connect: %m"); + goto error_free; + } + + impl->work_queue = pw_context_get_work_queue(context); + + spa_list_init(&impl->servers); + + avdecc_server_new(impl, &props->dict); + + return (struct pw_avb*)impl; + +error_free: + free(impl); +error_exit: + pw_properties_free(props); + if (res < 0) + errno = -res; + return NULL; +} + +static void impl_free(struct impl *impl) +{ + struct server *s; + + spa_list_consume(s, &impl->servers, link) + avdecc_server_free(s); + free(impl); +} + +void pw_avb_destroy(struct pw_avb *avb) +{ + struct impl *impl = (struct impl*)avb; + impl_free(impl); +}
View file
pipewire-0.3.56.tar.gz/src/modules/module-avb/avb.h
Added
@@ -0,0 +1,44 @@ +/* PipeWire + * + * Copyright © 2022 Wim Taymans + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef PIPEWIRE_AVB_H +#define PIPEWIRE_AVB_H + +#ifdef __cplusplus +extern "C" { +#endif + +struct pw_context; +struct pw_properties; +struct pw_avb; + +struct pw_avb *pw_avb_new(struct pw_context *context, + struct pw_properties *props, size_t user_data_size); +void pw_avb_destroy(struct pw_avb *avb); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* PIPEWIRE_AVB_H */
View file
pipewire-0.3.56.tar.gz/src/modules/module-avb/avdecc.c
Added
@@ -0,0 +1,335 @@ +/* PipeWire + * + * Copyright © 2022 Wim Taymans + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include <linux/if_ether.h> +#include <linux/if_packet.h> +#include <linux/filter.h> +#include <linux/net_tstamp.h> +#include <limits.h> +#include <net/if.h> +#include <arpa/inet.h> +#include <sys/ioctl.h> +#include <unistd.h> + +#include <spa/support/cpu.h> +#include <spa/debug/mem.h> + +#include <pipewire/pipewire.h> + +#include "avb.h" +#include "packets.h" +#include "internal.h" +#include "stream.h" +#include "acmp.h" +#include "adp.h" +#include "aecp.h" +#include "maap.h" +#include "mmrp.h" +#include "msrp.h" +#include "mvrp.h" +#include "descriptors.h" +#include "utils.h" + +#define DEFAULT_INTERVAL 1 + +#define server_emit(s,m,v,...) spa_hook_list_call(&s->listener_list, struct server_events, m, v, ##__VA_ARGS__) +#define server_emit_destroy(s) server_emit(s, destroy, 0) +#define server_emit_message(s,n,m,l) server_emit(s, message, 0, n, m, l) +#define server_emit_periodic(s,n) server_emit(s, periodic, 0, n) +#define server_emit_command(s,n,c,a,f) server_emit(s, command, 0, n, c, a, f) + +static void on_timer_event(void *data, uint64_t expirations) +{ + struct server *server = data; + struct timespec now; + clock_gettime(CLOCK_REALTIME, &now); + server_emit_periodic(server, SPA_TIMESPEC_TO_NSEC(&now)); +} + +static void on_socket_data(void *data, int fd, uint32_t mask) +{ + struct server *server = data; + struct timespec now; + + if (mask & SPA_IO_IN) { + int len; + uint8_t buffer2048; + + len = recv(fd, buffer, sizeof(buffer), 0); + + if (len < 0) { + pw_log_warn("got recv error: %m"); + } + else if (len < (int)sizeof(struct avb_packet_header)) { + pw_log_warn("short packet received (%d < %d)", len, + (int)sizeof(struct avb_packet_header)); + } else { + clock_gettime(CLOCK_REALTIME, &now); + server_emit_message(server, SPA_TIMESPEC_TO_NSEC(&now), buffer, len); + } + } +} + +int avb_server_send_packet(struct server *server, const uint8_t dest6, + uint16_t type, void *data, size_t size) +{ + struct avb_ethernet_header *hdr = (struct avb_ethernet_header*)data; + int res = 0; + + memcpy(hdr->dest, dest, ETH_ALEN); + memcpy(hdr->src, server->mac_addr, ETH_ALEN); + hdr->type = htons(type); + + if (send(server->source->fd, data, size, 0) < 0) { + res = -errno; + pw_log_warn("got send error: %m"); + } + return res; +} + +static int load_filter(int fd, uint16_t eth, const uint8_t dest6, const uint8_t mac6) +{ + struct sock_fprog filter; + struct sock_filter bpf_code = { + BPF_STMT(BPF_LD|BPF_H|BPF_ABS, 12), + BPF_JUMP(BPF_JMP|BPF_JEQ, eth, 0, 8), + BPF_STMT(BPF_LD|BPF_W|BPF_ABS, 2), + BPF_JUMP(BPF_JMP|BPF_JEQ, (dest2 << 24) | + (dest3 << 16) | + (dest4 << 8) | + (dest5), 0, 2), + BPF_STMT(BPF_LD|BPF_H|BPF_ABS, 0), + BPF_JUMP(BPF_JMP|BPF_JEQ, (dest0 << 8) | + (dest1), 3, 4), + BPF_JUMP(BPF_JMP|BPF_JEQ, (mac2 << 24) | + (mac3 << 16) | + (mac4 << 8) | + (mac5), 0, 3), + BPF_STMT(BPF_LD|BPF_H|BPF_ABS, 0), + BPF_JUMP(BPF_JMP|BPF_JEQ, (mac0 << 8) | + (mac1), 0, 1), + BPF_STMT(BPF_RET, 0x00040000), + BPF_STMT(BPF_RET, 0x00000000), + }; + filter.len = sizeof(bpf_code) / 8; + filter.filter = bpf_code; + + if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, + &filter, sizeof(filter)) < 0) { + pw_log_error("setsockopt(ATTACH_FILTER) failed: %m"); + return -errno; + } + return 0; +} + +int avb_server_make_socket(struct server *server, uint16_t type, const uint8_t mac6) +{ + int fd, res; + struct ifreq req; + struct packet_mreq mreq; + struct sockaddr_ll sll; + + fd = socket(AF_PACKET, SOCK_RAW|SOCK_NONBLOCK, htons(ETH_P_ALL)); + if (fd < 0) { + pw_log_error("socket() failed: %m"); + return -errno; + } + + spa_zero(req); + snprintf(req.ifr_name, sizeof(req.ifr_name), "%s", server->ifname); + if (ioctl(fd, SIOCGIFINDEX, &req) < 0) { + res = -errno; + pw_log_error("SIOCGIFINDEX %s failed: %m", server->ifname); + goto error_close; + } + server->ifindex = req.ifr_ifindex; + + spa_zero(req); + snprintf(req.ifr_name, sizeof(req.ifr_name), "%s", server->ifname); + if (ioctl(fd, SIOCGIFHWADDR, &req) < 0) { + res = -errno; + pw_log_error("SIOCGIFHWADDR %s failed: %m", server->ifname); + goto error_close; + } + memcpy(server->mac_addr, req.ifr_hwaddr.sa_data, sizeof(server->mac_addr)); + + server->entity_id = (uint64_t)server->mac_addr0 << 56 | + (uint64_t)server->mac_addr1 << 48 | + (uint64_t)server->mac_addr2 << 40 | + (uint64_t)0xff << 32 | + (uint64_t)0xfe << 24 | + (uint64_t)server->mac_addr3 << 16 | + (uint64_t)server->mac_addr4 << 8 | + (uint64_t)server->mac_addr5; + + spa_zero(sll); + sll.sll_family = AF_PACKET; + sll.sll_protocol = htons(ETH_P_ALL); + sll.sll_ifindex = server->ifindex; + if (bind(fd, (struct sockaddr *) &sll, sizeof(sll)) < 0) { + res = -errno; + pw_log_error("bind() failed: %m"); + goto error_close; + } + + spa_zero(mreq); + mreq.mr_ifindex = server->ifindex; + mreq.mr_type = PACKET_MR_MULTICAST; + mreq.mr_alen = ETH_ALEN;
View file
pipewire-0.3.56.tar.gz/src/modules/module-avb/descriptors.h
Added
@@ -0,0 +1,274 @@ +/* PipeWire + * + * Copyright © 2022 Wim Taymans + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "aecp-aem.h" +#include "aecp-aem-descriptors.h" +#include "internal.h" + +void init_descriptors(struct server *server) +{ + server_add_descriptor(server, AVB_AEM_DESC_STRINGS, 0, + sizeof(struct avb_aem_desc_strings), + &(struct avb_aem_desc_strings) + { + .string_0 = "PipeWire", + .string_1 = "Configuration 1", + .string_2 = "Wim Taymans", + }); + server_add_descriptor(server, AVB_AEM_DESC_LOCALE, 0, + sizeof(struct avb_aem_desc_locale), + &(struct avb_aem_desc_locale) + { + .locale_identifier = "en-EN", + .number_of_strings = htons(1), + .base_strings = htons(0) + }); + server_add_descriptor(server, AVB_AEM_DESC_ENTITY, 0, + sizeof(struct avb_aem_desc_entity), + &(struct avb_aem_desc_entity) + { + .entity_id = htobe64(server->entity_id), + .entity_model_id = htobe64(0), + .entity_capabilities = htonl( + AVB_ADP_ENTITY_CAPABILITY_AEM_SUPPORTED | + AVB_ADP_ENTITY_CAPABILITY_CLASS_A_SUPPORTED | + AVB_ADP_ENTITY_CAPABILITY_GPTP_SUPPORTED | + AVB_ADP_ENTITY_CAPABILITY_AEM_IDENTIFY_CONTROL_INDEX_VALID | + AVB_ADP_ENTITY_CAPABILITY_AEM_INTERFACE_INDEX_VALID), + + .talker_stream_sources = htons(8), + .talker_capabilities = htons( + AVB_ADP_TALKER_CAPABILITY_IMPLEMENTED | + AVB_ADP_TALKER_CAPABILITY_AUDIO_SOURCE), + .listener_stream_sinks = htons(8), + .listener_capabilities = htons( + AVB_ADP_LISTENER_CAPABILITY_IMPLEMENTED | + AVB_ADP_LISTENER_CAPABILITY_AUDIO_SINK), + .controller_capabilities = htons(0), + .available_index = htonl(0), + .association_id = htobe64(0), + .entity_name = "PipeWire", + .vendor_name_string = htons(2), + .model_name_string = htons(0), + .firmware_version = "0.3.48", + .group_name = "", + .serial_number = "", + .configurations_count = htons(1), + .current_configuration = htons(0) + }); + struct { + struct avb_aem_desc_configuration desc; + struct avb_aem_desc_descriptor_count descriptor_counts8; + } __attribute__ ((__packed__)) config = + { + { + .object_name = "Configuration 1", + .localized_description = htons(1), + .descriptor_counts_count = htons(8), + .descriptor_counts_offset = htons( + 4 + sizeof(struct avb_aem_desc_configuration)), + }, + .descriptor_counts = { + { htons(AVB_AEM_DESC_AUDIO_UNIT), htons(1) }, + { htons(AVB_AEM_DESC_STREAM_INPUT), htons(1) }, + { htons(AVB_AEM_DESC_STREAM_OUTPUT), htons(1) }, + { htons(AVB_AEM_DESC_AVB_INTERFACE), htons(1) }, + { htons(AVB_AEM_DESC_CLOCK_SOURCE), htons(1) }, + { htons(AVB_AEM_DESC_CONTROL), htons(2) }, + { htons(AVB_AEM_DESC_LOCALE), htons(1) }, + { htons(AVB_AEM_DESC_CLOCK_DOMAIN), htons(1) } + } + }; + server_add_descriptor(server, AVB_AEM_DESC_CONFIGURATION, 0, + sizeof(config), &config); + + struct { + struct avb_aem_desc_audio_unit desc; + struct avb_aem_desc_sampling_rate sampling_rates6; + } __attribute__ ((__packed__)) audio_unit = + { + { + .object_name = "PipeWire", + .localized_description = htons(0), + .clock_domain_index = htons(0), + .number_of_stream_input_ports = htons(1), + .base_stream_input_port = htons(0), + .number_of_stream_output_ports = htons(1), + .base_stream_output_port = htons(0), + .number_of_external_input_ports = htons(8), + .base_external_input_port = htons(0), + .number_of_external_output_ports = htons(8), + .base_external_output_port = htons(0), + .number_of_internal_input_ports = htons(0), + .base_internal_input_port = htons(0), + .number_of_internal_output_ports = htons(0), + .base_internal_output_port = htons(0), + .number_of_controls = htons(0), + .base_control = htons(0), + .number_of_signal_selectors = htons(0), + .base_signal_selector = htons(0), + .number_of_mixers = htons(0), + .base_mixer = htons(0), + .number_of_matrices = htons(0), + .base_matrix = htons(0), + .number_of_splitters = htons(0), + .base_splitter = htons(0), + .number_of_combiners = htons(0), + .base_combiner = htons(0), + .number_of_demultiplexers = htons(0), + .base_demultiplexer = htons(0), + .number_of_multiplexers = htons(0), + .base_multiplexer = htons(0), + .number_of_transcoders = htons(0), + .base_transcoder = htons(0), + .number_of_control_blocks = htons(0), + .base_control_block = htons(0), + .current_sampling_rate = htonl(48000), + .sampling_rates_offset = htons( + 4 + sizeof(struct avb_aem_desc_audio_unit)), + .sampling_rates_count = htons(6), + }, + .sampling_rates = { + { .pull_frequency = htonl(44100) }, + { .pull_frequency = htonl(48000) }, + { .pull_frequency = htonl(88200) }, + { .pull_frequency = htonl(96000) }, + { .pull_frequency = htonl(176400) }, + { .pull_frequency = htonl(192000) }, + } + }; + server_add_descriptor(server, AVB_AEM_DESC_AUDIO_UNIT, 0, + sizeof(audio_unit), &audio_unit); + + struct { + struct avb_aem_desc_stream desc; + uint64_t stream_formats6; + } __attribute__ ((__packed__)) stream_input_0 = + { + { + .object_name = "Stream Input 1", + .localized_description = htons(0xffff), + .clock_domain_index = htons(0), + .stream_flags = htons( + AVB_AEM_DESC_STREAM_FLAG_SYNC_SOURCE | + AVB_AEM_DESC_STREAM_FLAG_CLASS_A), + .current_format = htobe64(0x00a0020840000800ULL), + .formats_offset = htons( + 4 + sizeof(struct avb_aem_desc_stream)), + .number_of_formats = htons(6), + .backup_talker_entity_id_0 = htobe64(0), + .backup_talker_unique_id_0 = htons(0), + .backup_talker_entity_id_1 = htobe64(0), + .backup_talker_unique_id_1 = htons(0), + .backup_talker_entity_id_2 = htobe64(0), + .backup_talker_unique_id_2 = htons(0), + .backedup_talker_entity_id = htobe64(0), + .backedup_talker_unique = htons(0), + .avb_interface_index = htons(0), + .buffer_length = htons(8) + }, + .stream_formats = { + htobe64(0x00a0010860000800ULL), + htobe64(0x00a0020860000800ULL), + htobe64(0x00a0030860000800ULL), + htobe64(0x00a0040860000800ULL), + htobe64(0x00a0050860000800ULL), + htobe64(0x00a0060860000800ULL), + }, + };
View file
pipewire-0.3.56.tar.gz/src/modules/module-avb/iec61883.h
Added
@@ -0,0 +1,110 @@ +/* AVB support + * + * Copyright © 2022 Wim Taymans + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef AVB_IEC61883_H +#define AVB_IEC61883_H + +#include "packets.h" + +struct avb_packet_iec61883 { + uint8_t subtype; +#if __BYTE_ORDER == __BIG_ENDIAN + unsigned sv:1; + unsigned version:3; + unsigned mr:1; + unsigned _r1:1; + unsigned gv:1; + unsigned tv:1; + + uint8_t seq_number; + + unsigned _r2:7; + unsigned tu:1; +#elif __BYTE_ORDER == __LITTLE_ENDIAN + unsigned tv:1; + unsigned gv:1; + unsigned _r1:1; + unsigned mr:1; + unsigned version:3; + unsigned sv:1; + + uint8_t seq_num; + + unsigned tu:1; + unsigned _r2:7; +#endif + uint64_t stream_id; + uint32_t timestamp; + uint32_t gateway_info; + uint16_t data_len; +#if __BYTE_ORDER == __BIG_ENDIAN + uint8_t tag:2; + uint8_t channel:6; + + uint8_t tcode:4; + uint8_t app:4; + + uint8_t qi1:2; /* CIP Quadlet Indicator 1 */ + uint8_t sid:6; /* CIP Source ID */ + + uint8_t dbs; /* CIP Data Block Size */ + + uint8_t fn:2; /* CIP Fraction Number */ + uint8_t qpc:3; /* CIP Quadlet Padding Count */ + uint8_t sph:1; /* CIP Source Packet Header */ + uint8_t _r3:2; + + uint8_t dbc; /* CIP Data Block Continuity */ + + uint8_t qi2:2; /* CIP Quadlet Indicator 2 */ + uint8_t format_id:6; /* CIP Format ID */ +#elif __BYTE_ORDER == __LITTLE_ENDIAN + uint8_t channel:6; + uint8_t tag:2; + + uint8_t app:4; + uint8_t tcode:4; + + uint8_t sid:6; /* CIP Source ID */ + uint8_t qi1:2; /* CIP Quadlet Indicator 1 */ + + uint8_t dbs; /* CIP Data Block Size */ + + uint8_t _r3:2; + uint8_t sph:1; /* CIP Source Packet Header */ + uint8_t qpc:3; /* CIP Quadlet Padding Count */ + uint8_t fn:2; /* CIP Fraction Number */ + + uint8_t dbc; /* CIP Data Block Continuity */ + + uint8_t format_id:6; /* CIP Format ID */ + uint8_t qi2:2; /* CIP Quadlet Indicator 2 */ +#endif + uint8_t fdf; /* CIP Format Dependent Field */ + uint16_t syt; + + uint8_t payload0; +} __attribute__ ((__packed__)); + +#endif /* AVB_IEC61883_H */
View file
pipewire-0.3.56.tar.gz/src/modules/module-avb/internal.h
Added
@@ -0,0 +1,167 @@ +/* PipeWire + * + * Copyright © 2022 Wim Taymans + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef AVB_INTERNAL_H +#define AVB_INTERNAL_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <pipewire/pipewire.h> + +struct server; +struct avb_mrp; + +#define AVB_TSN_ETH 0x22f0 +#define AVB_BROADCAST_MAC { 0x91, 0xe0, 0xf0, 0x01, 0x00, 0x00 }; + +struct impl { + struct pw_loop *loop; + struct pw_context *context; + struct spa_hook context_listener; + struct pw_core *core; + unsigned do_disconnect:1; + + struct pw_properties *props; + struct pw_work_queue *work_queue; + + struct spa_list servers; +}; + +struct server_events { +#define AVB_VERSION_SERVER_EVENTS 0 + uint32_t version; + + /** the server is destroyed */ + void (*destroy) (void *data); + + int (*message) (void *data, uint64_t now, const void *message, int len); + + void (*periodic) (void *data, uint64_t now); + + int (*command) (void *data, uint64_t now, const char *command, const char *args, FILE *out); +}; + +struct descriptor { + struct spa_list link; + uint16_t type; + uint16_t index; + uint32_t size; + void *ptr; +}; + +struct server { + struct spa_list link; + struct impl *impl; + + char *ifname; + uint8_t mac_addr6; + uint64_t entity_id; + int ifindex; + + struct spa_source *source; + struct spa_source *timer; + + struct spa_hook_list listener_list; + + struct spa_list descriptors; + struct spa_list streams; + + unsigned debug_messages:1; + + struct avb_mrp *mrp; + struct avb_mmrp *mmrp; + struct avb_mvrp *mvrp; + struct avb_msrp *msrp; + struct avb_maap *maap; + + struct avb_msrp_attribute *domain_attr; +}; + +#include "stream.h" + +static inline const struct descriptor *server_find_descriptor(struct server *server, + uint16_t type, uint16_t index) +{ + struct descriptor *d; + spa_list_for_each(d, &server->descriptors, link) { + if (d->type == type && + d->index == index) + return d; + } + return NULL; +} +static inline void *server_add_descriptor(struct server *server, + uint16_t type, uint16_t index, size_t size, void *ptr) +{ + struct descriptor *d; + + if ((d = calloc(1, sizeof(struct descriptor) + size)) == NULL) + return NULL; + + d->type = type; + d->index = index; + d->size = size; + d->ptr = SPA_PTROFF(d, sizeof(struct descriptor), void); + if (ptr) + memcpy(d->ptr, ptr, size); + spa_list_append(&server->descriptors, &d->link); + return d->ptr; +} + +static inline struct stream *server_find_stream(struct server *server, + enum spa_direction direction, uint16_t index) +{ + struct stream *s; + spa_list_for_each(s, &server->streams, link) { + if (s->direction == direction && + s->index == index) + return s; + } + return NULL; +} + +struct server *avdecc_server_new(struct impl *impl, struct spa_dict *props); +void avdecc_server_free(struct server *server); + +void avdecc_server_add_listener(struct server *server, struct spa_hook *listener, + const struct server_events *events, void *data); + +int avb_server_make_socket(struct server *server, uint16_t type, const uint8_t mac6); + +int avb_server_send_packet(struct server *server, const uint8_t dest6, + uint16_t type, void *data, size_t size); + +struct aecp { + struct server *server; + struct spa_hook server_listener; +}; + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* AVB_INTERNAL_H */
View file
pipewire-0.3.56.tar.gz/src/modules/module-avb/maap.c
Added
@@ -0,0 +1,467 @@ +/* AVB support + * + * Copyright © 2022 Wim Taymans + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include <unistd.h> + +#include <spa/utils/json.h> + +#include <pipewire/pipewire.h> +#include <pipewire/conf.h> + +#include "utils.h" +#include "maap.h" + +#define MAAP_ALLOCATION_POOL_SIZE 0xFE00 +#define MAAP_ALLOCATION_POOL_BASE { 0x91, 0xe0, 0xf0, 0x00, 0x00, 0x00 } +static uint8_t maap_base6 = MAAP_ALLOCATION_POOL_BASE; + +#define MAAP_PROBE_RETRANSMITS 3 + +#define MAAP_PROBE_INTERVAL_MS 500 +#define MAAP_PROBE_INTERVAL_VAR_MS 100 + +#define MAAP_ANNOUNCE_INTERVAL_MS 3000 +#define MAAP_ANNOUNCE_INTERVAL_VAR_MS 2000 + +struct maap { + struct server *server; + struct spa_hook server_listener; + + struct pw_properties *props; + + struct spa_source *source; + +#define STATE_IDLE 0 +#define STATE_PROBE 1 +#define STATE_ANNOUNCE 2 + uint32_t state; + uint64_t timeout; + uint32_t probe_count; + + unsigned short xsubi3; + + uint16_t offset; + uint16_t count; +}; + +static const char *message_type_as_string(uint8_t message_type) +{ + switch (message_type) { + case AVB_MAAP_MESSAGE_TYPE_PROBE: + return "PROBE"; + case AVB_MAAP_MESSAGE_TYPE_DEFEND: + return "DEFEND"; + case AVB_MAAP_MESSAGE_TYPE_ANNOUNCE: + return "ANNOUNCE"; + } + return "INVALID"; +} + +static void maap_message_debug(struct maap *maap, const struct avb_packet_maap *p) +{ + uint32_t v; + const uint8_t *addr; + + v = AVB_PACKET_MAAP_GET_MESSAGE_TYPE(p); + pw_log_info("message-type: %d (%s)", v, message_type_as_string(v)); + pw_log_info(" maap-version: %d", AVB_PACKET_MAAP_GET_MAAP_VERSION(p)); + pw_log_info(" length: %d", AVB_PACKET_GET_LENGTH(&p->hdr)); + + pw_log_info(" stream-id: 0x%"PRIx64, AVB_PACKET_MAAP_GET_STREAM_ID(p)); + addr = AVB_PACKET_MAAP_GET_REQUEST_START(p); + pw_log_info(" request-start: %02x:%02x:%02x:%02x:%02x:%02x", + addr0, addr1, addr2, addr3, addr4, addr5); + pw_log_info(" request-count: %d", AVB_PACKET_MAAP_GET_REQUEST_COUNT(p)); + addr = AVB_PACKET_MAAP_GET_CONFLICT_START(p); + pw_log_info(" conflict-start: %02x:%02x:%02x:%02x:%02x:%02x", + addr0, addr1, addr2, addr3, addr4, addr5); + pw_log_info(" conflict-count: %d", AVB_PACKET_MAAP_GET_CONFLICT_COUNT(p)); +} + +#define PROBE_TIMEOUT(n) ((n) + (MAAP_PROBE_INTERVAL_MS + \ + drand48() * MAAP_PROBE_INTERVAL_VAR_MS) * SPA_NSEC_PER_MSEC) +#define ANNOUNCE_TIMEOUT(n) ((n) + (MAAP_ANNOUNCE_INTERVAL_MS + \ + drand48() * MAAP_ANNOUNCE_INTERVAL_VAR_MS) * SPA_NSEC_PER_MSEC) + +static int make_new_address(struct maap *maap, uint64_t now, int range) +{ + maap->offset = nrand48(maap->xsubi) % (MAAP_ALLOCATION_POOL_SIZE - range); + maap->count = range; + maap->state = STATE_PROBE; + maap->probe_count = MAAP_PROBE_RETRANSMITS; + maap->timeout = PROBE_TIMEOUT(now); + return 0; +} + +static uint16_t maap_check_conflict(struct maap *maap, const uint8_t request_start6, + uint16_t request_count, uint8_t conflict_start6) +{ + uint16_t our_start, our_end; + uint16_t req_start, req_end; + uint16_t conf_start, conf_count = 0; + + if (memcmp(request_start, maap_base, 4) != 0) + return 0; + + our_start = maap->offset; + our_end = our_start + maap->count; + req_start = request_start4 << 8 | request_start5; + req_end = req_start + request_count; + + if (our_start >= req_start && our_start <= req_end) { + conf_start = our_start; + conf_count = SPA_MIN(our_end, req_end) - our_start; + } + else if (req_start >= our_start && req_start <= our_end) { + conf_start = req_start; + conf_count = SPA_MIN(req_end, our_end) - req_start; + } + if (conf_count == 0) + return 0; + + conflict_start4 = conf_start >> 8; + conflict_start5 = conf_start; + return conf_count; +} + +static int send_packet(struct maap *maap, uint64_t now, + uint8_t type, const uint8_t conflict_start6, uint16_t conflict_count) +{ + struct avb_ethernet_header *h; + struct avb_packet_maap *p; + uint8_t buf1024; + uint8_t bmac6 = AVB_MAAP_MAC; + int res = 0; + uint8_t start6; + + spa_memzero(buf, sizeof(buf)); + h = (void*)buf; + p = SPA_PTROFF(h, sizeof(*h), void); + + memcpy(h->dest, bmac, 6); + memcpy(h->src, maap->server->mac_addr, 6); + h->type = htons(AVB_TSN_ETH); + + p->hdr.subtype = AVB_SUBTYPE_MAAP; + AVB_PACKET_SET_LENGTH(&p->hdr, sizeof(*p)); + + AVB_PACKET_MAAP_SET_MAAP_VERSION(p, 1); + AVB_PACKET_MAAP_SET_MESSAGE_TYPE(p, type); + + memcpy(start, maap_base, 4); + start4 = maap->offset >> 8; + start5 = maap->offset; + AVB_PACKET_MAAP_SET_REQUEST_START(p, start); + AVB_PACKET_MAAP_SET_REQUEST_COUNT(p, maap->count); + if (conflict_count) { + AVB_PACKET_MAAP_SET_CONFLICT_START(p, conflict_start); + AVB_PACKET_MAAP_SET_CONFLICT_COUNT(p, conflict_count); + } + + if (maap->server->debug_messages) { + pw_log_info("send: %d (%s)", type, message_type_as_string(type)); + maap_message_debug(maap, p); + } + + if (send(maap->source->fd, p, sizeof(*h) + sizeof(*p), 0) < 0) { + res = -errno; + pw_log_warn("got send error: %m"); + } + return res; +} + +static int handle_probe(struct maap *maap, uint64_t now, const struct avb_packet_maap *p) +{ + uint8_t conflict_start6; + uint16_t conflict_count; + + conflict_count = maap_check_conflict(maap, p->request_start, ntohs(p->request_count),
View file
pipewire-0.3.56.tar.gz/src/modules/module-avb/maap.h
Added
@@ -0,0 +1,70 @@ +/* AVB support + * + * Copyright © 2022 Wim Taymans + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef AVB_MAAP_H +#define AVB_MAAP_H + +#include "packets.h" +#include "internal.h" + +#define AVB_TSN_ETH 0x22f0 +#define AVB_MAAP_MAC { 0x91, 0xe0, 0xf0, 0x00, 0xff, 0x00 }; + +#define AVB_MAAP_MESSAGE_TYPE_PROBE 1 +#define AVB_MAAP_MESSAGE_TYPE_DEFEND 2 +#define AVB_MAAP_MESSAGE_TYPE_ANNOUNCE 3 + +struct avb_packet_maap { + struct avb_packet_header hdr; + uint64_t stream_id; + uint8_t request_start6; + uint16_t request_count; + uint8_t conflict_start6; + uint16_t conflict_count; +} __attribute__ ((__packed__)); + +#define AVB_PACKET_MAAP_SET_MESSAGE_TYPE(p,v) AVB_PACKET_SET_SUB1(&(p)->hdr, v) +#define AVB_PACKET_MAAP_SET_MAAP_VERSION(p,v) AVB_PACKET_SET_SUB2(&(p)->hdr, v) +#define AVB_PACKET_MAAP_SET_STREAM_ID(p,v) ((p)->stream_id = htobe64(v)) +#define AVB_PACKET_MAAP_SET_REQUEST_START(p,v) memcpy((p)->request_start, (v), 6) +#define AVB_PACKET_MAAP_SET_REQUEST_COUNT(p,v) ((p)->request_count = htons(v)) +#define AVB_PACKET_MAAP_SET_CONFLICT_START(p,v) memcpy((p)->conflict_start, (v), 6) +#define AVB_PACKET_MAAP_SET_CONFLICT_COUNT(p,v) ((p)->conflict_count = htons(v)) + +#define AVB_PACKET_MAAP_GET_MESSAGE_TYPE(p) AVB_PACKET_GET_SUB1(&(p)->hdr) +#define AVB_PACKET_MAAP_GET_MAAP_VERSION(p) AVB_PACKET_GET_SUB2(&(p)->hdr) +#define AVB_PACKET_MAAP_GET_STREAM_ID(p) be64toh((p)->stream_id) +#define AVB_PACKET_MAAP_GET_REQUEST_START(p) ((p)->request_start) +#define AVB_PACKET_MAAP_GET_REQUEST_COUNT(p) ntohs((p)->request_count) +#define AVB_PACKET_MAAP_GET_CONFLICT_START(p) ((p)->conflict_start) +#define AVB_PACKET_MAAP_GET_CONFLICT_COUNT(p) ntohs((p)->conflict_count) + +struct avb_maap; + +struct avb_maap *avb_maap_register(struct server *server); + +int avb_maap_reserve(struct avb_maap *maap, uint32_t count); +int avb_maap_get_address(struct avb_maap *maap, uint8_t addr6, uint32_t index); + +#endif /* AVB_MAAP_H */
View file
pipewire-0.3.56.tar.gz/src/modules/module-avb/mmrp.c
Added
@@ -0,0 +1,233 @@ +/* AVB support + * + * Copyright © 2022 Wim Taymans + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include <unistd.h> + +#include <pipewire/pipewire.h> + +#include "utils.h" +#include "mmrp.h" + +static const uint8_t mmrp_mac6 = AVB_MMRP_MAC; + +struct attr { + struct avb_mmrp_attribute attr; + struct spa_list link; +}; + +struct mmrp { + struct server *server; + struct spa_hook server_listener; + + struct spa_source *source; + + struct spa_list attributes; +}; + +static bool mmrp_check_header(void *data, const void *hdr, size_t *hdr_size, bool *has_params) +{ + const struct avb_packet_mmrp_msg *msg = hdr; + uint8_t attr_type = msg->attribute_type; + + if (!AVB_MMRP_ATTRIBUTE_TYPE_VALID(attr_type)) + return false; + + *hdr_size = sizeof(*msg); + *has_params = false; + return true; +} + +static int mmrp_attr_event(void *data, uint64_t now, uint8_t attribute_type, uint8_t event) +{ + struct mmrp *mmrp = data; + struct attr *a; + spa_list_for_each(a, &mmrp->attributes, link) + if (a->attr.type == attribute_type) + avb_mrp_attribute_update_state(a->attr.mrp, now, event); + return 0; +} + +static void debug_service_requirement(const struct avb_packet_mmrp_service_requirement *t) +{ + char buf128; + pw_log_info("service requirement"); + pw_log_info(" %s", avb_utils_format_addr(buf, sizeof(buf), t->addr)); +} + +static int process_service_requirement(struct mmrp *mmrp, uint64_t now, uint8_t attr_type, + const void *m, uint8_t event, uint8_t param, int num) +{ + const struct avb_packet_mmrp_service_requirement *t = m; + struct attr *a; + + debug_service_requirement(t); + + spa_list_for_each(a, &mmrp->attributes, link) + if (a->attr.type == attr_type && + memcmp(a->attr.attr.service_requirement.addr, t->addr, 6) == 0) + avb_mrp_attribute_rx_event(a->attr.mrp, now, event); + return 0; +} + +static void debug_process_mac(const struct avb_packet_mmrp_mac *t) +{ + char buf128; + pw_log_info("mac"); + pw_log_info(" %s", avb_utils_format_addr(buf, sizeof(buf), t->addr)); +} + +static int process_mac(struct mmrp *mmrp, uint64_t now, uint8_t attr_type, + const void *m, uint8_t event, uint8_t param, int num) +{ + const struct avb_packet_mmrp_mac *t = m; + struct attr *a; + + debug_process_mac(t); + + spa_list_for_each(a, &mmrp->attributes, link) + if (a->attr.type == attr_type && + memcmp(a->attr.attr.mac.addr, t->addr, 6) == 0) + avb_mrp_attribute_rx_event(a->attr.mrp, now, event); + return 0; +} + +static const struct { + int (*dispatch) (struct mmrp *mmrp, uint64_t now, uint8_t attr_type, + const void *m, uint8_t event, uint8_t param, int num); +} dispatch = { + AVB_MMRP_ATTRIBUTE_TYPE_SERVICE_REQUIREMENT = { process_service_requirement, }, + AVB_MMRP_ATTRIBUTE_TYPE_MAC = { process_mac, }, +}; + +static int mmrp_process(void *data, uint64_t now, uint8_t attribute_type, const void *value, + uint8_t event, uint8_t param, int index) +{ + struct mmrp *mmrp = data; + return dispatchattribute_type.dispatch(mmrp, now, + attribute_type, value, event, param, index); +} + +static const struct avb_mrp_parse_info info = { + AVB_VERSION_MRP_PARSE_INFO, + .check_header = mmrp_check_header, + .attr_event = mmrp_attr_event, + .process = mmrp_process, +}; + +static int mmrp_message(struct mmrp *mmrp, uint64_t now, const void *message, int len) +{ + pw_log_debug("MMRP"); + return avb_mrp_parse_packet(mmrp->server->mrp, + now, message, len, &info, mmrp); +} + +static void on_socket_data(void *data, int fd, uint32_t mask) +{ + struct mmrp *mmrp = data; + struct timespec now; + + if (mask & SPA_IO_IN) { + int len; + uint8_t buffer2048; + + len = recv(fd, buffer, sizeof(buffer), 0); + + if (len < 0) { + pw_log_warn("got recv error: %m"); + } + else if (len < (int)sizeof(struct avb_packet_header)) { + pw_log_warn("short packet received (%d < %d)", len, + (int)sizeof(struct avb_packet_header)); + } else { + clock_gettime(CLOCK_REALTIME, &now); + mmrp_message(mmrp, SPA_TIMESPEC_TO_NSEC(&now), buffer, len); + } + } +} +static void mmrp_destroy(void *data) +{ + struct mmrp *mmrp = data; + spa_hook_remove(&mmrp->server_listener); + pw_loop_destroy_source(mmrp->server->impl->loop, mmrp->source); + free(mmrp); +} + +static const struct server_events server_events = { + AVB_VERSION_SERVER_EVENTS, + .destroy = mmrp_destroy, +}; + +struct avb_mmrp_attribute *avb_mmrp_attribute_new(struct avb_mmrp *m, + uint8_t type) +{ + struct mmrp *mmrp = (struct mmrp*)m; + struct avb_mrp_attribute *attr; + struct attr *a; + + attr = avb_mrp_attribute_new(mmrp->server->mrp, sizeof(struct attr)); + + a = attr->user_data; + a->attr.mrp = attr; + a->attr.type = type; + spa_list_append(&mmrp->attributes, &a->link); + + return &a->attr; +} + +struct avb_mmrp *avb_mmrp_register(struct server *server) +{
View file
pipewire-0.3.56.tar.gz/src/modules/module-avb/mmrp.h
Added
@@ -0,0 +1,68 @@ +/* AVB support + * + * Copyright © 2022 Wim Taymans + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef AVB_MMRP_H +#define AVB_MMRP_H + +#include "mrp.h" +#include "internal.h" + +#define AVB_MMRP_ETH 0x88f6 +#define AVB_MMRP_MAC { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x20 } + +#define AVB_MMRP_ATTRIBUTE_TYPE_SERVICE_REQUIREMENT 1 +#define AVB_MMRP_ATTRIBUTE_TYPE_MAC 2 +#define AVB_MMRP_ATTRIBUTE_TYPE_VALID(t) ((t)>=1 && (t)<=2) + +struct avb_packet_mmrp_msg { + uint8_t attribute_type; + uint8_t attribute_length; + uint8_t attribute_list0; +} __attribute__ ((__packed__)); + +struct avb_packet_mmrp_service_requirement { + unsigned char addr6; +} __attribute__ ((__packed__)); + +struct avb_packet_mmrp_mac { + unsigned char addr6; +} __attribute__ ((__packed__)); + +struct avb_mmrp; + +struct avb_mmrp_attribute { + struct avb_mrp_attribute *mrp; + uint8_t type; + union { + struct avb_packet_mmrp_service_requirement service_requirement; + struct avb_packet_mmrp_mac mac; + } attr; +}; + +struct avb_mmrp_attribute *avb_mmrp_attribute_new(struct avb_mmrp *mmrp, + uint8_t type); + +struct avb_mmrp *avb_mmrp_register(struct server *server); + +#endif /* AVB_MMRP_H */
View file
pipewire-0.3.56.tar.gz/src/modules/module-avb/mrp.c
Added
@@ -0,0 +1,612 @@ +/* AVB support + * + * Copyright © 2022 Wim Taymans + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include <pipewire/pipewire.h> + +#include "mrp.h" + +#define MRP_JOINTIMER_MS 100 +#define MRP_LVTIMER_MS 1000 +#define MRP_LVATIMER_MS 10000 +#define MRP_PERIODTIMER_MS 1000 + +#define mrp_emit(s,m,v,...) spa_hook_list_call(&s->listener_list, struct avb_mrp_events, m, v, ##__VA_ARGS__) +#define mrp_emit_event(s,n,e) mrp_emit(s,event,0,n,e) +#define mrp_emit_notify(s,n,a,e) mrp_emit(s,notify,0,n,a,e) + +#define mrp_attribute_emit(a,m,v,...) spa_hook_list_call(&a->listener_list, struct avb_mrp_attribute_events, m, v, ##__VA_ARGS__) +#define mrp_attribute_emit_notify(a,n,e) mrp_attribute_emit(a,notify,0,n,e) + + +struct mrp; + +struct attribute { + struct avb_mrp_attribute attr; + struct mrp *mrp; + struct spa_list link; + uint8_t applicant_state; + uint8_t registrar_state; + uint64_t leave_timeout; + unsigned joined:1; + struct spa_hook_list listener_list; +}; + +struct mrp { + struct server *server; + struct spa_hook server_listener; + + struct spa_hook_list listener_list; + + struct spa_list attributes; + + uint64_t periodic_timeout; + uint64_t leave_all_timeout; + uint64_t join_timeout; +}; + +static void mrp_destroy(void *data) +{ + struct mrp *mrp = data; + spa_hook_remove(&mrp->server_listener); + free(mrp); +} + +static void global_event(struct mrp *mrp, uint64_t now, uint8_t event) +{ + struct attribute *a; + spa_list_for_each(a, &mrp->attributes, link) + avb_mrp_attribute_update_state(&a->attr, now, event); + mrp_emit_event(mrp, now, event); +} + +static void mrp_periodic(void *data, uint64_t now) +{ + struct mrp *mrp = data; + bool leave_all = false; + struct attribute *a; + + if (now > mrp->periodic_timeout) { + if (mrp->periodic_timeout > 0) + global_event(mrp, now, AVB_MRP_EVENT_PERIODIC); + mrp->periodic_timeout = now + MRP_PERIODTIMER_MS * SPA_NSEC_PER_MSEC; + } + if (now > mrp->leave_all_timeout) { + if (mrp->leave_all_timeout > 0) { + global_event(mrp, now, AVB_MRP_EVENT_RX_LVA); + leave_all = true; + } + mrp->leave_all_timeout = now + (MRP_LVATIMER_MS + (random() % (MRP_LVATIMER_MS / 2))) + * SPA_NSEC_PER_MSEC; + } + + if (now > mrp->join_timeout) { + if (mrp->join_timeout > 0) { + uint8_t event = leave_all ? AVB_MRP_EVENT_TX_LVA : AVB_MRP_EVENT_TX; + global_event(mrp, now, event); + } + mrp->join_timeout = now + MRP_JOINTIMER_MS * SPA_NSEC_PER_MSEC; + } + + spa_list_for_each(a, &mrp->attributes, link) { + if (a->leave_timeout > 0 && now > a->leave_timeout) { + a->leave_timeout = 0; + avb_mrp_attribute_update_state(&a->attr, now, AVB_MRP_EVENT_LV_TIMER); + } + } +} + +static const struct server_events server_events = { + AVB_VERSION_SERVER_EVENTS, + .destroy = mrp_destroy, + .periodic = mrp_periodic, +}; + +int avb_mrp_parse_packet(struct avb_mrp *mrp, uint64_t now, const void *pkt, int len, + const struct avb_mrp_parse_info *info, void *data) +{ + uint8_t *e = SPA_PTROFF(pkt, len, uint8_t); + uint8_t *m = SPA_PTROFF(pkt, sizeof(struct avb_packet_mrp), uint8_t); + + while (m < e && (m0 != 0 || m1 != 0)) { + const struct avb_packet_mrp_hdr *hdr = (const struct avb_packet_mrp_hdr*)m; + uint8_t attr_type = hdr->attribute_type; + uint8_t attr_len = hdr->attribute_length; + size_t hdr_size; + bool has_param; + + if (!info->check_header(data, hdr, &hdr_size, &has_param)) + return -EINVAL; + + m += hdr_size; + + while (m < e && (m0 != 0 || m1 != 0)) { + const struct avb_packet_mrp_vector *v = + (const struct avb_packet_mrp_vector*)m; + uint16_t i, num_values = AVB_MRP_VECTOR_GET_NUM_VALUES(v); + uint8_t event_len = (num_values+2)/3; + uint8_t param_len = has_param ? (num_values+3)/4 : 0; + int plen = sizeof(*v) + attr_len + event_len + param_len; + const uint8_t *first = v->first_value; + uint8_t event3, param4 = { 0, }; + + if (m + plen > e) + return -EPROTO; + + if (v->lva) + info->attr_event(data, now, attr_type, AVB_MRP_EVENT_RX_LVA); + + for (i = 0; i < num_values; i++) { + if (i % 3 == 0) { + uint8_t ep = firstattr_len + i/3; + event2 = ep % 6; ep /= 6; + event1 = ep % 6; ep /= 6; + event0 = ep % 6; + } + if (has_param && (i % 4 == 0)) { + uint8_t ep = firstattr_len + event_len + i/4; + param3 = ep % 4; ep /= 4; + param2 = ep % 4; ep /= 4; + param1 = ep % 4; ep /= 4; + param0 = ep % 4; + } + info->process(data, now, attr_type, first, + eventi%3, parami%4, i); + } + m += plen; + } + m += 2; + } + return 0; +} + +const char *avb_mrp_notify_name(uint8_t notify) +{ + switch(notify) { + case AVB_MRP_NOTIFY_NEW: + return "new"; + case AVB_MRP_NOTIFY_JOIN: + return "join"; + case AVB_MRP_NOTIFY_LEAVE: + return "leave"; + } + return "unknown"; +} + +const char *avb_mrp_send_name(uint8_t send) +{ + switch(send) { + case AVB_MRP_SEND_NEW:
View file
pipewire-0.3.56.tar.gz/src/modules/module-avb/mrp.h
Added
@@ -0,0 +1,181 @@ +/* AVB support + * + * Copyright © 2022 Wim Taymans + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef AVB_MRP_H +#define AVB_MRP_H + +#include "packets.h" +#include "internal.h" + +#define AVB_MRP_PROTOCOL_VERSION 0 + +struct avb_packet_mrp { + struct avb_ethernet_header eth; + uint8_t version; +} __attribute__ ((__packed__)); + +struct avb_packet_mrp_hdr { + uint8_t attribute_type; + uint8_t attribute_length; +} __attribute__ ((__packed__)); + +struct avb_packet_mrp_vector { +#if __BYTE_ORDER == __BIG_ENDIAN + unsigned lva:3; + unsigned nv1:5; +#elif __BYTE_ORDER == __LITTLE_ENDIAN + unsigned nv1:5; + unsigned lva:3; +#endif + uint8_t nv2; + uint8_t first_value0; +} __attribute__ ((__packed__)); + +#define AVB_MRP_VECTOR_SET_NUM_VALUES(a,v) ((a)->nv1 = ((v) >> 8),(a)->nv2 = (v)) +#define AVB_MRP_VECTOR_GET_NUM_VALUES(a) ((a)->nv1 << 8 | (a)->nv2) + +struct avb_packet_mrp_footer { + uint16_t end_mark; +} __attribute__ ((__packed__)); + +/* applicant states */ +#define AVB_MRP_VO 0 /* Very anxious Observer */ +#define AVB_MRP_VP 1 /* Very anxious Passive */ +#define AVB_MRP_VN 2 /* Very anxious New */ +#define AVB_MRP_AN 3 /* Anxious New */ +#define AVB_MRP_AA 4 /* Anxious Active */ +#define AVB_MRP_QA 5 /* Quiet Active */ +#define AVB_MRP_LA 6 /* Leaving Active */ +#define AVB_MRP_AO 7 /* Anxious Observer */ +#define AVB_MRP_QO 8 /* Quiet Observer */ +#define AVB_MRP_AP 9 /* Anxious Passive */ +#define AVB_MRP_QP 10 /* Quiet Passive */ +#define AVB_MRP_LO 11 /* Leaving Observer */ + +/* registrar states */ +#define AVB_MRP_IN 16 +#define AVB_MRP_LV 17 +#define AVB_MRP_MT 18 + +/* events */ +#define AVB_MRP_EVENT_BEGIN 0 +#define AVB_MRP_EVENT_NEW 1 +#define AVB_MRP_EVENT_JOIN 2 +#define AVB_MRP_EVENT_LV 3 +#define AVB_MRP_EVENT_TX 4 +#define AVB_MRP_EVENT_TX_LVA 5 +#define AVB_MRP_EVENT_TX_LVAF 6 +#define AVB_MRP_EVENT_RX_NEW 7 +#define AVB_MRP_EVENT_RX_JOININ 8 +#define AVB_MRP_EVENT_RX_IN 9 +#define AVB_MRP_EVENT_RX_JOINMT 10 +#define AVB_MRP_EVENT_RX_MT 11 +#define AVB_MRP_EVENT_RX_LV 12 +#define AVB_MRP_EVENT_RX_LVA 13 +#define AVB_MRP_EVENT_FLUSH 14 +#define AVB_MRP_EVENT_REDECLARE 15 +#define AVB_MRP_EVENT_PERIODIC 16 +#define AVB_MRP_EVENT_LV_TIMER 17 +#define AVB_MRP_EVENT_LVA_TIMER 18 + +/* attribute events */ +#define AVB_MRP_ATTRIBUTE_EVENT_NEW 0 +#define AVB_MRP_ATTRIBUTE_EVENT_JOININ 1 +#define AVB_MRP_ATTRIBUTE_EVENT_IN 2 +#define AVB_MRP_ATTRIBUTE_EVENT_JOINMT 3 +#define AVB_MRP_ATTRIBUTE_EVENT_MT 4 +#define AVB_MRP_ATTRIBUTE_EVENT_LV 5 + +#define AVB_MRP_SEND_NEW 1 +#define AVB_MRP_SEND_JOININ 2 +#define AVB_MRP_SEND_IN 3 +#define AVB_MRP_SEND_JOINMT 4 +#define AVB_MRP_SEND_MT 5 +#define AVB_MRP_SEND_LV 6 + +#define AVB_MRP_NOTIFY_NEW 1 +#define AVB_MRP_NOTIFY_JOIN 2 +#define AVB_MRP_NOTIFY_LEAVE 3 + +const char *avb_mrp_notify_name(uint8_t notify); +const char *avb_mrp_send_name(uint8_t send); + +struct avb_mrp_attribute { + uint8_t pending_send; + void *user_data; +}; + +struct avb_mrp_attribute_events { +#define AVB_VERSION_MRP_ATTRIBUTE_EVENTS 0 + uint32_t version; + + void (*notify) (void *data, uint64_t now, uint8_t notify); +}; + +struct avb_mrp_attribute *avb_mrp_attribute_new(struct avb_mrp *mrp, + size_t user_size); +void avb_mrp_attribute_destroy(struct avb_mrp_attribute *attr); + +void avb_mrp_attribute_update_state(struct avb_mrp_attribute *attr, uint64_t now, int event); + +void avb_mrp_attribute_rx_event(struct avb_mrp_attribute *attr, uint64_t now, uint8_t event); + +void avb_mrp_attribute_begin(struct avb_mrp_attribute *attr, uint64_t now); +void avb_mrp_attribute_join(struct avb_mrp_attribute *attr, uint64_t now, bool is_new); +void avb_mrp_attribute_leave(struct avb_mrp_attribute *attr, uint64_t now); + +void avb_mrp_attribute_add_listener(struct avb_mrp_attribute *attr, struct spa_hook *listener, + const struct avb_mrp_attribute_events *events, void *data); + +struct avb_mrp_parse_info { +#define AVB_VERSION_MRP_PARSE_INFO 0 + uint32_t version; + + bool (*check_header) (void *data, const void *hdr, size_t *hdr_size, bool *has_params); + + int (*attr_event) (void *data, uint64_t now, uint8_t attribute_type, uint8_t event); + + int (*process) (void *data, uint64_t now, uint8_t attribute_type, const void *value, + uint8_t event, uint8_t param, int index); +}; + +int avb_mrp_parse_packet(struct avb_mrp *mrp, uint64_t now, const void *pkt, int size, + const struct avb_mrp_parse_info *cb, void *data); + +struct avb_mrp_events { +#define AVB_VERSION_MRP_EVENTS 0 + uint32_t version; + + void (*event) (void *data, uint64_t now, uint8_t event); + + void (*notify) (void *data, uint64_t now, struct avb_mrp_attribute *attr, uint8_t notify); +}; + +struct avb_mrp *avb_mrp_new(struct server *server); +void avb_mrp_destroy(struct avb_mrp *mrp); + +void avb_mrp_add_listener(struct avb_mrp *mrp, struct spa_hook *listener, + const struct avb_mrp_events *events, void *data); + +#endif /* AVB_MRP_H */
View file
pipewire-0.3.56.tar.gz/src/modules/module-avb/msrp.c
Added
@@ -0,0 +1,459 @@ +/* AVB support + * + * Copyright © 2022 Wim Taymans + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include <unistd.h> + +#include <spa/debug/mem.h> + +#include <pipewire/pipewire.h> + +#include "utils.h" +#include "msrp.h" + +static const uint8_t msrp_mac6 = AVB_MSRP_MAC; + +struct attr { + struct avb_msrp_attribute attr; + struct msrp *msrp; + struct spa_hook listener; + struct spa_list link; +}; + +struct msrp { + struct server *server; + struct spa_hook server_listener; + struct spa_hook mrp_listener; + + struct spa_source *source; + + struct spa_list attributes; +}; + +static void debug_msrp_talker_common(const struct avb_packet_msrp_talker *t) +{ + char buf128; + pw_log_info(" stream-id: %s", avb_utils_format_id(buf, sizeof(buf), be64toh(t->stream_id))); + pw_log_info(" dest-addr: %s", avb_utils_format_addr(buf, sizeof(buf), t->dest_addr)); + pw_log_info(" vlan-id: %d", ntohs(t->vlan_id)); + pw_log_info(" tspec-max-frame-size: %d", ntohs(t->tspec_max_frame_size)); + pw_log_info(" tspec-max-interval-frames: %d", ntohs(t->tspec_max_interval_frames)); + pw_log_info(" priority: %d", t->priority); + pw_log_info(" rank: %d", t->rank); + pw_log_info(" accumulated-latency: %d", ntohl(t->accumulated_latency)); +} + +static void debug_msrp_talker(const struct avb_packet_msrp_talker *t) +{ + pw_log_info("talker"); + debug_msrp_talker_common(t); +} + +static void notify_talker(struct msrp *msrp, uint64_t now, struct attr *attr, uint8_t notify) +{ + pw_log_info("> notify talker: %s", avb_mrp_notify_name(notify)); + debug_msrp_talker(&attr->attr.attr.talker); +} + +static int process_talker(struct msrp *msrp, uint64_t now, uint8_t attr_type, + const void *m, uint8_t event, uint8_t param, int num) +{ + const struct avb_packet_msrp_talker *t = m; + struct attr *a; + spa_list_for_each(a, &msrp->attributes, link) + if (a->attr.type == attr_type && + a->attr.attr.talker.stream_id == t->stream_id) { + a->attr.attr.talker = *t; + avb_mrp_attribute_rx_event(a->attr.mrp, now, event); + } + return 0; +} +static int encode_talker(struct msrp *msrp, struct attr *a, void *m) +{ + struct avb_packet_msrp_msg *msg = m; + struct avb_packet_mrp_vector *v; + struct avb_packet_msrp_talker *t; + struct avb_packet_mrp_footer *f; + uint8_t *ev; + size_t attr_list_length = sizeof(*v) + sizeof(*t) + sizeof(*f) + 1; + + msg->attribute_type = AVB_MSRP_ATTRIBUTE_TYPE_TALKER_ADVERTISE; + msg->attribute_length = sizeof(*t); + msg->attribute_list_length = htons(attr_list_length); + + v = (struct avb_packet_mrp_vector *)msg->attribute_list; + v->lva = 0; + AVB_MRP_VECTOR_SET_NUM_VALUES(v, 1); + + t = (struct avb_packet_msrp_talker *)v->first_value; + *t = a->attr.attr.talker; + + ev = SPA_PTROFF(t, sizeof(*t), uint8_t); + *ev = a->attr.mrp->pending_send * 6 * 6; + + f = SPA_PTROFF(ev, sizeof(*ev), struct avb_packet_mrp_footer); + f->end_mark = 0; + + return attr_list_length + sizeof(*msg); +} + + +static void debug_msrp_talker_fail(const struct avb_packet_msrp_talker_fail *t) +{ + char buf128; + pw_log_info("talker fail"); + debug_msrp_talker_common(&t->talker); + pw_log_info(" bridge-id: %s", avb_utils_format_id(buf, sizeof(buf), be64toh(t->bridge_id))); + pw_log_info(" failure-code: %d", t->failure_code); +} + +static int process_talker_fail(struct msrp *msrp, uint64_t now, uint8_t attr_type, + const void *m, uint8_t event, uint8_t param, int num) +{ + const struct avb_packet_msrp_talker_fail *t = m; + struct attr *a; + + debug_msrp_talker_fail(t); + + spa_list_for_each(a, &msrp->attributes, link) + if (a->attr.type == attr_type && + a->attr.attr.talker_fail.talker.stream_id == t->talker.stream_id) + avb_mrp_attribute_rx_event(a->attr.mrp, now, event); + return 0; +} + +static void debug_msrp_listener(const struct avb_packet_msrp_listener *l, uint8_t param) +{ + char buf128; + pw_log_info("listener"); + pw_log_info(" %s", avb_utils_format_id(buf, sizeof(buf), be64toh(l->stream_id))); + pw_log_info(" %d", param); +} + +static void notify_listener(struct msrp *msrp, uint64_t now, struct attr *attr, uint8_t notify) +{ + pw_log_info("> notify listener: %s", avb_mrp_notify_name(notify)); + debug_msrp_listener(&attr->attr.attr.listener, attr->attr.param); +} + +static int process_listener(struct msrp *msrp, uint64_t now, uint8_t attr_type, + const void *m, uint8_t event, uint8_t param, int num) +{ + const struct avb_packet_msrp_listener *l = m; + struct attr *a; + spa_list_for_each(a, &msrp->attributes, link) + if (a->attr.type == attr_type && + a->attr.attr.listener.stream_id == l->stream_id) + avb_mrp_attribute_rx_event(a->attr.mrp, now, event); + return 0; +} +static int encode_listener(struct msrp *msrp, struct attr *a, void *m) +{ + struct avb_packet_msrp_msg *msg = m; + struct avb_packet_mrp_vector *v; + struct avb_packet_msrp_listener *l; + struct avb_packet_mrp_footer *f; + uint8_t *ev; + size_t attr_list_length = sizeof(*v) + sizeof(*l) + sizeof(*f) + 1 + 1; + + msg->attribute_type = AVB_MSRP_ATTRIBUTE_TYPE_LISTENER; + msg->attribute_length = sizeof(*l); + msg->attribute_list_length = htons(attr_list_length); + + v = (struct avb_packet_mrp_vector *)msg->attribute_list; + v->lva = 0; + AVB_MRP_VECTOR_SET_NUM_VALUES(v, 1); + + l = (struct avb_packet_msrp_listener *)v->first_value; + *l = a->attr.attr.listener; + + ev = SPA_PTROFF(l, sizeof(*l), uint8_t); + *ev = a->attr.mrp->pending_send * 6 * 6; + + ev = SPA_PTROFF(ev, sizeof(*ev), uint8_t); + *ev = a->attr.param * 4 * 4 * 4; + + f = SPA_PTROFF(ev, sizeof(*ev), struct avb_packet_mrp_footer); + f->end_mark = 0; + + return attr_list_length + sizeof(*msg);
View file
pipewire-0.3.56.tar.gz/src/modules/module-avb/msrp.h
Added
@@ -0,0 +1,134 @@ +/* AVB support + * + * Copyright © 2022 Wim Taymans + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef AVB_MSRP_H +#define AVB_MSRP_H + +#include "internal.h" +#include "mrp.h" + +#define AVB_MSRP_ETH 0x22ea +#define AVB_MSRP_MAC { 0x01, 0x80, 0xc2, 0x00, 0x00, 0xe }; + +#define AVB_MSRP_ATTRIBUTE_TYPE_TALKER_ADVERTISE 1 +#define AVB_MSRP_ATTRIBUTE_TYPE_TALKER_FAILED 2 +#define AVB_MSRP_ATTRIBUTE_TYPE_LISTENER 3 +#define AVB_MSRP_ATTRIBUTE_TYPE_DOMAIN 4 +#define AVB_MSRP_ATTRIBUTE_TYPE_VALID(t) ((t)>=1 && (t)<=4) + +struct avb_packet_msrp_msg { + uint8_t attribute_type; + uint8_t attribute_length; + uint16_t attribute_list_length; + uint8_t attribute_list0; +} __attribute__ ((__packed__)); + +#define AVB_MSRP_TSPEC_MAX_INTERVAL_FRAMES_DEFAULT 1 +#define AVB_MSRP_RANK_DEFAULT 1 +#define AVB_MSRP_PRIORITY_DEFAULT 3 + +struct avb_packet_msrp_talker { + uint64_t stream_id; + uint8_t dest_addr6; + uint16_t vlan_id; + uint16_t tspec_max_frame_size; + uint16_t tspec_max_interval_frames; +#if __BYTE_ORDER == __BIG_ENDIAN + unsigned priority:3; + unsigned rank:1; + unsigned reserved:4; +#elif __BYTE_ORDER == __LITTLE_ENDIAN + unsigned reserved:4; + unsigned rank:1; + unsigned priority:3; +#endif + uint32_t accumulated_latency; +} __attribute__ ((__packed__)); + +/* failure codes */ +#define AVB_MRP_FAIL_BANDWIDTH 1 +#define AVB_MRP_FAIL_BRIDGE 2 +#define AVB_MRP_FAIL_TC_BANDWIDTH 3 +#define AVB_MRP_FAIL_ID_BUSY 4 +#define AVB_MRP_FAIL_DSTADDR_BUSY 5 +#define AVB_MRP_FAIL_PREEMPTED 6 +#define AVB_MRP_FAIL_LATENCY_CHNG 7 +#define AVB_MRP_FAIL_PORT_NOT_AVB 8 +#define AVB_MRP_FAIL_DSTADDR_FULL 9 +#define AVB_MRP_FAIL_AVB_MRP_RESOURCE 10 +#define AVB_MRP_FAIL_MMRP_RESOURCE 11 +#define AVB_MRP_FAIL_DSTADDR_FAIL 12 +#define AVB_MRP_FAIL_PRIO_NOT_SR 13 +#define AVB_MRP_FAIL_FRAME_SIZE 14 +#define AVB_MRP_FAIL_FANIN_EXCEED 15 +#define AVB_MRP_FAIL_STREAM_CHANGE 16 +#define AVB_MRP_FAIL_VLAN_BLOCKED 17 +#define AVB_MRP_FAIL_VLAN_DISABLED 18 +#define AVB_MRP_FAIL_SR_PRIO_ERR 19 + +struct avb_packet_msrp_talker_fail { + struct avb_packet_msrp_talker talker; + uint64_t bridge_id; + uint8_t failure_code; +} __attribute__ ((__packed__)); + +struct avb_packet_msrp_listener { + uint64_t stream_id; +} __attribute__ ((__packed__)); + +/* domain discovery */ +#define AVB_MSRP_CLASS_ID_DEFAULT 6 +#define AVB_DEFAULT_VLAN 2 + +struct avb_packet_msrp_domain { + uint8_t sr_class_id; + uint8_t sr_class_priority; + uint16_t sr_class_vid; +} __attribute__ ((__packed__)); + +#define AVB_MSRP_LISTENER_PARAM_IGNORE 0 +#define AVB_MSRP_LISTENER_PARAM_ASKING_FAILED 1 +#define AVB_MSRP_LISTENER_PARAM_READY 2 +#define AVB_MSRP_LISTENER_PARAM_READY_FAILED 3 + +struct avb_msrp_attribute { + struct avb_mrp_attribute *mrp; + uint8_t type; + uint8_t param; + union { + struct avb_packet_msrp_talker talker; + struct avb_packet_msrp_talker_fail talker_fail; + struct avb_packet_msrp_listener listener; + struct avb_packet_msrp_domain domain; + } attr; +}; + +struct avb_msrp; + +struct avb_msrp_attribute *avb_msrp_attribute_new(struct avb_msrp *msrp, + uint8_t type); + +struct avb_msrp *avb_msrp_register(struct server *server); + +#endif /* AVB_MSRP_H */
View file
pipewire-0.3.56.tar.gz/src/modules/module-avb/mvrp.c
Added
@@ -0,0 +1,297 @@ +/* AVB support + * + * Copyright © 2022 Wim Taymans + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include <unistd.h> + +#include <pipewire/pipewire.h> + +#include "mvrp.h" + +static const uint8_t mvrp_mac6 = AVB_MVRP_MAC; + +struct attr { + struct avb_mvrp_attribute attr; + struct spa_hook listener; + struct spa_list link; + struct mvrp *mvrp; +}; + +struct mvrp { + struct server *server; + struct spa_hook server_listener; + struct spa_hook mrp_listener; + + struct spa_source *source; + + struct spa_list attributes; +}; + +static bool mvrp_check_header(void *data, const void *hdr, size_t *hdr_size, bool *has_params) +{ + const struct avb_packet_mvrp_msg *msg = hdr; + uint8_t attr_type = msg->attribute_type; + + if (!AVB_MVRP_ATTRIBUTE_TYPE_VALID(attr_type)) + return false; + + *hdr_size = sizeof(*msg); + *has_params = false; + return true; +} + +static int mvrp_attr_event(void *data, uint64_t now, uint8_t attribute_type, uint8_t event) +{ + struct mvrp *mvrp = data; + struct attr *a; + spa_list_for_each(a, &mvrp->attributes, link) + if (a->attr.type == attribute_type) + avb_mrp_attribute_rx_event(a->attr.mrp, now, event); + return 0; +} + +static void debug_vid(const struct avb_packet_mvrp_vid *t) +{ + pw_log_info("vid"); + pw_log_info(" %d", ntohs(t->vlan)); +} + +static int process_vid(struct mvrp *mvrp, uint64_t now, uint8_t attr_type, + const void *m, uint8_t event, uint8_t param, int num) +{ + return mvrp_attr_event(mvrp, now, attr_type, event); +} + +static int encode_vid(struct mvrp *mvrp, struct attr *a, void *m) +{ + struct avb_packet_mvrp_msg *msg = m; + struct avb_packet_mrp_vector *v; + struct avb_packet_mvrp_vid *d; + struct avb_packet_mrp_footer *f; + uint8_t *ev; + size_t attr_list_length = sizeof(*v) + sizeof(*d) + sizeof(*f) + 1; + + msg->attribute_type = AVB_MVRP_ATTRIBUTE_TYPE_VID; + msg->attribute_length = sizeof(*d); + + v = (struct avb_packet_mrp_vector *)msg->attribute_list; + v->lva = 0; + AVB_MRP_VECTOR_SET_NUM_VALUES(v, 1); + + d = (struct avb_packet_mvrp_vid *)v->first_value; + *d = a->attr.attr.vid; + + ev = SPA_PTROFF(d, sizeof(*d), uint8_t); + *ev = a->attr.mrp->pending_send * 36; + + f = SPA_PTROFF(ev, sizeof(*ev), struct avb_packet_mrp_footer); + f->end_mark = 0; + + return attr_list_length + sizeof(*msg); +} + +static void notify_vid(struct mvrp *mvrp, uint64_t now, struct attr *attr, uint8_t notify) +{ + pw_log_info("> notify vid: %s", avb_mrp_notify_name(notify)); + debug_vid(&attr->attr.attr.vid); +} + +static const struct { + const char *name; + int (*process) (struct mvrp *mvrp, uint64_t now, uint8_t attr_type, + const void *m, uint8_t event, uint8_t param, int num); + int (*encode) (struct mvrp *mvrp, struct attr *attr, void *m); + void (*notify) (struct mvrp *mvrp, uint64_t now, struct attr *attr, uint8_t notify); +} dispatch = { + AVB_MVRP_ATTRIBUTE_TYPE_VID = { "vid", process_vid, encode_vid, notify_vid }, +}; + +static int mvrp_process(void *data, uint64_t now, uint8_t attribute_type, const void *value, + uint8_t event, uint8_t param, int index) +{ + struct mvrp *mvrp = data; + return dispatchattribute_type.process(mvrp, now, + attribute_type, value, event, param, index); +} + +static const struct avb_mrp_parse_info info = { + AVB_VERSION_MRP_PARSE_INFO, + .check_header = mvrp_check_header, + .attr_event = mvrp_attr_event, + .process = mvrp_process, +}; + +static int mvrp_message(struct mvrp *mvrp, uint64_t now, const void *message, int len) +{ + pw_log_debug("MVRP"); + return avb_mrp_parse_packet(mvrp->server->mrp, + now, message, len, &info, mvrp); +} + +static void on_socket_data(void *data, int fd, uint32_t mask) +{ + struct mvrp *mvrp = data; + struct timespec now; + + if (mask & SPA_IO_IN) { + int len; + uint8_t buffer2048; + + len = recv(fd, buffer, sizeof(buffer), 0); + + if (len < 0) { + pw_log_warn("got recv error: %m"); + } + else if (len < (int)sizeof(struct avb_packet_header)) { + pw_log_warn("short packet received (%d < %d)", len, + (int)sizeof(struct avb_packet_header)); + } else { + clock_gettime(CLOCK_REALTIME, &now); + mvrp_message(mvrp, SPA_TIMESPEC_TO_NSEC(&now), buffer, len); + } + } +} + +static void mvrp_destroy(void *data) +{ + struct mvrp *mvrp = data; + spa_hook_remove(&mvrp->server_listener); + pw_loop_destroy_source(mvrp->server->impl->loop, mvrp->source); + free(mvrp); +} + +static const struct server_events server_events = { + AVB_VERSION_SERVER_EVENTS, + .destroy = mvrp_destroy, +}; + +static void mvrp_notify(void *data, uint64_t now, uint8_t notify) +{ + struct attr *a = data; + struct mvrp *mvrp = a->mvrp; + return dispatcha->attr.type.notify(mvrp, now, a, notify); +} + +static const struct avb_mrp_attribute_events mrp_attr_events = { + AVB_VERSION_MRP_ATTRIBUTE_EVENTS, + .notify = mvrp_notify, +}; +
View file
pipewire-0.3.56.tar.gz/src/modules/module-avb/mvrp.h
Added
@@ -0,0 +1,62 @@ +/* AVB support + * + * Copyright © 2022 Wim Taymans + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef AVB_MVRP_H +#define AVB_MVRP_H + +#include "mrp.h" +#include "internal.h" + +#define AVB_MVRP_ETH 0x88f5 +#define AVB_MVRP_MAC { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x21 }; + +struct avb_packet_mvrp_msg { + uint8_t attribute_type; + uint8_t attribute_length; + uint8_t attribute_list0; +} __attribute__ ((__packed__)); + +#define AVB_MVRP_ATTRIBUTE_TYPE_VID 1 +#define AVB_MVRP_ATTRIBUTE_TYPE_VALID(t) ((t)==1) + +struct avb_packet_mvrp_vid { + uint16_t vlan; +} __attribute__ ((__packed__)); + +struct avb_mvrp; + +struct avb_mvrp_attribute { + struct avb_mrp_attribute *mrp; + uint8_t type; + union { + struct avb_packet_mvrp_vid vid; + } attr; +}; + +struct avb_mvrp_attribute *avb_mvrp_attribute_new(struct avb_mvrp *mvrp, + uint8_t type); + +struct avb_mvrp *avb_mvrp_register(struct server *server); + +#endif /* AVB_MVRP_H */
View file
pipewire-0.3.56.tar.gz/src/modules/module-avb/packets.h
Added
@@ -0,0 +1,101 @@ +/* Spa AVB support + * + * Copyright © 2022 Wim Taymans + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef AVB_PACKETS_H +#define AVB_PACKETS_H + +#include <arpa/inet.h> + +#define AVB_SUBTYPE_61883_IIDC 0x00 +#define AVB_SUBTYPE_MMA_STREAM 0x01 +#define AVB_SUBTYPE_AAF 0x02 +#define AVB_SUBTYPE_CVF 0x03 +#define AVB_SUBTYPE_CRF 0x04 +#define AVB_SUBTYPE_TSCF 0x05 +#define AVB_SUBTYPE_SVF 0x06 +#define AVB_SUBTYPE_RVF 0x07 +#define AVB_SUBTYPE_AEF_CONTINUOUS 0x6E +#define AVB_SUBTYPE_VSF_STREAM 0x6F +#define AVB_SUBTYPE_EF_STREAM 0x7F +#define AVB_SUBTYPE_NTSCF 0x82 +#define AVB_SUBTYPE_ESCF 0xEC +#define AVB_SUBTYPE_EECF 0xED +#define AVB_SUBTYPE_AEF_DISCRETE 0xEE +#define AVB_SUBTYPE_ADP 0xFA +#define AVB_SUBTYPE_AECP 0xFB +#define AVB_SUBTYPE_ACMP 0xFC +#define AVB_SUBTYPE_MAAP 0xFE +#define AVB_SUBTYPE_EF_CONTROL 0xFF + +struct avb_ethernet_header { + uint8_t dest6; + uint8_t src6; + uint16_t type; +} __attribute__ ((__packed__)); + +struct avb_frame_header { + uint8_t dest6; + uint8_t src6; + uint16_t type; /* 802.1Q Virtual Lan 0x8100 */ + uint16_t prio_cfi_id; + uint16_t etype; +} __attribute__ ((__packed__)); + +struct avb_packet_header { + uint8_t subtype; +#if __BYTE_ORDER == __BIG_ENDIAN + unsigned sv:1; /* stream_id valid */ + unsigned version:3; + unsigned subtype_data1:4; + + unsigned subtype_data2:5; + unsigned len1:3; +#elif __BYTE_ORDER == __LITTLE_ENDIAN + unsigned subtype_data1:4; + unsigned version:3; + unsigned sv:1; + + unsigned len1:3; + unsigned subtype_data2:5; +#elif +#error "Unknown byte order" +#endif + uint8_t len2:8; +} __attribute__ ((__packed__)); + +#define AVB_PACKET_SET_SUBTYPE(p,v) ((p)->subtype = (v)) +#define AVB_PACKET_SET_SV(p,v) ((p)->sv = (v)) +#define AVB_PACKET_SET_VERSION(p,v) ((p)->version = (v)) +#define AVB_PACKET_SET_SUB1(p,v) ((p)->subtype_data1 = (v)) +#define AVB_PACKET_SET_SUB2(p,v) ((p)->subtype_data2 = (v)) +#define AVB_PACKET_SET_LENGTH(p,v) ((p)->len1 = ((v) >> 8),(p)->len2 = (v)) + +#define AVB_PACKET_GET_SUBTYPE(p) ((p)->subtype) +#define AVB_PACKET_GET_SV(p) ((p)->sv) +#define AVB_PACKET_GET_VERSION(p) ((p)->version) +#define AVB_PACKET_GET_SUB1(p) ((p)->subtype_data1) +#define AVB_PACKET_GET_SUB2(p) ((p)->subtype_data2) +#define AVB_PACKET_GET_LENGTH(p) ((p)->len1 << 8 | (p)->len2) + +#endif /* AVB_PACKETS_H */
View file
pipewire-0.3.56.tar.gz/src/modules/module-avb/srp.c
Added
@@ -0,0 +1,59 @@ +/* AVB support + * + * Copyright © 2022 Wim Taymans + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include <pipewire/pipewire.h> + +#include "srp.h" + +struct srp { + struct server *server; + struct spa_hook server_listener; +}; + +static void srp_destroy(void *data) +{ + struct srp *srp = data; + spa_hook_remove(&srp->server_listener); + free(srp); +} + +static const struct server_events server_events = { + AVB_VERSION_SERVER_EVENTS, + .destroy = srp_destroy, +}; + +int avb_srp_register(struct server *server) +{ + struct srp *srp; + + srp = calloc(1, sizeof(*srp)); + if (srp == NULL) + return -errno; + + srp->server = server; + + avdecc_server_add_listener(server, &srp->server_listener, &server_events, srp); + + return 0; +}
View file
pipewire-0.3.56.tar.gz/src/modules/module-avb/srp.h
Added
@@ -0,0 +1,32 @@ +/* AVB support + * + * Copyright © 2022 Wim Taymans + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef AVB_SRP_H +#define AVB_SRP_H + +#include "internal.h" + +int avb_srp_register(struct server *server); + +#endif /* AVB_SRP_H */
View file
pipewire-0.3.56.tar.gz/src/modules/module-avb/stream.c
Added
@@ -0,0 +1,589 @@ +/* AVB support + * + * Copyright © 2022 Wim Taymans + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include <unistd.h> +#include <linux/if_ether.h> +#include <linux/if_packet.h> +#include <linux/net_tstamp.h> +#include <net/if.h> +#include <sys/ioctl.h> + +#include <spa/debug/mem.h> +#include <spa/pod/builder.h> +#include <spa/param/audio/format-utils.h> + +#include "iec61883.h" +#include "stream.h" +#include "utils.h" +#include "aecp-aem-descriptors.h" + +static void on_stream_destroy(void *d) +{ + struct stream *stream = d; + spa_hook_remove(&stream->stream_listener); + stream->stream = NULL; +} + +static void on_source_stream_process(void *data) +{ + struct stream *stream = data; + struct pw_buffer *buf; + struct spa_data *d; + uint32_t index, n_bytes; + int32_t avail, wanted; + + if ((buf = pw_stream_dequeue_buffer(stream->stream)) == NULL) { + pw_log_debug("out of buffers: %m"); + return; + } + + d = buf->buffer->datas; + + wanted = buf->requested ? buf->requested * stream->stride : d0.maxsize; + + n_bytes = SPA_MIN(d0.maxsize, (uint32_t)wanted); + + avail = spa_ringbuffer_get_read_index(&stream->ring, &index); + + if (avail < wanted) { + pw_log_debug("capture underrun %d < %d", avail, wanted); + memset(d0.data, 0, n_bytes); + } else { + spa_ringbuffer_read_data(&stream->ring, + stream->buffer_data, + stream->buffer_size, + index % stream->buffer_size, + d0.data, n_bytes); + index += n_bytes; + spa_ringbuffer_read_update(&stream->ring, index); + } + + d0.chunk->size = n_bytes; + d0.chunk->stride = stream->stride; + d0.chunk->offset = 0; + buf->size = n_bytes / stream->stride; + + pw_stream_queue_buffer(stream->stream, buf); +} + +static const struct pw_stream_events source_stream_events = { + PW_VERSION_STREAM_EVENTS, + .destroy = on_stream_destroy, + .process = on_source_stream_process +}; + +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 flush_write(struct stream *stream, uint64_t current_time) +{ + int32_t avail; + uint32_t index; + uint64_t ptime, txtime; + int pdu_count; + ssize_t n; + struct avb_frame_header *h = (void*)stream->pdu; + struct avb_packet_iec61883 *p = SPA_PTROFF(h, sizeof(*h), void); + uint8_t dbc; + + avail = spa_ringbuffer_get_read_index(&stream->ring, &index); + + pdu_count = (avail / stream->stride) / stream->frames_per_pdu; + + txtime = current_time + stream->t_uncertainty; + ptime = txtime + stream->mtt; + dbc = stream->dbc; + + while (pdu_count--) { + *(uint64_t*)CMSG_DATA(stream->cmsg) = txtime; + + set_iovec(&stream->ring, + stream->buffer_data, + stream->buffer_size, + index % stream->buffer_size, + &stream->iov1, stream->payload_size); + + p->seq_num = stream->pdu_seq++; + p->tv = 1; + p->timestamp = ptime; + p->dbc = dbc; + + n = sendmsg(stream->source->fd, &stream->msg, 0); + if (n < 0 || n != (ssize_t)stream->pdu_size) { + pw_log_error("sendmsg() failed %zd != %zd: %m", + n, stream->pdu_size); + } + txtime += stream->pdu_period; + ptime += stream->pdu_period; + index += stream->payload_size; + dbc += stream->frames_per_pdu; + } + stream->dbc = dbc; + spa_ringbuffer_read_update(&stream->ring, index); + return 0; +} + +static void on_sink_stream_process(void *data) +{ + struct stream *stream = data; + struct pw_buffer *buf; + struct spa_data *d; + int32_t filled; + uint32_t index, offs, avail, size; + struct timespec now; + + if ((buf = pw_stream_dequeue_buffer(stream->stream)) == NULL) { + pw_log_debug("out of buffers: %m"); + return; + } + + d = buf->buffer->datas; + + offs = SPA_MIN(d0.chunk->offset, d0.maxsize); + size = SPA_MIN(d0.chunk->size, d0.maxsize - offs); + avail = size - offs; + + filled = spa_ringbuffer_get_write_index(&stream->ring, &index); + + if (filled >= (int32_t)stream->buffer_size) { + pw_log_warn("playback overrun %d >= %zd", filled, stream->buffer_size); + } else { + spa_ringbuffer_write_data(&stream->ring, + stream->buffer_data, + stream->buffer_size, + index % stream->buffer_size, + SPA_PTROFF(d0.data, offs, void), avail); + index += avail; + spa_ringbuffer_write_update(&stream->ring, index); + } + pw_stream_queue_buffer(stream->stream, buf); + + clock_gettime(CLOCK_TAI, &now); + flush_write(stream, SPA_TIMESPEC_TO_NSEC(&now)); +} + +static void setup_pdu(struct stream *stream) +{ + struct avb_frame_header *h; + struct avb_packet_iec61883 *p; + ssize_t payload_size, hdr_size, pdu_size; + + spa_memzero(stream->pdu, sizeof(stream->pdu));
View file
pipewire-0.3.56.tar.gz/src/modules/module-avb/stream.h
Added
@@ -0,0 +1,104 @@ +/* AVB support + * + * Copyright © 2022 Wim Taymans + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef AVB_STREAM_H +#define AVB_STREAM_H + +#include <sys/socket.h> +#include <sys/types.h> +#include <linux/if_packet.h> +#include <net/if.h> + +#include <spa/utils/ringbuffer.h> +#include <spa/param/audio/format.h> + +#include <pipewire/pipewire.h> + +#define BUFFER_SIZE (1u<<16) +#define BUFFER_MASK (BUFFER_SIZE-1) + +struct stream { + struct spa_list link; + + struct server *server; + + uint16_t direction; + uint16_t index; + const struct descriptor *desc; + uint64_t id; + uint64_t peer_id; + + struct pw_stream *stream; + struct spa_hook stream_listener; + + uint8_t addr6; + struct spa_source *source; + int prio; + int vlan_id; + int mtt; + int t_uncertainty; + uint32_t frames_per_pdu; + int ptime_tolerance; + + uint8_t pdu2048; + size_t hdr_size; + size_t payload_size; + size_t pdu_size; + int64_t pdu_period; + uint8_t pdu_seq; + uint8_t prev_seq; + uint8_t dbc; + + struct iovec iov3; + struct sockaddr_ll sock_addr; + struct msghdr msg; + char controlCMSG_SPACE(sizeof(uint64_t)); + struct cmsghdr *cmsg; + + struct spa_ringbuffer ring; + void *buffer_data; + size_t buffer_size; + + uint64_t format; + uint32_t stride; + struct spa_audio_info info; + + struct avb_msrp_attribute *talker_attr; + struct avb_msrp_attribute *listener_attr; + struct avb_mvrp_attribute *vlan_attr; +}; + +#include "msrp.h" +#include "mvrp.h" +#include "maap.h" + +struct stream *server_create_stream(struct server *server, + enum spa_direction direction, uint16_t index); + +void stream_destroy(struct stream *stream); + +int stream_activate(struct stream *stream, uint64_t now); +int stream_deactivate(struct stream *stream, uint64_t now); + +#endif /* AVB_STREAM_H */
View file
pipewire-0.3.56.tar.gz/src/modules/module-avb/utils.h
Added
@@ -0,0 +1,86 @@ +/* AVB support + * + * Copyright © 2022 Wim Taymans + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef AVB_UTILS_H +#define AVB_UTILS_H + +#include <spa/utils/json.h> + +#include "internal.h" + +static inline char *avb_utils_format_id(char *str, size_t size, const uint64_t id) +{ + snprintf(str, size, "%02x:%02x:%02x:%02x:%02x:%02x:%04x", + (uint8_t)(id >> 56), + (uint8_t)(id >> 48), + (uint8_t)(id >> 40), + (uint8_t)(id >> 32), + (uint8_t)(id >> 24), + (uint8_t)(id >> 16), + (uint16_t)(id)); + return str; +} + +static inline int avb_utils_parse_id(const char *str, int len, uint64_t *id) +{ + char s64; + uint8_t v6; + uint16_t unique_id; + if (spa_json_parse_stringn(str, len, s, sizeof(s)) <= 0) + return -EINVAL; + if (sscanf(s, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx:%hx", + &v0, &v1, &v2, &v3, + &v4, &v5, &unique_id) == 7) { + *id = (uint64_t) v0 << 56 | + (uint64_t) v1 << 48 | + (uint64_t) v2 << 40 | + (uint64_t) v3 << 32 | + (uint64_t) v4 << 24 | + (uint64_t) v5 << 16 | + unique_id; + } else if (!spa_atou64(str, id, 0)) + return -EINVAL; + return 0; +} + +static inline char *avb_utils_format_addr(char *str, size_t size, const uint8_t addr6) +{ + snprintf(str, size, "%02x:%02x:%02x:%02x:%02x:%02x", + addr0, addr1, addr2, addr3, addr4, addr5); + return str; +} +static inline int avb_utils_parse_addr(const char *str, int len, uint8_t addr6) +{ + char s64; + uint8_t v6; + if (spa_json_parse_stringn(str, len, s, sizeof(s)) <= 0) + return -EINVAL; + if (sscanf(s, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx", + &v0, &v1, &v2, &v3, &v4, &v5) != 6) + return -EINVAL; + memcpy(addr, v, 6); + return 0; +} + +#endif /* AVB_UTILS_H */
View file
pipewire-0.3.54.tar.gz/src/modules/module-filter-chain.c -> pipewire-0.3.56.tar.gz/src/modules/module-filter-chain.c
Changed
@@ -591,6 +591,8 @@ struct graph *graph = &impl->graph; uint32_t i, outsize = 0, n_hndl = graph->n_hndl; int32_t stride = 0; + struct graph_port *port; + struct spa_data *bd; if ((in = pw_stream_dequeue_buffer(impl->capture)) == NULL) pw_log_debug("out of capture buffers: %m"); @@ -602,30 +604,37 @@ goto done; for (i = 0; i < in->buffer->n_datas; i++) { - struct spa_data *ds = &in->buffer->datasi; - struct graph_port *port = &graph->inputi; uint32_t offs, size; - offs = SPA_MIN(ds->chunk->offset, ds->maxsize); - size = SPA_MIN(ds->chunk->size, ds->maxsize - offs); + bd = &in->buffer->datasi; - if (port->desc) + offs = SPA_MIN(bd->chunk->offset, bd->maxsize); + size = SPA_MIN(bd->chunk->size, bd->maxsize - offs); + + port = i < graph->n_input ? &graph->inputi : NULL; + + if (port && port->desc) port->desc->connect_port(port->hndl, port->port, - SPA_PTROFF(ds->data, offs, void)); + SPA_PTROFF(bd->data, offs, void)); - outsize = SPA_MAX(outsize, size); - stride = SPA_MAX(stride, ds->chunk->stride); + outsize = i == 0 ? size : SPA_MIN(outsize, size); + stride = SPA_MAX(stride, bd->chunk->stride); } for (i = 0; i < out->buffer->n_datas; i++) { - struct spa_data *dd = &out->buffer->datasi; - struct graph_port *port = &graph->outputi; - if (port->desc) - port->desc->connect_port(port->hndl, port->port, dd->data); + bd = &out->buffer->datasi; + + outsize = SPA_MIN(outsize, bd->maxsize); + + port = i < graph->n_output ? &graph->outputi : NULL; + + if (port && port->desc) + port->desc->connect_port(port->hndl, port->port, bd->data); else - memset(dd->data, 0, outsize); - dd->chunk->offset = 0; - dd->chunk->size = outsize; - dd->chunk->stride = stride; + memset(bd->data, 0, outsize); + + bd->chunk->offset = 0; + bd->chunk->size = outsize; + bd->chunk->stride = stride; } for (i = 0; i < n_hndl; i++) { struct graph_hndl *hndl = &graph->hndli;
View file
pipewire-0.3.54.tar.gz/src/modules/module-loopback.c -> pipewire-0.3.56.tar.gz/src/modules/module-loopback.c
Changed
@@ -99,7 +99,8 @@ * playback.props = { * node.name = "playback.CM106_stereo_pair_2" * audio.position = RL RR - * node.target = "alsa_output.usb-0d8c_USB_Sound_Device-00.analog-surround-71" + * target.object = "alsa_output.usb-0d8c_USB_Sound_Device-00.analog-surround-71" + * node.dont-reconnect = true * stream.dont-remix = true * node.passive = true * } @@ -188,33 +189,35 @@ pw_log_debug("out of playback buffers: %m"); if (in != NULL && out != NULL) { + uint32_t outsize = UINT32_MAX; + int32_t stride = 0; + struct spa_data *d; + const void *srcin->buffer->n_datas; - for (i = 0; i < out->buffer->n_datas; i++) { - struct spa_data *ds, *dd; - uint32_t outsize = 0; - int32_t stride = 0; - - dd = &out->buffer->datasi; + for (i = 0; i < in->buffer->n_datas; i++) { + uint32_t offs, size; - if (i < in->buffer->n_datas) { - uint32_t offs, size; + d = &in->buffer->datasi; + offs = SPA_MIN(d->chunk->offset, d->maxsize); + size = SPA_MIN(d->chunk->size, d->maxsize - offs); - ds = &in->buffer->datasi; + srci = SPA_PTROFF(d->data, offs, void); + outsize = SPA_MIN(outsize, size); + stride = SPA_MAX(stride, d->chunk->stride); + } + for (i = 0; i < out->buffer->n_datas; i++) { + d = &out->buffer->datasi; - offs = SPA_MIN(ds->chunk->offset, ds->maxsize); - size = SPA_MIN(ds->chunk->size, ds->maxsize - offs); - stride = SPA_MAX(stride, stride); + outsize = SPA_MIN(outsize, d->maxsize); - memcpy(dd->data, - SPA_PTROFF(ds->data, offs, void), size); + if (i < in->buffer->n_datas) + memcpy(d->data, srci, outsize); + else + memset(d->data, 0, outsize); - outsize = SPA_MAX(outsize, size); - } else { - memset(dd->data, 0, outsize); - } - dd->chunk->offset = 0; - dd->chunk->size = outsize; - dd->chunk->stride = stride; + d->chunk->offset = 0; + d->chunk->size = outsize; + d->chunk->stride = stride; } }
View file
pipewire-0.3.54.tar.gz/src/modules/module-pipe-tunnel.c -> pipewire-0.3.56.tar.gz/src/modules/module-pipe-tunnel.c
Changed
@@ -94,7 +94,7 @@ * - \ref PW_KEY_NODE_GROUP * - \ref PW_KEY_NODE_VIRTUAL * - \ref PW_KEY_MEDIA_CLASS - * - \ref PW_KEY_NODE_TARGET to specify the remote name or id to link to + * - \ref PW_KEY_TARGET_OBJECT to specify the remote name or serial id to link to * * When not otherwise specified, the pipe will accept or produce a * 16 bits, stereo, 48KHz sample stream. @@ -112,7 +112,7 @@ * #audio.rate=<sample rate> * #audio.channels=<number of channels> * #audio.position=<channel map> - * #node.target=<remote target node> + * #target.object=<remote target node> * stream.props = { * # extra sink properties * } @@ -139,7 +139,7 @@ " node.latency=<latency as fraction> " \ " node.name=<name of the nodes> " \ " node.description=<description of the nodes> " \ - " node.target=<remote node target name> " \ + " target.object=<remote node target name> " \ " audio.format=<sample format> " \ " audio.rate=<sample rate> " \ " audio.channels=<number of channels> " \
View file
pipewire-0.3.54.tar.gz/src/modules/module-protocol-pulse/pulse-server.c -> pipewire-0.3.56.tar.gz/src/modules/module-protocol-pulse/pulse-server.c
Changed
@@ -1277,7 +1277,7 @@ stream->timestamp = pd->pwt.now; stream->delay = pd->pwt.buffered * SPA_USEC_PER_SEC / stream->ss.rate; if (pd->pwt.rate.denom > 0) - stream->delay = pd->pwt.delay * SPA_USEC_PER_SEC / pd->pwt.rate.denom; + stream->delay += pd->pwt.delay * SPA_USEC_PER_SEC * pd->pwt.rate.num / pd->pwt.rate.denom; if (stream->direction == PW_DIRECTION_OUTPUT) { if (pd->quantum != stream->last_quantum) @@ -1332,6 +1332,7 @@ stream->read_index += skip; avail = stream->attr.fragsize; } + pw_log_trace("avail:%d index:%u", avail, index); while ((uint32_t)avail >= stream->attr.fragsize) { towrite = SPA_MIN((uint32_t)avail, stream->attr.fragsize); @@ -2124,7 +2125,7 @@ if (stream == NULL || stream->type != STREAM_TYPE_PLAYBACK) return -ENOENT; - pw_log_debug("read:%"PRIx64" write:%"PRIx64" queued:%"PRIi64" delay:%"PRIi64 + pw_log_debug("read:0x%"PRIx64" write:0x%"PRIx64" queued:%"PRIi64" delay:%"PRIi64 " playing:%"PRIu64, stream->read_index, stream->write_index, stream->write_index - stream->read_index, stream->delay, @@ -2173,6 +2174,11 @@ if (stream == NULL || stream->type != STREAM_TYPE_RECORD) return -ENOENT; + pw_log_debug("read:0x%"PRIx64" write:0x%"PRIx64" queued:%"PRIi64" delay:%"PRIi64, + stream->read_index, stream->write_index, + stream->write_index - stream->read_index, stream->delay); + + gettimeofday(&now, NULL); reply = reply_new(client, tag); message_put(reply,
View file
pipewire-0.3.54.tar.gz/src/modules/module-pulse-tunnel.c -> pipewire-0.3.56.tar.gz/src/modules/module-pulse-tunnel.c
Changed
@@ -90,7 +90,7 @@ * - \ref PW_KEY_NODE_GROUP * - \ref PW_KEY_NODE_VIRTUAL * - \ref PW_KEY_MEDIA_CLASS - * - \ref PW_KEY_NODE_TARGET to specify the remote name or id to link to + * - \ref PW_KEY_TARGET_OBJECT to specify the remote node.name or serial.id to link to * * ## Example configuration of a virtual sink * @@ -104,7 +104,7 @@ * #audio.rate=<sample rate> * #audio.channels=<number of channels> * #audio.position=<channel map> - * #node.target=<remote target node> + * #target.object=<remote target name> * stream.props = { * # extra sink properties * } @@ -660,7 +660,9 @@ pa_stream_set_overflow_callback(impl->pa_stream, stream_overflow_cb, impl); pa_stream_set_latency_update_callback(impl->pa_stream, stream_latency_update_cb, impl); - remote_node_target = pw_properties_get(impl->props, PW_KEY_NODE_TARGET); + remote_node_target = pw_properties_get(impl->props, PW_KEY_TARGET_OBJECT); + if (remote_node_target == NULL) + remote_node_target = pw_properties_get(impl->props, PW_KEY_NODE_TARGET); bufferattr.fragsize = (uint32_t) -1; bufferattr.minreq = (uint32_t) -1;
View file
pipewire-0.3.54.tar.gz/src/modules/module-raop-discover.c -> pipewire-0.3.56.tar.gz/src/modules/module-raop-discover.c
Changed
@@ -223,6 +223,8 @@ * 4 = FairPlay SAPv2.5. */ if (str_in_list(value, ",", "1")) value = "RSA"; + else if (str_in_list(value, ",", "4")) + value = "auth_setup"; else value = "none"; pw_properties_set(props, "raop.encryption.type", value);
View file
pipewire-0.3.54.tar.gz/src/modules/module-raop-sink.c -> pipewire-0.3.56.tar.gz/src/modules/module-raop-sink.c
Changed
@@ -130,6 +130,7 @@ enum { CRYPTO_NONE, CRYPTO_RSA, + CRYPTO_AUTH_SETUP, }; enum { CODEC_PCM, @@ -257,7 +258,8 @@ return timespec_to_ntp(&now); } -static int send_udp_sync_packet(struct impl *impl) +static int send_udp_sync_packet(struct impl *impl, + struct sockaddr *dest_addr, socklen_t addrlen) { uint32_t pkt5; uint32_t rtptime = impl->rtptime; @@ -278,10 +280,11 @@ pw_log_debug("sync: delayed:%u now:%"PRIu64" rtptime:%u", rtptime - delay, transmitted, rtptime); - return write(impl->control_fd, pkt, sizeof(pkt)); + return sendto(impl->control_fd, pkt, sizeof(pkt), 0, dest_addr, addrlen); } -static int send_udp_timing_packet(struct impl *impl, uint64_t remote, uint64_t received) +static int send_udp_timing_packet(struct impl *impl, uint64_t remote, uint64_t received, + struct sockaddr *dest_addr, socklen_t addrlen) { uint32_t pkt8; uint64_t transmitted; @@ -299,7 +302,7 @@ pw_log_debug("sync: remote:%"PRIu64" received:%"PRIu64" transmitted:%"PRIu64, remote, received, transmitted); - return write(impl->timing_fd, pkt, sizeof(pkt)); + return sendto(impl->timing_fd, pkt, sizeof(pkt), 0, dest_addr, addrlen); } static int write_codec_pcm(void *dst, void *frames, uint32_t n_frames) @@ -345,7 +348,7 @@ impl->sync++; if (impl->first || impl->sync == impl->sync_period) { impl->sync = 0; - send_udp_sync_packet(impl); + send_udp_sync_packet(impl, NULL, 0); } pkt0 = htonl(0x80600000); if (impl->first) @@ -373,7 +376,7 @@ impl->seq = (impl->seq + 1) & 0xffff; pw_log_debug("send %u", len + 12); - res = write(impl->server_fd, pkt, len + 12); + res = send(impl->server_fd, pkt, len + 12, 0); impl->first = false; @@ -417,7 +420,7 @@ impl->seq = (impl->seq + 1) & 0xffff; pw_log_debug("send %u", len + 16); - res = write(impl->server_fd, pkt, len + 16); + res = send(impl->server_fd, pkt, len + 16, 0); impl->first = false; @@ -593,9 +596,12 @@ if (mask & SPA_IO_IN) { uint64_t remote, received; + struct sockaddr_storage sender; + socklen_t sender_size = sizeof(sender); received = ntp_now(CLOCK_MONOTONIC); - bytes = read(impl->timing_fd, packet, sizeof(packet)); + bytes = recvfrom(impl->timing_fd, packet, sizeof(packet), 0, + (struct sockaddr*)&sender, &sender_size); if (bytes < 0) { pw_log_debug("error reading timing packet: %m"); return; @@ -609,7 +615,11 @@ return; remote = ((uint64_t)ntohl(packet6)) << 32 | ntohl(packet7); - send_udp_timing_packet(impl, remote, received); + if (send_udp_timing_packet(impl, remote, received, + (struct sockaddr *)&sender, sender_size) < 0) { + pw_log_warn("error sending timing packet"); + return; + } } } @@ -833,10 +843,8 @@ return; ntp = ntp_now(CLOCK_MONOTONIC); - send_udp_timing_packet(impl, ntp, ntp); + send_udp_timing_packet(impl, ntp, ntp, NULL, 0); - impl->timing_source = pw_loop_add_io(impl->loop, impl->timing_fd, - SPA_IO_IN, false, on_timing_source_io, impl); impl->control_source = pw_loop_add_io(impl->loop, impl->control_fd, SPA_IO_IN, false, on_control_source_io, impl); @@ -868,6 +876,9 @@ if (impl->control_fd < 0 || impl->timing_fd < 0) goto error; + impl->timing_source = pw_loop_add_io(impl->loop, impl->timing_fd, + SPA_IO_IN, false, on_timing_source_io, impl); + pw_properties_setf(impl->headers, "Transport", "RTP/AVP/UDP;unicast;interleaved=0-1;mode=record;" "control_port=%u;timing_port=%u", @@ -988,7 +999,7 @@ char iv16*2; int res, frames, i, ip_version; char *sdp; - char local_ip256; + char local_ip256; host = pw_properties_get(impl->props, "raop.hostname"); @@ -1048,6 +1059,32 @@ return res; } +static void rtsp_auth_setup_reply(void *data, int status, const struct spa_dict *headers) +{ + struct impl *impl = data; + + pw_log_info("reply %d", status); + + impl->encryption = CRYPTO_NONE; + + rtsp_do_announce(impl); +} + +static int rtsp_do_auth_setup(struct impl *impl) +{ + int res; + + char output = + "\x01" + "\x59\x02\xed\xe9\x0d\x4e\xf2\xbd\x4c\xb6\x8a\x63\x30\x03\x82\x07" + "\xa9\x4d\xbd\x50\xd8\xaa\x46\x5b\x5d\x8c\x01\x2a\x0c\x7e\x1d\x4e"; + + res = pw_rtsp_client_url_send(impl->rtsp, "/auth-setup", "POST", &impl->headers->dict, + "application/octet-stream", output, rtsp_auth_setup_reply, impl); + + return res; +} + static const char *find_attr(char **tokens, const char *key) { int i; @@ -1168,7 +1205,10 @@ rtsp_do_auth(impl, headers); break; case 200: - rtsp_do_announce(impl); + if (impl->encryption == CRYPTO_AUTH_SETUP) + rtsp_do_auth_setup(impl); + else + rtsp_do_announce(impl); break; } } @@ -1648,6 +1688,8 @@ impl->encryption = CRYPTO_NONE; else if (spa_streq(str, "RSA")) impl->encryption = CRYPTO_RSA; + else if (spa_streq(str, "auth_setup")) + impl->encryption = CRYPTO_AUTH_SETUP; else { pw_log_error( "can't handle encryption type %s", str); res = -EINVAL;
View file
pipewire-0.3.54.tar.gz/src/modules/module-raop/rtsp-client.c -> pipewire-0.3.56.tar.gz/src/modules/module-raop/rtsp-client.c
Changed
@@ -273,12 +273,30 @@ int cseq; struct message *msg; const struct spa_dict_item *it; + const char *content_type; + unsigned int content_length; spa_dict_for_each(it, &client->headers->dict) pw_log_info(" %s: %s", it->key, it->value); cseq = pw_properties_get_int32(client->headers, "CSeq", 0); - + content_type = pw_properties_get(client->headers, "Content-Type"); + if (content_type != NULL && strcmp(content_type, "application/octet-stream") == 0) { + pw_log_info("binary response received"); + content_length = pw_properties_get_uint64(client->headers, "Content-Length", 0); + char content_bufcontent_length; + res = read(client->source->fd, content_buf, content_length); + pw_log_debug("read %d bytes", res); + if (res == 0) + return -EPIPE; + if (res < 0) { + res = -errno; + if (res != -EAGAIN && res != -EWOULDBLOCK) + return res; + return 0; + } + pw_properties_set(client->headers, "body", content_buf); + } if ((msg = find_pending(client, cseq)) != NULL) { msg->reply(msg->user_data, client->status, &client->headers->dict); spa_list_remove(&msg->link); @@ -466,7 +484,7 @@ return 0; } -int pw_rtsp_client_send(struct pw_rtsp_client *client, +int pw_rtsp_client_url_send(struct pw_rtsp_client *client, const char *url, const char *cmd, const struct spa_dict *headers, const char *content_type, const char *content, void (*reply) (void *user_data, int status, const struct spa_dict *headers), @@ -485,7 +503,7 @@ cseq = ++client->cseq; - fprintf(f, "%s %s RTSP/1.0\r\n", cmd, client->url); + fprintf(f, "%s %s RTSP/1.0\r\n", cmd, url); fprintf(f, "CSeq: %d\r\n", cseq); if (headers != NULL) { @@ -519,3 +537,12 @@ } return 0; } + +int pw_rtsp_client_send(struct pw_rtsp_client *client, + const char *cmd, const struct spa_dict *headers, + const char *content_type, const char *content, + void (*reply) (void *user_data, int status, const struct spa_dict *headers), + void *user_data) +{ + return pw_rtsp_client_url_send(client, client->url, cmd, headers, content_type, content, reply, user_data); +}
View file
pipewire-0.3.54.tar.gz/src/modules/module-raop/rtsp-client.h -> pipewire-0.3.56.tar.gz/src/modules/module-raop/rtsp-client.h
Changed
@@ -71,6 +71,12 @@ int pw_rtsp_client_get_local_ip(struct pw_rtsp_client *client, int *version, char *ip, size_t len); +int pw_rtsp_client_url_send(struct pw_rtsp_client *client, const char *url, + const char *cmd, const struct spa_dict *headers, + const char *content_type, const char *content, + void (*reply) (void *user_data, int status, const struct spa_dict *headers), + void *user_data); + int pw_rtsp_client_send(struct pw_rtsp_client *client, const char *cmd, const struct spa_dict *headers, const char *content_type, const char *content,
View file
pipewire-0.3.54.tar.gz/src/modules/module-rt.c -> pipewire-0.3.56.tar.gz/src/modules/module-rt.c
Changed
@@ -520,9 +520,8 @@ */ static bool check_realtime_privileges(rlim_t priority) { - int old_policy; + int err, old_policy, new_policy = REALTIME_POLICY; struct sched_param old_sched_params; - int new_policy = REALTIME_POLICY; struct sched_param new_sched_params; /* We could check `RLIMIT_RTPRIO`, but the BSDs generally don't have @@ -530,8 +529,8 @@ * scheduling without that rlimit being set such as `CAP_SYS_NICE` or * running as root. Instead of checking a bunch of preconditions, we * just try if setting realtime scheduling works or not. */ - if (pthread_getschedparam(pthread_self(),&old_policy,&old_sched_params) < 0) { - pw_log_warn("Failed to check RLIMIT_RTPRIO %m"); + if ((err = pthread_getschedparam(pthread_self(),&old_policy,&old_sched_params)) != 0) { + pw_log_warn("Failed to check RLIMIT_RTPRIO: %s", strerror(err)); return false; } @@ -600,8 +599,8 @@ rttime = pw_rtkit_get_rttime_usec_max(impl->system_bus); if (rttime >= 0) { if ((rlim_t)rttime < rl.rlim_cur) { - pw_log_debug("clamping rt.time.soft from %ld to %lld because of RTKit", - rl.rlim_cur, rttime); + pw_log_debug("clamping rt.time.soft from %llu to %lld because of RTKit", + (long long)rl.rlim_cur, rttime); } rl.rlim_cur = SPA_MIN(rl.rlim_cur, (rlim_t)rttime);
View file
pipewire-0.3.54.tar.gz/src/pipewire/buffers.c -> pipewire-0.3.56.tar.gz/src/pipewire/buffers.c
Changed
@@ -166,8 +166,8 @@ uint8_t ibuf4096; struct spa_pod_builder ib = { 0 }; struct spa_pod *oparam, *iparam; - uint32_t iidx, oidx, num = 0; - int in_res = -EIO, out_res = -EIO; + uint32_t iidx, oidx; + int in_res = -EIO, out_res = -EIO, num = 0; for (iidx = 0;;) { spa_pod_builder_init(&ib, ibuf, sizeof(ibuf));
View file
pipewire-0.3.54.tar.gz/src/pipewire/context.c -> pipewire-0.3.56.tar.gz/src/pipewire/context.c
Changed
@@ -543,6 +543,12 @@ } SPA_EXPORT +struct pw_data_loop *pw_context_get_data_loop(struct pw_context *context) +{ + return context->data_loop_impl; +} + +SPA_EXPORT struct pw_work_queue *pw_context_get_work_queue(struct pw_context *context) { return context->work_queue; @@ -1113,7 +1119,7 @@ * the desired final value and activate the followers and then the driver. * * A complete graph evaluation is performed for each change that is made to the - * graph, such as making/destroting links, adding/removing nodes, property changes such + * graph, such as making/destroying links, adding/removing nodes, property changes such * as quantum/rate changes or metadata changes. */ int pw_context_recalc_graph(struct pw_context *context, const char *reason)
View file
pipewire-0.3.54.tar.gz/src/pipewire/context.h -> pipewire-0.3.56.tar.gz/src/pipewire/context.h
Changed
@@ -136,6 +136,9 @@ /** get the context main loop */ struct pw_loop *pw_context_get_main_loop(struct pw_context *context); +/** get the context data loop. Since 0.3.56 */ +struct pw_data_loop *pw_context_get_data_loop(struct pw_context *context); + /** Get the work queue from the context: Since 0.3.26 */ struct pw_work_queue *pw_context_get_work_queue(struct pw_context *context);
View file
pipewire-0.3.54.tar.gz/src/pipewire/pipewire.c -> pipewire-0.3.56.tar.gz/src/pipewire/pipewire.c
Changed
@@ -826,8 +826,7 @@ return global_support.no_color == spa_atob(value); else if (spa_streq(option, "no-config")) return global_support.no_config == spa_atob(value); - else - return false; + return false; } /** Get the client name @@ -841,15 +840,11 @@ const char *cc; static char cname256; - if ((cc = pw_get_application_name())) + if ((cc = pw_get_application_name()) || (cc = pw_get_prgname())) return cc; - else if ((cc = pw_get_prgname())) - return cc; - else { - if (snprintf(cname, sizeof(cname), "pipewire-pid-%zd", (size_t) getpid()) < 0) - return NULL; - return cname; - } + else if (snprintf(cname, sizeof(cname), "pipewire-pid-%zd", (size_t) getpid()) < 0) + return NULL; + return cname; } /** Reverse the direction */
View file
pipewire-0.3.54.tar.gz/src/pipewire/stream.c -> pipewire-0.3.56.tar.gz/src/pipewire/stream.c
Changed
@@ -313,7 +313,7 @@ } -static inline int push_queue(struct stream *stream, struct queue *queue, struct buffer *buffer) +static inline int queue_push(struct stream *stream, struct queue *queue, struct buffer *buffer) { uint32_t index; @@ -330,7 +330,13 @@ return 0; } -static inline struct buffer *pop_queue(struct stream *stream, struct queue *queue) +static inline bool queue_is_empty(struct stream *stream, struct queue *queue) +{ + uint32_t index; + return spa_ringbuffer_get_read_index(&queue->ring, &index) < 1; +} + +static inline struct buffer *queue_pop(struct stream *stream, struct queue *queue) { uint32_t index, id; struct buffer *buffer; @@ -568,7 +574,7 @@ buffer->this.requested = impl->quantum; res = 1; } - pw_log_trace_fp("%p: update buffer:%u size:%u", impl, id, r->size); + pw_log_trace_fp("%p: update buffer:%u size:%"PRIu64, impl, id, buffer->this.requested); return res; } @@ -788,7 +794,7 @@ if (impl->direction == SPA_DIRECTION_INPUT) { struct buffer *b; - while ((b = pop_queue(impl, &impl->dequeued))) { + while ((b = queue_pop(impl, &impl->dequeued))) { if (b->busy) ATOMIC_DEC(b->busy->count); } @@ -927,7 +933,7 @@ if (impl->direction == SPA_DIRECTION_OUTPUT) { pw_log_trace("%p: recycle buffer %d", stream, b->id); - push_queue(impl, &impl->dequeued, b); + queue_push(impl, &impl->dequeued, b); } SPA_FLAG_SET(b->flags, BUFFER_FLAG_ADDED); @@ -945,7 +951,7 @@ struct stream *d = object; pw_log_trace("%p: recycle buffer %d", d, buffer_id); if (buffer_id < d->n_buffers) - push_queue(d, &d->queued, &d->buffersbuffer_id); + queue_push(d, &d->queued, &d->buffersbuffer_id); return 0; } @@ -984,7 +990,7 @@ if (io->status == SPA_STATUS_HAVE_DATA && (b = get_buffer(stream, io->buffer_id)) != NULL) { /* push new buffer */ - if (push_queue(impl, &impl->dequeued, b) == 0) { + if (queue_push(impl, &impl->dequeued, b) == 0) { copy_position(impl, impl->dequeued.incount); if (b->busy) ATOMIC_INC(b->busy->count); @@ -993,7 +999,7 @@ } if (io->status != SPA_STATUS_NEED_DATA) { /* pop buffer to recycle */ - if ((b = pop_queue(impl, &impl->queued))) { + if ((b = queue_pop(impl, &impl->queued))) { pw_log_trace_fp("%p: recycle buffer %d", stream, b->id); } else if (io->status == -EPIPE) return io->status; @@ -1013,28 +1019,36 @@ struct spa_io_buffers *io = impl->io; struct buffer *b; int res; - uint32_t index; - bool recycled; + bool ask_more; again: pw_log_trace_fp("%p: process out status:%d id:%d", stream, io->status, io->buffer_id); - recycled = false; + ask_more = false; if ((res = io->status) != SPA_STATUS_HAVE_DATA) { /* recycle old buffer */ if ((b = get_buffer(stream, io->buffer_id)) != NULL) { pw_log_trace_fp("%p: recycle buffer %d", stream, b->id); - push_queue(impl, &impl->dequeued, b); - recycled = true; + queue_push(impl, &impl->dequeued, b); } /* pop new buffer */ - if ((b = pop_queue(impl, &impl->queued)) != NULL) { + if ((b = queue_pop(impl, &impl->queued)) != NULL) { impl->drained = false; io->buffer_id = b->id; res = io->status = SPA_STATUS_HAVE_DATA; pw_log_trace_fp("%p: pop %d %p", stream, b->id, io); + /* we have a buffer, if we are not rt and don't follow + * any rate matching and there are no more + * buffers queued and there is a buffer to dequeue, ask for + * more buffers so that we have one in the next round. + * If we are using rate matching we need to wait until the + * rate matching node (audioconvert) has been scheduled to + * update the values. */ + ask_more = !impl->process_rt && impl->rate_match == NULL && + queue_is_empty(impl, &impl->queued) && + !queue_is_empty(impl, &impl->dequeued); } else if (impl->draining || impl->drained) { impl->draining = true; impl->drained = true; @@ -1045,7 +1059,12 @@ io->buffer_id = SPA_ID_INVALID; res = io->status = SPA_STATUS_NEED_DATA; pw_log_trace_fp("%p: no more buffers %p", stream, io); + ask_more = true; } + } else { + ask_more = !impl->process_rt && + queue_is_empty(impl, &impl->queued) && + !queue_is_empty(impl, &impl->dequeued); } copy_position(impl, impl->queued.outcount); @@ -1053,18 +1072,13 @@ if (!impl->draining && !impl->driving) { /* we're not draining, not a driver check if we need to get * more buffers */ - if (!impl->process_rt && (recycled || res == SPA_STATUS_NEED_DATA)) { - /* not realtime and we have a free buffer, trigger process so that we have - * data in the next round. */ - if (update_requested(impl) > 0) - call_process(impl); - } else if (res == SPA_STATUS_NEED_DATA) { - /* realtime and we don't have a buffer, trigger process and try - * again when there is something in the queue now */ + if (ask_more) { if (update_requested(impl) > 0) call_process(impl); - if (impl->draining || - spa_ringbuffer_get_read_index(&impl->queued.ring, &index) > 0) + /* realtime, we can try again now if there is something. + * non-realtime, we will have to try in the next round */ + if (impl->process_rt && + (impl->draining || !queue_is_empty(impl, &impl->queued))) goto again; } } @@ -1155,7 +1169,7 @@ struct control *c; const struct spa_pod *type, *pod; uint32_t iid, choice, n_vals, container = SPA_ID_INVALID; - float *vals, bool_range3 = { 1.0, 0.0, 1.0 }, dbl3; + float *vals, bool_range3 = { 1.0f, 0.0f, 1.0f }, dbl3; if (spa_pod_parse_object(param, SPA_TYPE_OBJECT_PropInfo, NULL, @@ -1279,7 +1293,7 @@ case SPA_TYPE_Bool: if (spa_pod_get_bool(&prop->value, &value.b) < 0) continue; - value.f = value.b ? 1.0 : 0.0; + value.f = value.b ? 1.0f : 0.0f; n_values = 1; values = &value.f; break; @@ -2229,7 +2243,7 @@ struct buffer *b; int res; - if ((b = pop_queue(impl, &impl->dequeued)) == NULL) { + if ((b = queue_pop(impl, &impl->dequeued)) == NULL) { res = -errno; pw_log_trace_fp("%p: no more buffers: %m", stream); errno = -res; @@ -2240,7 +2254,7 @@ if (b->busy && impl->direction == SPA_DIRECTION_OUTPUT) { if (ATOMIC_INC(b->busy->count) > 1) { ATOMIC_DEC(b->busy->count); - push_queue(impl, &impl->dequeued, b); + queue_push(impl, &impl->dequeued, b); pw_log_trace_fp("%p: buffer busy", stream); errno = EBUSY; return NULL; @@ -2260,7 +2274,7 @@ ATOMIC_DEC(b->busy->count); pw_log_trace_fp("%p: queue buffer %d", stream, b->id); - if ((res = push_queue(impl, &impl->queued, b)) < 0) + if ((res = queue_push(impl, &impl->queued, b)) < 0)
View file
pipewire-0.3.54.tar.gz/src/pipewire/stream.h -> pipewire-0.3.56.tar.gz/src/pipewire/stream.h
Changed
@@ -436,9 +436,16 @@ int pw_stream_connect(struct pw_stream *stream, /**< a \ref pw_stream */ enum pw_direction direction, /**< the stream direction */ - uint32_t target_id, /**< the target object id to connect to or - * PW_ID_ANY to let the manager - * select a target. */ + uint32_t target_id, /**< should have the value PW_ID_ANY. + * To select a specific target + * node, specify the + * PW_KEY_OBJECT_SERIAL or the + * PW_KEY_NODE_NAME value of the target + * node in the PW_KEY_TARGET_OBJECT + * property of the stream. + * Specifying target nodes by + * their id is deprecated. + */ enum pw_stream_flags flags, /**< stream flags */ const struct spa_pod **params, /**< an array with params. The params * should ideally contain supported
View file
pipewire-0.3.54.tar.gz/src/tests/meson.build -> pipewire-0.3.56.tar.gz/src/tests/meson.build
Changed
@@ -3,6 +3,7 @@ 'test-interfaces', # 'test-remote', 'test-stream', + 'test-filter', foreach a : test_apps
View file
pipewire-0.3.56.tar.gz/src/tests/test-filter.c
Added
@@ -0,0 +1,375 @@ +/* PipeWire + * + * Copyright © 2022 Wim Taymans + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include <pipewire/pipewire.h> +#include <pipewire/main-loop.h> +#include <pipewire/filter.h> + +#include <spa/utils/string.h> + +#define TEST_FUNC(a,b,func) \ +do { \ + a.func = b.func; \ + spa_assert_se(SPA_PTRDIFF(&a.func, &a) == SPA_PTRDIFF(&b.func, &b)); \ +} while(0) + +static void test_abi(void) +{ + static const struct { + uint32_t version; + void (*destroy) (void *data); + void (*state_changed) (void *data, enum pw_filter_state old, + enum pw_filter_state state, const char *error); + void (*io_changed) (void *data, void *port_data, uint32_t id, void *area, uint32_t size); + void (*param_changed) (void *data, void *port_data, uint32_t id, const struct spa_pod *param); + void (*add_buffer) (void *data, void *port_data, struct pw_buffer *buffer); + void (*remove_buffer) (void *data, void *port_data, struct pw_buffer *buffer); + void (*process) (void *data, struct spa_io_position *position); + void (*drained) (void *data); + void (*command) (void *data, const struct spa_command *command); + } test = { PW_VERSION_FILTER_EVENTS, NULL }; + + struct pw_filter_events ev; + + TEST_FUNC(ev, test, destroy); + TEST_FUNC(ev, test, state_changed); + TEST_FUNC(ev, test, io_changed); + TEST_FUNC(ev, test, param_changed); + TEST_FUNC(ev, test, add_buffer); + TEST_FUNC(ev, test, remove_buffer); + TEST_FUNC(ev, test, process); + TEST_FUNC(ev, test, drained); + TEST_FUNC(ev, test, command); + + spa_assert_se(PW_VERSION_FILTER_EVENTS == 1); + spa_assert_se(sizeof(ev) == sizeof(test)); + + spa_assert_se(PW_FILTER_STATE_ERROR == -1); + spa_assert_se(PW_FILTER_STATE_UNCONNECTED == 0); + spa_assert_se(PW_FILTER_STATE_CONNECTING == 1); + spa_assert_se(PW_FILTER_STATE_PAUSED == 2); + spa_assert_se(PW_FILTER_STATE_STREAMING == 3); + + spa_assert_se(pw_filter_state_as_string(PW_FILTER_STATE_ERROR) != NULL); + spa_assert_se(pw_filter_state_as_string(PW_FILTER_STATE_UNCONNECTED) != NULL); + spa_assert_se(pw_filter_state_as_string(PW_FILTER_STATE_CONNECTING) != NULL); + spa_assert_se(pw_filter_state_as_string(PW_FILTER_STATE_PAUSED) != NULL); + spa_assert_se(pw_filter_state_as_string(PW_FILTER_STATE_STREAMING) != NULL); +} + +static void filter_destroy_error(void *data) +{ + spa_assert_not_reached(); +} +static void filter_state_changed_error(void *data, enum pw_filter_state old, + enum pw_filter_state state, const char *error) +{ + spa_assert_not_reached(); +} +static void filter_io_changed_error(void *data, void *port_data, uint32_t id, void *area, uint32_t size) +{ + spa_assert_not_reached(); +} +static void filter_param_changed_error(void *data, void *port_data, uint32_t id, const struct spa_pod *format) +{ + spa_assert_not_reached(); +} +static void filter_add_buffer_error(void *data, void *port_data, struct pw_buffer *buffer) +{ + spa_assert_not_reached(); +} +static void filter_remove_buffer_error(void *data, void *port_data, struct pw_buffer *buffer) +{ + spa_assert_not_reached(); +} +static void filter_process_error(void *data, struct spa_io_position *position) +{ + spa_assert_not_reached(); +} +static void filter_drained_error(void *data) +{ + spa_assert_not_reached(); +} + +static const struct pw_filter_events filter_events_error = +{ + PW_VERSION_FILTER_EVENTS, + .destroy = filter_destroy_error, + .state_changed = filter_state_changed_error, + .io_changed = filter_io_changed_error, + .param_changed = filter_param_changed_error, + .add_buffer = filter_add_buffer_error, + .remove_buffer = filter_remove_buffer_error, + .process = filter_process_error, + .drained = filter_drained_error +}; + +static int destroy_count = 0; +static void filter_destroy_count(void *data) +{ + destroy_count++; +} +static void test_create(void) +{ + struct pw_main_loop *loop; + struct pw_context *context; + struct pw_core *core; + struct pw_filter *filter; + struct pw_filter_events filter_events = filter_events_error; + struct spa_hook listener = { 0, }; + const char *error = NULL; + + loop = pw_main_loop_new(NULL); + context = pw_context_new(pw_main_loop_get_loop(loop), NULL, 12); + spa_assert_se(context != NULL); + core = pw_context_connect_self(context, NULL, 0); + spa_assert_se(core != NULL); + filter = pw_filter_new(core, "test", NULL); + spa_assert_se(filter != NULL); + pw_filter_add_listener(filter, &listener, &filter_events, filter); + + /* check state */ + spa_assert_se(pw_filter_get_state(filter, &error) == PW_FILTER_STATE_UNCONNECTED); + spa_assert_se(error == NULL); + /* check name */ + spa_assert_se(spa_streq(pw_filter_get_name(filter), "test")); + + /* check id, only when connected */ + spa_assert_se(pw_filter_get_node_id(filter) == SPA_ID_INVALID); + + /* check destroy */ + destroy_count = 0; + filter_events.destroy = filter_destroy_count; + pw_filter_destroy(filter); + spa_assert_se(destroy_count == 1); + + pw_context_destroy(context); + pw_main_loop_destroy(loop); +} + +static void test_properties(void) +{ + struct pw_main_loop *loop; + struct pw_context *context; + struct pw_core *core; + const struct pw_properties *props; + struct pw_filter *filter; + struct pw_filter_events filter_events = filter_events_error; + struct spa_hook listener = { { NULL }, }; + struct spa_dict_item items3; + + loop = pw_main_loop_new(NULL); + context = pw_context_new(pw_main_loop_get_loop(loop), NULL, 12); + spa_assert_se(context != NULL); + core = pw_context_connect_self(context, NULL, 0); + spa_assert_se(core != NULL); + filter = pw_filter_new(core, "test", + pw_properties_new("foo", "bar", + "biz", "fuzz", + NULL)); + spa_assert_se(filter != NULL); + pw_filter_add_listener(filter, &listener, &filter_events, filter); + + props = pw_filter_get_properties(filter, NULL); + spa_assert_se(props != NULL); + spa_assert_se(spa_streq(pw_properties_get(props, "foo"), "bar")); + spa_assert_se(spa_streq(pw_properties_get(props, "biz"), "fuzz")); + spa_assert_se(pw_properties_get(props, "buzz") == NULL); +
View file
pipewire-0.3.54.tar.gz/src/tests/test-stream.c -> pipewire-0.3.56.tar.gz/src/tests/test-stream.c
Changed
@@ -251,5 +251,7 @@ test_create(); test_properties(); + pw_deinit(); + return 0; }
View file
pipewire-0.3.54.tar.gz/src/tools/pw-cat.c -> pipewire-0.3.56.tar.gz/src/tools/pw-cat.c
Changed
@@ -1439,9 +1439,7 @@ case OPT_VOLUME: data.volume = atof(optarg); break; - default: - fprintf(stderr, "error: unknown option '%c'\n", c); goto error_usage; } }
View file
pipewire-0.3.54.tar.gz/test/test-spa-json.c -> pipewire-0.3.56.tar.gz/test/test-spa-json.c
Changed
@@ -86,7 +86,7 @@ { const char *value; int len; - float f; + float f = 0.0f; pwtest_int_gt((len = spa_json_next(it, &value)), 0); check_type(TYPE_FLOAT, value, len); pwtest_int_gt(spa_json_parse_float(value, len, &f), 0); @@ -281,6 +281,36 @@ return PWTEST_PASS; } +PWTEST(json_float_check) +{ + struct { + const char *str; + int res; + } val = { + { "0.0", 1 }, + { ".0", 1 }, + { "+.0E0", 1 }, + { "-.0e0", 1 }, + + { "0,0", 0 }, + { "0.0.5", 0 }, + { "0x0", 0 }, + { "0x0.0", 0 }, + { "E10", 0 }, + { "e20", 0 }, + { " 0.0", 0 }, + { "0.0 ", 0 }, + { " 0.0 ", 0 }, + }; + unsigned i; + float v; + + for (i = 0; i < SPA_N_ELEMENTS(val); i++) { + pwtest_int_eq(spa_json_parse_float(vali.str, strlen(vali.str), &v), vali.res); + } + return PWTEST_PASS; +} + PWTEST(json_int) { int v; @@ -296,6 +326,7 @@ pwtest_add(json_array, PWTEST_NOARG); pwtest_add(json_overflow, PWTEST_NOARG); pwtest_add(json_float, PWTEST_NOARG); + pwtest_add(json_float_check, PWTEST_NOARG); pwtest_add(json_int, PWTEST_NOARG); return PWTEST_PASS;
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
.