Projects
Essentials
pipewire-aptx
Sign Up
Log In
Username
Password
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
Expand all
Collapse all
Changes of Revision 16
View file
pipewire-aptx.changes
Changed
@@ -1,4 +1,9 @@ ------------------------------------------------------------------- +Sat Oct 15 16:39:17 UTC 2022 - Bjørn Lie <zaitor@opensuse.org> + +- Update to version 0.3.59 + +------------------------------------------------------------------- Sun Sep 18 13:29:55 UTC 2022 - Bjørn Lie <zaitor@opensuse.org> - Update to version 0.3.58
View file
pipewire-aptx.spec
Changed
@@ -7,7 +7,7 @@ %define soversion 0_2 Name: pipewire-aptx -Version: 0.3.58 +Version: 0.3.59 Release: 0 Summary: PipeWire Bluetooth aptX codec plugin License: MIT
View file
pipewire-0.3.58.tar.gz/spa/plugins/audioconvert/resample-peaks-c.c
Deleted
@@ -1,37 +0,0 @@ -/* 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" - -static inline float find_abs_max_c(const float *s, uint32_t n_samples, float m) -{ - uint32_t n; - for (n = 0; n < n_samples; n++) - m = fmaxf(fabsf(sn), m); - return m; -} - -MAKE_PEAKS(c);
View file
pipewire-0.3.58.tar.gz/spa/plugins/audioconvert/resample-peaks-impl.h
Deleted
@@ -1,92 +0,0 @@ -/* Spa - * - * Copyright © 2020 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 <spa/utils/defs.h> - -#include "resample.h" - -struct peaks_data { - uint32_t o_count; - uint32_t i_count; - float max_f; -}; - -#define DEFINE_PEAKS(arch) \ -void resample_peaks_process_##arch(struct resample *r, \ - const void * SPA_RESTRICT src, uint32_t *in_len, \ - void * SPA_RESTRICT dst, uint32_t *out_len) - -#define MAKE_PEAKS(arch) \ -DEFINE_PEAKS(arch) \ -{ \ - struct peaks_data *pd = r->data; \ - uint32_t c, i, o, end, chunk, i_count, o_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); \ - \ - m = find_abs_max_##arch(&si, chunk - i, m); \ - \ - i += chunk; \ - \ - 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; \ - } \ -} - - -DEFINE_PEAKS(c); -#if defined (HAVE_SSE) -DEFINE_PEAKS(sse); -#endif
View file
pipewire-0.3.58.tar.gz/spa/plugins/audioconvert/resample-peaks-sse.c
Deleted
@@ -1,64 +0,0 @@ -/* 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 <xmmintrin.h> - -#include "resample-peaks-impl.h" - -static inline float hmax_ps(__m128 val) -{ - __m128 t = _mm_movehl_ps(val, val); - t = _mm_max_ps(t, val); - val = _mm_shuffle_ps(t, t, 0x55); - val = _mm_max_ss(t, val); - return _mm_cvtss_f32(val); -} - -static inline float find_abs_max_sse(const float *s, uint32_t n_samples, float m) -{ - __m128 in2, max; - uint32_t n, unrolled; - const __m128 mask = _mm_set1_ps(-0.0f); - - max = _mm_set1_ps(m); - - unrolled = n_samples & ~7; - - for (n = 0; n < unrolled; n += 8) { - in0 = _mm_loadu_ps(&sn + 0); - in1 = _mm_loadu_ps(&sn + 4); - in0 = _mm_andnot_ps(mask, in0); - in1 = _mm_andnot_ps(mask, in1); - max = _mm_max_ps(max, in0); - max = _mm_max_ps(max, in1); - } - for (; n < n_samples; n++) - m = fmaxf(fabsf(sn), m); - - return fmaxf(hmax_ps(max), m); -} - -MAKE_PEAKS(sse);
View file
pipewire-0.3.58.tar.gz/spa/plugins/bluez5/a2dp-codecs.c
Deleted
@@ -1,209 +0,0 @@ -/* - * BlueALSA - bluez-a2dp.c - * Copyright (c) 2016-2017 Arkadiusz Bokowy - * - * This file is a part of bluez-alsa. - * - * This project is licensed under the terms of the MIT license. - * - */ - -#include <spa/utils/string.h> - -#include "a2dp-codecs.h" - -int a2dp_codec_select_config(const struct a2dp_codec_config configs, size_t n, - uint32_t cap, int preferred_value) -{ - size_t i; - int *scores, res; - unsigned int max_priority; - - if (n == 0) - return -EINVAL; - - scores = calloc(n, sizeof(int)); - if (scores == NULL) - return -errno; - - max_priority = configs0.priority; - for (i = 1; i < n; ++i) { - if (configsi.priority > max_priority) - max_priority = configsi.priority; - } - - for (i = 0; i < n; ++i) { - if (!(configsi.config & cap)) { - scoresi = -1; - continue; - } - if (configsi.value == preferred_value) - scoresi = 100 * (max_priority + 1); - else if (configsi.value > preferred_value) - scoresi = 10 * (max_priority + 1); - else - scoresi = 1; - - scoresi *= configsi.priority + 1; - } - - res = 0; - for (i = 1; i < n; ++i) { - if (scoresi > scoresres) - res = i; - } - - if (scoresres < 0) - res = -EINVAL; - - free(scores); - return res; -} - -bool a2dp_codec_check_caps(const struct a2dp_codec *codec, unsigned int codec_id, - const void *caps, size_t caps_size, - const struct a2dp_codec_audio_info *info, - const struct spa_dict *global_settings) -{ - uint8_t configA2DP_MAX_CAPS_SIZE; - int res; - - if (codec_id != codec->codec_id) - return false; - - if (caps == NULL) - return false; - - res = codec->select_config(codec, 0, caps, caps_size, info, global_settings, config); - if (res < 0) - return false; - - return ((size_t)res == caps_size); -} - -#ifdef CODEC_PLUGIN - -struct impl { - struct spa_handle handle; - struct spa_bluez5_codec_a2dp bluez5_codec_a2dp; -}; - -static int -impl_get_interface(struct spa_handle *handle, const char *type, void **interface) -{ - struct impl *this; - - spa_return_val_if_fail(handle != NULL, -EINVAL); - spa_return_val_if_fail(interface != NULL, -EINVAL); - - this = (struct impl *) handle; - - if (spa_streq(type, SPA_TYPE_INTERFACE_Bluez5CodecA2DP)) - *interface = &this->bluez5_codec_a2dp; - else - return -ENOENT; - - return 0; -} - -static int -impl_clear(struct spa_handle *handle) -{ - spa_return_val_if_fail(handle != NULL, -EINVAL); - return 0; -} - -static size_t -impl_get_size(const struct spa_handle_factory *factory, const struct spa_dict *params) -{ - return sizeof(struct impl); -} - -static int -impl_init(const struct spa_handle_factory *factory, - struct spa_handle *handle, - const struct spa_dict *info, - const struct spa_support *support, - uint32_t n_support) -{ - struct impl *this; - - spa_return_val_if_fail(factory != NULL, -EINVAL); - spa_return_val_if_fail(handle != NULL, -EINVAL); - - handle->get_interface = impl_get_interface; - handle->clear = impl_clear; - - this = (struct impl *) handle; - - this->bluez5_codec_a2dp.codecs = codec_plugin_a2dp_codecs; - this->bluez5_codec_a2dp.iface = SPA_INTERFACE_INIT( - SPA_TYPE_INTERFACE_Bluez5CodecA2DP, - SPA_VERSION_BLUEZ5_CODEC_A2DP, - NULL, - this); - - return 0; -} - -static const struct spa_interface_info impl_interfaces = { - {SPA_TYPE_INTERFACE_Bluez5CodecA2DP,}, -}; - -static int -impl_enum_interface_info(const struct spa_handle_factory *factory, - const struct spa_interface_info **info, - uint32_t *index) -{ - spa_return_val_if_fail(factory != NULL, -EINVAL); - spa_return_val_if_fail(info != NULL, -EINVAL); - spa_return_val_if_fail(index != NULL, -EINVAL); - - switch (*index) { - case 0: - *info = &impl_interfaces*index; - break; - default: - return 0; - } - (*index)++; - - return 1; -} - -static const struct spa_dict_item handle_info_items = { - { SPA_KEY_FACTORY_DESCRIPTION, "Bluetooth codec plugin" }, -}; - -static const struct spa_dict handle_info = SPA_DICT_INIT_ARRAY(handle_info_items); - -static struct spa_handle_factory handle_factory = { - SPA_VERSION_HANDLE_FACTORY, - NULL, - &handle_info, - impl_get_size, - impl_init, - impl_enum_interface_info, -}; - -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); - - if (handle_factory.name == NULL) - handle_factory.name = codec_plugin_factory_name; - - switch (*index) { - case 0: - *factory = &handle_factory; - break; - default: - return 0; - } - (*index)++; - return 1; -} - -#endif
View file
pipewire-0.3.58.tar.gz/spa/plugins/bluez5/a2dp-codecs.h
Deleted
@@ -1,167 +0,0 @@ -/* Spa A2DP codec API - * - * Copyright © 2020 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_BLUEZ5_A2DP_CODECS_H_ -#define SPA_BLUEZ5_A2DP_CODECS_H_ - -#include <stdint.h> -#include <stddef.h> - -#include <spa/param/audio/format.h> -#include <spa/param/bluetooth/audio.h> -#include <spa/utils/names.h> -#include <spa/support/plugin.h> -#include <spa/pod/pod.h> -#include <spa/pod/builder.h> -#include <spa/support/log.h> - -#include "a2dp-codec-caps.h" - -/* - * The codec plugin SPA interface is private. The version should be incremented - * when any of the structs or semantics change. - */ - -#define SPA_TYPE_INTERFACE_Bluez5CodecA2DP SPA_TYPE_INFO_INTERFACE_BASE "Bluez5:Codec:A2DP:Private" - -#define SPA_VERSION_BLUEZ5_CODEC_A2DP 5 - -struct spa_bluez5_codec_a2dp { - struct spa_interface iface; - const struct a2dp_codec * const *codecs; /**< NULL terminated array */ -}; - -#define A2DP_CODEC_FACTORY_NAME(basename) (SPA_NAME_API_CODEC_BLUEZ5_A2DP "." basename) - -#ifdef CODEC_PLUGIN -#define A2DP_CODEC_EXPORT_DEF(basename,...) \ - const char *codec_plugin_factory_name = A2DP_CODEC_FACTORY_NAME(basename); \ - static const struct a2dp_codec * const codec_plugin_a2dp_codec_list = { __VA_ARGS__, NULL }; \ - const struct a2dp_codec * const * const codec_plugin_a2dp_codecs = codec_plugin_a2dp_codec_list; - -extern const struct a2dp_codec * const * const codec_plugin_a2dp_codecs; -extern const char *codec_plugin_factory_name; -#endif - -#define A2DP_CODEC_FLAG_SINK (1 << 0) - -#define A2DP_CODEC_DEFAULT_RATE 48000 -#define A2DP_CODEC_DEFAULT_CHANNELS 2 - -enum { - NEED_FLUSH_NO = 0, - NEED_FLUSH_ALL = 1, - NEED_FLUSH_FRAGMENT = 2, -}; - -struct a2dp_codec_audio_info { - uint32_t rate; - uint32_t channels; -}; - -struct a2dp_codec { - enum spa_bluetooth_audio_codec id; - uint8_t codec_id; - a2dp_vendor_codec_t vendor; - - const char *name; - const char *description; - const char *endpoint_name; /**< Endpoint name. If NULL, same as name */ - const struct spa_dict *info; - - const size_t send_buf_size; - - const struct a2dp_codec *duplex_codec; /**< Codec for non-standard A2DP duplex channel */ - - int (*fill_caps) (const struct a2dp_codec *codec, uint32_t flags, - uint8_t capsA2DP_MAX_CAPS_SIZE); - int (*select_config) (const struct a2dp_codec *codec, uint32_t flags, - const void *caps, size_t caps_size, - const struct a2dp_codec_audio_info *info, - const struct spa_dict *global_settings, uint8_t configA2DP_MAX_CAPS_SIZE); - int (*enum_config) (const struct a2dp_codec *codec, uint32_t flags, - const void *caps, size_t caps_size, uint32_t id, uint32_t idx, - struct spa_pod_builder *builder, struct spa_pod **param); - int (*validate_config) (const struct a2dp_codec *codec, uint32_t flags, - const void *caps, size_t caps_size, - struct spa_audio_info *info); - - /** qsort comparison sorting caps in order of preference for the codec. - * Used in codec switching to select best remote endpoints. - * The caps handed in correspond to this codec_id, but are - * otherwise not checked beforehand. - */ - int (*caps_preference_cmp) (const struct a2dp_codec *codec, uint32_t flags, const void *caps1, size_t caps1_size, - const void *caps2, size_t caps2_size, const struct a2dp_codec_audio_info *info, - const struct spa_dict *global_settings); - - void *(*init_props) (const struct a2dp_codec *codec, uint32_t flags, const struct spa_dict *settings); - void (*clear_props) (void *); - int (*enum_props) (void *props, const struct spa_dict *settings, uint32_t id, uint32_t idx, - struct spa_pod_builder *builder, struct spa_pod **param); - int (*set_props) (void *props, const struct spa_pod *param); - - void *(*init) (const struct a2dp_codec *codec, uint32_t flags, void *config, size_t config_size, - const struct spa_audio_info *info, void *props, size_t mtu); - void (*deinit) (void *data); - - int (*update_props) (void *data, void *props); - - int (*get_block_size) (void *data); - - int (*abr_process) (void *data, size_t unsent); - - int (*start_encode) (void *data, - void *dst, size_t dst_size, uint16_t seqnum, uint32_t timestamp); - int (*encode) (void *data, - const void *src, size_t src_size, - void *dst, size_t dst_size, - size_t *dst_out, int *need_flush); - - int (*start_decode) (void *data, - const void *src, size_t src_size, uint16_t *seqnum, uint32_t *timestamp); - int (*decode) (void *data, - const void *src, size_t src_size, - void *dst, size_t dst_size, - size_t *dst_out); - - int (*reduce_bitpool) (void *data); - int (*increase_bitpool) (void *data); - - void (*set_log) (struct spa_log *global_log); -}; - -struct a2dp_codec_config { - uint32_t config; - int value; - unsigned int priority; -}; - -int a2dp_codec_select_config(const struct a2dp_codec_config configs, size_t n, - uint32_t cap, int preferred_value); - -bool a2dp_codec_check_caps(const struct a2dp_codec *codec, unsigned int codec_id, - const void *caps, size_t caps_size, const struct a2dp_codec_audio_info *info, - const struct spa_dict *global_settings); - -#endif
View file
pipewire-0.3.58.tar.gz/spa/plugins/bluez5/a2dp-sink.c
Deleted
@@ -1,1786 +0,0 @@ -/* Spa A2DP Sink - * - * 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 <unistd.h> -#include <stddef.h> -#include <stdio.h> -#include <arpa/inet.h> -#include <sys/ioctl.h> - -#include <spa/support/plugin.h> -#include <spa/support/loop.h> -#include <spa/support/log.h> -#include <spa/support/system.h> -#include <spa/utils/list.h> -#include <spa/utils/keys.h> -#include <spa/utils/names.h> -#include <spa/utils/result.h> -#include <spa/utils/string.h> -#include <spa/monitor/device.h> - -#include <spa/node/node.h> -#include <spa/node/utils.h> -#include <spa/node/io.h> -#include <spa/node/keys.h> -#include <spa/param/param.h> -#include <spa/param/latency-utils.h> -#include <spa/param/audio/format.h> -#include <spa/param/audio/format-utils.h> -#include <spa/pod/filter.h> - -#include <sbc/sbc.h> - -#include "defs.h" -#include "rtp.h" -#include "a2dp-codecs.h" - -static struct spa_log_topic log_topic = SPA_LOG_TOPIC(0, "spa.bluez5.sink.a2dp"); -#undef SPA_LOG_TOPIC_DEFAULT -#define SPA_LOG_TOPIC_DEFAULT &log_topic - -#define DEFAULT_CLOCK_NAME "clock.system.monotonic" - -struct props { - uint32_t min_latency; - uint32_t max_latency; - int64_t latency_offset; - char clock_name64; -}; - -#define FILL_FRAMES 2 -#define MAX_BUFFERS 32 -#define MIN_LATENCY 128 -#define MAX_LATENCY 8192 -#define BUFFER_SIZE (MAX_LATENCY*8) - -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; -}; - -struct port { - struct spa_audio_info current_format; - uint32_t frame_size; - unsigned int have_format:1; - - uint64_t info_all; - struct spa_port_info info; - struct spa_io_buffers *io; - struct spa_latency_info latency; -#define IDX_EnumFormat 0 -#define IDX_Meta 1 -#define IDX_IO 2 -#define IDX_Format 3 -#define IDX_Buffers 4 -#define IDX_Latency 5 -#define N_PORT_PARAMS 6 - struct spa_param_info paramsN_PORT_PARAMS; - - struct buffer buffersMAX_BUFFERS; - uint32_t n_buffers; - - struct spa_list free; - struct spa_list ready; - - size_t ready_offset; -}; - -struct impl { - struct spa_handle handle; - struct spa_node node; - - struct spa_log *log; - struct spa_loop *data_loop; - struct spa_system *data_system; - - struct spa_hook_list hooks; - struct spa_callbacks callbacks; - - uint64_t info_all; - struct spa_node_info info; -#define IDX_PropInfo 0 -#define IDX_Props 1 -#define N_NODE_PARAMS 2 - struct spa_param_info paramsN_NODE_PARAMS; - struct props props; - - struct spa_bt_transport *transport; - struct spa_hook transport_listener; - - struct port port; - - unsigned int started:1; - unsigned int following:1; - - unsigned int is_duplex:1; - - struct spa_source source; - int timerfd; - struct spa_source flush_source; - struct spa_source flush_timer_source; - int flush_timerfd; - - struct spa_io_clock *clock; - struct spa_io_position *position; - - uint64_t current_time; - uint64_t next_time; - uint64_t last_error; - - const struct a2dp_codec *codec; - bool codec_props_changed; - void *codec_props; - void *codec_data; - struct spa_audio_info codec_format; - - int need_flush; - bool fragment; - uint64_t fragment_timeout; - uint32_t block_size; - uint8_t bufferBUFFER_SIZE; - uint32_t buffer_used; - uint32_t header_size; - uint32_t frame_count; - uint16_t seqnum; - uint32_t timestamp; - uint64_t sample_count; - uint8_t tmp_bufferBUFFER_SIZE; - uint32_t tmp_buffer_used; - uint32_t fd_buffer_size; -}; - -#define CHECK_PORT(this,d,p) ((d) == SPA_DIRECTION_INPUT && (p) == 0) - -static void reset_props(struct impl *this, struct props *props) -{ - props->min_latency = MIN_LATENCY; - props->max_latency = MAX_LATENCY; - props->latency_offset = 0; - strncpy(props->clock_name, DEFAULT_CLOCK_NAME, sizeof(props->clock_name)); -} - -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 impl *this = object; - struct spa_pod *param; - struct spa_pod_builder b = { 0 }; - uint8_t buffer1024; - struct spa_result_node_params result; - uint32_t count = 0, index_offset = 0; - bool enum_codec = false; - - 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: - { - struct props *p = &this->props; - - switch (result.index) { - case 0: - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_PropInfo, id, - SPA_PROP_INFO_id, SPA_POD_Id(SPA_PROP_minLatency), - SPA_PROP_INFO_description, SPA_POD_String("The minimum latency"), - SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Int(p->min_latency, 1, INT32_MAX)); - break; - case 1: - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_PropInfo, id, - SPA_PROP_INFO_id, SPA_POD_Id(SPA_PROP_maxLatency), - SPA_PROP_INFO_description, SPA_POD_String("The maximum latency"), - SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Int(p->max_latency, 1, INT32_MAX)); - break; - case 2: - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_PropInfo, id, - SPA_PROP_INFO_id, SPA_POD_Id(SPA_PROP_latencyOffsetNsec), - SPA_PROP_INFO_description, SPA_POD_String("Latency offset (ns)"), - SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Long(0LL, INT64_MIN, INT64_MAX)); - break; - default: - enum_codec = true; - index_offset = 3; - } - break; - } - case SPA_PARAM_Props: - { - struct props *p = &this->props; - - switch (result.index) { - case 0: - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_Props, id, - SPA_PROP_minLatency, SPA_POD_Int(p->min_latency), - SPA_PROP_maxLatency, SPA_POD_Int(p->max_latency), - SPA_PROP_latencyOffsetNsec, SPA_POD_Long(p->latency_offset)); - break; - default: - enum_codec = true; - index_offset = 1; - } - break; - } - default: - return -ENOENT; - } - - if (enum_codec) { - int res; - if (this->codec->enum_props == NULL || this->codec_props == NULL || - this->transport == NULL) - return 0; - else if ((res = this->codec->enum_props(this->codec_props, - this->transport->device->settings, - id, result.index - index_offset, &b, ¶m)) != 1) - return res; - } - - 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; -} - -static int set_timeout(struct impl *this, uint64_t time) -{ - struct itimerspec ts; - ts.it_value.tv_sec = time / SPA_NSEC_PER_SEC; - ts.it_value.tv_nsec = time % SPA_NSEC_PER_SEC; - ts.it_interval.tv_sec = 0; - ts.it_interval.tv_nsec = 0; - return spa_system_timerfd_settime(this->data_system, - this->timerfd, SPA_FD_TIMER_ABSTIME, &ts, NULL); -} - -static int set_timers(struct impl *this) -{ - struct timespec now; - - spa_system_clock_gettime(this->data_system, CLOCK_MONOTONIC, &now); - this->next_time = SPA_TIMESPEC_TO_NSEC(&now); - - return set_timeout(this, this->following ? 0 : this->next_time); -} - -static int do_reassign_follower(struct spa_loop *loop, - bool async, - uint32_t seq, - const void *data, - size_t size, - void *user_data) -{ - struct impl *this = user_data; - set_timers(this); - return 0; -} - -static inline bool is_following(struct impl *this) -{ - return this->position && this->clock && this->position->clock.id != this->clock->id; -} - -static int impl_node_set_io(void *object, uint32_t id, void *data, size_t size) -{ - struct impl *this = object; - bool following; - - spa_return_val_if_fail(this != NULL, -EINVAL); - - switch (id) { - case SPA_IO_Clock: - this->clock = data; - if (this->clock != NULL) { - spa_scnprintf(this->clock->name, - sizeof(this->clock->name), - "%s", this->props.clock_name); - } - break; - case SPA_IO_Position: - this->position = data; - break; - default: - return -ENOENT; - } - - following = is_following(this); - if (this->started && following != this->following) { - spa_log_debug(this->log, "%p: reassign follower %d->%d", this, this->following, following); - this->following = following; - spa_loop_invoke(this->data_loop, do_reassign_follower, 0, NULL, 0, true, this); - } - return 0; -} - -static void emit_node_info(struct impl *this, bool full); - -static void emit_port_info(struct impl *this, struct port *port, bool full); - -static void set_latency(struct impl *this, bool emit_latency) -{ - struct port *port = &this->port; - int64_t delay; - - if (this->transport == NULL) - return; - - delay = spa_bt_transport_get_delay_nsec(this->transport); - delay += SPA_CLAMP(this->props.latency_offset, -delay, INT64_MAX / 2); - port->latency.min_ns = port->latency.max_ns = delay; - - if (emit_latency) { - port->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS; - port->paramsIDX_Latency.flags ^= SPA_PARAM_INFO_SERIAL; - emit_port_info(this, port, false); - } -} - -static int apply_props(struct impl *this, const struct spa_pod *param) -{ - struct props new_props = this->props; - int changed = 0; - - if (param == NULL) { - reset_props(this, &new_props); - } else { - spa_pod_parse_object(param, - SPA_TYPE_OBJECT_Props, NULL, - SPA_PROP_minLatency, SPA_POD_OPT_Int(&new_props.min_latency), - SPA_PROP_maxLatency, SPA_POD_OPT_Int(&new_props.max_latency), - SPA_PROP_latencyOffsetNsec, SPA_POD_OPT_Long(&new_props.latency_offset)); - } - - changed = (memcmp(&new_props, &this->props, sizeof(struct props)) != 0); - this->props = new_props; - - if (changed) - set_latency(this, true); - - return changed; -} - -static int impl_node_set_param(void *object, uint32_t id, uint32_t flags, - const struct spa_pod *param) -{ - struct impl *this = object; - - spa_return_val_if_fail(this != NULL, -EINVAL); - - switch (id) { - case SPA_PARAM_Props: - { - int res, codec_res = 0; - res = apply_props(this, param); - if (this->codec_props && this->codec->set_props) { - codec_res = this->codec->set_props(this->codec_props, param); - if (codec_res > 0) - this->codec_props_changed = true; - } - if (res > 0 || codec_res > 0) { - this->info.change_mask |= SPA_NODE_CHANGE_MASK_PARAMS; - this->paramsIDX_Props.flags ^= SPA_PARAM_INFO_SERIAL; - emit_node_info(this, false); - } - break; - } - default: - return -ENOENT; - } - - return 0; -} - -static int reset_buffer(struct impl *this) -{ - if (this->codec_props_changed && this->codec_props - && this->codec->update_props) { - this->codec->update_props(this->codec_data, this->codec_props); - this->codec_props_changed = false; - } - this->need_flush = 0; - this->frame_count = 0; - this->fragment = false; - this->buffer_used = this->codec->start_encode(this->codec_data, - this->buffer, sizeof(this->buffer), - this->seqnum++, this->timestamp); - this->header_size = this->buffer_used; - this->timestamp = this->sample_count; - return 0; -} - -static int get_transport_unused_size(struct impl *this) -{ - int res, value; - res = ioctl(this->flush_source.fd, TIOCOUTQ, &value); - if (res < 0) { - spa_log_error(this->log, "%p: ioctl fail: %m", this); - return -errno; - } - spa_log_trace(this->log, "%p: fd unused buffer size:%d/%d", this, value, this->fd_buffer_size); - return value; -} - -static int send_buffer(struct impl *this) -{ - int written, unsent; - unsent = get_transport_unused_size(this); - if (unsent >= 0) { - unsent = this->fd_buffer_size - unsent; - this->codec->abr_process(this->codec_data, unsent); - } - - spa_log_trace(this->log, "%p: send %d %u %u %u %u", - this, this->frame_count, this->block_size, this->seqnum, - this->timestamp, this->buffer_used); - - written = send(this->flush_source.fd, this->buffer, - this->buffer_used, MSG_DONTWAIT | MSG_NOSIGNAL); - - spa_log_trace(this->log, "%p: send %d", this, written); - - if (written < 0) { - spa_log_debug(this->log, "%p: %m", this); - return -errno; - } - - return written; -} - -static int encode_buffer(struct impl *this, const void *data, uint32_t size) -{ - int processed; - size_t out_encoded; - struct port *port = &this->port; - const void *from_data = data; - int from_size = size; - - spa_log_trace(this->log, "%p: encode %d used %d, %d %d %d", - this, size, this->buffer_used, port->frame_size, this->block_size, - this->frame_count); - - if (this->need_flush) - return 0; - - if (this->buffer_used >= sizeof(this->buffer)) - return -ENOSPC; - - if (size < this->block_size - this->tmp_buffer_used) { - memcpy(this->tmp_buffer + this->tmp_buffer_used, data, size); - this->tmp_buffer_used += size; - return size; - } else if (this->tmp_buffer_used > 0) { - memcpy(this->tmp_buffer + this->tmp_buffer_used, data, this->block_size - this->tmp_buffer_used); - from_data = this->tmp_buffer; - from_size = this->block_size; - this->tmp_buffer_used = this->block_size - this->tmp_buffer_used; - } - - processed = this->codec->encode(this->codec_data, - from_data, from_size, - this->buffer + this->buffer_used, - sizeof(this->buffer) - this->buffer_used, - &out_encoded, &this->need_flush); - if (processed < 0) - return processed; - - this->sample_count += processed / port->frame_size; - this->frame_count += processed / this->block_size; - this->buffer_used += out_encoded; - - spa_log_trace(this->log, "%p: processed %d %zd used %d", - this, processed, out_encoded, this->buffer_used); - - if (this->tmp_buffer_used) { - processed = this->tmp_buffer_used; - this->tmp_buffer_used = 0; - } - return processed; -} - -static int encode_fragment(struct impl *this) -{ - int res; - size_t out_encoded; - struct port *port = &this->port; - - spa_log_trace(this->log, "%p: encode fragment used %d, %d %d %d", - this, this->buffer_used, port->frame_size, this->block_size, - this->frame_count); - - if (this->need_flush) - return 0; - - res = this->codec->encode(this->codec_data, - NULL, 0, - this->buffer + this->buffer_used, - sizeof(this->buffer) - this->buffer_used, - &out_encoded, &this->need_flush); - if (res < 0) - return res; - if (res != 0) - return -EINVAL; - - this->buffer_used += out_encoded; - - spa_log_trace(this->log, "%p: processed fragment %zd used %d", - this, out_encoded, this->buffer_used); - - return 0; -} - -static int flush_buffer(struct impl *this) -{ - spa_log_trace(this->log, "%p: used:%d block_size:%d", this, - this->buffer_used, this->block_size); - - if (this->need_flush) - return send_buffer(this); - - return 0; -} - -static int add_data(struct impl *this, const void *data, uint32_t size) -{ - int processed, total = 0; - - while (size > 0) { - processed = encode_buffer(this, data, size); - - if (processed <= 0) - return total > 0 ? total : processed; - - data = SPA_PTROFF(data, processed, void); - size -= processed; - total += processed; - } - return total; -} - -static void enable_flush(struct impl *this, bool enabled, uint64_t timeout) -{ - bool flush_enabled = enabled && (timeout == 0); - struct itimerspec ts; - - if (SPA_FLAG_IS_SET(this->flush_source.mask, SPA_IO_OUT) != flush_enabled) { - SPA_FLAG_UPDATE(this->flush_source.mask, SPA_IO_OUT, flush_enabled); - spa_loop_update_source(this->data_loop, &this->flush_source); - } - - if (!enabled) - timeout = 0; - - ts.it_value.tv_sec = timeout / SPA_NSEC_PER_SEC; - ts.it_value.tv_nsec = timeout % SPA_NSEC_PER_SEC; - ts.it_interval.tv_sec = 0; - ts.it_interval.tv_nsec = 0; - spa_system_timerfd_settime(this->data_system, - this->flush_timerfd, 0, &ts, NULL); -} - -static int flush_data(struct impl *this, uint64_t now_time) -{ - int written; - uint32_t total_frames; - struct port *port = &this->port; - - if (!this->flush_source.loop) { - /* I/O in error state */ - return -EIO; - } - - total_frames = 0; -again: - written = 0; - if (this->fragment && !this->need_flush) { - int res; - this->fragment = false; - if ((res = encode_fragment(this)) < 0) { - /* Error */ - reset_buffer(this); - return res; - } - } - while (!spa_list_is_empty(&port->ready) && !this->need_flush) { - uint8_t *src; - uint32_t n_bytes, n_frames; - struct buffer *b; - struct spa_data *d; - uint32_t index, offs, avail, l0, l1; - - b = spa_list_first(&port->ready, struct buffer, link); - d = b->buf->datas; - - src = d0.data; - - index = d0.chunk->offset + port->ready_offset; - avail = d0.chunk->size - port->ready_offset; - avail /= port->frame_size; - - offs = index % d0.maxsize; - n_frames = avail; - n_bytes = n_frames * port->frame_size; - - l0 = SPA_MIN(n_bytes, d0.maxsize - offs); - l1 = n_bytes - l0; - - written = add_data(this, src + offs, l0); - if (written > 0 && l1 > 0) - written += add_data(this, src, l1); - if (written <= 0) { - if (written < 0 && written != -ENOSPC) { - spa_list_remove(&b->link); - SPA_FLAG_SET(b->flags, BUFFER_FLAG_OUT); - this->port.io->buffer_id = b->id; - spa_log_warn(this->log, "%p: error %s, reuse buffer %u", - this, spa_strerror(written), b->id); - spa_node_call_reuse_buffer(&this->callbacks, 0, b->id); - port->ready_offset = 0; - } - break; - } - - n_frames = written / port->frame_size; - - port->ready_offset += written; - - if (port->ready_offset >= d0.chunk->size) { - spa_list_remove(&b->link); - SPA_FLAG_SET(b->flags, BUFFER_FLAG_OUT); - spa_log_trace(this->log, "%p: reuse buffer %u", this, b->id); - this->port.io->buffer_id = b->id; - - spa_node_call_reuse_buffer(&this->callbacks, 0, b->id); - port->ready_offset = 0; - } - total_frames += n_frames; - - spa_log_trace(this->log, "%p: written %u frames", this, total_frames); - } - - if (written > 0 && this->buffer_used == this->header_size) { - enable_flush(this, false, 0); - return 0; - } - - written = flush_buffer(this); - - if (written == -EAGAIN) { - spa_log_trace(this->log, "%p: fail flush", this); - if (now_time - this->last_error > SPA_NSEC_PER_SEC / 2) { - spa_log_trace(this->log, "%p: reduce bitpool", this); - this->codec->reduce_bitpool(this->codec_data); - this->last_error = now_time; - } - - /* - * The socket buffer is full, and the device is not processing data - * fast enough, so should just skip this packet. There will be a sound - * glitch in any case. - */ - written = this->buffer_used; - reset_buffer(this); - } - - if (written < 0) { - spa_log_trace(this->log, "%p: error flushing %s", this, - spa_strerror(written)); - reset_buffer(this); - enable_flush(this, false, 0); - return written; - } - else if (written > 0) { - /* - * We cannot write all data we have at once, since this can exceed - * device buffers. We'll want a limited number of "excess" - * samples. This is an issue for the "low-latency" A2DP codecs. - * - * Flushing the rest of the data (if any) is delayed after a timeout, - * selected on an average-rate basis: - * - * npackets = quantum / packet_samples - * write_end_time = npackets * timeout - * max_excess = quantum - sample_rate * write_end_time - * packet_time = packet_samples / sample_rate - * => timeout = (quantum - max_excess)/quantum * packet_time - */ - uint64_t max_excess = 2*256; - uint64_t packet_samples = (uint64_t)this->frame_count * this->block_size / port->frame_size; - uint64_t packet_time = packet_samples * SPA_NSEC_PER_SEC / port->current_format.info.raw.rate; - uint64_t quantum = SPA_LIKELY(this->clock) ? this->clock->duration : 0; - uint64_t timeout = (quantum > max_excess) ? - (packet_time * (quantum - max_excess) / quantum) : 0; - - if (this->need_flush == NEED_FLUSH_FRAGMENT) { - reset_buffer(this); - this->fragment = true; - this->fragment_timeout = (packet_samples > 0) ? timeout : this->fragment_timeout; - goto again; - } - if (this->fragment_timeout > 0) { - timeout = this->fragment_timeout; - this->fragment_timeout = 0; - } - - reset_buffer(this); - if (now_time - this->last_error > SPA_NSEC_PER_SEC) { - if (get_transport_unused_size(this) == (int)this->fd_buffer_size) { - spa_log_trace(this->log, "%p: increase bitpool", this); - this->codec->increase_bitpool(this->codec_data); - } - this->last_error = now_time; - } - if (!spa_list_is_empty(&port->ready)) { - spa_log_trace(this->log, "%p: flush after %d ns", this, (int)timeout); - if (timeout == 0) - goto again; - else - enable_flush(this, true, timeout); - } else { - enable_flush(this, false, 0); - } - } - else { - /* Don't want to flush yet, or failed to write anything */ - spa_log_trace(this->log, "%p: skip flush", this); - enable_flush(this, false, 0); - } - return 0; -} - -static void a2dp_on_flush(struct spa_source *source) -{ - struct impl *this = source->data; - - spa_log_trace(this->log, "%p: flushing", this); - - if (!SPA_FLAG_IS_SET(source->rmask, SPA_IO_OUT)) { - spa_log_warn(this->log, "%p: error %d", this, source->rmask); - if (this->flush_source.loop) - spa_loop_remove_source(this->data_loop, &this->flush_source); - return; - } - - if (this->transport == NULL) { - enable_flush(this, false, 0); - return; - } - - flush_data(this, this->current_time); -} - -static void a2dp_on_flush_timeout(struct spa_source *source) -{ - struct impl *this = source->data; - uint64_t exp; - - spa_log_trace(this->log, "%p: flush on timeout", this); - - if (spa_system_timerfd_read(this->data_system, this->flush_timerfd, &exp) < 0) - spa_log_warn(this->log, "error reading timerfd: %s", strerror(errno)); - - if (this->transport == NULL) { - enable_flush(this, false, 0); - return; - } - - flush_data(this, this->current_time); -} - -static void a2dp_on_timeout(struct spa_source *source) -{ - struct impl *this = source->data; - struct port *port = &this->port; - uint64_t exp, duration; - uint32_t rate; - struct spa_io_buffers *io = port->io; - uint64_t prev_time, now_time; - - if (this->transport == NULL) - return; - - if (this->started && spa_system_timerfd_read(this->data_system, this->timerfd, &exp) < 0) - spa_log_warn(this->log, "error reading timerfd: %s", strerror(errno)); - - prev_time = this->current_time; - now_time = this->current_time = this->next_time; - - spa_log_debug(this->log, "%p: timer %"PRIu64" %"PRIu64"", this, - now_time, now_time - prev_time); - - if (SPA_LIKELY(this->position)) { - duration = this->position->clock.duration; - rate = this->position->clock.rate.denom; - } else { - duration = 1024; - rate = 48000; - } - - this->next_time = now_time + duration * SPA_NSEC_PER_SEC / rate; - - if (SPA_LIKELY(this->clock)) { - int64_t delay_nsec; - - this->clock->nsec = now_time; - this->clock->position += duration; - this->clock->duration = duration; - this->clock->rate_diff = 1.0f; - this->clock->next_nsec = this->next_time; - - delay_nsec = spa_bt_transport_get_delay_nsec(this->transport); - - /* Negative delay doesn't work properly, so disallow it */ - delay_nsec += SPA_CLAMP(this->props.latency_offset, -delay_nsec, INT64_MAX / 2); - - this->clock->delay = (delay_nsec * this->clock->rate.denom) / SPA_NSEC_PER_SEC; - } - - - spa_log_trace(this->log, "%p: %d", this, io->status); - io->status = SPA_STATUS_NEED_DATA; - spa_node_call_ready(&this->callbacks, SPA_STATUS_NEED_DATA); - - set_timeout(this, this->next_time); -} - -static int do_start(struct impl *this) -{ - int res, val, size; - struct port *port; - socklen_t len; - uint8_t *conf; - - if (this->started) - return 0; - - spa_return_val_if_fail(this->transport, -EIO); - - this->following = is_following(this); - - spa_log_debug(this->log, "%p: start following:%d", this, this->following); - - if ((res = spa_bt_transport_acquire(this->transport, false)) < 0) - return res; - - port = &this->port; - - conf = this->transport->configuration; - size = this->transport->configuration_len; - - spa_log_debug(this->log, "Transport configuration:"); - spa_log_hexdump(this->log, SPA_LOG_LEVEL_DEBUG, 2, conf, (size_t)size); - - this->codec_data = this->codec->init(this->codec, - this->is_duplex ? A2DP_CODEC_FLAG_SINK : 0, - this->transport->configuration, - this->transport->configuration_len, - &port->current_format, - this->codec_props, - this->transport->write_mtu); - if (this->codec_data == NULL) - return -EIO; - - spa_log_info(this->log, "%p: using A2DP codec %s, delay:%"PRIi64" ms", this, this->codec->description, - (int64_t)(spa_bt_transport_get_delay_nsec(this->transport) / SPA_NSEC_PER_MSEC)); - - this->seqnum = 0; - - this->block_size = this->codec->get_block_size(this->codec_data); - if (this->block_size > sizeof(this->tmp_buffer)) { - spa_log_error(this->log, "block-size %d > %zu", - this->block_size, sizeof(this->tmp_buffer)); - return -EIO; - } - - spa_log_debug(this->log, "%p: block_size %d", this, - this->block_size); - - val = this->codec->send_buf_size > 0 - /* The kernel doubles the SO_SNDBUF option value set by setsockopt(). */ - ? this->codec->send_buf_size / 2 + this->codec->send_buf_size % 2 - : FILL_FRAMES * this->transport->write_mtu; - if (setsockopt(this->transport->fd, SOL_SOCKET, SO_SNDBUF, &val, sizeof(val)) < 0) - spa_log_warn(this->log, "%p: SO_SNDBUF %m", this); - - len = sizeof(val); - if (getsockopt(this->transport->fd, SOL_SOCKET, SO_SNDBUF, &val, &len) < 0) { - spa_log_warn(this->log, "%p: SO_SNDBUF %m", this); - } - else { - spa_log_debug(this->log, "%p: SO_SNDBUF: %d", this, val); - } - this->fd_buffer_size = val; - - val = FILL_FRAMES * this->transport->read_mtu; - if (setsockopt(this->transport->fd, SOL_SOCKET, SO_RCVBUF, &val, sizeof(val)) < 0) - spa_log_warn(this->log, "%p: SO_RCVBUF %m", this); - - val = 6; - if (setsockopt(this->transport->fd, SOL_SOCKET, SO_PRIORITY, &val, sizeof(val)) < 0) - spa_log_warn(this->log, "SO_PRIORITY failed: %m"); - - reset_buffer(this); - - this->source.data = this; - this->source.fd = this->timerfd; - this->source.func = a2dp_on_timeout; - this->source.mask = SPA_IO_IN; - this->source.rmask = 0; - spa_loop_add_source(this->data_loop, &this->source); - - this->flush_timer_source.data = this; - this->flush_timer_source.fd = this->flush_timerfd; - this->flush_timer_source.func = a2dp_on_flush_timeout; - this->flush_timer_source.mask = SPA_IO_IN; - this->flush_timer_source.rmask = 0; - spa_loop_add_source(this->data_loop, &this->flush_timer_source); - - this->flush_source.data = this; - this->flush_source.fd = this->transport->fd; - this->flush_source.func = a2dp_on_flush; - this->flush_source.mask = 0; - this->flush_source.rmask = 0; - spa_loop_add_source(this->data_loop, &this->flush_source); - - set_timers(this); - this->started = true; - - return 0; -} - -static int do_remove_source(struct spa_loop *loop, - bool async, - uint32_t seq, - const void *data, - size_t size, - void *user_data) -{ - struct impl *this = user_data; - struct itimerspec ts; - - if (this->source.loop) - spa_loop_remove_source(this->data_loop, &this->source); - ts.it_value.tv_sec = 0; - ts.it_value.tv_nsec = 0; - ts.it_interval.tv_sec = 0; - ts.it_interval.tv_nsec = 0; - spa_system_timerfd_settime(this->data_system, this->timerfd, 0, &ts, NULL); - - if (this->flush_source.loop) - spa_loop_remove_source(this->data_loop, &this->flush_source); - - if (this->flush_timer_source.loop) - spa_loop_remove_source(this->data_loop, &this->flush_timer_source); - ts.it_value.tv_sec = 0; - ts.it_value.tv_nsec = 0; - ts.it_interval.tv_sec = 0; - ts.it_interval.tv_nsec = 0; - spa_system_timerfd_settime(this->data_system, this->flush_timerfd, 0, &ts, NULL); - - return 0; -} - -static int do_stop(struct impl *this) -{ - int res = 0; - - if (!this->started) - return 0; - - spa_log_trace(this->log, "%p: stop", this); - - spa_loop_invoke(this->data_loop, do_remove_source, 0, NULL, 0, true, this); - - this->started = false; - - if (this->transport) - res = spa_bt_transport_release(this->transport); - - if (this->codec_data) - this->codec->deinit(this->codec_data); - this->codec_data = NULL; - - return res; -} - -static int impl_node_send_command(void *object, const struct spa_command *command) -{ - struct impl *this = object; - struct port *port; - int res; - - spa_return_val_if_fail(this != NULL, -EINVAL); - spa_return_val_if_fail(command != NULL, -EINVAL); - - port = &this->port; - - switch (SPA_NODE_COMMAND_ID(command)) { - case SPA_NODE_COMMAND_Start: - if (!port->have_format) - return -EIO; - if (port->n_buffers == 0) - return -EIO; - - if ((res = do_start(this)) < 0) - return res; - break; - case SPA_NODE_COMMAND_Suspend: - case SPA_NODE_COMMAND_Pause: - if ((res = do_stop(this)) < 0) - return res; - break; - default: - return -ENOTSUP; - } - return 0; -} - -static void emit_node_info(struct impl *this, bool full) -{ - struct spa_dict_item node_info_items = { - { SPA_KEY_DEVICE_API, "bluez5" }, - { SPA_KEY_MEDIA_CLASS, "Audio/Sink" }, - { SPA_KEY_NODE_DRIVER, "true" }, - }; - uint64_t old = full ? this->info.change_mask : 0; - if (full) - this->info.change_mask = this->info_all; - if (this->info.change_mask) { - this->info.props = &SPA_DICT_INIT_ARRAY(node_info_items); - spa_node_emit_info(&this->hooks, &this->info); - this->info.change_mask = old; - } -} - -static void emit_port_info(struct impl *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) { - spa_node_emit_port_info(&this->hooks, - SPA_DIRECTION_INPUT, 0, &port->info); - port->info.change_mask = old; - } -} - -static int -impl_node_add_listener(void *object, - struct spa_hook *listener, - const struct spa_node_events *events, - void *data) -{ - struct impl *this = object; - struct spa_hook_list save; - - spa_return_val_if_fail(this != NULL, -EINVAL); - - spa_hook_list_isolate(&this->hooks, &save, listener, events, data); - - emit_node_info(this, true); - emit_port_info(this, &this->port, true); - - spa_hook_list_join(&this->hooks, &save); - - return 0; -} - -static int -impl_node_set_callbacks(void *object, - const struct spa_node_callbacks *callbacks, - void *data) -{ - struct impl *this = object; - - spa_return_val_if_fail(this != NULL, -EINVAL); - - this->callbacks = SPA_CALLBACKS_INIT(callbacks, data); - - return 0; -} - -static int impl_node_sync(void *object, int seq) -{ - struct impl *this = object; - - spa_return_val_if_fail(this != NULL, -EINVAL); - - spa_node_emit_result(&this->hooks, seq, 0, 0, NULL); - - return 0; -} - -static int impl_node_add_port(void *object, enum spa_direction direction, uint32_t port_id, - const struct spa_dict *props) -{ - return -ENOTSUP; -} - -static int impl_node_remove_port(void *object, enum spa_direction direction, uint32_t port_id) -{ - return -ENOTSUP; -} - -static int -impl_node_port_enum_params(void *object, int seq, - enum spa_direction direction, uint32_t port_id, - uint32_t id, uint32_t start, uint32_t num, - const struct spa_pod *filter) -{ - - struct impl *this = object; - struct port *port; - struct spa_pod *param; - struct spa_pod_builder b = { 0 }; - uint8_t buffer1024; - struct spa_result_node_params result; - uint32_t count = 0; - int res; - - spa_return_val_if_fail(this != NULL, -EINVAL); - spa_return_val_if_fail(num != 0, -EINVAL); - - spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL); - port = &this->port; - - result.id = id; - result.next = start; - next: - result.index = result.next++; - - spa_pod_builder_init(&b, buffer, sizeof(buffer)); - - switch (id) { - case SPA_PARAM_EnumFormat: - if (this->codec == NULL) - return -EIO; - if (this->transport == NULL) - return -EIO; - - if ((res = this->codec->enum_config(this->codec, - this->is_duplex ? A2DP_CODEC_FLAG_SINK : 0, - this->transport->configuration, - this->transport->configuration_len, - id, result.index, &b, ¶m)) != 1) - return res; - break; - - case SPA_PARAM_Format: - if (!port->have_format) - return -EIO; - if (result.index > 0) - return 0; - - param = spa_format_audio_raw_build(&b, id, &port->current_format.info.raw); - break; - - case SPA_PARAM_Buffers: - if (!port->have_format) - return -EIO; - if (result.index > 0) - return 0; - - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_ParamBuffers, id, - SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(2, 2, MAX_BUFFERS), - SPA_PARAM_BUFFERS_blocks, SPA_POD_Int(1), - SPA_PARAM_BUFFERS_size, SPA_POD_CHOICE_RANGE_Int( - this->props.min_latency * port->frame_size, - this->props.min_latency * port->frame_size, - INT32_MAX), - SPA_PARAM_BUFFERS_stride, SPA_POD_Int(port->frame_size)); - break; - - case SPA_PARAM_Meta: - switch (result.index) { - case 0: - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_ParamMeta, id, - SPA_PARAM_META_type, SPA_POD_Id(SPA_META_Header), - SPA_PARAM_META_size, SPA_POD_Int(sizeof(struct spa_meta_header))); - 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_Buffers), - SPA_PARAM_IO_size, SPA_POD_Int(sizeof(struct spa_io_buffers))); - break; - default: - return 0; - } - break; - - case SPA_PARAM_Latency: - switch (result.index) { - case 0: - param = spa_latency_build(&b, id, &port->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; -} - -static int clear_buffers(struct impl *this, struct port *port) -{ - do_stop(this); - if (port->n_buffers > 0) { - spa_list_init(&port->ready); - port->n_buffers = 0; - } - return 0; -} - -static int port_set_format(struct impl *this, struct port *port, - uint32_t flags, - const struct spa_pod *format) -{ - int err; - - if (format == NULL) { - spa_log_debug(this->log, "clear format"); - clear_buffers(this, port); - port->have_format = false; - } else { - struct spa_audio_info info = { 0 }; - - if ((err = spa_format_parse(format, &info.media_type, &info.media_subtype)) < 0) - return err; - - if (info.media_type != SPA_MEDIA_TYPE_audio || - info.media_subtype != SPA_MEDIA_SUBTYPE_raw) - return -EINVAL; - - if (spa_format_audio_raw_parse(format, &info.info.raw) < 0) - return -EINVAL; - - port->frame_size = info.info.raw.channels; - switch (info.info.raw.format) { - case SPA_AUDIO_FORMAT_S16: - port->frame_size *= 2; - break; - case SPA_AUDIO_FORMAT_S24: - port->frame_size *= 3; - break; - case SPA_AUDIO_FORMAT_S24_32: - case SPA_AUDIO_FORMAT_S32: - case SPA_AUDIO_FORMAT_F32: - port->frame_size *= 4; - break; - default: - return -EINVAL; - } - - port->current_format = info; - port->have_format = true; - } - - port->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS; - if (port->have_format) { - port->info.change_mask |= SPA_PORT_CHANGE_MASK_FLAGS; - port->info.flags = SPA_PORT_FLAG_LIVE; - port->info.change_mask |= SPA_PORT_CHANGE_MASK_RATE; - port->info.rate = SPA_FRACTION(1, port->current_format.info.raw.rate); - port->paramsIDX_Format = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_READWRITE); - port->paramsIDX_Buffers = SPA_PARAM_INFO(SPA_PARAM_Buffers, SPA_PARAM_INFO_READ); - port->paramsIDX_Latency.flags ^= SPA_PARAM_INFO_SERIAL; - } else { - port->paramsIDX_Format = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE); - port->paramsIDX_Buffers = SPA_PARAM_INFO(SPA_PARAM_Buffers, 0); - } - emit_port_info(this, port, false); - - return 0; -} - -static int -impl_node_port_set_param(void *object, - enum spa_direction direction, uint32_t port_id, - uint32_t id, uint32_t flags, - const struct spa_pod *param) -{ - struct impl *this = object; - struct port *port; - int res; - - spa_return_val_if_fail(this != NULL, -EINVAL); - spa_return_val_if_fail(CHECK_PORT(node, direction, port_id), -EINVAL); - port = &this->port; - - switch (id) { - case SPA_PARAM_Format: - res = port_set_format(this, port, flags, param); - break; - case SPA_PARAM_Latency: - res = 0; - break; - default: - res = -ENOENT; - break; - } - return res; -} - -static int -impl_node_port_use_buffers(void *object, - enum spa_direction direction, uint32_t port_id, - uint32_t flags, - struct spa_buffer **buffers, uint32_t n_buffers) -{ - struct impl *this = object; - struct port *port; - uint32_t i; - - spa_return_val_if_fail(this != NULL, -EINVAL); - spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL); - port = &this->port; - - spa_log_debug(this->log, "use buffers %d", n_buffers); - - if (!port->have_format) - return -EIO; - - clear_buffers(this, port); - - for (i = 0; i < n_buffers; i++) { - struct buffer *b = &port->buffersi; - - b->buf = buffersi; - b->id = i; - SPA_FLAG_SET(b->flags, BUFFER_FLAG_OUT); - - b->h = spa_buffer_find_meta_data(buffersi, SPA_META_Header, sizeof(*b->h)); - - if (buffersi->datas0.data == NULL) { - spa_log_error(this->log, "%p: need mapped memory", this); - return -EINVAL; - } - } - port->n_buffers = n_buffers; - - return 0; -} - -static int -impl_node_port_set_io(void *object, - enum spa_direction direction, - uint32_t port_id, - uint32_t id, - void *data, size_t size) -{ - struct impl *this = object; - struct port *port; - - spa_return_val_if_fail(this != NULL, -EINVAL); - - spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL); - port = &this->port; - - switch (id) { - case SPA_IO_Buffers: - port->io = data; - break; - default: - return -ENOENT; - } - return 0; -} - -static int impl_node_port_reuse_buffer(void *object, uint32_t port_id, uint32_t buffer_id) -{ - return -ENOTSUP; -} - -static int impl_node_process(void *object) -{ - struct impl *this = object; - struct port *port; - struct spa_io_buffers *io; - - spa_return_val_if_fail(this != NULL, -EINVAL); - - port = &this->port; - if ((io = port->io) == NULL) - return -EIO; - - if (this->position && this->position->clock.flags & SPA_IO_CLOCK_FLAG_FREEWHEEL) { - io->status = SPA_STATUS_NEED_DATA; - return SPA_STATUS_HAVE_DATA; - } - - if (io->status == SPA_STATUS_HAVE_DATA && io->buffer_id < port->n_buffers) { - struct buffer *b = &port->buffersio->buffer_id; - - if (!SPA_FLAG_IS_SET(b->flags, BUFFER_FLAG_OUT)) { - spa_log_warn(this->log, "%p: buffer %u in use", this, io->buffer_id); - io->status = -EINVAL; - return -EINVAL; - } - - spa_log_trace(this->log, "%p: queue buffer %u", this, io->buffer_id); - - spa_list_append(&port->ready, &b->link); - SPA_FLAG_CLEAR(b->flags, BUFFER_FLAG_OUT); - - io->buffer_id = SPA_ID_INVALID; - io->status = SPA_STATUS_OK; - } - if (!spa_list_is_empty(&port->ready)) { - if (this->following) { - if (this->position) { - this->current_time = this->position->clock.nsec; - } else { - struct timespec now; - spa_system_clock_gettime(this->data_system, CLOCK_MONOTONIC, &now); - this->current_time = SPA_TIMESPEC_TO_NSEC(&now); - } - } - if (this->need_flush) - reset_buffer(this); - flush_data(this, this->current_time); - } - - return SPA_STATUS_HAVE_DATA; -} - -static const struct spa_node_methods impl_node = { - SPA_VERSION_NODE_METHODS, - .add_listener = impl_node_add_listener, - .set_callbacks = impl_node_set_callbacks, - .sync = impl_node_sync, - .enum_params = impl_node_enum_params, - .set_param = impl_node_set_param, - .set_io = impl_node_set_io, - .send_command = impl_node_send_command, - .add_port = impl_node_add_port, - .remove_port = impl_node_remove_port, - .port_enum_params = impl_node_port_enum_params, - .port_set_param = impl_node_port_set_param, - .port_use_buffers = impl_node_port_use_buffers, - .port_set_io = impl_node_port_set_io, - .port_reuse_buffer = impl_node_port_reuse_buffer, - .process = impl_node_process, -}; - -static void transport_delay_changed(void *data) -{ - struct impl *this = data; - spa_log_debug(this->log, "transport %p delay changed", this->transport); - set_latency(this, true); -} - -static int do_transport_destroy(struct spa_loop *loop, - bool async, - uint32_t seq, - const void *data, - size_t size, - void *user_data) -{ - struct impl *this = user_data; - this->transport = NULL; - return 0; -} - -static void transport_destroy(void *data) -{ - struct impl *this = data; - spa_log_debug(this->log, "transport %p destroy", this->transport); - spa_loop_invoke(this->data_loop, do_transport_destroy, 0, NULL, 0, true, this); -} - -static void transport_state_changed(void *data, - enum spa_bt_transport_state old, - enum spa_bt_transport_state state) -{ - struct impl *this = data; - - spa_log_debug(this->log, "%p: transport %p state %d->%d", this, this->transport, old, state); - - if (state < SPA_BT_TRANSPORT_STATE_ACTIVE && old == SPA_BT_TRANSPORT_STATE_ACTIVE && - this->started) { - uint8_t buffer1024; - struct spa_pod_builder b = { 0 }; - - spa_log_debug(this->log, "%p: transport %p becomes inactive: stop and indicate error", - this, this->transport); - - /* - * If establishing connection fails due to remote end not activating - * the transport, we won't get a write error, but instead see a transport - * state change. - * - * Stop and emit a node error, to let upper levels handle it. - */ - - do_stop(this); - - spa_pod_builder_init(&b, buffer, sizeof(buffer)); - spa_node_emit_event(&this->hooks, - spa_pod_builder_add_object(&b, - SPA_TYPE_EVENT_Node, SPA_NODE_EVENT_Error)); - } -} - -static const struct spa_bt_transport_events transport_events = { - SPA_VERSION_BT_TRANSPORT_EVENTS, - .delay_changed = transport_delay_changed, - .state_changed = transport_state_changed, - .destroy = transport_destroy, -}; - -static int impl_get_interface(struct spa_handle *handle, const char *type, void **interface) -{ - struct impl *this; - - spa_return_val_if_fail(handle != NULL, -EINVAL); - spa_return_val_if_fail(interface != NULL, -EINVAL); - - this = (struct impl *) handle; - - if (spa_streq(type, SPA_TYPE_INTERFACE_Node)) - *interface = &this->node; - else - return -ENOENT; - - return 0; -} - -static int impl_clear(struct spa_handle *handle) -{ - struct impl *this = (struct impl *) handle; - - if (this->codec_data) - this->codec->deinit(this->codec_data); - if (this->codec_props && this->codec->clear_props) - this->codec->clear_props(this->codec_props); - if (this->transport) - spa_hook_remove(&this->transport_listener); - spa_system_close(this->data_system, this->timerfd); - spa_system_close(this->data_system, this->flush_timerfd); - return 0; -} - -static size_t -impl_get_size(const struct spa_handle_factory *factory, - const struct spa_dict *params) -{ - return sizeof(struct impl); -} - -static int -impl_init(const struct spa_handle_factory *factory, - struct spa_handle *handle, - const struct spa_dict *info, - const struct spa_support *support, - uint32_t n_support) -{ - struct impl *this; - struct port *port; - const char *str; - - spa_return_val_if_fail(factory != NULL, -EINVAL); - spa_return_val_if_fail(handle != NULL, -EINVAL); - - handle->get_interface = impl_get_interface; - handle->clear = impl_clear; - - this = (struct impl *) handle; - - this->log = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log); - this->data_loop = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_DataLoop); - this->data_system = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_DataSystem); - - spa_log_topic_init(this->log, &log_topic); - - if (this->data_loop == NULL) { - spa_log_error(this->log, "a data loop is needed"); - return -EINVAL; - } - if (this->data_system == NULL) { - spa_log_error(this->log, "a data system is needed"); - return -EINVAL; - } - - this->node.iface = SPA_INTERFACE_INIT( - SPA_TYPE_INTERFACE_Node, - SPA_VERSION_NODE, - &impl_node, this); - spa_hook_list_init(&this->hooks); - - this->info_all = SPA_NODE_CHANGE_MASK_FLAGS | - SPA_NODE_CHANGE_MASK_PARAMS | - SPA_NODE_CHANGE_MASK_PROPS; - this->info = SPA_NODE_INFO_INIT(); - this->info.max_input_ports = 1; - this->info.max_output_ports = 0; - this->info.flags = SPA_NODE_FLAG_RT; - this->paramsIDX_PropInfo = SPA_PARAM_INFO(SPA_PARAM_PropInfo, SPA_PARAM_INFO_READ); - this->paramsIDX_Props = SPA_PARAM_INFO(SPA_PARAM_Props, SPA_PARAM_INFO_READWRITE); - this->info.params = this->params; - this->info.n_params = N_NODE_PARAMS; - - port = &this->port; - port->info_all = SPA_PORT_CHANGE_MASK_FLAGS | - SPA_PORT_CHANGE_MASK_PARAMS; - port->info = SPA_PORT_INFO_INIT(); - port->info.flags = 0; - port->paramsIDX_EnumFormat = SPA_PARAM_INFO(SPA_PARAM_EnumFormat, SPA_PARAM_INFO_READ); - port->paramsIDX_Meta = SPA_PARAM_INFO(SPA_PARAM_Meta, SPA_PARAM_INFO_READ); - port->paramsIDX_IO = SPA_PARAM_INFO(SPA_PARAM_IO, SPA_PARAM_INFO_READ); - port->paramsIDX_Format = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE); - port->paramsIDX_Buffers = SPA_PARAM_INFO(SPA_PARAM_Buffers, 0); - port->paramsIDX_Latency = SPA_PARAM_INFO(SPA_PARAM_Latency, SPA_PARAM_INFO_READWRITE); - port->info.params = port->params; - port->info.n_params = N_PORT_PARAMS; - - port->latency = SPA_LATENCY_INFO(SPA_DIRECTION_INPUT); - port->latency.min_quantum = 1.0f; - port->latency.max_quantum = 1.0f; - set_latency(this, false); - - spa_list_init(&port->ready); - - if (info && (str = spa_dict_lookup(info, "api.bluez5.a2dp-duplex")) != NULL) - this->is_duplex = spa_atob(str); - - if (info && (str = spa_dict_lookup(info, SPA_KEY_API_BLUEZ5_TRANSPORT))) - sscanf(str, "pointer:%p", &this->transport); - - if (this->transport == NULL) { - spa_log_error(this->log, "a transport is needed"); - return -EINVAL; - } - if (this->transport->a2dp_codec == NULL) { - spa_log_error(this->log, "a transport codec is needed"); - return -EINVAL; - } - - this->codec = this->transport->a2dp_codec; - - if (this->is_duplex) { - if (!this->codec->duplex_codec) { - spa_log_error(this->log, "transport codec doesn't support duplex"); - return -EINVAL; - } - this->codec = this->codec->duplex_codec; - } - - if (this->codec->init_props != NULL) - this->codec_props = this->codec->init_props(this->codec, - this->is_duplex ? A2DP_CODEC_FLAG_SINK : 0, - this->transport->device->settings); - - reset_props(this, &this->props); - - spa_bt_transport_add_listener(this->transport, - &this->transport_listener, &transport_events, this); - - this->timerfd = spa_system_timerfd_create(this->data_system, - CLOCK_MONOTONIC, SPA_FD_CLOEXEC | SPA_FD_NONBLOCK); - - this->flush_timerfd = spa_system_timerfd_create(this->data_system, - CLOCK_MONOTONIC, SPA_FD_CLOEXEC | SPA_FD_NONBLOCK); - - return 0; -} - -static const struct spa_interface_info impl_interfaces = { - {SPA_TYPE_INTERFACE_Node,}, -}; - -static int -impl_enum_interface_info(const struct spa_handle_factory *factory, - const struct spa_interface_info **info, uint32_t *index) -{ - spa_return_val_if_fail(factory != NULL, -EINVAL); - spa_return_val_if_fail(info != NULL, -EINVAL); - spa_return_val_if_fail(index != NULL, -EINVAL); - - switch (*index) { - case 0: - *info = &impl_interfaces*index; - break; - default: - return 0; - } - (*index)++; - return 1; -} - -static const struct spa_dict_item info_items = { - { SPA_KEY_FACTORY_AUTHOR, "Wim Taymans <wim.taymans@gmail.com>" }, - { SPA_KEY_FACTORY_DESCRIPTION, "Play audio with the a2dp" }, - { SPA_KEY_FACTORY_USAGE, SPA_KEY_API_BLUEZ5_TRANSPORT"=<transport>" }, -}; - -static const struct spa_dict info = SPA_DICT_INIT_ARRAY(info_items); - -const struct spa_handle_factory spa_a2dp_sink_factory = { - SPA_VERSION_HANDLE_FACTORY, - SPA_NAME_API_BLUEZ5_A2DP_SINK, - &info, - impl_get_size, - impl_init, - impl_enum_interface_info, -};
View file
pipewire-0.3.58.tar.gz/spa/plugins/bluez5/a2dp-source.c
Deleted
@@ -1,1643 +0,0 @@ -/* Spa A2DP Source - * - * Copyright © 2018 Wim Taymans - * Copyright © 2019 Collabora Ltd. - * - * 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 <stddef.h> -#include <stdio.h> -#include <time.h> -#include <fcntl.h> -#include <sys/types.h> -#include <sys/socket.h> - -#include <spa/support/plugin.h> -#include <spa/support/loop.h> -#include <spa/support/log.h> -#include <spa/support/system.h> -#include <spa/utils/list.h> -#include <spa/utils/keys.h> -#include <spa/utils/names.h> -#include <spa/utils/result.h> -#include <spa/utils/string.h> -#include <spa/monitor/device.h> - -#include <spa/node/node.h> -#include <spa/node/utils.h> -#include <spa/node/io.h> -#include <spa/node/keys.h> -#include <spa/param/param.h> -#include <spa/param/latency-utils.h> -#include <spa/param/audio/format.h> -#include <spa/param/audio/format-utils.h> -#include <spa/pod/filter.h> - -#include "defs.h" -#include "rtp.h" -#include "a2dp-codecs.h" - -static struct spa_log_topic log_topic = SPA_LOG_TOPIC(0, "spa.bluez5.source.a2dp"); -#undef SPA_LOG_TOPIC_DEFAULT -#define SPA_LOG_TOPIC_DEFAULT &log_topic - -#include "decode-buffer.h" - -#define DEFAULT_CLOCK_NAME "clock.system.monotonic" - -struct props { - char clock_name64; -}; - -#define FILL_FRAMES 2 -#define MAX_BUFFERS 32 - -struct buffer { - uint32_t id; - unsigned int outstanding:1; - struct spa_buffer *buf; - struct spa_meta_header *h; - struct spa_list link; -}; - -struct port { - struct spa_audio_info current_format; - uint32_t frame_size; - unsigned int have_format:1; - - uint64_t info_all; - struct spa_port_info info; - struct spa_io_buffers *io; - struct spa_io_rate_match *rate_match; - struct spa_latency_info latency; -#define IDX_EnumFormat 0 -#define IDX_Meta 1 -#define IDX_IO 2 -#define IDX_Format 3 -#define IDX_Buffers 4 -#define IDX_Latency 5 -#define N_PORT_PARAMS 6 - struct spa_param_info paramsN_PORT_PARAMS; - - struct buffer buffersMAX_BUFFERS; - uint32_t n_buffers; - - struct spa_list free; - struct spa_list ready; - - struct spa_bt_decode_buffer buffer; -}; - -struct impl { - struct spa_handle handle; - struct spa_node node; - - struct spa_log *log; - struct spa_loop *data_loop; - struct spa_system *data_system; - - struct spa_hook_list hooks; - struct spa_callbacks callbacks; - - uint32_t quantum_limit; - - uint64_t info_all; - struct spa_node_info info; -#define IDX_PropInfo 0 -#define IDX_Props 1 -#define IDX_NODE_IO 2 -#define N_NODE_PARAMS 3 - struct spa_param_info paramsN_NODE_PARAMS; - struct props props; - - struct spa_bt_transport *transport; - struct spa_hook transport_listener; - - struct port port; - - unsigned int started:1; - unsigned int transport_acquired:1; - unsigned int following:1; - unsigned int matching:1; - unsigned int resampling:1; - - unsigned int is_input:1; - unsigned int is_duplex:1; - unsigned int use_duplex_source:1; - - int fd; - struct spa_source source; - - struct spa_source timer_source; - int timerfd; - - struct spa_io_clock *clock; - struct spa_io_position *position; - - uint64_t current_time; - uint64_t next_time; - - const struct a2dp_codec *codec; - bool codec_props_changed; - void *codec_props; - void *codec_data; - struct spa_audio_info codec_format; - - uint8_t buffer_read4096; - struct timespec now; - uint64_t sample_count; - - int duplex_timerfd; - uint64_t duplex_timeout; -}; - -#define CHECK_PORT(this,d,p) ((d) == SPA_DIRECTION_OUTPUT && (p) == 0) - -static void reset_props(struct props *props) -{ - strncpy(props->clock_name, DEFAULT_CLOCK_NAME, sizeof(props->clock_name)); -} - -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 impl *this = object; - struct spa_pod *param; - struct spa_pod_builder b = { 0 }; - uint8_t buffer1024; - struct spa_result_node_params result; - uint32_t count = 0, index_offset = 0; - bool enum_codec = false; - - 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: - enum_codec = true; - index_offset = 0; - } - break; - } - case SPA_PARAM_Props: - { - switch (result.index) { - default: - enum_codec = true; - index_offset = 0; - } - break; - } - default: - return -ENOENT; - } - - if (enum_codec) { - int res; - if (this->codec->enum_props == NULL || this->codec_props == NULL || - this->transport == NULL) - return 0; - else if ((res = this->codec->enum_props(this->codec_props, - this->transport->device->settings, - id, result.index - index_offset, - &b, ¶m)) != 1) - return res; - } - - 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; -} - -static int set_timeout(struct impl *this, uint64_t time) -{ - struct itimerspec ts; - ts.it_value.tv_sec = time / SPA_NSEC_PER_SEC; - ts.it_value.tv_nsec = time % SPA_NSEC_PER_SEC; - ts.it_interval.tv_sec = 0; - ts.it_interval.tv_nsec = 0; - return spa_system_timerfd_settime(this->data_system, - this->timerfd, SPA_FD_TIMER_ABSTIME, &ts, NULL); -} - -static int set_timers(struct impl *this) -{ - struct timespec now; - - spa_system_clock_gettime(this->data_system, CLOCK_MONOTONIC, &now); - this->next_time = SPA_TIMESPEC_TO_NSEC(&now); - - return set_timeout(this, this->following ? 0 : this->next_time); -} - -static int do_reassign_follower(struct spa_loop *loop, - bool async, - uint32_t seq, - const void *data, - size_t size, - void *user_data) -{ - struct impl *this = user_data; - struct port *port = &this->port; - - spa_bt_decode_buffer_recover(&port->buffer); - return 0; -} - -static inline bool is_following(struct impl *this) -{ - return this->position && this->clock && this->position->clock.id != this->clock->id; -} - -static int impl_node_set_io(void *object, uint32_t id, void *data, size_t size) -{ - struct impl *this = object; - bool following; - - spa_return_val_if_fail(this != NULL, -EINVAL); - - switch (id) { - case SPA_IO_Clock: - this->clock = data; - if (this->clock != NULL) { - spa_scnprintf(this->clock->name, - sizeof(this->clock->name), - "%s", this->props.clock_name); - } - break; - case SPA_IO_Position: - this->position = data; - break; - default: - return -ENOENT; - } - - following = is_following(this); - if (this->started && following != this->following) { - spa_log_debug(this->log, "%p: reassign follower %d->%d", this, this->following, following); - this->following = following; - spa_loop_invoke(this->data_loop, do_reassign_follower, 0, NULL, 0, true, this); - } - return 0; -} - -static void emit_node_info(struct impl *this, bool full); - -static int apply_props(struct impl *this, const struct spa_pod *param) -{ - struct props new_props = this->props; - int changed = 0; - - if (param == NULL) { - reset_props(&new_props); - } else { - /* noop */ - } - - changed = (memcmp(&new_props, &this->props, sizeof(struct props)) != 0); - this->props = new_props; - return changed; -} - -static int impl_node_set_param(void *object, uint32_t id, uint32_t flags, - const struct spa_pod *param) -{ - struct impl *this = object; - - spa_return_val_if_fail(this != NULL, -EINVAL); - - switch (id) { - case SPA_PARAM_Props: - { - int res, codec_res = 0; - res = apply_props(this, param); - if (this->codec_props && this->codec->set_props) { - codec_res = this->codec->set_props(this->codec_props, param); - if (codec_res > 0) - this->codec_props_changed = true; - } - if (res > 0 || codec_res > 0) { - this->info.change_mask |= SPA_NODE_CHANGE_MASK_PARAMS; - this->paramsIDX_Props.flags ^= SPA_PARAM_INFO_SERIAL; - emit_node_info(this, false); - } - break; - } - default: - return -ENOENT; - } - - return 0; -} - -static void reset_buffers(struct port *port) -{ - uint32_t i; - - spa_list_init(&port->free); - spa_list_init(&port->ready); - - for (i = 0; i < port->n_buffers; i++) { - struct buffer *b = &port->buffersi; - spa_list_append(&port->free, &b->link); - b->outstanding = false; - } -} - -static void recycle_buffer(struct impl *this, struct port *port, uint32_t buffer_id) -{ - struct buffer *b = &port->buffersbuffer_id; - - if (b->outstanding) { - spa_log_trace(this->log, "%p: recycle buffer %u", this, buffer_id); - spa_list_append(&port->free, &b->link); - b->outstanding = false; - } -} - -static int32_t read_data(struct impl *this) { - const ssize_t b_size = sizeof(this->buffer_read); - int32_t size_read = 0; - -again: - /* read data from socket */ - size_read = recv(this->fd, this->buffer_read, b_size, MSG_DONTWAIT); - - if (size_read == 0) - return 0; - else if (size_read < 0) { - /* retry if interrupted */ - if (errno == EINTR) - goto again; - - /* return socket has no data */ - if (errno == EAGAIN || errno == EWOULDBLOCK) - return 0; - - /* go to 'stop' if socket has an error */ - spa_log_error(this->log, "read error: %s", strerror(errno)); - return -errno; - } - - return size_read; -} - -static int32_t decode_data(struct impl *this, uint8_t *src, uint32_t src_size, - uint8_t *dst, uint32_t dst_size) -{ - ssize_t processed; - size_t written, avail; - - if ((processed = this->codec->start_decode(this->codec_data, - src, src_size, NULL, NULL)) < 0) - return processed; - - src += processed; - src_size -= processed; - - /* decode */ - avail = dst_size; - while (src_size > 0) { - if ((processed = this->codec->decode(this->codec_data, - src, src_size, dst, avail, &written)) <= 0) - return processed; - - /* update source and dest pointers */ - spa_return_val_if_fail (avail > written, -ENOSPC); - src_size -= processed; - src += processed; - avail -= written; - dst += written; - } - return dst_size - avail; -} - -static void a2dp_on_ready_read(struct spa_source *source) -{ - struct impl *this = source->data; - struct port *port = &this->port; - struct timespec now; - void *buf; - int32_t size_read, decoded; - uint32_t avail; - uint64_t dt; - - /* make sure the source is an input */ - if ((source->rmask & SPA_IO_IN) == 0) { - spa_log_error(this->log, "source is not an input, rmask=%d", source->rmask); - goto stop; - } - if (this->transport == NULL) { - spa_log_debug(this->log, "no transport, stop reading"); - goto stop; - } - - spa_log_trace(this->log, "socket poll"); - - /* read */ - size_read = read_data (this); - if (size_read == 0) - return; - if (size_read < 0) { - spa_log_error(this->log, "failed to read data: %s", spa_strerror(size_read)); - goto stop; - } - - /* update the current pts */ - spa_system_clock_gettime(this->data_system, CLOCK_MONOTONIC, &now); - - if (this->codec_props_changed && this->codec_props - && this->codec->update_props) { - this->codec->update_props(this->codec_data, this->codec_props); - this->codec_props_changed = false; - } - - /* decode to buffer */ - buf = spa_bt_decode_buffer_get_write(&port->buffer, &avail); - spa_log_trace(this->log, "read socket data size:%d, avail:%d", size_read, avail); - decoded = decode_data(this, this->buffer_read, size_read, buf, avail); - if (decoded < 0) { - spa_log_debug(this->log, "failed to decode data: %d", decoded); - return; - } - if (decoded == 0) { - spa_log_trace(this->log, "no decoded socket data"); - return; - } - - /* discard when not started */ - if (!this->started) - return; - - spa_bt_decode_buffer_write_packet(&port->buffer, decoded); - - dt = SPA_TIMESPEC_TO_NSEC(&this->now); - this->now = now; - dt = SPA_TIMESPEC_TO_NSEC(&this->now) - dt; - - spa_log_trace(this->log, "decoded socket data size:%d frames:%d dt:%d dms", - (int)decoded, (int)decoded/port->frame_size, - (int)(dt / 100000)); - - return; - -stop: - if (this->source.loop) - spa_loop_remove_source(this->data_loop, &this->source); -} - -static int set_duplex_timeout(struct impl *this, uint64_t timeout) -{ - struct itimerspec ts; - ts.it_value.tv_sec = timeout / SPA_NSEC_PER_SEC; - ts.it_value.tv_nsec = timeout % SPA_NSEC_PER_SEC; - ts.it_interval.tv_sec = 0; - ts.it_interval.tv_nsec = 0; - return spa_system_timerfd_settime(this->data_system, - this->duplex_timerfd, 0, &ts, NULL); -} - -static void a2dp_on_duplex_timeout(struct spa_source *source) -{ - struct impl *this = source->data; - uint64_t exp; - - if (spa_system_timerfd_read(this->data_system, this->duplex_timerfd, &exp) < 0) - spa_log_warn(this->log, "error reading timerfd: %s", strerror(errno)); - - set_duplex_timeout(this, this->duplex_timeout); - - a2dp_on_ready_read(source); -} - -static int setup_matching(struct impl *this) -{ - struct port *port = &this->port; - - if (this->position && port->rate_match) { - port->rate_match->rate = 1 / port->buffer.corr; - - this->matching = this->following; - this->resampling = this->matching || - (port->current_format.info.raw.rate != this->position->clock.rate.denom); - } else { - this->matching = false; - this->resampling = false; - } - - if (port->rate_match) - SPA_FLAG_UPDATE(port->rate_match->flags, SPA_IO_RATE_MATCH_FLAG_ACTIVE, this->matching); - - return 0; -} - -static void a2dp_on_timeout(struct spa_source *source) -{ - struct impl *this = source->data; - struct port *port = &this->port; - uint64_t exp, duration; - uint32_t rate; - struct spa_io_buffers *io = port->io; - uint64_t prev_time, now_time; - - if (this->transport == NULL) - return; - - if (this->started && spa_system_timerfd_read(this->data_system, this->timerfd, &exp) < 0) - spa_log_warn(this->log, "error reading timerfd: %s", strerror(errno)); - - prev_time = this->current_time; - now_time = this->current_time = this->next_time; - - spa_log_trace(this->log, "%p: timer %"PRIu64" %"PRIu64"", this, - now_time, now_time - prev_time); - - if (SPA_LIKELY(this->position)) { - duration = this->position->clock.duration; - rate = this->position->clock.rate.denom; - } else { - duration = 1024; - rate = 48000; - } - - setup_matching(this); - - this->next_time = now_time + duration * SPA_NSEC_PER_SEC / port->buffer.corr / rate; - - if (SPA_LIKELY(this->clock)) { - this->clock->nsec = now_time; - this->clock->position += duration; - this->clock->duration = duration; - this->clock->rate_diff = port->buffer.corr; - this->clock->next_nsec = this->next_time; - } - - spa_log_trace(this->log, "%p: %d", this, io->status); - io->status = SPA_STATUS_HAVE_DATA; - spa_node_call_ready(&this->callbacks, SPA_STATUS_HAVE_DATA); - - set_timeout(this, this->next_time); -} - -static int transport_start(struct impl *this) -{ - int res, val; - struct port *port = &this->port; - - if (this->transport_acquired) - return 0; - - spa_log_debug(this->log, "%p: transport %p acquire", this, - this->transport); - if ((res = spa_bt_transport_acquire(this->transport, false)) < 0) - return res; - - this->transport_acquired = true; - - this->codec_data = this->codec->init(this->codec, - this->is_duplex ? 0 : A2DP_CODEC_FLAG_SINK, - this->transport->configuration, - this->transport->configuration_len, - &port->current_format, - this->codec_props, - this->transport->read_mtu); - if (this->codec_data == NULL) - return -EIO; - - spa_log_info(this->log, "%p: using A2DP codec %s", this, this->codec->description); - - val = fcntl(this->transport->fd, F_GETFL); - if (fcntl(this->transport->fd, F_SETFL, val | O_NONBLOCK) < 0) - spa_log_warn(this->log, "%p: fcntl %u %m", this, val | O_NONBLOCK); - - val = FILL_FRAMES * this->transport->write_mtu; - if (setsockopt(this->transport->fd, SOL_SOCKET, SO_SNDBUF, &val, sizeof(val)) < 0) - spa_log_warn(this->log, "%p: SO_SNDBUF %m", this); - - val = FILL_FRAMES * this->transport->read_mtu; - if (setsockopt(this->transport->fd, SOL_SOCKET, SO_RCVBUF, &val, sizeof(val)) < 0) - spa_log_warn(this->log, "%p: SO_RCVBUF %m", this); - - val = 6; - if (setsockopt(this->transport->fd, SOL_SOCKET, SO_PRIORITY, &val, sizeof(val)) < 0) - spa_log_warn(this->log, "SO_PRIORITY failed: %m"); - - reset_buffers(port); - - spa_bt_decode_buffer_clear(&port->buffer); - if ((res = spa_bt_decode_buffer_init(&port->buffer, this->log, - port->frame_size, port->current_format.info.raw.rate, - this->quantum_limit, this->quantum_limit)) < 0) - return res; - - this->fd = this->transport->fd; - - this->source.data = this; - - if (!this->use_duplex_source) { - this->source.fd = this->transport->fd; - this->source.func = a2dp_on_ready_read; - this->source.mask = SPA_IO_IN; - this->source.rmask = 0; - spa_loop_add_source(this->data_loop, &this->source); - } else { - /* - * XXX: For an unknown reason (on Linux 5.13.10), the socket when working with - * XXX: "duplex" stream sometimes stops waking up from the poll, even though - * XXX: you can recv() from the socket with no problem. - * XXX: - * XXX: The reason for this should be found and fixed. - * XXX: To work around this, for now we just do the stupid thing and poll - * XXX: on a timer, chosen so that it's fast enough for the aptX-LL codec - * XXX: we currently support (which sends mSBC data), and also for Opus - * XXX: forward stream. - */ - this->source.fd = this->duplex_timerfd; - this->source.func = a2dp_on_duplex_timeout; - this->source.mask = SPA_IO_IN; - this->source.rmask = 0; - spa_loop_add_source(this->data_loop, &this->source); - - this->duplex_timeout = SPA_NSEC_PER_MSEC * 25/10; - set_duplex_timeout(this, this->duplex_timeout); - } - - this->timer_source.data = this; - this->timer_source.fd = this->timerfd; - this->timer_source.func = a2dp_on_timeout; - this->timer_source.mask = SPA_IO_IN; - this->timer_source.rmask = 0; - spa_loop_add_source(this->data_loop, &this->timer_source); - - this->sample_count = 0; - - setup_matching(this); - - set_timers(this); - - return 0; -} - -static int do_start(struct impl *this) -{ - int res = 0; - - if (this->started) - return 0; - - spa_return_val_if_fail(this->transport != NULL, -EIO); - - this->following = is_following(this); - - spa_log_debug(this->log, "%p: start state:%d following:%d", - this, this->transport->state, this->following); - - if (this->transport->state >= SPA_BT_TRANSPORT_STATE_PENDING || - this->is_duplex) - res = transport_start(this); - - this->started = true; - - return res; -} - -static int do_remove_source(struct spa_loop *loop, - bool async, - uint32_t seq, - const void *data, - size_t size, - void *user_data) -{ - struct impl *this = user_data; - struct itimerspec ts; - - spa_log_debug(this->log, "%p: remove source", this); - - set_duplex_timeout(this, 0); - - if (this->source.loop) - spa_loop_remove_source(this->data_loop, &this->source); - - if (this->timer_source.loop) - spa_loop_remove_source(this->data_loop, &this->timer_source); - ts.it_value.tv_sec = 0; - ts.it_value.tv_nsec = 0; - ts.it_interval.tv_sec = 0; - ts.it_interval.tv_nsec = 0; - spa_system_timerfd_settime(this->data_system, this->timerfd, 0, &ts, NULL); - - return 0; -} - -static int transport_stop(struct impl *this) -{ - struct port *port = &this->port; - int res; - - spa_log_debug(this->log, "%p: transport stop", this); - - spa_loop_invoke(this->data_loop, do_remove_source, 0, NULL, 0, true, this); - - if (this->transport && this->transport_acquired) - res = spa_bt_transport_release(this->transport); - else - res = 0; - - this->transport_acquired = false; - - if (this->codec_data) - this->codec->deinit(this->codec_data); - this->codec_data = NULL; - - spa_bt_decode_buffer_clear(&port->buffer); - - return res; -} - -static int do_stop(struct impl *this) -{ - int res; - - if (!this->started) - return 0; - - spa_log_debug(this->log, "%p: stop", this); - - res = transport_stop(this); - - this->started = false; - - return res; -} - -static int impl_node_send_command(void *object, const struct spa_command *command) -{ - struct impl *this = object; - struct port *port; - int res; - - spa_return_val_if_fail(this != NULL, -EINVAL); - spa_return_val_if_fail(command != NULL, -EINVAL); - - port = &this->port; - - switch (SPA_NODE_COMMAND_ID(command)) { - case SPA_NODE_COMMAND_Start: - if (!port->have_format) - return -EIO; - if (port->n_buffers == 0) - return -EIO; - - if ((res = do_start(this)) < 0) - return res; - break; - case SPA_NODE_COMMAND_Suspend: - case SPA_NODE_COMMAND_Pause: - if ((res = do_stop(this)) < 0) - return res; - break; - default: - return -ENOTSUP; - } - return 0; -} - -static void emit_node_info(struct impl *this, bool full) -{ - uint64_t old = full ? this->info.change_mask : 0; - - struct spa_dict_item node_info_items = { - { SPA_KEY_DEVICE_API, "bluez5" }, - { SPA_KEY_MEDIA_CLASS, this->is_input ? "Audio/Source" : "Stream/Output/Audio" }, - { SPA_KEY_NODE_LATENCY, this->is_input ? "" : "512/48000" }, - { "media.name", ((this->transport && this->transport->device->name) ? - this->transport->device->name : "A2DP") }, - { SPA_KEY_NODE_DRIVER, this->is_input ? "true" : "false" }, - }; - - if (full) - this->info.change_mask = this->info_all; - if (this->info.change_mask) { - this->info.props = &SPA_DICT_INIT_ARRAY(node_info_items); - spa_node_emit_info(&this->hooks, &this->info); - this->info.change_mask = old; - } -} - -static void emit_port_info(struct impl *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) { - spa_node_emit_port_info(&this->hooks, - SPA_DIRECTION_OUTPUT, 0, &port->info); - port->info.change_mask = old; - } -} - -static int -impl_node_add_listener(void *object, - struct spa_hook *listener, - const struct spa_node_events *events, - void *data) -{ - struct impl *this = object; - struct spa_hook_list save; - - spa_return_val_if_fail(this != NULL, -EINVAL); - - spa_hook_list_isolate(&this->hooks, &save, listener, events, data); - - emit_node_info(this, true); - emit_port_info(this, &this->port, true); - - spa_hook_list_join(&this->hooks, &save); - - return 0; -} - -static int -impl_node_set_callbacks(void *object, - const struct spa_node_callbacks *callbacks, - void *data) -{ - struct impl *this = object; - - spa_return_val_if_fail(this != NULL, -EINVAL); - - this->callbacks = SPA_CALLBACKS_INIT(callbacks, data); - - return 0; -} - -static int impl_node_sync(void *object, int seq) -{ - struct impl *this = object; - - spa_return_val_if_fail(this != NULL, -EINVAL); - - spa_node_emit_result(&this->hooks, seq, 0, 0, NULL); - - return 0; -} - -static int impl_node_add_port(void *object, enum spa_direction direction, uint32_t port_id, - const struct spa_dict *props) -{ - return -ENOTSUP; -} - -static int impl_node_remove_port(void *object, enum spa_direction direction, uint32_t port_id) -{ - return -ENOTSUP; -} - -static int -impl_node_port_enum_params(void *object, int seq, - enum spa_direction direction, uint32_t port_id, - uint32_t id, uint32_t start, uint32_t num, - const struct spa_pod *filter) -{ - - struct impl *this = object; - struct port *port; - struct spa_pod *param; - struct spa_pod_builder b = { 0 }; - uint8_t buffer1024; - struct spa_result_node_params result; - uint32_t count = 0; - int res; - - spa_return_val_if_fail(this != NULL, -EINVAL); - spa_return_val_if_fail(num != 0, -EINVAL); - - spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL); - port = &this->port; - - result.id = id; - result.next = start; - next: - result.index = result.next++; - - spa_pod_builder_init(&b, buffer, sizeof(buffer)); - - switch (id) { - case SPA_PARAM_EnumFormat: - if (result.index > 0) - return 0; - if (this->codec == NULL) - return -EIO; - if (this->transport == NULL) - return -EIO; - - if ((res = this->codec->enum_config(this->codec, - this->is_duplex ? 0 : A2DP_CODEC_FLAG_SINK, - this->transport->configuration, - this->transport->configuration_len, - id, result.index, &b, ¶m)) != 1) - return res; - break; - - case SPA_PARAM_Format: - if (!port->have_format) - return -EIO; - if (result.index > 0) - return 0; - - param = spa_format_audio_raw_build(&b, id, &port->current_format.info.raw); - break; - - case SPA_PARAM_Buffers: - if (!port->have_format) - return -EIO; - if (result.index > 0) - return 0; - - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_ParamBuffers, id, - SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(2, 1, MAX_BUFFERS), - SPA_PARAM_BUFFERS_blocks, SPA_POD_Int(1), - SPA_PARAM_BUFFERS_size, SPA_POD_CHOICE_RANGE_Int( - this->quantum_limit * port->frame_size, - 16 * port->frame_size, - INT32_MAX), - SPA_PARAM_BUFFERS_stride, SPA_POD_Int(port->frame_size)); - break; - - case SPA_PARAM_Meta: - switch (result.index) { - case 0: - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_ParamMeta, id, - SPA_PARAM_META_type, SPA_POD_Id(SPA_META_Header), - SPA_PARAM_META_size, SPA_POD_Int(sizeof(struct spa_meta_header))); - 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_Buffers), - SPA_PARAM_IO_size, SPA_POD_Int(sizeof(struct spa_io_buffers))); - break; - case 1: - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_ParamIO, id, - SPA_PARAM_IO_id, SPA_POD_Id(SPA_IO_RateMatch), - SPA_PARAM_IO_size, SPA_POD_Int(sizeof(struct spa_io_rate_match))); - break; - default: - return 0; - } - break; - - case SPA_PARAM_Latency: - switch (result.index) { - case 0: - param = spa_latency_build(&b, id, &port->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; -} - -static int clear_buffers(struct impl *this, struct port *port) -{ - do_stop(this); - if (port->n_buffers > 0) { - spa_list_init(&port->free); - spa_list_init(&port->ready); - port->n_buffers = 0; - } - return 0; -} - -static int port_set_format(struct impl *this, struct port *port, - uint32_t flags, - const struct spa_pod *format) -{ - int err; - - if (format == NULL) { - spa_log_debug(this->log, "clear format"); - clear_buffers(this, port); - port->have_format = false; - } else { - struct spa_audio_info info = { 0 }; - - if ((err = spa_format_parse(format, &info.media_type, &info.media_subtype)) < 0) - return err; - - if (info.media_type != SPA_MEDIA_TYPE_audio || - info.media_subtype != SPA_MEDIA_SUBTYPE_raw) - return -EINVAL; - - if (spa_format_audio_raw_parse(format, &info.info.raw) < 0) - return -EINVAL; - - port->frame_size = info.info.raw.channels; - - switch (info.info.raw.format) { - case SPA_AUDIO_FORMAT_S16: - port->frame_size *= 2; - break; - case SPA_AUDIO_FORMAT_S24: - port->frame_size *= 3; - break; - case SPA_AUDIO_FORMAT_S24_32: - case SPA_AUDIO_FORMAT_S32: - case SPA_AUDIO_FORMAT_F32: - port->frame_size *= 4; - break; - default: - return -EINVAL; - } - - port->current_format = info; - port->have_format = true; - } - - port->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS; - if (port->have_format) { - port->info.change_mask |= SPA_PORT_CHANGE_MASK_FLAGS; - port->info.flags = SPA_PORT_FLAG_LIVE; - port->info.change_mask |= SPA_PORT_CHANGE_MASK_RATE; - port->info.rate = SPA_FRACTION(1, port->current_format.info.raw.rate); - port->paramsIDX_Format = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_READWRITE); - port->paramsIDX_Buffers = SPA_PARAM_INFO(SPA_PARAM_Buffers, SPA_PARAM_INFO_READ); - port->paramsIDX_Latency.flags ^= SPA_PARAM_INFO_SERIAL; - } else { - port->paramsIDX_Format = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE); - port->paramsIDX_Buffers = SPA_PARAM_INFO(SPA_PARAM_Buffers, 0); - } - emit_port_info(this, port, false); - - return 0; -} - -static int -impl_node_port_set_param(void *object, - enum spa_direction direction, uint32_t port_id, - uint32_t id, uint32_t flags, - const struct spa_pod *param) -{ - struct impl *this = object; - struct port *port; - int res; - - spa_return_val_if_fail(this != NULL, -EINVAL); - - spa_return_val_if_fail(CHECK_PORT(node, direction, port_id), -EINVAL); - port = &this->port; - - switch (id) { - case SPA_PARAM_Format: - res = port_set_format(this, port, flags, param); - break; - case SPA_PARAM_Latency: - res = 0; - break; - default: - res = -ENOENT; - break; - } - return res; -} - -static int -impl_node_port_use_buffers(void *object, - enum spa_direction direction, uint32_t port_id, - uint32_t flags, - struct spa_buffer **buffers, uint32_t n_buffers) -{ - struct impl *this = object; - struct port *port; - uint32_t i; - - spa_return_val_if_fail(this != NULL, -EINVAL); - - spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL); - port = &this->port; - - spa_log_debug(this->log, "use buffers %d", n_buffers); - - if (!port->have_format) - return -EIO; - - clear_buffers(this, port); - - for (i = 0; i < n_buffers; i++) { - struct buffer *b = &port->buffersi; - struct spa_data *d = buffersi->datas; - - b->buf = buffersi; - b->id = i; - - b->h = spa_buffer_find_meta_data(buffersi, SPA_META_Header, sizeof(*b->h)); - - if (d0.data == NULL) { - spa_log_error(this->log, "%p: need mapped memory", this); - return -EINVAL; - } - spa_list_append(&port->free, &b->link); - b->outstanding = false; - } - port->n_buffers = n_buffers; - - return 0; -} - -static int -impl_node_port_set_io(void *object, - enum spa_direction direction, - uint32_t port_id, - uint32_t id, - void *data, size_t size) -{ - struct impl *this = object; - struct port *port; - - spa_return_val_if_fail(this != NULL, -EINVAL); - - spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL); - port = &this->port; - - switch (id) { - case SPA_IO_Buffers: - port->io = data; - break; - case SPA_IO_RateMatch: - port->rate_match = data; - break; - default: - return -ENOENT; - } - return 0; -} - -static int impl_node_port_reuse_buffer(void *object, uint32_t port_id, uint32_t buffer_id) -{ - struct impl *this = object; - struct port *port; - - spa_return_val_if_fail(this != NULL, -EINVAL); - - spa_return_val_if_fail(port_id == 0, -EINVAL); - port = &this->port; - - if (port->n_buffers == 0) - return -EIO; - - if (buffer_id >= port->n_buffers) - return -EINVAL; - - recycle_buffer(this, port, buffer_id); - - return 0; -} - -static uint32_t get_samples(struct impl *this, uint32_t *duration) -{ - struct port *port = &this->port; - uint32_t samples; - - if (SPA_LIKELY(port->rate_match) && this->resampling) { - samples = port->rate_match->size; - } else { - if (SPA_LIKELY(this->position)) - samples = this->position->clock.duration * port->current_format.info.raw.rate - / this->position->clock.rate.denom; - else - samples = 1024; - } - - if (SPA_LIKELY(this->position)) - *duration = this->position->clock.duration * port->current_format.info.raw.rate - / this->position->clock.rate.denom; - else if (SPA_LIKELY(this->clock)) - *duration = this->clock->duration * port->current_format.info.raw.rate - / this->clock->rate.denom; - else - *duration = 1024 * port->current_format.info.raw.rate / 48000; - - return samples; -} - -static void process_buffering(struct impl *this) -{ - struct port *port = &this->port; - uint32_t duration; - const uint32_t samples = get_samples(this, &duration); - uint32_t avail; - void *buf; - - spa_bt_decode_buffer_process(&port->buffer, samples, duration); - - setup_matching(this); - - buf = spa_bt_decode_buffer_get_read(&port->buffer, &avail); - - /* copy data to buffers */ - if (!spa_list_is_empty(&port->free) && avail > 0) { - struct buffer *buffer; - struct spa_data *datas; - uint32_t data_size; - - data_size = samples * port->frame_size; - - avail = SPA_MIN(avail, data_size); - - spa_bt_decode_buffer_read(&port->buffer, avail); - - buffer = spa_list_first(&port->free, struct buffer, link); - spa_list_remove(&buffer->link); - - spa_log_trace(this->log, "dequeue %d", buffer->id); - - if (buffer->h) { - buffer->h->seq = this->sample_count; - buffer->h->pts = SPA_TIMESPEC_TO_NSEC(&this->now); - buffer->h->dts_offset = 0; - } - - datas = buffer->buf->datas; - - spa_assert(datas0.maxsize >= data_size); - - datas0.chunk->offset = 0; - datas0.chunk->size = avail; - datas0.chunk->stride = port->frame_size; - - memcpy(datas0.data, buf, avail); - - this->sample_count += avail / port->frame_size; - - /* ready buffer if full */ - spa_log_trace(this->log, "queue %d frames:%d", buffer->id, (int)avail / port->frame_size); - spa_list_append(&port->ready, &buffer->link); - } -} - -static int impl_node_process(void *object) -{ - struct impl *this = object; - struct port *port; - struct spa_io_buffers *io; - struct buffer *buffer; - - spa_return_val_if_fail(this != NULL, -EINVAL); - - port = &this->port; - if ((io = port->io) == NULL) - return -EIO; - - spa_log_trace(this->log, "%p status:%d", this, io->status); - - /* Return if we already have a buffer */ - if (io->status == SPA_STATUS_HAVE_DATA) - return SPA_STATUS_HAVE_DATA; - - /* Recycle */ - if (io->buffer_id < port->n_buffers) { - recycle_buffer(this, port, io->buffer_id); - io->buffer_id = SPA_ID_INVALID; - } - - /* Handle buffering delay */ - process_buffering(this); - - /* Return if there are no buffers ready to be processed */ - if (spa_list_is_empty(&port->ready)) - return SPA_STATUS_OK; - - /* Get the new buffer from the ready list */ - buffer = spa_list_first(&port->ready, struct buffer, link); - spa_list_remove(&buffer->link); - buffer->outstanding = true; - - /* Set the new buffer in IO */ - io->buffer_id = buffer->id; - io->status = SPA_STATUS_HAVE_DATA; - - /* Notify we have a buffer ready to be processed */ - return SPA_STATUS_HAVE_DATA; -} - -static const struct spa_node_methods impl_node = { - SPA_VERSION_NODE_METHODS, - .add_listener = impl_node_add_listener, - .set_callbacks = impl_node_set_callbacks, - .sync = impl_node_sync, - .enum_params = impl_node_enum_params, - .set_param = impl_node_set_param, - .set_io = impl_node_set_io, - .send_command = impl_node_send_command, - .add_port = impl_node_add_port, - .remove_port = impl_node_remove_port, - .port_enum_params = impl_node_port_enum_params, - .port_set_param = impl_node_port_set_param, - .port_use_buffers = impl_node_port_use_buffers, - .port_set_io = impl_node_port_set_io, - .port_reuse_buffer = impl_node_port_reuse_buffer, - .process = impl_node_process, -}; - -static int do_transport_destroy(struct spa_loop *loop, - bool async, - uint32_t seq, - const void *data, - size_t size, - void *user_data) -{ - struct impl *this = user_data; - this->transport = NULL; - this->transport_acquired = false; - return 0; -} - -static void transport_destroy(void *data) -{ - struct impl *this = data; - spa_log_debug(this->log, "transport %p destroy", this->transport); - spa_loop_invoke(this->data_loop, do_transport_destroy, 0, NULL, 0, true, this); -} - -static const struct spa_bt_transport_events transport_events = { - SPA_VERSION_BT_TRANSPORT_EVENTS, - .destroy = transport_destroy, -}; - -static int impl_get_interface(struct spa_handle *handle, const char *type, void **interface) -{ - struct impl *this; - - spa_return_val_if_fail(handle != NULL, -EINVAL); - spa_return_val_if_fail(interface != NULL, -EINVAL); - - this = (struct impl *) handle; - - if (spa_streq(type, SPA_TYPE_INTERFACE_Node)) - *interface = &this->node; - else - return -ENOENT; - - return 0; -} - -static int impl_clear(struct spa_handle *handle) -{ - struct impl *this = (struct impl *) handle; - struct port *port = &this->port; - if (this->codec_data) - this->codec->deinit(this->codec_data); - if (this->codec_props && this->codec->clear_props) - this->codec->clear_props(this->codec_props); - if (this->transport) - spa_hook_remove(&this->transport_listener); - spa_system_close(this->data_system, this->timerfd); - if (this->duplex_timerfd >= 0) { - spa_system_close(this->data_system, this->duplex_timerfd); - this->duplex_timerfd = -1; - } - spa_bt_decode_buffer_clear(&port->buffer); - return 0; -} - -static size_t -impl_get_size(const struct spa_handle_factory *factory, - const struct spa_dict *params) -{ - return sizeof(struct impl); -} - -static int -impl_init(const struct spa_handle_factory *factory, - struct spa_handle *handle, - const struct spa_dict *info, - const struct spa_support *support, - uint32_t n_support) -{ - struct impl *this; - struct port *port; - const char *str; - - spa_return_val_if_fail(factory != NULL, -EINVAL); - spa_return_val_if_fail(handle != NULL, -EINVAL); - - handle->get_interface = impl_get_interface; - handle->clear = impl_clear; - - this = (struct impl *) handle; - - this->log = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log); - this->data_loop = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_DataLoop); - this->data_system = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_DataSystem); - - spa_log_topic_init(this->log, &log_topic); - - if (this->data_loop == NULL) { - spa_log_error(this->log, "a data loop is needed"); - return -EINVAL; - } - if (this->data_system == NULL) { - spa_log_error(this->log, "a data system is needed"); - return -EINVAL; - } - - this->node.iface = SPA_INTERFACE_INIT( - SPA_TYPE_INTERFACE_Node, - SPA_VERSION_NODE, - &impl_node, this); - spa_hook_list_init(&this->hooks); - - reset_props(&this->props); - - /* set the node info */ - this->info_all = SPA_NODE_CHANGE_MASK_FLAGS | - SPA_NODE_CHANGE_MASK_PROPS | - SPA_NODE_CHANGE_MASK_PARAMS; - this->info = SPA_NODE_INFO_INIT(); - this->info.max_input_ports = 0; - this->info.max_output_ports = 1; - this->info.flags = SPA_NODE_FLAG_RT; - this->paramsIDX_PropInfo = SPA_PARAM_INFO(SPA_PARAM_PropInfo, SPA_PARAM_INFO_READ); - this->paramsIDX_Props = SPA_PARAM_INFO(SPA_PARAM_Props, SPA_PARAM_INFO_READWRITE); - this->paramsIDX_NODE_IO = SPA_PARAM_INFO(SPA_PARAM_IO, SPA_PARAM_INFO_READ); - this->info.params = this->params; - this->info.n_params = N_NODE_PARAMS; - - /* set the port info */ - port = &this->port; - port->info_all = SPA_PORT_CHANGE_MASK_FLAGS | - SPA_PORT_CHANGE_MASK_PARAMS; - port->info = SPA_PORT_INFO_INIT(); - port->info.change_mask = SPA_PORT_CHANGE_MASK_FLAGS; - port->info.flags = SPA_PORT_FLAG_LIVE | - SPA_PORT_FLAG_TERMINAL; - port->paramsIDX_EnumFormat = SPA_PARAM_INFO(SPA_PARAM_EnumFormat, SPA_PARAM_INFO_READ); - port->paramsIDX_Meta = SPA_PARAM_INFO(SPA_PARAM_Meta, SPA_PARAM_INFO_READ); - port->paramsIDX_IO = SPA_PARAM_INFO(SPA_PARAM_IO, SPA_PARAM_INFO_READ); - port->paramsIDX_Format = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE); - port->paramsIDX_Buffers = SPA_PARAM_INFO(SPA_PARAM_Buffers, 0); - port->paramsIDX_Latency = SPA_PARAM_INFO(SPA_PARAM_Latency, SPA_PARAM_INFO_READWRITE); - port->info.params = port->params; - port->info.n_params = N_PORT_PARAMS; - - port->latency = SPA_LATENCY_INFO(SPA_DIRECTION_OUTPUT); - port->latency.min_quantum = 1.0f; - port->latency.max_quantum = 1.0f; - - /* Init the buffer lists */ - spa_list_init(&port->ready); - spa_list_init(&port->free); - - this->quantum_limit = 8192; - - if (info != NULL) { - if (info && (str = spa_dict_lookup(info, "clock.quantum-limit"))) - spa_atou32(str, &this->quantum_limit, 0); - if ((str = spa_dict_lookup(info, SPA_KEY_API_BLUEZ5_TRANSPORT)) != NULL) - sscanf(str, "pointer:%p", &this->transport); - if ((str = spa_dict_lookup(info, "bluez5.a2dp-source-role")) != NULL) - this->is_input = spa_streq(str, "input"); - if ((str = spa_dict_lookup(info, "api.bluez5.a2dp-duplex")) != NULL) - this->is_duplex = spa_atob(str); - } - - if (this->transport == NULL) { - spa_log_error(this->log, "a transport is needed"); - return -EINVAL; - } - if (this->transport->a2dp_codec == NULL) { - spa_log_error(this->log, "a transport codec is needed"); - return -EINVAL; - } - this->codec = this->transport->a2dp_codec; - - if (this->is_duplex) { - if (!this->codec->duplex_codec) { - spa_log_error(this->log, "transport codec doesn't support duplex"); - return -EINVAL; - } - this->codec = this->codec->duplex_codec; - this->is_input = true; - } - this->use_duplex_source = this->is_duplex || (this->codec->duplex_codec != NULL); - - if (this->codec->init_props != NULL) - this->codec_props = this->codec->init_props(this->codec, - this->is_duplex ? 0 : A2DP_CODEC_FLAG_SINK, - this->transport->device->settings); - - spa_bt_transport_add_listener(this->transport, - &this->transport_listener, &transport_events, this); - - this->timerfd = spa_system_timerfd_create(this->data_system, - CLOCK_MONOTONIC, SPA_FD_CLOEXEC | SPA_FD_NONBLOCK); - - if (this->use_duplex_source) { - this->duplex_timerfd = spa_system_timerfd_create(this->data_system, - CLOCK_MONOTONIC, SPA_FD_CLOEXEC | SPA_FD_NONBLOCK); - } else { - this->duplex_timerfd = -1; - } - - return 0; -} - -static const struct spa_interface_info impl_interfaces = { - {SPA_TYPE_INTERFACE_Node,}, -}; - -static int -impl_enum_interface_info(const struct spa_handle_factory *factory, - const struct spa_interface_info **info, uint32_t *index) -{ - spa_return_val_if_fail(factory != NULL, -EINVAL); - spa_return_val_if_fail(info != NULL, -EINVAL); - spa_return_val_if_fail(index != NULL, -EINVAL); - - switch (*index) { - case 0: - *info = &impl_interfaces*index; - break; - default: - return 0; - } - (*index)++; - return 1; -} - -static const struct spa_dict_item info_items = { - { SPA_KEY_FACTORY_AUTHOR, "Collabora Ltd. <contact@collabora.com>" }, - { SPA_KEY_FACTORY_DESCRIPTION, "Capture bluetooth audio with a2dp" }, - { SPA_KEY_FACTORY_USAGE, SPA_KEY_API_BLUEZ5_TRANSPORT"=<transport>" }, -}; - -static const struct spa_dict info = SPA_DICT_INIT_ARRAY(info_items); - -const struct spa_handle_factory spa_a2dp_source_factory = { - SPA_VERSION_HANDLE_FACTORY, - SPA_NAME_API_BLUEZ5_A2DP_SOURCE, - &info, - impl_get_size, - impl_init, - impl_enum_interface_info, -};
View file
pipewire-0.3.58.tar.gz/.gitlab-ci.yml -> pipewire-0.3.59.tar.gz/.gitlab-ci.yml
Changed
@@ -268,9 +268,9 @@ extends: - .build_on_fedora variables: - # Fedora doesn't have libfreeaptx, lc3plus, or roc + # Fedora doesn't have libfreeaptx, lc3plus, lc3, or roc # libcamera has no stable API, so let's not chase that target - MESON_OPTIONS: "-Dauto_features=enabled -Dbluez5-codec-aptx=disabled -Dbluez5-codec-lc3plus=disabled -Droc=disabled -Dlibcamera=disabled" + MESON_OPTIONS: "-Dauto_features=enabled -Dbluez5-codec-aptx=disabled -Dbluez5-codec-lc3plus=disabled -Dbluez5-codec-lc3=disabled -Droc=disabled -Dlibcamera=disabled" parallel: matrix: - CC: gcc, clang
View file
pipewire-0.3.58.tar.gz/NEWS -> pipewire-0.3.59.tar.gz/NEWS
Changed
@@ -1,3 +1,80 @@ +# PipeWire 0.3.59 (2022-09-30) + +This is a bugfix release that is API and ABI compatible with previous +0.3.x releases. + +## Highlights + - Fix possible wrong samplerate in loopback streams after suspend and + rate switch. + - module-filter-chain can now adapt to the graph samplerate. + - Fix some potential stuttering and crackling in pulse-server. + - Add Bluetooth LE support. This requires experimental kernel and bluez + support. + - The ALSA plugin has more options to control the buffer size. This can + be used to work around high latency in davinci resolve. + - Many bugfixes and improvements. + + +## PipeWire + - Add audio capture example with volume meter. + - Fix a case where a rate switch would not suspend all the nodes of the + driver first. This could cause wrong samplerates in streams. + - Fix a case where a node would be Paused while still added to the + graph, causing potential crashes. (#2701) + +## Modules + - module-filter-chain and module-loopback now use the resample.prefill + option to avoid buffering extra samples and causing unwanted latency + when resampling is activated. + - module-filter-chain can now adapt to the graph samplerate. + - Improve module-raop to support the ALAC codec as raw PCM. + - Improve RTSP parsing to improve compatibility. + +## Tools + - Fix 100% CPU in pw-cli monitor mode. (#2709) + - spa-acp-tool can now be exited with ctrl-D. + +## SPA + - Various libcamera fixes and improvements. + - Set stride on audioconvert output buffers. + - Make sure we always place the last requested size from the resampler + on the buffers in pw-stream. + - Add resample.prefill option in the resampler to fill the history with + 0 so that we don't have smaller buffers at the start. + - Make sure that when an overflow corrupts a POD, that it will always + stay corrupted. + - Rate limit some ALSA warnings and reduce some unwanted warnings. + - Don't recalculate the audioconverter state for each pause/play. (#2701) + - Fix some POD parsing inconsistencies and potential overflows. + - Add support for Asus Xonar SE. + - Fix Flush command handling. It should not stop playback. (#2726) + - Refactor the peaks function and add some unit tests and optimizations. + - The channelmix has an optimized nXm converter and new unit tests. + - Normalization in the channelmixer was fixed. + +## pulse-server + - The requested latency of record streams was reduced to fix some + stuttering in Teamspeak. (#2702) + - Tweak the max amount of bytes sent to a client. (#2711) (#2715) + - Improve maxlength calculations, this fixes some crackling noise with + high samplerate and channel counts in some players (audacious). + +## Bluetooth + - Merge Bluetooth LE support. + - Make sure we are backward compatible with WirePlumber. + - Fix some HFP and HSP AT command parsing. (#2463) + - Use HFP by default over HSP. + +## ALSA + - Increase max number of periods. + - The parameters handling was improved. There is now an option to set the + buffer-bytes of the ALSA plugin. + - PIPEWIRE_ALSA can now be used as an environment variable to restrict the + plugin formats and buffer size. + +Older versions: + + # PipeWire 0.3.58 (2022-09-15) This is a bugfix release that is API and ABI compatible with previous @@ -64,9 +141,6 @@ wakeups. (#1697) -Older versions: - - # PipeWire 0.3.57 (2022-09-02) This is a bugfix release that is API and ABI compatible with previous
View file
pipewire-0.3.58.tar.gz/man/pipewire.1.rst.in -> pipewire-0.3.59.tar.gz/man/pipewire.1.rst.in
Changed
@@ -47,6 +47,8 @@ SEE ALSO ======== +``pw-top(1)``, +``pw-dump(1)``, ``pw-mon(1)``, ``pw-cat(1)``, ``pw-cli(1)``,
View file
pipewire-0.3.58.tar.gz/man/pw-profiler.1.rst.in -> pipewire-0.3.59.tar.gz/man/pw-profiler.1.rst.in
Changed
@@ -27,6 +27,8 @@ SVG files from the .plot files is generated, along with a .html file to visualize the profiling results in a browser. +This function uses the same data used by *pw-top*. + OPTIONS ======= @@ -52,3 +54,4 @@ ======== ``pipewire(1)``, +``pw-top(1)``,
View file
pipewire-0.3.58.tar.gz/man/pw-top.1.rst.in -> pipewire-0.3.59.tar.gz/man/pw-top.1.rst.in
Changed
@@ -168,4 +168,7 @@ ======== ``pipewire(1)``, +``pw-dump(1)``, +``pw-cli(1)``, +``pw-profiler(1)``,
View file
pipewire-0.3.58.tar.gz/meson.build -> pipewire-0.3.59.tar.gz/meson.build
Changed
@@ -1,9 +1,9 @@ project('pipewire', 'c' , - version : '0.3.58', + version : '0.3.59', license : 'MIT', 'LGPL-2.1-or-later', 'GPL-2.0-only' , meson_version : '>= 0.59.0', default_options : 'warning_level=3', - 'c_std=gnu99', + 'c_std=gnu11', 'cpp_std=c++17', 'b_pie=true', #'b_sanitize=address,undefined',
View file
pipewire-0.3.58.tar.gz/meson_options.txt -> pipewire-0.3.59.tar.gz/meson_options.txt
Changed
@@ -120,6 +120,10 @@ description: 'Enable Opus open source codec implementation', type: 'feature', value: 'auto') +option('bluez5-codec-lc3', + description: 'Enable LC3 open source codec implementation', + type: 'feature', + value: 'disabled') option('control', description: 'Enable control spa plugin integration', type: 'feature',
View file
pipewire-0.3.58.tar.gz/pipewire-alsa/alsa-plugins/pcm_pipewire.c -> pipewire-0.3.59.tar.gz/pipewire-alsa/alsa-plugins/pcm_pipewire.c
Changed
@@ -64,6 +64,26 @@ #define MIN_PERIOD 64 +#define MIN_PERIOD_BYTES (128) +#define MAX_PERIOD_BYTES (2*1024*1024) + +#define MIN_BUFFER_BYTES (2*MIN_PERIOD_BYTES) +#define MAX_BUFFER_BYTES (2*MAX_PERIOD_BYTES) + +struct params { + const char *node_name; + const char *server_name; + const char *playback_node; + const char *capture_node; + const char *role; + snd_pcm_format_t format; + int rate; + int channels; + int period_bytes; + int buffer_bytes; + uint32_t flags; +}; + typedef struct { snd_pcm_ioplug_t io; @@ -178,6 +198,7 @@ pw_thread_loop_destroy(pw->main_loop); free(pw->node_name); free(pw->target); + free(pw->role); snd_output_close(pw->output); fclose(pw->log_file); free(pw); @@ -942,8 +963,7 @@ .query_chmaps = snd_pcm_pipewire_query_chmaps, }; -static int pipewire_set_hw_constraint(snd_pcm_pipewire_t *pw, int rate, - snd_pcm_format_t format, int channels, int period_bytes) +static int pipewire_set_hw_constraint(snd_pcm_pipewire_t *pw, struct params *p) { unsigned int access_list = { SND_PCM_ACCESS_MMAP_INTERLEAVED, @@ -975,26 +995,38 @@ int max_channels; int min_period_bytes; int max_period_bytes; + int min_buffer_bytes; + int max_buffer_bytes; int err; - if (rate > 0) { - min_rate = max_rate = rate; + if (p->rate > 0) { + min_rate = max_rate = SPA_CLAMP(p->rate, 1, MAX_RATE); } else { min_rate = 1; max_rate = MAX_RATE; } - if (channels > 0) { - min_channels = max_channels = channels; + if (p->channels > 0) { + min_channels = max_channels = SPA_CLAMP(p->channels, 1, MAX_CHANNELS); } else { min_channels = 1; max_channels = MAX_CHANNELS; } - if (period_bytes > 0) { - min_period_bytes = max_period_bytes = period_bytes; + if (p->period_bytes > 0) { + min_period_bytes = max_period_bytes = SPA_CLAMP(p->period_bytes, + MIN_PERIOD_BYTES, MAX_PERIOD_BYTES); } else { - min_period_bytes = 128; - max_period_bytes = 2*1024*1024; + min_period_bytes = MIN_PERIOD_BYTES; + max_period_bytes = MAX_PERIOD_BYTES; } + if (p->buffer_bytes > 0) { + min_buffer_bytes = max_buffer_bytes = SPA_CLAMP(p->buffer_bytes, + MIN_BUFFER_BYTES, MAX_BUFFER_BYTES); + } else { + min_buffer_bytes = MIN_BUFFER_BYTES; + max_buffer_bytes = MAX_BUFFER_BYTES; + } + if (min_period_bytes * 2 > max_buffer_bytes) + min_period_bytes = max_period_bytes = max_buffer_bytes / 2; if ((err = snd_pcm_ioplug_set_param_list(&pw->io, SND_PCM_IOPLUG_HW_ACCESS, SPA_N_ELEMENTS(access_list), access_list)) < 0 || @@ -1003,22 +1035,22 @@ (err = snd_pcm_ioplug_set_param_minmax(&pw->io, SND_PCM_IOPLUG_HW_RATE, min_rate, max_rate)) < 0 || (err = snd_pcm_ioplug_set_param_minmax(&pw->io, SND_PCM_IOPLUG_HW_BUFFER_BYTES, - MIN_BUFFERS*min_period_bytes, - MIN_BUFFERS*max_period_bytes)) < 0 || + min_buffer_bytes, + max_buffer_bytes)) < 0 || (err = snd_pcm_ioplug_set_param_minmax(&pw->io, SND_PCM_IOPLUG_HW_PERIOD_BYTES, min_period_bytes, max_period_bytes)) < 0 || (err = snd_pcm_ioplug_set_param_minmax(&pw->io, SND_PCM_IOPLUG_HW_PERIODS, - MIN_BUFFERS, MAX_BUFFERS)) < 0) { + MIN_BUFFERS, 1024)) < 0) { pw_log_warn("Can't set param list: %s", snd_strerror(err)); return err; } - if (format != SND_PCM_FORMAT_UNKNOWN) { + if (p->format != SND_PCM_FORMAT_UNKNOWN) { err = snd_pcm_ioplug_set_param_list(&pw->io, SND_PCM_IOPLUG_HW_FORMAT, - 1, (unsigned int *)&format); + 1, (unsigned int *)&p->format); if (err < 0) { pw_log_warn("Can't set param list: %s", snd_strerror(err)); return err; @@ -1075,60 +1107,77 @@ .write = log_write, }; -static int snd_pcm_pipewire_open(snd_pcm_t **pcmp, const char *name, - const char *node_name, - const char *server_name, - const char *playback_node, - const char *capture_node, - const char *role, - snd_pcm_stream_t stream, - int mode, - uint32_t flags, - int rate, - snd_pcm_format_t format, - int channels, - int period_bytes) +static int snd_pcm_pipewire_open(snd_pcm_t **pcmp, + struct params *p, snd_pcm_stream_t stream, int mode) { snd_pcm_pipewire_t *pw; int err; const char *str; struct pw_properties *props = NULL; struct pw_loop *loop; + uint32_t val; assert(pcmp); pw = calloc(1, sizeof(*pw)); if (!pw) return -ENOMEM; + props = pw_properties_new(NULL, NULL); + if (props == NULL) { + err = -errno; + goto error; + } + + str = getenv("PIPEWIRE_ALSA"); + if (str != NULL) { + pw_properties_update_string(props, str, strlen(str)); + if ((str = pw_properties_get(props, "alsa.format"))) + p->format = snd_pcm_format_value(str); + if ((str = pw_properties_get(props, "alsa.rate")) && + spa_atou32(str, &val, 0)) + p->rate = val; + if ((str = pw_properties_get(props, "alsa.channels")) && + spa_atou32(str, &val, 0)) + p->channels = val; + if ((str = pw_properties_get(props, "alsa.period-bytes")) && + spa_atou32(str, &val, 0)) + p->period_bytes = val; + if ((str = pw_properties_get(props, "alsa.buffer-bytes")) && + spa_atou32(str, &val, 0)) + p->buffer_bytes = val; + } + str = getenv("PIPEWIRE_REMOTE"); if (str != NULL && str0 != '\0') - server_name = str; + p->server_name = str; str = getenv("PIPEWIRE_NODE"); pw_log_debug("%p: open name:%s stream:%s mode:%d flags:%08x rate:%d format:%s " - "channels:%d period-bytes:%d target:'%s'", pw, name, - snd_pcm_stream_name(stream), mode, flags, rate, - snd_pcm_format_name(format), channels, period_bytes, str); + "channels:%d period-bytes:%d buffer-bytes:%d target:'%s'", pw, p->node_name, + snd_pcm_stream_name(stream), mode, p->flags, p->rate, + snd_pcm_format_name(p->format), p->channels, p->period_bytes, + p->buffer_bytes, str); pw->fd = -1; pw->io.poll_fd = -1; - pw->flags = flags; + pw->flags = p->flags; pw->log_file = fopencookie(pw, "w", io_funcs); if (pw->log_file == NULL) { pw_log_error("can't create log file: %m"); - return -errno; + err = -errno; + goto error; } if ((err = snd_output_stdio_attach(&pw->output, pw->log_file, 0)) < 0) { pw_log_error("can't attach log file: %s", snd_strerror(err)); - return err; + goto error; } - if (node_name == NULL) + if (p->node_name == NULL) pw->node_name = spa_aprintf("ALSA %s", stream == SND_PCM_STREAM_PLAYBACK ? "Playback" : "Capture"); else - pw->node_name = strdup(node_name); + pw->node_name = strdup(p->node_name); if (pw->node_name == NULL) { err = -errno; @@ -1140,12 +1189,12 @@ pw->target = strdup(str); else { if (stream == SND_PCM_STREAM_PLAYBACK) - pw->target = playback_node ? strdup(playback_node) : NULL; + pw->target = p->playback_node ? strdup(p->playback_node) : NULL; else - pw->target = capture_node ? strdup(capture_node) : NULL; + pw->target = p->capture_node ? strdup(p->capture_node) : NULL; } - pw->role = (role && *role) ? strdup(role) : NULL; + pw->role = (p->role && *p->role) ? strdup(p->role) : NULL; pw->main_loop = pw_thread_loop_new("alsa-pipewire", NULL); if (pw->main_loop == NULL) { @@ -1163,13 +1212,11 @@ goto error; } - props = pw_properties_new(NULL, NULL); - pw_properties_setf(props, PW_KEY_APP_NAME, "PipeWire ALSA %s", pw_get_prgname()); - if (server_name) - pw_properties_set(props, PW_KEY_REMOTE_NAME, server_name); + if (p->server_name) + pw_properties_set(props, PW_KEY_REMOTE_NAME, p->server_name); if ((err = pw_thread_loop_start(pw->main_loop)) < 0) goto error; @@ -1201,15 +1248,13 @@ #endif pw->io.flags |= SND_PCM_IOPLUG_FLAG_MONOTONIC; - if ((err = snd_pcm_ioplug_create(&pw->io, name, stream, mode)) < 0) + if ((err = snd_pcm_ioplug_create(&pw->io, p->node_name, stream, mode)) < 0) goto error; - - if ((err = pipewire_set_hw_constraint(pw, rate, format, channels, - period_bytes)) < 0) + if ((err = pipewire_set_hw_constraint(pw, p)) < 0) goto error; - pw_log_debug("%p: opened name:%s stream:%s mode:%d", pw, name, + pw_log_debug("%p: opened name:%s stream:%s mode:%d", pw, p->node_name, snd_pcm_stream_name(pw->io.stream), mode); *pcmp = pw->io.pcm; @@ -1217,7 +1262,7 @@ return 0; error: - pw_log_debug("%p: failed to open %s :%s", pw, name, spa_strerror(err)); + pw_log_debug("%p: failed to open %s :%s", pw, p->node_name, spa_strerror(err)); pw_properties_free(props); snd_pcm_pipewire_free(pw); return err; @@ -1228,16 +1273,7 @@ SND_PCM_PLUGIN_DEFINE_FUNC(pipewire) { snd_config_iterator_t i, next; - const char *node_name = NULL; - const char *server_name = NULL; - const char *playback_node = NULL; - const char *capture_node = NULL; - const char *role = NULL; - snd_pcm_format_t format = SND_PCM_FORMAT_UNKNOWN; - int rate = 0; - int channels = 0; - int period_bytes = 0; - uint32_t flags = 0; + struct params params; int err; pw_init(NULL, NULL); @@ -1245,6 +1281,8 @@ return -ENOTSUP; PW_LOG_TOPIC_INIT(alsa_log_topic); + spa_zero(params); + params.format = SND_PCM_FORMAT_UNKNOWN; snd_config_for_each(i, next, conf) { snd_config_t *n = snd_config_iterator_entry(i); @@ -1254,35 +1292,35 @@ if (spa_streq(id, "comment") || spa_streq(id, "type") || spa_streq(id, "hint")) continue; if (spa_streq(id, "name")) { - snd_config_get_string(n, &node_name); + snd_config_get_string(n, ¶ms.node_name); continue; } if (spa_streq(id, "server")) { - snd_config_get_string(n, &server_name); + snd_config_get_string(n, ¶ms.server_name); continue; } if (spa_streq(id, "playback_node")) { - snd_config_get_string(n, &playback_node); + snd_config_get_string(n, ¶ms.playback_node); continue; } if (spa_streq(id, "capture_node")) { - snd_config_get_string(n, &capture_node); + snd_config_get_string(n, ¶ms.capture_node); continue; } if (spa_streq(id, "role")) { - snd_config_get_string(n, &role); + snd_config_get_string(n, ¶ms.role); continue; } if (spa_streq(id, "exclusive")) { if (snd_config_get_bool(n)) - flags |= PW_STREAM_FLAG_EXCLUSIVE; + params.flags |= PW_STREAM_FLAG_EXCLUSIVE; continue; } if (spa_streq(id, "rate")) { long val; if (snd_config_get_integer(n, &val) == 0) - rate = val; + params.rate = val; else SNDERR("%s: invalid type", id); continue; @@ -1291,8 +1329,8 @@ const char *str; if (snd_config_get_string(n, &str) == 0) { - format = snd_pcm_format_value(str); - if (format == SND_PCM_FORMAT_UNKNOWN) + params.format = snd_pcm_format_value(str); + if (*str && params.format == SND_PCM_FORMAT_UNKNOWN) SNDERR("%s: invalid value %s", id, str); } else { SNDERR("%s: invalid type", id); @@ -1303,7 +1341,7 @@ long val; if (snd_config_get_integer(n, &val) == 0) - channels = val; + params.channels = val; else SNDERR("%s: invalid type", id); continue; @@ -1312,7 +1350,16 @@ long val; if (snd_config_get_integer(n, &val) == 0) - period_bytes = val; + params.period_bytes = val; + else + SNDERR("%s: invalid type", id); + continue; + } + if (spa_streq(id, "buffer_bytes")) { + long val; + + if (snd_config_get_integer(n, &val) == 0) + params.buffer_bytes = val; else SNDERR("%s: invalid type", id); continue; @@ -1321,9 +1368,7 @@ return -EINVAL; } - err = snd_pcm_pipewire_open(pcmp, name, node_name, server_name, playback_node, - capture_node, role, stream, mode, flags, rate, format, - channels, period_bytes); + err = snd_pcm_pipewire_open(pcmp, ¶ms, stream, mode); return err; }
View file
pipewire-0.3.58.tar.gz/pipewire-alsa/conf/50-pipewire.conf -> pipewire-0.3.59.tar.gz/pipewire-alsa/conf/50-pipewire.conf
Changed
@@ -4,9 +4,14 @@ defaults.pipewire.node "-1" defaults.pipewire.exclusive false defaults.pipewire.role "" +defaults.pipewire.rate 0 +defaults.pipewire.format "" +defaults.pipewire.channels 0 +defaults.pipewire.period_bytes 0 +defaults.pipewire.buffer_bytes 0 pcm.pipewire { - @args SERVER NODE EXCLUSIVE ROLE + @args SERVER NODE EXCLUSIVE ROLE RATE FORMAT CHANNELS PERIOD_BYTES BUFFER_BYTES @args.SERVER { type string default { @@ -35,7 +40,41 @@ name defaults.pipewire.role } } - + @args.RATE { + type integer + default { + @func refer + name defaults.pipewire.rate + } + } + @args.FORMAT { + type string + default { + @func refer + name defaults.pipewire.format + } + } + @args.CHANNELS { + type integer + default { + @func refer + name defaults.pipewire.channels + } + } + @args.PERIOD_BYTES { + type integer + default { + @func refer + name defaults.pipewire.period_bytes + } + } + @args.BUFFER_BYTES { + type integer + default { + @func refer + name defaults.pipewire.buffer_bytes + } + } type pipewire server $SERVER @@ -43,6 +82,11 @@ capture_node $NODE exclusive $EXCLUSIVE role $ROLE + rate $RATE + format $FORMAT + channels $CHANNELS + period_bytes $PERIOD_BYTES + buffer_bytes $BUFFER_BYTES hint { show on description "PipeWire Sound Server"
View file
pipewire-0.3.58.tar.gz/pipewire-jack/src/pipewire-jack-extensions.h -> pipewire-0.3.59.tar.gz/pipewire-jack/src/pipewire-jack-extensions.h
Changed
@@ -24,6 +24,7 @@ #ifndef PIPEWIRE_JACK_EXTENSIONS_H #define PIPEWIRE_JACK_EXTENSIONS_H +#include <stdint.h> #ifdef __cplusplus extern "C" {
View file
pipewire-0.3.58.tar.gz/po/hu.po -> pipewire-0.3.59.tar.gz/po/hu.po
Changed
@@ -1,18 +1,18 @@ -# Hungarian translation of PipeWire -# Copyright (C) 2012, 2016. Free Software Foundation, Inc. +# Hungarian translation for PipeWire. +# Copyright (C) 2012, 2016, 2022. Free Software Foundation, Inc. # This file is distributed under the same license as the PipeWire package. # -# KAMI <kami911@gmail.com>, 2012. +# KAMI <kami911 at gmail dot com>, 2012. # Gabor Kelemen <kelemeng at ubuntu dot com>, 2016. -# Balázs Úr <urbalazs at gmail dot com>, 2016. +# Balázs Úr <ur.balazs at fsf dot hu>, 2016, 2022. msgid "" msgstr "" "Project-Id-Version: PipeWire master\n" -"Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/pipewire/pipewire/" -"issues/new\n" -"POT-Creation-Date: 2021-04-18 16:54+0800\n" -"PO-Revision-Date: 2020-07-21 15:29+0000\n" -"Last-Translator: Balázs Meskó <meskobalazs@mailbox.org>\n" +"Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/pipewire/pipewire/-/" +"issues\n" +"POT-Creation-Date: 2022-09-15 15:26+0000\n" +"PO-Revision-Date: 2022-09-21 22:35+0200\n" +"Last-Translator: Balázs Úr <ur.balazs at fsf dot hu>\n" "Language-Team: Hungarian <https://translate.fedoraproject.org/projects/" "pipewire/pipewire/hu/>\n" "Language: hu\n" @@ -20,12 +20,9 @@ "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 4.1.1\n" -"X-Poedit-Language: Hungarian\n" -"X-Poedit-Country: HUNGARY\n" -"X-Poedit-SourceCharset: utf-8\n" +"X-Generator: Lokalize 19.12.3\n" -#: src/daemon/pipewire.c:43 +#: src/daemon/pipewire.c:46 #, c-format msgid "" "%s options\n" @@ -33,40 +30,64 @@ " --version Show version\n" " -c, --config Load config (Default %s)\n" msgstr "" +"%s kapcsolók\n" +" -h, --help Ezen súgó megjelenítése\n" +" --version Verzió megjelenítése\n" +" -c, --config Beállítás betöltése (alapérték: %s)\n" #: src/daemon/pipewire.desktop.in:4 msgid "PipeWire Media System" -msgstr "" +msgstr "PipeWire médiarendszer" #: src/daemon/pipewire.desktop.in:5 msgid "Start the PipeWire Media System" -msgstr "" +msgstr "A PipeWire médiarendszer indítása" -#: src/examples/media-session/alsa-monitor.c:526 -#: spa/plugins/alsa/acp/compat.c:187 -msgid "Built-in Audio" -msgstr "Belső hangforrás" +#: src/modules/module-protocol-pulse/modules/module-tunnel-sink.c:180 +#: src/modules/module-protocol-pulse/modules/module-tunnel-source.c:180 +#, c-format +msgid "Tunnel to %s/%s" +msgstr "Alagút ide: %s/%s" -#: src/examples/media-session/alsa-monitor.c:530 -#: spa/plugins/alsa/acp/compat.c:192 -msgid "Modem" -msgstr "Modem" +#: src/modules/module-fallback-sink.c:51 +msgid "Dummy Output" +msgstr "Üres kimenet" + +#: src/modules/module-pulse-tunnel.c:662 +#, c-format +msgid "Tunnel for %s@%s" +msgstr "Alagút ehhez: %s@%s" -#: src/examples/media-session/alsa-monitor.c:539 +#: src/modules/module-zeroconf-discover.c:332 msgid "Unknown device" -msgstr "" +msgstr "Ismeretlen eszköz" + +#: src/modules/module-zeroconf-discover.c:344 +#, c-format +msgid "%s on %s@%s" +msgstr "%s ezen: %s@%s" -#: src/tools/pw-cat.c:991 +#: src/modules/module-zeroconf-discover.c:348 +#, c-format +msgid "%s on %s" +msgstr "%s ezen: %s" + +#: src/tools/pw-cat.c:784 #, c-format msgid "" -"%s options <file>\n" +"%s options <file>|-\n" " -h, --help Show this help\n" " --version Show version\n" " -v, --verbose Enable verbose operations\n" "\n" msgstr "" +"%s kapcsolók <fájl>|-\n" +" -h, --help Ezen súgó megjelenítése\n" +" --version Verzió megjelenítése\n" +" -v, --verbose Részletes műveletek engedélyezése\n" +"\n" -#: src/tools/pw-cat.c:998 +#: src/tools/pw-cat.c:791 #, c-format msgid "" " -R, --remote Remote daemon name\n" @@ -80,11 +101,30 @@ " or direct samples (256)\n" " the rate is the one of the source " "file\n" -" --list-targets List available targets for --target\n" +" -P --properties Set node properties\n" "\n" msgstr "" +" -R, --remote Távoli démon neve\n" +" --media-type Médiatípus beállítása (alapérték: " +"%s)\n" +" --media-category Médiakategória beállítása\n" +" (alapérték: %s)\n" +" --media-role Médiaszerep beállítása (alapérték: " +"%s)\n" +" --target Csomópont céljának beállítása\n" +" (alapérték: %s), a 0 azt jelenti,\n" +" hogy ne linkeljen\n" +" --latency Csomópont késleltetésének " +"beállítása\n" +" (alapérték: %s)\n" +" Xegység (egység = s, ms, us, ns)\n" +" vagy közvetlen minták (256)\n" +" a gyakoriság a forrásfájl egyike\n" +" -P --properties Csomópont tulajdonságainak " +"beállítása\n" +"\n" -#: src/tools/pw-cat.c:1016 +#: src/tools/pw-cat.c:809 #, c-format msgid "" " --rate Sample rate (req. for rec) (default " @@ -103,16 +143,39 @@ "%d)\n" "\n" msgstr "" +" --rate Mintavételi gyakoriság (kötelező a\n" +" rögzítéshez) (alapérték: %u)\n" +" --channels Csatornák száma (kötelező a\n" +" rögzítéshez) (alapérték: %u)\n" +" --channel-map Csatornaleképezés\n" +" ezek egyike: „stereo”, " +"„surround-51”\n" +" stb. vagy csatornanevek vesszővel\n" +" tagolt listája, például: „FL,FR”\n" +" --format Mintavételi formátum: %s (kötelező " +"a\n" +" rögzítéshez) (alapérték: %s)\n" +" --volume Adatfolyam hangereje 0-1.0\n" +" (alapérték: %.3f)\n" +" -q --quality Újramintavételezési minőség (0-15)\n" +" (alapérték: %d)\n" +"\n" -#: src/tools/pw-cat.c:1033 +#: src/tools/pw-cat.c:826 msgid "" " -p, --playback Playback mode\n" " -r, --record Recording mode\n" " -m, --midi Midi mode\n" +" -d, --dsd DSD mode\n" "\n" msgstr "" +" -p, --playback Lejátszási mód\n" +" -r, --record Rögzítési mód\n" +" -m, --midi Midi mód\n" +" -d, --dsd DSD mód\n" +"\n" -#: src/tools/pw-cli.c:2932 +#: src/tools/pw-cli.c:2255 #, c-format msgid "" "%s options command\n" @@ -122,360 +185,353 @@ " -r, --remote Remote daemon name\n" "\n" msgstr "" +"%s kapcsolók parancs\n" +" -h, --help Ezen súgó megjelenítése\n" +" --version Verzió megjelenítése\n" +" -d, --daemon Indítás démonként (alapérték: " +"hamis)\n" +" -r, --remote Távoli démon neve\n" +"\n" -#: spa/plugins/alsa/acp/acp.c:290 +#: spa/plugins/alsa/acp/acp.c:321 msgid "Pro Audio" -msgstr "" +msgstr "Pro Audio" -#: spa/plugins/alsa/acp/acp.c:411 spa/plugins/alsa/acp/alsa-mixer.c:4704 -#: spa/plugins/bluez5/bluez5-device.c:1000 +#: spa/plugins/alsa/acp/acp.c:444 spa/plugins/alsa/acp/alsa-mixer.c:4648 +#: spa/plugins/bluez5/bluez5-device.c:1236 msgid "Off" -msgstr "Kikapcsolva" +msgstr "Ki" -#: spa/plugins/alsa/acp/channelmap.h:466 -msgid "(invalid)" -msgstr "(Érvénytelen)" - -#: spa/plugins/alsa/acp/alsa-mixer.c:2709 +#: spa/plugins/alsa/acp/alsa-mixer.c:2652 msgid "Input" msgstr "Bemenet" -#: spa/plugins/alsa/acp/alsa-mixer.c:2710 +#: spa/plugins/alsa/acp/alsa-mixer.c:2653 msgid "Docking Station Input" msgstr "Dokkolóállomás bemenet" -#: spa/plugins/alsa/acp/alsa-mixer.c:2711 +#: spa/plugins/alsa/acp/alsa-mixer.c:2654 msgid "Docking Station Microphone" msgstr "Dokkolóállomás mikrofon" -#: spa/plugins/alsa/acp/alsa-mixer.c:2712 +#: spa/plugins/alsa/acp/alsa-mixer.c:2655 msgid "Docking Station Line In" msgstr "Dokkolóállomás vonalbemenet" -#: spa/plugins/alsa/acp/alsa-mixer.c:2713 -#: spa/plugins/alsa/acp/alsa-mixer.c:2804 +#: spa/plugins/alsa/acp/alsa-mixer.c:2656 +#: spa/plugins/alsa/acp/alsa-mixer.c:2747 msgid "Line In" msgstr "Vonalbemenet" -#: spa/plugins/alsa/acp/alsa-mixer.c:2714 -#: spa/plugins/alsa/acp/alsa-mixer.c:2798 -#: spa/plugins/bluez5/bluez5-device.c:1145 +#: spa/plugins/alsa/acp/alsa-mixer.c:2657 +#: spa/plugins/alsa/acp/alsa-mixer.c:2741 +#: spa/plugins/bluez5/bluez5-device.c:1454 msgid "Microphone" msgstr "Mikrofon" -#: spa/plugins/alsa/acp/alsa-mixer.c:2715 -#: spa/plugins/alsa/acp/alsa-mixer.c:2799 +#: spa/plugins/alsa/acp/alsa-mixer.c:2658 +#: spa/plugins/alsa/acp/alsa-mixer.c:2742 msgid "Front Microphone" -msgstr "Első mikrofon" +msgstr "Elülső mikrofon" -#: spa/plugins/alsa/acp/alsa-mixer.c:2716 -#: spa/plugins/alsa/acp/alsa-mixer.c:2800 +#: spa/plugins/alsa/acp/alsa-mixer.c:2659 +#: spa/plugins/alsa/acp/alsa-mixer.c:2743 msgid "Rear Microphone" msgstr "Hátsó mikrofon" -#: spa/plugins/alsa/acp/alsa-mixer.c:2717 +#: spa/plugins/alsa/acp/alsa-mixer.c:2660 msgid "External Microphone" msgstr "Külső mikrofon" -#: spa/plugins/alsa/acp/alsa-mixer.c:2718 -#: spa/plugins/alsa/acp/alsa-mixer.c:2802 +#: spa/plugins/alsa/acp/alsa-mixer.c:2661 +#: spa/plugins/alsa/acp/alsa-mixer.c:2745 msgid "Internal Microphone" msgstr "Belső mikrofon" -#: spa/plugins/alsa/acp/alsa-mixer.c:2719 -#: spa/plugins/alsa/acp/alsa-mixer.c:2805 +#: spa/plugins/alsa/acp/alsa-mixer.c:2662 +#: spa/plugins/alsa/acp/alsa-mixer.c:2748 msgid "Radio" msgstr "Rádió" -#: spa/plugins/alsa/acp/alsa-mixer.c:2720 -#: spa/plugins/alsa/acp/alsa-mixer.c:2806 +#: spa/plugins/alsa/acp/alsa-mixer.c:2663 +#: spa/plugins/alsa/acp/alsa-mixer.c:2749 msgid "Video" msgstr "Videó" -#: spa/plugins/alsa/acp/alsa-mixer.c:2721 +#: spa/plugins/alsa/acp/alsa-mixer.c:2664 msgid "Automatic Gain Control" msgstr "Automatikus erősítésszabályzás" -#: spa/plugins/alsa/acp/alsa-mixer.c:2722 +#: spa/plugins/alsa/acp/alsa-mixer.c:2665 msgid "No Automatic Gain Control" msgstr "Nincs automatikus erősítésszabályzás" -#: spa/plugins/alsa/acp/alsa-mixer.c:2723 +#: spa/plugins/alsa/acp/alsa-mixer.c:2666 msgid "Boost" msgstr "Erősítés" -#: spa/plugins/alsa/acp/alsa-mixer.c:2724 +#: spa/plugins/alsa/acp/alsa-mixer.c:2667 msgid "No Boost" msgstr "Nincs erősítés" -#: spa/plugins/alsa/acp/alsa-mixer.c:2725 +#: spa/plugins/alsa/acp/alsa-mixer.c:2668 msgid "Amplifier" msgstr "Erősítő" -#: spa/plugins/alsa/acp/alsa-mixer.c:2726 +#: spa/plugins/alsa/acp/alsa-mixer.c:2669 msgid "No Amplifier" msgstr "Nincs erősítő" -#: spa/plugins/alsa/acp/alsa-mixer.c:2727 +#: spa/plugins/alsa/acp/alsa-mixer.c:2670 msgid "Bass Boost" msgstr "Basszuskiemelés" -#: spa/plugins/alsa/acp/alsa-mixer.c:2728 +#: spa/plugins/alsa/acp/alsa-mixer.c:2671 msgid "No Bass Boost" msgstr "Nincs basszuskiemelés" -#: spa/plugins/alsa/acp/alsa-mixer.c:2729 -#: spa/plugins/bluez5/bluez5-device.c:1150 +#: spa/plugins/alsa/acp/alsa-mixer.c:2672 +#: spa/plugins/bluez5/bluez5-device.c:1460 msgid "Speaker" msgstr "Hangszóró" -#: spa/plugins/alsa/acp/alsa-mixer.c:2730 -#: spa/plugins/alsa/acp/alsa-mixer.c:2808 +#: spa/plugins/alsa/acp/alsa-mixer.c:2673 +#: spa/plugins/alsa/acp/alsa-mixer.c:2751 msgid "Headphones" -msgstr "Analóg fejhallgató" +msgstr "Fejhallgató" -#: spa/plugins/alsa/acp/alsa-mixer.c:2797 +#: spa/plugins/alsa/acp/alsa-mixer.c:2740 msgid "Analog Input" msgstr "Analóg bemenet" -#: spa/plugins/alsa/acp/alsa-mixer.c:2801 +#: spa/plugins/alsa/acp/alsa-mixer.c:2744 msgid "Dock Microphone" msgstr "Dokkolóállomás mikrofon" -#: spa/plugins/alsa/acp/alsa-mixer.c:2803 +#: spa/plugins/alsa/acp/alsa-mixer.c:2746 msgid "Headset Microphone" msgstr "Fejhallgató mikrofon" -#: spa/plugins/alsa/acp/alsa-mixer.c:2807 +#: spa/plugins/alsa/acp/alsa-mixer.c:2750 msgid "Analog Output" msgstr "Analóg kimenet" -#: spa/plugins/alsa/acp/alsa-mixer.c:2809 -#, fuzzy +#: spa/plugins/alsa/acp/alsa-mixer.c:2752 msgid "Headphones 2" -msgstr "Analóg fejhallgató" +msgstr "2. fejhallgató" -#: spa/plugins/alsa/acp/alsa-mixer.c:2810 -#, fuzzy +#: spa/plugins/alsa/acp/alsa-mixer.c:2753 msgid "Headphones Mono Output" -msgstr "Analóg mono kimenet" +msgstr "Fejhallató monó kimenet" -#: spa/plugins/alsa/acp/alsa-mixer.c:2811 +#: spa/plugins/alsa/acp/alsa-mixer.c:2754 msgid "Line Out" msgstr "Vonalkimenet" -#: spa/plugins/alsa/acp/alsa-mixer.c:2812 +#: spa/plugins/alsa/acp/alsa-mixer.c:2755 msgid "Analog Mono Output" -msgstr "Analóg mono kimenet" +msgstr "Analóg monó kimenet" -#: spa/plugins/alsa/acp/alsa-mixer.c:2813 +#: spa/plugins/alsa/acp/alsa-mixer.c:2756 msgid "Speakers" msgstr "Hangszórók" -#: spa/plugins/alsa/acp/alsa-mixer.c:2814 +#: spa/plugins/alsa/acp/alsa-mixer.c:2757 msgid "HDMI / DisplayPort" msgstr "HDMI / DisplayPort" -#: spa/plugins/alsa/acp/alsa-mixer.c:2815 +#: spa/plugins/alsa/acp/alsa-mixer.c:2758 msgid "Digital Output (S/PDIF)" msgstr "Digitális kimenet (S/PDIF)" -#: spa/plugins/alsa/acp/alsa-mixer.c:2816 +#: spa/plugins/alsa/acp/alsa-mixer.c:2759 msgid "Digital Input (S/PDIF)" msgstr "Digitális bemenet (S/PDIF)" -#: spa/plugins/alsa/acp/alsa-mixer.c:2817 +#: spa/plugins/alsa/acp/alsa-mixer.c:2760 msgid "Multichannel Input" msgstr "Többcsatornás bemenet" -#: spa/plugins/alsa/acp/alsa-mixer.c:2818 +#: spa/plugins/alsa/acp/alsa-mixer.c:2761 msgid "Multichannel Output" msgstr "Többcsatornás kimenet" -#: spa/plugins/alsa/acp/alsa-mixer.c:2819 -#, fuzzy +#: spa/plugins/alsa/acp/alsa-mixer.c:2762 msgid "Game Output" -msgstr "%s kimenet" +msgstr "Játék kimenet" -#: spa/plugins/alsa/acp/alsa-mixer.c:2820 -#: spa/plugins/alsa/acp/alsa-mixer.c:2821 -#, fuzzy +#: spa/plugins/alsa/acp/alsa-mixer.c:2763 +#: spa/plugins/alsa/acp/alsa-mixer.c:2764 msgid "Chat Output" -msgstr "%s kimenet" +msgstr "Csevegés kimenet" -#: spa/plugins/alsa/acp/alsa-mixer.c:2822 -#, fuzzy +#: spa/plugins/alsa/acp/alsa-mixer.c:2765 msgid "Chat Input" -msgstr "%s bemenet" +msgstr "Csevegés bemenet" -#: spa/plugins/alsa/acp/alsa-mixer.c:2823 -#, fuzzy +#: spa/plugins/alsa/acp/alsa-mixer.c:2766 msgid "Virtual Surround 7.1" -msgstr "Virtuális térhatású nyelő" +msgstr "Virtuális térhatás 7.1" -#: spa/plugins/alsa/acp/alsa-mixer.c:4527 +#: spa/plugins/alsa/acp/alsa-mixer.c:4471 msgid "Analog Mono" -msgstr "Analóg mono" +msgstr "Analóg monó" -#: spa/plugins/alsa/acp/alsa-mixer.c:4528 -#, fuzzy +#: spa/plugins/alsa/acp/alsa-mixer.c:4472 msgid "Analog Mono (Left)" -msgstr "Analóg mono" +msgstr "Analóg monó (bal)" -#: spa/plugins/alsa/acp/alsa-mixer.c:4529 -#, fuzzy +#: spa/plugins/alsa/acp/alsa-mixer.c:4473 msgid "Analog Mono (Right)" -msgstr "Analóg mono" +msgstr "Analóg monó (jobb)" #. Note: Not translated to "Analog Stereo Input", because the source #. * name gets "Input" appended to it automatically, so adding "Input" #. * here would lead to the source name to become "Analog Stereo Input #. * Input". The same logic applies to analog-stereo-output, #. * multichannel-input and multichannel-output. -#: spa/plugins/alsa/acp/alsa-mixer.c:4530 -#: spa/plugins/alsa/acp/alsa-mixer.c:4538 -#: spa/plugins/alsa/acp/alsa-mixer.c:4539 +#: spa/plugins/alsa/acp/alsa-mixer.c:4474 +#: spa/plugins/alsa/acp/alsa-mixer.c:4482 +#: spa/plugins/alsa/acp/alsa-mixer.c:4483 msgid "Analog Stereo" msgstr "Analóg sztereó" -#: spa/plugins/alsa/acp/alsa-mixer.c:4531 +#: spa/plugins/alsa/acp/alsa-mixer.c:4475 msgid "Mono" -msgstr "Mono" +msgstr "Monó" -#: spa/plugins/alsa/acp/alsa-mixer.c:4532 +#: spa/plugins/alsa/acp/alsa-mixer.c:4476 msgid "Stereo" msgstr "Sztereó" -#: spa/plugins/alsa/acp/alsa-mixer.c:4540 -#: spa/plugins/alsa/acp/alsa-mixer.c:4698 -#: spa/plugins/bluez5/bluez5-device.c:1135 +#: spa/plugins/alsa/acp/alsa-mixer.c:4484 +#: spa/plugins/alsa/acp/alsa-mixer.c:4642 +#: spa/plugins/bluez5/bluez5-device.c:1442 msgid "Headset" msgstr "Fejhallgató" -#: spa/plugins/alsa/acp/alsa-mixer.c:4541 -#: spa/plugins/alsa/acp/alsa-mixer.c:4699 -#, fuzzy +#: spa/plugins/alsa/acp/alsa-mixer.c:4485 +#: spa/plugins/alsa/acp/alsa-mixer.c:4643 msgid "Speakerphone" -msgstr "Hangszóró" +msgstr "Mikrofonos fejhallgató" -#: spa/plugins/alsa/acp/alsa-mixer.c:4542 -#: spa/plugins/alsa/acp/alsa-mixer.c:4543 +#: spa/plugins/alsa/acp/alsa-mixer.c:4486 +#: spa/plugins/alsa/acp/alsa-mixer.c:4487 msgid "Multichannel" msgstr "Többcsatornás" -#: spa/plugins/alsa/acp/alsa-mixer.c:4544 +#: spa/plugins/alsa/acp/alsa-mixer.c:4488 msgid "Analog Surround 2.1" msgstr "Analóg térhatású 2.1" -#: spa/plugins/alsa/acp/alsa-mixer.c:4545 +#: spa/plugins/alsa/acp/alsa-mixer.c:4489 msgid "Analog Surround 3.0" msgstr "Analóg térhatású 3.0" -#: spa/plugins/alsa/acp/alsa-mixer.c:4546 +#: spa/plugins/alsa/acp/alsa-mixer.c:4490 msgid "Analog Surround 3.1" msgstr "Analóg térhatású 3.1" -#: spa/plugins/alsa/acp/alsa-mixer.c:4547 +#: spa/plugins/alsa/acp/alsa-mixer.c:4491 msgid "Analog Surround 4.0" msgstr "Analóg térhatású 4.0" -#: spa/plugins/alsa/acp/alsa-mixer.c:4548 +#: spa/plugins/alsa/acp/alsa-mixer.c:4492 msgid "Analog Surround 4.1" msgstr "Analóg térhatású 4.1" -#: spa/plugins/alsa/acp/alsa-mixer.c:4549 +#: spa/plugins/alsa/acp/alsa-mixer.c:4493 msgid "Analog Surround 5.0" msgstr "Analóg térhatású 5.0" -#: spa/plugins/alsa/acp/alsa-mixer.c:4550 +#: spa/plugins/alsa/acp/alsa-mixer.c:4494 msgid "Analog Surround 5.1" msgstr "Analóg térhatású 5.1" -#: spa/plugins/alsa/acp/alsa-mixer.c:4551 +#: spa/plugins/alsa/acp/alsa-mixer.c:4495 msgid "Analog Surround 6.0" msgstr "Analóg térhatású 6.0" -#: spa/plugins/alsa/acp/alsa-mixer.c:4552 +#: spa/plugins/alsa/acp/alsa-mixer.c:4496 msgid "Analog Surround 6.1" msgstr "Analóg térhatású 6.1" -#: spa/plugins/alsa/acp/alsa-mixer.c:4553 +#: spa/plugins/alsa/acp/alsa-mixer.c:4497 msgid "Analog Surround 7.0" msgstr "Analóg térhatású 7.0" -#: spa/plugins/alsa/acp/alsa-mixer.c:4554 +#: spa/plugins/alsa/acp/alsa-mixer.c:4498 msgid "Analog Surround 7.1" msgstr "Analóg térhatású 7.1" -#: spa/plugins/alsa/acp/alsa-mixer.c:4555 +#: spa/plugins/alsa/acp/alsa-mixer.c:4499 msgid "Digital Stereo (IEC958)" msgstr "Digitális sztereó (IEC958)" -#: spa/plugins/alsa/acp/alsa-mixer.c:4556 +#: spa/plugins/alsa/acp/alsa-mixer.c:4500 msgid "Digital Surround 4.0 (IEC958/AC3)" msgstr "Digitális térhatású 4.0 (IEC958/AC3)" -#: spa/plugins/alsa/acp/alsa-mixer.c:4557 +#: spa/plugins/alsa/acp/alsa-mixer.c:4501 msgid "Digital Surround 5.1 (IEC958/AC3)" msgstr "Digitális térhatású 5.1 (IEC958/AC3)" -#: spa/plugins/alsa/acp/alsa-mixer.c:4558 +#: spa/plugins/alsa/acp/alsa-mixer.c:4502 msgid "Digital Surround 5.1 (IEC958/DTS)" msgstr "Digitális térhatású 5.1 (IEC958/DTS)" -#: spa/plugins/alsa/acp/alsa-mixer.c:4559 +#: spa/plugins/alsa/acp/alsa-mixer.c:4503 msgid "Digital Stereo (HDMI)" -msgstr "Digitális térhatású (HDMI)" +msgstr "Digitális sztereó (HDMI)" -#: spa/plugins/alsa/acp/alsa-mixer.c:4560 +#: spa/plugins/alsa/acp/alsa-mixer.c:4504 msgid "Digital Surround 5.1 (HDMI)" msgstr "Digitális térhatású 5.1 (HDMI)" -#: spa/plugins/alsa/acp/alsa-mixer.c:4561 +#: spa/plugins/alsa/acp/alsa-mixer.c:4505 msgid "Chat" -msgstr "" +msgstr "Csevegés" -#: spa/plugins/alsa/acp/alsa-mixer.c:4562 +#: spa/plugins/alsa/acp/alsa-mixer.c:4506 msgid "Game" -msgstr "" +msgstr "Játék" -#: spa/plugins/alsa/acp/alsa-mixer.c:4696 +#: spa/plugins/alsa/acp/alsa-mixer.c:4640 msgid "Analog Mono Duplex" -msgstr "Analóg mono duplex" +msgstr "Analóg monó kétirányú" -#: spa/plugins/alsa/acp/alsa-mixer.c:4697 +#: spa/plugins/alsa/acp/alsa-mixer.c:4641 msgid "Analog Stereo Duplex" -msgstr "Analóg sztereó duplex" +msgstr "Analóg sztereó kétirányú" -#: spa/plugins/alsa/acp/alsa-mixer.c:4700 +#: spa/plugins/alsa/acp/alsa-mixer.c:4644 msgid "Digital Stereo Duplex (IEC958)" -msgstr "Analóg sztereó duplex (IEC958)" +msgstr "Digitális sztereó kétirányú (IEC958)" -#: spa/plugins/alsa/acp/alsa-mixer.c:4701 +#: spa/plugins/alsa/acp/alsa-mixer.c:4645 msgid "Multichannel Duplex" -msgstr "Többcsatornás duplex" +msgstr "Többcsatornás kétirányú" -#: spa/plugins/alsa/acp/alsa-mixer.c:4702 -#, fuzzy +#: spa/plugins/alsa/acp/alsa-mixer.c:4646 msgid "Stereo Duplex" -msgstr "Analóg sztereó duplex" +msgstr "Sztereó kétirányú" -#: spa/plugins/alsa/acp/alsa-mixer.c:4703 +#: spa/plugins/alsa/acp/alsa-mixer.c:4647 msgid "Mono Chat + 7.1 Surround" -msgstr "" +msgstr "Monó csevegés + 7.1 térhatású" -#: spa/plugins/alsa/acp/alsa-mixer.c:4806 +#: spa/plugins/alsa/acp/alsa-mixer.c:4754 #, c-format msgid "%s Output" msgstr "%s kimenet" -#: spa/plugins/alsa/acp/alsa-mixer.c:4813 +#: spa/plugins/alsa/acp/alsa-mixer.c:4761 #, c-format msgid "%s Input" msgstr "%s bemenet" -#: spa/plugins/alsa/acp/alsa-util.c:1175 spa/plugins/alsa/acp/alsa-util.c:1269 -#, fuzzy, c-format +#: spa/plugins/alsa/acp/alsa-util.c:1187 spa/plugins/alsa/acp/alsa-util.c:1281 +#, c-format msgid "" "snd_pcm_avail() returned a value that is exceptionally large: %lu byte (%lu " "ms).\n" @@ -487,18 +543,18 @@ "Most likely this is a bug in the ALSA driver '%s'. Please report this issue " "to the ALSA developers." msgstr0 "" -"A „snd_pcm_avail()” függvény visszatérési értéke váratlanul nagy értékű: %lu " +"Az „snd_pcm_avail()” függvény különlegesen nagy értéket adott vissza: %lu " "bájt (%lu ms).\n" "Ez valószínűleg egy hiba eredménye az ALSA „%s” illesztőprogramban. Jelentse " "ezt a problémát az ALSA fejlesztői felé." msgstr1 "" -"A „snd_pcm_avail()” függvény visszatérési értéke váratlanul nagy értékű: %lu " +"Az „snd_pcm_avail()” függvény különlegesen nagy értéket adott vissza: %lu " "bájt (%lu ms).\n" "Ez valószínűleg egy hiba eredménye az ALSA „%s” illesztőprogramban. Jelentse " "ezt a problémát az ALSA fejlesztői felé." -#: spa/plugins/alsa/acp/alsa-util.c:1241 -#, fuzzy, c-format +#: spa/plugins/alsa/acp/alsa-util.c:1253 +#, c-format msgid "" "snd_pcm_delay() returned a value that is exceptionally large: %li byte (%s" "%lu ms).\n" @@ -510,17 +566,17 @@ "Most likely this is a bug in the ALSA driver '%s'. Please report this issue " "to the ALSA developers." msgstr0 "" -"A „snd_pcm_delay()” függvény visszatérési értéke váratlanul nagy értékű: %li " +"Az „snd_pcm_delay()” függvény különlegesen nagy értéket adott vissza: %li " "bájt (%s%lu ms).\n" "Ez valószínűleg egy hiba eredménye az ALSA „%s” illesztőprogramban. Jelentse " "ezt a problémát az ALSA fejlesztői felé." msgstr1 "" -"A „snd_pcm_delay()” függvény visszatérési értéke váratlanul nagy értékű: %li " +"Az „snd_pcm_delay()” függvény különlegesen nagy értéket adott vissza: %li " "bájt (%s%lu ms).\n" "Ez valószínűleg egy hiba eredménye az ALSA „%s” illesztőprogramban. Jelentse " "ezt a problémát az ALSA fejlesztői felé." -#: spa/plugins/alsa/acp/alsa-util.c:1288 +#: spa/plugins/alsa/acp/alsa-util.c:1300 #, c-format msgid "" "snd_pcm_avail_delay() returned strange values: delay %lu is less than avail " @@ -528,13 +584,13 @@ "Most likely this is a bug in the ALSA driver '%s'. Please report this issue " "to the ALSA developers." msgstr "" -"A „snd_pcm_avail_delay()” függvény furcsa értékeket adott vissza: a " +"Az „snd_pcm_avail_delay()” függvény furcsa értékeket adott vissza: a " "késleltetés (%lu) kisebb, mint az elérhető %lu.\n" "Ez valószínűleg egy hiba eredménye az ALSA „%s” illesztőprogramban. Jelentse " "ezt a problémát az ALSA fejlesztői felé." -#: spa/plugins/alsa/acp/alsa-util.c:1331 -#, fuzzy, c-format +#: spa/plugins/alsa/acp/alsa-util.c:1343 +#, c-format msgid "" "snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu byte " "(%lu ms).\n" @@ -546,73 +602,114 @@ "Most likely this is a bug in the ALSA driver '%s'. Please report this issue " "to the ALSA developers." msgstr0 "" -"A „snd_pcm_mmap_begin()” függvény visszatérési értéke kivételesen nagy: %lu " -"bájt (%lu ms).\n" +"Az „snd_pcm_mmap_begin()” függvény különlegesen nagy értéket adott vissza: " +"%lu bájt (%lu ms).\n" "Ez valószínűleg egy hiba eredménye az ALSA „%s” illesztőprogramban. Jelentse " "ezt a problémát az ALSA fejlesztői felé." msgstr1 "" -"A „snd_pcm_mmap_begin()” függvény visszatérési értéke kivételesen nagy: %lu " -"bájt (%lu ms).\n" +"Az „snd_pcm_mmap_begin()” függvény különlegesen nagy értéket adott vissza: " +"%lu bájt (%lu ms).\n" "Ez valószínűleg egy hiba eredménye az ALSA „%s” illesztőprogramban. Jelentse " "ezt a problémát az ALSA fejlesztői felé." -#: spa/plugins/bluez5/bluez5-device.c:1010 +#: spa/plugins/alsa/acp/channelmap.h:457 +msgid "(invalid)" +msgstr "(érvénytelen)" + +#: spa/plugins/alsa/acp/compat.c:189 +msgid "Built-in Audio" +msgstr "Beépített hangforrás" + +#: spa/plugins/alsa/acp/compat.c:194 +msgid "Modem" +msgstr "Modem" + +#: spa/plugins/bluez5/bluez5-device.c:1247 msgid "Audio Gateway (A2DP Source & HSP/HFP AG)" -msgstr "" +msgstr "Hang átjáró (A2DP forrás és HSP/HFP AG)" -#: spa/plugins/bluez5/bluez5-device.c:1033 +#: spa/plugins/bluez5/bluez5-device.c:1272 #, c-format msgid "High Fidelity Playback (A2DP Sink, codec %s)" -msgstr "" +msgstr "Magas hűségű lejátszás (A2DP fogadó, %s kodek)" -#: spa/plugins/bluez5/bluez5-device.c:1035 +#: spa/plugins/bluez5/bluez5-device.c:1275 #, c-format msgid "High Fidelity Duplex (A2DP Source/Sink, codec %s)" -msgstr "" +msgstr "Magas hűségű kétirányú (A2DP forrás/fogadó, %s kodek)" -#: spa/plugins/bluez5/bluez5-device.c:1041 +#: spa/plugins/bluez5/bluez5-device.c:1283 msgid "High Fidelity Playback (A2DP Sink)" -msgstr "" +msgstr "Magas hűségű lejátszás (A2DP fogadó)" -#: spa/plugins/bluez5/bluez5-device.c:1043 +#: spa/plugins/bluez5/bluez5-device.c:1285 msgid "High Fidelity Duplex (A2DP Source/Sink)" -msgstr "" +msgstr "Magas hűségű kétirányú (A2DP forrás/fogadó)" + +#: spa/plugins/bluez5/bluez5-device.c:1322 +#, c-format +msgid "High Fidelity Playback (BAP Sink, codec %s)" +msgstr "Magas hűségű lejátszás (BAP fogadó, %s kodek)" + +#: spa/plugins/bluez5/bluez5-device.c:1326 +#, c-format +msgid "High Fidelity Input (BAP Source, codec %s)" +msgstr "Magas hűségű bemenet (BAP forrás, %s kodek)" + +#: spa/plugins/bluez5/bluez5-device.c:1330 +#, c-format +msgid "High Fidelity Duplex (BAP Source/Sink, codec %s)" +msgstr "Magas hűségű kétirányú (BAP forrás/fogadó, %s kodek)" -#: spa/plugins/bluez5/bluez5-device.c:1070 +#: spa/plugins/bluez5/bluez5-device.c:1359 #, c-format msgid "Headset Head Unit (HSP/HFP, codec %s)" -msgstr "" +msgstr "Fejhallgató fejegység (HSP/HFP, %s kodek)" -#: spa/plugins/bluez5/bluez5-device.c:1074 +#: spa/plugins/bluez5/bluez5-device.c:1364 msgid "Headset Head Unit (HSP/HFP)" -msgstr "" - -# FIXME: utánanézni -#: spa/plugins/bluez5/bluez5-device.c:1140 +msgstr "Fejhallgató fejegység (HSP/HFP)" + +#: spa/plugins/bluez5/bluez5-device.c:1443 +#: spa/plugins/bluez5/bluez5-device.c:1448 +#: spa/plugins/bluez5/bluez5-device.c:1455 +#: spa/plugins/bluez5/bluez5-device.c:1461 +#: spa/plugins/bluez5/bluez5-device.c:1467 +#: spa/plugins/bluez5/bluez5-device.c:1473 +#: spa/plugins/bluez5/bluez5-device.c:1479 +#: spa/plugins/bluez5/bluez5-device.c:1485 +#: spa/plugins/bluez5/bluez5-device.c:1491 msgid "Handsfree" msgstr "Kihangosító" -#: spa/plugins/bluez5/bluez5-device.c:1155 +#: spa/plugins/bluez5/bluez5-device.c:1449 +msgid "Handsfree (HFP)" +msgstr "Kihangosító (HFP)" + +#: spa/plugins/bluez5/bluez5-device.c:1466 msgid "Headphone" -msgstr "Fülhallgató" +msgstr "Fejhallgató" -#: spa/plugins/bluez5/bluez5-device.c:1160 +#: spa/plugins/bluez5/bluez5-device.c:1472 msgid "Portable" msgstr "Hordozható" -#: spa/plugins/bluez5/bluez5-device.c:1165 +#: spa/plugins/bluez5/bluez5-device.c:1478 msgid "Car" msgstr "Autó" -#: spa/plugins/bluez5/bluez5-device.c:1170 +#: spa/plugins/bluez5/bluez5-device.c:1484 msgid "HiFi" msgstr "Hi-Fi" -#: spa/plugins/bluez5/bluez5-device.c:1175 +#: spa/plugins/bluez5/bluez5-device.c:1490 msgid "Phone" msgstr "Telefon" -#: spa/plugins/bluez5/bluez5-device.c:1181 -#, fuzzy +#: spa/plugins/bluez5/bluez5-device.c:1497 msgid "Bluetooth" -msgstr "Bluetooth bemenet" +msgstr "Bluetooth" + +#: spa/plugins/bluez5/bluez5-device.c:1498 +msgid "Bluetooth (HFP)" +msgstr "Bluetooth (HFP)"
View file
pipewire-0.3.58.tar.gz/po/it.po -> pipewire-0.3.59.tar.gz/po/it.po
Changed
@@ -34,11 +34,11 @@ #: src/daemon/pipewire.desktop.in:4 msgid "PipeWire Media System" -msgstr "Sistema Multimediale PipeWire" +msgstr "Sistema multimediale PipeWire" #: src/daemon/pipewire.desktop.in:5 msgid "Start the PipeWire Media System" -msgstr "Avvia il Sistema Multimediale PipeWire" +msgstr "Avvia il sistema multimediale PipeWire" #: src/examples/media-session/alsa-monitor.c:526 #: spa/plugins/alsa/acp/compat.c:187 @@ -144,16 +144,16 @@ #: spa/plugins/alsa/acp/alsa-mixer.c:2711 msgid "Docking Station Microphone" -msgstr "Microfono docking station" +msgstr "Microfono della docking station" #: spa/plugins/alsa/acp/alsa-mixer.c:2712 msgid "Docking Station Line In" -msgstr "Linea in docking station" +msgstr "Linea di ingresso nella docking station" #: spa/plugins/alsa/acp/alsa-mixer.c:2713 #: spa/plugins/alsa/acp/alsa-mixer.c:2804 msgid "Line In" -msgstr "Line-In" +msgstr "Linea di ingresso" #: spa/plugins/alsa/acp/alsa-mixer.c:2714 #: spa/plugins/alsa/acp/alsa-mixer.c:2798 @@ -258,7 +258,7 @@ #: spa/plugins/alsa/acp/alsa-mixer.c:2811 msgid "Line Out" -msgstr "Line-Out" +msgstr "Linea di uscita" #: spa/plugins/alsa/acp/alsa-mixer.c:2812 msgid "Analog Mono Output" @@ -350,7 +350,7 @@ #: spa/plugins/alsa/acp/alsa-mixer.c:4542 #: spa/plugins/alsa/acp/alsa-mixer.c:4543 msgid "Multichannel" -msgstr "Multi canale" +msgstr "Multicanale" #: spa/plugins/alsa/acp/alsa-mixer.c:4544 msgid "Analog Surround 2.1" @@ -442,7 +442,7 @@ #: spa/plugins/alsa/acp/alsa-mixer.c:4701 msgid "Multichannel Duplex" -msgstr "Duplex multi canale" +msgstr "Duplex multicanale" #: spa/plugins/alsa/acp/alsa-mixer.c:4702 msgid "Stereo Duplex" @@ -477,11 +477,11 @@ msgstr0 "" "snd_pcm_avail() ha restituito un valore molto grande: %lu byte (%lu ms).\n" "Molto probabilmente si tratta di un bug nel driver ALSA «%s». Segnalare " -"questo problema agli sviluppatori ALSA." +"questo problema ai suoi sviluppatori." msgstr1 "" "snd_pcm_avail() ha restituito un valore molto grande: %lu byte (%lu ms).\n" "Molto probabilmente si tratta di un bug nel driver ALSA «%s». Segnalare " -"questo problema agli sviluppatori ALSA." +"questo problema ai suoi sviluppatori." #: spa/plugins/alsa/acp/alsa-util.c:1241 #, c-format @@ -498,11 +498,11 @@ msgstr0 "" "snd_pcm_delay() ha restituito un valore molto grande: %li byte (%s%lu ms).\n" "Molto probabilmente si tratta di un bug nel driver ALSA «%s». Segnalare " -"questo problema agli sviluppatori ALSA." +"questo problema ai suoi sviluppatori." msgstr1 "" "snd_pcm_delay() ha restituito un valore molto grande: %li byte (%s%lu ms).\n" "Molto probabilmente si tratta di un bug nel driver ALSA «%s». Segnalare " -"questo problema agli sviluppatori ALSA." +"questo problema ai suoi sviluppatori." #: spa/plugins/alsa/acp/alsa-util.c:1288 #, c-format @@ -515,7 +515,7 @@ "snd_pcm_avail() ha restituito dei valori strani: delay %lu è minore di avail " "%lu.\n" "Molto probabilmente si tratta di un bug nel driver ALSA «%s». Segnalare " -"questo problema agli sviluppatori ALSA." +"questo problema ai suoi sviluppatori." #: spa/plugins/alsa/acp/alsa-util.c:1331 #, c-format @@ -533,12 +533,12 @@ "snd_pcm_mmap_begin() ha restituito un valore molto grande: %lu byte (%lu " "ms).\n" "Molto probabilmente si tratta di un bug nel driver ALSA «%s». Segnalare " -"questo problema agli sviluppatori ALSA." +"questo problema ai suoi sviluppatori." msgstr1 "" "snd_pcm_mmap_begin() ha restituito un valore molto grande: %lu byte (%lu " "ms).\n" "Molto probabilmente si tratta di un bug nel driver ALSA «%s». Segnalare " -"questo problema agli sviluppatori ALSA." +"questo problema ai suoi sviluppatori." #: spa/plugins/bluez5/bluez5-device.c:1010 msgid "Audio Gateway (A2DP Source & HSP/HFP AG)" @@ -547,20 +547,20 @@ #: spa/plugins/bluez5/bluez5-device.c:1033 #, c-format msgid "High Fidelity Playback (A2DP Sink, codec %s)" -msgstr "Riproduzione ad Alta Fedeltà (A2DP Sink, codec %s)" +msgstr "Riproduzione ad alta fedeltà (A2DP Sink, codec %s)" #: spa/plugins/bluez5/bluez5-device.c:1035 #, c-format msgid "High Fidelity Duplex (A2DP Source/Sink, codec %s)" -msgstr "Duplex ad Alta Fedeltà (Sorgente/Sink, A2DP codec %s)" +msgstr "Duplex ad alta fedeltà (Sorgente/Sink, A2DP codec %s)" #: spa/plugins/bluez5/bluez5-device.c:1041 msgid "High Fidelity Playback (A2DP Sink)" -msgstr "Riproduzione ad Alta Fedeltà (A2DP Sink)" +msgstr "Riproduzione ad alta fedeltà (A2DP Sink)" #: spa/plugins/bluez5/bluez5-device.c:1043 msgid "High Fidelity Duplex (A2DP Source/Sink)" -msgstr "Duplex ad Alta Fedeltà (A2DP Source/Sink)" +msgstr "Duplex ad alta fedeltà (A2DP Source/Sink)" #: spa/plugins/bluez5/bluez5-device.c:1070 #, c-format @@ -573,7 +573,7 @@ #: spa/plugins/bluez5/bluez5-device.c:1140 msgid "Handsfree" -msgstr "Sistema mani-libere" +msgstr "Vivavoce" #: spa/plugins/bluez5/bluez5-device.c:1155 msgid "Headphone"
View file
pipewire-0.3.58.tar.gz/po/pl.po -> pipewire-0.3.59.tar.gz/po/pl.po
Changed
@@ -8,8 +8,8 @@ "Project-Id-Version: pipewire\n" "Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/pipewire/pipewire/-/" "issues\n" -"POT-Creation-Date: 2022-08-27 13:57+0000\n" -"PO-Revision-Date: 2022-08-27 16:00+0200\n" +"POT-Creation-Date: 2022-09-15 15:26+0000\n" +"PO-Revision-Date: 2022-09-25 15:20+0200\n" "Last-Translator: Piotr Drąg <piotrdrag@gmail.com>\n" "Language-Team: Polish <community-poland@mozilla.org>\n" "Language: pl\n" @@ -51,7 +51,7 @@ msgid "Dummy Output" msgstr "Głuche wyjście" -#: src/modules/module-pulse-tunnel.c:648 +#: src/modules/module-pulse-tunnel.c:662 #, c-format msgid "Tunnel for %s@%s" msgstr "Tunel dla %s@%s" @@ -195,7 +195,7 @@ msgstr "Dźwięk w zastosowaniach profesjonalnych" #: spa/plugins/alsa/acp/acp.c:444 spa/plugins/alsa/acp/alsa-mixer.c:4648 -#: spa/plugins/bluez5/bluez5-device.c:1188 +#: spa/plugins/bluez5/bluez5-device.c:1236 msgid "Off" msgstr "Wyłączone" @@ -222,7 +222,7 @@ #: spa/plugins/alsa/acp/alsa-mixer.c:2657 #: spa/plugins/alsa/acp/alsa-mixer.c:2741 -#: spa/plugins/bluez5/bluez5-device.c:1360 +#: spa/plugins/bluez5/bluez5-device.c:1454 msgid "Microphone" msgstr "Mikrofon" @@ -288,7 +288,7 @@ msgstr "Brak podbicia basów" #: spa/plugins/alsa/acp/alsa-mixer.c:2672 -#: spa/plugins/bluez5/bluez5-device.c:1366 +#: spa/plugins/bluez5/bluez5-device.c:1460 msgid "Speaker" msgstr "Głośnik" @@ -403,7 +403,7 @@ #: spa/plugins/alsa/acp/alsa-mixer.c:4484 #: spa/plugins/alsa/acp/alsa-mixer.c:4642 -#: spa/plugins/bluez5/bluez5-device.c:1348 +#: spa/plugins/bluez5/bluez5-device.c:1442 msgid "Headset" msgstr "Słuchawki z mikrofonem" @@ -627,77 +627,92 @@ msgid "Modem" msgstr "Modem" -#: spa/plugins/bluez5/bluez5-device.c:1199 +#: spa/plugins/bluez5/bluez5-device.c:1247 msgid "Audio Gateway (A2DP Source & HSP/HFP AG)" msgstr "Bramka dźwięku (źródło A2DP i AG HSP/HFP)" -#: spa/plugins/bluez5/bluez5-device.c:1224 +#: spa/plugins/bluez5/bluez5-device.c:1272 #, c-format msgid "High Fidelity Playback (A2DP Sink, codec %s)" msgstr "Odtwarzanie o wysokiej dokładności (odpływ A2DP, kodek %s)" -#: spa/plugins/bluez5/bluez5-device.c:1227 +#: spa/plugins/bluez5/bluez5-device.c:1275 #, c-format msgid "High Fidelity Duplex (A2DP Source/Sink, codec %s)" msgstr "Dupleks o wysokiej dokładności (źródło/odpływ A2DP, kodek %s)" -#: spa/plugins/bluez5/bluez5-device.c:1235 +#: spa/plugins/bluez5/bluez5-device.c:1283 msgid "High Fidelity Playback (A2DP Sink)" msgstr "Odtwarzanie o wysokiej dokładności (odpływ A2DP)" -#: spa/plugins/bluez5/bluez5-device.c:1237 +#: spa/plugins/bluez5/bluez5-device.c:1285 msgid "High Fidelity Duplex (A2DP Source/Sink)" msgstr "Dupleks o wysokiej dokładności (źródło/odpływ A2DP)" -#: spa/plugins/bluez5/bluez5-device.c:1265 +#: spa/plugins/bluez5/bluez5-device.c:1322 +#, c-format +msgid "High Fidelity Playback (BAP Sink, codec %s)" +msgstr "Odtwarzanie o wysokiej dokładności (odpływ BAP, kodek %s)" + +#: spa/plugins/bluez5/bluez5-device.c:1326 +#, c-format +msgid "High Fidelity Input (BAP Source, codec %s)" +msgstr "Wejście o wysokiej dokładności (źródło BAP, kodek %s)" + +#: spa/plugins/bluez5/bluez5-device.c:1330 +#, c-format +msgid "High Fidelity Duplex (BAP Source/Sink, codec %s)" +msgstr "Dupleks o wysokiej dokładności (źródło/odpływ BAP, kodek %s)" + +#: spa/plugins/bluez5/bluez5-device.c:1359 #, c-format msgid "Headset Head Unit (HSP/HFP, codec %s)" msgstr "Jednostka główna słuchawek z mikrofonem (HSP/HFP, kodek %s)" -#: spa/plugins/bluez5/bluez5-device.c:1270 +#: spa/plugins/bluez5/bluez5-device.c:1364 msgid "Headset Head Unit (HSP/HFP)" msgstr "Jednostka główna słuchawek z mikrofonem (HSP/HFP)" -#: spa/plugins/bluez5/bluez5-device.c:1349 -#: spa/plugins/bluez5/bluez5-device.c:1354 -#: spa/plugins/bluez5/bluez5-device.c:1361 -#: spa/plugins/bluez5/bluez5-device.c:1367 -#: spa/plugins/bluez5/bluez5-device.c:1373 -#: spa/plugins/bluez5/bluez5-device.c:1379 -#: spa/plugins/bluez5/bluez5-device.c:1385 -#: spa/plugins/bluez5/bluez5-device.c:1391 -#: spa/plugins/bluez5/bluez5-device.c:1397 +#: spa/plugins/bluez5/bluez5-device.c:1443 +#: spa/plugins/bluez5/bluez5-device.c:1448 +#: spa/plugins/bluez5/bluez5-device.c:1455 +#: spa/plugins/bluez5/bluez5-device.c:1461 +#: spa/plugins/bluez5/bluez5-device.c:1467 +#: spa/plugins/bluez5/bluez5-device.c:1473 +#: spa/plugins/bluez5/bluez5-device.c:1479 +#: spa/plugins/bluez5/bluez5-device.c:1485 +#: spa/plugins/bluez5/bluez5-device.c:1491 msgid "Handsfree" msgstr "Zestaw głośnomówiący" -#: spa/plugins/bluez5/bluez5-device.c:1355 +#: spa/plugins/bluez5/bluez5-device.c:1449 msgid "Handsfree (HFP)" msgstr "Zestaw głośnomówiący (HFP)" -#: spa/plugins/bluez5/bluez5-device.c:1372 +#: spa/plugins/bluez5/bluez5-device.c:1466 msgid "Headphone" msgstr "Słuchawki" -#: spa/plugins/bluez5/bluez5-device.c:1378 +#: spa/plugins/bluez5/bluez5-device.c:1472 msgid "Portable" msgstr "Przenośne" -#: spa/plugins/bluez5/bluez5-device.c:1384 +#: spa/plugins/bluez5/bluez5-device.c:1478 msgid "Car" msgstr "Samochód" -#: spa/plugins/bluez5/bluez5-device.c:1390 +#: spa/plugins/bluez5/bluez5-device.c:1484 msgid "HiFi" msgstr "HiFi" -#: spa/plugins/bluez5/bluez5-device.c:1396 +#: spa/plugins/bluez5/bluez5-device.c:1490 msgid "Phone" msgstr "Telefon" -#: spa/plugins/bluez5/bluez5-device.c:1403 +#: spa/plugins/bluez5/bluez5-device.c:1497 msgid "Bluetooth" msgstr "Bluetooth" -#: spa/plugins/bluez5/bluez5-device.c:1404 +#: spa/plugins/bluez5/bluez5-device.c:1498 msgid "Bluetooth (HFP)" msgstr "Bluetooth (HFP)"
View file
pipewire-0.3.58.tar.gz/spa/include/spa/buffer/alloc.h -> pipewire-0.3.59.tar.gz/spa/include/spa/buffer/alloc.h
Changed
@@ -161,8 +161,9 @@ *target += info->chunk_size; for (i = 0, size = 0; i < n_datas; i++) { + int64_t align = data_alignsi; info->max_align = SPA_MAX(info->max_align, data_alignsi); - size = SPA_ROUND_UP_N(size, data_alignsi); + size = SPA_ROUND_UP_N(size, align); size += datasi.maxsize; } info->data_size = size;
View file
pipewire-0.3.58.tar.gz/spa/include/spa/buffer/meta.h -> pipewire-0.3.59.tar.gz/spa/include/spa/buffer/meta.h
Changed
@@ -64,9 +64,15 @@ void *data; /**< pointer to metadata */ }; -#define spa_meta_first(m) ((m)->data) -#define spa_meta_end(m) SPA_PTROFF((m)->data,(m)->size,void) -#define spa_meta_check(p,m) (SPA_PTROFF(p,sizeof(*p),void) <= spa_meta_end(m)) +static inline void *spa_meta_first(const struct spa_meta *m) { + return m->data; +} +#define spa_meta_first spa_meta_first +static inline void *spa_meta_end(const struct spa_meta *m) { + return SPA_PTROFF(m->data,m->size,void); +} +#define spa_meta_end spa_meta_end +#define spa_meta_check(p,m) (SPA_PTROFF(p,sizeof(*(p)),void) <= spa_meta_end(m)) /** * Describes essential buffer header metadata such as flags and @@ -92,11 +98,14 @@ struct spa_region region; }; -#define spa_meta_region_is_valid(m) ((m)->region.size.width != 0 && (m)->region.size.height != 0) +static inline bool spa_meta_region_is_valid(const struct spa_meta_region *m) { + return m->region.size.width != 0 && m->region.size.height != 0; +} +#define spa_meta_region_is_valid spa_meta_region_is_valid /** iterate all the items in a metadata */ #define spa_meta_for_each(pos,meta) \ - for (pos = (__typeof(pos))spa_meta_first(meta); \ + for ((pos) = (__typeof(pos))spa_meta_first(meta); \ spa_meta_check(pos, meta); \ (pos)++)
View file
pipewire-0.3.58.tar.gz/spa/include/spa/debug/log.h -> pipewire-0.3.59.tar.gz/spa/include/spa/debug/log.h
Changed
@@ -36,10 +36,10 @@ */ #ifndef spa_debug -#define spa_debug(fmt,...) ({ printf(fmt"\n", ## __VA_ARGS__); }) +#define spa_debug(fmt,...) ({ printf((fmt"\n"), ## __VA_ARGS__); }) #endif #ifndef spa_debugn -#define spa_debugn(fmt,...) ({ printf(fmt, ## __VA_ARGS__); }) +#define spa_debugn(fmt,...) ({ printf((fmt), ## __VA_ARGS__); }) #endif /**
View file
pipewire-0.3.58.tar.gz/spa/include/spa/graph/graph.h -> pipewire-0.3.59.tar.gz/spa/include/spa/graph/graph.h
Changed
@@ -121,12 +121,12 @@ int __res = 0; \ spa_callbacks_call_res(&(n)->callbacks, \ struct spa_graph_node_callbacks, __res, \ - method, version, ##__VA_ARGS__); \ + method, (version), ##__VA_ARGS__); \ __res; \ }) -#define spa_graph_node_process(n) spa_graph_node_call(n, process, 0, n) -#define spa_graph_node_reuse_buffer(n,p,i) spa_graph_node_call(n, reuse_buffer, 0, n, p, i) +#define spa_graph_node_process(n) spa_graph_node_call((n), process, 0, (n)) +#define spa_graph_node_reuse_buffer(n,p,i) spa_graph_node_call((n), reuse_buffer, 0, (n), (p), (i)) struct spa_graph_port { struct spa_list link; /**< link in node port list */
View file
pipewire-0.3.58.tar.gz/spa/include/spa/interfaces/audio/aec.h -> pipewire-0.3.59.tar.gz/spa/include/spa/interfaces/audio/aec.h
Changed
@@ -80,10 +80,10 @@ #define spa_audio_aec_method(o,method,version,...) \ ({ \ int _res = -ENOTSUP; \ - struct spa_audio_aec *_o = o; \ + struct spa_audio_aec *_o = (o); \ spa_interface_call_res(&_o->iface, \ struct spa_audio_aec_methods, _res, \ - method, version, ##__VA_ARGS__); \ + method, (version), ##__VA_ARGS__); \ _res; \ })
View file
pipewire-0.3.58.tar.gz/spa/include/spa/monitor/device.h -> pipewire-0.3.59.tar.gz/spa/include/spa/monitor/device.h
Changed
@@ -71,7 +71,7 @@ uint32_t n_params; /**< number of elements in params */ }; -#define SPA_DEVICE_INFO_INIT() (struct spa_device_info){ SPA_VERSION_DEVICE_INFO, } +#define SPA_DEVICE_INFO_INIT() ((struct spa_device_info){ SPA_VERSION_DEVICE_INFO, }) /** * Information about a device object @@ -92,7 +92,7 @@ const struct spa_dict *props; /**< extra object properties */ }; -#define SPA_DEVICE_OBJECT_INFO_INIT() (struct spa_device_object_info){ SPA_VERSION_DEVICE_OBJECT_INFO, } +#define SPA_DEVICE_OBJECT_INFO_INIT() ((struct spa_device_object_info){ SPA_VERSION_DEVICE_OBJECT_INFO, }) /** the result of spa_device_enum_params() */ #define SPA_RESULT_TYPE_DEVICE_PARAMS 1 @@ -243,10 +243,10 @@ #define spa_device_method(o,method,version,...) \ ({ \ int _res = -ENOTSUP; \ - struct spa_device *_o = o; \ + struct spa_device *_o = (o); \ spa_interface_call_res(&_o->iface, \ struct spa_device_methods, _res, \ - method, version, ##__VA_ARGS__); \ + method, (version), ##__VA_ARGS__); \ _res; \ })
View file
pipewire-0.3.58.tar.gz/spa/include/spa/node/io.h -> pipewire-0.3.59.tar.gz/spa/include/spa/node/io.h
Changed
@@ -100,7 +100,7 @@ uint32_t buffer_id; /**< a buffer id */ }; -#define SPA_IO_BUFFERS_INIT (struct spa_io_buffers) { SPA_STATUS_OK, SPA_ID_INVALID, } +#define SPA_IO_BUFFERS_INIT ((struct spa_io_buffers) { SPA_STATUS_OK, SPA_ID_INVALID, }) /** * IO area to exchange a memory region @@ -110,7 +110,7 @@ uint32_t size; /**< the size of \a data */ void *data; /**< a memory pointer */ }; -#define SPA_IO_MEMORY_INIT (struct spa_io_memory) { SPA_STATUS_OK, 0, NULL, } +#define SPA_IO_MEMORY_INIT ((struct spa_io_memory) { SPA_STATUS_OK, 0, NULL, }) /** A range, suitable for input ports that can suggest a range to output ports */ struct spa_io_range {
View file
pipewire-0.3.58.tar.gz/spa/include/spa/node/node.h -> pipewire-0.3.59.tar.gz/spa/include/spa/node/node.h
Changed
@@ -85,7 +85,7 @@ uint32_t n_params; /**< number of items in \a params */ }; -#define SPA_NODE_INFO_INIT() (struct spa_node_info) { 0, } +#define SPA_NODE_INFO_INIT() ((struct spa_node_info) { 0, }) /** * Port information structure @@ -124,7 +124,7 @@ uint32_t n_params; /**< number of items in \a params */ }; -#define SPA_PORT_INFO_INIT() (struct spa_port_info) { 0, } +#define SPA_PORT_INFO_INIT() ((struct spa_port_info) { 0, }) #define SPA_RESULT_TYPE_NODE_ERROR 1 #define SPA_RESULT_TYPE_NODE_PARAMS 2
View file
pipewire-0.3.58.tar.gz/spa/include/spa/param/audio/dsd.h -> pipewire-0.3.59.tar.gz/spa/include/spa/param/audio/dsd.h
Changed
@@ -68,7 +68,7 @@ uint32_t positionSPA_AUDIO_MAX_CHANNELS; /*< channel position from enum spa_audio_channel */ }; -#define SPA_AUDIO_INFO_DSD_INIT(...) (struct spa_audio_info_dsd) { __VA_ARGS__ } +#define SPA_AUDIO_INFO_DSD_INIT(...) ((struct spa_audio_info_dsd) { __VA_ARGS__ }) /** * \}
View file
pipewire-0.3.58.tar.gz/spa/include/spa/param/audio/iec958.h -> pipewire-0.3.59.tar.gz/spa/include/spa/param/audio/iec958.h
Changed
@@ -56,7 +56,7 @@ uint32_t rate; /*< sample rate */ }; -#define SPA_AUDIO_INFO_IEC958_INIT(...) (struct spa_audio_info_iec958) { __VA_ARGS__ } +#define SPA_AUDIO_INFO_IEC958_INIT(...) ((struct spa_audio_info_iec958) { __VA_ARGS__ }) /** * \}
View file
pipewire-0.3.58.tar.gz/spa/include/spa/param/audio/raw.h -> pipewire-0.3.59.tar.gz/spa/include/spa/param/audio/raw.h
Changed
@@ -294,7 +294,7 @@ uint32_t positionSPA_AUDIO_MAX_CHANNELS; /*< channel position from enum spa_audio_channel */ }; -#define SPA_AUDIO_INFO_RAW_INIT(...) (struct spa_audio_info_raw) { __VA_ARGS__ } +#define SPA_AUDIO_INFO_RAW_INIT(...) ((struct spa_audio_info_raw) { __VA_ARGS__ }) #define SPA_KEY_AUDIO_FORMAT "audio.format" /**< an audio format as string, * Ex. "S16LE" */ @@ -311,7 +311,7 @@ enum spa_audio_format format; /*< format, one of the DSP formats in enum spa_audio_format_dsp */ }; -#define SPA_AUDIO_INFO_DSP_INIT(...) (struct spa_audio_info_dsp) { __VA_ARGS__ } +#define SPA_AUDIO_INFO_DSP_INIT(...) ((struct spa_audio_info_dsp) { __VA_ARGS__ }) /** * \}
View file
pipewire-0.3.58.tar.gz/spa/include/spa/param/bluetooth/audio.h -> pipewire-0.3.59.tar.gz/spa/include/spa/param/bluetooth/audio.h
Changed
@@ -58,6 +58,9 @@ /* HFP */ SPA_BLUETOOTH_AUDIO_CODEC_CVSD = 0x100, SPA_BLUETOOTH_AUDIO_CODEC_MSBC, + + /* BAP */ + SPA_BLUETOOTH_AUDIO_CODEC_LC3 = 0x200, }; /**
View file
pipewire-0.3.58.tar.gz/spa/include/spa/param/bluetooth/type-info.h -> pipewire-0.3.59.tar.gz/spa/include/spa/param/bluetooth/type-info.h
Changed
@@ -62,6 +62,8 @@ { SPA_BLUETOOTH_AUDIO_CODEC_CVSD, SPA_TYPE_Int, SPA_TYPE_INFO_BLUETOOTH_AUDIO_CODEC_BASE "cvsd", NULL }, { SPA_BLUETOOTH_AUDIO_CODEC_MSBC, SPA_TYPE_Int, SPA_TYPE_INFO_BLUETOOTH_AUDIO_CODEC_BASE "msbc", NULL }, + { SPA_BLUETOOTH_AUDIO_CODEC_LC3, SPA_TYPE_Int, SPA_TYPE_INFO_BLUETOOTH_AUDIO_CODEC_BASE "lc3", NULL }, + { 0, 0, NULL, NULL }, };
View file
pipewire-0.3.58.tar.gz/spa/include/spa/param/latency-utils.h -> pipewire-0.3.59.tar.gz/spa/include/spa/param/latency-utils.h
Changed
@@ -50,7 +50,7 @@ uint64_t max_ns; }; -#define SPA_LATENCY_INFO(dir,...) (struct spa_latency_info) { .direction = (dir), ## __VA_ARGS__ } +#define SPA_LATENCY_INFO(dir,...) ((struct spa_latency_info) { .direction = (dir), ## __VA_ARGS__ }) static inline int spa_latency_info_compare(const struct spa_latency_info *a, struct spa_latency_info *b) @@ -146,7 +146,7 @@ uint64_t ns; }; -#define SPA_PROCESS_LATENCY_INFO_INIT(...) (struct spa_process_latency_info) { __VA_ARGS__ } +#define SPA_PROCESS_LATENCY_INFO_INIT(...) ((struct spa_process_latency_info) { __VA_ARGS__ }) static inline int spa_process_latency_parse(const struct spa_pod *latency, struct spa_process_latency_info *info)
View file
pipewire-0.3.58.tar.gz/spa/include/spa/param/param.h -> pipewire-0.3.59.tar.gz/spa/include/spa/param/param.h
Changed
@@ -75,7 +75,7 @@ uint32_t padding5; }; -#define SPA_PARAM_INFO(id,flags) (struct spa_param_info){ (id), (flags) } +#define SPA_PARAM_INFO(id,flags) ((struct spa_param_info){ (id), (flags) }) /** properties for SPA_TYPE_OBJECT_ParamBuffers */ enum spa_param_buffers {
View file
pipewire-0.3.58.tar.gz/spa/include/spa/param/video/raw.h -> pipewire-0.3.59.tar.gz/spa/include/spa/param/video/raw.h
Changed
@@ -206,14 +206,14 @@ enum spa_video_color_primaries color_primaries; /**< color primaries. used to convert between R'G'B' and CIE XYZ */ }; -#define SPA_VIDEO_INFO_RAW_INIT(...) (struct spa_video_info_raw) { __VA_ARGS__ } +#define SPA_VIDEO_INFO_RAW_INIT(...) ((struct spa_video_info_raw) { __VA_ARGS__ }) struct spa_video_info_dsp { enum spa_video_format format; int64_t modifier; }; -#define SPA_VIDEO_INFO_DSP_INIT(...) (struct spa_video_info_dsp) { __VA_ARGS__ } +#define SPA_VIDEO_INFO_DSP_INIT(...) ((struct spa_video_info_dsp) { __VA_ARGS__ }) /** * \}
View file
pipewire-0.3.58.tar.gz/spa/include/spa/pod/builder.h -> pipewire-0.3.59.tar.gz/spa/include/spa/pod/builder.h
Changed
@@ -69,7 +69,7 @@ struct spa_callbacks callbacks; }; -#define SPA_POD_BUILDER_INIT(buffer,size) (struct spa_pod_builder){ buffer, size, 0, {}, {} } +#define SPA_POD_BUILDER_INIT(buffer,size) ((struct spa_pod_builder){ (buffer), (size), 0, {}, {} }) static inline void spa_pod_builder_get_state(struct spa_pod_builder *builder, struct spa_pod_builder_state *state) @@ -143,8 +143,10 @@ if (offset + size > builder->size) { res = -ENOSPC; - spa_callbacks_call_res(&builder->callbacks, struct spa_pod_builder_callbacks, res, - overflow, 0, offset + size); + if (offset <= builder->size) + spa_callbacks_call_res(&builder->callbacks, + struct spa_pod_builder_callbacks, res, + overflow, 0, offset + size); } if (res == 0 && data) memcpy(SPA_PTROFF(builder->data, offset, void), data, size); @@ -212,7 +214,7 @@ return res; } -#define SPA_POD_INIT(size,type) (struct spa_pod) { size, type } +#define SPA_POD_INIT(size,type) ((struct spa_pod) { (size), (type) }) #define SPA_POD_INIT_None() SPA_POD_INIT(0, SPA_TYPE_None) @@ -229,7 +231,7 @@ return spa_pod_builder_raw(builder, &p, sizeof(p)); } -#define SPA_POD_INIT_Bool(val) (struct spa_pod_bool){ { sizeof(uint32_t), SPA_TYPE_Bool }, val ? 1 : 0, 0 } +#define SPA_POD_INIT_Bool(val) ((struct spa_pod_bool){ { sizeof(uint32_t), SPA_TYPE_Bool }, (val) ? 1 : 0, 0 }) static inline int spa_pod_builder_bool(struct spa_pod_builder *builder, bool val) { @@ -237,7 +239,7 @@ return spa_pod_builder_primitive(builder, &p.pod); } -#define SPA_POD_INIT_Id(val) (struct spa_pod_id){ { sizeof(uint32_t), SPA_TYPE_Id }, (uint32_t)val, 0 } +#define SPA_POD_INIT_Id(val) ((struct spa_pod_id){ { sizeof(uint32_t), SPA_TYPE_Id }, (val), 0 }) static inline int spa_pod_builder_id(struct spa_pod_builder *builder, uint32_t val) { @@ -245,7 +247,7 @@ return spa_pod_builder_primitive(builder, &p.pod); } -#define SPA_POD_INIT_Int(val) (struct spa_pod_int){ { sizeof(int32_t), SPA_TYPE_Int }, (int32_t)val, 0 } +#define SPA_POD_INIT_Int(val) ((struct spa_pod_int){ { sizeof(int32_t), SPA_TYPE_Int }, (val), 0 }) static inline int spa_pod_builder_int(struct spa_pod_builder *builder, int32_t val) { @@ -253,7 +255,7 @@ return spa_pod_builder_primitive(builder, &p.pod); } -#define SPA_POD_INIT_Long(val) (struct spa_pod_long){ { sizeof(int64_t), SPA_TYPE_Long }, (int64_t)val } +#define SPA_POD_INIT_Long(val) ((struct spa_pod_long){ { sizeof(int64_t), SPA_TYPE_Long }, (val) }) static inline int spa_pod_builder_long(struct spa_pod_builder *builder, int64_t val) { @@ -261,7 +263,7 @@ return spa_pod_builder_primitive(builder, &p.pod); } -#define SPA_POD_INIT_Float(val) (struct spa_pod_float){ { sizeof(float), SPA_TYPE_Float }, val, 0 } +#define SPA_POD_INIT_Float(val) ((struct spa_pod_float){ { sizeof(float), SPA_TYPE_Float }, (val), 0 }) static inline int spa_pod_builder_float(struct spa_pod_builder *builder, float val) { @@ -269,7 +271,7 @@ return spa_pod_builder_primitive(builder, &p.pod); } -#define SPA_POD_INIT_Double(val) (struct spa_pod_double){ { sizeof(double), SPA_TYPE_Double }, val } +#define SPA_POD_INIT_Double(val) ((struct spa_pod_double){ { sizeof(double), SPA_TYPE_Double }, (val) }) static inline int spa_pod_builder_double(struct spa_pod_builder *builder, double val) { @@ -277,7 +279,7 @@ return spa_pod_builder_primitive(builder, &p.pod); } -#define SPA_POD_INIT_String(len) (struct spa_pod_string){ { len, SPA_TYPE_String } } +#define SPA_POD_INIT_String(len) ((struct spa_pod_string){ { (len), SPA_TYPE_String } }) static inline int spa_pod_builder_write_string(struct spa_pod_builder *builder, const char *str, uint32_t len) @@ -307,7 +309,7 @@ return spa_pod_builder_string_len(builder, str ? str : "", len); } -#define SPA_POD_INIT_Bytes(len) (struct spa_pod_bytes){ { len, SPA_TYPE_Bytes } } +#define SPA_POD_INIT_Bytes(len) ((struct spa_pod_bytes){ { (len), SPA_TYPE_Bytes } }) static inline int spa_pod_builder_bytes(struct spa_pod_builder *builder, const void *bytes, uint32_t len) @@ -327,7 +329,7 @@ return SPA_POD_BODY(spa_pod_builder_deref(builder, offset)); } -#define SPA_POD_INIT_Pointer(type,value) (struct spa_pod_pointer){ { sizeof(struct spa_pod_pointer_body), SPA_TYPE_Pointer }, { type, 0, value } } +#define SPA_POD_INIT_Pointer(type,value) ((struct spa_pod_pointer){ { sizeof(struct spa_pod_pointer_body), SPA_TYPE_Pointer }, { (type), 0, (value) } }) static inline int spa_pod_builder_pointer(struct spa_pod_builder *builder, uint32_t type, const void *val) @@ -336,7 +338,7 @@ return spa_pod_builder_primitive(builder, &p.pod); } -#define SPA_POD_INIT_Fd(fd) (struct spa_pod_fd){ { sizeof(int64_t), SPA_TYPE_Fd }, fd } +#define SPA_POD_INIT_Fd(fd) ((struct spa_pod_fd){ { sizeof(int64_t), SPA_TYPE_Fd }, (fd) }) static inline int spa_pod_builder_fd(struct spa_pod_builder *builder, int64_t fd) { @@ -344,7 +346,7 @@ return spa_pod_builder_primitive(builder, &p.pod); } -#define SPA_POD_INIT_Rectangle(val) (struct spa_pod_rectangle){ { sizeof(struct spa_rectangle), SPA_TYPE_Rectangle }, val } +#define SPA_POD_INIT_Rectangle(val) ((struct spa_pod_rectangle){ { sizeof(struct spa_rectangle), SPA_TYPE_Rectangle }, (val) }) static inline int spa_pod_builder_rectangle(struct spa_pod_builder *builder, uint32_t width, uint32_t height) @@ -353,7 +355,7 @@ return spa_pod_builder_primitive(builder, &p.pod); } -#define SPA_POD_INIT_Fraction(val) (struct spa_pod_fraction){ { sizeof(struct spa_fraction), SPA_TYPE_Fraction }, val } +#define SPA_POD_INIT_Fraction(val) ((struct spa_pod_fraction){ { sizeof(struct spa_fraction), SPA_TYPE_Fraction }, (val) }) static inline int spa_pod_builder_fraction(struct spa_pod_builder *builder, uint32_t num, uint32_t denom) @@ -389,12 +391,12 @@ } #define SPA_POD_INIT_CHOICE_BODY(type, flags, child_size, child_type) \ - (struct spa_pod_choice_body) { type, flags, { child_size, child_type }} + ((struct spa_pod_choice_body) { (type), (flags), { (child_size), (child_type) }}) #define SPA_POD_INIT_Choice(type, ctype, child_type, n_vals, ...) \ - (struct { struct spa_pod_choice choice; ctype valsn_vals;}) \ - { { { n_vals * sizeof(ctype) + sizeof(struct spa_pod_choice_body), SPA_TYPE_Choice }, \ - { type, 0, { sizeof(ctype), child_type } } }, { __VA_ARGS__ } } + ((struct { struct spa_pod_choice choice; ctype vals(n_vals);}) \ + { { { (n_vals) * sizeof(ctype) + sizeof(struct spa_pod_choice_body), SPA_TYPE_Choice }, \ + { (type), 0, { sizeof(ctype), (child_type) } } }, { __VA_ARGS__ } }) static inline int spa_pod_builder_push_choice(struct spa_pod_builder *builder, struct spa_pod_frame *frame, @@ -409,7 +411,7 @@ return res; } -#define SPA_POD_INIT_Struct(size) (struct spa_pod_struct){ { size, SPA_TYPE_Struct } } +#define SPA_POD_INIT_Struct(size) ((struct spa_pod_struct){ { (size), SPA_TYPE_Struct } }) static inline int spa_pod_builder_push_struct(struct spa_pod_builder *builder, struct spa_pod_frame *frame) @@ -421,7 +423,7 @@ return res; } -#define SPA_POD_INIT_Object(size,type,id,...) (struct spa_pod_object){ { size, SPA_TYPE_Object }, { type, id }, ##__VA_ARGS__ } +#define SPA_POD_INIT_Object(size,type,id,...) ((struct spa_pod_object){ { (size), SPA_TYPE_Object }, { (type), (id) }, ##__VA_ARGS__ }) static inline int spa_pod_builder_push_object(struct spa_pod_builder *builder, struct spa_pod_frame *frame, @@ -436,7 +438,7 @@ } #define SPA_POD_INIT_Prop(key,flags,size,type) \ - (struct spa_pod_prop){ key, flags, { size, type } } + ((struct spa_pod_prop){ (key), (flags), { (size), (type) } }) static inline int spa_pod_builder_prop(struct spa_pod_builder *builder, uint32_t key, uint32_t flags) @@ -446,7 +448,7 @@ } #define SPA_POD_INIT_Sequence(size,unit) \ - (struct spa_pod_sequence){ { size, SPA_TYPE_Sequence}, {unit, 0 } } + ((struct spa_pod_sequence){ { (size), SPA_TYPE_Sequence}, {(unit), 0 } }) static inline int spa_pod_builder_push_sequence(struct spa_pod_builder *builder, struct spa_pod_frame *frame, uint32_t unit) @@ -650,26 +652,29 @@ #define spa_pod_builder_add_object(b,type,id,...) \ ({ \ + struct spa_pod_builder *_b = (b); \ struct spa_pod_frame _f; \ - spa_pod_builder_push_object(b, &_f, type, id); \ - spa_pod_builder_add(b, ##__VA_ARGS__, 0); \ - spa_pod_builder_pop(b, &_f); \ + spa_pod_builder_push_object(_b, &_f, type, id); \ + spa_pod_builder_add(_b, ##__VA_ARGS__, 0); \ + spa_pod_builder_pop(_b, &_f); \ }) #define spa_pod_builder_add_struct(b,...) \ ({ \ + struct spa_pod_builder *_b = (b); \ struct spa_pod_frame _f; \ - spa_pod_builder_push_struct(b, &_f); \ - spa_pod_builder_add(b, ##__VA_ARGS__, NULL); \ - spa_pod_builder_pop(b, &_f); \ + spa_pod_builder_push_struct(_b, &_f); \ + spa_pod_builder_add(_b, ##__VA_ARGS__, NULL); \ + spa_pod_builder_pop(_b, &_f); \ }) #define spa_pod_builder_add_sequence(b,unit,...) \ ({ \ + struct spa_pod_builder *_b = (b); \ struct spa_pod_frame _f; \ - spa_pod_builder_push_sequence(b, &_f, unit); \ - spa_pod_builder_add(b, ##__VA_ARGS__, 0, 0); \ - spa_pod_builder_pop(b, &_f); \ + spa_pod_builder_push_sequence(_b, &_f, unit); \ + spa_pod_builder_add(_b, ##__VA_ARGS__, 0, 0); \ + spa_pod_builder_pop(_b, &_f); \ }) /** Copy a pod structure */
View file
pipewire-0.3.58.tar.gz/spa/include/spa/pod/command.h -> pipewire-0.3.59.tar.gz/spa/include/spa/pod/command.h
Changed
@@ -47,12 +47,12 @@ }; #define SPA_COMMAND_TYPE(cmd) ((cmd)->body.body.type) -#define SPA_COMMAND_ID(cmd,type) (SPA_COMMAND_TYPE(cmd) == type ? \ +#define SPA_COMMAND_ID(cmd,type) (SPA_COMMAND_TYPE(cmd) == (type) ? \ (cmd)->body.body.id : SPA_ID_INVALID) -#define SPA_COMMAND_INIT_FULL(t,size,type,id,...) (t) \ - { { size, SPA_TYPE_Object }, \ - { { type, id }, ##__VA_ARGS__ } } \ +#define SPA_COMMAND_INIT_FULL(t,size,type,id,...) ((t) \ + { { (size), SPA_TYPE_Object }, \ + { { (type), (id) }, ##__VA_ARGS__ } }) #define SPA_COMMAND_INIT(type,id) \ SPA_COMMAND_INIT_FULL(struct spa_command, \
View file
pipewire-0.3.58.tar.gz/spa/include/spa/pod/event.h -> pipewire-0.3.59.tar.gz/spa/include/spa/pod/event.h
Changed
@@ -46,7 +46,7 @@ }; #define SPA_EVENT_TYPE(ev) ((ev)->body.body.type) -#define SPA_EVENT_ID(ev,type) (SPA_EVENT_TYPE(ev) == type ? \ +#define SPA_EVENT_ID(ev,type) (SPA_EVENT_TYPE(ev) == (type) ? \ (ev)->body.body.id : SPA_ID_INVALID) #define SPA_EVENT_INIT_FULL(t,size,type,id,...) (t) \
View file
pipewire-0.3.58.tar.gz/spa/include/spa/pod/parser.h -> pipewire-0.3.59.tar.gz/spa/include/spa/pod/parser.h
Changed
@@ -53,7 +53,7 @@ struct spa_pod_parser_state state; }; -#define SPA_POD_PARSER_INIT(buffer,size) (struct spa_pod_parser){ buffer, size, 0, {} } +#define SPA_POD_PARSER_INIT(buffer,size) ((struct spa_pod_parser){ (buffer), (size), 0, {} }) static inline void spa_pod_parser_init(struct spa_pod_parser *parser, const void *data, uint32_t size) @@ -82,12 +82,20 @@ static inline struct spa_pod * spa_pod_parser_deref(struct spa_pod_parser *parser, uint32_t offset, uint32_t size) { - if (offset + 8 <= size) { - struct spa_pod *pod = SPA_PTROFF(parser->data, offset, struct spa_pod); - if (offset + SPA_POD_SIZE(pod) <= size) - return pod; + /* Cast to uint64_t to avoid wraparound. Add 8 for the pod itself. */ + const uint64_t long_offset = (uint64_t)offset + 8; + if (long_offset <= size && (offset & 7) == 0) { + /* Use void* because creating a misaligned pointer is undefined. */ + void *pod = SPA_PTROFF(parser->data, offset, void); + /* + * Check that the pointer is aligned and that the size (rounded + * to the next multiple of 8) is in bounds. + */ + if (SPA_IS_ALIGNED(pod, __alignof__(struct spa_pod)) && + long_offset + SPA_ROUND_UP_N((uint64_t)SPA_POD_BODY_SIZE(pod), 8) <= size) + return (struct spa_pod *)pod; } - return NULL; + return NULL; } static inline struct spa_pod *spa_pod_parser_frame(struct spa_pod_parser *parser, struct spa_pod_frame *frame) @@ -285,10 +293,15 @@ if (pod == NULL) return false; - if (spa_pod_is_choice(pod) && - SPA_POD_CHOICE_TYPE(pod) == SPA_CHOICE_None && - spa_pod_parser_can_collect(SPA_POD_CHOICE_CHILD(pod), type)) - return true; + if (SPA_POD_TYPE(pod) == SPA_TYPE_Choice) { + if (!spa_pod_is_choice(pod)) + return false; + if (type == 'V') + return true; + if (SPA_POD_CHOICE_TYPE(pod) != SPA_CHOICE_None) + return false; + pod = SPA_POD_CHOICE_CHILD(pod); + } switch (type) { case 'P': @@ -328,7 +341,6 @@ case 'O': return spa_pod_is_object(pod) || spa_pod_is_none(pod); case 'V': - return spa_pod_is_choice(pod); default: return false; } @@ -355,7 +367,7 @@ break; \ case 's': \ *va_arg(args, char**) = \ - (pod == NULL || (SPA_POD_TYPE(pod) == SPA_TYPE_None) \ + ((pod) == NULL || (SPA_POD_TYPE(pod) == SPA_TYPE_None) \ ? NULL \ : (char *)SPA_POD_CONTENTS(struct spa_pod_string, pod)); \ break; \ @@ -407,8 +419,8 @@ { \ const struct spa_pod **d = va_arg(args, const struct spa_pod**); \ if (d) \ - *d = (pod == NULL || (SPA_POD_TYPE(pod) == SPA_TYPE_None) \ - ? NULL : pod); \ + *d = ((pod) == NULL || (SPA_POD_TYPE(pod) == SPA_TYPE_None) \ + ? NULL : (pod)); \ break; \ } \ default: \ @@ -493,8 +505,7 @@ } SPA_POD_PARSER_SKIP(*format, args); } else { - if (pod->type == SPA_TYPE_Choice && *format != 'V' && - SPA_POD_CHOICE_TYPE(pod) == SPA_CHOICE_None) + if (pod->type == SPA_TYPE_Choice && *format != 'V') pod = SPA_POD_CHOICE_CHILD(pod); SPA_POD_PARSER_COLLECT(pod, *format, args);
View file
pipewire-0.3.58.tar.gz/spa/include/spa/pod/pod.h -> pipewire-0.3.59.tar.gz/spa/include/spa/pod/pod.h
Changed
@@ -39,7 +39,7 @@ #define SPA_POD_BODY_SIZE(pod) (((struct spa_pod*)(pod))->size) #define SPA_POD_TYPE(pod) (((struct spa_pod*)(pod))->type) -#define SPA_POD_SIZE(pod) (sizeof(struct spa_pod) + SPA_POD_BODY_SIZE(pod)) +#define SPA_POD_SIZE(pod) ((uint64_t)sizeof(struct spa_pod) + SPA_POD_BODY_SIZE(pod)) #define SPA_POD_CONTENTS_SIZE(type,pod) (SPA_POD_SIZE(pod)-sizeof(type)) #define SPA_POD_CONTENTS(type,pod) SPA_PTROFF((pod),sizeof(type),void) @@ -52,7 +52,7 @@ uint32_t type; /* a basic id of enum spa_type */ }; -#define SPA_POD_VALUE(type,pod) (((type*)pod)->value) +#define SPA_POD_VALUE(type,pod) (((type*)(pod))->value) struct spa_pod_bool { struct spa_pod pod;
View file
pipewire-0.3.58.tar.gz/spa/include/spa/support/log-impl.h -> pipewire-0.3.59.tar.gz/spa/include/spa/support/log-impl.h
Changed
@@ -121,7 +121,7 @@ #define SPA_LOG_IMPL_INIT(name) \ { { { SPA_TYPE_INTERFACE_Log, SPA_VERSION_LOG, \ - SPA_CALLBACKS_INIT(&name.methods, &name) }, \ + SPA_CALLBACKS_INIT(&(name).methods, &(name)) }, \ SPA_LOG_LEVEL_INFO, }, \ { SPA_VERSION_LOG_METHODS, \ spa_log_impl_log, \
View file
pipewire-0.3.58.tar.gz/spa/include/spa/support/log.h -> pipewire-0.3.59.tar.gz/spa/include/spa/support/log.h
Changed
@@ -212,7 +212,7 @@ #define SPA_LOG_TOPIC(v, t) \ - (struct spa_log_topic){ .version = v, .topic = (t)} + (struct spa_log_topic){ .version = (v), .topic = (t)} #define spa_log_topic_init(l, topic) \ do { \ @@ -231,10 +231,10 @@ ({ \ struct spa_log *_log = l; \ enum spa_log_level _lev = _log ? _log->level : SPA_LOG_LEVEL_NONE; \ - struct spa_log_topic *_t = (struct spa_log_topic *)topic; \ + struct spa_log_topic *_t = (struct spa_log_topic *)(topic); \ if (_t && _t->has_custom_level) \ _lev = _t->level; \ - _lev >= lev; \ + _lev >= (lev); \ }) /* Transparently calls to version 0 log if v1 is not supported */ @@ -269,26 +269,32 @@ } \ }) +#define spa_logt_lev(l,lev,t,...) \ + spa_log_logt(l,lev,t,__FILE__,__LINE__,__func__,__VA_ARGS__) + +#define spa_log_lev(l,lev,...) \ + spa_logt_lev(l,lev,SPA_LOG_TOPIC_DEFAULT,__VA_ARGS__) + #define spa_log_log(l,lev,...) \ spa_log_logt(l,lev,SPA_LOG_TOPIC_DEFAULT,__VA_ARGS__) #define spa_log_logv(l,lev,...) \ spa_log_logtv(l,lev,SPA_LOG_TOPIC_DEFAULT,__VA_ARGS__) -#define spa_log_error(l,...) spa_log_log(l,SPA_LOG_LEVEL_ERROR,__FILE__,__LINE__,__func__,__VA_ARGS__) -#define spa_log_warn(l,...) spa_log_log(l,SPA_LOG_LEVEL_WARN,__FILE__,__LINE__,__func__,__VA_ARGS__) -#define spa_log_info(l,...) spa_log_log(l,SPA_LOG_LEVEL_INFO,__FILE__,__LINE__,__func__,__VA_ARGS__) -#define spa_log_debug(l,...) spa_log_log(l,SPA_LOG_LEVEL_DEBUG,__FILE__,__LINE__,__func__,__VA_ARGS__) -#define spa_log_trace(l,...) spa_log_log(l,SPA_LOG_LEVEL_TRACE,__FILE__,__LINE__,__func__,__VA_ARGS__) +#define spa_log_error(l,...) spa_log_lev(l,SPA_LOG_LEVEL_ERROR,__VA_ARGS__) +#define spa_log_warn(l,...) spa_log_lev(l,SPA_LOG_LEVEL_WARN,__VA_ARGS__) +#define spa_log_info(l,...) spa_log_lev(l,SPA_LOG_LEVEL_INFO,__VA_ARGS__) +#define spa_log_debug(l,...) spa_log_lev(l,SPA_LOG_LEVEL_DEBUG,__VA_ARGS__) +#define spa_log_trace(l,...) spa_log_lev(l,SPA_LOG_LEVEL_TRACE,__VA_ARGS__) -#define spa_logt_error(l,t,...) spa_log_logt(l,SPA_LOG_LEVEL_ERROR,t,__FILE__,__LINE__,__func__,__VA_ARGS__) -#define spa_logt_warn(l,t,...) spa_log_logt(l,SPA_LOG_LEVEL_WARN,t,__FILE__,__LINE__,__func__,__VA_ARGS__) -#define spa_logt_info(l,t,...) spa_log_logt(l,SPA_LOG_LEVEL_INFO,t,__FILE__,__LINE__,__func__,__VA_ARGS__) -#define spa_logt_debug(l,t,...) spa_log_logt(l,SPA_LOG_LEVEL_DEBUG,t,__FILE__,__LINE__,__func__,__VA_ARGS__) -#define spa_logt_trace(l,t,...) spa_log_logt(l,SPA_LOG_LEVEL_TRACE,t,__FILE__,__LINE__,__func__,__VA_ARGS__) +#define spa_logt_error(l,t,...) spa_logt_lev(l,SPA_LOG_LEVEL_ERROR,t,__VA_ARGS__) +#define spa_logt_warn(l,t,...) spa_logt_lev(l,SPA_LOG_LEVEL_WARN,t,__VA_ARGS__) +#define spa_logt_info(l,t,...) spa_logt_lev(l,SPA_LOG_LEVEL_INFO,t,__VA_ARGS__) +#define spa_logt_debug(l,t,...) spa_logt_lev(l,SPA_LOG_LEVEL_DEBUG,t,__VA_ARGS__) +#define spa_logt_trace(l,t,...) spa_logt_lev(l,SPA_LOG_LEVEL_TRACE,t,__VA_ARGS__) #ifndef FASTPATH -#define spa_log_trace_fp(l,...) spa_log_log(l,SPA_LOG_LEVEL_TRACE,__FILE__,__LINE__,__func__,__VA_ARGS__) +#define spa_log_trace_fp(l,...) spa_log_lev(l,SPA_LOG_LEVEL_TRACE,__VA_ARGS__) #else #define spa_log_trace_fp(l,...) #endif @@ -296,18 +302,16 @@ #define spa_log_hexdump(l,lev,indent,data,len) \ ({ \ char str512; \ - uint8_t *buf = (uint8_t *)data; \ - size_t i; \ + uint8_t *buf = (uint8_t *)(data); \ + size_t i, j = (len); \ int pos = 0; \ \ - for (i = 0; i < len; i++) { \ + for (i = 0; i < j; i++) { \ if (i % 16 == 0) \ pos = 0; \ pos += sprintf(str + pos, "%02x ", bufi); \ - if (i % 16 == 15 || i == len - 1) { \ - spa_log_log(l,lev,__FILE__,__LINE__,__func__, \ - "%*s" "%s",indent,"", str); \ - } \ + if (i % 16 == 15 || i == j - 1) \ + spa_log_lev(l,lev, "%*s" "%s",indent,"", str); \ } \ })
View file
pipewire-0.3.58.tar.gz/spa/include/spa/support/plugin.h -> pipewire-0.3.59.tar.gz/spa/include/spa/support/plugin.h
Changed
@@ -105,7 +105,7 @@ return NULL; } -#define SPA_SUPPORT_INIT(type,data) (struct spa_support) { (type), (data) } +#define SPA_SUPPORT_INIT(type,data) ((struct spa_support) { (type), (data) }) struct spa_handle_factory { /** The version of this structure */
View file
pipewire-0.3.58.tar.gz/spa/include/spa/support/system.h -> pipewire-0.3.59.tar.gz/spa/include/spa/support/system.h
Changed
@@ -119,7 +119,7 @@ #define spa_system_method_r(o,method,version,...) \ ({ \ - int _res = -ENOTSUP; \ + volatile int _res = -ENOTSUP; \ struct spa_system *_o = o; \ spa_interface_call_res(&_o->iface, \ struct spa_system_methods, _res, \
View file
pipewire-0.3.58.tar.gz/spa/include/spa/utils/defs.h -> pipewire-0.3.59.tar.gz/spa/include/spa/utils/defs.h
Changed
@@ -27,8 +27,18 @@ #ifdef __cplusplus extern "C" { +# if __cplusplus >= 201103L +# define SPA_STATIC_ASSERT static_assert +# endif #else -#include <stdbool.h> +# include <stdbool.h> +# if __STDC_VERSION__ >= 201112L +# define SPA_STATIC_ASSERT _Static_assert +# endif +#endif +#ifndef SPA_STATIC_ASSERT +#define SPA_STATIC_ASSERT(a, b) \ + ((void)sizeof(struct { int spa_static_assertion_failed : 2 * !!(a) - 1; })) #endif #include <inttypes.h> #include <signal.h> @@ -74,8 +84,16 @@ #define SPA_FLAG_MASK(field,mask,flag) (((field) & (mask)) == (flag)) #define SPA_FLAG_IS_SET(field,flag) SPA_FLAG_MASK(field,flag,flag) #define SPA_FLAG_SET(field,flag) ((field) |= (flag)) -#define SPA_FLAG_CLEAR(field,flag) ((field) &= ~(flag)) -#define SPA_FLAG_UPDATE(field,flag,val) ((val) ? SPA_FLAG_SET(field,flag) : SPA_FLAG_CLEAR(field,flag)) +#define SPA_FLAG_CLEAR(field, flag) \ +({ \ + SPA_STATIC_ASSERT(__builtin_constant_p(flag) ? \ + (__typeof__(flag))(__typeof__(field))(__typeof__(flag))(flag) == (flag) : \ + sizeof(field) >= sizeof(flag), \ + "truncation problem when masking " #field \ + " with ~" #flag); \ + ((field) &= ~(__typeof__(field))(flag)); \ +}) +#define SPA_FLAG_UPDATE(field,flag,val) ((val) ? SPA_FLAG_SET((field),(flag)) : SPA_FLAG_CLEAR((field),(flag))) enum spa_direction { SPA_DIRECTION_INPUT = 0, @@ -84,25 +102,25 @@ #define SPA_DIRECTION_REVERSE(d) ((d) ^ 1) -#define SPA_RECTANGLE(width,height) (struct spa_rectangle){ width, height } +#define SPA_RECTANGLE(width,height) ((struct spa_rectangle){ (width), (height) }) struct spa_rectangle { uint32_t width; uint32_t height; }; -#define SPA_POINT(x,y) (struct spa_point){ x, y } +#define SPA_POINT(x,y) ((struct spa_point){ (x), (y) }) struct spa_point { int32_t x; int32_t y; }; -#define SPA_REGION(x,y,width,height) (struct spa_region){ SPA_POINT(x,y), SPA_RECTANGLE(width,height) } +#define SPA_REGION(x,y,width,height) ((struct spa_region){ SPA_POINT(x,y), SPA_RECTANGLE(width,height) }) struct spa_region { struct spa_point position; struct spa_rectangle size; }; -#define SPA_FRACTION(num,denom) (struct spa_fraction){ num, denom } +#define SPA_FRACTION(num,denom) ((struct spa_fraction){ (num), (denom) }) struct spa_fraction { uint32_t num; uint32_t denom; @@ -120,7 +138,7 @@ * ``` */ #define SPA_FOR_EACH_ELEMENT(arr, ptr) \ - for (ptr = arr; (void*)ptr < SPA_PTROFF(arr, sizeof(arr), void); ptr++) + for ((ptr) = arr; (void*)(ptr) < SPA_PTROFF(arr, sizeof(arr), void); (ptr)++) #define SPA_ABS(a) \ ({ \ @@ -156,7 +174,7 @@ #define SPA_SWAP(a,b) \ ({ \ __typeof__(a) _t = (a); \ - a = b; b = _t; \ + (a) = b; (b) = _t; \ }) #define SPA_TYPECHECK(type,x) \ @@ -180,7 +198,7 @@ #define SPA_MEMBER(b,o,t) SPA_PTROFF(b,o,t) #define SPA_MEMBER_ALIGN(b,o,a,t) SPA_PTROFF_ALIGN(b,o,a,t) -#define SPA_CONTAINER_OF(p,t,m) ((t*)((uintptr_t)p - offsetof(t,m))) +#define SPA_CONTAINER_OF(p,t,m) ((t*)((uintptr_t)(p) - offsetof(t,m))) #define SPA_PTRDIFF(p1,p2) ((intptr_t)(p1) - (intptr_t)(p2)) @@ -194,7 +212,7 @@ #define SPA_IDX_INVALID ((unsigned int)-1) #define SPA_ID_INVALID ((uint32_t)0xffffffff) -#define SPA_NSEC_PER_SEC (1000000000ll) +#define SPA_NSEC_PER_SEC (1000000000LL) #define SPA_NSEC_PER_MSEC (1000000ll) #define SPA_NSEC_PER_USEC (1000ll) #define SPA_USEC_PER_SEC (1000000ll) @@ -239,7 +257,17 @@ #define SPA_ROUND_DOWN(num,value) ((num) - ((num) % (value))) #define SPA_ROUND_UP(num,value) ((((num) + (value) - 1) / (value)) * (value)) -#define SPA_ROUND_DOWN_N(num,align) ((num) & ~((align) - 1)) +#define SPA_MASK_NEGATED(num1, num2) \ +({ \ + SPA_STATIC_ASSERT(__builtin_constant_p(num2) ? \ + (__typeof__(num2))(__typeof__(num1))(__typeof__(num2))(num2) == (num2) : \ + sizeof(num1) >= sizeof(num2), \ + "truncation problem when masking " #num1 \ + " with ~" #num2); \ + ((num1) & ~(__typeof__(num1))(num2)); \ +}) + +#define SPA_ROUND_DOWN_N(num,align) SPA_MASK_NEGATED((num), (align) - 1) #define SPA_ROUND_UP_N(num,align) SPA_ROUND_DOWN_N((num) + ((align) - 1),align) #define SPA_PTR_ALIGNMENT(p,align) ((intptr_t)(p) & ((align)-1))
View file
pipewire-0.3.58.tar.gz/spa/include/spa/utils/dict.h -> pipewire-0.3.59.tar.gz/spa/include/spa/utils/dict.h
Changed
@@ -48,7 +48,7 @@ const char *value; }; -#define SPA_DICT_ITEM_INIT(key,value) (struct spa_dict_item) { key, value } +#define SPA_DICT_ITEM_INIT(key,value) ((struct spa_dict_item) { (key), (value) }) struct spa_dict { #define SPA_DICT_FLAG_SORTED (1<<0) /**< items are sorted */ @@ -57,8 +57,8 @@ const struct spa_dict_item *items; }; -#define SPA_DICT_INIT(items,n_items) (struct spa_dict) { 0, n_items, items } -#define SPA_DICT_INIT_ARRAY(items) (struct spa_dict) { 0, SPA_N_ELEMENTS(items), items } +#define SPA_DICT_INIT(items,n_items) ((struct spa_dict) { 0, (n_items), (items) }) +#define SPA_DICT_INIT_ARRAY(items) ((struct spa_dict) { 0, SPA_N_ELEMENTS(items), (items) }) #define spa_dict_for_each(item, dict) \ for ((item) = (dict)->items; \
View file
pipewire-0.3.58.tar.gz/spa/include/spa/utils/hook.h -> pipewire-0.3.59.tar.gz/spa/include/spa/utils/hook.h
Changed
@@ -142,7 +142,7 @@ * Initialize the set of functions \a funcs as a \ref spa_callbacks, together * with \a _data. */ -#define SPA_CALLBACKS_INIT(_funcs,_data) (struct spa_callbacks){ _funcs, _data, } +#define SPA_CALLBACKS_INIT(_funcs,_data) ((struct spa_callbacks){ (_funcs), (_data), }) /** \struct spa_interface */
View file
pipewire-0.3.58.tar.gz/spa/include/spa/utils/json.h -> pipewire-0.3.59.tar.gz/spa/include/spa/utils/json.h
Changed
@@ -58,20 +58,20 @@ uint32_t depth; }; -#define SPA_JSON_INIT(data,size) (struct spa_json) { (data), (data)+(size), } +#define SPA_JSON_INIT(data,size) ((struct spa_json) { (data), (data)+(size), }) static inline void spa_json_init(struct spa_json * iter, const char *data, size_t size) { *iter = SPA_JSON_INIT(data, size); } -#define SPA_JSON_ENTER(iter) (struct spa_json) { (iter)->cur, (iter)->end, (iter), } +#define SPA_JSON_ENTER(iter) ((struct spa_json) { (iter)->cur, (iter)->end, (iter), }) static inline void spa_json_enter(struct spa_json * iter, struct spa_json * sub) { *sub = SPA_JSON_ENTER(iter); } -#define SPA_JSON_SAVE(iter) (struct spa_json) { (iter)->cur, (iter)->end, } +#define SPA_JSON_SAVE(iter) ((struct spa_json) { (iter)->cur, (iter)->end, }) /** Get the next token. \a value points to the token and the return value * is the length. */
View file
pipewire-0.3.58.tar.gz/spa/include/spa/utils/list.h -> pipewire-0.3.59.tar.gz/spa/include/spa/utils/list.h
Changed
@@ -44,7 +44,7 @@ struct spa_list *prev; }; -#define SPA_LIST_INIT(list) (struct spa_list){ list, list } +#define SPA_LIST_INIT(list) ((struct spa_list){ (list), (list) }) static inline void spa_list_init(struct spa_list *list) { @@ -98,25 +98,25 @@ (&(pos)->member == (head)) #define spa_list_next(pos, member) \ - SPA_CONTAINER_OF((pos)->member.next, __typeof__(*pos), member) + SPA_CONTAINER_OF((pos)->member.next, __typeof__(*(pos)), member) #define spa_list_prev(pos, member) \ - SPA_CONTAINER_OF((pos)->member.prev, __typeof__(*pos), member) + SPA_CONTAINER_OF((pos)->member.prev, __typeof__(*(pos)), member) #define spa_list_consume(pos, head, member) \ - for (pos = spa_list_first(head, __typeof__(*pos), member); \ + for ((pos) = spa_list_first(head, __typeof__(*(pos)), member); \ !spa_list_is_empty(head); \ - pos = spa_list_first(head, __typeof__(*pos), member)) + (pos) = spa_list_first(head, __typeof__(*(pos)), member)) #define spa_list_for_each_next(pos, head, curr, member) \ - for (pos = spa_list_first(curr, __typeof__(*pos), member); \ + for ((pos) = spa_list_first(curr, __typeof__(*(pos)), member); \ !spa_list_is_end(pos, head, member); \ - pos = spa_list_next(pos, member)) + (pos) = spa_list_next(pos, member)) #define spa_list_for_each_prev(pos, head, curr, member) \ - for (pos = spa_list_last(curr, __typeof__(*pos), member); \ + for ((pos) = spa_list_last(curr, __typeof__(*(pos)), member); \ !spa_list_is_end(pos, head, member); \ - pos = spa_list_prev(pos, member)) + (pos) = spa_list_prev(pos, member)) #define spa_list_for_each(pos, head, member) \ spa_list_for_each_next(pos, head, head, member) @@ -125,16 +125,16 @@ spa_list_for_each_prev(pos, head, head, member) #define spa_list_for_each_safe_next(pos, tmp, head, curr, member) \ - for (pos = spa_list_first(curr, __typeof__(*pos), member); \ - tmp = spa_list_next(pos, member), \ + for ((pos) = spa_list_first(curr, __typeof__(*(pos)), member); \ + (tmp) = spa_list_next(pos, member), \ !spa_list_is_end(pos, head, member); \ - pos = tmp) + (pos) = (tmp)) #define spa_list_for_each_safe_prev(pos, tmp, head, curr, member) \ - for (pos = spa_list_last(curr, __typeof__(*pos), member); \ - tmp = spa_list_prev(pos, member), \ + for ((pos) = spa_list_last(curr, __typeof__(*(pos)), member); \ + (tmp) = spa_list_prev(pos, member), \ !spa_list_is_end(pos, head, member); \ - pos = tmp) + (pos) = (tmp)) #define spa_list_for_each_safe(pos, tmp, head, member) \ spa_list_for_each_safe_next(pos, tmp, head, head, member) @@ -146,11 +146,11 @@ spa_list_prepend(head, &(cursor).member) #define spa_list_for_each_cursor(pos, cursor, head, member) \ - for(pos = spa_list_first(&(cursor).member, __typeof__(*(pos)), member); \ + for((pos) = spa_list_first(&(cursor).member, __typeof__(*(pos)), member); \ spa_list_remove(&(pos)->member), \ spa_list_append(&(cursor).member, &(pos)->member), \ !spa_list_is_end(pos, head, member); \ - pos = spa_list_next(&cursor, member)) + (pos) = spa_list_next(&(cursor), member)) #define spa_list_cursor_end(cursor, member) \ spa_list_remove(&(cursor).member)
View file
pipewire-0.3.58.tar.gz/spa/include/spa/utils/names.h -> pipewire-0.3.59.tar.gz/spa/include/spa/utils/names.h
Changed
@@ -114,13 +114,15 @@ /** keys for bluez5 factory names */ #define SPA_NAME_API_BLUEZ5_ENUM_DBUS "api.bluez5.enum.dbus" /**< a dbus Device interface */ #define SPA_NAME_API_BLUEZ5_DEVICE "api.bluez5.device" /**< a Device interface */ -#define SPA_NAME_API_BLUEZ5_A2DP_SINK "api.bluez5.a2dp.sink" /**< a playback Node interface for A2DP profiles */ -#define SPA_NAME_API_BLUEZ5_A2DP_SOURCE "api.bluez5.a2dp.source" /**< a capture Node interface for A2DP profiles */ +#define SPA_NAME_API_BLUEZ5_MEDIA_SINK "api.bluez5.media.sink" /**< a playback Node interface for A2DP/BAP profiles */ +#define SPA_NAME_API_BLUEZ5_MEDIA_SOURCE "api.bluez5.media.source" /**< a capture Node interface for A2DP/BAP profiles */ +#define SPA_NAME_API_BLUEZ5_A2DP_SINK "api.bluez5.a2dp.sink" /**< alias for media.sink */ +#define SPA_NAME_API_BLUEZ5_A2DP_SOURCE "api.bluez5.a2dp.source" /**< alias for media.source */ #define SPA_NAME_API_BLUEZ5_SCO_SINK "api.bluez5.sco.sink" /**< a playback Node interface for HSP/HFP profiles */ #define SPA_NAME_API_BLUEZ5_SCO_SOURCE "api.bluez5.sco.source" /**< a capture Node interface for HSP/HFP profiles */ /** keys for codec factory names */ -#define SPA_NAME_API_CODEC_BLUEZ5_A2DP "api.codec.bluez5.a2dp" /**< Bluez5 A2DP codec plugin */ +#define SPA_NAME_API_CODEC_BLUEZ5_MEDIA "api.codec.bluez5.media" /**< Bluez5 Media codec plugin */ /** keys for v4l2 factory names */ #define SPA_NAME_API_V4L2_ENUM_UDEV "api.v4l2.enum.udev" /**< a v4l2 udev Device interface */
View file
pipewire-0.3.58.tar.gz/spa/include/spa/utils/result.h -> pipewire-0.3.59.tar.gz/spa/include/spa/utils/result.h
Changed
@@ -55,7 +55,7 @@ #define spa_strerror(err) \ ({ \ - int _err = -err; \ + int _err = -(err); \ if (SPA_RESULT_IS_ASYNC(err)) \ _err = EINPROGRESS; \ strerror(_err); \
View file
pipewire-0.3.58.tar.gz/spa/include/spa/utils/ringbuffer.h -> pipewire-0.3.59.tar.gz/spa/include/spa/utils/ringbuffer.h
Changed
@@ -53,7 +53,7 @@ uint32_t writeindex; /*< the current write index */ }; -#define SPA_RINGBUFFER_INIT() (struct spa_ringbuffer) { 0, 0 } +#define SPA_RINGBUFFER_INIT() ((struct spa_ringbuffer) { 0, 0 }) /** * Initialize a spa_ringbuffer with \a size.
View file
pipewire-0.3.58.tar.gz/spa/meson.build -> pipewire-0.3.59.tar.gz/spa/meson.build
Changed
@@ -64,6 +64,8 @@ summary({'LC3plus': lc3plus_dep.found()}, bool_yn: true, section: 'Bluetooth audio codecs') opus_dep = dependency('opus', required : get_option('bluez5-codec-opus')) summary({'Opus': opus_dep.found()}, bool_yn: true, section: 'Bluetooth audio codecs') + lc3_dep = dependency('lc3', required : get_option('bluez5-codec-lc3')) + summary({'LC3': lc3_dep.found()}, bool_yn: true, section: 'Bluetooth audio codecs') endif avcodec_dep = dependency('libavcodec', required: get_option('ffmpeg')) jack_dep = dependency('jack', version : '>= 1.9.10', required: get_option('jack'))
View file
pipewire-0.3.58.tar.gz/spa/plugins/alsa/90-pipewire-alsa.rules -> pipewire-0.3.59.tar.gz/spa/plugins/alsa/90-pipewire-alsa.rules
Changed
@@ -181,6 +181,9 @@ # have any digital outputs. ATTRS{idVendor}=="0a12", ATTRS{idProduct}=="4007", ENV{ACP_PROFILE_SET}="analog-only.conf" +# Asus Xonar SE +ATTRS{idVendor}=="0b05", ATTRS{idProduct}=="189d", ENV{ACP_PROFILE_SET}="asus-xonar-se.conf" + GOTO="pipewire_end" LABEL="pipewire_check_pci"
View file
pipewire-0.3.58.tar.gz/spa/plugins/alsa/acp-tool.c -> pipewire-0.3.59.tar.gz/spa/plugins/alsa/acp-tool.c
Changed
@@ -584,6 +584,9 @@ return -errno; bufr = 0; + if (r == 0) + return -EPIPE; + if ((p = strchr(buf, '#'))) *p = '\0'; @@ -679,8 +682,12 @@ if (err < 0) return -errno; - if (pfds0.revents & POLLIN) - handle_input(data); + if (pfds0.revents & POLLIN) { + if ((err = handle_input(data)) < 0) { + if (err == -EPIPE) + break; + } + } if (count < 2) continue;
View file
pipewire-0.3.58.tar.gz/spa/plugins/alsa/acp/acp.h -> pipewire-0.3.59.tar.gz/spa/plugins/alsa/acp/acp.h
Changed
@@ -49,7 +49,7 @@ const char *key; const char *value; }; -#define ACP_DICT_ITEM_INIT(key,value) (struct acp_dict_item) { key, value } +#define ACP_DICT_ITEM_INIT(key,value) ((struct acp_dict_item) { (key), (value) }) struct acp_dict { uint32_t flags; @@ -115,8 +115,8 @@ uint32_t mapACP_MAX_CHANNELS; }; -#define ACP_DICT_INIT(items,n_items) (struct acp_dict) { 0, n_items, items } -#define ACP_DICT_INIT_ARRAY(items) (struct acp_dict) { 0, sizeof(items)/sizeof((items)0), items } +#define ACP_DICT_INIT(items,n_items) ((struct acp_dict) { 0, (n_items), (items) }) +#define ACP_DICT_INIT_ARRAY(items) ((struct acp_dict) { 0, sizeof(items)/sizeof((items)0), (items) }) #define acp_dict_for_each(item, dict) \ for ((item) = (dict)->items; \
View file
pipewire-0.3.58.tar.gz/spa/plugins/alsa/acp/array.h -> pipewire-0.3.59.tar.gz/spa/plugins/alsa/acp/array.h
Changed
@@ -39,7 +39,7 @@ size_t extend; /**< number of bytes to extend with */ } pa_array; -#define PW_ARRAY_INIT(extend) (struct pa_array) { NULL, 0, 0, extend } +#define PW_ARRAY_INIT(extend) ((struct pa_array) { NULL, 0, 0, (extend) }) #define pa_array_get_len_s(a,s) ((a)->size / (s)) #define pa_array_get_unchecked_s(a,idx,s,t) (t*)((uint8_t*)(a)->data + (int)((idx)*(s)))
View file
pipewire-0.3.58.tar.gz/spa/plugins/alsa/alsa-pcm.c -> pipewire-0.3.59.tar.gz/spa/plugins/alsa/alsa-pcm.c
Changed
@@ -511,6 +511,9 @@ } CHECK(snd_output_stdio_attach(&state->output, state->log_file, 0), "attach failed"); + state->rate_limit.interval = 2 * SPA_NSEC_PER_SEC; + state->rate_limit.burst = 1; + return 0; } @@ -1779,15 +1782,17 @@ static int get_avail(struct state *state, uint64_t current_time) { - int res; + int res, missed; snd_pcm_sframes_t avail; if (SPA_UNLIKELY((avail = snd_pcm_avail(state->hndl)) < 0)) { if ((res = alsa_recover(state, avail)) < 0) return res; if ((avail = snd_pcm_avail(state->hndl)) < 0) { - spa_log_warn(state->log, "%s: snd_pcm_avail after recover: %s", - state->props.device, snd_strerror(avail)); + if ((missed = ratelimit_test(&state->rate_limit, current_time)) >= 0) { + spa_log_warn(state->log, "%s: (%d missed) snd_pcm_avail after recover: %s", + state->props.device, missed, snd_strerror(avail)); + } avail = state->threshold * 2; } } else { @@ -1799,7 +1804,7 @@ #if 0 static int get_avail_htimestamp(struct state *state, uint64_t current_time) { - int res; + int res, missed; snd_pcm_uframes_t avail; snd_htimestamp_t tstamp; uint64_t then; @@ -1808,8 +1813,10 @@ if ((res = alsa_recover(state, avail)) < 0) return res; if ((res = snd_pcm_htimestamp(state->hndl, &avail, &tstamp)) < 0) { - spa_log_warn(state->log, "%s: snd_pcm_htimestamp error: %s", - state->props.device, snd_strerror(res)); + if ((missed = ratelimit_test(&state->rate_limit, current_time)) >= 0) { + spa_log_warn(state->log, "%s: (%d missed) snd_pcm_htimestamp error: %s", + state->props.device, missed, snd_strerror(res)); + } avail = state->threshold * 2; } } else { @@ -1880,15 +1887,14 @@ state, follower, state->last_threshold, state->threshold, diff, err); state->last_threshold = state->threshold; state->alsa_sync = true; + state->alsa_sync_warning = false; } if (err > state->max_error) { err = state->max_error; state->alsa_sync = true; - state->alsa_sync_warning = (diff == 0); } else if (err < -state->max_error) { err = -state->max_error; state->alsa_sync = true; - state->alsa_sync_warning = (diff == 0); } if (!follower || state->matching) @@ -1985,7 +1991,7 @@ const snd_pcm_channel_area_t *my_areas; snd_pcm_uframes_t written, frames, offset, off, to_write, total_written, max_write; snd_pcm_sframes_t commitres; - int res = 0; + int res = 0, missed; size_t frame_size = state->frame_size; check_position_config(state); @@ -2005,13 +2011,18 @@ return res; if (SPA_UNLIKELY(state->alsa_sync)) { - if (SPA_UNLIKELY(state->alsa_sync_warning)) { - spa_log_warn(state->log, "%s: follower delay:%ld target:%ld thr:%u, resync", - state->props.device, delay, target, state->threshold); - state->alsa_sync_warning = false; - } else - spa_log_info(state->log, "%s: follower delay:%ld target:%ld thr:%u, resync", - state->props.device, delay, target, state->threshold); + enum spa_log_level lev; + + if (SPA_UNLIKELY(state->alsa_sync_warning)) + lev = SPA_LOG_LEVEL_WARN; + else + lev = SPA_LOG_LEVEL_INFO; + + if ((missed = ratelimit_test(&state->rate_limit, current_time)) >= 0) { + spa_log_lev(state->log, lev, "%s: follower delay:%ld target:%ld thr:%u, " + "resync (%d missed)", state->props.device, delay, + target, state->threshold, missed); + } if (delay > target) snd_pcm_rewind(state->hndl, delay - target); @@ -2019,7 +2030,8 @@ spa_alsa_silence(state, target - delay); delay = target; state->alsa_sync = false; - } + } else + state->alsa_sync_warning = true; } total_written = 0; @@ -2212,7 +2224,7 @@ const snd_pcm_channel_area_t *my_areas; snd_pcm_uframes_t read, frames, offset; snd_pcm_sframes_t commitres; - int res = 0; + int res = 0, missed; check_position_config(state); @@ -2221,7 +2233,6 @@ if (state->following && state->alsa_started) { uint64_t current_time; snd_pcm_uframes_t avail, delay, target; - uint32_t threshold = state->threshold; current_time = state->position->clock.nsec; @@ -2234,13 +2245,18 @@ return res; if (state->alsa_sync) { - if (SPA_UNLIKELY(state->alsa_sync_warning)) { - spa_log_warn(state->log, "%s: follower delay:%lu target:%lu thr:%u, resync", - state->props.device, delay, target, threshold); - state->alsa_sync_warning = false; - } else - spa_log_info(state->log, "%s: follower delay:%lu target:%lu thr:%u, resync", - state->props.device, delay, target, threshold); + enum spa_log_level lev; + + if (SPA_UNLIKELY(state->alsa_sync_warning)) + lev = SPA_LOG_LEVEL_WARN; + else + lev = SPA_LOG_LEVEL_INFO; + + if ((missed = ratelimit_test(&state->rate_limit, current_time)) >= 0) { + spa_log_lev(state->log, lev, "%s: follower delay:%ld target:%ld thr:%u, " + "resync (%d missed)", state->props.device, delay, + target, state->threshold, missed); + } if (delay < target) max_read = target - delay; @@ -2248,7 +2264,8 @@ snd_pcm_forward(state->hndl, delay - target); delay = target; state->alsa_sync = false; - } + } else + state->alsa_sync_warning = true; if (avail < state->read_size) max_read = 0; @@ -2537,6 +2554,7 @@ reset_buffers(state); state->alsa_sync = true; + state->alsa_sync_warning = false; state->alsa_recovering = false; state->alsa_started = false;
View file
pipewire-0.3.58.tar.gz/spa/plugins/alsa/alsa-pcm.h -> pipewire-0.3.59.tar.gz/spa/plugins/alsa/alsa-pcm.h
Changed
@@ -87,7 +87,6 @@ uint32_t posSPA_AUDIO_MAX_CHANNELS; }; - struct card { struct spa_list link; int ref; @@ -98,6 +97,13 @@ uint32_t rate; }; +struct ratelimit { + uint64_t interval; + uint64_t begin; + unsigned burst; + unsigned n_printed, n_missed; +}; + struct state { struct spa_handle handle; struct spa_node node; @@ -107,6 +113,7 @@ struct spa_loop *data_loop; FILE *log_file; + struct ratelimit rate_limit; uint32_t card_index; struct card *card; @@ -342,6 +349,23 @@ return i; } +static inline int ratelimit_test(struct ratelimit *r, uint64_t now) +{ + unsigned missed = 0; + if (r->begin + r->interval < now) { + missed = r->n_missed; + r->begin = now; + r->n_printed = 0; + r->n_missed = 0; + } else if (r->n_printed >= r->burst) { + r->n_missed++; + return -1; + } + r->n_printed++; + return missed; +} + + #ifdef __cplusplus } /* extern "C" */ #endif
View file
pipewire-0.3.58.tar.gz/spa/plugins/alsa/alsa-udev.c -> pipewire-0.3.59.tar.gz/spa/plugins/alsa/alsa-udev.c
Changed
@@ -256,7 +256,7 @@ /* Check device class */ spa_scnprintf(path, sizeof(path), "/sys/class/sound/%s/pcm_class", devname); - f = fopen(path, "r"); + f = fopen(path, "re"); if (f == NULL) return -errno; sz = fread(buf, 1, sizeof(buf) - 1, f); @@ -361,7 +361,7 @@ spa_scnprintf(path, sizeof(path), "/proc/asound/card%u/%s/%s/status", (unsigned int)device->id, entry->d_name, entry_pcm->d_name); - f = fopen(path, "r"); + f = fopen(path, "re"); if (f == NULL) goto done; sz = fread(buf, 1, 6, f);
View file
pipewire-0.3.59.tar.gz/spa/plugins/alsa/mixer/profile-sets/asus-xonar-se.conf
Added
@@ -0,0 +1,79 @@ +# This file is part of PulseAudio. +# +# PulseAudio is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation; either version 2.1 of the +# License, or (at your option) any later version. +# +# PulseAudio is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with PulseAudio; if not, see <http://www.gnu.org/licenses/>. + +; ASUS Xonar SE card. +; This card has two devices for each rear and front panel jacks. +; +; See default.conf for an explanation on the directives used here. + +General +auto-profiles = yes + +Mapping analog-stereo-front +description = Analog Stereo Front +device-strings = hw:%f,1 +channel-map = left,right +paths-output = analog-output analog-output-headphones +paths-input = analog-input-mic analog-input-headphone-mic analog-input-headset-mic +priority = 15 + +Mapping analog-stereo-rear +description = Analog Stereo Rear +device-strings = hw:%f,0 +channel-map = left,right +paths-output = analog-output analog-output-speaker +paths-input = analog-input analog-input-mic analog-input-linein +priority = 14 + +Mapping analog-surround-21 +device-strings = surround21:%f +channel-map = front-left,front-right,lfe +paths-output = analog-output-speaker +priority = 13 +direction = output + +Mapping analog-surround-40 +device-strings = surround40:%f +channel-map = front-left,front-right,rear-left,rear-right +paths-output = analog-output-speaker +priority = 12 +direction = output + +Mapping analog-surround-41 +device-strings = surround41:%f +channel-map = front-left,front-right,rear-left,rear-right,lfe +paths-output = analog-output-speaker +priority = 13 +direction = output + +Mapping analog-surround-50 +device-strings = surround50:%f +channel-map = front-left,front-right,rear-left,rear-right,front-center +paths-output = analog-output-speaker +priority = 12 +direction = output + +Mapping analog-surround-51 +device-strings = surround51:%f +channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe +paths-output = analog-output-speaker +priority = 13 +direction = output + +Mapping iec958-stereo +device-strings = iec958:%f +channel-map = left,right +paths-output = iec958-stereo-output +priority = 5 \ No newline at end of file
View file
pipewire-0.3.58.tar.gz/spa/plugins/audioconvert/audioadapter.c -> pipewire-0.3.59.tar.gz/spa/plugins/audioconvert/audioadapter.c
Changed
@@ -336,7 +336,7 @@ struct spa_data *datas; uint32_t follower_flags, conv_flags; - spa_log_debug(this->log, "%p: %d", this, this->n_buffers); + spa_log_debug(this->log, "%p: n_buffers:%d", this, this->n_buffers); if (this->target == this->follower) return 0; @@ -738,6 +738,8 @@ struct spa_pod_builder b = { 0 }; int res; + spa_log_debug(this->log, "%p: have_format:%d", this, this->have_format); + if (this->have_format) return 0; @@ -746,7 +748,6 @@ spa_pod_builder_init(&b, buffer, sizeof(buffer)); - spa_log_debug(this->log, "%p: negiotiate", this); spa_node_send_command(this->follower, &SPA_NODE_COMMAND_INIT(SPA_NODE_COMMAND_ParamBegin)); @@ -813,6 +814,8 @@ switch (SPA_NODE_COMMAND_ID(command)) { case SPA_NODE_COMMAND_Start: + if (this->started) + return 0; if ((res = negotiate_format(this)) < 0) return res; if ((res = negotiate_buffers(this)) < 0) @@ -821,11 +824,12 @@ case SPA_NODE_COMMAND_Suspend: configure_format(this, 0, NULL); SPA_FALLTHROUGH - case SPA_NODE_COMMAND_Flush: - this->io_buffers.status = SPA_STATUS_OK; - SPA_FALLTHROUGH case SPA_NODE_COMMAND_Pause: this->started = false; + spa_log_debug(this->log, "%p: stopped", this); + break; + case SPA_NODE_COMMAND_Flush: + this->io_buffers.status = SPA_STATUS_OK; break; default: break; @@ -849,6 +853,7 @@ switch (SPA_NODE_COMMAND_ID(command)) { case SPA_NODE_COMMAND_Start: this->started = true; + spa_log_debug(this->log, "%p: started", this); break; } return res; @@ -1396,6 +1401,11 @@ struct impl *this = object; int status = 0, fstatus, retry = 8; + if (!this->started) { + spa_log_warn(this->log, "%p: scheduling stopped node", this); + return -EIO; + } + spa_log_trace_fp(this->log, "%p: process convert:%p driver:%d", this, this->convert, this->driver);
View file
pipewire-0.3.58.tar.gz/spa/plugins/audioconvert/audioconvert.c -> pipewire-0.3.59.tar.gz/spa/plugins/audioconvert/audioconvert.c
Changed
@@ -90,8 +90,8 @@ struct volumes monitor; unsigned int have_soft_volume:1; unsigned int mix_disabled:1; - unsigned int resample_quality; unsigned int resample_disabled:1; + unsigned int resample_quality; double rate; }; @@ -105,10 +105,11 @@ init_volumes(&props->channel); init_volumes(&props->soft); init_volumes(&props->monitor); + props->have_soft_volume = false; props->mix_disabled = false; - props->rate = 1.0; - props->resample_quality = RESAMPLE_DEFAULT_QUALITY; props->resample_disabled = false; + props->resample_quality = RESAMPLE_DEFAULT_QUALITY; + props->rate = 1.0; } struct buffer { @@ -215,7 +216,8 @@ uint32_t in_offset; uint32_t out_offset; unsigned int started:1; - unsigned int peaks:1; + unsigned int setup:1; + unsigned int resample_peaks:1; unsigned int is_passthrough:1; unsigned int drained:1; @@ -834,6 +836,8 @@ snprintf(value, sizeof(value), "%s", SPA_POD_VALUE(struct spa_pod_bool, pod) ? "true" : "false"); + } else if (spa_pod_is_none(pod)) { + spa_zero(value); } else continue; @@ -1353,7 +1357,7 @@ this->resample.quality = this->props.resample_quality; this->resample.cpu_flags = this->cpu_flags; - if (this->peaks) + if (this->resample_peaks) res = resample_peaks_init(&this->resample); else res = resample_native_init(&this->resample); @@ -1463,6 +1467,12 @@ in = &this->dirSPA_DIRECTION_INPUT; out = &this->dirSPA_DIRECTION_OUTPUT; + spa_log_debug(this->log, "%p: setup:%d in_format:%d out_format:%d", this, + this->setup, in->have_format, out->have_format); + + if (this->setup) + return 0; + if (!in->have_format || !out->have_format) return -EINVAL; @@ -1506,6 +1516,7 @@ this->tmp_datas1i = SPA_PTROFF(this->tmp1, this->empty_size * i, void); this->tmp_datas1i = SPA_PTR_ALIGN(this->tmp_datas1i, MAX_ALIGN, void); } + this->setup = true; emit_node_info(this, false); @@ -1537,13 +1548,14 @@ this->started = true; break; case SPA_NODE_COMMAND_Suspend: - SPA_FALLTHROUGH; - case SPA_NODE_COMMAND_Flush: - reset_node(this); + this->setup = false; SPA_FALLTHROUGH; case SPA_NODE_COMMAND_Pause: this->started = false; break; + case SPA_NODE_COMMAND_Flush: + reset_node(this); + break; default: return -ENOTSUP; } @@ -2467,6 +2479,7 @@ volume, mon_max); bd->chunk->size = mon_max * port->stride; + bd->chunk->stride = port->stride; spa_log_trace_fp(this->log, "%p: monitor %d %d", this, remap, mon_max); @@ -2667,9 +2680,10 @@ for (j = 0; j < port->blocks; j++) { bd = &buf->buf->datasj; bd->chunk->size = this->out_offset * port->stride; + bd->chunk->stride = port->stride; SPA_FLAG_UPDATE(bd->chunk->flags, SPA_CHUNK_FLAG_EMPTY, in_empty); - spa_log_trace_fp(this->log, "out: %d %d %d", this->out_offset, - port->stride, bd->chunk->size); + spa_log_trace_fp(this->log, "out: offs:%d stride:%d size:%d", + this->out_offset, port->stride, bd->chunk->size); } io->status = SPA_STATUS_HAVE_DATA; io->buffer_id = buf->id; @@ -2678,7 +2692,7 @@ this->drained = draining; this->out_offset = 0; } - else if (n_samples == 0 && this->peaks) { + else if (n_samples == 0 && this->resample_peaks) { for (i = 0; i < dir->n_ports; i++) { port = GET_OUT_PORT(this, i); if (port->is_monitor || port->is_control) @@ -2841,15 +2855,20 @@ if (spa_streq(k, "clock.quantum-limit")) spa_atou32(s, &this->quantum_limit, 0); else if (spa_streq(k, "resample.peaks")) - this->peaks = spa_atob(s); + this->resample_peaks = spa_atob(s); + else if (spa_streq(k, "resample.prefill")) + SPA_FLAG_UPDATE(this->resample.options, + RESAMPLE_OPTION_PREFILL, spa_atob(s)); else if (spa_streq(k, "factory.mode")) { if (spa_streq(s, "merge")) this->direction = SPA_DIRECTION_OUTPUT; else this->direction = SPA_DIRECTION_INPUT; } - else if (spa_streq(k, SPA_KEY_AUDIO_POSITION)) - this->props.n_channels = parse_position(this->props.channel_map, s, strlen(s)); + else if (spa_streq(k, SPA_KEY_AUDIO_POSITION)) { + if (s != NULL) + this->props.n_channels = parse_position(this->props.channel_map, s, strlen(s)); + } else audioconvert_set_param(this, k, s); }
View file
pipewire-0.3.58.tar.gz/spa/plugins/audioconvert/channelmix-ops-c.c -> pipewire-0.3.59.tar.gz/spa/plugins/audioconvert/channelmix-ops-c.c
Changed
@@ -46,6 +46,16 @@ dn = sn * vol; } } +static inline void conv_c(float *d, const float **s, float *c, uint32_t n_c, uint32_t n_samples) +{ + uint32_t n, j; + for (n = 0; n < n_samples; n++) { + float sum = 0.0f; + for (j = 0; j < n_c; j++) + sum += sjn * cj; + dn = sum; + } +} static inline void avg_c(float *d, const float *s0, const float *s1, uint32_t n_samples) { @@ -78,7 +88,7 @@ channelmix_f32_n_m_c(struct channelmix *mix, void * SPA_RESTRICT dst, const void * SPA_RESTRICT src, uint32_t n_samples) { - uint32_t i, j, n, n_dst = mix->dst_chan, n_src = mix->src_chan; + uint32_t i, j, n_dst = mix->dst_chan, n_src = mix->src_chan; float **d = (float **) dst; const float **s = (const float **) src; @@ -94,16 +104,27 @@ clear_c(di, n_samples); } else { - for (n = 0; n < n_samples; n++) { - for (i = 0; i < n_dst; i++) { - float sum = 0.0f; - for (j = 0; j < n_src; j++) - sum += sjn * mix->matrixij; - din = sum; + for (i = 0; i < n_dst; i++) { + float *di = di; + float mjn_src; + const float *sjn_src; + uint32_t n_j = 0; + + for (j = 0; j < n_src; j++) { + if (mix->matrixij == 0.0f) + continue; + mjn_j = mix->matrixij; + sjn_j++ = sj; + } + if (n_j == 0) { + clear_c(di, n_samples); + } else if (n_j == 1) { + lr4_process(&mix->lr4i, di, sj0, mj0, n_samples); + } else { + conv_c(di, sj, mj, n_j, n_samples); + lr4_process(&mix->lr4i, di, di, 1.0f, n_samples); } } - for (i = 0; i < n_dst; i++) - lr4_process(&mix->lr4i, di, di, 1.0f, n_samples); } }
View file
pipewire-0.3.58.tar.gz/spa/plugins/audioconvert/channelmix-ops-sse.c -> pipewire-0.3.59.tar.gz/spa/plugins/audioconvert/channelmix-ops-sse.c
Changed
@@ -68,6 +68,39 @@ } } +static inline void conv_sse(float *d, const float **s, float *c, uint32_t n_c, uint32_t n_samples) +{ + __m128 min_c, sum2; + uint32_t n, j, unrolled; + bool aligned = true; + + for (j = 0; j < n_c; j++) { + mij = _mm_set1_ps(cj); + aligned &= SPA_IS_ALIGNED(sj, 16); + } + + if (aligned && SPA_IS_ALIGNED(d, 16)) + unrolled = n_samples & ~7; + else + unrolled = 0; + + for (n = 0; n < unrolled; n += 8) { + sum0 = sum1 = _mm_setzero_ps(); + for (j = 0; j < n_c; j++) { + sum0 = _mm_add_ps(sum0, _mm_mul_ps(_mm_load_ps(&sjn + 0), mij)); + sum1 = _mm_add_ps(sum1, _mm_mul_ps(_mm_load_ps(&sjn + 4), mij)); + } + _mm_store_ps(&dn + 0, sum0); + _mm_store_ps(&dn + 4, sum1); + } + for (; n < n_samples; n++) { + sum0 = _mm_setzero_ps(); + for (j = 0; j < n_c; j++) + sum0 = _mm_add_ss(sum0, _mm_mul_ss(_mm_load_ss(&sjn), mij)); + _mm_store_ss(&dn, sum0); + } +} + void channelmix_copy_sse(struct channelmix *mix, void * SPA_RESTRICT dst, const void * SPA_RESTRICT src, uint32_t n_samples) { @@ -78,6 +111,40 @@ vol_sse(di, si, mix->matrixii, n_samples); } +void +channelmix_f32_n_m_sse(struct channelmix *mix, void * SPA_RESTRICT dst, + const void * SPA_RESTRICT src, uint32_t n_samples) +{ + float **d = (float **) dst; + const float **s = (const float **) src; + uint32_t i, j, n_dst = mix->dst_chan, n_src = mix->src_chan; + + for (i = 0; i < n_dst; i++) { + float *di = di; + float mjn_src; + const float *sjn_src; + uint32_t n_j = 0; + + for (j = 0; j < n_src; j++) { + if (mix->matrixij == 0.0f) + continue; + mjn_j = mix->matrixij; + sjn_j++ = sj; + } + if (n_j == 0) { + clear_sse(di, n_samples); + } else if (n_j == 1) { + if (mix->lr4i.active) + lr4_process(&mix->lr4i, di, sj0, mj0, n_samples); + else + vol_sse(di, sj0, mj0, n_samples); + } else { + conv_sse(di, sj, mj, n_j, n_samples); + lr4_process(&mix->lr4i, di, di, 1.0f, n_samples); + } + } +} + /* FL+FR+FC+LFE -> FL+FR */ void channelmix_f32_3p1_2_sse(struct channelmix *mix, void * SPA_RESTRICT dst,
View file
pipewire-0.3.58.tar.gz/spa/plugins/audioconvert/channelmix-ops.c -> pipewire-0.3.59.tar.gz/spa/plugins/audioconvert/channelmix-ops.c
Changed
@@ -94,6 +94,9 @@ MAKE(8, MASK_7_1, 4, MASK_QUAD, channelmix_f32_7p1_4_c), MAKE(8, MASK_7_1, 4, MASK_3_1, channelmix_f32_7p1_3p1_c), +#if defined (HAVE_SSE) + MAKE(ANY, 0, ANY, 0, channelmix_f32_n_m_sse), +#endif MAKE(ANY, 0, ANY, 0, channelmix_f32_n_m_c), }; #undef MAKE @@ -142,6 +145,8 @@ float matrixSPA_AUDIO_MAX_CHANNELSSPA_AUDIO_MAX_CHANNELS = {{ 0.0f }}; uint64_t src_mask = mix->src_mask; uint64_t dst_mask = mix->dst_mask; + uint32_t src_chan = mix->src_chan; + uint32_t dst_chan = mix->dst_chan; uint64_t unassigned, keep; uint32_t i, j, ic, jc, matrix_encoding = MATRIX_NORMAL; float clev = SQRT1_2; @@ -157,7 +162,7 @@ /* move the MONO mask to FRONT so that the lower bits can be shifted * away. */ if ((src_mask & (1Ull << SPA_AUDIO_CHANNEL_MONO)) != 0) { - if (mix->src_chan == 1) + if (src_chan == 1) src_mask = 0; else src_mask |= (1ULL << SPA_AUDIO_CHANNEL_FC); @@ -171,19 +176,19 @@ /* unknown channels or just 1 channel */ if (src_mask == 0 || dst_mask == 0) { - if (mix->src_chan == 1) { + if (src_chan == 1) { /* one FC/MONO src goes everywhere */ - spa_log_debug(mix->log, "distribute FC/MONO"); + spa_log_debug(mix->log, "distribute FC/MONO (%f)", 1.0f); for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++) matrixi0= 1.0f; - } else if (mix->dst_chan == 1) { + } else if (dst_chan == 1) { /* one FC/MONO dst get average of everything */ - spa_log_debug(mix->log, "average FC/MONO"); + spa_log_debug(mix->log, "average FC/MONO (%f)", 1.0f / src_chan); for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++) - matrix0i= 1.0f / mix->src_chan; + matrix0i= 1.0f / src_chan; } else { /* just pair channels */ - spa_log_debug(mix->log, "pairing channels"); + spa_log_debug(mix->log, "pairing channels (%f)", 1.0f); for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++) matrixii= 1.0f; } @@ -197,7 +202,7 @@ spa_log_debug(mix->log, "matching channels"); for (i = 0; i < SPA_AUDIO_MAX_CHANNELS; i++) { if ((src_mask & dst_mask & (1ULL << i))) { - spa_log_debug(mix->log, "matched %u", i); + spa_log_debug(mix->log, "matched channel %u (%f)", i, 1.0f); matrixii= 1.0f; } } @@ -222,11 +227,12 @@ if (unassigned & FRONT){ if ((dst_mask & STEREO) == STEREO){ - spa_log_debug(mix->log, "assign FC to STEREO"); if(src_mask & STEREO) { + spa_log_debug(mix->log, "assign FC to STEREO (%f)", clev); _MATRIX(FL,FC) += clev; _MATRIX(FR,FC) += clev; } else { + spa_log_debug(mix->log, "assign FC to STEREO (%f)", SQRT1_2); _MATRIX(FL,FC) += SQRT1_2; _MATRIX(FR,FC) += SQRT1_2; } @@ -237,11 +243,13 @@ if (unassigned & STEREO){ if (dst_mask & FRONT) { - spa_log_debug(mix->log, "assign STEREO to FC"); + spa_log_debug(mix->log, "assign STEREO to FC (%f)", SQRT1_2); _MATRIX(FC,FL) += SQRT1_2; _MATRIX(FC,FR) += SQRT1_2; - if (src_mask & FRONT) + if (src_mask & FRONT) { + spa_log_debug(mix->log, "assign FC to FC (%f)", clev * SQRT2); _MATRIX(FC,FC) = clev * SQRT2; + } keep &= ~FRONT; } else { spa_log_warn(mix->log, "can't assign STEREO"); @@ -250,11 +258,11 @@ if (unassigned & _MASK(RC)) { if (dst_mask & REAR){ - spa_log_debug(mix->log, "assign RC to RL+RR"); + spa_log_debug(mix->log, "assign RC to RL+RR (%f)", SQRT1_2); _MATRIX(RL,RC) += SQRT1_2; _MATRIX(RR,RC) += SQRT1_2; } else if (dst_mask & SIDE) { - spa_log_debug(mix->log, "assign RC to SL+SR"); + spa_log_debug(mix->log, "assign RC to SL+SR (%f)", SQRT1_2); _MATRIX(SL,RC) += SQRT1_2; _MATRIX(SR,RC) += SQRT1_2; } else if(dst_mask & STEREO) { @@ -273,7 +281,7 @@ _MATRIX(FR,RC) += slev * SQRT1_2; } } else if (dst_mask & FRONT) { - spa_log_debug(mix->log, "assign RC to FC"); + spa_log_debug(mix->log, "assign RC to FC (%f)", slev * SQRT1_2); _MATRIX(FC,RC) += slev * SQRT1_2; } else { spa_log_warn(mix->log, "can't assign RC"); @@ -295,7 +303,7 @@ _MATRIX(SR,RR) += 1.0f; } } else if (dst_mask & STEREO) { - spa_log_debug(mix->log, "assign RL+RR to FL+FR %f", slev); + spa_log_debug(mix->log, "assign RL+RR to FL+FR (%f)", slev); if (matrix_encoding == MATRIX_DOLBY) { _MATRIX(FL,RL) -= slev * SQRT1_2; _MATRIX(FL,RR) -= slev * SQRT1_2; @@ -311,7 +319,8 @@ _MATRIX(FR,RR) += slev; } } else if (dst_mask & FRONT) { - spa_log_debug(mix->log, "assign RL+RR to FC"); + spa_log_debug(mix->log, "assign RL+RR to FC (%f)", + slev * SQRT1_2); _MATRIX(FC,RL)+= slev * SQRT1_2; _MATRIX(FC,RR)+= slev * SQRT1_2; } else { @@ -321,36 +330,41 @@ if (unassigned & SIDE) { if (dst_mask & REAR) { - spa_log_debug(mix->log, "assign SL+SR to RL+RR"); if (src_mask & _MASK(RL)) { + spa_log_debug(mix->log, "assign SL+SR to RL+RR (%f)", SQRT1_2); _MATRIX(RL,SL) += SQRT1_2; _MATRIX(RR,SR) += SQRT1_2; } else { + spa_log_debug(mix->log, "assign SL+SR to RL+RR (%f)", 1.0f); _MATRIX(RL,SL) += 1.0f; _MATRIX(RR,SR) += 1.0f; } } else if (dst_mask & _MASK(RC)) { - spa_log_debug(mix->log, "assign SL+SR to RC"); + spa_log_debug(mix->log, "assign SL+SR to RC (%f)", SQRT1_2); _MATRIX(RC,SL)+= SQRT1_2; _MATRIX(RC,SR)+= SQRT1_2; } else if (dst_mask & STEREO) { - spa_log_debug(mix->log, "assign SL+SR to FL+FR"); if (matrix_encoding == MATRIX_DOLBY) { + spa_log_debug(mix->log, "assign SL+SR to FL+FR (%f)", + slev * SQRT1_2); _MATRIX(FL,SL) -= slev * SQRT1_2; _MATRIX(FL,SR) -= slev * SQRT1_2; _MATRIX(FR,SL) += slev * SQRT1_2; _MATRIX(FR,SR) += slev * SQRT1_2; } else if (matrix_encoding == MATRIX_DPLII) { + spa_log_debug(mix->log, "assign SL+SR to FL+FR (%f / %f)", + slev * SQRT3_2, slev * SQRT1_2); _MATRIX(FL,SL) -= slev * SQRT3_2; _MATRIX(FL,SR) -= slev * SQRT1_2; _MATRIX(FR,SL) += slev * SQRT1_2; _MATRIX(FR,SR) += slev * SQRT3_2; } else { + spa_log_debug(mix->log, "assign SL+SR to FL+FR (%f)", slev); _MATRIX(FL,SL) += slev; _MATRIX(FR,SR) += slev; } } else if (dst_mask & FRONT) { - spa_log_debug(mix->log, "assign SL+SR to FC"); + spa_log_debug(mix->log, "assign SL+SR to FC (%f)", slev * SQRT1_2); _MATRIX(FC,SL) += slev * SQRT1_2; _MATRIX(FC,SR) += slev * SQRT1_2; } else { @@ -360,11 +374,11 @@ if (unassigned & _MASK(FLC)) { if (dst_mask & STEREO) { - spa_log_debug(mix->log, "assign FLC+FRC to FL+FR"); + spa_log_debug(mix->log, "assign FLC+FRC to FL+FR (%f)", 1.0f); _MATRIX(FL,FLC)+= 1.0f; _MATRIX(FR,FRC)+= 1.0f; } else if(dst_mask & FRONT) { - spa_log_debug(mix->log, "assign FLC+FRC to FC"); + spa_log_debug(mix->log, "assign FLC+FRC to FC (%f)", SQRT1_2); _MATRIX(FC,FLC)+= SQRT1_2; _MATRIX(FC,FRC)+= SQRT1_2; } else { @@ -374,10 +388,11 @@ if (unassigned & _MASK(LFE) && SPA_FLAG_IS_SET(mix->options, CHANNELMIX_OPTION_MIX_LFE)) { if (dst_mask & FRONT) { - spa_log_debug(mix->log, "assign LFE to FC"); + spa_log_debug(mix->log, "assign LFE to FC (%f)", llev); _MATRIX(FC,LFE) += llev; } else if (dst_mask & STEREO) { - spa_log_debug(mix->log, "assign LFE to FL+FR"); + spa_log_debug(mix->log, "assign LFE to FL+FR (%f)", + llev * SQRT1_2); _MATRIX(FL,LFE) += llev * SQRT1_2; _MATRIX(FR,LFE) += llev * SQRT1_2; } else { @@ -392,7 +407,7 @@ if (unassigned & STEREO) { if ((src_mask & FRONT) == FRONT) { - spa_log_debug(mix->log, "produce STEREO from FC"); + spa_log_debug(mix->log, "produce STEREO from FC (%f)", clev); _MATRIX(FL,FC) += clev; _MATRIX(FR,FC) += clev; } else { @@ -401,7 +416,7 @@ } if (unassigned & FRONT) { if ((src_mask & STEREO) == STEREO) { - spa_log_debug(mix->log, "produce FC from STEREO"); + spa_log_debug(mix->log, "produce FC from STEREO (%f)", clev); _MATRIX(FC,FL) += clev; _MATRIX(FC,FR) += clev; filter_fc = true; @@ -411,12 +426,12 @@ } if (unassigned & _MASK(LFE)) { if ((src_mask & STEREO) == STEREO) { - spa_log_debug(mix->log, "produce LFE from STEREO"); + spa_log_debug(mix->log, "produce LFE from STEREO (%f)", llev); _MATRIX(LFE,FL) += llev; _MATRIX(LFE,FR) += llev; filter_lfe = true; } else if ((src_mask & FRONT) == FRONT) { - spa_log_debug(mix->log, "produce LFE from FC"); + spa_log_debug(mix->log, "produce LFE from FC (%f)", llev); _MATRIX(LFE,FC) += llev; filter_lfe = true; } else { @@ -425,16 +440,16 @@ } if (unassigned & SIDE) { if ((src_mask & REAR) == REAR) { - spa_log_debug(mix->log, "produce SIDE from REAR"); + spa_log_debug(mix->log, "produce SIDE from REAR (%f)", 1.0f); _MATRIX(SL,RL) += 1.0f; _MATRIX(SR,RR) += 1.0f; } else if ((src_mask & STEREO) == STEREO) { - spa_log_debug(mix->log, "produce SIDE from STEREO"); + spa_log_debug(mix->log, "produce SIDE from STEREO (%f)", slev); _MATRIX(SL,FL) += slev; _MATRIX(SR,FR) += slev; } else if ((src_mask & FRONT) == FRONT && mix->upmix == CHANNELMIX_UPMIX_SIMPLE) { - spa_log_debug(mix->log, "produce SIDE from FC"); + spa_log_debug(mix->log, "produce SIDE from FC (%f)", clev); _MATRIX(SL,FC) += clev; _MATRIX(SR,FC) += clev; } else { @@ -443,16 +458,16 @@ } if (unassigned & REAR) { if ((src_mask & SIDE) == SIDE) { - spa_log_debug(mix->log, "produce REAR from SIDE"); + spa_log_debug(mix->log, "produce REAR from SIDE (%f)", 1.0f); _MATRIX(RL,SL) += 1.0f; _MATRIX(RR,SR) += 1.0f; } else if ((src_mask & STEREO) == STEREO) { - spa_log_debug(mix->log, "produce REAR from STEREO"); + spa_log_debug(mix->log, "produce REAR from STEREO (%f)", slev); _MATRIX(RL,FL) += slev; _MATRIX(RR,FR) += slev; } else if ((src_mask & FRONT) == FRONT && mix->upmix == CHANNELMIX_UPMIX_SIMPLE) { - spa_log_debug(mix->log, "produce REAR from FC"); + spa_log_debug(mix->log, "produce REAR from FC (%f)", clev); _MATRIX(RL,FC) += clev; _MATRIX(RR,FC) += clev; } else { @@ -468,6 +483,8 @@ for (jc = 0, j = 0; j < SPA_AUDIO_MAX_CHANNELS; j++) { if ((src_mask & (1UL << j)) == 0) continue; + if (ic >= dst_chan || jc >= src_chan) + continue; mix->matrix_origicjc++ = matrixij; sum += fabs(matrixij); } @@ -486,11 +503,10 @@ if (SPA_FLAG_IS_SET(mix->options, CHANNELMIX_OPTION_NORMALIZE) && maxsum > 1.0f) { spa_log_debug(mix->log, "normalize %f", maxsum); - for (i = 0; i < ic; i++) - for (j = 0; j < jc; j++) + for (i = 0; i < dst_chan; i++) + for (j = 0; j < src_chan; j++) mix->matrix_origij /= maxsum; } - return 0; } @@ -524,6 +540,12 @@ mix->matrixij = mix->matrix_origij * volumesi; } } + } else if (n_channel_volumes == 0) { + for (i = 0; i < dst_chan; i++) { + for (j = 0; j < src_chan; j++) { + mix->matrixij = mix->matrix_origij * vol; + } + } } SPA_FLAG_SET(mix->flags, CHANNELMIX_FLAG_ZERO);
View file
pipewire-0.3.58.tar.gz/spa/plugins/audioconvert/channelmix-ops.h -> pipewire-0.3.59.tar.gz/spa/plugins/audioconvert/channelmix-ops.h
Changed
@@ -147,6 +147,7 @@ #if defined (HAVE_SSE) DEFINE_FUNCTION(copy, sse); +DEFINE_FUNCTION(f32_n_m, sse); DEFINE_FUNCTION(f32_3p1_2, sse); DEFINE_FUNCTION(f32_5p1_2, sse); DEFINE_FUNCTION(f32_5p1_3p1, sse);
View file
pipewire-0.3.58.tar.gz/spa/plugins/audioconvert/fmt-ops-c.c -> pipewire-0.3.59.tar.gz/spa/plugins/audioconvert/fmt-ops-c.c
Changed
@@ -230,38 +230,57 @@ return (int32_t)(*state); } -static inline void update_noise_c(struct convert *conv, uint32_t n_samples) +void conv_noise_none_c(struct convert *conv, float *noise, uint32_t n_samples) +{ + memset(noise, 0, n_samples * sizeof(float)); +} + +void conv_noise_rect_c(struct convert *conv, float *noise, uint32_t n_samples) +{ + uint32_t n; + uint32_t *state = &conv->random0; + const float scale = conv->scale; + + for (n = 0; n < n_samples; n++) + noisen = lcnoise(state) * scale; +} + +void conv_noise_tri_c(struct convert *conv, float *noise, uint32_t n_samples) +{ + uint32_t n; + const float scale = conv->scale; + uint32_t *state = &conv->random0; + + for (n = 0; n < n_samples; n++) + noisen = (lcnoise(state) - lcnoise(state)) * scale; +} + +void conv_noise_tri_hf_c(struct convert *conv, float *noise, uint32_t n_samples) { uint32_t n; - float *noise = conv->noise, scale = conv->scale; + const float scale = conv->scale; uint32_t *state = &conv->random0; 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; + old = *prev; + for (n = 0; n < n_samples; n++) { + new = lcnoise(state); + noisen = (new - old) * scale; + old = new; } + *prev = old; +} + +void conv_noise_pattern_c(struct convert *conv, float *noise, uint32_t n_samples) +{ + uint32_t n; + const float scale = conv->scale; + int32_t *prev = &conv->prev0, old; + + old = *prev; + for (n = 0; n < n_samples; n++) + noisen = scale * (1-((old++>>10)&1)); + *prev = old; } #define MAKE_D_noise(dname,dtype,func) \ @@ -271,7 +290,7 @@ { \ 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)); \ + convert_update_noise(conv, noise, SPA_MIN(n_samples, noise_size)); \ for (i = 0; i < n_channels; i++) { \ const float *s = srci; \ dtype *d = dsti; \ @@ -292,7 +311,7 @@ dtype *d = dst0; \ 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)); \ + convert_update_noise(conv, noise, SPA_MIN(n_samples, noise_size)); \ for (j = 0; j < n_samples;) { \ chunk = SPA_MIN(n_samples - j, noise_size); \ for (k = 0; k < chunk; k++, j++) { \ @@ -342,9 +361,10 @@ uint32_t n_samples) \ { \ uint32_t i, j, k, chunk, n_channels = conv->n_channels, noise_size = conv->noise_size; \ - const float *noise = conv->noise, *ns = conv->ns; \ + float *noise = conv->noise; \ + const float *ns = conv->ns; \ uint32_t n, n_ns = conv->n_ns; \ - update_noise_c(conv, SPA_MIN(n_samples, noise_size)); \ + convert_update_noise(conv, noise, SPA_MIN(n_samples, noise_size)); \ for (i = 0; i < n_channels; i++) { \ const float *s = srci; \ dtype *d = dsti; \ @@ -366,9 +386,10 @@ { \ dtype *d0 = dst0; \ uint32_t i, j, k, chunk, n_channels = conv->n_channels, noise_size = conv->noise_size; \ - const float *noise = conv->noise, *ns = conv->ns; \ + float *noise = conv->noise; \ + const float *ns = conv->ns; \ uint32_t n, n_ns = conv->n_ns; \ - update_noise_c(conv, SPA_MIN(n_samples, noise_size)); \ + convert_update_noise(conv, noise, SPA_MIN(n_samples, noise_size)); \ for (i = 0; i < n_channels; i++) { \ const float *s = srci; \ dtype *d = &d0i; \
View file
pipewire-0.3.58.tar.gz/spa/plugins/audioconvert/fmt-ops-sse2.c -> pipewire-0.3.59.tar.gz/spa/plugins/audioconvert/fmt-ops-sse2.c
Changed
@@ -576,61 +576,64 @@ i; \ }) +void conv_noise_rect_sse2(struct convert *conv, float *noise, uint32_t n_samples) +{ + uint32_t n; + const uint32_t *r = conv->random; + __m128 scale = _mm_set1_ps(conv->scale); + __m128i in1; + __m128 out1; + + for (n = 0; n < n_samples; n += 4) { + in0 = _MM_XORSHIFT_EPI32(r); + out0 = _mm_cvtepi32_ps(in0); + out0 = _mm_mul_ps(out0, scale); + _mm_store_ps(&noisen, out0); + } +} -static inline void update_noise_sse2(struct convert *conv, uint32_t n_samples) +void conv_noise_tri_sse2(struct convert *conv, float *noise, uint32_t n_samples) { uint32_t n; - const uint32_t *r = SPA_PTR_ALIGN(conv->random, 16, uint32_t); - int32_t *p = SPA_PTR_ALIGN(conv->prev, 16, int32_t), op; + const uint32_t *r = conv->random; __m128 scale = _mm_set1_ps(conv->scale); + __m128i in1; __m128 out1; - float *noise = SPA_PTR_ALIGN(conv->noise, 16, float); + + for (n = 0; n < n_samples; n += 4) { + in0 = _mm_sub_epi32( _MM_XORSHIFT_EPI32(r), _MM_XORSHIFT_EPI32(r)); + out0 = _mm_cvtepi32_ps(in0); + out0 = _mm_mul_ps(out0, scale); + _mm_store_ps(&noisen, out0); + } +} + +void conv_noise_tri_hf_sse2(struct convert *conv, float *noise, uint32_t n_samples) +{ + uint32_t n; + int32_t *p = conv->prev; + const uint32_t *r = conv->random; + __m128 scale = _mm_set1_ps(conv->scale); __m128i in1, old1, new1; + __m128 out1; - switch (conv->noise_method) { - case DITHER_METHOD_RECTANGULAR: - for (n = 0; n < n_samples; n += 4) { - in0 = _MM_XORSHIFT_EPI32(r); - out0 = _mm_cvtepi32_ps(_MM_XORSHIFT_EPI32(r)); - out0 = _mm_mul_ps(out0, scale); - _mm_store_ps(&noisen, out0); - } - break; - case DITHER_METHOD_TRIANGULAR: - for (n = 0; n < n_samples; n += 4) { - in0 = _mm_sub_epi32( _MM_XORSHIFT_EPI32(r), _MM_XORSHIFT_EPI32(r)); - out0 = _mm_cvtepi32_ps(in0); - out0 = _mm_mul_ps(out0, scale); - _mm_store_ps(&noisen, out0); - } - break; - case DITHER_METHOD_TRIANGULAR_HF: - old0 = _mm_load_si128((__m128i*)p); - for (n = 0; n < n_samples; n += 4) { - new0 = _MM_XORSHIFT_EPI32(r); - in0 = _mm_sub_epi32(old0, new0); - old0 = new0; - out0 = _mm_cvtepi32_ps(in0); - out0 = _mm_mul_ps(out0, scale); - _mm_store_ps(&noisen, out0); - } - _mm_store_si128((__m128i*)p, old0); - break; - case NOISE_METHOD_PATTERN: - op = *p; - for (n = 0; n < n_samples; n++) - noisen = conv->scale * (1-((op++>>10)&1)); - *p = op; - break; + old0 = _mm_load_si128((__m128i*)p); + for (n = 0; n < n_samples; n += 4) { + new0 = _MM_XORSHIFT_EPI32(r); + in0 = _mm_sub_epi32(old0, new0); + old0 = new0; + out0 = _mm_cvtepi32_ps(in0); + out0 = _mm_mul_ps(out0, scale); + _mm_store_ps(&noisen, out0); } + _mm_store_si128((__m128i*)p, old0); } static void conv_f32d_to_s32_1s_noise_sse2(struct convert *conv, void * SPA_RESTRICT dst, const void * SPA_RESTRICT src, - uint32_t n_channels, uint32_t n_samples) + float *noise, uint32_t n_channels, uint32_t n_samples) { const float *s = src; - float *noise = SPA_PTR_ALIGN(conv->noise, 16, float); int32_t *d = dst; uint32_t n, unrolled; __m128 in1; @@ -676,14 +679,16 @@ { int32_t *d = dst0; uint32_t i, k, chunk, n_channels = conv->n_channels; + float *noise = conv->noise; - update_noise_sse2(conv, SPA_MIN(n_samples, conv->noise_size)); + convert_update_noise(conv, noise, SPA_MIN(n_samples, conv->noise_size)); for(i = 0; i < n_channels; i++) { const float *s = srci; for(k = 0; k < n_samples; k += chunk) { chunk = SPA_MIN(n_samples - k, conv->noise_size); - conv_f32d_to_s32_1s_noise_sse2(conv, &di + k*n_channels, &sk, n_channels, chunk); + conv_f32d_to_s32_1s_noise_sse2(conv, &di + k*n_channels, + &sk, noise, n_channels, chunk); } } } @@ -1261,11 +1266,10 @@ static void conv_f32d_to_s16_1s_noise_sse2(struct convert *conv, void * SPA_RESTRICT dst, const void * SPA_RESTRICT src, - uint32_t n_channels, uint32_t n_samples) + const float *noise, uint32_t n_channels, uint32_t n_samples) { const float *s0 = src; int16_t *d = dst; - float *noise = SPA_PTR_ALIGN(conv->noise, 16, float); uint32_t n, unrolled; __m128 in2; __m128i out2; @@ -1312,25 +1316,26 @@ { int16_t *d = dst0; uint32_t i, k, chunk, n_channels = conv->n_channels; + float *noise = conv->noise; - update_noise_sse2(conv, SPA_MIN(n_samples, conv->noise_size)); + convert_update_noise(conv, noise, SPA_MIN(n_samples, conv->noise_size)); for(i = 0; i < n_channels; i++) { const float *s = srci; for(k = 0; k < n_samples; k += chunk) { chunk = SPA_MIN(n_samples - k, conv->noise_size); - conv_f32d_to_s16_1s_noise_sse2(conv, &di + k*n_channels, &sk, n_channels, chunk); + conv_f32d_to_s16_1s_noise_sse2(conv, &di + k*n_channels, + &sk, noise, n_channels, chunk); } } } static void conv_f32_to_s16_1_noise_sse2(struct convert *conv, void * SPA_RESTRICT dst, const void * SPA_RESTRICT src, - uint32_t n_samples) + const float *noise, uint32_t n_samples) { const float *s = src; int16_t *d = dst; - float *noise = SPA_PTR_ALIGN(conv->noise, 16, float); uint32_t n, unrolled; __m128 in2; __m128i out2; @@ -1366,15 +1371,16 @@ uint32_t n_samples) { uint32_t i, k, chunk, n_channels = conv->n_channels; + float *noise = conv->noise; - update_noise_sse2(conv, SPA_MIN(n_samples, conv->noise_size)); + convert_update_noise(conv, noise, SPA_MIN(n_samples, conv->noise_size)); for(i = 0; i < n_channels; i++) { const float *s = srci; int16_t *d = dsti; for(k = 0; k < n_samples; k += chunk) { chunk = SPA_MIN(n_samples - k, conv->noise_size); - conv_f32_to_s16_1_noise_sse2(conv, &dk, &sk, chunk); + conv_f32_to_s16_1_noise_sse2(conv, &dk, &sk, noise, chunk); } } }
View file
pipewire-0.3.58.tar.gz/spa/plugins/audioconvert/fmt-ops.c -> pipewire-0.3.59.tar.gz/spa/plugins/audioconvert/fmt-ops.c
Changed
@@ -32,7 +32,8 @@ #include "fmt-ops.h" -#define DITHER_SIZE (1<<10) +#define NOISE_SIZE (1<<10) +#define RANDOM_SIZE (16) typedef void (*convert_func_t) (struct convert *conv, void * SPA_RESTRICT dst, const void * SPA_RESTRICT src, uint32_t n_samples); @@ -359,7 +360,6 @@ uint32_t n_channels, uint32_t cpu_flags, uint32_t conv_flags) { size_t i; - for (i = 0; i < SPA_N_ELEMENTS(conv_table); i++) { if (conv_tablei.src_fmt == src_fmt && conv_tablei.dst_fmt == dst_fmt && @@ -371,11 +371,52 @@ return NULL; } +typedef void (*noise_func_t) (struct convert *conv, float * noise, uint32_t n_samples); + +struct noise_info { + uint32_t method; + + noise_func_t noise; + const char *name; + + uint32_t cpu_flags; +}; + +#define MAKE(method,func,...) \ + { NOISE_METHOD_ ##method, func, #func , __VA_ARGS__ } + +static struct noise_info noise_table = +{ +#if defined (HAVE_SSE2) + MAKE(RECTANGULAR, conv_noise_rect_sse2, SPA_CPU_FLAG_SSE2), + MAKE(TRIANGULAR, conv_noise_tri_sse2, SPA_CPU_FLAG_SSE2), + MAKE(TRIANGULAR_HF, conv_noise_tri_hf_sse2, SPA_CPU_FLAG_SSE2), +#endif + MAKE(NONE, conv_noise_none_c), + MAKE(RECTANGULAR, conv_noise_rect_c), + MAKE(TRIANGULAR, conv_noise_tri_c), + MAKE(TRIANGULAR_HF, conv_noise_tri_hf_c), + MAKE(PATTERN, conv_noise_pattern_c), +}; +#undef MAKE + +static const struct noise_info *find_noise_info(uint32_t method, + uint32_t cpu_flags) +{ + size_t i; + for (i = 0; i < SPA_N_ELEMENTS(noise_table); i++) { + if (noise_tablei.method == method && + MATCH_CPU_FLAGS(noise_tablei.cpu_flags, cpu_flags)) + return &noise_tablei; + } + return NULL; +} + static void impl_convert_free(struct convert *conv) { conv->process = NULL; - free(conv->noise); - conv->noise = NULL; + free(conv->data); + conv->data = NULL; } static bool need_dither(uint32_t format) @@ -449,7 +490,8 @@ { const struct conv_info *info; const struct dither_info *dinfo; - uint32_t i, conv_flags; + const struct noise_info *ninfo; + uint32_t i, conv_flags, data_size3; conv->scale = 1.0f / (float)(INT32_MAX); @@ -494,17 +536,31 @@ if (info == NULL) return -ENOTSUP; - conv->noise_size = DITHER_SIZE; - conv->noise = calloc(conv->noise_size + 16 + - FMT_OPS_MAX_ALIGN / sizeof(float), sizeof(float)); - if (conv->noise == NULL) + ninfo = find_noise_info(conv->noise_method, conv->cpu_flags); + if (ninfo == NULL) + return -ENOTSUP; + + conv->noise_size = NOISE_SIZE; + + data_size0 = SPA_ROUND_UP(conv->noise_size * sizeof(float), FMT_OPS_MAX_ALIGN); + data_size1 = SPA_ROUND_UP(RANDOM_SIZE * sizeof(uint32_t), FMT_OPS_MAX_ALIGN); + data_size2 = SPA_ROUND_UP(RANDOM_SIZE * sizeof(int32_t), FMT_OPS_MAX_ALIGN); + + conv->data = calloc(FMT_OPS_MAX_ALIGN + + data_size0 + data_size1 + data_size2, 1); + if (conv->data == NULL) return -errno; - for (i = 0; i < SPA_N_ELEMENTS(conv->random); i++) + conv->noise = SPA_PTR_ALIGN(conv->data, FMT_OPS_MAX_ALIGN, float); + conv->random = SPA_PTROFF(conv->noise, data_size0, uint32_t); + conv->prev = SPA_PTROFF(conv->random, data_size1, int32_t); + + for (i = 0; i < RANDOM_SIZE; i++) conv->randomi = random(); conv->is_passthrough = conv->src_fmt == conv->dst_fmt; conv->cpu_flags = info->cpu_flags; + conv->update_noise = ninfo->noise; conv->process = info->process; conv->free = impl_convert_free; conv->func_name = info->name;
View file
pipewire-0.3.58.tar.gz/spa/plugins/audioconvert/fmt-ops.h -> pipewire-0.3.59.tar.gz/spa/plugins/audioconvert/fmt-ops.h
Changed
@@ -226,8 +226,8 @@ unsigned int is_passthrough:1; float scale; - uint32_t random16 + FMT_OPS_MAX_ALIGN/4; - int32_t prev16 + FMT_OPS_MAX_ALIGN/4; + uint32_t *random; + int32_t *prev; #define NOISE_METHOD_NONE 0 #define NOISE_METHOD_RECTANGULAR 1 #define NOISE_METHOD_TRIANGULAR 2 @@ -240,9 +240,12 @@ uint32_t n_ns; struct shaper shaper64; + void (*update_noise) (struct convert *conv, float *noise, uint32_t n_samples); void (*process) (struct convert *conv, void * SPA_RESTRICT dst, const void * SPA_RESTRICT src, uint32_t n_samples); void (*free) (struct convert *conv); + + void *data; }; int convert_init(struct convert *conv); @@ -276,12 +279,30 @@ return DITHER_METHOD_NONE; } +#define convert_update_noise(conv,...) (conv)->update_noise(conv, __VA_ARGS__) #define convert_process(conv,...) (conv)->process(conv, __VA_ARGS__) #define convert_free(conv) (conv)->free(conv) -#define DEFINE_FUNCTION(name,arch) \ +#define DEFINE_NOISE_FUNCTION(name,arch) \ +void conv_noise_##name##_##arch(struct convert *conv, float *noise, \ + uint32_t n_samples) + +DEFINE_NOISE_FUNCTION(none, c); +DEFINE_NOISE_FUNCTION(rect, c); +DEFINE_NOISE_FUNCTION(tri, c); +DEFINE_NOISE_FUNCTION(tri_hf, c); +DEFINE_NOISE_FUNCTION(pattern, c); +#if defined(HAVE_SSE2) +DEFINE_NOISE_FUNCTION(rect, sse2); +DEFINE_NOISE_FUNCTION(tri, sse2); +DEFINE_NOISE_FUNCTION(tri_hf, sse2); +#endif + +#undef DEFINE_NOISE_FUNCTION + +#define DEFINE_FUNCTION(name,arch) \ void conv_##name##_##arch(struct convert *conv, void * SPA_RESTRICT dst, \ - const void * SPA_RESTRICT src, uint32_t n_samples) \ + const void * SPA_RESTRICT src, uint32_t n_samples) DEFINE_FUNCTION(copy8d, c); DEFINE_FUNCTION(copy8, c);
View file
pipewire-0.3.58.tar.gz/spa/plugins/audioconvert/meson.build -> pipewire-0.3.59.tar.gz/spa/plugins/audioconvert/meson.build
Changed
@@ -12,8 +12,8 @@ 'biquad.c', 'crossover.c', 'volume-ops-c.c', + 'peaks-ops-c.c', 'resample-native-c.c', - 'resample-peaks-c.c', 'fmt-ops-c.c' , c_args : '-Ofast', '-ffast-math', dependencies : spa_dep , @@ -24,8 +24,8 @@ if have_sse audioconvert_sse = static_library('audioconvert_sse', 'resample-native-sse.c', - 'resample-peaks-sse.c', 'volume-ops-sse.c', + 'peaks-ops-sse.c', 'channelmix-ops-sse.c' , c_args : sse_args, '-Ofast', '-DHAVE_SSE', dependencies : spa_dep , @@ -101,6 +101,7 @@ audioconvert_lib = static_library('audioconvert', 'fmt-ops.c', 'channelmix-ops.c', + 'peaks-ops.c', 'resample-native.c', 'resample-peaks.c', 'volume-ops.c' , @@ -132,6 +133,7 @@ 'test-audioconvert', 'test-channelmix', 'test-fmt-ops', + 'test-peaks', 'test-resample',
View file
pipewire-0.3.59.tar.gz/spa/plugins/audioconvert/peaks-ops-c.c
Added
@@ -0,0 +1,50 @@ +/* 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 <math.h> + +#include "peaks-ops.h" + +void peaks_min_max_c(struct peaks *peaks, const float * SPA_RESTRICT src, + uint32_t n_samples, float *min, float *max) +{ + uint32_t n; + float t, mi = *min, ma = *max; + for (n = 0; n < n_samples; n++) { + t = srcn; + mi = fminf(mi, t); + ma = fmaxf(ma, t); + } + *min = mi; + *max = ma; +} + +float peaks_abs_max_c(struct peaks *peaks, const float * SPA_RESTRICT src, + uint32_t n_samples, float max) +{ + uint32_t n; + for (n = 0; n < n_samples; n++) + max = fmaxf(fabsf(srcn), max); + return max; +}
View file
pipewire-0.3.59.tar.gz/spa/plugins/audioconvert/peaks-ops-sse.c
Added
@@ -0,0 +1,122 @@ +/* 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 <math.h> + +#include <xmmintrin.h> + +#include "peaks-ops.h" + +static inline float hmin_ps(__m128 val) +{ + __m128 t = _mm_movehl_ps(val, val); + t = _mm_min_ps(t, val); + val = _mm_shuffle_ps(t, t, 0x55); + val = _mm_min_ss(t, val); + return _mm_cvtss_f32(val); +} + +static inline float hmax_ps(__m128 val) +{ + __m128 t = _mm_movehl_ps(val, val); + t = _mm_max_ps(t, val); + val = _mm_shuffle_ps(t, t, 0x55); + val = _mm_max_ss(t, val); + return _mm_cvtss_f32(val); +} + +void peaks_min_max_sse(struct peaks *peaks, const float * SPA_RESTRICT src, + uint32_t n_samples, float *min, float *max) +{ + uint32_t n; + __m128 in; + __m128 mi = _mm_set1_ps(*min); + __m128 ma = _mm_set1_ps(*max); + + for (n = 0; n < n_samples; n++) { + if (SPA_IS_ALIGNED(&srcn, 16)) + break; + in = _mm_set1_ps(srcn); + mi = _mm_min_ps(mi, in); + ma = _mm_max_ps(ma, in); + } + for (; n + 15 < n_samples; n += 16) { + in = _mm_load_ps(&srcn + 0); + mi = _mm_min_ps(mi, in); + ma = _mm_max_ps(ma, in); + in = _mm_load_ps(&srcn + 4); + mi = _mm_min_ps(mi, in); + ma = _mm_max_ps(ma, in); + in = _mm_load_ps(&srcn + 8); + mi = _mm_min_ps(mi, in); + ma = _mm_max_ps(ma, in); + in = _mm_load_ps(&srcn + 12); + mi = _mm_min_ps(mi, in); + ma = _mm_max_ps(ma, in); + } + for (; n < n_samples; n++) { + in = _mm_set1_ps(srcn); + mi = _mm_min_ps(mi, in); + ma = _mm_max_ps(ma, in); + } + *min = hmin_ps(mi); + *max = hmax_ps(ma); +} + +float peaks_abs_max_sse(struct peaks *peaks, const float * SPA_RESTRICT src, + uint32_t n_samples, float max) +{ + uint32_t n; + __m128 in; + __m128 ma = _mm_set1_ps(max); + const __m128 mask = _mm_set1_ps(-0.0f); + + for (n = 0; n < n_samples; n++) { + if (SPA_IS_ALIGNED(&srcn, 16)) + break; + in = _mm_set1_ps(srcn); + in = _mm_andnot_ps(mask, in); + ma = _mm_max_ps(ma, in); + } + for (; n + 15 < n_samples; n += 16) { + in = _mm_load_ps(&srcn + 0); + in = _mm_andnot_ps(mask, in); + ma = _mm_max_ps(ma, in); + in = _mm_load_ps(&srcn + 4); + in = _mm_andnot_ps(mask, in); + ma = _mm_max_ps(ma, in); + in = _mm_load_ps(&srcn + 8); + in = _mm_andnot_ps(mask, in); + ma = _mm_max_ps(ma, in); + in = _mm_load_ps(&srcn + 12); + in = _mm_andnot_ps(mask, in); + ma = _mm_max_ps(ma, in); + } + for (; n < n_samples; n++) { + in = _mm_set1_ps(srcn); + in = _mm_andnot_ps(mask, in); + ma = _mm_max_ps(ma, in); + } + return hmax_ps(ma); +}
View file
pipewire-0.3.59.tar.gz/spa/plugins/audioconvert/peaks-ops.c
Added
@@ -0,0 +1,91 @@ +/* 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 <string.h> +#include <stdio.h> +#include <math.h> +#include <errno.h> + +#include <spa/support/cpu.h> +#include <spa/support/log.h> +#include <spa/utils/defs.h> + +#include "peaks-ops.h" + +typedef void (*peaks_min_max_func_t) (struct peaks *peaks, const float * SPA_RESTRICT src, + uint32_t n_samples, float *min, float *max); +typedef float (*peaks_abs_max_func_t) (struct peaks *peaks, const float * SPA_RESTRICT src, + uint32_t n_samples, float max); + +#define MAKE(min_max,abs_max,...) \ + { min_max, abs_max, #min_max , __VA_ARGS__ } + +static const struct peaks_info { + peaks_min_max_func_t min_max; + peaks_abs_max_func_t abs_max; + const char *name; + uint32_t cpu_flags; +} peaks_table = +{ +#if defined (HAVE_SSE) + MAKE(peaks_min_max_sse, peaks_abs_max_sse, SPA_CPU_FLAG_SSE), +#endif + MAKE(peaks_min_max_c, peaks_abs_max_c), +}; +#undef MAKE + +#define MATCH_CPU_FLAGS(a,b) ((a) == 0 || ((a) & (b)) == a) + +static const struct peaks_info *find_peaks_info(uint32_t cpu_flags) +{ + size_t i; + for (i = 0; i < SPA_N_ELEMENTS(peaks_table); i++) { + if (!MATCH_CPU_FLAGS(peaks_tablei.cpu_flags, cpu_flags)) + continue; + return &peaks_tablei; + } + return NULL; +} + +static void impl_peaks_free(struct peaks *peaks) +{ + peaks->min_max = NULL; + peaks->abs_max = NULL; +} + +int peaks_init(struct peaks *peaks) +{ + const struct peaks_info *info; + + info = find_peaks_info(peaks->cpu_flags); + if (info == NULL) + return -ENOTSUP; + + peaks->cpu_flags = info->cpu_flags; + peaks->func_name = info->name; + peaks->free = impl_peaks_free; + peaks->min_max = info->min_max; + peaks->abs_max = info->abs_max; + return 0; +}
View file
pipewire-0.3.59.tar.gz/spa/plugins/audioconvert/peaks-ops.h
Added
@@ -0,0 +1,72 @@ +/* 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 <string.h> +#include <stdio.h> + +#include <spa/utils/defs.h> + +struct peaks { + uint32_t cpu_flags; + const char *func_name; + + struct spa_log *log; + + uint32_t flags; + + void (*min_max) (struct peaks *peaks, const float * SPA_RESTRICT src, + uint32_t n_samples, float *min, float *max); + float (*abs_max) (struct peaks *peaks, const float * SPA_RESTRICT src, + uint32_t n_samples, float max); + + void (*free) (struct peaks *peaks); +}; + +int peaks_init(struct peaks *peaks); + +#define peaks_min_max(peaks,...) (peaks)->min_max(peaks, __VA_ARGS__) +#define peaks_abs_max(peaks,...) (peaks)->abs_max(peaks, __VA_ARGS__) +#define peaks_free(peaks) (peaks)->free(peaks) + +#define DEFINE_MIN_MAX_FUNCTION(arch) \ +void peaks_min_max_##arch(struct peaks *peaks, \ + const float * SPA_RESTRICT src, \ + uint32_t n_samples, float *min, float *max); + +#define DEFINE_ABS_MAX_FUNCTION(arch) \ +float peaks_abs_max_##arch(struct peaks *peaks, \ + const float * SPA_RESTRICT src, \ + uint32_t n_samples, float max); + +#define PEAKS_OPS_MAX_ALIGN 16 + +DEFINE_MIN_MAX_FUNCTION(c); +DEFINE_ABS_MAX_FUNCTION(c); + +#if defined (HAVE_SSE) +DEFINE_MIN_MAX_FUNCTION(sse); +DEFINE_ABS_MAX_FUNCTION(sse); +#endif + +#undef DEFINE_FUNCTION
View file
pipewire-0.3.58.tar.gz/spa/plugins/audioconvert/resample-native.c -> pipewire-0.3.59.tar.gz/spa/plugins/audioconvert/resample-native.c
Changed
@@ -304,7 +304,10 @@ if (d == NULL) return; memset(d->hist_mem, 0, r->channels * sizeof(float) * d->n_taps * 2); - d->hist = (d->n_taps / 2) - 1; + if (r->options & RESAMPLE_OPTION_PREFILL) + d->hist = d->n_taps - 1; + else + d->hist = (d->n_taps / 2) - 1; d->phase = 0; }
View file
pipewire-0.3.58.tar.gz/spa/plugins/audioconvert/resample-peaks.c -> pipewire-0.3.59.tar.gz/spa/plugins/audioconvert/resample-peaks.c
Changed
@@ -27,40 +27,70 @@ #include <spa/param/audio/format.h> -#include "resample-peaks-impl.h" - -struct resample_info { - uint32_t format; - uint32_t cpu_flags; - void (*process) (struct resample *r, - const void * SPA_RESTRICT src, uint32_t *in_len, - void * SPA_RESTRICT dst, uint32_t *out_len); +#include "peaks-ops.h" +#include "resample.h" + +struct peaks_data { + uint32_t o_count; + uint32_t i_count; + struct peaks peaks; + float max_f; }; -static struct resample_info resample_table = +static void resample_peaks_process(struct resample *r, + const void * SPA_RESTRICT src, uint32_t *in_len, + void * SPA_RESTRICT dst, uint32_t *out_len) { -#if defined (HAVE_SSE) - { SPA_AUDIO_FORMAT_F32, SPA_CPU_FLAG_SSE, resample_peaks_process_sse, }, -#endif - { SPA_AUDIO_FORMAT_F32, 0, resample_peaks_process_c, }, -}; + struct peaks_data *pd = r->data; + uint32_t c, i, o, end, chunk, i_count, o_count; -#define MATCH_CPU_FLAGS(a,b) ((a) == 0 || ((a) & (b)) == a) -static const struct resample_info *find_resample_info(uint32_t format, uint32_t cpu_flags) -{ - size_t i; - for (i = 0; i < SPA_N_ELEMENTS(resample_table); i++) { - if (resample_tablei.format == format && - MATCH_CPU_FLAGS(resample_tablei.cpu_flags, cpu_flags)) { - return &resample_tablei; + 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); + + m = peaks_abs_max(&pd->peaks, &si, chunk - i, m); + + i += chunk; + + 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; } - return NULL; } static void impl_peaks_free(struct resample *r) { - free(r->data); + struct peaks_data *d = r->data; + if (d != NULL) { + peaks_free(&d->peaks); + free(d); + } r->data = NULL; } @@ -87,27 +117,32 @@ int resample_peaks_init(struct resample *r) { struct peaks_data *d; - const struct resample_info *info; + int res; r->free = impl_peaks_free; r->update_rate = impl_peaks_update_rate; - if ((info = find_resample_info(SPA_AUDIO_FORMAT_F32, r->cpu_flags)) == NULL) - return -ENOTSUP; + d = calloc(1, sizeof(struct peaks_data) + sizeof(float) * r->channels); + if (d == NULL) + return -errno; + + d->peaks.log = r->log; + d->peaks.cpu_flags = r->cpu_flags; + if ((res = peaks_init(&d->peaks)) < 0) { + free(d); + return res; + } - r->process = info->process; + r->data = d; + r->process = resample_peaks_process; r->reset = impl_peaks_reset; r->delay = impl_peaks_delay; r->in_len = impl_peaks_in_len; - d = r->data = calloc(1, sizeof(struct peaks_data) + sizeof(float) * r->channels); - if (r->data == NULL) - return -errno; - spa_log_debug(r->log, "peaks %p: in:%d out:%d features:%08x:%08x", r, - r->i_rate, r->o_rate, r->cpu_flags, info->cpu_flags); + r->i_rate, r->o_rate, r->cpu_flags, d->peaks.cpu_flags); - r->cpu_flags = info->cpu_flags; + r->cpu_flags = d->peaks.cpu_flags; d->i_count = d->o_count = 0; return 0; }
View file
pipewire-0.3.58.tar.gz/spa/plugins/audioconvert/resample.h -> pipewire-0.3.59.tar.gz/spa/plugins/audioconvert/resample.h
Changed
@@ -32,6 +32,8 @@ struct resample { struct spa_log *log; +#define RESAMPLE_OPTION_PREFILL (1<<0) + uint32_t options; uint32_t cpu_flags; const char *func_name;
View file
pipewire-0.3.58.tar.gz/spa/plugins/audioconvert/test-channelmix.c -> pipewire-0.3.59.tar.gz/spa/plugins/audioconvert/test-channelmix.c
Changed
@@ -22,6 +22,8 @@ * DEALINGS IN THE SOFTWARE. */ +#include "config.h" + #include <string.h> #include <stdio.h> #include <stdlib.h> @@ -32,20 +34,26 @@ #include <spa/support/log-impl.h> #include <spa/debug/mem.h> +static uint32_t cpu_flags; + SPA_LOG_IMPL(logger); #define MATRIX(...) (float) { __VA_ARGS__ } +#include "test-helper.h" #include "channelmix-ops.c" + +#define CLOSE_ENOUGH(a,b) (fabs((a)-(b)) < 0.000001f) + static void dump_matrix(struct channelmix *mix, float *coeff) { uint32_t i, j; for (i = 0; i < mix->dst_chan; i++) { for (j = 0; j < mix->src_chan; j++) { - float v = mix->matrix_origij; + float v = mix->matrixij; spa_log_debug(mix->log, "%d %d: %f <-> %f", i, j, v, *coeff); - spa_assert_se(fabs(v - *coeff) < 0.000001); + spa_assert_se(CLOSE_ENOUGH(v, *coeff)); coeff++; } } @@ -65,7 +73,8 @@ mix.dst_mask = dst_mask; mix.log = &logger.log; - channelmix_init(&mix); + spa_assert_se(channelmix_init(&mix) == 0); + channelmix_set_volume(&mix, 1.0f, false, 0, NULL); dump_matrix(&mix, coeff); } @@ -220,10 +229,87 @@ 0.0, 1.0, 0.707107, 0.0, 0.0, 0.707107, 0.0, 0.707107)); } +static void run_n_m_impl(struct channelmix *mix, const void **src, uint32_t n_samples) +{ + uint32_t dst_chan = mix->dst_chan, i, j; + float dst_c_datadst_chann_samples; + float dst_x_datadst_chann_samples; + void *dst_cdst_chan, *dst_xdst_chan; + + for (i = 0; i < dst_chan; i++) { + dst_ci = dst_c_datai; + dst_xi = dst_x_datai; + } + + channelmix_f32_n_m_c(mix, dst_c, src, n_samples); + +#if defined(HAVE_SSE) + if (cpu_flags & SPA_CPU_FLAG_SSE) { + channelmix_f32_n_m_sse(mix, dst_x, src, n_samples); + for (i = 0; i < mix->dst_chan; i++) { + for (j = 0; j < n_samples; j++) { + spa_assert_se(CLOSE_ENOUGH(dst_c_dataij, dst_x_dataij)); + } + } + } +#endif +} + +static void test_n_m_impl(void) +{ + struct channelmix mix; + unsigned int i, j; +#define N_SAMPLES 251 + float src_data16N_SAMPLES, *src16; + + spa_log_debug(&logger.log, "start"); + + for (i = 0; i < 16; i++) { + for (j = 0; j < N_SAMPLES; j++) + src_dataij = (drand48() - 0.5f) * 2.5f; + srci = src_datai; + } + + spa_zero(mix); + mix.src_chan = 16; + mix.dst_chan = 12; + mix.log = &logger.log; + mix.cpu_flags = cpu_flags; + spa_assert_se(channelmix_init(&mix) == 0); + channelmix_set_volume(&mix, 1.0f, false, 0, NULL); + + /* identity matrix */ + run_n_m_impl(&mix, (const void**)src, N_SAMPLES); + + /* some zero destination */ + mix.matrix_orig22 = 0.0f; + mix.matrix_orig77 = 0.0f; + channelmix_set_volume(&mix, 1.0f, false, 0, NULL); + run_n_m_impl(&mix, (const void**)src, N_SAMPLES); + + /* random matrix */ + for (i = 0; i < mix.dst_chan; i++) { + for (j = 0; j < mix.src_chan; j++) { + mix.matrix_origij = drand48() - 0.5f; + } + } + channelmix_set_volume(&mix, 1.0f, false, 0, NULL); + + run_n_m_impl(&mix, (const void**)src, N_SAMPLES); +} + int main(int argc, char *argv) { + struct timespec ts; + + clock_gettime(CLOCK_MONOTONIC, &ts); + srand48(SPA_TIMESPEC_TO_NSEC(&ts)); + logger.log.level = SPA_LOG_LEVEL_TRACE; + cpu_flags = get_cpu_flags(); + printf("got CPU flags %d\n", cpu_flags); + test_1_N_MONO(); test_1_N_FC(); test_N_1(); @@ -232,5 +318,7 @@ test_5p1_N(); test_7p1_N(); + test_n_m_impl(); + return 0; }
View file
pipewire-0.3.59.tar.gz/spa/plugins/audioconvert/test-peaks.c
Added
@@ -0,0 +1,128 @@ +/* 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/support/log-impl.h> +#include <spa/debug/mem.h> + +SPA_LOG_IMPL(logger); + +static uint32_t cpu_flags; + +#include "test-helper.h" + +#include "peaks-ops.c" + +static void test_impl(void) +{ + struct peaks peaks; + unsigned int i; + float vals1038; + float min2 = { 0.0f, 0.0f }, max2 = { 0.0f, 0.0f }, absmax2 = { 0.0f, 0.0f }; + + for (i = 0; i < SPA_N_ELEMENTS(vals); i++) + valsi = (drand48() - 0.5f) * 2.5f; + + peaks_min_max_c(&peaks, &vals1, SPA_N_ELEMENTS(vals) - 1, &min0, &max0); + printf("c peaks min:%f max:%f\n", min0, max0); + + absmax0 = peaks_abs_max_c(&peaks, &vals1, SPA_N_ELEMENTS(vals) - 1, 0.0f); + printf("c peaks abs-max:%f\n", absmax0); + +#if defined(HAVE_SSE) + if (cpu_flags & SPA_CPU_FLAG_SSE) { + peaks_min_max_sse(&peaks, &vals1, SPA_N_ELEMENTS(vals) - 1, &min1, &max1); + printf("sse peaks min:%f max:%f\n", min1, max1); + + absmax1 = peaks_abs_max_sse(&peaks, &vals1, SPA_N_ELEMENTS(vals) - 1, 0.0f); + printf("sse peaks abs-max:%f\n", absmax1); + + spa_assert(min0 == min1); + spa_assert(max0 == max1); + spa_assert(absmax0 == absmax1); + } +#endif + +} + +static void test_min_max(void) +{ + struct peaks peaks; + const float vals = { 0.0f, 0.5f, -0.5f, 0.0f, 0.6f, -0.8f, -0.5f, 0.0f }; + float min = 0.0f, max = 0.0f; + + spa_zero(peaks); + peaks.log = &logger.log; + peaks.cpu_flags = cpu_flags; + peaks_init(&peaks); + + peaks_min_max(&peaks, vals, SPA_N_ELEMENTS(vals), &min, &max); + + spa_assert(min == -0.8f); + spa_assert(max == 0.6f); +} + +static void test_abs_max(void) +{ + struct peaks peaks; + const float vals = { 0.0f, 0.5f, -0.5f, 0.0f, 0.6f, -0.8f, -0.5f, 0.0f }; + float max = 0.0f; + + spa_zero(peaks); + peaks.log = &logger.log; + peaks.cpu_flags = cpu_flags; + peaks_init(&peaks); + + max = peaks_abs_max(&peaks, vals, SPA_N_ELEMENTS(vals), max); + + spa_assert(max == 0.8f); +} + +int main(int argc, char *argv) +{ + struct timespec ts; + + clock_gettime(CLOCK_MONOTONIC, &ts); + srand48(SPA_TIMESPEC_TO_NSEC(&ts)); + + logger.log.level = SPA_LOG_LEVEL_TRACE; + + cpu_flags = get_cpu_flags(); + printf("got CPU flags %d\n", cpu_flags); + + test_impl(); + + test_min_max(); + test_abs_max(); + + return 0; +}
View file
pipewire-0.3.58.tar.gz/spa/plugins/avb/avb-pcm.c -> pipewire-0.3.59.tar.gz/spa/plugins/avb/avb-pcm.c
Changed
@@ -877,7 +877,7 @@ SPA_AVBTP_PACKET_AAF_SET_SEQ_NUM(pdu, state->pdu_seq++); SPA_AVBTP_PACKET_AAF_SET_TIMESTAMP(pdu, ptime); - n = sendmsg(state->sockfd, &state->msg, 0); + n = sendmsg(state->sockfd, &state->msg, MSG_NOSIGNAL); if (n < 0 || n != (ssize_t)state->pdu_size) { spa_log_error(state->log, "sendmdg() failed: %m"); }
View file
pipewire-0.3.58.tar.gz/spa/plugins/bluez5/a2dp-codec-aac.c -> pipewire-0.3.59.tar.gz/spa/plugins/bluez5/a2dp-codec-aac.c
Changed
@@ -34,7 +34,7 @@ #include <fdk-aac/aacdecoder_lib.h> #include "rtp.h" -#include "a2dp-codecs.h" +#include "media-codecs.h" static struct spa_log *log; static struct spa_log_topic log_topic = SPA_LOG_TOPIC(0, "spa.bluez5.codecs.aac"); @@ -65,7 +65,7 @@ int samplesize; }; -static int codec_fill_caps(const struct a2dp_codec *codec, uint32_t flags, +static int codec_fill_caps(const struct media_codec *codec, uint32_t flags, uint8_t capsA2DP_MAX_CAPS_SIZE) { static const a2dp_aac_t a2dp_aac = { @@ -98,7 +98,7 @@ return sizeof(a2dp_aac); } -static const struct a2dp_codec_config +static const struct media_codec_config aac_frequencies = { { AAC_SAMPLING_FREQ_48000, 48000, 11 }, { AAC_SAMPLING_FREQ_44100, 44100, 10 }, @@ -114,7 +114,7 @@ { AAC_SAMPLING_FREQ_8000, 8000, 0 }, }; -static const struct a2dp_codec_config +static const struct media_codec_config aac_channel_modes = { { AAC_CHANNELS_2, 2, 1 }, { AAC_CHANNELS_1, 1, 0 }, @@ -130,9 +130,9 @@ } } -static int codec_select_config(const struct a2dp_codec *codec, uint32_t flags, +static int codec_select_config(const struct media_codec *codec, uint32_t flags, const void *caps, size_t caps_size, - const struct a2dp_codec_audio_info *info, + const struct media_codec_audio_info *info, const struct spa_dict *settings, uint8_t configA2DP_MAX_CAPS_SIZE) { a2dp_aac_t conf; @@ -154,7 +154,7 @@ else return -ENOTSUP; - if ((i = a2dp_codec_select_config(aac_frequencies, + if ((i = media_codec_select_config(aac_frequencies, SPA_N_ELEMENTS(aac_frequencies), AAC_GET_FREQUENCY(conf), info ? info->rate : A2DP_CODEC_DEFAULT_RATE @@ -162,7 +162,7 @@ return -ENOTSUP; AAC_SET_FREQUENCY(conf, aac_frequenciesi.config); - if ((i = a2dp_codec_select_config(aac_channel_modes, + if ((i = media_codec_select_config(aac_channel_modes, SPA_N_ELEMENTS(aac_channel_modes), conf.channels, info ? info->channels : A2DP_CODEC_DEFAULT_CHANNELS @@ -177,7 +177,7 @@ return sizeof(conf); } -static int codec_enum_config(const struct a2dp_codec *codec, uint32_t flags, +static int codec_enum_config(const struct media_codec *codec, uint32_t flags, const void *caps, size_t caps_size, uint32_t id, uint32_t idx, struct spa_pod_builder *b, struct spa_pod **param) { @@ -246,7 +246,7 @@ return *param == NULL ? -EIO : 1; } -static int codec_validate_config(const struct a2dp_codec *codec, uint32_t flags, +static int codec_validate_config(const struct media_codec *codec, uint32_t flags, const void *caps, size_t caps_size, struct spa_audio_info *info) { @@ -296,7 +296,7 @@ return 0; } -static void *codec_init_props(const struct a2dp_codec *codec, uint32_t flags, const struct spa_dict *settings) +static void *codec_init_props(const struct media_codec *codec, uint32_t flags, const struct spa_dict *settings) { struct props *p = calloc(1, sizeof(struct props)); const char *str; @@ -316,7 +316,7 @@ free(props); } -static void *codec_init(const struct a2dp_codec *codec, uint32_t flags, +static void *codec_init(const struct media_codec *codec, uint32_t flags, void *config, size_t config_len, const struct spa_audio_info *info, void *props, size_t mtu) { @@ -630,7 +630,7 @@ spa_log_topic_init(log, &log_topic); } -const struct a2dp_codec a2dp_codec_aac = { +const struct media_codec a2dp_codec_aac = { .id = SPA_BLUETOOTH_AUDIO_CODEC_AAC, .codec_id = A2DP_CODEC_MPEG24, .name = "aac", @@ -654,7 +654,7 @@ .set_log = codec_set_log, }; -A2DP_CODEC_EXPORT_DEF( +MEDIA_CODEC_EXPORT_DEF( "aac", &a2dp_codec_aac );
View file
pipewire-0.3.58.tar.gz/spa/plugins/bluez5/a2dp-codec-aptx.c -> pipewire-0.3.59.tar.gz/spa/plugins/bluez5/a2dp-codec-aptx.c
Changed
@@ -35,7 +35,7 @@ #include <freeaptx.h> #include "rtp.h" -#include "a2dp-codecs.h" +#include "media-codecs.h" #define APTX_LL_LEVEL1(level) (((level) >> 8) & 0xFF) #define APTX_LL_LEVEL2(level) (((level) >> 0) & 0xFF) @@ -71,19 +71,19 @@ sbc_t msbc; }; -static inline bool codec_is_hd(const struct a2dp_codec *codec) +static inline bool codec_is_hd(const struct media_codec *codec) { return codec->vendor.codec_id == APTX_HD_CODEC_ID && codec->vendor.vendor_id == APTX_HD_VENDOR_ID; } -static inline bool codec_is_ll(const struct a2dp_codec *codec) +static inline bool codec_is_ll(const struct media_codec *codec) { return (codec->id == SPA_BLUETOOTH_AUDIO_CODEC_APTX_LL) || (codec->id == SPA_BLUETOOTH_AUDIO_CODEC_APTX_LL_DUPLEX); } -static inline size_t codec_get_caps_size(const struct a2dp_codec *codec) +static inline size_t codec_get_caps_size(const struct media_codec *codec) { if (codec_is_hd(codec)) return sizeof(a2dp_aptx_hd_t); @@ -93,7 +93,7 @@ return sizeof(a2dp_aptx_t); } -static int codec_fill_caps(const struct a2dp_codec *codec, uint32_t flags, +static int codec_fill_caps(const struct media_codec *codec, uint32_t flags, uint8_t capsA2DP_MAX_CAPS_SIZE) { size_t actual_conf_size = codec_get_caps_size(codec); @@ -119,7 +119,7 @@ return actual_conf_size; } -static const struct a2dp_codec_config +static const struct media_codec_config aptx_frequencies = { { APTX_SAMPLING_FREQ_48000, 48000, 3 }, { APTX_SAMPLING_FREQ_44100, 44100, 2 }, @@ -127,9 +127,9 @@ { APTX_SAMPLING_FREQ_16000, 16000, 0 }, }; -static int codec_select_config(const struct a2dp_codec *codec, uint32_t flags, +static int codec_select_config(const struct media_codec *codec, uint32_t flags, const void *caps, size_t caps_size, - const struct a2dp_codec_audio_info *info, + const struct media_codec_audio_info *info, const struct spa_dict *settings, uint8_t configA2DP_MAX_CAPS_SIZE) { a2dp_aptx_t conf; @@ -145,7 +145,7 @@ codec->vendor.codec_id != conf.info.codec_id) return -ENOTSUP; - if ((i = a2dp_codec_select_config(aptx_frequencies, + if ((i = media_codec_select_config(aptx_frequencies, SPA_N_ELEMENTS(aptx_frequencies), conf.frequency, info ? info->rate : A2DP_CODEC_DEFAULT_RATE @@ -163,9 +163,9 @@ return actual_conf_size; } -static int codec_select_config_ll(const struct a2dp_codec *codec, uint32_t flags, +static int codec_select_config_ll(const struct media_codec *codec, uint32_t flags, const void *caps, size_t caps_size, - const struct a2dp_codec_audio_info *info, + const struct media_codec_audio_info *info, const struct spa_dict *settings, uint8_t configA2DP_MAX_CAPS_SIZE) { a2dp_aptx_ll_ext_t conf = { 0 }; @@ -218,7 +218,7 @@ return actual_conf_size; } -static int codec_enum_config(const struct a2dp_codec *codec, uint32_t flags, +static int codec_enum_config(const struct media_codec *codec, uint32_t flags, const void *caps, size_t caps_size, uint32_t id, uint32_t idx, struct spa_pod_builder *b, struct spa_pod **param) { @@ -315,7 +315,7 @@ return this->codesize; } -static void *codec_init(const struct a2dp_codec *codec, uint32_t flags, +static void *codec_init(const struct media_codec *codec, uint32_t flags, void *config, size_t config_len, const struct spa_audio_info *info, void *props, size_t mtu) { @@ -458,7 +458,7 @@ * When connected as SRC to SNK, aptX-LL sink may send back mSBC data. */ -static int msbc_enum_config(const struct a2dp_codec *codec, uint32_t flags, +static int msbc_enum_config(const struct media_codec *codec, uint32_t flags, const void *caps, size_t caps_size, uint32_t id, uint32_t idx, struct spa_pod_builder *b, struct spa_pod **param) { @@ -479,7 +479,7 @@ return *param == NULL ? -EIO : 1; } -static int msbc_validate_config(const struct a2dp_codec *codec, uint32_t flags, +static int msbc_validate_config(const struct media_codec *codec, uint32_t flags, const void *caps, size_t caps_size, struct spa_audio_info *info) { @@ -508,7 +508,7 @@ return MSBC_DECODED_SIZE; } -static void *msbc_init(const struct a2dp_codec *codec, uint32_t flags, +static void *msbc_init(const struct media_codec *codec, uint32_t flags, void *config, size_t config_len, const struct spa_audio_info *info, void *props, size_t mtu) { @@ -610,7 +610,7 @@ } -const struct a2dp_codec a2dp_codec_aptx = { +const struct media_codec a2dp_codec_aptx = { .id = SPA_BLUETOOTH_AUDIO_CODEC_APTX, .codec_id = A2DP_CODEC_VENDOR, .vendor = { .vendor_id = APTX_VENDOR_ID, @@ -633,7 +633,7 @@ }; -const struct a2dp_codec a2dp_codec_aptx_hd = { +const struct media_codec a2dp_codec_aptx_hd = { .id = SPA_BLUETOOTH_AUDIO_CODEC_APTX_HD, .codec_id = A2DP_CODEC_VENDOR, .vendor = { .vendor_id = APTX_HD_VENDOR_ID, @@ -671,7 +671,7 @@ .increase_bitpool = codec_increase_bitpool -const struct a2dp_codec a2dp_codec_aptx_ll_0 = { +const struct media_codec a2dp_codec_aptx_ll_0 = { APTX_LL_COMMON_DEFS, .id = SPA_BLUETOOTH_AUDIO_CODEC_APTX_LL, .vendor = { .vendor_id = APTX_LL_VENDOR_ID, @@ -680,7 +680,7 @@ .endpoint_name = "aptx_ll_0", }; -const struct a2dp_codec a2dp_codec_aptx_ll_1 = { +const struct media_codec a2dp_codec_aptx_ll_1 = { APTX_LL_COMMON_DEFS, .id = SPA_BLUETOOTH_AUDIO_CODEC_APTX_LL, .vendor = { .vendor_id = APTX_LL_VENDOR_ID2, @@ -690,7 +690,7 @@ }; /* Voice channel mSBC, not a real A2DP codec */ -static const struct a2dp_codec aptx_ll_msbc = { +static const struct media_codec aptx_ll_msbc = { .codec_id = A2DP_CODEC_VENDOR, .name = "aptx_ll_msbc", .description = "aptX-LL mSBC", @@ -715,7 +715,7 @@ }; static const struct spa_dict duplex_info = SPA_DICT_INIT_ARRAY(duplex_info_items); -const struct a2dp_codec a2dp_codec_aptx_ll_duplex_0 = { +const struct media_codec a2dp_codec_aptx_ll_duplex_0 = { APTX_LL_COMMON_DEFS, .id = SPA_BLUETOOTH_AUDIO_CODEC_APTX_LL_DUPLEX, .vendor = { .vendor_id = APTX_LL_VENDOR_ID, @@ -726,7 +726,7 @@ .info = &duplex_info, }; -const struct a2dp_codec a2dp_codec_aptx_ll_duplex_1 = { +const struct media_codec a2dp_codec_aptx_ll_duplex_1 = { APTX_LL_COMMON_DEFS, .id = SPA_BLUETOOTH_AUDIO_CODEC_APTX_LL_DUPLEX, .vendor = { .vendor_id = APTX_LL_VENDOR_ID2, @@ -737,7 +737,7 @@ .info = &duplex_info, }; -A2DP_CODEC_EXPORT_DEF( +MEDIA_CODEC_EXPORT_DEF( "aptx", &a2dp_codec_aptx_hd, &a2dp_codec_aptx,
View file
pipewire-0.3.58.tar.gz/spa/plugins/bluez5/a2dp-codec-faststream.c -> pipewire-0.3.59.tar.gz/spa/plugins/bluez5/a2dp-codec-faststream.c
Changed
@@ -36,7 +36,7 @@ #include <sbc/sbc.h> -#include "a2dp-codecs.h" +#include "media-codecs.h" struct impl { sbc_t sbc; @@ -51,7 +51,7 @@ sbc_t sbc; }; -static int codec_fill_caps(const struct a2dp_codec *codec, uint32_t flags, +static int codec_fill_caps(const struct media_codec *codec, uint32_t flags, uint8_t capsA2DP_MAX_CAPS_SIZE) { const a2dp_faststream_t a2dp_faststream = { @@ -69,20 +69,20 @@ return sizeof(a2dp_faststream); } -static const struct a2dp_codec_config +static const struct media_codec_config frequencies = { { FASTSTREAM_SINK_SAMPLING_FREQ_48000, 48000, 1 }, { FASTSTREAM_SINK_SAMPLING_FREQ_44100, 44100, 0 }, }; -static const struct a2dp_codec_config +static const struct media_codec_config duplex_frequencies = { { FASTSTREAM_SOURCE_SAMPLING_FREQ_16000, 16000, 0 }, }; -static int codec_select_config(const struct a2dp_codec *codec, uint32_t flags, +static int codec_select_config(const struct media_codec *codec, uint32_t flags, const void *caps, size_t caps_size, - const struct a2dp_codec_audio_info *info, + const struct media_codec_audio_info *info, const struct spa_dict *settings, uint8_t configA2DP_MAX_CAPS_SIZE) { a2dp_faststream_t conf; @@ -108,7 +108,7 @@ if (codec->duplex_codec) conf.direction |= FASTSTREAM_DIRECTION_SOURCE; - if ((i = a2dp_codec_select_config(frequencies, + if ((i = media_codec_select_config(frequencies, SPA_N_ELEMENTS(frequencies), conf.sink_frequency, info ? info->rate : A2DP_CODEC_DEFAULT_RATE @@ -116,7 +116,7 @@ return -ENOTSUP; conf.sink_frequency = frequenciesi.config; - if ((i = a2dp_codec_select_config(duplex_frequencies, + if ((i = media_codec_select_config(duplex_frequencies, SPA_N_ELEMENTS(duplex_frequencies), conf.source_frequency, 16000 @@ -129,7 +129,7 @@ return sizeof(conf); } -static int codec_enum_config(const struct a2dp_codec *codec, uint32_t flags, +static int codec_enum_config(const struct media_codec *codec, uint32_t flags, const void *caps, size_t caps_size, uint32_t id, uint32_t idx, struct spa_pod_builder *b, struct spa_pod **param) { @@ -209,7 +209,7 @@ return v; } -static void *codec_init(const struct a2dp_codec *codec, uint32_t flags, +static void *codec_init(const struct media_codec *codec, uint32_t flags, void *config, size_t config_len, const struct spa_audio_info *info, void *props, size_t mtu) { @@ -372,7 +372,7 @@ * When connected as SRC to SNK, FastStream sink may send back SBC data. */ -static int duplex_enum_config(const struct a2dp_codec *codec, uint32_t flags, +static int duplex_enum_config(const struct media_codec *codec, uint32_t flags, const void *caps, size_t caps_size, uint32_t id, uint32_t idx, struct spa_pod_builder *b, struct spa_pod **param) { @@ -411,7 +411,7 @@ return *param == NULL ? -EIO : 1; } -static int duplex_validate_config(const struct a2dp_codec *codec, uint32_t flags, +static int duplex_validate_config(const struct media_codec *codec, uint32_t flags, const void *caps, size_t caps_size, struct spa_audio_info *info) { @@ -441,7 +441,7 @@ return 0; } -static void *duplex_init(const struct a2dp_codec *codec, uint32_t flags, +static void *duplex_init(const struct media_codec *codec, uint32_t flags, void *config, size_t config_len, const struct spa_audio_info *info, void *props, size_t mtu) { @@ -577,7 +577,7 @@ } /* Voice channel SBC, not a real A2DP codec */ -static const struct a2dp_codec duplex_codec = { +static const struct media_codec duplex_codec = { .codec_id = A2DP_CODEC_VENDOR, .name = "faststream_sbc", .description = "FastStream duplex SBC", @@ -614,7 +614,7 @@ .reduce_bitpool = codec_reduce_bitpool, \ .increase_bitpool = codec_increase_bitpool -static const struct a2dp_codec a2dp_codec_faststream = { +const struct media_codec a2dp_codec_faststream = { FASTSTREAM_COMMON_DEFS, .id = SPA_BLUETOOTH_AUDIO_CODEC_FASTSTREAM, .name = "faststream", @@ -625,7 +625,7 @@ }; static const struct spa_dict duplex_info = SPA_DICT_INIT_ARRAY(duplex_info_items); -const struct a2dp_codec a2dp_codec_faststream_duplex = { +const struct media_codec a2dp_codec_faststream_duplex = { FASTSTREAM_COMMON_DEFS, .id = SPA_BLUETOOTH_AUDIO_CODEC_FASTSTREAM_DUPLEX, .name = "faststream_duplex", @@ -633,7 +633,7 @@ .info = &duplex_info, }; -A2DP_CODEC_EXPORT_DEF( +MEDIA_CODEC_EXPORT_DEF( "faststream", &a2dp_codec_faststream, &a2dp_codec_faststream_duplex
View file
pipewire-0.3.58.tar.gz/spa/plugins/bluez5/a2dp-codec-lc3plus.c -> pipewire-0.3.59.tar.gz/spa/plugins/bluez5/a2dp-codec-lc3plus.c
Changed
@@ -41,7 +41,7 @@ #endif #include "rtp.h" -#include "a2dp-codecs.h" +#include "media-codecs.h" #define BITRATE_MIN 96000 #define BITRATE_MAX 512000 @@ -86,7 +86,7 @@ int32_t buf2LC3PLUS_MAX_SAMPLES; }; -static int codec_fill_caps(const struct a2dp_codec *codec, uint32_t flags, +static int codec_fill_caps(const struct media_codec *codec, uint32_t flags, uint8_t capsA2DP_MAX_CAPS_SIZE) { const a2dp_lc3plus_hr_t a2dp_lc3plus_hr = { @@ -102,9 +102,9 @@ return sizeof(a2dp_lc3plus_hr); } -static int codec_select_config(const struct a2dp_codec *codec, uint32_t flags, +static int codec_select_config(const struct media_codec *codec, uint32_t flags, const void *caps, size_t caps_size, - const struct a2dp_codec_audio_info *info, + const struct media_codec_audio_info *info, const struct spa_dict *settings, uint8_t configA2DP_MAX_CAPS_SIZE) { a2dp_lc3plus_hr_t conf; @@ -150,8 +150,8 @@ return sizeof(conf); } -static int codec_caps_preference_cmp(const struct a2dp_codec *codec, uint32_t flags, const void *caps1, size_t caps1_size, - const void *caps2, size_t caps2_size, const struct a2dp_codec_audio_info *info, const struct spa_dict *global_settings) +static int codec_caps_preference_cmp(const struct media_codec *codec, uint32_t flags, const void *caps1, size_t caps1_size, + const void *caps2, size_t caps2_size, const struct media_codec_audio_info *info, const struct spa_dict *global_settings) { a2dp_lc3plus_hr_t conf1, conf2; a2dp_lc3plus_hr_t *conf; @@ -160,7 +160,7 @@ /* Order selected configurations by preference */ res1 = codec->select_config(codec, 0, caps1, caps1_size, info, NULL, (uint8_t *)&conf1); - res2 = codec->select_config(codec, 0, caps2, caps2_size, info, NULL, (uint8_t *)&conf2); + res2 = codec->select_config(codec, 0, caps2, caps2_size, info , NULL, (uint8_t *)&conf2); #define PREFER_EXPR(expr) \ do { \ @@ -190,7 +190,7 @@ #undef PREFER_BOOL } -static int codec_enum_config(const struct a2dp_codec *codec, uint32_t flags, +static int codec_enum_config(const struct media_codec *codec, uint32_t flags, const void *caps, size_t caps_size, uint32_t id, uint32_t idx, struct spa_pod_builder *b, struct spa_pod **param) { @@ -263,7 +263,7 @@ return *param == NULL ? -EIO : 1; } -static int codec_validate_config(const struct a2dp_codec *codec, uint32_t flags, +static int codec_validate_config(const struct media_codec *codec, uint32_t flags, const void *caps, size_t caps_size, struct spa_audio_info *info) { @@ -342,7 +342,7 @@ return (size_t)this->mtu >= header_size + ceildiv(payload_size, max_fragments); } -static void *codec_init(const struct a2dp_codec *codec, uint32_t flags, +static void *codec_init(const struct media_codec *codec, uint32_t flags, void *config, size_t config_len, const struct spa_audio_info *info, void *props, size_t mtu) { @@ -758,7 +758,7 @@ return 0; } -const struct a2dp_codec a2dp_codec_lc3plus_hr = { +const struct media_codec a2dp_codec_lc3plus_hr = { .id = SPA_BLUETOOTH_AUDIO_CODEC_LC3PLUS_HR, .name = "lc3plus_hr", .codec_id = A2DP_CODEC_VENDOR, @@ -782,7 +782,7 @@ .increase_bitpool = codec_increase_bitpool }; -A2DP_CODEC_EXPORT_DEF( +MEDIA_CODEC_EXPORT_DEF( "lc3plus", &a2dp_codec_lc3plus_hr );
View file
pipewire-0.3.58.tar.gz/spa/plugins/bluez5/a2dp-codec-ldac.c -> pipewire-0.3.59.tar.gz/spa/plugins/bluez5/a2dp-codec-ldac.c
Changed
@@ -40,7 +40,7 @@ #endif #include "rtp.h" -#include "a2dp-codecs.h" +#include "media-codecs.h" #define LDACBT_EQMID_AUTO -1 @@ -79,7 +79,7 @@ int frame_count; }; -static int codec_fill_caps(const struct a2dp_codec *codec, uint32_t flags, uint8_t capsA2DP_MAX_CAPS_SIZE) +static int codec_fill_caps(const struct media_codec *codec, uint32_t flags, uint8_t capsA2DP_MAX_CAPS_SIZE) { static const a2dp_ldac_t a2dp_ldac = { .info.vendor_id = LDAC_VENDOR_ID, @@ -97,7 +97,7 @@ return sizeof(a2dp_ldac); } -static const struct a2dp_codec_config +static const struct media_codec_config ldac_frequencies = { { LDACBT_SAMPLING_FREQ_044100, 44100, 3 }, { LDACBT_SAMPLING_FREQ_048000, 48000, 2 }, @@ -105,16 +105,16 @@ { LDACBT_SAMPLING_FREQ_096000, 96000, 0 }, }; -static const struct a2dp_codec_config +static const struct media_codec_config ldac_channel_modes = { { LDACBT_CHANNEL_MODE_STEREO, 2, 2 }, { LDACBT_CHANNEL_MODE_DUAL_CHANNEL, 2, 1 }, { LDACBT_CHANNEL_MODE_MONO, 1, 0 }, }; -static int codec_select_config(const struct a2dp_codec *codec, uint32_t flags, +static int codec_select_config(const struct media_codec *codec, uint32_t flags, const void *caps, size_t caps_size, - const struct a2dp_codec_audio_info *info, + const struct media_codec_audio_info *info, const struct spa_dict *settings, uint8_t configA2DP_MAX_CAPS_SIZE) { a2dp_ldac_t conf; @@ -129,7 +129,7 @@ codec->vendor.codec_id != conf.info.codec_id) return -ENOTSUP; - if ((i = a2dp_codec_select_config(ldac_frequencies, + if ((i = media_codec_select_config(ldac_frequencies, SPA_N_ELEMENTS(ldac_frequencies), conf.frequency, info ? info->rate : A2DP_CODEC_DEFAULT_RATE @@ -137,7 +137,7 @@ return -ENOTSUP; conf.frequency = ldac_frequenciesi.config; - if ((i = a2dp_codec_select_config(ldac_channel_modes, + if ((i = media_codec_select_config(ldac_channel_modes, SPA_N_ELEMENTS(ldac_channel_modes), conf.channel_mode, info ? info->channels : A2DP_CODEC_DEFAULT_CHANNELS @@ -150,7 +150,7 @@ return sizeof(conf); } -static int codec_enum_config(const struct a2dp_codec *codec, uint32_t flags, +static int codec_enum_config(const struct media_codec *codec, uint32_t flags, const void *caps, size_t caps_size, uint32_t id, uint32_t idx, struct spa_pod_builder *b, struct spa_pod **param) { @@ -284,7 +284,7 @@ return LDACBT_EQMID_AUTO; } -static void *codec_init_props(const struct a2dp_codec *codec, uint32_t flags, const struct spa_dict *settings) +static void *codec_init_props(const struct media_codec *codec, uint32_t flags, const struct spa_dict *settings) { struct props *p = calloc(1, sizeof(struct props)); const char *str; @@ -385,7 +385,7 @@ return prev_eqmid != p->eqmid; } -static void *codec_init(const struct a2dp_codec *codec, uint32_t flags, +static void *codec_init(const struct media_codec *codec, uint32_t flags, void *config, size_t config_len, const struct spa_audio_info *info, void *props, size_t mtu) { @@ -570,7 +570,7 @@ return src_used; } -const struct a2dp_codec a2dp_codec_ldac = { +const struct media_codec a2dp_codec_ldac = { .id = SPA_BLUETOOTH_AUDIO_CODEC_LDAC, .codec_id = A2DP_CODEC_VENDOR, .vendor = { .vendor_id = LDAC_VENDOR_ID, @@ -598,7 +598,7 @@ .increase_bitpool = codec_increase_bitpool, }; -A2DP_CODEC_EXPORT_DEF( +MEDIA_CODEC_EXPORT_DEF( "ldac", &a2dp_codec_ldac );
View file
pipewire-0.3.58.tar.gz/spa/plugins/bluez5/a2dp-codec-opus.c -> pipewire-0.3.59.tar.gz/spa/plugins/bluez5/a2dp-codec-opus.c
Changed
@@ -44,7 +44,7 @@ #include <opus_multistream.h> #include "rtp.h" -#include "a2dp-codecs.h" +#include "media-codecs.h" static struct spa_log *log; static struct spa_log_topic log_topic = SPA_LOG_TOPIC(0, "spa.bluez5.codecs.opus"); @@ -395,7 +395,7 @@ props->bidi_application = OPUS_APPLICATION_RESTRICTED_LOWDELAY; } -static int set_channel_conf(const struct a2dp_codec *codec, a2dp_opus_05_t *caps, const struct props *props) +static int set_channel_conf(const struct media_codec *codec, a2dp_opus_05_t *caps, const struct props *props) { /* * Predefined codec profiles @@ -469,7 +469,7 @@ return 0; } -static void get_default_bitrates(const struct a2dp_codec *codec, bool bidi, int *min, int *max, int *init) +static void get_default_bitrates(const struct media_codec *codec, bool bidi, int *min, int *max, int *init) { int tmp; @@ -514,7 +514,7 @@ }; } -static int get_mapping(const struct a2dp_codec *codec, const a2dp_opus_05_direction_t *conf, +static int get_mapping(const struct media_codec *codec, const a2dp_opus_05_direction_t *conf, bool use_surround_encoder, uint8_t *streams_ret, uint8_t *coupled_streams_ret, const uint8_t **surround_mapping, uint32_t *positions) { @@ -576,7 +576,7 @@ return 0; } -static int codec_fill_caps(const struct a2dp_codec *codec, uint32_t flags, +static int codec_fill_caps(const struct media_codec *codec, uint32_t flags, uint8_t capsA2DP_MAX_CAPS_SIZE) { a2dp_opus_05_t a2dp_opus_05 = { @@ -613,9 +613,9 @@ return sizeof(a2dp_opus_05); } -static int codec_select_config(const struct a2dp_codec *codec, uint32_t flags, +static int codec_select_config(const struct media_codec *codec, uint32_t flags, const void *caps, size_t caps_size, - const struct a2dp_codec_audio_info *info, + const struct media_codec_audio_info *info, const struct spa_dict *global_settings, uint8_t configA2DP_MAX_CAPS_SIZE) { struct props props; @@ -715,8 +715,8 @@ return sizeof(conf); } -static int codec_caps_preference_cmp(const struct a2dp_codec *codec, uint32_t flags, const void *caps1, size_t caps1_size, - const void *caps2, size_t caps2_size, const struct a2dp_codec_audio_info *info, +static int codec_caps_preference_cmp(const struct media_codec *codec, uint32_t flags, const void *caps1, size_t caps1_size, + const void *caps2, size_t caps2_size, const struct media_codec_audio_info *info, const struct spa_dict *global_settings) { a2dp_opus_05_t conf1, conf2, cap1, cap2; @@ -768,12 +768,12 @@ #undef PREFER_BOOL } -static bool is_duplex_codec(const struct a2dp_codec *codec) +static bool is_duplex_codec(const struct media_codec *codec) { return codec->id == 0; } -static bool use_surround_encoder(const struct a2dp_codec *codec, bool is_sink) +static bool use_surround_encoder(const struct media_codec *codec, bool is_sink) { if (codec->id == SPA_BLUETOOTH_AUDIO_CODEC_OPUS_05_PRO) return false; @@ -784,11 +784,11 @@ return !is_sink; } -static int codec_enum_config(const struct a2dp_codec *codec, uint32_t flags, +static int codec_enum_config(const struct media_codec *codec, uint32_t flags, const void *caps, size_t caps_size, uint32_t id, uint32_t idx, struct spa_pod_builder *b, struct spa_pod **param) { - const bool surround_encoder = use_surround_encoder(codec, flags & A2DP_CODEC_FLAG_SINK); + const bool surround_encoder = use_surround_encoder(codec, flags & MEDIA_CODEC_FLAG_SINK); a2dp_opus_05_t conf; a2dp_opus_05_direction_t *dir; struct spa_pod_frame f1; @@ -823,11 +823,11 @@ return *param == NULL ? -EIO : 1; } -static int codec_validate_config(const struct a2dp_codec *codec, uint32_t flags, +static int codec_validate_config(const struct media_codec *codec, uint32_t flags, const void *caps, size_t caps_size, struct spa_audio_info *info) { - const bool surround_encoder = use_surround_encoder(codec, flags & A2DP_CODEC_FLAG_SINK); + const bool surround_encoder = use_surround_encoder(codec, flags & MEDIA_CODEC_FLAG_SINK); const a2dp_opus_05_direction_t *dir1, *dir2; const a2dp_opus_05_t *conf; @@ -898,7 +898,7 @@ } } -static void *codec_init_props(const struct a2dp_codec *codec, uint32_t flags, const struct spa_dict *settings) +static void *codec_init_props(const struct media_codec *codec, uint32_t flags, const struct spa_dict *settings) { struct props *p; @@ -919,11 +919,11 @@ free(props); } -static void *codec_init(const struct a2dp_codec *codec, uint32_t flags, +static void *codec_init(const struct media_codec *codec, uint32_t flags, void *config, size_t config_len, const struct spa_audio_info *info, void *props, size_t mtu) { - const bool surround_encoder = use_surround_encoder(codec, flags & A2DP_CODEC_FLAG_SINK); + const bool surround_encoder = use_surround_encoder(codec, flags & MEDIA_CODEC_FLAG_SINK); a2dp_opus_05_t *conf = config; a2dp_opus_05_direction_t *dir; struct impl *this = NULL; @@ -1380,21 +1380,21 @@ .start_decode = codec_start_decode, \ .decode = codec_decode -const struct a2dp_codec a2dp_codec_opus_05 = { +const struct media_codec a2dp_codec_opus_05 = { OPUS_05_COMMON_FULL_DEFS, .id = SPA_BLUETOOTH_AUDIO_CODEC_OPUS_05, .name = "opus_05", .description = "Opus", }; -const struct a2dp_codec a2dp_codec_opus_05_51 = { +const struct media_codec a2dp_codec_opus_05_51 = { OPUS_05_COMMON_DEFS, .id = SPA_BLUETOOTH_AUDIO_CODEC_OPUS_05_51, .name = "opus_05_51", .description = "Opus 5.1 Surround", }; -const struct a2dp_codec a2dp_codec_opus_05_71 = { +const struct media_codec a2dp_codec_opus_05_71 = { OPUS_05_COMMON_DEFS, .id = SPA_BLUETOOTH_AUDIO_CODEC_OPUS_05_71, .name = "opus_05_71", @@ -1402,14 +1402,14 @@ }; /* Bidi return channel codec: doesn't have endpoints */ -const struct a2dp_codec a2dp_codec_opus_05_return = { +const struct media_codec a2dp_codec_opus_05_return = { OPUS_05_COMMON_FULL_DEFS, .id = 0, .name = "opus_05_duplex_bidi", .description = "Opus Duplex Bidi channel", }; -const struct a2dp_codec a2dp_codec_opus_05_duplex = { +const struct media_codec a2dp_codec_opus_05_duplex = { OPUS_05_COMMON_FULL_DEFS, .id = SPA_BLUETOOTH_AUDIO_CODEC_OPUS_05_DUPLEX, .name = "opus_05_duplex", @@ -1417,7 +1417,7 @@ .duplex_codec = &a2dp_codec_opus_05_return, }; -const struct a2dp_codec a2dp_codec_opus_05_pro = { +const struct media_codec a2dp_codec_opus_05_pro = { OPUS_05_COMMON_DEFS, .id = SPA_BLUETOOTH_AUDIO_CODEC_OPUS_05_PRO, .name = "opus_05_pro", @@ -1427,7 +1427,7 @@ .duplex_codec = &a2dp_codec_opus_05_return, }; -A2DP_CODEC_EXPORT_DEF( +MEDIA_CODEC_EXPORT_DEF( "opus", &a2dp_codec_opus_05, &a2dp_codec_opus_05_51,
View file
pipewire-0.3.58.tar.gz/spa/plugins/bluez5/a2dp-codec-sbc.c -> pipewire-0.3.59.tar.gz/spa/plugins/bluez5/a2dp-codec-sbc.c
Changed
@@ -33,7 +33,7 @@ #include <sbc/sbc.h> #include "rtp.h" -#include "a2dp-codecs.h" +#include "media-codecs.h" #define MAX_FRAME_COUNT 16 @@ -51,7 +51,7 @@ int max_bitpool; }; -static int codec_fill_caps(const struct a2dp_codec *codec, uint32_t flags, +static int codec_fill_caps(const struct media_codec *codec, uint32_t flags, uint8_t capsA2DP_MAX_CAPS_SIZE) { static const a2dp_sbc_t a2dp_sbc = { @@ -121,7 +121,7 @@ } -static const struct a2dp_codec_config +static const struct media_codec_config sbc_frequencies = { { SBC_SAMPLING_FREQ_48000, 48000, 3 }, { SBC_SAMPLING_FREQ_44100, 44100, 2 }, @@ -129,13 +129,13 @@ { SBC_SAMPLING_FREQ_16000, 16000, 0 }, }; -static const struct a2dp_codec_config +static const struct media_codec_config sbc_xq_frequencies = { { SBC_SAMPLING_FREQ_44100, 44100, 1 }, { SBC_SAMPLING_FREQ_48000, 48000, 0 }, }; -static const struct a2dp_codec_config +static const struct media_codec_config sbc_channel_modes = { { SBC_CHANNEL_MODE_JOINT_STEREO, 2, 3 }, { SBC_CHANNEL_MODE_STEREO, 2, 2 }, @@ -143,22 +143,22 @@ { SBC_CHANNEL_MODE_MONO, 1, 0 }, }; -static const struct a2dp_codec_config +static const struct media_codec_config sbc_xq_channel_modes = { { SBC_CHANNEL_MODE_DUAL_CHANNEL, 2, 2 }, { SBC_CHANNEL_MODE_JOINT_STEREO, 2, 1 }, { SBC_CHANNEL_MODE_STEREO, 2, 0 }, }; -static int codec_select_config(const struct a2dp_codec *codec, uint32_t flags, +static int codec_select_config(const struct media_codec *codec, uint32_t flags, const void *caps, size_t caps_size, - const struct a2dp_codec_audio_info *info, + const struct media_codec_audio_info *info, const struct spa_dict *settings, uint8_t configA2DP_MAX_CAPS_SIZE) { a2dp_sbc_t conf; int bitpool, i; size_t n; - const struct a2dp_codec_config *configs; + const struct media_codec_config *configs; bool xq = false; @@ -176,7 +176,7 @@ configs = sbc_frequencies; n = SPA_N_ELEMENTS(sbc_frequencies); } - if ((i = a2dp_codec_select_config(configs, n, conf.frequency, + if ((i = media_codec_select_config(configs, n, conf.frequency, info ? info->rate : A2DP_CODEC_DEFAULT_RATE )) < 0) return -ENOTSUP; @@ -189,7 +189,7 @@ configs = sbc_channel_modes; n = SPA_N_ELEMENTS(sbc_channel_modes); } - if ((i = a2dp_codec_select_config(configs, n, conf.channel_mode, + if ((i = media_codec_select_config(configs, n, conf.channel_mode, info ? info->channels : A2DP_CODEC_DEFAULT_CHANNELS )) < 0) return -ENOTSUP; @@ -229,8 +229,8 @@ return sizeof(conf); } -static int codec_caps_preference_cmp(const struct a2dp_codec *codec, uint32_t flags, const void *caps1, size_t caps1_size, - const void *caps2, size_t caps2_size, const struct a2dp_codec_audio_info *info, const struct spa_dict *global_settings) +static int codec_caps_preference_cmp(const struct media_codec *codec, uint32_t flags, const void *caps1, size_t caps1_size, + const void *caps2, size_t caps2_size, const struct media_codec_audio_info *info, const struct spa_dict *global_settings) { a2dp_sbc_t conf1, conf2; a2dp_sbc_t *conf; @@ -275,7 +275,7 @@ #undef PREFER_BOOL } -static int codec_validate_config(const struct a2dp_codec *codec, uint32_t flags, +static int codec_validate_config(const struct media_codec *codec, uint32_t flags, const void *caps, size_t caps_size, struct spa_audio_info *info) { @@ -356,7 +356,7 @@ return this->sbc.bitpool; } -static int codec_enum_config(const struct a2dp_codec *codec, uint32_t flags, +static int codec_enum_config(const struct media_codec *codec, uint32_t flags, const void *caps, size_t caps_size, uint32_t id, uint32_t idx, struct spa_pod_builder *b, struct spa_pod **param) { @@ -453,7 +453,7 @@ return this->codesize; } -static void *codec_init(const struct a2dp_codec *codec, uint32_t flags, +static void *codec_init(const struct media_codec *codec, uint32_t flags, void *config, size_t config_len, const struct spa_audio_info *info, void *props, size_t mtu) { @@ -638,7 +638,7 @@ return res; } -const struct a2dp_codec a2dp_codec_sbc = { +const struct media_codec a2dp_codec_sbc = { .id = SPA_BLUETOOTH_AUDIO_CODEC_SBC, .codec_id = A2DP_CODEC_SBC, .name = "sbc", @@ -660,7 +660,7 @@ .increase_bitpool = codec_increase_bitpool, }; -const struct a2dp_codec a2dp_codec_sbc_xq = { +const struct media_codec a2dp_codec_sbc_xq = { .id = SPA_BLUETOOTH_AUDIO_CODEC_SBC_XQ, .codec_id = A2DP_CODEC_SBC, .name = "sbc_xq", @@ -682,7 +682,7 @@ .increase_bitpool = codec_increase_bitpool, }; -A2DP_CODEC_EXPORT_DEF( +MEDIA_CODEC_EXPORT_DEF( "sbc", &a2dp_codec_sbc, &a2dp_codec_sbc_xq
View file
pipewire-0.3.58.tar.gz/spa/plugins/bluez5/backend-native.c -> pipewire-0.3.59.tar.gz/spa/plugins/bluez5/backend-native.c
Changed
@@ -78,7 +78,7 @@ struct spa_dbus *dbus; DBusConnection *conn; -#define DEFAULT_ENABLED_PROFILES (SPA_BT_PROFILE_HSP_HS | SPA_BT_PROFILE_HFP_AG) +#define DEFAULT_ENABLED_PROFILES (SPA_BT_PROFILE_HFP_HF | SPA_BT_PROFILE_HFP_AG) enum spa_bt_profile enabled_profiles; struct spa_source sco; @@ -259,6 +259,7 @@ #define RFCOMM_MESSAGE_MAX_LENGTH 256 +/* from HF/HS to AG */ SPA_PRINTF_FUNC(2, 3) static ssize_t rfcomm_send_cmd(const struct rfcomm *rfcomm, const char *format, ...) { @@ -279,7 +280,14 @@ spa_log_debug(backend->log, "RFCOMM >> %s", message); - messagelen = '\n'; + /* + * The format of an AT command from the HF to the AG shall be: <AT command><cr> + * - HFP 1.8, 4.34.1 + * + * The format for a command from the HS to the AG is thus: AT<cmd>=<value><cr> + * - HSP 1.2, 4.8.1 + */ + messagelen = '\r'; /* `message` is no longer null-terminated */ len = write(rfcomm->source.fd, message, len + 1); @@ -293,6 +301,7 @@ return len; } +/* from AG to HF/HS */ SPA_PRINTF_FUNC(2, 3) static ssize_t rfcomm_send_reply(const struct rfcomm *rfcomm, const char *format, ...) { @@ -313,6 +322,18 @@ spa_log_debug(backend->log, "RFCOMM >> %s", &message2); + /* + * The format of the OK code from the AG to the HF shall be: <cr><lf>OK<cr><lf> + * The format of the generic ERROR code from the AG to the HF shall be: <cr><lf>ERROR<cr><lf> + * The format of an unsolicited result code from the AG to the HF shall be: <cr><lf><result code><cr><lf> + * - HFP 1.8, 4.34.1 + * + * If the command is processed successfully, the resulting response from the AG to the HS is: <cr><lf>OK<cr><lf> + * If the command is not processed successfully, or is not recognized, + * the resulting response from the AG to the HS is: <cr><lf>ERROR<cr><lf> + * The format for an unsolicited result code (such as RING) from the AG to the HS is: <cr><lf><result code><cr><lf> + * - HSP 1.2, 4.8.1 + */ message0 = '\r'; message1 = '\n'; messagelen + 2 = '\r'; @@ -747,26 +768,21 @@ /* retrieve supported codecs */ /* response has the form AT+BAC=<codecID1>,<codecID2>,<codecIDx> strategy: split the string into tokens */ - static const char separators = "=,"; char* token; int cntr = 0; - token = strtok (buf, separators); - while (token != NULL) - { + while ((token = strsep(&buf, "=,"))) { + unsigned int codec_id; + /* skip token 0 i.e. the "AT+BAC=" part */ - if (cntr > 0) { - int codec_id; - sscanf (token, "%u", &codec_id); + if (cntr > 0 && sscanf(token, "%u", &codec_id) == 1) { spa_log_debug(backend->log, "RFCOMM AT+BAC found codec %u", codec_id); if (codec_id == HFP_AUDIO_CODEC_MSBC) { rfcomm->msbc_supported_by_hfp = true; spa_log_debug(backend->log, "RFCOMM headset supports mSBC codec"); } } - /* get next token */ - token = strtok (NULL, separators); cntr++; } @@ -809,7 +825,7 @@ } else if (!rfcomm->slc_configured) { spa_log_warn(backend->log, "RFCOMM receive command before SLC completed: %s", buf); rfcomm_send_reply(rfcomm, "ERROR"); - return false; + return true; } else if (sscanf(buf, "AT+BCS=%u", &selected_codec) == 1) { /* parse BCS(=Bluetooth Codec Selection) reply */ bool was_switching_codec = rfcomm->hfp_ag_switching_codec && (rfcomm->device != NULL); @@ -918,29 +934,18 @@ static bool rfcomm_hfp_hf(struct rfcomm *rfcomm, char* buf) { - static const char separators = "\r\n:"; - struct impl *backend = rfcomm->backend; unsigned int features; unsigned int gain; unsigned int selected_codec; char* token; - token = strtok(buf, separators); - while (token != NULL) - { - if (spa_strstartswith(token, "+BRSF")) { - /* get next token */ - token = strtok(NULL, separators); - features = atoi(token); + while ((token = strsep(&buf, "\r\n"))) { + if (sscanf(token, "+BRSF:%u", &features) == 1) { if (((features & (SPA_BT_HFP_AG_FEATURE_CODEC_NEGOTIATION)) != 0) && rfcomm->msbc_supported_by_hfp) rfcomm->codec_negotiation_supported = true; - } else if (spa_strstartswith(token, "+BCS") && rfcomm->codec_negotiation_supported) { - /* get next token */ - token = strtok(NULL, separators); - selected_codec = atoi(token); - + } else if (sscanf(token, "+BCS:%u", &selected_codec) == 1 && rfcomm->codec_negotiation_supported) { if (selected_codec != HFP_AUDIO_CODEC_CVSD && selected_codec != HFP_AUDIO_CODEC_MSBC) { spa_log_warn(backend->log, "unsupported codec negotiation: %d", selected_codec); } else { @@ -965,24 +970,13 @@ } } } - } else if (spa_strstartswith(token, "+CIND")) { - /* get next token and discard it */ - token = strtok(NULL, separators); - } else if (spa_strstartswith(token, "+VGM")) { - /* get next token */ - token = strtok(NULL, separators); - gain = atoi(token); - + } else if (sscanf(token, "+VGM%*1:=%u", &gain) == 1) { if (gain <= SPA_BT_VOLUME_HS_MAX) { rfcomm_emit_volume_changed(rfcomm, SPA_BT_VOLUME_ID_TX, gain); } else { spa_log_debug(backend->log, "RFCOMM receive unsupported VGM gain: %s", token); } - } else if (spa_strstartswith(token, "+VGS")) { - /* get next token */ - token = strtok(NULL, separators); - gain = atoi(token); - + } else if (sscanf(token, "+VGS%*1:=%u", &gain) == 1) { if (gain <= SPA_BT_VOLUME_HS_MAX) { rfcomm_emit_volume_changed(rfcomm, SPA_BT_VOLUME_ID_RX, gain); } else { @@ -1041,8 +1035,6 @@ break; } } - /* get next token */ - token = strtok(NULL, separators); } return true;
View file
pipewire-0.3.59.tar.gz/spa/plugins/bluez5/bap-codec-caps.h
Added
@@ -0,0 +1,113 @@ +/* Spa BAP codec API + * + * Copyright © 2022 Collabora + * + * 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_BLUEZ5_BAP_CODEC_CAPS_H_ +#define SPA_BLUEZ5_BAP_CODEC_CAPS_H_ + +#define BAP_CODEC_LC3 0x06 + +#define LC3_TYPE_FREQ 0x01 +#define LC3_FREQ_8KHZ (1 << 0) +#define LC3_FREQ_11KHZ (1 << 1) +#define LC3_FREQ_16KHZ (1 << 2) +#define LC3_FREQ_22KHZ (1 << 3) +#define LC3_FREQ_24KHZ (1 << 4) +#define LC3_FREQ_32KHZ (1 << 5) +#define LC3_FREQ_44KHZ (1 << 6) +#define LC3_FREQ_48KHZ (1 << 7) +#define LC3_FREQ_ANY (LC3_FREQ_8KHZ | \ + LC3_FREQ_11KHZ | \ + LC3_FREQ_16KHZ | \ + LC3_FREQ_22KHZ | \ + LC3_FREQ_24KHZ | \ + LC3_FREQ_32KHZ | \ + LC3_FREQ_44KHZ | \ + LC3_FREQ_48KHZ) + +#define LC3_TYPE_DUR 0x02 +#define LC3_DUR_7_5 (1 << 0) +#define LC3_DUR_10 (1 << 1) +#define LC3_DUR_ANY (LC3_DUR_7_5 | \ + LC3_DUR_10) + +#define LC3_TYPE_CHAN 0x03 +#define LC3_CHAN_1 (1 << 0) +#define LC3_CHAN_2 (1 << 1) + +#define LC3_TYPE_FRAMELEN 0x04 +#define LC3_TYPE_BLKS 0x05 + +/* LC3 config parameters */ +#define LC3_CONFIG_FREQ_8KHZ 0x01 +#define LC3_CONFIG_FREQ_11KHZ 0x02 +#define LC3_CONFIG_FREQ_16KHZ 0x03 +#define LC3_CONFIG_FREQ_22KHZ 0x04 +#define LC3_CONFIG_FREQ_24KHZ 0x05 +#define LC3_CONFIG_FREQ_32KHZ 0x06 +#define LC3_CONFIG_FREQ_44KHZ 0x07 +#define LC3_CONFIG_FREQ_48KHZ 0x08 + +#define LC3_CONFIG_DURATION_7_5 0x00 +#define LC3_CONFIG_DURATION_10 0x01 + +#define LC3_CONFIG_CHNL_NOT_ALLOWED 0x00000000 +#define LC3_CONFIG_CHNL_FL 0x00000001 /* front left */ +#define LC3_CONFIG_CHNL_FR 0x00000002 /* front right */ +#define LC3_CONFIG_CHNL_FC 0x00000004 /* front center */ +#define LC3_CONFIG_CHNL_LFE 0x00000008 /* LFE */ +#define LC3_CONFIG_CHNL_BL 0x00000010 /* back left */ +#define LC3_CONFIG_CHNL_BR 0x00000020 /* back right */ +#define LC3_CONFIG_CHNL_FLC 0x00000040 /* front left center */ +#define LC3_CONFIG_CHNL_FRC 0x00000080 /* front right center */ +#define LC3_CONFIG_CHNL_BC 0x00000100 /* back center */ +#define LC3_CONFIG_CHNL_LFE2 0x00000200 /* LFE 2 */ +#define LC3_CONFIG_CHNL_SL 0x00000400 /* side left */ +#define LC3_CONFIG_CHNL_SR 0x00000800 /* side right */ +#define LC3_CONFIG_CHNL_TFL 0x00001000 /* top front left */ +#define LC3_CONFIG_CHNL_TFR 0x00002000 /* top front right */ +#define LC3_CONFIG_CHNL_TFC 0x00004000 /* top front center */ +#define LC3_CONFIG_CHNL_TC 0x00008000 /* top center */ +#define LC3_CONFIG_CHNL_TBL 0x00010000 /* top back left */ +#define LC3_CONFIG_CHNL_TBR 0x00020000 /* top back right */ +#define LC3_CONFIG_CHNL_TSL 0x00040000 /* top side left */ +#define LC3_CONFIG_CHNL_TSR 0x00080000 /* top side right */ +#define LC3_CONFIG_CHNL_TBC 0x00100000 /* top back center */ +#define LC3_CONFIG_CHNL_BFC 0x00200000 /* bottom front center */ +#define LC3_CONFIG_CHNL_BFL 0x00400000 /* bottom front left */ +#define LC3_CONFIG_CHNL_BFR 0x00800000 /* bottom front right */ +#define LC3_CONFIG_CHNL_FLW 0x01000000 /* front left wide */ +#define LC3_CONFIG_CHNL_FRW 0x02000000 /* front right wide */ +#define LC3_CONFIG_CHNL_LS 0x04000000 /* left surround */ +#define LC3_CONFIG_CHNL_RS 0x08000000 /* right surround */ + +#define LC3_MAX_CHANNELS 28 + +typedef struct { + uint8_t rate; + uint8_t frame_duration; + uint32_t channels; + uint16_t framelen; + uint8_t n_blks; +} __attribute__ ((packed)) bap_lc3_t; + +#endif
View file
pipewire-0.3.59.tar.gz/spa/plugins/bluez5/bap-codec-lc3.c
Added
@@ -0,0 +1,772 @@ +/* Spa BAP LC3 codec + * + * Copyright © 2020 Wim Taymans + * Copyright © 2022 Pauli Virtanen + * Copyright © 2022 Collabora + * + * 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 <bits/stdint-uintn.h> +#include <string.h> +#include <unistd.h> +#include <stddef.h> +#include <errno.h> +#include <arpa/inet.h> +#include <bluetooth/bluetooth.h> + +#include <spa/param/audio/format.h> +#include <spa/param/audio/format-utils.h> + +#include <lc3.h> + +#include "media-codecs.h" +#include "bap-codec-caps.h" + +struct impl { + lc3_encoder_t encLC3_MAX_CHANNELS; + lc3_decoder_t decLC3_MAX_CHANNELS; + + int mtu; + int samplerate; + int channels; + int frame_dus; + int framelen; + int samples; + unsigned int codesize; +}; + +struct ltv { + uint8_t len; + uint8_t type; + uint8_t value0; +} __packed; + +static int write_ltv(uint8_t *dest, uint8_t type, void* value, size_t len) +{ + struct ltv *ltv = (struct ltv *)dest; + + ltv->len = len + 1; + ltv->type = type; + memcpy(ltv->value, value, len); + + return len + 2; +} + +static int write_ltv_uint8(uint8_t *dest, uint8_t type, uint8_t value) +{ + return write_ltv(dest, type, &value, sizeof(value)); +} + +static int write_ltv_uint16(uint8_t *dest, uint8_t type, uint16_t value) +{ + return write_ltv(dest, type, &value, sizeof(value)); +} + +static int write_ltv_uint32(uint8_t *dest, uint8_t type, uint32_t value) +{ + return write_ltv(dest, type, &value, sizeof(value)); +} + +static int codec_fill_caps(const struct media_codec *codec, uint32_t flags, + uint8_t capsA2DP_MAX_CAPS_SIZE) +{ + uint8_t *data = caps; + uint16_t framelen2 = {htobs(LC3_MIN_FRAME_BYTES), htobs(LC3_MAX_FRAME_BYTES)}; + + data += write_ltv_uint16(data, LC3_TYPE_FREQ, + htobs(LC3_FREQ_48KHZ | LC3_FREQ_24KHZ | LC3_FREQ_16KHZ | LC3_FREQ_8KHZ)); + data += write_ltv_uint8(data, LC3_TYPE_DUR, LC3_DUR_ANY); + data += write_ltv_uint8(data, LC3_TYPE_CHAN, LC3_CHAN_1 | LC3_CHAN_2); + data += write_ltv(data, LC3_TYPE_FRAMELEN, framelen, sizeof(framelen)); + data += write_ltv_uint8(data, LC3_TYPE_BLKS, 2); + + return data - caps; +} + +static bool parse_capabilities(bap_lc3_t *conf, const uint8_t *data, size_t data_size) +{ + uint16_t framelen_min = 0, framelen_max = 0; + + if (!data_size) + return false; + memset(conf, 0, sizeof(*conf)); + + conf->frame_duration = 0xFF; + + while (data_size > 0) { + struct ltv *ltv = (struct ltv *)data; + + if (ltv->len > data_size) + return false; + + switch (ltv->type) { + case LC3_TYPE_FREQ: + spa_return_val_if_fail(ltv->len == 3, false); + { + uint16_t rate = ltv->value0 + (ltv->value1 << 8); + if (rate & LC3_FREQ_48KHZ) + conf->rate = LC3_CONFIG_FREQ_48KHZ; + else if (rate & LC3_FREQ_24KHZ) + conf->rate = LC3_CONFIG_FREQ_24KHZ; + else if (rate & LC3_FREQ_16KHZ) + conf->rate = LC3_CONFIG_FREQ_16KHZ; + else if (rate & LC3_FREQ_8KHZ) + conf->rate = LC3_CONFIG_FREQ_8KHZ; + else + return false; + } + break; + case LC3_TYPE_DUR: + spa_return_val_if_fail(ltv->len == 2, false); + { + uint8_t duration = ltv->value0; + if (duration & LC3_DUR_10) + conf->frame_duration = LC3_CONFIG_DURATION_10; + else if (duration & LC3_DUR_7_5) + conf->frame_duration = LC3_CONFIG_DURATION_7_5; + else + return false; + } + break; + case LC3_TYPE_CHAN: + spa_return_val_if_fail(ltv->len == 2, false); + { + uint8_t channels = ltv->value0; + /* Only mono or stereo streams are currently supported, + * in both case Audio location is defined as both Front Left + * and Front Right, difference is done by the n_blks parameter. + */ + if ((channels & LC3_CHAN_2) || (channels & LC3_CHAN_1)) + conf->channels = LC3_CONFIG_CHNL_FR | LC3_CONFIG_CHNL_FL; + else + return false; + } + break; + case LC3_TYPE_FRAMELEN: + spa_return_val_if_fail(ltv->len == 5, false); + framelen_min = ltv->value0 + (ltv->value1 << 8); + framelen_max = ltv->value2 + (ltv->value3 << 8); + break; + case LC3_TYPE_BLKS: + spa_return_val_if_fail(ltv->len == 2, false); + conf->n_blks = ltv->value0; + if (!conf->n_blks) + return false; + break; + default: + return false; + } + data_size -= ltv->len + 1; + data += ltv->len + 1; + } + + if (framelen_min < LC3_MIN_FRAME_BYTES || framelen_max > LC3_MAX_FRAME_BYTES) + return false; + if (conf->frame_duration == 0xFF || !conf->rate) + return false; + if (!conf->channels) + conf->channels = LC3_CONFIG_CHNL_FL; + + switch (conf->rate) { + case LC3_CONFIG_FREQ_48KHZ: + if (conf->frame_duration == LC3_CONFIG_DURATION_7_5) + conf->framelen = 117; + else + conf->framelen = 120; + break; + case LC3_CONFIG_FREQ_24KHZ: + if (conf->frame_duration == LC3_CONFIG_DURATION_7_5) + conf->framelen = 45; + else + conf->framelen = 60; + break; + case LC3_CONFIG_FREQ_16KHZ: + if (conf->frame_duration == LC3_CONFIG_DURATION_7_5) + conf->framelen = 30; + else + conf->framelen = 40; + break; + case LC3_CONFIG_FREQ_8KHZ: + if (conf->frame_duration == LC3_CONFIG_DURATION_7_5) + conf->framelen = 26; + else + conf->framelen = 30; + break; + default: + return false; + } + + return true; +} + +static bool parse_conf(bap_lc3_t *conf, const uint8_t *data, size_t data_size) +{ + if (!data_size) + return false; + memset(conf, 0, sizeof(*conf)); + + conf->frame_duration = 0xFF; + + while (data_size > 0) { + struct ltv *ltv = (struct ltv *)data; + + if (ltv->len > data_size) + return false; + + switch (ltv->type) { + case LC3_TYPE_FREQ: + spa_return_val_if_fail(ltv->len == 2, false); + conf->rate = ltv->value0; + break; + case LC3_TYPE_DUR: + spa_return_val_if_fail(ltv->len == 2, false); + conf->frame_duration = ltv->value0; + break; + case LC3_TYPE_CHAN: + spa_return_val_if_fail(ltv->len == 5, false); + conf->channels = ltv->value0 + (ltv->value1 << 8) + (ltv->value2 << 16) + (ltv->value3 << 24); + break; + case LC3_TYPE_FRAMELEN: + spa_return_val_if_fail(ltv->len == 3, false); + conf->framelen = ltv->value0 + (ltv->value1 << 8); + break; + case LC3_TYPE_BLKS: + spa_return_val_if_fail(ltv->len == 2, false); + conf->n_blks = ltv->value0; + if (!conf->n_blks) + return false; + break; + default: + return false; + } + data_size -= ltv->len + 1; + data += ltv->len + 1; + } + + if (conf->frame_duration == 0xFF || !conf->rate) + return false; + + return true; +} + +static int codec_select_config(const struct media_codec *codec, uint32_t flags, + const void *caps, size_t caps_size, + const struct media_codec_audio_info *info, + const struct spa_dict *settings, uint8_t configA2DP_MAX_CAPS_SIZE) +{ + bap_lc3_t conf; + uint8_t *data = config; + + if (caps == NULL) + return -EINVAL; + + if (!parse_capabilities(&conf, caps, caps_size)) + return -ENOTSUP; + + data += write_ltv_uint8(data, LC3_TYPE_FREQ, conf.rate); + data += write_ltv_uint8(data, LC3_TYPE_DUR, conf.frame_duration); + data += write_ltv_uint32(data, LC3_TYPE_CHAN, htobl(conf.channels)); + data += write_ltv_uint16(data, LC3_TYPE_FRAMELEN, htobs(conf.framelen)); + data += write_ltv_uint8(data, LC3_TYPE_BLKS, conf.n_blks); + + return data - config; +} + +static int codec_caps_preference_cmp(const struct media_codec *codec, uint32_t flags, const void *caps1, size_t caps1_size, + const void *caps2, size_t caps2_size, const struct media_codec_audio_info *info, const struct spa_dict *global_settings) +{ + bap_lc3_t conf1, conf2; + bap_lc3_t *conf; + int res1, res2; + int a, b; + + /* Order selected configurations by preference */ + res1 = codec->select_config(codec, 0, caps1, caps1_size, info, NULL, (uint8_t *)&conf1); + res2 = codec->select_config(codec, 0, caps2, caps2_size, info , NULL, (uint8_t *)&conf2); + +#define PREFER_EXPR(expr) \ + do { \ + conf = &conf1; \ + a = (expr); \ + conf = &conf2; \ + b = (expr); \ + if (a != b) \ + return b - a; \ + } while (0) + +#define PREFER_BOOL(expr) PREFER_EXPR((expr) ? 1 : 0) + + /* Prefer valid */ + a = (res1 > 0 && (size_t)res1 == sizeof(bap_lc3_t)) ? 1 : 0; + b = (res2 > 0 && (size_t)res2 == sizeof(bap_lc3_t)) ? 1 : 0; + if (!a || !b) + return b - a; + + PREFER_BOOL(conf->channels & LC3_CHAN_2); + PREFER_BOOL(conf->rate & (LC3_CONFIG_FREQ_48KHZ | LC3_CONFIG_FREQ_24KHZ | LC3_CONFIG_FREQ_16KHZ | LC3_CONFIG_FREQ_8KHZ)); + PREFER_BOOL(conf->rate & LC3_CONFIG_FREQ_48KHZ); + + return 0; + +#undef PREFER_EXPR +#undef PREFER_BOOL +} + +static uint8_t channels_to_positions(uint32_t channels, uint8_t n_channels, uint32_t *position) +{ + uint8_t n_positions = 0; + + spa_assert(n_channels <= SPA_AUDIO_MAX_CHANNELS); + + /* First check if stream is configure for Mono, i.e. 1 block for both Front + * Left anf Front Right, + * else map LE Audio locations to PipeWire locations in the ascending order + * which will be used as block order in stream. + */ + if ((channels & (LC3_CONFIG_CHNL_FR | LC3_CONFIG_CHNL_FL)) == (LC3_CONFIG_CHNL_FR | LC3_CONFIG_CHNL_FL) && + n_channels == 1) { + position0 = SPA_AUDIO_CHANNEL_MONO; + n_positions = 1; + } else { +#define CHANNEL_2_SPACHANNEL(channel,spa_channel) if (channels & channel) positionn_positions++ = spa_channel; + + CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_FL, SPA_AUDIO_CHANNEL_FL); + CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_FR, SPA_AUDIO_CHANNEL_FR); + CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_FC, SPA_AUDIO_CHANNEL_FC); + CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_LFE, SPA_AUDIO_CHANNEL_LFE); + CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_BL, SPA_AUDIO_CHANNEL_RL); + CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_BR, SPA_AUDIO_CHANNEL_RR); + CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_FLC, SPA_AUDIO_CHANNEL_FLC); + CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_FRC, SPA_AUDIO_CHANNEL_FRC); + CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_BC, SPA_AUDIO_CHANNEL_BC); + CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_LFE2, SPA_AUDIO_CHANNEL_LFE2); + CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_SL, SPA_AUDIO_CHANNEL_SL); + CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_SR, SPA_AUDIO_CHANNEL_SR); + CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_TFL, SPA_AUDIO_CHANNEL_TFL); + CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_TFR, SPA_AUDIO_CHANNEL_TFR); + CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_TFC, SPA_AUDIO_CHANNEL_TFC); + CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_TC, SPA_AUDIO_CHANNEL_TC); + CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_TBL, SPA_AUDIO_CHANNEL_TRL); + CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_TBR, SPA_AUDIO_CHANNEL_TRR); + CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_TSL, SPA_AUDIO_CHANNEL_TSL); + CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_TSR, SPA_AUDIO_CHANNEL_TSR); + CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_TBC, SPA_AUDIO_CHANNEL_TRC); + CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_BFC, SPA_AUDIO_CHANNEL_BC); + CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_BFL, SPA_AUDIO_CHANNEL_BLC); + CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_BFR, SPA_AUDIO_CHANNEL_BRC); + CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_FLW, SPA_AUDIO_CHANNEL_FLW); + CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_FRW, SPA_AUDIO_CHANNEL_FRW); + CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_LS, SPA_AUDIO_CHANNEL_LLFE); /* is it the right mapping? */ + CHANNEL_2_SPACHANNEL(LC3_CONFIG_CHNL_RS, SPA_AUDIO_CHANNEL_RLFE); /* is it the right mapping? */ + +#undef CHANNEL_2_SPACHANNEL + } + + return n_positions; +} + +static int codec_enum_config(const struct media_codec *codec, uint32_t flags, + const void *caps, size_t caps_size, uint32_t id, uint32_t idx, + struct spa_pod_builder *b, struct spa_pod **param) +{ + bap_lc3_t conf; + struct spa_pod_frame f2; + struct spa_pod_choice *choice; + uint32_t positionSPA_AUDIO_MAX_CHANNELS; + uint32_t i = 0; + uint8_t res; + + if (!parse_conf(&conf, caps, caps_size)) + return -EINVAL; + + if (idx > 0) + return 0; + + spa_pod_builder_push_object(b, &f0, SPA_TYPE_OBJECT_Format, id); + spa_pod_builder_add(b, + SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_audio), + SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw), + SPA_FORMAT_AUDIO_format, SPA_POD_Id(SPA_AUDIO_FORMAT_S24_32), + 0); + spa_pod_builder_prop(b, SPA_FORMAT_AUDIO_rate, 0); + + spa_pod_builder_push_choice(b, &f1, SPA_CHOICE_None, 0); + choice = (struct spa_pod_choice*)spa_pod_builder_frame(b, &f1); + i = 0; + if (conf.rate & LC3_CONFIG_FREQ_48KHZ) { + if (i++ == 0) + spa_pod_builder_int(b, 48000); + spa_pod_builder_int(b, 48000); + } + if (conf.rate & LC3_CONFIG_FREQ_24KHZ) { + if (i++ == 0) + spa_pod_builder_int(b, 24000); + spa_pod_builder_int(b, 24000); + } + if (conf.rate & LC3_CONFIG_FREQ_16KHZ) { + if (i++ == 0) + spa_pod_builder_int(b, 16000); + spa_pod_builder_int(b, 16000); + } + if (conf.rate & LC3_CONFIG_FREQ_8KHZ) { + if (i++ == 0) + spa_pod_builder_int(b, 8000); + spa_pod_builder_int(b, 8000); + } + if (i == 0) + return -EINVAL; + if (i > 1) + choice->body.type = SPA_CHOICE_Enum; + spa_pod_builder_pop(b, &f1); + + res = channels_to_positions(conf.channels, conf.n_blks, position); + if (res == 0) + return -EINVAL; + spa_pod_builder_add(b, + SPA_FORMAT_AUDIO_channels, SPA_POD_Int(res), + SPA_FORMAT_AUDIO_position, SPA_POD_Array(sizeof(uint32_t), + SPA_TYPE_Id, res, position), + 0); + + *param = spa_pod_builder_pop(b, &f0); + return *param == NULL ? -EIO : 1; +} + +static int codec_validate_config(const struct media_codec *codec, uint32_t flags, + const void *caps, size_t caps_size, + struct spa_audio_info *info) +{ + bap_lc3_t conf; + uint8_t res; + + if (caps == NULL) + return -EINVAL; + + if (!parse_conf(&conf, caps, caps_size)) + return -ENOTSUP; + + spa_zero(*info); + info->media_type = SPA_MEDIA_TYPE_audio; + info->media_subtype = SPA_MEDIA_SUBTYPE_raw; + info->info.raw.format = SPA_AUDIO_FORMAT_S24_32; + + switch (conf.rate) { + case LC3_CONFIG_FREQ_48KHZ: + info->info.raw.rate = 48000U; + break; + case LC3_CONFIG_FREQ_24KHZ: + info->info.raw.rate = 24000U; + break; + case LC3_CONFIG_FREQ_16KHZ: + info->info.raw.rate = 16000U; + break; + case LC3_CONFIG_FREQ_8KHZ: + info->info.raw.rate = 8000U; + break; + default: + return -EINVAL; + } + + res = channels_to_positions(conf.channels, conf.n_blks, info->info.raw.position); + if (res == 0) + return -EINVAL; + info->info.raw.channels = res; + + switch (conf.frame_duration) { + case LC3_CONFIG_DURATION_10: + case LC3_CONFIG_DURATION_7_5: + break; + default: + return -EINVAL; + } + + return 0; +} + +static void codec_get_qos(const struct media_codec *codec, + const void *config, size_t config_size, + struct codec_qos *qos) +{ + bap_lc3_t conf; + + memset(qos, 0, sizeof(*qos)); + + if (!parse_conf(&conf, config, config_size)) + return; + + qos->framing = false; + qos->phy = "2M"; + qos->retransmission = 2; /* default */ + qos->sdu = conf.framelen * conf.n_blks; + qos->latency = 20; /* default */ + qos->delay = 40000U; + qos->interval = (conf.frame_duration == LC3_CONFIG_DURATION_7_5 ? 7500 : 10000); + + switch (conf.rate) { + case LC3_CONFIG_FREQ_8KHZ: + case LC3_CONFIG_FREQ_16KHZ: + case LC3_CONFIG_FREQ_24KHZ: + case LC3_CONFIG_FREQ_32KHZ: + qos->retransmission = 2; + qos->latency = (conf.frame_duration == LC3_CONFIG_DURATION_7_5 ? 8 : 10); + break; + case LC3_CONFIG_FREQ_48KHZ: + qos->retransmission = 5; + qos->latency = (conf.frame_duration == LC3_CONFIG_DURATION_7_5 ? 15 : 20); + break; + } +} + +static void *codec_init(const struct media_codec *codec, uint32_t flags, + void *config, size_t config_len, const struct spa_audio_info *info, + void *props, size_t mtu) +{ + bap_lc3_t conf; + struct impl *this = NULL; + struct spa_audio_info config_info; + int res, ich; + + if (info->media_type != SPA_MEDIA_TYPE_audio || + info->media_subtype != SPA_MEDIA_SUBTYPE_raw || + info->info.raw.format != SPA_AUDIO_FORMAT_S24_32) { + res = -EINVAL; + goto error; + } + + if ((this = calloc(1, sizeof(struct impl))) == NULL) + goto error_errno; + + if ((res = codec_validate_config(codec, flags, config, config_len, &config_info)) < 0) + goto error; + + if (!parse_conf(&conf, config, config_len)) { + res = -ENOTSUP; + goto error; + } + + this->mtu = mtu; + this->samplerate = config_info.info.raw.rate; + this->channels = config_info.info.raw.channels; + this->framelen = conf.framelen; + + switch (conf.frame_duration) { + case LC3_CONFIG_DURATION_10: + this->frame_dus = 10000; + break; + case LC3_CONFIG_DURATION_7_5: + this->frame_dus = 7500; + break; + default: + res = -EINVAL; + goto error; + } + + this->samples = lc3_frame_samples(this->frame_dus, this->samplerate); + if (this->samples < 0) { + res = -EINVAL; + goto error; + } + this->codesize = this->samples * this->channels * sizeof(int32_t); + + if (flags & MEDIA_CODEC_FLAG_SINK) { + for (ich = 0; ich < this->channels; ich++) { + this->encich = lc3_setup_encoder(this->frame_dus, this->samplerate, 0, calloc(1, lc3_encoder_size(this->frame_dus, this->samplerate))); + if (this->encich == NULL) { + res = -EINVAL; + goto error; + } + } + } else { + for (ich = 0; ich < this->channels; ich++) { + this->decich = lc3_setup_decoder(this->frame_dus, this->samplerate, 0, calloc(1, lc3_decoder_size(this->frame_dus, this->samplerate))); + if (this->decich == NULL) { + res = -EINVAL; + goto error; + } + } + } + + return this; + +error_errno: + res = -errno; + goto error; + +error: + if (this) { + for (ich = 0; ich < this->channels; ich++) { + if (this->encich) + free(this->encich); + if (this->decich) + free(this->decich); + } + } + free(this); + errno = -res; + return NULL; +} + +static void codec_deinit(void *data) +{ + struct impl *this = data; + int ich; + + for (ich = 0; ich < this->channels; ich++) { + if (this->encich) + free(this->encich); + if (this->decich) + free(this->decich); + } + free(this); +} + +static int codec_get_block_size(void *data) +{ + struct impl *this = data; + return this->codesize; +} + +static int codec_abr_process (void *data, size_t unsent) +{ + return -ENOTSUP; +} + +static int codec_start_encode (void *data, + void *dst, size_t dst_size, uint16_t seqnum, uint32_t timestamp) +{ + return 0; +} + +static int codec_encode(void *data, + const void *src, size_t src_size, + void *dst, size_t dst_size, + size_t *dst_out, int *need_flush) +{ + struct impl *this = data; + int frame_bytes; + int ich, res; + int size, processed; + + frame_bytes = lc3_frame_bytes(this->frame_dus, this->samplerate); + processed = 0; + size = 0; + + if (src_size < (size_t)this->codesize) + goto done; + if (dst_size < (size_t)frame_bytes) + goto done; + + for (ich = 0; ich < this->channels; ich++) { + uint8_t *in = (uint8_t *)src + (ich * 4); + uint8_t *out = (uint8_t *)dst + ich * this->framelen; + res = lc3_encode(this->encich, LC3_PCM_FORMAT_S24, in, this->channels, this->framelen, out); + size += this->framelen; + if (SPA_UNLIKELY(res != 0)) + return -EINVAL; + } + *dst_out = size; + + processed += this->codesize; + +done: + spa_assert(size <= this->mtu); + *need_flush = NEED_FLUSH_ALL; + + return processed; +} + +static SPA_UNUSED int codec_start_decode (void *data, + const void *src, size_t src_size, uint16_t *seqnum, uint32_t *timestamp) +{ + return 0; +} + +static SPA_UNUSED int codec_decode(void *data, + const void *src, size_t src_size, + void *dst, size_t dst_size, + size_t *dst_out) +{ + struct impl *this = data; + int ich, res; + int consumed; + int samples; + + spa_return_val_if_fail((size_t)(this->framelen * this->channels) == src_size, -EINVAL); + consumed = 0; + + samples = lc3_frame_samples(this->frame_dus, this->samplerate); + if (samples == -1) + return -EINVAL; + if (dst_size < this->codesize) + return -EINVAL; + + for (ich = 0; ich < this->channels; ich++) { + uint8_t *in = (uint8_t *)src + ich * this->framelen; + uint8_t *out = (uint8_t *)dst + (ich * 4); + res = lc3_decode(this->decich, in, this->framelen, LC3_PCM_FORMAT_S24, out, this->channels); + if (SPA_UNLIKELY(res < 0)) + return -EINVAL; + consumed += this->framelen; + } + + *dst_out = this->codesize; + + return consumed; +} + +static int codec_reduce_bitpool(void *data) +{ + return -ENOTSUP; +} + +static int codec_increase_bitpool(void *data) +{ + return -ENOTSUP; +} + +const struct media_codec bap_codec_lc3 = { + .id = SPA_BLUETOOTH_AUDIO_CODEC_LC3, + .name = "lc3", + .codec_id = BAP_CODEC_LC3, + .bap = true, + .description = "LC3", + .fill_caps = codec_fill_caps, + .select_config = codec_select_config, + .enum_config = codec_enum_config, + .validate_config = codec_validate_config, + .get_qos = codec_get_qos, + .caps_preference_cmp = codec_caps_preference_cmp, + .init = codec_init, + .deinit = codec_deinit, + .get_block_size = codec_get_block_size, + .abr_process = codec_abr_process, + .start_encode = codec_start_encode, + .encode = codec_encode, + .start_decode = codec_start_decode, + .decode = codec_decode, + .reduce_bitpool = codec_reduce_bitpool, + .increase_bitpool = codec_increase_bitpool +}; + +MEDIA_CODEC_EXPORT_DEF( + "lc3", + &bap_codec_lc3 +);
View file
pipewire-0.3.58.tar.gz/spa/plugins/bluez5/bluez5-dbus.c -> pipewire-0.3.59.tar.gz/spa/plugins/bluez5/bluez5-dbus.c
Changed
@@ -100,7 +100,7 @@ uint32_t id; - const struct a2dp_codec * const * a2dp_codecs; + const struct media_codec * const * media_codecs; /* * Lists of BlueZ objects, kept up-to-date by following DBus events @@ -130,7 +130,9 @@ struct spa_dict global_settings; /* A reference audio info for A2DP codec configuration. */ - struct a2dp_codec_audio_info default_audio_info; + struct media_codec_audio_info default_audio_info; + + bool le_audio_supported; }; /* Stream endpoints owned by BlueZ for each device */ @@ -146,6 +148,7 @@ uint8_t *capabilities; int capabilities_len; bool delay_reporting; + bool acceptor; }; /* @@ -155,7 +158,7 @@ * with the desired capabilities. * The codec switch struct tracks candidates still to be tried. */ -struct spa_bt_a2dp_codec_switch { +struct spa_bt_media_codec_switch { struct spa_bt_device *device; struct spa_list device_link; @@ -172,10 +175,10 @@ * Called asynchronously, so endpoint paths instead of pointers (which may be * invalidated in the meantime). */ - const struct a2dp_codec **codecs; + const struct media_codec **codecs; char **paths; - const struct a2dp_codec **codec_iter; /**< outer iterator over codecs */ + const struct media_codec **codec_iter; /**< outer iterator over codecs */ char **path_iter; /**< inner iterator over endpoint paths */ uint16_t retries; @@ -430,10 +433,17 @@ } } -static int a2dp_codec_to_endpoint(const struct a2dp_codec *codec, - const char * endpoint, +static int media_codec_to_endpoint(const struct media_codec *codec, + enum spa_bt_media_direction direction, char** object_path) { + const char * endpoint; + + if (direction == SPA_BT_MEDIA_SOURCE) + endpoint = codec->bap ? BAP_SOURCE_ENDPOINT : A2DP_SOURCE_ENDPOINT; + else + endpoint = codec->bap ? BAP_SINK_ENDPOINT : A2DP_SINK_ENDPOINT; + *object_path = spa_aprintf("%s/%s", endpoint, codec->endpoint_name ? codec->endpoint_name : codec->name); if (*object_path == NULL) @@ -441,10 +451,10 @@ return 0; } -static const struct a2dp_codec *a2dp_endpoint_to_codec(struct spa_bt_monitor *monitor, const char *endpoint, bool *sink) +static const struct media_codec *media_endpoint_to_codec(struct spa_bt_monitor *monitor, const char *endpoint, bool *sink) { const char *ep_name; - const struct a2dp_codec * const * const a2dp_codecs = monitor->a2dp_codecs; + const struct media_codec * const * const media_codecs = monitor->media_codecs; int i; if (spa_strstartswith(endpoint, A2DP_SINK_ENDPOINT "/")) { @@ -453,12 +463,19 @@ } else if (spa_strstartswith(endpoint, A2DP_SOURCE_ENDPOINT "/")) { ep_name = endpoint + strlen(A2DP_SOURCE_ENDPOINT "/"); *sink = false; + } else if (spa_strstartswith(endpoint, BAP_SOURCE_ENDPOINT "/")) { + ep_name = endpoint + strlen(BAP_SOURCE_ENDPOINT "/"); + *sink = false; + } else if (spa_strstartswith(endpoint, BAP_SINK_ENDPOINT "/")) { + ep_name = endpoint + strlen(BAP_SINK_ENDPOINT "/"); + *sink = true; } else { + *sink = true; return NULL; } - for (i = 0; a2dp_codecsi; i++) { - const struct a2dp_codec *codec = a2dp_codecsi; + for (i = 0; media_codecsi; i++) { + const struct media_codec *codec = media_codecsi; const char *codec_ep_name = codec->endpoint_name ? codec->endpoint_name : codec->name; if (spa_streq(ep_name, codec_ep_name)) @@ -467,18 +484,22 @@ return NULL; } -static int a2dp_endpoint_to_profile(const char *endpoint) +static int media_endpoint_to_profile(const char *endpoint) { if (spa_strstartswith(endpoint, A2DP_SINK_ENDPOINT "/")) return SPA_BT_PROFILE_A2DP_SOURCE; else if (spa_strstartswith(endpoint, A2DP_SOURCE_ENDPOINT "/")) return SPA_BT_PROFILE_A2DP_SINK; + else if (spa_strstartswith(endpoint, BAP_SINK_ENDPOINT "/")) + return SPA_BT_PROFILE_BAP_SOURCE; + else if (spa_strstartswith(endpoint, BAP_SOURCE_ENDPOINT "/")) + return SPA_BT_PROFILE_BAP_SINK; else return SPA_BT_PROFILE_NULL; } -static bool is_a2dp_codec_enabled(struct spa_bt_monitor *monitor, const struct a2dp_codec *codec) +static bool is_media_codec_enabled(struct spa_bt_monitor *monitor, const struct media_codec *codec) { return spa_dict_lookup(&monitor->enabled_codecs, codec->name) != NULL; } @@ -492,7 +513,7 @@ DBusMessage *r; DBusError err; int size, res; - const struct a2dp_codec *codec; + const struct media_codec *codec; bool sink; dbus_error_init(&err); @@ -508,14 +529,14 @@ spa_log_info(monitor->log, "%p: %s select conf %d", monitor, path, size); spa_log_hexdump(monitor->log, SPA_LOG_LEVEL_DEBUG, 2, cap, (size_t)size); - codec = a2dp_endpoint_to_codec(monitor, path, &sink); + codec = media_endpoint_to_codec(monitor, path, &sink); if (codec != NULL) /* FIXME: We can't determine which device the SelectConfiguration() * call is associated with, therefore device settings are not passed. * This causes inconsistency with SelectConfiguration() triggered * by codec switching. */ - res = codec->select_config(codec, sink ? A2DP_CODEC_FLAG_SINK : 0, cap, size, &monitor->default_audio_info, + res = codec->select_config(codec, sink ? MEDIA_CODEC_FLAG_SINK : 0, cap, size, &monitor->default_audio_info, &monitor->global_settings, config); else res = -ENOTSUP; @@ -545,6 +566,171 @@ return DBUS_HANDLER_RESULT_HANDLED; } +static void append_basic_variant_dict_entry(DBusMessageIter *dict, const char* key, int variant_type_int, const char* variant_type_str, void* variant); +static void append_basic_array_variant_dict_entry(DBusMessageIter *dict, const char* key, const char* variant_type_str, const char* array_type_str, int array_type_int, void* data, int data_size); +static struct spa_bt_remote_endpoint *remote_endpoint_find(struct spa_bt_monitor *monitor, const char *path); + +static DBusHandlerResult endpoint_select_properties(DBusConnection *conn, DBusMessage *m, void *userdata) +{ + struct spa_bt_monitor *monitor = userdata; + const char *path; + const char *object_path; + DBusMessageIter args, props, iter; + DBusMessage *r = NULL; + int size, res; + const struct media_codec *codec; + bool sink; + + if (!dbus_message_iter_init(m, &args) || !spa_streq(dbus_message_get_signature(m), "a{sv}")) { + spa_log_error(monitor->log, "Invalid signature for method SelectProperties()"); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + + dbus_message_iter_recurse(&args, &props); + if (dbus_message_iter_get_arg_type(&props) != DBUS_TYPE_DICT_ENTRY) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + path = dbus_message_get_path(m); + + codec = media_endpoint_to_codec(monitor, path, &sink); + if (!codec) { + res = -ENOTSUP; + spa_log_error(monitor->log, "Unsupported codec: %d (%s)", + res, spa_strerror(res)); + if ((r = dbus_message_new_error(m, "org.bluez.Error.InvalidArguments", + "Unsupported codec")) == NULL) + return DBUS_HANDLER_RESULT_NEED_MEMORY; + goto exit_send; + } + + /* Read transport properties */ + while (dbus_message_iter_get_arg_type(&props) == DBUS_TYPE_DICT_ENTRY) { + const char *key; + DBusMessageIter value, entry; + int var; + + dbus_message_iter_recurse(&props, &entry); + dbus_message_iter_get_basic(&entry, &key); + + dbus_message_iter_next(&entry); + dbus_message_iter_recurse(&entry, &value); + + var = dbus_message_iter_get_arg_type(&value); + + if (spa_streq(key, "Capabilities")) { + DBusMessageIter array, dict; + uint8_t configA2DP_MAX_CAPS_SIZE, *cap; + uint8_t *pconf = (uint8_t *) config; + + if (r) { + spa_log_warn(monitor->log, "Multiple Capabilities entries, skipped"); + goto next_entry; + } + + if (var != DBUS_TYPE_ARRAY) { + spa_log_error(monitor->log, "Property %s of wrong type %c", key, (char)var); + if ((r = dbus_message_new_error(m, "org.bluez.Error.InvalidArguments", + "Invalid property")) == NULL) + return DBUS_HANDLER_RESULT_NEED_MEMORY; + goto exit_send; + } + + dbus_message_iter_recurse(&value, &array); + var = dbus_message_iter_get_arg_type(&array); + if (var != DBUS_TYPE_BYTE) { + spa_log_error(monitor->log, "%s is an array of wrong type %c", key, (char)var); + if ((r = dbus_message_new_error(m, "org.bluez.Error.InvalidArguments", + "Invalid property")) == NULL) + return DBUS_HANDLER_RESULT_NEED_MEMORY; + goto exit_send; + } + + dbus_message_iter_get_fixed_array(&array, &cap, &size); + spa_log_info(monitor->log, "%p: %s select properties %d", monitor, path, size); + spa_log_hexdump(monitor->log, SPA_LOG_LEVEL_DEBUG, ' ', cap, (size_t)size); + + /* FIXME: We can't determine which device the SelectConfiguration() + * call is associated with, therefore device settings are not passed. + * This causes inconsistency with SelectConfiguration() triggered + * by codec switching. + */ + res = codec->select_config(codec, 0, cap, size, &monitor->default_audio_info, NULL, config); + + if (res < 0 || res != size) { + spa_log_error(monitor->log, "can't select config: %d (%s)", + res, spa_strerror(res)); + if ((r = dbus_message_new_error(m, "org.bluez.Error.InvalidArguments", + "Unable to select properties")) == NULL) + return DBUS_HANDLER_RESULT_NEED_MEMORY; + goto exit_send; + } + spa_log_info(monitor->log, "%p: selected conf %d", monitor, size); + spa_log_hexdump(monitor->log, SPA_LOG_LEVEL_DEBUG, ' ', pconf, (size_t)size); + + if ((r = dbus_message_new_method_return(m)) == NULL) + return DBUS_HANDLER_RESULT_NEED_MEMORY; + dbus_message_iter_init_append(r, &iter); + + dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, + DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING + DBUS_TYPE_STRING_AS_STRING + DBUS_TYPE_VARIANT_AS_STRING + DBUS_DICT_ENTRY_END_CHAR_AS_STRING, + &dict); + append_basic_array_variant_dict_entry(&dict, "Capabilities", "ay", "y", DBUS_TYPE_BYTE, &config, size); + + if (codec->get_qos) + { + struct codec_qos qos; + dbus_bool_t framing; + + codec->get_qos(codec, config, size, &qos); + + append_basic_variant_dict_entry(&dict, "Interval", DBUS_TYPE_UINT32, "u", &qos.interval); + framing = (qos.framing ? TRUE : FALSE); + append_basic_variant_dict_entry(&dict, "Framing", DBUS_TYPE_BOOLEAN, "b", &framing); + append_basic_variant_dict_entry(&dict, "PHY", DBUS_TYPE_STRING, "s", &qos.phy); + append_basic_variant_dict_entry(&dict, "SDU", DBUS_TYPE_UINT16, "q", &qos.sdu); + append_basic_variant_dict_entry(&dict, "Retransmissions", DBUS_TYPE_BYTE, "y", &qos.retransmission); + append_basic_variant_dict_entry(&dict, "Latency", DBUS_TYPE_UINT16, "q", &qos.latency); + append_basic_variant_dict_entry(&dict, "Delay", DBUS_TYPE_UINT32, "u", &qos.delay); + } + + dbus_message_iter_close_container(&iter, &dict); + } else if (spa_streq(key, "Endpoint")) { + dbus_message_iter_get_basic(&value, &object_path); + + if (codec->bap) { + struct spa_bt_remote_endpoint *ep; + + ep = remote_endpoint_find(monitor, object_path); + if (!ep) { + spa_log_warn(monitor->log, "Unable to find remote endpoint for %s", object_path); + goto next_entry; + } + + /* Call of SelectProperties means that local device acts as an initiator + * and therefor remote endpoint is an acceptor + */ + ep->acceptor = true; + } + } + +next_entry: + dbus_message_iter_next(&props); + } + +exit_send: + if (r) { + if (!dbus_connection_send(conn, r, NULL)) + return DBUS_HANDLER_RESULT_NEED_MEMORY; + + dbus_message_unref(r); + } + + return DBUS_HANDLER_RESULT_HANDLED; +} + static struct spa_bt_adapter *adapter_find(struct spa_bt_monitor *monitor, const char *path) { struct spa_bt_adapter *d; @@ -683,6 +869,12 @@ if (profile && (adapter->profiles & profile) == 0) { spa_log_debug(monitor->log, "adapter %p: add UUID=%s", adapter, uuid); adapter->profiles |= profile; + } else if (strcasecmp(uuid, SPA_BT_UUID_PACS) == 0 && + (adapter->profiles & SPA_BT_PROFILE_BAP_SINK) == 0) { + spa_log_debug(monitor->log, "adapter %p: add UUID=%s", adapter, SPA_BT_UUID_BAP_SINK); + adapter->profiles |= SPA_BT_PROFILE_BAP_SINK; + spa_log_debug(monitor->log, "adapter %p: add UUID=%s", adapter, SPA_BT_UUID_BAP_SOURCE); + adapter->profiles |= SPA_BT_PROFILE_BAP_SOURCE; } dbus_message_iter_next(&iter); } @@ -757,7 +949,7 @@ if (str == NULL) goto fail; snprintf(path, sizeof(path), "/sys/class/bluetooth/%s/device/modalias", str); - if ((f = fopen(path, "rb")) == NULL) { + if ((f = fopen(path, "rbe")) == NULL) { res = -errno; goto fail; } @@ -837,6 +1029,11 @@ if (profiles & SPA_BT_PROFILE_A2DP_SOURCE) mask |= SPA_BT_PROFILE_A2DP_SINK; + if (profiles & SPA_BT_PROFILE_BAP_SINK) + mask |= SPA_BT_PROFILE_BAP_SOURCE; + if (profiles & SPA_BT_PROFILE_BAP_SOURCE) + mask |= SPA_BT_PROFILE_BAP_SINK; + if (profiles & SPA_BT_PROFILE_HSP_AG) mask |= SPA_BT_PROFILE_HSP_HS; if (profiles & SPA_BT_PROFILE_HSP_HS) @@ -905,7 +1102,7 @@ static int device_stop_timer(struct spa_bt_device *device); -static void a2dp_codec_switch_free(struct spa_bt_a2dp_codec_switch *sw); +static void media_codec_switch_free(struct spa_bt_media_codec_switch *sw); static void device_clear_sub(struct spa_bt_device *device) { @@ -916,7 +1113,7 @@ static void device_free(struct spa_bt_device *device) { struct spa_bt_remote_endpoint *ep, *tep; - struct spa_bt_a2dp_codec_switch *sw; + struct spa_bt_media_codec_switch *sw; struct spa_bt_transport *t, *tt; struct spa_bt_monitor *monitor = device->monitor; @@ -946,7 +1143,7 @@ } spa_list_consume(sw, &device->codec_switch_list, device_link) - a2dp_codec_switch_free(sw); + media_codec_switch_free(sw); spa_list_remove(&device->link); free(device->path); @@ -1208,6 +1405,10 @@ device_try_connect_profile(device, SPA_BT_UUID_A2DP_SINK); if (reconnect & SPA_BT_PROFILE_A2DP_SOURCE) device_try_connect_profile(device, SPA_BT_UUID_A2DP_SOURCE); + if (reconnect & SPA_BT_PROFILE_BAP_SINK) + device_try_connect_profile(device, SPA_BT_UUID_BAP_SINK); + if (reconnect & SPA_BT_PROFILE_BAP_SOURCE) + device_try_connect_profile(device, SPA_BT_UUID_BAP_SOURCE); return reconnect; } @@ -1295,8 +1496,8 @@ uint32_t connectable_profiles = device->adapter ? adapter_connectable_profiles(device->adapter) : 0; uint32_t direction_masks3 = { - SPA_BT_PROFILE_A2DP_SINK | SPA_BT_PROFILE_HEADSET_HEAD_UNIT, - SPA_BT_PROFILE_A2DP_SOURCE, + SPA_BT_PROFILE_MEDIA_SINK | SPA_BT_PROFILE_HEADSET_HEAD_UNIT, + SPA_BT_PROFILE_MEDIA_SOURCE, SPA_BT_PROFILE_HEADSET_AUDIO_GATEWAY, }; bool direction_connected = false; @@ -1347,9 +1548,9 @@ spa_bt_device_check_profiles(device, false); else { /* Stop codec switch on disconnect */ - struct spa_bt_a2dp_codec_switch *sw; + struct spa_bt_media_codec_switch *sw; spa_list_consume(sw, &device->codec_switch_list, device_link) - a2dp_codec_switch_free(sw); + media_codec_switch_free(sw); if (device->reconnect_state != BT_DEVICE_RECONNECT_INIT) device_stop_timer(device); @@ -1514,12 +1715,12 @@ profile = spa_bt_profile_from_uuid(uuid); - /* Only add A2DP profiles if HSP/HFP backed is none. + /* Only add A2DP/BAP profiles if HSP/HFP backed is none. * This allows BT device to connect instantly instead of waiting for * profile timeout, because all available profiles are connected. */ if (monitor->backend_selection != BACKEND_NONE || (monitor->backend_selection == BACKEND_NONE && - profile & (SPA_BT_PROFILE_A2DP_SINK | SPA_BT_PROFILE_A2DP_SOURCE))) { + profile & (SPA_BT_PROFILE_MEDIA_SINK | SPA_BT_PROFILE_MEDIA_SOURCE))) { if (profile && (device->profiles & profile) == 0) { spa_log_debug(monitor->log, "device %p: add UUID=%s", device, uuid); device->profiles |= profile; @@ -1550,7 +1751,7 @@ return device->adapter && device->address; } -bool spa_bt_device_supports_a2dp_codec(struct spa_bt_device *device, const struct a2dp_codec *codec, bool sink) +bool spa_bt_device_supports_media_codec(struct spa_bt_device *device, const struct media_codec *codec, bool sink) { struct spa_bt_monitor *monitor = device->monitor; struct spa_bt_remote_endpoint *ep; @@ -1563,7 +1764,7 @@ }; size_t i; - if (!is_a2dp_codec_enabled(device->monitor, codec)) + if (!is_media_codec_enabled(device->monitor, codec)) return false; if (!device->adapter->application_registered) { @@ -1587,13 +1788,17 @@ spa_list_for_each(ep, &device->remote_endpoint_list, device_link) { const enum spa_bt_profile profile = spa_bt_profile_from_uuid(ep->uuid); - const enum spa_bt_profile expected = sink ? - SPA_BT_PROFILE_A2DP_SINK : SPA_BT_PROFILE_A2DP_SOURCE; + enum spa_bt_profile expected; + + if (codec->bap) + expected = sink ? SPA_BT_PROFILE_BAP_SINK : SPA_BT_PROFILE_BAP_SOURCE; + else + expected = sink ? SPA_BT_PROFILE_A2DP_SINK : SPA_BT_PROFILE_A2DP_SOURCE; if (profile != expected) continue; - if (a2dp_codec_check_caps(codec, ep->codec, ep->capabilities, ep->capabilities_len, + if (media_codec_check_caps(codec, ep->codec, ep->capabilities, ep->capabilities_len, &ep->monitor->default_audio_info, &monitor->global_settings)) return true; } @@ -1601,34 +1806,34 @@ return false; } -const struct a2dp_codec **spa_bt_device_get_supported_a2dp_codecs(struct spa_bt_device *device, size_t *count, bool sink) +const struct media_codec **spa_bt_device_get_supported_media_codecs(struct spa_bt_device *device, size_t *count, bool sink) { struct spa_bt_monitor *monitor = device->monitor; - const struct a2dp_codec * const * const a2dp_codecs = monitor->a2dp_codecs; - const struct a2dp_codec **supported_codecs; + const struct media_codec * const * const media_codecs = monitor->media_codecs; + const struct media_codec **supported_codecs; size_t i, j, size; *count = 0; size = 8; - supported_codecs = malloc(size * sizeof(const struct a2dp_codec *)); + supported_codecs = malloc(size * sizeof(const struct media_codec *)); if (supported_codecs == NULL) return NULL; j = 0; - for (i = 0; a2dp_codecsi != NULL; ++i) { - if (spa_bt_device_supports_a2dp_codec(device, a2dp_codecsi, sink)) { - supported_codecsj = a2dp_codecsi; + for (i = 0; media_codecsi != NULL; ++i) { + if (spa_bt_device_supports_media_codec(device, media_codecsi, sink)) { + supported_codecsj = media_codecsi; ++j; } if (j >= size) { - const struct a2dp_codec **p; + const struct media_codec **p; size = size * 2; #ifdef HAVE_REALLOCARRRAY - p = reallocarray(supported_codecs, size, sizeof(const struct a2dp_codec *)); + p = reallocarray(supported_codecs, size, sizeof(const struct media_codec *)); #else - p = realloc(supported_codecs, size * sizeof(const struct a2dp_codec *)); + p = realloc(supported_codecs, size * sizeof(const struct media_codec *)); #endif if (p == NULL) { free(supported_codecs); @@ -1832,6 +2037,7 @@ t->delay = SPA_BT_UNKNOWN_DELAY; t->user_data = SPA_PTROFF(t, sizeof(struct spa_bt_transport), void); spa_hook_list_init(&t->listener_list); + spa_list_init(&t->bap_transport_linked); spa_list_append(&monitor->transport_list, &t->link); @@ -1911,6 +2117,8 @@ if (device && device->connected_profiles != prev_connected) spa_bt_device_emit_profiles_changed(device, device->profiles, prev_connected); + spa_list_remove(&transport->bap_transport_linked); + free(transport->endpoint_path); free(transport->path); free(transport); @@ -2170,10 +2378,10 @@ /* Fallback values when device does not provide information */ - if (t->a2dp_codec == NULL) + if (t->media_codec == NULL) return 30 * SPA_NSEC_PER_MSEC; - switch (t->a2dp_codec->id) { + switch (t->media_codec->id) { case SPA_BLUETOOTH_AUDIO_CODEC_SBC: case SPA_BLUETOOTH_AUDIO_CODEC_SBC_XQ: return 200 * SPA_NSEC_PER_MSEC; @@ -2189,6 +2397,7 @@ case SPA_BLUETOOTH_AUDIO_CODEC_APTX_LL_DUPLEX: case SPA_BLUETOOTH_AUDIO_CODEC_FASTSTREAM: case SPA_BLUETOOTH_AUDIO_CODEC_FASTSTREAM_DUPLEX: + case SPA_BLUETOOTH_AUDIO_CODEC_LC3: return 40 * SPA_NSEC_PER_MSEC; default: break; @@ -2229,6 +2438,12 @@ case SPA_BT_PROFILE_A2DP_SINK: transport->profile = SPA_BT_PROFILE_A2DP_SOURCE; break; + case SPA_BT_PROFILE_BAP_SOURCE: + transport->profile = SPA_BT_PROFILE_BAP_SINK; + break; + case SPA_BT_PROFILE_BAP_SINK: + transport->profile = SPA_BT_PROFILE_BAP_SOURCE; + break; default: spa_log_warn(monitor->log, "unknown profile %s", value); break; @@ -2249,6 +2464,16 @@ spa_log_warn(monitor->log, "could not find device %s", value); } } + else if (spa_streq(key, "Endpoint")) { + struct spa_bt_remote_endpoint *ep = remote_endpoint_find(monitor, value); + if (!ep) { + spa_log_warn(monitor->log, "Unable to find remote endpoint for %s", value); + goto next; + } + + // If the remote endpoint is an acceptor this transport is an initiator + transport->bap_initiator = ep->acceptor; + } } else if (spa_streq(key, "Codec")) { uint8_t value; @@ -2321,6 +2546,47 @@ transport->delay = value; spa_bt_transport_emit_delay_changed(transport); } + else if (spa_streq(key, "PresentationDelay")) { + uint32_t value; + + if (type != DBUS_TYPE_UINT32) + goto next; + dbus_message_iter_get_basic(&it1, &value); + + spa_log_debug(monitor->log, "transport %p: %s=%02x", transport, key, value); + + transport->delay = value / 100; + spa_bt_transport_emit_delay_changed(transport); + } + else if (spa_streq(key, "Links")) { + DBusMessageIter iter; + + if (!check_iter_signature(&it1, "ao")) + goto next; + + dbus_message_iter_recurse(&it1, &iter); + while (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_INVALID) { + const char *transport_path; + struct spa_bt_transport *t; + + dbus_message_iter_get_basic(&iter, &transport_path); + + spa_log_debug(monitor->log, "transport %p: Linked with=%s", transport, transport_path); + t = spa_bt_transport_find(monitor, transport_path); + if (!t) { + spa_log_warn(monitor->log, "Unable to find linked transport"); + dbus_message_iter_next(&iter); + continue; + } + + if (spa_list_is_empty(&t->bap_transport_linked)) + spa_list_append(&transport->bap_transport_linked, &t->bap_transport_linked); + else if (spa_list_is_empty(&transport->bap_transport_linked)) + spa_list_append(&t->bap_transport_linked, &transport->bap_transport_linked); + + dbus_message_iter_next(&iter); + } + } next: dbus_message_iter_next(props_iter); } @@ -2404,10 +2670,27 @@ { struct spa_bt_transport *transport = data; struct spa_bt_monitor *monitor = transport->monitor; - DBusMessage *m, *r; + DBusMessage *m, *r = NULL; DBusError err; int ret = 0; const char *method = optional ? "TryAcquire" : "Acquire"; + struct spa_bt_transport *t_linked; + + /* For LE Audio, multiple transport from the same device may share the same + * stream (CIS) and group (CIG) but for different direction, e.g. a speaker and + * a microphone. In this case they are linked. + * If one of them has already been acquired this function should not call Acquire + * or TryAcquire but re-use values from the previously acquired transport. + */ + spa_list_for_each(t_linked, &transport->bap_transport_linked, bap_transport_linked) { + if (t_linked->acquired && t_linked->device == transport->device) { + transport->fd = t_linked->fd; + transport->read_mtu = t_linked->read_mtu; + transport->write_mtu = t_linked->write_mtu; + spa_log_debug(monitor->log, "transport %p: linked transport %s", transport, t_linked->path); + goto done; + } + } m = dbus_message_new_method_call(BLUEZ_SERVICE, transport->path, @@ -2451,6 +2734,7 @@ ret = -EIO; goto finish; } +done: spa_log_debug(monitor->log, "transport %p: %s %s, fd %d MTU %d:%d", transport, method, transport->path, transport->fd, transport->read_mtu, transport->write_mtu); @@ -2459,7 +2743,8 @@ transport_sync_volume(transport); finish: - dbus_message_unref(r); + if (r) + dbus_message_unref(r); return ret; } @@ -2470,12 +2755,30 @@ DBusMessage *m, *r; DBusError err; bool is_idle = (transport->state == SPA_BT_TRANSPORT_STATE_IDLE); + struct spa_bt_transport *t_linked; + bool linked = false; spa_log_debug(monitor->log, "transport %p: Release %s", transport, transport->path); spa_bt_player_set_state(transport->device->adapter->dummy_player, SPA_BT_PLAYER_STOPPED); + /* For LE Audio, multiple transport stream (CIS) can be linked together (CIG). + * If they are part of the same device they re-use the same fd, and call to + * release should be done for the last one only. + */ + spa_list_for_each(t_linked, &transport->bap_transport_linked, bap_transport_linked) { + if (t_linked->acquired && t_linked->device == transport->device) { + linked = true; + break; + } + } + if (linked) { + spa_log_info(monitor->log, "Linked transport %s released", transport->path); + transport->fd = -1; + return 0; + } + close(transport->fd); transport->fd = -1; @@ -2523,23 +2826,21 @@ .set_volume = transport_set_volume, }; -static void append_basic_array_variant_dict_entry(DBusMessageIter *dict, const char* key, const char* variant_type_str, const char* array_type_str, int array_type_int, void* data, int data_size); - -static void a2dp_codec_switch_reply(DBusPendingCall *pending, void *userdata); +static void media_codec_switch_reply(DBusPendingCall *pending, void *userdata); -static int a2dp_codec_switch_cmp(const void *a, const void *b); +static int media_codec_switch_cmp(const void *a, const void *b); -static struct spa_bt_a2dp_codec_switch *a2dp_codec_switch_cmp_sw; /* global for qsort */ +static struct spa_bt_media_codec_switch *media_codec_switch_cmp_sw; /* global for qsort */ -static int a2dp_codec_switch_start_timer(struct spa_bt_a2dp_codec_switch *sw, uint64_t timeout); +static int media_codec_switch_start_timer(struct spa_bt_media_codec_switch *sw, uint64_t timeout); -static int a2dp_codec_switch_stop_timer(struct spa_bt_a2dp_codec_switch *sw); +static int media_codec_switch_stop_timer(struct spa_bt_media_codec_switch *sw); -static void a2dp_codec_switch_free(struct spa_bt_a2dp_codec_switch *sw) +static void media_codec_switch_free(struct spa_bt_media_codec_switch *sw) { char **p; - a2dp_codec_switch_stop_timer(sw); + media_codec_switch_stop_timer(sw); if (sw->pending != NULL) { dbus_pending_call_cancel(sw->pending); @@ -2558,7 +2859,7 @@ free(sw); } -static void a2dp_codec_switch_next(struct spa_bt_a2dp_codec_switch *sw) +static void media_codec_switch_next(struct spa_bt_media_codec_switch *sw) { spa_assert(*sw->codec_iter != NULL && *sw->path_iter != NULL); @@ -2571,13 +2872,13 @@ sw->retries = CODEC_SWITCH_RETRIES; } -static bool a2dp_codec_switch_process_current(struct spa_bt_a2dp_codec_switch *sw) +static bool media_codec_switch_process_current(struct spa_bt_media_codec_switch *sw) { struct spa_bt_remote_endpoint *ep; struct spa_bt_transport *t; - const struct a2dp_codec *codec; + const struct media_codec *codec; uint8_t configA2DP_MAX_CAPS_SIZE; - char *local_endpoint_base; + enum spa_bt_media_direction direction; char *local_endpoint = NULL; int res, config_size; dbus_bool_t dbus_ret; @@ -2590,43 +2891,43 @@ codec = *sw->codec_iter; - spa_log_debug(sw->device->monitor->log, "a2dp codec switch %p: consider codec %s for remote endpoint %s", + spa_log_debug(sw->device->monitor->log, "media codec switch %p: consider codec %s for remote endpoint %s", sw, (*sw->codec_iter)->name, *sw->path_iter); ep = device_remote_endpoint_find(sw->device, *sw->path_iter); if (ep == NULL || ep->capabilities == NULL || ep->uuid == NULL) { - spa_log_debug(sw->device->monitor->log, "a2dp codec switch %p: endpoint %s not valid, try next", + spa_log_debug(sw->device->monitor->log, "media codec switch %p: endpoint %s not valid, try next", sw, *sw->path_iter); goto next; } /* Setup and check compatible configuration */ if (ep->codec != codec->codec_id) { - spa_log_debug(sw->device->monitor->log, "a2dp codec switch %p: different codec, try next", sw); + spa_log_debug(sw->device->monitor->log, "media codec switch %p: different codec, try next", sw); goto next; } if (!(sw->profile & spa_bt_profile_from_uuid(ep->uuid))) { - spa_log_debug(sw->device->monitor->log, "a2dp codec switch %p: wrong uuid (%s) for profile, try next", + spa_log_debug(sw->device->monitor->log, "media codec switch %p: wrong uuid (%s) for profile, try next", sw, ep->uuid); goto next; } - if (sw->profile & SPA_BT_PROFILE_A2DP_SINK) { - local_endpoint_base = A2DP_SOURCE_ENDPOINT; + if ((sw->profile & SPA_BT_PROFILE_A2DP_SINK) || (sw->profile & SPA_BT_PROFILE_BAP_SINK) ) { + direction = SPA_BT_MEDIA_SOURCE; sink = false; - } else if (sw->profile & SPA_BT_PROFILE_A2DP_SOURCE) { - local_endpoint_base = A2DP_SINK_ENDPOINT; + } else if ((sw->profile & SPA_BT_PROFILE_A2DP_SOURCE) || (sw->profile & SPA_BT_PROFILE_BAP_SOURCE) ) { + direction = SPA_BT_MEDIA_SINK; sink = true; } else { - spa_log_debug(sw->device->monitor->log, "a2dp codec switch %p: bad profile (%d), try next", + spa_log_debug(sw->device->monitor->log, "media codec switch %p: bad profile (%d), try next", sw, sw->profile); goto next; } - if (a2dp_codec_to_endpoint(codec, local_endpoint_base, &local_endpoint) < 0) { - spa_log_debug(sw->device->monitor->log, "a2dp codec switch %p: no endpoint for codec %s, try next", + if (media_codec_to_endpoint(codec, direction, &local_endpoint) < 0) { + spa_log_debug(sw->device->monitor->log, "media codec switch %p: no endpoint for codec %s, try next", sw, codec->name); goto next; } @@ -2638,36 +2939,36 @@ if (t->device->adapter != sw->device->adapter) continue; if (spa_streq(t->endpoint_path, local_endpoint)) { - spa_log_debug(sw->device->monitor->log, "a2dp codec switch %p: endpoint %s in use, try next", + spa_log_debug(sw->device->monitor->log, "media codec switch %p: endpoint %s in use, try next", sw, local_endpoint); goto next; } } - res = codec->select_config(codec, sink ? A2DP_CODEC_FLAG_SINK : 0, ep->capabilities, ep->capabilities_len, + res = codec->select_config(codec, sink ? MEDIA_CODEC_FLAG_SINK : 0, ep->capabilities, ep->capabilities_len, &sw->device->monitor->default_audio_info, &sw->device->monitor->global_settings, config); if (res < 0) { - spa_log_debug(sw->device->monitor->log, "a2dp codec switch %p: incompatible capabilities (%d), try next", + spa_log_debug(sw->device->monitor->log, "media codec switch %p: incompatible capabilities (%d), try next", sw, res); goto next; } config_size = res; - spa_log_debug(sw->device->monitor->log, "a2dp codec switch %p: configuration %d", sw, config_size); + spa_log_debug(sw->device->monitor->log, "media codec switch %p: configuration %d", sw, config_size); for (i = 0; i < config_size; i++) - spa_log_debug(sw->device->monitor->log, "a2dp codec switch %p: %d: %02x", sw, i, configi); + spa_log_debug(sw->device->monitor->log, "media codec switch %p: %d: %02x", sw, i, configi); /* org.bluez.MediaEndpoint1.SetConfiguration on remote endpoint */ m = dbus_message_new_method_call(BLUEZ_SERVICE, ep->path, BLUEZ_MEDIA_ENDPOINT_INTERFACE, "SetConfiguration"); if (m == NULL) { - spa_log_debug(sw->device->monitor->log, "a2dp codec switch %p: dbus allocation failure, try next", sw); + spa_log_debug(sw->device->monitor->log, "media codec switch %p: dbus allocation failure, try next", sw); goto next; } spa_bt_device_update_last_bluez_action_time(sw->device); - spa_log_info(sw->device->monitor->log, "a2dp codec switch %p: trying codec %s for endpoint %s, local endpoint %s", + spa_log_info(sw->device->monitor->log, "media codec switch %p: trying codec %s for endpoint %s, local endpoint %s", sw, codec->name, ep->path, local_endpoint); dbus_message_iter_init_append(m, &iter); @@ -2680,16 +2981,16 @@ dbus_ret = dbus_connection_send_with_reply(sw->device->monitor->conn, m, &sw->pending, -1); if (!dbus_ret || sw->pending == NULL) { - spa_log_error(sw->device->monitor->log, "a2dp codec switch %p: dbus call failure, try next", sw); + spa_log_error(sw->device->monitor->log, "media codec switch %p: dbus call failure, try next", sw); dbus_message_unref(m); goto next; } - dbus_ret = dbus_pending_call_set_notify(sw->pending, a2dp_codec_switch_reply, sw, NULL); + dbus_ret = dbus_pending_call_set_notify(sw->pending, media_codec_switch_reply, sw, NULL); dbus_message_unref(m); if (!dbus_ret) { - spa_log_error(sw->device->monitor->log, "a2dp codec switch %p: dbus set notify failure", sw); + spa_log_error(sw->device->monitor->log, "media codec switch %p: dbus set notify failure", sw); goto next; } @@ -2701,7 +3002,7 @@ return false; } -static void a2dp_codec_switch_process(struct spa_bt_a2dp_codec_switch *sw) +static void media_codec_switch_process(struct spa_bt_media_codec_switch *sw) { while (*sw->codec_iter != NULL && *sw->path_iter != NULL) { struct timespec ts; @@ -2713,60 +3014,60 @@ threshold = sw->device->last_bluez_action_time + BLUEZ_ACTION_RATE_MSEC * SPA_NSEC_PER_MSEC; if (now < threshold) { /* Wait for timeout */ - a2dp_codec_switch_start_timer(sw, threshold - now); + media_codec_switch_start_timer(sw, threshold - now); return; } if (sw->path_iter == sw->paths && (*sw->codec_iter)->caps_preference_cmp) { /* Sort endpoints according to codec preference, when at a new codec. */ - a2dp_codec_switch_cmp_sw = sw; - qsort(sw->paths, sw->num_paths, sizeof(char *), a2dp_codec_switch_cmp); + media_codec_switch_cmp_sw = sw; + qsort(sw->paths, sw->num_paths, sizeof(char *), media_codec_switch_cmp); } - if (a2dp_codec_switch_process_current(sw)) { + if (media_codec_switch_process_current(sw)) { /* Wait for dbus reply */ return; } - a2dp_codec_switch_next(sw); + media_codec_switch_next(sw); }; /* Didn't find any suitable endpoint. Report failure. */ - spa_log_info(sw->device->monitor->log, "a2dp codec switch %p: failed to get an endpoint", sw); + spa_log_info(sw->device->monitor->log, "media codec switch %p: failed to get an endpoint", sw); spa_bt_device_emit_codec_switched(sw->device, -ENODEV); spa_bt_device_check_profiles(sw->device, false); - a2dp_codec_switch_free(sw); + media_codec_switch_free(sw); } -static bool a2dp_codec_switch_goto_active(struct spa_bt_a2dp_codec_switch *sw) +static bool media_codec_switch_goto_active(struct spa_bt_media_codec_switch *sw) { struct spa_bt_device *device = sw->device; - struct spa_bt_a2dp_codec_switch *active_sw; + struct spa_bt_media_codec_switch *active_sw; - active_sw = spa_list_first(&device->codec_switch_list, struct spa_bt_a2dp_codec_switch, device_link); + active_sw = spa_list_first(&device->codec_switch_list, struct spa_bt_media_codec_switch, device_link); if (active_sw != sw) { - struct spa_bt_a2dp_codec_switch *t; + struct spa_bt_media_codec_switch *t; /* This codec switch has been canceled. Switch to the newest one. */ spa_log_debug(sw->device->monitor->log, - "a2dp codec switch %p: canceled, go to new switch", sw); + "media codec switch %p: canceled, go to new switch", sw); spa_list_for_each_safe(sw, t, &device->codec_switch_list, device_link) { if (sw != active_sw) - a2dp_codec_switch_free(sw); + media_codec_switch_free(sw); } - a2dp_codec_switch_process(active_sw); + media_codec_switch_process(active_sw); return false; } return true; } -static void a2dp_codec_switch_timer_event(struct spa_source *source) +static void media_codec_switch_timer_event(struct spa_source *source) { - struct spa_bt_a2dp_codec_switch *sw = source->data; + struct spa_bt_media_codec_switch *sw = source->data; struct spa_bt_device *device = sw->device; struct spa_bt_monitor *monitor = device->monitor; uint64_t exp; @@ -2774,19 +3075,19 @@ if (spa_system_timerfd_read(monitor->main_system, source->fd, &exp) < 0) spa_log_warn(monitor->log, "error reading timerfd: %s", strerror(errno)); - spa_log_debug(monitor->log, "a2dp codec switch %p: rate limit timer event", sw); + spa_log_debug(monitor->log, "media codec switch %p: rate limit timer event", sw); - a2dp_codec_switch_stop_timer(sw); + media_codec_switch_stop_timer(sw); - if (!a2dp_codec_switch_goto_active(sw)) + if (!media_codec_switch_goto_active(sw)) return; - a2dp_codec_switch_process(sw); + media_codec_switch_process(sw); } -static void a2dp_codec_switch_reply(DBusPendingCall *pending, void *user_data) +static void media_codec_switch_reply(DBusPendingCall *pending, void *user_data) { - struct spa_bt_a2dp_codec_switch *sw = user_data; + struct spa_bt_media_codec_switch *sw = user_data; struct spa_bt_device *device = sw->device; DBusMessage *r; @@ -2798,7 +3099,7 @@ spa_bt_device_update_last_bluez_action_time(device); - if (!a2dp_codec_switch_goto_active(sw)) { + if (!media_codec_switch_goto_active(sw)) { if (r != NULL) dbus_message_unref(r); return; @@ -2806,14 +3107,14 @@ if (r == NULL) { spa_log_error(sw->device->monitor->log, - "a2dp codec switch %p: empty reply from dbus, trying next", + "media codec switch %p: empty reply from dbus, trying next", sw); goto next; } if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) { spa_log_debug(sw->device->monitor->log, - "a2dp codec switch %p: failed (%s), trying next", + "media codec switch %p: failed (%s), trying next", sw, dbus_message_get_error_name(r)); dbus_message_unref(r); goto next; @@ -2822,34 +3123,34 @@ dbus_message_unref(r); /* Success */ - spa_log_info(sw->device->monitor->log, "a2dp codec switch %p: success", sw); + spa_log_info(sw->device->monitor->log, "media codec switch %p: success", sw); spa_bt_device_emit_codec_switched(sw->device, 0); spa_bt_device_check_profiles(sw->device, false); - a2dp_codec_switch_free(sw); + media_codec_switch_free(sw); return; next: if (sw->retries > 0) --sw->retries; else - a2dp_codec_switch_next(sw); + media_codec_switch_next(sw); - a2dp_codec_switch_process(sw); + media_codec_switch_process(sw); return; } -static int a2dp_codec_switch_start_timer(struct spa_bt_a2dp_codec_switch *sw, uint64_t timeout) +static int media_codec_switch_start_timer(struct spa_bt_media_codec_switch *sw, uint64_t timeout) { struct spa_bt_monitor *monitor = sw->device->monitor; struct itimerspec ts; spa_assert(sw->timer.data == NULL); - spa_log_debug(monitor->log, "a2dp codec switch %p: starting rate limit timer", sw); + spa_log_debug(monitor->log, "media codec switch %p: starting rate limit timer", sw); if (sw->timer.data == NULL) { sw->timer.data = sw; - sw->timer.func = a2dp_codec_switch_timer_event; + sw->timer.func = media_codec_switch_timer_event; sw->timer.fd = spa_system_timerfd_create(monitor->main_system, CLOCK_MONOTONIC, SPA_FD_CLOEXEC | SPA_FD_NONBLOCK); sw->timer.mask = SPA_IO_IN; @@ -2864,7 +3165,7 @@ return 0; } -static int a2dp_codec_switch_stop_timer(struct spa_bt_a2dp_codec_switch *sw) +static int media_codec_switch_stop_timer(struct spa_bt_media_codec_switch *sw) { struct spa_bt_monitor *monitor = sw->device->monitor; struct itimerspec ts; @@ -2872,7 +3173,7 @@ if (sw->timer.data == NULL) return 0; - spa_log_debug(monitor->log, "a2dp codec switch %p: stopping rate limit timer", sw); + spa_log_debug(monitor->log, "media codec switch %p: stopping rate limit timer", sw); spa_loop_remove_source(monitor->main_loop, &sw->timer); ts.it_value.tv_sec = 0; @@ -2885,10 +3186,10 @@ return 0; } -static int a2dp_codec_switch_cmp(const void *a, const void *b) +static int media_codec_switch_cmp(const void *a, const void *b) { - struct spa_bt_a2dp_codec_switch *sw = a2dp_codec_switch_cmp_sw; - const struct a2dp_codec *codec = *sw->codec_iter; + struct spa_bt_media_codec_switch *sw = media_codec_switch_cmp_sw; + const struct media_codec *codec = *sw->codec_iter; const char *path1 = *(char **)a, *path2 = *(char **)b; struct spa_bt_remote_endpoint *ep1, *ep2; uint32_t flags; @@ -2912,7 +3213,10 @@ else if (ep2 == NULL) return -1; - flags = spa_streq(ep1->uuid, SPA_BT_UUID_A2DP_SOURCE) ? A2DP_CODEC_FLAG_SINK : 0; + if (codec->bap) + flags = spa_streq(ep1->uuid, SPA_BT_UUID_BAP_SOURCE) ? MEDIA_CODEC_FLAG_SINK : 0; + else + flags = spa_streq(ep1->uuid, SPA_BT_UUID_A2DP_SOURCE) ? MEDIA_CODEC_FLAG_SINK : 0; return codec->caps_preference_cmp(codec, flags, ep1->capabilities, ep1->capabilities_len, ep2->capabilities, ep2->capabilities_len, &sw->device->monitor->default_audio_info, @@ -2920,12 +3224,12 @@ } /* Ensure there's a transport for at least one of the listed codecs */ -int spa_bt_device_ensure_a2dp_codec(struct spa_bt_device *device, const struct a2dp_codec * const *codecs) +int spa_bt_device_ensure_media_codec(struct spa_bt_device *device, const struct media_codec * const *codecs) { - struct spa_bt_a2dp_codec_switch *sw; + struct spa_bt_media_codec_switch *sw; struct spa_bt_remote_endpoint *ep; struct spa_bt_transport *t; - const struct a2dp_codec *preferred_codec = NULL; + const struct media_codec *preferred_codec = NULL; size_t i, j, num_codecs, num_eps; if (!device->adapter->application_registered) { @@ -2934,7 +3238,7 @@ } for (i = 0; codecsi != NULL; ++i) { - if (spa_bt_device_supports_a2dp_codec(device, codecsi, true)) { + if (spa_bt_device_supports_media_codec(device, codecsi, true)) { preferred_codec = codecsi; break; } @@ -2946,7 +3250,7 @@ */ if (spa_list_is_empty(&device->codec_switch_list) && preferred_codec != NULL) { spa_list_for_each(t, &device->transport_list, device_link) { - if (t->a2dp_codec != preferred_codec) + if (t->media_codec != preferred_codec) continue; if ((device->connected_profiles & t->profile) != t->profile) @@ -2959,7 +3263,7 @@ /* Setup and start iteration */ - sw = calloc(1, sizeof(struct spa_bt_a2dp_codec_switch)); + sw = calloc(1, sizeof(struct spa_bt_media_codec_switch)); if (sw == NULL) return -ENOMEM; @@ -2971,17 +3275,17 @@ while (codecsnum_codecs != NULL) ++num_codecs; - sw->codecs = calloc(num_codecs + 1, sizeof(const struct a2dp_codec *)); + sw->codecs = calloc(num_codecs + 1, sizeof(const struct media_codec *)); sw->paths = calloc(num_eps + 1, sizeof(char *)); sw->num_paths = num_eps; if (sw->codecs == NULL || sw->paths == NULL) { - a2dp_codec_switch_free(sw); + media_codec_switch_free(sw); return -ENOMEM; } for (i = 0, j = 0; i < num_codecs; ++i) { - if (is_a2dp_codec_enabled(device->monitor, codecsi)) { + if (is_media_codec_enabled(device->monitor, codecsi)) { sw->codecsj = codecsi; ++j; } @@ -2992,7 +3296,7 @@ spa_list_for_each(ep, &device->remote_endpoint_list, device_link) { sw->pathsi = strdup(ep->path); if (sw->pathsi == NULL) { - a2dp_codec_switch_free(sw); + media_codec_switch_free(sw); return -ENOMEM; } ++i; @@ -3018,13 +3322,13 @@ * to wait to pass in any case, so we don't cancel it either. */ spa_log_debug(sw->device->monitor->log, - "a2dp codec switch %p: already in progress, canceling previous", + "media codec switch %p: already in progress, canceling previous", sw); spa_list_prepend(&device->codec_switch_list, &sw->device_link); } else { spa_list_prepend(&device->codec_switch_list, &sw->device_link); - a2dp_codec_switch_process(sw); + media_codec_switch_process(sw); } return 0; @@ -3050,7 +3354,7 @@ DBusMessageIter it2; DBusMessage *r; struct spa_bt_transport *transport; - const struct a2dp_codec *codec; + const struct media_codec *codec; int profile; bool sink; @@ -3060,8 +3364,8 @@ } endpoint = dbus_message_get_path(m); - profile = a2dp_endpoint_to_profile(endpoint); - codec = a2dp_endpoint_to_codec(monitor, endpoint, &sink); + profile = media_endpoint_to_profile(endpoint); + codec = media_endpoint_to_codec(monitor, endpoint, &sink); if (codec == NULL) { spa_log_warn(monitor->log, "unknown SetConfiguration() codec"); return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; @@ -3102,7 +3406,7 @@ free(transport->endpoint_path); transport->endpoint_path = strdup(endpoint); transport->profile = profile; - transport->a2dp_codec = codec; + transport->media_codec = codec; transport_update_props(transport, &it1, NULL); if (transport->device == NULL || transport->device->adapter == NULL) { @@ -3122,7 +3426,7 @@ if (codec->validate_config) { struct spa_audio_info info; - if (codec->validate_config(codec, sink ? A2DP_CODEC_FLAG_SINK : 0, + if (codec->validate_config(codec, sink ? MEDIA_CODEC_FLAG_SINK : 0, transport->configuration, transport->configuration_len, &info) < 0) { spa_log_error(monitor->log, "invalid transport configuration"); @@ -3244,6 +3548,8 @@ res = endpoint_set_configuration(c, path, m, userdata); else if (dbus_message_is_method_call(m, BLUEZ_MEDIA_ENDPOINT_INTERFACE, "SelectConfiguration")) res = endpoint_select_configuration(c, m, userdata); + else if (dbus_message_is_method_call(m, BLUEZ_MEDIA_ENDPOINT_INTERFACE, "SelectProperties")) + res = endpoint_select_properties(c, m, userdata); else if (dbus_message_is_method_call(m, BLUEZ_MEDIA_ENDPOINT_INTERFACE, "ClearConfiguration")) res = endpoint_clear_configuration(c, m, userdata); else if (dbus_message_is_method_call(m, BLUEZ_MEDIA_ENDPOINT_INTERFACE, "Release")) @@ -3303,24 +3609,22 @@ } static int bluez_register_endpoint(struct spa_bt_monitor *monitor, - const char *path, const char *endpoint, - const char *uuid, const struct a2dp_codec *codec) { - char *object_path = NULL; + const char *path, enum spa_bt_media_direction direction, + const char *uuid, const struct media_codec *codec) { + char *object_path = NULL; DBusMessage *m; DBusMessageIter object_it, dict_it; DBusPendingCall *call; uint8_t capsA2DP_MAX_CAPS_SIZE; int ret, caps_size; uint16_t codec_id = codec->codec_id; - bool sink; + bool sink = (direction == SPA_BT_MEDIA_SINK); - ret = a2dp_codec_to_endpoint(codec, endpoint, &object_path); + ret = media_codec_to_endpoint(codec, direction, &object_path); if (ret < 0) goto error; - sink = spa_streq(endpoint, A2DP_SINK_ENDPOINT); - - ret = caps_size = codec->fill_caps(codec, sink ? A2DP_CODEC_FLAG_SINK : 0, caps); + ret = caps_size = codec->fill_caps(codec, sink ? MEDIA_CODEC_FLAG_SINK : 0, caps); if (ret < 0) goto error; @@ -3357,8 +3661,8 @@ return ret; } -static int register_a2dp_endpoint(struct spa_bt_monitor *monitor, - const struct a2dp_codec *codec, const char *endpoint) +static int register_media_endpoint(struct spa_bt_monitor *monitor, + const struct media_codec *codec, enum spa_bt_media_direction direction) { int ret; char* object_path = NULL; @@ -3366,7 +3670,7 @@ .message_function = endpoint_handler, }; - ret = a2dp_codec_to_endpoint(codec, endpoint, &object_path); + ret = media_codec_to_endpoint(codec, direction, &object_path); if (ret < 0) return ret; @@ -3387,7 +3691,7 @@ static int adapter_register_endpoints(struct spa_bt_adapter *a) { struct spa_bt_monitor *monitor = a->monitor; - const struct a2dp_codec * const * const a2dp_codecs = monitor->a2dp_codecs; + const struct media_codec * const * const media_codecs = monitor->media_codecs; int i; int err = 0; @@ -3398,28 +3702,28 @@ * It doesn't make sense to register codecs other than SBC * as bluez5 will probably use SBC anyway and we have no control over it * let's incentivize users to upgrade their bluez5 daemon - * if they want proper a2dp codec support + * if they want proper media codec support * */ spa_log_warn(monitor->log, "Using legacy bluez5 API for A2DP - only SBC will be supported. " "Please upgrade bluez5."); - for (i = 0; a2dp_codecsi; i++) { - const struct a2dp_codec *codec = a2dp_codecsi; + for (i = 0; media_codecsi; i++) { + const struct media_codec *codec = media_codecsi; - if (!is_a2dp_codec_enabled(monitor, codec)) + if (!is_media_codec_enabled(monitor, codec)) continue; if (!(codec->codec_id == A2DP_CODEC_SBC && spa_streq(codec->name, "sbc"))) continue; if ((err = bluez_register_endpoint(monitor, a->path, - A2DP_SOURCE_ENDPOINT, + SPA_BT_MEDIA_SOURCE, SPA_BT_UUID_A2DP_SOURCE, codec))) goto out; if ((err = bluez_register_endpoint(monitor, a->path, - A2DP_SINK_ENDPOINT, + SPA_BT_MEDIA_SINK, SPA_BT_UUID_A2DP_SINK, codec))) goto out; @@ -3441,7 +3745,7 @@ return err; } -static void append_a2dp_object(DBusMessageIter *iter, const char *endpoint, +static void append_media_object(DBusMessageIter *iter, const char *endpoint, const char *uuid, uint8_t codec_id, uint8_t *caps, size_t caps_size) { const char *interface_name = BLUEZ_MEDIA_ENDPOINT_INTERFACE; @@ -3475,7 +3779,7 @@ static DBusHandlerResult object_manager_handler(DBusConnection *c, DBusMessage *m, void *user_data) { struct spa_bt_monitor *monitor = user_data; - const struct a2dp_codec * const * const a2dp_codecs = monitor->a2dp_codecs; + const struct media_codec * const * const media_codecs = monitor->media_codecs; const char *path, *interface, *member; char *endpoint; DBusMessage *r; @@ -3509,24 +3813,41 @@ dbus_message_iter_init_append(r, &iter); dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{oa{sa{sv}}}", &array); - for (i = 0; a2dp_codecsi; i++) { - const struct a2dp_codec *codec = a2dp_codecsi; + for (i = 0; media_codecsi; i++) { + const struct media_codec *codec = media_codecsi; uint8_t capsA2DP_MAX_CAPS_SIZE; int caps_size, ret; uint16_t codec_id = codec->codec_id; - if (!is_a2dp_codec_enabled(monitor, codec)) + if (!is_media_codec_enabled(monitor, codec)) continue; + caps_size = codec->fill_caps(codec, 0, caps); + if (caps_size < 0) + continue; + + if (codec->bap && !monitor->le_audio_supported) { + /* The legacy bluez5 api doesn't support LE Audio + * It doesn't make sense to register unsupported codecs as it prevents + * registration of A2DP codecs + * let's incentivize users to upgrade their bluez5 daemon + * if they want proper media codec support + * */ + spa_log_warn(monitor->log, "Trying to use legacy bluez5 API for LE Audio - only A2DP will be supported. " + "Please upgrade bluez5."); + continue; + } + if (codec->decode != NULL) { - caps_size = codec->fill_caps(codec, A2DP_CODEC_FLAG_SINK, caps); + caps_size = codec->fill_caps(codec, MEDIA_CODEC_FLAG_SINK, caps); if (caps_size < 0) continue; - ret = a2dp_codec_to_endpoint(codec, A2DP_SINK_ENDPOINT, &endpoint); + ret = media_codec_to_endpoint(codec, SPA_BT_MEDIA_SINK, &endpoint); if (ret == 0) { - spa_log_info(monitor->log, "register A2DP sink codec %s: %s", a2dp_codecsi->name, endpoint); - append_a2dp_object(&array, endpoint, SPA_BT_UUID_A2DP_SINK, + spa_log_info(monitor->log, "register media sink codec %s: %s", media_codecsi->name, endpoint); + append_media_object(&array, endpoint, + codec->bap ? SPA_BT_UUID_BAP_SINK : SPA_BT_UUID_A2DP_SINK, codec_id, caps, caps_size); free(endpoint); } @@ -3537,10 +3858,11 @@ if (caps_size < 0) continue; - ret = a2dp_codec_to_endpoint(codec, A2DP_SOURCE_ENDPOINT, &endpoint); + ret = media_codec_to_endpoint(codec, SPA_BT_MEDIA_SOURCE, &endpoint); if (ret == 0) { - spa_log_info(monitor->log, "register A2DP source codec %s: %s", a2dp_codecsi->name, endpoint); - append_a2dp_object(&array, endpoint, SPA_BT_UUID_A2DP_SOURCE, + spa_log_info(monitor->log, "register media source codec %s: %s", media_codecsi->name, endpoint); + append_media_object(&array, endpoint, + codec->bap ? SPA_BT_UUID_BAP_SOURCE : SPA_BT_UUID_A2DP_SOURCE, codec_id, caps, caps_size); free(endpoint); } @@ -3593,28 +3915,28 @@ static int register_media_application(struct spa_bt_monitor * monitor) { - const struct a2dp_codec * const * const a2dp_codecs = monitor->a2dp_codecs; + const struct media_codec * const * const media_codecs = monitor->media_codecs; const DBusObjectPathVTable vtable_object_manager = { .message_function = object_manager_handler, }; - spa_log_info(monitor->log, "Registering media application object: " A2DP_OBJECT_MANAGER_PATH); + spa_log_info(monitor->log, "Registering media application object: " MEDIA_OBJECT_MANAGER_PATH); if (!dbus_connection_register_object_path(monitor->conn, - A2DP_OBJECT_MANAGER_PATH, + MEDIA_OBJECT_MANAGER_PATH, &vtable_object_manager, monitor)) return -EIO; - for (int i = 0; a2dp_codecsi; i++) { - const struct a2dp_codec *codec = a2dp_codecsi; + for (int i = 0; media_codecsi; i++) { + const struct media_codec *codec = media_codecsi; - if (!is_a2dp_codec_enabled(monitor, codec)) + if (!is_media_codec_enabled(monitor, codec)) continue; if (codec->encode != NULL) - register_a2dp_endpoint(monitor, codec, A2DP_SOURCE_ENDPOINT); + register_media_endpoint(monitor, codec, SPA_BT_MEDIA_SOURCE); if (codec->decode != NULL) - register_a2dp_endpoint(monitor, codec, A2DP_SINK_ENDPOINT); + register_media_endpoint(monitor, codec, SPA_BT_MEDIA_SINK); } return 0; @@ -3622,34 +3944,34 @@ static void unregister_media_application(struct spa_bt_monitor * monitor) { - const struct a2dp_codec * const * const a2dp_codecs = monitor->a2dp_codecs; + const struct media_codec * const * const media_codecs = monitor->media_codecs; int ret; char *object_path = NULL; - for (int i = 0; a2dp_codecsi; i++) { - const struct a2dp_codec *codec = a2dp_codecsi; + for (int i = 0; media_codecsi; i++) { + const struct media_codec *codec = media_codecsi; - if (!is_a2dp_codec_enabled(monitor, codec)) + if (!is_media_codec_enabled(monitor, codec)) continue; - ret = a2dp_codec_to_endpoint(codec, A2DP_SOURCE_ENDPOINT, &object_path); + ret = media_codec_to_endpoint(codec, SPA_BT_MEDIA_SOURCE, &object_path); if (ret == 0) { dbus_connection_unregister_object_path(monitor->conn, object_path); free(object_path); } - ret = a2dp_codec_to_endpoint(codec, A2DP_SINK_ENDPOINT, &object_path); + ret = media_codec_to_endpoint(codec, SPA_BT_MEDIA_SINK, &object_path); if (ret == 0) { dbus_connection_unregister_object_path(monitor->conn, object_path); free(object_path); } } - dbus_connection_unregister_object_path(monitor->conn, A2DP_OBJECT_MANAGER_PATH); + dbus_connection_unregister_object_path(monitor->conn, MEDIA_OBJECT_MANAGER_PATH); } static int adapter_register_application(struct spa_bt_adapter *a) { - const char *object_manager_path = A2DP_OBJECT_MANAGER_PATH; + const char *object_manager_path = MEDIA_OBJECT_MANAGER_PATH; struct spa_bt_monitor *monitor = a->monitor; DBusMessage *m; DBusMessageIter i, d; @@ -3745,6 +4067,48 @@ backend ? backend->name : "none"); } +static int media_update_props(struct spa_bt_monitor *monitor, + DBusMessageIter *props_iter, + DBusMessageIter *invalidated_iter) +{ + while (dbus_message_iter_get_arg_type(props_iter) != DBUS_TYPE_INVALID) { + DBusMessageIter it2; + const char *key; + + dbus_message_iter_recurse(props_iter, &it0); + dbus_message_iter_get_basic(&it0, &key); + dbus_message_iter_next(&it0); + dbus_message_iter_recurse(&it0, &it1); + + if (spa_streq(key, "SupportedUUIDs")) { + DBusMessageIter iter; + + if (!check_iter_signature(&it1, "as")) + goto next; + + dbus_message_iter_recurse(&it1, &iter); + + while (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_INVALID) { + const char *uuid; + + dbus_message_iter_get_basic(&iter, &uuid); + + if (spa_streq(uuid, SPA_BT_UUID_BAP_SINK)) { + monitor->le_audio_supported = true; + spa_log_info(monitor->log, "LE Audio supported"); + } + dbus_message_iter_next(&iter); + } + } + else + spa_log_debug(monitor->log, "media: unhandled key %s", key); + + next: + dbus_message_iter_next(props_iter); + } + return 0; +} + static void interface_added(struct spa_bt_monitor *monitor, DBusConnection *conn, const char *object_path, @@ -3818,6 +4182,9 @@ if (d) spa_bt_device_emit_profiles_changed(d, d->profiles, d->connected_profiles); } + else if (spa_streq(interface_name, BLUEZ_MEDIA_INTERFACE)) { + media_update_props(monitor, props_iter, NULL); + } } static void interfaces_added(struct spa_bt_monitor *monitor, DBusMessageIter *arg_iter) @@ -4189,7 +4556,7 @@ transport_update_props(transport, &it1, NULL); } - } + } fail: dbus_error_free(&err); @@ -4361,7 +4728,7 @@ spa_bt_quirks_destroy(monitor->quirks); - free_a2dp_codecs(monitor->a2dp_codecs); + free_media_codecs(monitor->media_codecs); return 0; } @@ -4397,6 +4764,10 @@ profiles |= SPA_BT_PROFILE_A2DP_SINK; } else if (spa_streq(role_name, "a2dp_source")) { profiles |= SPA_BT_PROFILE_A2DP_SOURCE; + } else if (spa_streq(role_name, "bap_sink")) { + profiles |= SPA_BT_PROFILE_BAP_SINK; + } else if (spa_streq(role_name, "bap_source")) { + profiles |= SPA_BT_PROFILE_BAP_SOURCE; } } @@ -4405,7 +4776,7 @@ static int parse_codec_array(struct spa_bt_monitor *this, const struct spa_dict *info) { - const struct a2dp_codec * const * const a2dp_codecs = this->a2dp_codecs; + const struct media_codec * const * const media_codecs = this->media_codecs; const char *str; struct spa_dict_item *codecs; struct spa_json it, it_array; @@ -4416,7 +4787,7 @@ /* Parse bluez5.codecs property to a dict of enabled codecs */ num_codecs = 0; - while (a2dp_codecsnum_codecs) + while (media_codecsnum_codecs) ++num_codecs; codecs = calloc(num_codecs, sizeof(struct spa_dict_item)); @@ -4438,8 +4809,8 @@ while (spa_json_get_string(&it_array, codec_name, sizeof(codec_name)) > 0) { int i; - for (i = 0; a2dp_codecsi; ++i) { - const struct a2dp_codec *codec = a2dp_codecsi; + for (i = 0; media_codecsi; ++i) { + const struct media_codec *codec = media_codecsi; if (!spa_streq(codec->name, codec_name)) continue; @@ -4461,16 +4832,16 @@ spa_dict_qsort(&this->enabled_codecs); - for (i = 0; a2dp_codecsi; ++i) { - const struct a2dp_codec *codec = a2dp_codecsi; - if (!is_a2dp_codec_enabled(this, codec)) + for (i = 0; media_codecsi; ++i) { + const struct media_codec *codec = media_codecsi; + if (!is_media_codec_enabled(this, codec)) spa_log_debug(this->log, "disabling codec %s", codec->name); } return 0; fallback: - for (i = 0; a2dp_codecsi; ++i) { - const struct a2dp_codec *codec = a2dp_codecsi; + for (i = 0; media_codecsi; ++i) { + const struct media_codec *codec = media_codecsi; spa_log_debug(this->log, "enabling codec %s", codec->name); codecsi.key = codec->name; codecsi.value = "true"; @@ -4536,14 +4907,14 @@ return -EINVAL; } - this->a2dp_codecs = NULL; + this->media_codecs = NULL; this->quirks = NULL; this->conn = NULL; this->dbus_connection = NULL; - this->a2dp_codecs = load_a2dp_codecs(this->plugin_loader, this->log); - if (this->a2dp_codecs == NULL) { - spa_log_error(this->log, "failed to load required A2DP codec plugins"); + this->media_codecs = load_media_codecs(this->plugin_loader, this->log); + if (this->media_codecs == NULL) { + spa_log_error(this->log, "failed to load required media codec plugins"); res = -EIO; goto fail; } @@ -4640,15 +5011,15 @@ return 0; fail: - if (this->a2dp_codecs) - free_a2dp_codecs(this->a2dp_codecs); + if (this->media_codecs) + free_media_codecs(this->media_codecs); if (this->quirks) spa_bt_quirks_destroy(this->quirks); if (this->conn) dbus_connection_unref(this->conn); if (this->dbus_connection) spa_dbus_connection_destroy(this->dbus_connection); - this->a2dp_codecs = NULL; + this->media_codecs = NULL; this->quirks = NULL; this->conn = NULL; this->dbus_connection = NULL;
View file
pipewire-0.3.58.tar.gz/spa/plugins/bluez5/bluez5-device.c -> pipewire-0.3.59.tar.gz/spa/plugins/bluez5/bluez5-device.c
Changed
@@ -51,7 +51,7 @@ #include <spa/debug/pod.h> #include "defs.h" -#include "a2dp-codecs.h" +#include "media-codecs.h" static struct spa_log_topic log_topic = SPA_LOG_TOPIC(0, "spa.bluez5.device"); #undef SPA_LOG_TOPIC_DEFAULT @@ -73,6 +73,7 @@ DEVICE_PROFILE_AG = 1, DEVICE_PROFILE_A2DP = 2, DEVICE_PROFILE_HSP_HFP = 3, + DEVICE_PROFILE_BAP = 4, }; struct props { @@ -140,11 +141,11 @@ unsigned int save_profile:1; uint32_t prev_bt_connected_profiles; - const struct a2dp_codec **supported_codecs; + const struct media_codec **supported_codecs; size_t supported_codec_count; - struct dynamic_node dyn_a2dp_source; - struct dynamic_node dyn_a2dp_sink; + struct dynamic_node dyn_media_source; + struct dynamic_node dyn_media_sink; struct dynamic_node dyn_sco_source; struct dynamic_node dyn_sco_sink; @@ -167,9 +168,9 @@ } } -static void get_a2dp_codecs(struct impl *this, enum spa_bluetooth_audio_codec id, const struct a2dp_codec **codecs, size_t size) +static void get_media_codecs(struct impl *this, enum spa_bluetooth_audio_codec id, const struct media_codec **codecs, size_t size) { - const struct a2dp_codec * const *c; + const struct media_codec * const *c; spa_assert(size > 0); spa_assert(this->supported_codecs); @@ -184,18 +185,18 @@ *codecs = NULL; } -static const struct a2dp_codec *get_supported_a2dp_codec(struct impl *this, enum spa_bluetooth_audio_codec id, size_t *idx) +static const struct media_codec *get_supported_media_codec(struct impl *this, enum spa_bluetooth_audio_codec id, size_t *idx) { - const struct a2dp_codec *a2dp_codec = NULL; + const struct media_codec *media_codec = NULL; size_t i; for (i = 0; i < this->supported_codec_count; ++i) { if (this->supported_codecsi->id == id) { - a2dp_codec = this->supported_codecsi; + media_codec = this->supported_codecsi; if (idx) *idx = i; } } - return a2dp_codec; + return media_codec; } static unsigned int get_hfp_codec(enum spa_bluetooth_audio_codec id) @@ -245,10 +246,10 @@ static const char *get_codec_name(struct spa_bt_transport *t, bool a2dp_duplex) { - if (t->a2dp_codec != NULL) { - if (a2dp_duplex && t->a2dp_codec->duplex_codec) - return t->a2dp_codec->duplex_codec->name; - return t->a2dp_codec->name; + if (t->media_codec != NULL) { + if (a2dp_duplex && t->media_codec->duplex_codec) + return t->media_codec->duplex_codec->name; + return t->media_codec->name; } return get_hfp_codec_name(t->codec); } @@ -301,7 +302,7 @@ static float get_soft_volume_boost(struct node *node) { - const struct a2dp_codec *codec = node->transport ? node->transport->a2dp_codec : NULL; + const struct media_codec *codec = node->transport ? node->transport->media_codec : NULL; /* * For A2DP duplex, the duplex microphone channel sometimes does not appear @@ -351,6 +352,7 @@ /* PW is the controller for remote device. */ if (impl->profile != DEVICE_PROFILE_A2DP + && impl->profile != DEVICE_PROFILE_BAP && impl->profile != DEVICE_PROFILE_HSP_HFP) return false; @@ -406,16 +408,16 @@ static void get_channels(struct spa_bt_transport *t, bool a2dp_duplex, uint32_t *n_channels, uint32_t *channels) { - const struct a2dp_codec *codec; + const struct media_codec *codec; struct spa_audio_info info = { 0 }; - if (!a2dp_duplex || !t->a2dp_codec || !t->a2dp_codec->duplex_codec) { + if (!a2dp_duplex || !t->media_codec || !t->media_codec->duplex_codec) { *n_channels = t->n_channels; memcpy(channels, t->channels, t->n_channels * sizeof(uint32_t)); return; } - codec = t->a2dp_codec->duplex_codec; + codec = t->media_codec->duplex_codec; if (!codec->validate_config || codec->validate_config(codec, 0, @@ -514,7 +516,7 @@ spa_list_for_each(t, &device->transport_list, device_link) { bool codec_ok = codec == 0 || - (t->a2dp_codec != NULL && t->a2dp_codec->id == codec) || + (t->media_codec != NULL && t->media_codec->id == codec) || get_hfp_codec_id(t->codec) == codec; if ((t->profile & device->connected_profiles) && @@ -676,15 +678,15 @@ 1, SPA_NAME_API_BLUEZ5_SCO_SINK, false); } } - if (this->bt_dev->connected_profiles & SPA_BT_PROFILE_A2DP_SOURCE) { + if (this->bt_dev->connected_profiles & (SPA_BT_PROFILE_A2DP_SOURCE)) { t = find_transport(this, SPA_BT_PROFILE_A2DP_SOURCE, 0); if (t) { - this->props.codec = t->a2dp_codec->id; - emit_dynamic_node(&this->dyn_a2dp_source, this, t, + this->props.codec = t->media_codec->id; + emit_dynamic_node(&this->dyn_media_source, this, t, 2, SPA_NAME_API_BLUEZ5_A2DP_SOURCE, false); - if (t->a2dp_codec->duplex_codec) { - emit_dynamic_node(&this->dyn_a2dp_sink, this, t, + if (t->media_codec->duplex_codec) { + emit_dynamic_node(&this->dyn_media_sink, this, t, 3, SPA_NAME_API_BLUEZ5_A2DP_SINK, true); } } @@ -694,11 +696,11 @@ if (this->bt_dev->connected_profiles & SPA_BT_PROFILE_A2DP_SOURCE) { t = find_transport(this, SPA_BT_PROFILE_A2DP_SOURCE, 0); if (t) { - this->props.codec = t->a2dp_codec->id; - emit_dynamic_node(&this->dyn_a2dp_source, this, t, + this->props.codec = t->media_codec->id; + emit_dynamic_node(&this->dyn_media_source, this, t, DEVICE_ID_SOURCE, SPA_NAME_API_BLUEZ5_A2DP_SOURCE, false); - if (t->a2dp_codec->duplex_codec) { + if (t->media_codec->duplex_codec) { emit_node(this, t, DEVICE_ID_SINK, SPA_NAME_API_BLUEZ5_A2DP_SINK, true); } @@ -708,17 +710,45 @@ if (this->bt_dev->connected_profiles & SPA_BT_PROFILE_A2DP_SINK) { t = find_transport(this, SPA_BT_PROFILE_A2DP_SINK, this->props.codec); if (t) { - this->props.codec = t->a2dp_codec->id; + this->props.codec = t->media_codec->id; emit_node(this, t, DEVICE_ID_SINK, SPA_NAME_API_BLUEZ5_A2DP_SINK, false); - if (t->a2dp_codec->duplex_codec) { + if (t->media_codec->duplex_codec) { emit_node(this, t, DEVICE_ID_SOURCE, SPA_NAME_API_BLUEZ5_A2DP_SOURCE, true); } } } - if (get_supported_a2dp_codec(this, this->props.codec, NULL) == NULL) + if (get_supported_media_codec(this, this->props.codec, NULL) == NULL) + this->props.codec = 0; + break; + case DEVICE_PROFILE_BAP: + if (this->bt_dev->connected_profiles & (SPA_BT_PROFILE_BAP_SOURCE)) { + t = find_transport(this, SPA_BT_PROFILE_BAP_SOURCE, 0); + if (t) { + this->props.codec = t->media_codec->id; + if (t->bap_initiator) + emit_node(this, t, DEVICE_ID_SOURCE, SPA_NAME_API_BLUEZ5_MEDIA_SOURCE, false); + else + emit_dynamic_node(&this->dyn_media_source, this, t, + DEVICE_ID_SOURCE, SPA_NAME_API_BLUEZ5_MEDIA_SOURCE, false); + } + } + + if (this->bt_dev->connected_profiles & (SPA_BT_PROFILE_BAP_SINK)) { + t = find_transport(this, SPA_BT_PROFILE_BAP_SINK, this->props.codec); + if (t) { + this->props.codec = t->media_codec->id; + if (t->bap_initiator) + emit_node(this, t, DEVICE_ID_SINK, SPA_NAME_API_BLUEZ5_MEDIA_SINK, false); + else + emit_dynamic_node(&this->dyn_media_sink, this, t, + DEVICE_ID_SINK, SPA_NAME_API_BLUEZ5_MEDIA_SINK, false); + } + } + + if (get_supported_media_codec(this, this->props.codec, NULL) == NULL) this->props.codec = 0; break; case DEVICE_PROFILE_HSP_HFP: @@ -766,8 +796,8 @@ static void emit_remove_nodes(struct impl *this) { - remove_dynamic_node (&this->dyn_a2dp_source); - remove_dynamic_node (&this->dyn_a2dp_sink); + remove_dynamic_node (&this->dyn_media_source); + remove_dynamic_node (&this->dyn_media_sink); remove_dynamic_node (&this->dyn_sco_source); remove_dynamic_node (&this->dyn_sco_sink); @@ -800,6 +830,7 @@ if (this->profile == profile && (this->profile != DEVICE_PROFILE_A2DP || codec == this->props.codec) && + (this->profile != DEVICE_PROFILE_BAP || codec == this->props.codec) && (this->profile != DEVICE_PROFILE_HSP_HFP || codec == this->props.codec)) return 0; @@ -812,19 +843,20 @@ this->props.codec = codec; /* - * A2DP: ensure there's a transport with the selected codec (0 means any). + * A2DP/BAP: ensure there's a transport with the selected codec (0 means any). * Don't try to switch codecs when the device is in the A2DP source role, since * devices do not appear to like that. */ - if (profile == DEVICE_PROFILE_A2DP && !(this->bt_dev->connected_profiles & SPA_BT_PROFILE_A2DP_SOURCE)) { + if ((profile == DEVICE_PROFILE_A2DP || profile == DEVICE_PROFILE_BAP) + && !(this->bt_dev->connected_profiles & SPA_BT_PROFILE_A2DP_SOURCE)) { int ret; - const struct a2dp_codec *codecs64; + const struct media_codec *codecs64; - get_a2dp_codecs(this, codec, codecs, SPA_N_ELEMENTS(codecs)); + get_media_codecs(this, codec, codecs, SPA_N_ELEMENTS(codecs)); this->switching_codec = true; - ret = spa_bt_device_ensure_a2dp_codec(this->bt_dev, codecs); + ret = spa_bt_device_ensure_media_codec(this->bt_dev, codecs); if (ret < 0) { if (ret != -ENOTSUP) spa_log_error(this->log, "failed to switch codec (%d), setting basic profile", ret); @@ -873,6 +905,8 @@ spa_log_error(this->log, "failed to switch codec (%d), setting fallback profile", status); if (this->profile == DEVICE_PROFILE_A2DP && this->props.codec != 0) { this->props.codec = 0; + } else if (this->profile == DEVICE_PROFILE_BAP && this->props.codec != 0) { + this->props.codec = 0; } else if (this->profile == DEVICE_PROFILE_HSP_HFP && this->props.codec != 0) { this->props.codec = 0; } else { @@ -912,9 +946,9 @@ if (this->switching_codec) return; - if (this->bt_dev->connected_profiles & SPA_BT_PROFILE_A2DP_SINK) { + if (this->bt_dev->connected_profiles & SPA_BT_PROFILE_MEDIA_SINK) { free(this->supported_codecs); - this->supported_codecs = spa_bt_device_get_supported_a2dp_codecs( + this->supported_codecs = spa_bt_device_get_supported_media_codecs( this->bt_dev, &this->supported_codec_count, true); } @@ -925,16 +959,17 @@ break; case DEVICE_PROFILE_AG: nodes_changed = (connected_change & (SPA_BT_PROFILE_HEADSET_AUDIO_GATEWAY | - SPA_BT_PROFILE_A2DP_SOURCE)); + SPA_BT_PROFILE_MEDIA_SOURCE)); spa_log_debug(this->log, "profiles changed: AG nodes changed: %d", nodes_changed); break; case DEVICE_PROFILE_A2DP: - if (get_supported_a2dp_codec(this, this->props.codec, NULL) == NULL) + case DEVICE_PROFILE_BAP: + if (get_supported_media_codec(this, this->props.codec, NULL) == NULL) this->props.codec = 0; - nodes_changed = (connected_change & (SPA_BT_PROFILE_A2DP_SINK | - SPA_BT_PROFILE_A2DP_SOURCE)); - spa_log_debug(this->log, "profiles changed: A2DP nodes changed: %d", + nodes_changed = (connected_change & (SPA_BT_PROFILE_MEDIA_SINK | + SPA_BT_PROFILE_MEDIA_SOURCE)); + spa_log_debug(this->log, "profiles changed: media nodes changed: %d", nodes_changed); break; case DEVICE_PROFILE_HSP_HFP: @@ -1019,15 +1054,16 @@ struct spa_bt_device *device = this->bt_dev; uint32_t mask; bool have_output = false, have_input = false; - const struct a2dp_codec *a2dp_codec; + const struct media_codec *media_codec; switch (index) { case DEVICE_PROFILE_A2DP: - if (device->connected_profiles & SPA_BT_PROFILE_A2DP_SINK) + case DEVICE_PROFILE_BAP: + if (device->connected_profiles & SPA_BT_PROFILE_MEDIA_SINK) have_output = true; - a2dp_codec = get_supported_a2dp_codec(this, codec, NULL); - if (a2dp_codec && a2dp_codec->duplex_codec) + media_codec = get_supported_media_codec(this, codec, NULL); + if (media_codec && media_codec->duplex_codec) have_input = true; break; case DEVICE_PROFILE_HSP_HFP: @@ -1060,6 +1096,7 @@ return index; } else if (index != SPA_ID_INVALID) { const struct spa_type_info *info; + uint32_t profile; *codec = index - 3; *next = SPA_ID_INVALID; @@ -1068,7 +1105,14 @@ if (info->type > *codec) *next = SPA_MIN(info->type + 3, *next); - return get_hfp_codec(*codec) ? DEVICE_PROFILE_HSP_HFP : DEVICE_PROFILE_A2DP; + if (get_hfp_codec(*codec)) + profile = DEVICE_PROFILE_HSP_HFP; + else if (*codec == SPA_BLUETOOTH_AUDIO_CODEC_LC3) + profile = DEVICE_PROFILE_BAP; + else + profile = DEVICE_PROFILE_A2DP; + + return profile; } *next = SPA_ID_INVALID; @@ -1080,8 +1124,8 @@ if (profile == DEVICE_PROFILE_OFF || profile == DEVICE_PROFILE_AG) return profile; - if (profile == DEVICE_PROFILE_A2DP) { - if (codec == 0 || (this->bt_dev->connected_profiles & SPA_BT_PROFILE_A2DP_SOURCE)) + if (profile == DEVICE_PROFILE_A2DP || profile == DEVICE_PROFILE_BAP) { + if (codec == 0 || (this->bt_dev->connected_profiles & SPA_BT_PROFILE_MEDIA_SOURCE)) return profile; return codec + 3; @@ -1129,11 +1173,11 @@ if (this->supported_codecs) free(this->supported_codecs); - this->supported_codecs = spa_bt_device_get_supported_a2dp_codecs( + this->supported_codecs = spa_bt_device_get_supported_media_codecs( this->bt_dev, &this->supported_codec_count, true); - /* Prefer A2DP, then HFP, then null, but select AG if the device - appears not to have A2DP_SINK or any HEAD_UNIT profile */ + /* Prefer BAP, then A2DP, then HFP, then null, but select AG if the device + appears not to have BAP_SINK, A2DP_SINK or any HEAD_UNIT profile */ /* If default profile is set to HSP/HFP, first try those and exit if found. */ if (this->bt_dev->settings != NULL) { @@ -1144,16 +1188,20 @@ return; } - for (i = SPA_BT_PROFILE_A2DP_SINK; i <= SPA_BT_PROFILE_A2DP_SOURCE; i <<= 1) { + for (i = SPA_BT_PROFILE_BAP_SINK; i <= SPA_BT_PROFILE_A2DP_SOURCE; i <<= 1) { if (!(this->bt_dev->connected_profiles & i)) continue; t = find_transport(this, i, 0); if (t) { - this->profile = (i == SPA_BT_PROFILE_A2DP_SOURCE) ? - DEVICE_PROFILE_AG : DEVICE_PROFILE_A2DP; - this->props.codec = t->a2dp_codec->id; - spa_log_debug(this->log, "initial profile A2DP profile:%d codec:%d", + if (i == SPA_BT_PROFILE_A2DP_SOURCE || i == SPA_BT_PROFILE_BAP_SOURCE) + this->profile = DEVICE_PROFILE_AG; + else if (i == SPA_BT_PROFILE_BAP_SINK) + this->profile = DEVICE_PROFILE_BAP; + else + this->profile = DEVICE_PROFILE_A2DP; + this->props.codec = t->media_codec->id; + spa_log_debug(this->log, "initial profile media profile:%d codec:%d", this->profile, this->props.codec); return; } @@ -1213,19 +1261,19 @@ n_sink++; if (codec) { size_t idx; - const struct a2dp_codec *a2dp_codec = get_supported_a2dp_codec(this, codec, &idx); - if (a2dp_codec == NULL) { + const struct media_codec *media_codec = get_supported_media_codec(this, codec, &idx); + if (media_codec == NULL) { errno = EINVAL; return NULL; } - name_and_codec = spa_aprintf("%s-%s", name, a2dp_codec->name); + name_and_codec = spa_aprintf("%s-%s", name, media_codec->name); name = name_and_codec; - if (profile == SPA_BT_PROFILE_A2DP_SINK && !a2dp_codec->duplex_codec) { + if (profile == SPA_BT_PROFILE_A2DP_SINK && !media_codec->duplex_codec) { desc_and_codec = spa_aprintf(_("High Fidelity Playback (A2DP Sink, codec %s)"), - a2dp_codec->description); + media_codec->description); } else { desc_and_codec = spa_aprintf(_("High Fidelity Duplex (A2DP Source/Sink, codec %s)"), - a2dp_codec->description); + media_codec->description); } desc = desc_and_codec; @@ -1240,6 +1288,52 @@ } break; } + case DEVICE_PROFILE_BAP: + { + uint32_t profile = device->connected_profiles & + (SPA_BT_PROFILE_BAP_SINK | SPA_BT_PROFILE_BAP_SOURCE); + size_t idx; + const struct media_codec *media_codec; + + if (profile == 0) + return NULL; + + if (!codec) { + errno = EINVAL; + return NULL; + } + + if (profile & (SPA_BT_PROFILE_BAP_SINK)) + n_sink++; + if (profile & (SPA_BT_PROFILE_BAP_SOURCE)) + n_source++; + + name = spa_bt_profile_name(profile); + + media_codec = get_supported_media_codec(this, codec, &idx); + if (media_codec == NULL) { + errno = EINVAL; + return NULL; + } + name_and_codec = spa_aprintf("%s-%s", name, media_codec->name); + name = name_and_codec; + switch (profile) { + case SPA_BT_PROFILE_BAP_SINK: + desc_and_codec = spa_aprintf(_("High Fidelity Playback (BAP Sink, codec %s)"), + media_codec->description); + break; + case SPA_BT_PROFILE_BAP_SOURCE: + desc_and_codec = spa_aprintf(_("High Fidelity Input (BAP Source, codec %s)"), + media_codec->description); + break; + default: + desc_and_codec = spa_aprintf(_("High Fidelity Duplex (BAP Source/Sink, codec %s)"), + media_codec->description); + } + desc = desc_and_codec; + priority = 128 + this->supported_codec_count - idx; /* order as in codec list */ + break; + } case DEVICE_PROFILE_HSP_HFP: { /* make this device profile visible only if there is a head unit */ @@ -1537,7 +1631,8 @@ spa_pod_builder_array(b, sizeof(uint32_t), SPA_TYPE_Id, node->n_channels, node->channels); - if (this->profile == DEVICE_PROFILE_A2DP && dev == DEVICE_ID_SINK) { + if ((this->profile == DEVICE_PROFILE_A2DP || this->profile == DEVICE_PROFILE_BAP) && + dev == DEVICE_ID_SINK) { spa_pod_builder_prop(b, SPA_PROP_latencyOffsetNsec, 0); spa_pod_builder_long(b, node->latency_offset); } @@ -1560,7 +1655,7 @@ return spa_pod_builder_pop(b, &f0); } -static bool iterate_supported_a2dp_codecs(struct impl *this, int *j, const struct a2dp_codec **codec) +static bool iterate_supported_media_codecs(struct impl *this, int *j, const struct media_codec **codec) { int i; @@ -1582,12 +1677,12 @@ { struct spa_pod_frame f2; struct spa_pod_choice *choice; - const struct a2dp_codec *codec; + const struct media_codec *codec; size_t n; int j; -#define FOR_EACH_A2DP_CODEC(j, codec) \ - for (j = -1; iterate_supported_a2dp_codecs(this, &j, &codec);) +#define FOR_EACH_MEDIA_CODEC(j, codec) \ + for (j = -1; iterate_supported_media_codecs(this, &j, &codec);) #define FOR_EACH_HFP_CODEC(j) \ for (j = HFP_AUDIO_CODEC_MSBC; j >= HFP_AUDIO_CODEC_CVSD; --j) \ if (spa_bt_device_supports_hfp_codec(this->bt_dev, j) == 1) @@ -1608,8 +1703,8 @@ spa_pod_builder_push_choice(b, &f1, SPA_CHOICE_Enum, 0); choice = (struct spa_pod_choice *)spa_pod_builder_frame(b, &f1); n = 0; - if (this->profile == DEVICE_PROFILE_A2DP) { - FOR_EACH_A2DP_CODEC(j, codec) { + if (this->profile == DEVICE_PROFILE_A2DP || this->profile == DEVICE_PROFILE_BAP) { + FOR_EACH_MEDIA_CODEC(j, codec) { if (n == 0) spa_pod_builder_int(b, codec->id); spa_pod_builder_int(b, codec->id); @@ -1628,8 +1723,8 @@ spa_pod_builder_pop(b, &f1); spa_pod_builder_prop(b, SPA_PROP_INFO_labels, 0); spa_pod_builder_push_struct(b, &f1); - if (this->profile == DEVICE_PROFILE_A2DP) { - FOR_EACH_A2DP_CODEC(j, codec) { + if (this->profile == DEVICE_PROFILE_A2DP || this->profile == DEVICE_PROFILE_BAP) { + FOR_EACH_MEDIA_CODEC(j, codec) { spa_pod_builder_int(b, codec->id); spa_pod_builder_string(b, codec->description); } @@ -1642,7 +1737,7 @@ spa_pod_builder_pop(b, &f1); return spa_pod_builder_pop(b, &f0); -#undef FOR_EACH_A2DP_CODEC +#undef FOR_EACH_MEDIA_CODEC #undef FOR_EACH_HFP_CODEC } @@ -1688,6 +1783,7 @@ case DEVICE_PROFILE_OFF: case DEVICE_PROFILE_AG: case DEVICE_PROFILE_A2DP: + case DEVICE_PROFILE_BAP: case DEVICE_PROFILE_HSP_HFP: param = build_profile(this, &b, id, result.index, profile, codec, false); if (param == NULL) @@ -2024,7 +2120,7 @@ if (codec_id == SPA_ID_INVALID) return 0; - if (this->profile == DEVICE_PROFILE_A2DP) { + if (this->profile == DEVICE_PROFILE_A2DP || this->profile == DEVICE_PROFILE_BAP) { size_t j; for (j = 0; j < this->supported_codec_count; ++j) { if (this->supported_codecsj->id == codec_id) {
View file
pipewire-0.3.58.tar.gz/spa/plugins/bluez5/codec-loader.c -> pipewire-0.3.59.tar.gz/spa/plugins/bluez5/codec-loader.c
Changed
@@ -29,7 +29,7 @@ #include "defs.h" #include "codec-loader.h" -#define A2DP_CODEC_LIB_BASE "bluez5/libspa-codec-bluez5-" +#define MEDIA_CODEC_LIB_BASE "bluez5/libspa-codec-bluez5-" /* AVDTP allows 0x3E endpoints, can't have more codecs than that */ #define MAX_CODECS 0x3E @@ -40,7 +40,7 @@ #define SPA_LOG_TOPIC_DEFAULT &log_topic struct impl { - const struct a2dp_codec *codecsMAX_CODECS + 1; + const struct media_codec *codecsMAX_CODECS + 1; struct spa_handle *handlesMAX_HANDLES; size_t n_codecs; size_t n_handles; @@ -48,9 +48,10 @@ struct spa_log *log; }; -static int codec_order(const struct a2dp_codec *c) +static int codec_order(const struct media_codec *c) { static const enum spa_bluetooth_audio_codec order = { + SPA_BLUETOOTH_AUDIO_CODEC_LC3, SPA_BLUETOOTH_AUDIO_CODEC_LDAC, SPA_BLUETOOTH_AUDIO_CODEC_APTX_HD, SPA_BLUETOOTH_AUDIO_CODEC_APTX, @@ -78,8 +79,8 @@ static int codec_order_cmp(const void *a, const void *b) { - const struct a2dp_codec * const *ca = a; - const struct a2dp_codec * const *cb = b; + const struct media_codec * const *ca = a; + const struct media_codec * const *cb = b; int ia = codec_order(*ca); int ib = codec_order(*cb); if (*ca == *cb) @@ -87,7 +88,7 @@ return (ia == ib) ? (*ca < *cb ? -1 : 1) : ia - ib; } -static int load_a2dp_codecs_from(struct impl *impl, const char *factory_name, const char *libname) +static int load_media_codecs_from(struct impl *impl, const char *factory_name, const char *libname) { struct spa_handle *handle = NULL; void *iface; @@ -108,7 +109,7 @@ spa_log_debug(impl->log, "loading codecs from %s", factory_name); - if ((res = spa_handle_get_interface(handle, SPA_TYPE_INTERFACE_Bluez5CodecA2DP, &iface)) < 0) { + if ((res = spa_handle_get_interface(handle, SPA_TYPE_INTERFACE_Bluez5CodecMedia, &iface)) < 0) { spa_log_warn(impl->log, "Bluetooth codec plugin %s has no codec interface", factory_name); goto fail; @@ -116,15 +117,15 @@ bluez5_codec_a2dp = iface; - if (bluez5_codec_a2dp->iface.version != SPA_VERSION_BLUEZ5_CODEC_A2DP) { + if (bluez5_codec_a2dp->iface.version != SPA_VERSION_BLUEZ5_CODEC_MEDIA) { spa_log_warn(impl->log, "codec plugin %s has incompatible ABI version (%d != %d)", - factory_name, bluez5_codec_a2dp->iface.version, SPA_VERSION_BLUEZ5_CODEC_A2DP); + factory_name, bluez5_codec_a2dp->iface.version, SPA_VERSION_BLUEZ5_CODEC_MEDIA); res = -ENOENT; goto fail; } for (i = 0; bluez5_codec_a2dp->codecsi; ++i) { - const struct a2dp_codec *c = bluez5_codec_a2dp->codecsi; + const struct media_codec *c = bluez5_codec_a2dp->codecsi; size_t j; if (impl->n_codecs >= MAX_CODECS) { @@ -134,14 +135,14 @@ /* Don't load duplicate endpoints */ for (j = 0; j < impl->n_codecs; ++j) { - const struct a2dp_codec *c2 = impl->codecsj; + const struct media_codec *c2 = impl->codecsj; const char *ep1 = c->endpoint_name ? c->endpoint_name : c->name; const char *ep2 = c2->endpoint_name ? c2->endpoint_name : c2->name; if (spa_streq(ep1, ep2)) goto next_codec; } - spa_log_debug(impl->log, "loaded A2DP codec %s from %s", c->name, factory_name); + spa_log_debug(impl->log, "loaded media codec %s from %s", c->name, factory_name); if (c->set_log) c->set_log(impl->log); @@ -166,22 +167,23 @@ return res; } -const struct a2dp_codec * const *load_a2dp_codecs(struct spa_plugin_loader *loader, struct spa_log *log) +const struct media_codec * const *load_media_codecs(struct spa_plugin_loader *loader, struct spa_log *log) { struct impl *impl; bool has_sbc; size_t i; const struct { const char *factory; const char *lib; } plugins = { -#define A2DP_CODEC_FACTORY_LIB(basename) \ - { A2DP_CODEC_FACTORY_NAME(basename), A2DP_CODEC_LIB_BASE basename } - A2DP_CODEC_FACTORY_LIB("aac"), - A2DP_CODEC_FACTORY_LIB("aptx"), - A2DP_CODEC_FACTORY_LIB("faststream"), - A2DP_CODEC_FACTORY_LIB("ldac"), - A2DP_CODEC_FACTORY_LIB("sbc"), - A2DP_CODEC_FACTORY_LIB("lc3plus"), - A2DP_CODEC_FACTORY_LIB("opus") -#undef A2DP_CODEC_FACTORY_LIB +#define MEDIA_CODEC_FACTORY_LIB(basename) \ + { MEDIA_CODEC_FACTORY_NAME(basename), MEDIA_CODEC_LIB_BASE basename } + MEDIA_CODEC_FACTORY_LIB("aac"), + MEDIA_CODEC_FACTORY_LIB("aptx"), + MEDIA_CODEC_FACTORY_LIB("faststream"), + MEDIA_CODEC_FACTORY_LIB("ldac"), + MEDIA_CODEC_FACTORY_LIB("sbc"), + MEDIA_CODEC_FACTORY_LIB("lc3plus"), + MEDIA_CODEC_FACTORY_LIB("opus"), + MEDIA_CODEC_FACTORY_LIB("lc3") +#undef MEDIA_CODEC_FACTORY_LIB }; impl = calloc(sizeof(struct impl), 1); @@ -194,7 +196,7 @@ spa_log_topic_init(impl->log, &log_topic); for (i = 0; i < SPA_N_ELEMENTS(plugins); ++i) - load_a2dp_codecs_from(impl, pluginsi.factory, pluginsi.lib); + load_media_codecs_from(impl, pluginsi.factory, pluginsi.lib); has_sbc = false; for (i = 0; i < impl->n_codecs; ++i) @@ -203,19 +205,19 @@ if (!has_sbc) { spa_log_error(impl->log, "failed to load A2DP SBC codec from plugins"); - free_a2dp_codecs(impl->codecs); + free_media_codecs(impl->codecs); errno = ENOENT; return NULL; } - qsort(impl->codecs, impl->n_codecs, sizeof(const struct a2dp_codec *), codec_order_cmp); + qsort(impl->codecs, impl->n_codecs, sizeof(const struct media_codec *), codec_order_cmp); return impl->codecs; } -void free_a2dp_codecs(const struct a2dp_codec * const *a2dp_codecs) +void free_media_codecs(const struct media_codec * const *media_codecs) { - struct impl *impl = SPA_CONTAINER_OF(a2dp_codecs, struct impl, codecs); + struct impl *impl = SPA_CONTAINER_OF(media_codecs, struct impl, codecs); size_t i; for (i = 0; i < impl->n_handles; ++i)
View file
pipewire-0.3.58.tar.gz/spa/plugins/bluez5/codec-loader.h -> pipewire-0.3.59.tar.gz/spa/plugins/bluez5/codec-loader.h
Changed
@@ -31,9 +31,9 @@ #include <spa/support/plugin-loader.h> #include "a2dp-codec-caps.h" -#include "a2dp-codecs.h" +#include "media-codecs.h" -const struct a2dp_codec * const *load_a2dp_codecs(struct spa_plugin_loader *loader, struct spa_log *log); -void free_a2dp_codecs(const struct a2dp_codec * const *a2dp_codecs); +const struct media_codec * const *load_media_codecs(struct spa_plugin_loader *loader, struct spa_log *log); +void free_media_codecs(const struct media_codec * const *media_codecs); #endif
View file
pipewire-0.3.58.tar.gz/spa/plugins/bluez5/defs.h -> pipewire-0.3.59.tar.gz/spa/plugins/bluez5/defs.h
Changed
@@ -141,6 +141,9 @@ #define SPA_BT_UUID_HSP_AG "00001112-0000-1000-8000-00805f9b34fb" #define SPA_BT_UUID_HFP_HF "0000111e-0000-1000-8000-00805f9b34fb" #define SPA_BT_UUID_HFP_AG "0000111f-0000-1000-8000-00805f9b34fb" +#define SPA_BT_UUID_PACS "00001850-0000-1000-8000-00805f9b34fb" +#define SPA_BT_UUID_BAP_SINK "00002bc9-0000-1000-8000-00805f9b34fb" +#define SPA_BT_UUID_BAP_SOURCE "00002bcb-0000-1000-8000-00805f9b34fb" #define PROFILE_HSP_AG "/Profile/HSPAG" #define PROFILE_HSP_HS "/Profile/HSPHS" @@ -158,9 +161,12 @@ #define HFP_AUDIO_CODEC_CVSD 0x01 #define HFP_AUDIO_CODEC_MSBC 0x02 -#define A2DP_OBJECT_MANAGER_PATH "/MediaEndpoint" -#define A2DP_SINK_ENDPOINT A2DP_OBJECT_MANAGER_PATH "/A2DPSink" -#define A2DP_SOURCE_ENDPOINT A2DP_OBJECT_MANAGER_PATH "/A2DPSource" +#define MEDIA_OBJECT_MANAGER_PATH "/MediaEndpoint" +#define A2DP_SINK_ENDPOINT MEDIA_OBJECT_MANAGER_PATH "/A2DPSink" +#define A2DP_SOURCE_ENDPOINT MEDIA_OBJECT_MANAGER_PATH "/A2DPSource" + +#define BAP_SINK_ENDPOINT MEDIA_OBJECT_MANAGER_PATH "/BAPSink" +#define BAP_SOURCE_ENDPOINT MEDIA_OBJECT_MANAGER_PATH "/BAPSource" #define SPA_BT_UNKNOWN_DELAY 0 @@ -172,19 +178,30 @@ #define MSBC_ENCODED_SIZE 60 /* 2 bytes header + 57 mSBC payload + 1 byte padding */ #define MSBC_PAYLOAD_SIZE 57 +enum spa_bt_media_direction { + SPA_BT_MEDIA_SOURCE, + SPA_BT_MEDIA_SINK, +}; + enum spa_bt_profile { SPA_BT_PROFILE_NULL = 0, - SPA_BT_PROFILE_A2DP_SINK = (1 << 0), - SPA_BT_PROFILE_A2DP_SOURCE = (1 << 1), - SPA_BT_PROFILE_HSP_HS = (1 << 2), - SPA_BT_PROFILE_HSP_AG = (1 << 3), - SPA_BT_PROFILE_HFP_HF = (1 << 4), - SPA_BT_PROFILE_HFP_AG = (1 << 5), + SPA_BT_PROFILE_BAP_SINK = (1 << 0), + SPA_BT_PROFILE_BAP_SOURCE = (1 << 1), + SPA_BT_PROFILE_A2DP_SINK = (1 << 2), + SPA_BT_PROFILE_A2DP_SOURCE = (1 << 3), + SPA_BT_PROFILE_HSP_HS = (1 << 4), + SPA_BT_PROFILE_HSP_AG = (1 << 5), + SPA_BT_PROFILE_HFP_HF = (1 << 6), + SPA_BT_PROFILE_HFP_AG = (1 << 7), SPA_BT_PROFILE_A2DP_DUPLEX = (SPA_BT_PROFILE_A2DP_SINK | SPA_BT_PROFILE_A2DP_SOURCE), + SPA_BT_PROFILE_BAP_DUPLEX = (SPA_BT_PROFILE_BAP_SINK | SPA_BT_PROFILE_BAP_SOURCE), SPA_BT_PROFILE_HEADSET_HEAD_UNIT = (SPA_BT_PROFILE_HSP_HS | SPA_BT_PROFILE_HFP_HF), SPA_BT_PROFILE_HEADSET_AUDIO_GATEWAY = (SPA_BT_PROFILE_HSP_AG | SPA_BT_PROFILE_HFP_AG), SPA_BT_PROFILE_HEADSET_AUDIO = (SPA_BT_PROFILE_HEADSET_HEAD_UNIT | SPA_BT_PROFILE_HEADSET_AUDIO_GATEWAY), + + SPA_BT_PROFILE_MEDIA_SINK = (SPA_BT_PROFILE_A2DP_SINK | SPA_BT_PROFILE_BAP_SINK), + SPA_BT_PROFILE_MEDIA_SOURCE = (SPA_BT_PROFILE_A2DP_SOURCE | SPA_BT_PROFILE_BAP_SOURCE), }; static inline enum spa_bt_profile spa_bt_profile_from_uuid(const char *uuid) @@ -203,6 +220,10 @@ return SPA_BT_PROFILE_HFP_HF; else if (strcasecmp(uuid, SPA_BT_UUID_HFP_AG) == 0) return SPA_BT_PROFILE_HFP_AG; + else if (strcasecmp(uuid, SPA_BT_UUID_BAP_SINK) == 0) + return SPA_BT_PROFILE_BAP_SINK; + else if (strcasecmp(uuid, SPA_BT_UUID_BAP_SOURCE) == 0) + return SPA_BT_PROFILE_BAP_SOURCE; else return 0; } @@ -303,6 +324,12 @@ return "headset-audio-gateway"; case SPA_BT_PROFILE_HEADSET_AUDIO: return "headset-audio"; + case SPA_BT_PROFILE_BAP_SOURCE: + return "bap-source"; + case SPA_BT_PROFILE_BAP_SINK: + return "bap-sink"; + case SPA_BT_PROFILE_BAP_DUPLEX: + return "bap-duplex"; default: break; } @@ -412,7 +439,7 @@ return SPA_BT_FORM_FACTOR_UNKNOWN; } -struct spa_bt_a2dp_codec_switch; +struct spa_bt_media_codec_switch; struct spa_bt_transport; struct spa_bt_device_events { @@ -482,16 +509,16 @@ DBusPendingCall *battery_pending_call; }; -struct a2dp_codec; +struct media_codec; struct spa_bt_device *spa_bt_device_find(struct spa_bt_monitor *monitor, const char *path); struct spa_bt_device *spa_bt_device_find_by_address(struct spa_bt_monitor *monitor, const char *remote_address, const char *local_address); int spa_bt_device_add_profile(struct spa_bt_device *device, enum spa_bt_profile profile); int spa_bt_device_connect_profile(struct spa_bt_device *device, enum spa_bt_profile profile); int spa_bt_device_check_profiles(struct spa_bt_device *device, bool force); -int spa_bt_device_ensure_a2dp_codec(struct spa_bt_device *device, const struct a2dp_codec * const *codecs); -bool spa_bt_device_supports_a2dp_codec(struct spa_bt_device *device, const struct a2dp_codec *codec, bool sink); -const struct a2dp_codec **spa_bt_device_get_supported_a2dp_codecs(struct spa_bt_device *device, size_t *count, bool sink); +int spa_bt_device_ensure_media_codec(struct spa_bt_device *device, const struct media_codec * const *codecs); +bool spa_bt_device_supports_media_codec(struct spa_bt_device *device, const struct media_codec *codec, bool sink); +const struct media_codec **spa_bt_device_get_supported_media_codecs(struct spa_bt_device *device, size_t *count, bool sink); int spa_bt_device_ensure_hfp_codec(struct spa_bt_device *device, unsigned int codec); int spa_bt_device_supports_hfp_codec(struct spa_bt_device *device, unsigned int codec); int spa_bt_device_release_transports(struct spa_bt_device *device); @@ -570,11 +597,13 @@ struct spa_list device_link; enum spa_bt_profile profile; enum spa_bt_transport_state state; - const struct a2dp_codec *a2dp_codec; + const struct media_codec *media_codec; unsigned int codec; void *configuration; int configuration_len; char *endpoint_path; + bool bap_initiator; + struct spa_list bap_transport_linked; uint32_t n_channels; uint32_t channels64;
View file
pipewire-0.3.59.tar.gz/spa/plugins/bluez5/media-codecs.c
Added
@@ -0,0 +1,209 @@ +/* + * BlueALSA - bluez-a2dp.c + * Copyright (c) 2016-2017 Arkadiusz Bokowy + * + * This file is a part of bluez-alsa. + * + * This project is licensed under the terms of the MIT license. + * + */ + +#include <spa/utils/string.h> + +#include "media-codecs.h" + +int media_codec_select_config(const struct media_codec_config configs, size_t n, + uint32_t cap, int preferred_value) +{ + size_t i; + int *scores, res; + unsigned int max_priority; + + if (n == 0) + return -EINVAL; + + scores = calloc(n, sizeof(int)); + if (scores == NULL) + return -errno; + + max_priority = configs0.priority; + for (i = 1; i < n; ++i) { + if (configsi.priority > max_priority) + max_priority = configsi.priority; + } + + for (i = 0; i < n; ++i) { + if (!(configsi.config & cap)) { + scoresi = -1; + continue; + } + if (configsi.value == preferred_value) + scoresi = 100 * (max_priority + 1); + else if (configsi.value > preferred_value) + scoresi = 10 * (max_priority + 1); + else + scoresi = 1; + + scoresi *= configsi.priority + 1; + } + + res = 0; + for (i = 1; i < n; ++i) { + if (scoresi > scoresres) + res = i; + } + + if (scoresres < 0) + res = -EINVAL; + + free(scores); + return res; +} + +bool media_codec_check_caps(const struct media_codec *codec, unsigned int codec_id, + const void *caps, size_t caps_size, + const struct media_codec_audio_info *info, + const struct spa_dict *global_settings) +{ + uint8_t configA2DP_MAX_CAPS_SIZE; + int res; + + if (codec_id != codec->codec_id) + return false; + + if (caps == NULL) + return false; + + res = codec->select_config(codec, 0, caps, caps_size, info, global_settings, config); + if (res < 0) + return false; + + return ((size_t)res == caps_size); +} + +#ifdef CODEC_PLUGIN + +struct impl { + struct spa_handle handle; + struct spa_bluez5_codec_a2dp bluez5_codec_a2dp; +}; + +static int +impl_get_interface(struct spa_handle *handle, const char *type, void **interface) +{ + struct impl *this; + + spa_return_val_if_fail(handle != NULL, -EINVAL); + spa_return_val_if_fail(interface != NULL, -EINVAL); + + this = (struct impl *) handle; + + if (spa_streq(type, SPA_TYPE_INTERFACE_Bluez5CodecMedia)) + *interface = &this->bluez5_codec_a2dp; + else + return -ENOENT; + + return 0; +} + +static int +impl_clear(struct spa_handle *handle) +{ + spa_return_val_if_fail(handle != NULL, -EINVAL); + return 0; +} + +static size_t +impl_get_size(const struct spa_handle_factory *factory, const struct spa_dict *params) +{ + return sizeof(struct impl); +} + +static int +impl_init(const struct spa_handle_factory *factory, + struct spa_handle *handle, + const struct spa_dict *info, + const struct spa_support *support, + uint32_t n_support) +{ + struct impl *this; + + spa_return_val_if_fail(factory != NULL, -EINVAL); + spa_return_val_if_fail(handle != NULL, -EINVAL); + + handle->get_interface = impl_get_interface; + handle->clear = impl_clear; + + this = (struct impl *) handle; + + this->bluez5_codec_a2dp.codecs = codec_plugin_media_codecs; + this->bluez5_codec_a2dp.iface = SPA_INTERFACE_INIT( + SPA_TYPE_INTERFACE_Bluez5CodecMedia, + SPA_VERSION_BLUEZ5_CODEC_MEDIA, + NULL, + this); + + return 0; +} + +static const struct spa_interface_info impl_interfaces = { + {SPA_TYPE_INTERFACE_Bluez5CodecMedia,}, +}; + +static int +impl_enum_interface_info(const struct spa_handle_factory *factory, + const struct spa_interface_info **info, + uint32_t *index) +{ + spa_return_val_if_fail(factory != NULL, -EINVAL); + spa_return_val_if_fail(info != NULL, -EINVAL); + spa_return_val_if_fail(index != NULL, -EINVAL); + + switch (*index) { + case 0: + *info = &impl_interfaces*index; + break; + default: + return 0; + } + (*index)++; + + return 1; +} + +static const struct spa_dict_item handle_info_items = { + { SPA_KEY_FACTORY_DESCRIPTION, "Bluetooth codec plugin" }, +}; + +static const struct spa_dict handle_info = SPA_DICT_INIT_ARRAY(handle_info_items); + +static struct spa_handle_factory handle_factory = { + SPA_VERSION_HANDLE_FACTORY, + NULL, + &handle_info, + impl_get_size, + impl_init, + impl_enum_interface_info, +}; + +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); + + if (handle_factory.name == NULL) + handle_factory.name = codec_plugin_factory_name; + + switch (*index) { + case 0: + *factory = &handle_factory; + break; + default: + return 0; + } + (*index)++; + return 1; +} + +#endif
View file
pipewire-0.3.59.tar.gz/spa/plugins/bluez5/media-codecs.h
Added
@@ -0,0 +1,184 @@ +/* Spa A2DP codec API + * + * Copyright © 2020 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_BLUEZ5_A2DP_CODECS_H_ +#define SPA_BLUEZ5_A2DP_CODECS_H_ + +#include <stdint.h> +#include <stddef.h> + +#include <spa/param/audio/format.h> +#include <spa/param/bluetooth/audio.h> +#include <spa/utils/names.h> +#include <spa/support/plugin.h> +#include <spa/pod/pod.h> +#include <spa/pod/builder.h> +#include <spa/support/log.h> + +#include "a2dp-codec-caps.h" + +/* + * The codec plugin SPA interface is private. The version should be incremented + * when any of the structs or semantics change. + */ + +#define SPA_TYPE_INTERFACE_Bluez5CodecMedia SPA_TYPE_INFO_INTERFACE_BASE "Bluez5:Codec:Media:Private" + +#define SPA_VERSION_BLUEZ5_CODEC_MEDIA 5 + +struct spa_bluez5_codec_a2dp { + struct spa_interface iface; + const struct media_codec * const *codecs; /**< NULL terminated array */ +}; + +#define MEDIA_CODEC_FACTORY_NAME(basename) (SPA_NAME_API_CODEC_BLUEZ5_MEDIA "." basename) + +#ifdef CODEC_PLUGIN +#define MEDIA_CODEC_EXPORT_DEF(basename,...) \ + const char *codec_plugin_factory_name = MEDIA_CODEC_FACTORY_NAME(basename); \ + static const struct media_codec * const codec_plugin_media_codec_list = { __VA_ARGS__, NULL }; \ + const struct media_codec * const * const codec_plugin_media_codecs = codec_plugin_media_codec_list; + +extern const struct media_codec * const * const codec_plugin_media_codecs; +extern const char *codec_plugin_factory_name; +#endif + +#define MEDIA_CODEC_FLAG_SINK (1 << 0) + +#define A2DP_CODEC_DEFAULT_RATE 48000 +#define A2DP_CODEC_DEFAULT_CHANNELS 2 + +enum { + NEED_FLUSH_NO = 0, + NEED_FLUSH_ALL = 1, + NEED_FLUSH_FRAGMENT = 2, +}; + +struct media_codec_audio_info { + uint32_t rate; + uint32_t channels; +}; + +struct codec_qos { + uint32_t interval; + bool framing; + char *phy; + uint16_t sdu; + uint8_t retransmission; + uint16_t latency; + uint32_t delay; +}; + +struct media_codec { + enum spa_bluetooth_audio_codec id; + uint8_t codec_id; + a2dp_vendor_codec_t vendor; + + bool bap; + + const char *name; + const char *description; + const char *endpoint_name; /**< Endpoint name. If NULL, same as name */ + const struct spa_dict *info; + + const size_t send_buf_size; + + const struct media_codec *duplex_codec; /**< Codec for non-standard A2DP duplex channel */ + + struct spa_log *log; + + int (*fill_caps) (const struct media_codec *codec, uint32_t flags, + uint8_t capsA2DP_MAX_CAPS_SIZE); + int (*select_config) (const struct media_codec *codec, uint32_t flags, + const void *caps, size_t caps_size, + const struct media_codec_audio_info *info, + const struct spa_dict *global_settings, uint8_t configA2DP_MAX_CAPS_SIZE); + int (*enum_config) (const struct media_codec *codec, uint32_t flags, + const void *caps, size_t caps_size, uint32_t id, uint32_t idx, + struct spa_pod_builder *builder, struct spa_pod **param); + int (*validate_config) (const struct media_codec *codec, uint32_t flags, + const void *caps, size_t caps_size, + struct spa_audio_info *info); + void (*get_qos)(const struct media_codec *codec, + const void *config, size_t config_size, + struct codec_qos *qos); + + /** qsort comparison sorting caps in order of preference for the codec. + * Used in codec switching to select best remote endpoints. + * The caps handed in correspond to this codec_id, but are + * otherwise not checked beforehand. + */ + int (*caps_preference_cmp) (const struct media_codec *codec, uint32_t flags, const void *caps1, size_t caps1_size, + const void *caps2, size_t caps2_size, const struct media_codec_audio_info *info, + const struct spa_dict *global_settings); + + void *(*init_props) (const struct media_codec *codec, uint32_t flags, const struct spa_dict *settings); + void (*clear_props) (void *); + int (*enum_props) (void *props, const struct spa_dict *settings, uint32_t id, uint32_t idx, + struct spa_pod_builder *builder, struct spa_pod **param); + int (*set_props) (void *props, const struct spa_pod *param); + + void *(*init) (const struct media_codec *codec, uint32_t flags, void *config, size_t config_size, + const struct spa_audio_info *info, void *props, size_t mtu); + void (*deinit) (void *data); + + int (*update_props) (void *data, void *props); + + int (*get_block_size) (void *data); + + int (*abr_process) (void *data, size_t unsent); + + int (*start_encode) (void *data, + void *dst, size_t dst_size, uint16_t seqnum, uint32_t timestamp); + int (*encode) (void *data, + const void *src, size_t src_size, + void *dst, size_t dst_size, + size_t *dst_out, int *need_flush); + + int (*start_decode) (void *data, + const void *src, size_t src_size, uint16_t *seqnum, uint32_t *timestamp); + int (*decode) (void *data, + const void *src, size_t src_size, + void *dst, size_t dst_size, + size_t *dst_out); + + int (*reduce_bitpool) (void *data); + int (*increase_bitpool) (void *data); + + void (*set_log) (struct spa_log *global_log); +}; + +struct media_codec_config { + uint32_t config; + int value; + unsigned int priority; +}; + +int media_codec_select_config(const struct media_codec_config configs, size_t n, + uint32_t cap, int preferred_value); + +bool media_codec_check_caps(const struct media_codec *codec, unsigned int codec_id, + const void *caps, size_t caps_size, const struct media_codec_audio_info *info, + const struct spa_dict *global_settings); + +#endif
View file
pipewire-0.3.59.tar.gz/spa/plugins/bluez5/media-sink.c
Added
@@ -0,0 +1,1854 @@ +/* Spa Media Sink + * + * 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 <unistd.h> +#include <stddef.h> +#include <stdio.h> +#include <arpa/inet.h> +#include <sys/ioctl.h> + +#include <spa/support/plugin.h> +#include <spa/support/loop.h> +#include <spa/support/log.h> +#include <spa/support/system.h> +#include <spa/utils/list.h> +#include <spa/utils/keys.h> +#include <spa/utils/names.h> +#include <spa/utils/result.h> +#include <spa/utils/string.h> +#include <spa/monitor/device.h> + +#include <spa/node/node.h> +#include <spa/node/utils.h> +#include <spa/node/io.h> +#include <spa/node/keys.h> +#include <spa/param/param.h> +#include <spa/param/latency-utils.h> +#include <spa/param/audio/format.h> +#include <spa/param/audio/format-utils.h> +#include <spa/pod/filter.h> + +#include <sbc/sbc.h> + +#include "defs.h" +#include "rtp.h" +#include "media-codecs.h" + +static struct spa_log_topic log_topic = SPA_LOG_TOPIC(0, "spa.bluez5.sink.media"); +#undef SPA_LOG_TOPIC_DEFAULT +#define SPA_LOG_TOPIC_DEFAULT &log_topic + +#define DEFAULT_CLOCK_NAME "clock.system.monotonic" + +struct props { + uint32_t min_latency; + uint32_t max_latency; + int64_t latency_offset; + char clock_name64; +}; + +#define FILL_FRAMES 2 +#define MAX_BUFFERS 32 +#define MIN_LATENCY 128 +#define MAX_LATENCY 8192 +#define BUFFER_SIZE (MAX_LATENCY*8) + +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; +}; + +struct port { + struct spa_audio_info current_format; + uint32_t frame_size; + unsigned int have_format:1; + + uint64_t info_all; + struct spa_port_info info; + struct spa_io_buffers *io; + struct spa_latency_info latency; +#define IDX_EnumFormat 0 +#define IDX_Meta 1 +#define IDX_IO 2 +#define IDX_Format 3 +#define IDX_Buffers 4 +#define IDX_Latency 5 +#define N_PORT_PARAMS 6 + struct spa_param_info paramsN_PORT_PARAMS; + + struct buffer buffersMAX_BUFFERS; + uint32_t n_buffers; + + struct spa_list free; + struct spa_list ready; + + size_t ready_offset; +}; + +struct impl { + struct spa_handle handle; + struct spa_node node; + + struct spa_log *log; + struct spa_loop *data_loop; + struct spa_system *data_system; + + struct spa_hook_list hooks; + struct spa_callbacks callbacks; + + uint64_t info_all; + struct spa_node_info info; +#define IDX_PropInfo 0 +#define IDX_Props 1 +#define N_NODE_PARAMS 2 + struct spa_param_info paramsN_NODE_PARAMS; + struct props props; + + struct spa_bt_transport *transport; + struct spa_hook transport_listener; + + struct port port; + + unsigned int started:1; + unsigned int following:1; + unsigned int is_output:1; + + unsigned int is_duplex:1; + + struct spa_source source; + int timerfd; + struct spa_source flush_source; + struct spa_source flush_timer_source; + int flush_timerfd; + + struct spa_io_clock *clock; + struct spa_io_position *position; + + uint64_t current_time; + uint64_t next_time; + uint64_t last_error; + + const struct media_codec *codec; + bool codec_props_changed; + void *codec_props; + void *codec_data; + struct spa_audio_info codec_format; + + int need_flush; + bool fragment; + uint64_t fragment_timeout; + uint32_t block_size; + uint8_t bufferBUFFER_SIZE; + uint32_t buffer_used; + uint32_t header_size; + uint32_t frame_count; + uint16_t seqnum; + uint32_t timestamp; + uint64_t sample_count; + uint8_t tmp_bufferBUFFER_SIZE; + uint32_t tmp_buffer_used; + uint32_t fd_buffer_size; + + /* Times */ + uint64_t start_time; + uint64_t total_samples; +}; + +#define CHECK_PORT(this,d,p) ((d) == SPA_DIRECTION_INPUT && (p) == 0) + +static void reset_props(struct impl *this, struct props *props) +{ + props->min_latency = MIN_LATENCY; + props->max_latency = MAX_LATENCY; + props->latency_offset = 0; + strncpy(props->clock_name, DEFAULT_CLOCK_NAME, sizeof(props->clock_name)); +} + +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 impl *this = object; + struct spa_pod *param; + struct spa_pod_builder b = { 0 }; + uint8_t buffer1024; + struct spa_result_node_params result; + uint32_t count = 0, index_offset = 0; + bool enum_codec = false; + + 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: + { + struct props *p = &this->props; + + switch (result.index) { + case 0: + param = spa_pod_builder_add_object(&b, + SPA_TYPE_OBJECT_PropInfo, id, + SPA_PROP_INFO_id, SPA_POD_Id(SPA_PROP_minLatency), + SPA_PROP_INFO_description, SPA_POD_String("The minimum latency"), + SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Int(p->min_latency, 1, INT32_MAX)); + break; + case 1: + param = spa_pod_builder_add_object(&b, + SPA_TYPE_OBJECT_PropInfo, id, + SPA_PROP_INFO_id, SPA_POD_Id(SPA_PROP_maxLatency), + SPA_PROP_INFO_description, SPA_POD_String("The maximum latency"), + SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Int(p->max_latency, 1, INT32_MAX)); + break; + case 2: + param = spa_pod_builder_add_object(&b, + SPA_TYPE_OBJECT_PropInfo, id, + SPA_PROP_INFO_id, SPA_POD_Id(SPA_PROP_latencyOffsetNsec), + SPA_PROP_INFO_description, SPA_POD_String("Latency offset (ns)"), + SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Long(0LL, INT64_MIN, INT64_MAX)); + break; + default: + enum_codec = true; + index_offset = 3; + } + break; + } + case SPA_PARAM_Props: + { + struct props *p = &this->props; + + switch (result.index) { + case 0: + param = spa_pod_builder_add_object(&b, + SPA_TYPE_OBJECT_Props, id, + SPA_PROP_minLatency, SPA_POD_Int(p->min_latency), + SPA_PROP_maxLatency, SPA_POD_Int(p->max_latency), + SPA_PROP_latencyOffsetNsec, SPA_POD_Long(p->latency_offset)); + break; + default: + enum_codec = true; + index_offset = 1; + } + break; + } + default: + return -ENOENT; + } + + if (enum_codec) { + int res; + if (this->codec->enum_props == NULL || this->codec_props == NULL || + this->transport == NULL) + return 0; + else if ((res = this->codec->enum_props(this->codec_props, + this->transport->device->settings, + id, result.index - index_offset, &b, ¶m)) != 1) + return res; + } + + 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; +} + +static int set_timeout(struct impl *this, uint64_t time) +{ + struct itimerspec ts; + ts.it_value.tv_sec = time / SPA_NSEC_PER_SEC; + ts.it_value.tv_nsec = time % SPA_NSEC_PER_SEC; + ts.it_interval.tv_sec = 0; + ts.it_interval.tv_nsec = 0; + return spa_system_timerfd_settime(this->data_system, + this->timerfd, SPA_FD_TIMER_ABSTIME, &ts, NULL); +} + +static int set_timers(struct impl *this) +{ + struct timespec now; + + spa_system_clock_gettime(this->data_system, CLOCK_MONOTONIC, &now); + this->next_time = SPA_TIMESPEC_TO_NSEC(&now); + + return set_timeout(this, this->following ? 0 : this->next_time); +} + +static int do_reassign_follower(struct spa_loop *loop, + bool async, + uint32_t seq, + const void *data, + size_t size, + void *user_data) +{ + struct impl *this = user_data; + set_timers(this); + return 0; +} + +static inline bool is_following(struct impl *this) +{ + return this->position && this->clock && this->position->clock.id != this->clock->id; +} + +static int impl_node_set_io(void *object, uint32_t id, void *data, size_t size) +{ + struct impl *this = object; + bool following; + + spa_return_val_if_fail(this != NULL, -EINVAL); + + switch (id) { + case SPA_IO_Clock: + this->clock = data; + if (this->clock != NULL) { + spa_scnprintf(this->clock->name, + sizeof(this->clock->name), + "%s", this->props.clock_name); + } + break; + case SPA_IO_Position: + this->position = data; + break; + default: + return -ENOENT; + } + + following = is_following(this); + if (this->started && following != this->following) { + spa_log_debug(this->log, "%p: reassign follower %d->%d", this, this->following, following); + this->following = following; + spa_loop_invoke(this->data_loop, do_reassign_follower, 0, NULL, 0, true, this); + } + return 0; +} + +static void emit_node_info(struct impl *this, bool full); + +static void emit_port_info(struct impl *this, struct port *port, bool full); + +static void set_latency(struct impl *this, bool emit_latency) +{ + struct port *port = &this->port; + int64_t delay; + + if (this->transport == NULL) + return; + + delay = spa_bt_transport_get_delay_nsec(this->transport); + delay += SPA_CLAMP(this->props.latency_offset, -delay, INT64_MAX / 2); + port->latency.min_ns = port->latency.max_ns = delay; + + if (emit_latency) { + port->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS; + port->paramsIDX_Latency.flags ^= SPA_PARAM_INFO_SERIAL; + emit_port_info(this, port, false); + } +} + +static int apply_props(struct impl *this, const struct spa_pod *param) +{ + struct props new_props = this->props; + int changed = 0; + + if (param == NULL) { + reset_props(this, &new_props); + } else { + spa_pod_parse_object(param, + SPA_TYPE_OBJECT_Props, NULL, + SPA_PROP_minLatency, SPA_POD_OPT_Int(&new_props.min_latency), + SPA_PROP_maxLatency, SPA_POD_OPT_Int(&new_props.max_latency), + SPA_PROP_latencyOffsetNsec, SPA_POD_OPT_Long(&new_props.latency_offset)); + } + + changed = (memcmp(&new_props, &this->props, sizeof(struct props)) != 0); + this->props = new_props; + + if (changed) + set_latency(this, true); + + return changed; +} + +static int impl_node_set_param(void *object, uint32_t id, uint32_t flags, + const struct spa_pod *param) +{ + struct impl *this = object; + + spa_return_val_if_fail(this != NULL, -EINVAL); + + switch (id) { + case SPA_PARAM_Props: + { + int res, codec_res = 0; + res = apply_props(this, param); + if (this->codec_props && this->codec->set_props) { + codec_res = this->codec->set_props(this->codec_props, param); + if (codec_res > 0) + this->codec_props_changed = true; + } + if (res > 0 || codec_res > 0) { + this->info.change_mask |= SPA_NODE_CHANGE_MASK_PARAMS; + this->paramsIDX_Props.flags ^= SPA_PARAM_INFO_SERIAL; + emit_node_info(this, false); + } + break; + } + default: + return -ENOENT; + } + + return 0; +} + +static int reset_buffer(struct impl *this) +{ + if (this->codec_props_changed && this->codec_props + && this->codec->update_props) { + this->codec->update_props(this->codec_data, this->codec_props); + this->codec_props_changed = false; + } + this->need_flush = 0; + this->frame_count = 0; + this->fragment = false; + this->buffer_used = this->codec->start_encode(this->codec_data, + this->buffer, sizeof(this->buffer), + this->seqnum++, this->timestamp); + this->header_size = this->buffer_used; + this->timestamp = this->sample_count; + return 0; +} + +static int get_transport_unused_size(struct impl *this) +{ + int res, value; + res = ioctl(this->flush_source.fd, TIOCOUTQ, &value); + if (res < 0) { + spa_log_error(this->log, "%p: ioctl fail: %m", this); + return -errno; + } + spa_log_trace(this->log, "%p: fd unused buffer size:%d/%d", this, value, this->fd_buffer_size); + return value; +} + +static int send_buffer(struct impl *this) +{ + int written, unsent; + unsent = get_transport_unused_size(this); + if (unsent >= 0) { + unsent = this->fd_buffer_size - unsent; + this->codec->abr_process(this->codec_data, unsent); + } + + spa_log_trace(this->log, "%p: send %d %u %u %u %u", + this, this->frame_count, this->block_size, this->seqnum, + this->timestamp, this->buffer_used); + + written = send(this->flush_source.fd, this->buffer, + this->buffer_used, MSG_DONTWAIT | MSG_NOSIGNAL); + + spa_log_trace(this->log, "%p: send %d", this, written); + + if (written < 0) { + spa_log_debug(this->log, "%p: %m", this); + return -errno; + } + + return written; +} + +static int encode_buffer(struct impl *this, const void *data, uint32_t size) +{ + int processed; + size_t out_encoded; + struct port *port = &this->port; + const void *from_data = data; + int from_size = size; + + spa_log_trace(this->log, "%p: encode %d used %d, %d %d %d", + this, size, this->buffer_used, port->frame_size, this->block_size, + this->frame_count); + + if (this->need_flush) + return 0; + + if (this->buffer_used >= sizeof(this->buffer)) + return -ENOSPC; + + if (size < this->block_size - this->tmp_buffer_used) { + memcpy(this->tmp_buffer + this->tmp_buffer_used, data, size); + this->tmp_buffer_used += size; + return size; + } else if (this->tmp_buffer_used > 0) { + memcpy(this->tmp_buffer + this->tmp_buffer_used, data, this->block_size - this->tmp_buffer_used); + from_data = this->tmp_buffer; + from_size = this->block_size; + this->tmp_buffer_used = this->block_size - this->tmp_buffer_used; + } + + processed = this->codec->encode(this->codec_data, + from_data, from_size, + this->buffer + this->buffer_used, + sizeof(this->buffer) - this->buffer_used, + &out_encoded, &this->need_flush); + if (processed < 0) + return processed; + + this->sample_count += processed / port->frame_size; + this->frame_count += processed / this->block_size; + this->buffer_used += out_encoded; + + spa_log_trace(this->log, "%p: processed %d %zd used %d", + this, processed, out_encoded, this->buffer_used); + + if (this->tmp_buffer_used) { + processed = this->tmp_buffer_used; + this->tmp_buffer_used = 0; + } + return processed; +} + +static int encode_fragment(struct impl *this) +{ + int res; + size_t out_encoded; + struct port *port = &this->port; + + spa_log_trace(this->log, "%p: encode fragment used %d, %d %d %d", + this, this->buffer_used, port->frame_size, this->block_size, + this->frame_count); + + if (this->need_flush) + return 0; + + res = this->codec->encode(this->codec_data, + NULL, 0, + this->buffer + this->buffer_used, + sizeof(this->buffer) - this->buffer_used, + &out_encoded, &this->need_flush); + if (res < 0) + return res; + if (res != 0) + return -EINVAL; + + this->buffer_used += out_encoded; + + spa_log_trace(this->log, "%p: processed fragment %zd used %d", + this, out_encoded, this->buffer_used); + + return 0; +} + +static int flush_buffer(struct impl *this) +{ + spa_log_trace(this->log, "%p: used:%d block_size:%d", this, + this->buffer_used, this->block_size); + + if (this->need_flush) + return send_buffer(this); + + return 0; +} + +static int add_data(struct impl *this, const void *data, uint32_t size) +{ + int processed, total = 0; + + while (size > 0) { + processed = encode_buffer(this, data, size); + + if (processed <= 0) + return total > 0 ? total : processed; + + data = SPA_PTROFF(data, processed, void); + size -= processed; + total += processed; + } + return total; +} + +static void enable_flush(struct impl *this, bool enabled, uint64_t timeout) +{ + bool flush_enabled = enabled && (timeout == 0); + struct itimerspec ts; + + if (SPA_FLAG_IS_SET(this->flush_source.mask, SPA_IO_OUT) != flush_enabled) { + SPA_FLAG_UPDATE(this->flush_source.mask, SPA_IO_OUT, flush_enabled); + spa_loop_update_source(this->data_loop, &this->flush_source); + } + + if (!enabled) + timeout = 0; + + ts.it_value.tv_sec = timeout / SPA_NSEC_PER_SEC; + ts.it_value.tv_nsec = timeout % SPA_NSEC_PER_SEC; + ts.it_interval.tv_sec = 0; + ts.it_interval.tv_nsec = 0; + spa_system_timerfd_settime(this->data_system, + this->flush_timerfd, 0, &ts, NULL); +} + +static uint64_t get_next_bap_timeout(struct impl *this) +{ + struct port *port = &this->port; + uint64_t playback_time = 0, elapsed_time = 0, next_time = 0; + struct timespec now; + uint64_t now_time; + + spa_system_clock_gettime(this->data_system, CLOCK_MONOTONIC, &now); + now_time = SPA_TIMESPEC_TO_NSEC(&now); + if (this->start_time == 0) + this->start_time = now_time; + + playback_time = (this->total_samples * SPA_NSEC_PER_SEC) / port->current_format.info.raw.rate; + if (now_time > this->start_time) + elapsed_time = now_time - this->start_time; + if (elapsed_time < playback_time) + next_time = playback_time - elapsed_time; + + return next_time; +} + +static int flush_data(struct impl *this, uint64_t now_time) +{ + int written; + uint32_t total_frames; + struct port *port = &this->port; + + if (!this->flush_source.loop) { + /* I/O in error state */ + return -EIO; + } + + total_frames = 0; +again: + written = 0; + if (this->fragment && !this->need_flush) { + int res; + this->fragment = false; + if ((res = encode_fragment(this)) < 0) { + /* Error */ + reset_buffer(this); + return res; + } + } + while (!spa_list_is_empty(&port->ready) && !this->need_flush) { + uint8_t *src; + uint32_t n_bytes, n_frames; + struct buffer *b; + struct spa_data *d; + uint32_t index, offs, avail, l0, l1; + + b = spa_list_first(&port->ready, struct buffer, link); + d = b->buf->datas; + + src = d0.data; + + index = d0.chunk->offset + port->ready_offset; + avail = d0.chunk->size - port->ready_offset; + avail /= port->frame_size; + + offs = index % d0.maxsize; + n_frames = avail; + n_bytes = n_frames * port->frame_size; + + l0 = SPA_MIN(n_bytes, d0.maxsize - offs); + l1 = n_bytes - l0; + + written = add_data(this, src + offs, l0); + if (written > 0 && l1 > 0) + written += add_data(this, src, l1); + if (written <= 0) { + if (written < 0 && written != -ENOSPC) { + spa_list_remove(&b->link); + SPA_FLAG_SET(b->flags, BUFFER_FLAG_OUT); + this->port.io->buffer_id = b->id; + spa_log_warn(this->log, "%p: error %s, reuse buffer %u", + this, spa_strerror(written), b->id); + spa_node_call_reuse_buffer(&this->callbacks, 0, b->id); + port->ready_offset = 0; + } + break; + } + + n_frames = written / port->frame_size; + + port->ready_offset += written; + + if (port->ready_offset >= d0.chunk->size) { + spa_list_remove(&b->link); + SPA_FLAG_SET(b->flags, BUFFER_FLAG_OUT); + spa_log_trace(this->log, "%p: reuse buffer %u", this, b->id); + this->port.io->buffer_id = b->id; + + spa_node_call_reuse_buffer(&this->callbacks, 0, b->id); + port->ready_offset = 0; + } + total_frames += n_frames; + this->total_samples += n_frames; + + spa_log_trace(this->log, "%p: written %u frames", this, total_frames); + } + + if (written > 0 && this->buffer_used == this->header_size) { + enable_flush(this, false, 0); + return 0; + } + + written = flush_buffer(this); + + if (written == -EAGAIN) { + spa_log_trace(this->log, "%p: fail flush", this); + if (now_time - this->last_error > SPA_NSEC_PER_SEC / 2) { + spa_log_trace(this->log, "%p: reduce bitpool", this); + this->codec->reduce_bitpool(this->codec_data); + this->last_error = now_time; + } + + /* + * The socket buffer is full, and the device is not processing data + * fast enough, so should just skip this packet. There will be a sound + * glitch in any case. + */ + written = this->buffer_used; + reset_buffer(this); + } + + if (written < 0) { + spa_log_trace(this->log, "%p: error flushing %s", this, + spa_strerror(written)); + reset_buffer(this); + enable_flush(this, false, 0); + return written; + } + else if (written > 0) { + if (this->codec->bap) { + uint64_t timeout = get_next_bap_timeout(this); + + reset_buffer(this); + if (!spa_list_is_empty(&port->ready)) { + spa_log_debug(this->log, "%p: flush after %d ns", this, (unsigned int)timeout); + if (timeout == 0) + goto again; + else + enable_flush(this, true, timeout); + } else { + enable_flush(this, false, 0); + } + } else { + /* + * We cannot write all data we have at once, since this can exceed + * device buffers. We'll want a limited number of "excess" + * samples. This is an issue for the "low-latency" A2DP codecs. + * + * Flushing the rest of the data (if any) is delayed after a timeout, + * selected on an average-rate basis: + * + * npackets = quantum / packet_samples + * write_end_time = npackets * timeout + * max_excess = quantum - sample_rate * write_end_time + * packet_time = packet_samples / sample_rate + * => timeout = (quantum - max_excess)/quantum * packet_time + */ + uint64_t max_excess = 2*256; + uint64_t packet_samples = (uint64_t)this->frame_count * this->block_size / port->frame_size; + uint64_t packet_time = packet_samples * SPA_NSEC_PER_SEC / port->current_format.info.raw.rate; + uint64_t quantum = SPA_LIKELY(this->clock) ? this->clock->duration : 0; + uint64_t timeout = (quantum > max_excess) ? + (packet_time * (quantum - max_excess) / quantum) : 0; + + if (this->need_flush == NEED_FLUSH_FRAGMENT) { + reset_buffer(this); + this->fragment = true; + this->fragment_timeout = (packet_samples > 0) ? timeout : this->fragment_timeout; + goto again; + } + if (this->fragment_timeout > 0) { + timeout = this->fragment_timeout; + this->fragment_timeout = 0; + } + + reset_buffer(this); + if (now_time - this->last_error > SPA_NSEC_PER_SEC) { + if (get_transport_unused_size(this) == (int)this->fd_buffer_size) { + spa_log_trace(this->log, "%p: increase bitpool", this); + this->codec->increase_bitpool(this->codec_data); + } + this->last_error = now_time; + } + if (!spa_list_is_empty(&port->ready)) { + spa_log_trace(this->log, "%p: flush after %d ns", this, (int)timeout); + if (timeout == 0) + goto again; + else + enable_flush(this, true, timeout); + } else { + enable_flush(this, false, 0); + } + } + } + else { + /* Don't want to flush yet, or failed to write anything */ + spa_log_trace(this->log, "%p: skip flush", this); + enable_flush(this, false, 0); + } + return 0; +} + +static void media_on_flush(struct spa_source *source) +{ + struct impl *this = source->data; + + spa_log_trace(this->log, "%p: flushing", this); + + if (!SPA_FLAG_IS_SET(source->rmask, SPA_IO_OUT)) { + spa_log_warn(this->log, "%p: error %d", this, source->rmask); + if (this->flush_source.loop) + spa_loop_remove_source(this->data_loop, &this->flush_source); + return; + } + + if (this->transport == NULL) { + enable_flush(this, false, 0); + return; + } + + flush_data(this, this->current_time); +} + +static void media_on_flush_timeout(struct spa_source *source) +{ + struct impl *this = source->data; + uint64_t exp; + + spa_log_trace(this->log, "%p: flush on timeout", this); + + if (spa_system_timerfd_read(this->data_system, this->flush_timerfd, &exp) < 0) + spa_log_warn(this->log, "error reading timerfd: %s", strerror(errno)); + + if (this->transport == NULL) { + enable_flush(this, false, 0); + return; + } + + flush_data(this, this->current_time); +} + +static void media_on_timeout(struct spa_source *source) +{ + struct impl *this = source->data; + struct port *port = &this->port; + uint64_t exp, duration; + uint32_t rate; + struct spa_io_buffers *io = port->io; + uint64_t prev_time, now_time; + + if (this->transport == NULL) + return; + + if (this->started && spa_system_timerfd_read(this->data_system, this->timerfd, &exp) < 0) + spa_log_warn(this->log, "error reading timerfd: %s", strerror(errno)); + + prev_time = this->current_time; + now_time = this->current_time = this->next_time; + + spa_log_debug(this->log, "%p: timer %"PRIu64" %"PRIu64"", this, + now_time, now_time - prev_time); + + if (SPA_LIKELY(this->position)) { + duration = this->position->clock.duration; + rate = this->position->clock.rate.denom; + } else { + duration = 1024; + rate = 48000; + } + + this->next_time = now_time + duration * SPA_NSEC_PER_SEC / rate; + + if (SPA_LIKELY(this->clock)) { + int64_t delay_nsec; + + this->clock->nsec = now_time; + this->clock->position += duration; + this->clock->duration = duration; + this->clock->rate_diff = 1.0f; + this->clock->next_nsec = this->next_time; + + delay_nsec = spa_bt_transport_get_delay_nsec(this->transport); + + /* Negative delay doesn't work properly, so disallow it */ + delay_nsec += SPA_CLAMP(this->props.latency_offset, -delay_nsec, INT64_MAX / 2); + + this->clock->delay = (delay_nsec * this->clock->rate.denom) / SPA_NSEC_PER_SEC; + } + + + spa_log_trace(this->log, "%p: %d", this, io->status); + io->status = SPA_STATUS_NEED_DATA; + spa_node_call_ready(&this->callbacks, SPA_STATUS_NEED_DATA); + + set_timeout(this, this->next_time); +} + +static int do_start(struct impl *this) +{ + int res, val, size; + struct port *port; + socklen_t len; + uint8_t *conf; + uint32_t flags; + + if (this->started) + return 0; + + spa_return_val_if_fail(this->transport, -EIO); + + this->following = is_following(this); + + spa_log_debug(this->log, "%p: start following:%d", this, this->following); + + if ((res = spa_bt_transport_acquire(this->transport, false)) < 0) + return res; + + port = &this->port; + + conf = this->transport->configuration; + size = this->transport->configuration_len; + + spa_log_debug(this->log, "Transport configuration:"); + spa_log_hexdump(this->log, SPA_LOG_LEVEL_DEBUG, 2, conf, (size_t)size); + + if (this->codec->bap) + flags = MEDIA_CODEC_FLAG_SINK; + else + flags = this->is_duplex ? MEDIA_CODEC_FLAG_SINK : 0; + + this->codec_data = this->codec->init(this->codec, + flags, + this->transport->configuration, + this->transport->configuration_len, + &port->current_format, + this->codec_props, + this->transport->write_mtu); + if (this->codec_data == NULL) + return -EIO; + + spa_log_info(this->log, "%p: using %s codec %s, delay:%"PRIi64" ms", this, + this->codec->bap ? "BAP" : "A2DP", this->codec->description, + (int64_t)(spa_bt_transport_get_delay_nsec(this->transport) / SPA_NSEC_PER_MSEC)); + + this->seqnum = 0; + + this->block_size = this->codec->get_block_size(this->codec_data); + if (this->block_size > sizeof(this->tmp_buffer)) { + spa_log_error(this->log, "block-size %d > %zu", + this->block_size, sizeof(this->tmp_buffer)); + return -EIO; + } + + spa_log_debug(this->log, "%p: block_size %d", this, this->block_size); + + val = this->codec->send_buf_size > 0 + /* The kernel doubles the SO_SNDBUF option value set by setsockopt(). */ + ? this->codec->send_buf_size / 2 + this->codec->send_buf_size % 2 + : FILL_FRAMES * this->transport->write_mtu; + if (setsockopt(this->transport->fd, SOL_SOCKET, SO_SNDBUF, &val, sizeof(val)) < 0) + spa_log_warn(this->log, "%p: SO_SNDBUF %m", this); + + len = sizeof(val); + if (getsockopt(this->transport->fd, SOL_SOCKET, SO_SNDBUF, &val, &len) < 0) { + spa_log_warn(this->log, "%p: SO_SNDBUF %m", this); + } + else { + spa_log_debug(this->log, "%p: SO_SNDBUF: %d", this, val); + } + this->fd_buffer_size = val; + + val = FILL_FRAMES * this->transport->read_mtu; + if (setsockopt(this->transport->fd, SOL_SOCKET, SO_RCVBUF, &val, sizeof(val)) < 0) + spa_log_warn(this->log, "%p: SO_RCVBUF %m", this); + + val = 6; + if (setsockopt(this->transport->fd, SOL_SOCKET, SO_PRIORITY, &val, sizeof(val)) < 0) + spa_log_warn(this->log, "SO_PRIORITY failed: %m"); + + reset_buffer(this); + + this->source.data = this; + this->source.fd = this->timerfd; + this->source.func = media_on_timeout; + this->source.mask = SPA_IO_IN; + this->source.rmask = 0; + spa_loop_add_source(this->data_loop, &this->source); + + this->flush_timer_source.data = this; + this->flush_timer_source.fd = this->flush_timerfd; + this->flush_timer_source.func = media_on_flush_timeout; + this->flush_timer_source.mask = SPA_IO_IN; + this->flush_timer_source.rmask = 0; + spa_loop_add_source(this->data_loop, &this->flush_timer_source); + + this->flush_source.data = this; + this->flush_source.fd = this->transport->fd; + this->flush_source.func = media_on_flush; + this->flush_source.mask = 0; + this->flush_source.rmask = 0; + spa_loop_add_source(this->data_loop, &this->flush_source); + + set_timers(this); + this->started = true; + + return 0; +} + +static int do_remove_source(struct spa_loop *loop, + bool async, + uint32_t seq, + const void *data, + size_t size, + void *user_data) +{ + struct impl *this = user_data; + struct itimerspec ts; + + this->start_time = 0; + this->total_samples = 0; + + if (this->source.loop) + spa_loop_remove_source(this->data_loop, &this->source); + ts.it_value.tv_sec = 0; + ts.it_value.tv_nsec = 0; + ts.it_interval.tv_sec = 0; + ts.it_interval.tv_nsec = 0; + spa_system_timerfd_settime(this->data_system, this->timerfd, 0, &ts, NULL); + + if (this->flush_source.loop) + spa_loop_remove_source(this->data_loop, &this->flush_source); + + if (this->flush_timer_source.loop) + spa_loop_remove_source(this->data_loop, &this->flush_timer_source); + ts.it_value.tv_sec = 0; + ts.it_value.tv_nsec = 0; + ts.it_interval.tv_sec = 0; + ts.it_interval.tv_nsec = 0; + spa_system_timerfd_settime(this->data_system, this->flush_timerfd, 0, &ts, NULL); + + return 0; +} + +static int do_stop(struct impl *this) +{ + int res = 0; + + if (!this->started) + return 0; + + spa_log_trace(this->log, "%p: stop", this); + + spa_loop_invoke(this->data_loop, do_remove_source, 0, NULL, 0, true, this); + + this->started = false; + + if (this->transport) + res = spa_bt_transport_release(this->transport); + + if (this->codec_data) + this->codec->deinit(this->codec_data); + this->codec_data = NULL; + + return res; +} + +static int impl_node_send_command(void *object, const struct spa_command *command) +{ + struct impl *this = object; + struct port *port; + int res; + + spa_return_val_if_fail(this != NULL, -EINVAL); + spa_return_val_if_fail(command != NULL, -EINVAL); + + port = &this->port; + + switch (SPA_NODE_COMMAND_ID(command)) { + case SPA_NODE_COMMAND_Start: + if (!port->have_format) + return -EIO; + if (port->n_buffers == 0) + return -EIO; + + if ((res = do_start(this)) < 0) + return res; + break; + case SPA_NODE_COMMAND_Suspend: + case SPA_NODE_COMMAND_Pause: + if ((res = do_stop(this)) < 0) + return res; + break; + default: + return -ENOTSUP; + } + return 0; +} + +static void emit_node_info(struct impl *this, bool full) +{ + struct spa_dict_item node_info_items = { + { SPA_KEY_DEVICE_API, "bluez5" }, + { SPA_KEY_MEDIA_CLASS, this->is_output ? "Audio/Sink" : "Stream/Input/Audio" }, + { "media.name", ((this->transport && this->transport->device->name) ? + this->transport->device->name : this->codec->bap ? "BAP" : "A2DP" ) }, + { SPA_KEY_NODE_DRIVER, this->is_output ? "true" : "false" }, + }; + uint64_t old = full ? this->info.change_mask : 0; + if (full) + this->info.change_mask = this->info_all; + if (this->info.change_mask) { + this->info.props = &SPA_DICT_INIT_ARRAY(node_info_items); + spa_node_emit_info(&this->hooks, &this->info); + this->info.change_mask = old; + } +} + +static void emit_port_info(struct impl *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) { + spa_node_emit_port_info(&this->hooks, + SPA_DIRECTION_INPUT, 0, &port->info); + port->info.change_mask = old; + } +} + +static int +impl_node_add_listener(void *object, + struct spa_hook *listener, + const struct spa_node_events *events, + void *data) +{ + struct impl *this = object; + struct spa_hook_list save; + + spa_return_val_if_fail(this != NULL, -EINVAL); + + spa_hook_list_isolate(&this->hooks, &save, listener, events, data); + + emit_node_info(this, true); + emit_port_info(this, &this->port, true); + + spa_hook_list_join(&this->hooks, &save); + + return 0; +} + +static int +impl_node_set_callbacks(void *object, + const struct spa_node_callbacks *callbacks, + void *data) +{ + struct impl *this = object; + + spa_return_val_if_fail(this != NULL, -EINVAL); + + this->callbacks = SPA_CALLBACKS_INIT(callbacks, data); + + return 0; +} + +static int impl_node_sync(void *object, int seq) +{ + struct impl *this = object; + + spa_return_val_if_fail(this != NULL, -EINVAL); + + spa_node_emit_result(&this->hooks, seq, 0, 0, NULL); + + return 0; +} + +static int impl_node_add_port(void *object, enum spa_direction direction, uint32_t port_id, + const struct spa_dict *props) +{ + return -ENOTSUP; +} + +static int impl_node_remove_port(void *object, enum spa_direction direction, uint32_t port_id) +{ + return -ENOTSUP; +} + +static int +impl_node_port_enum_params(void *object, int seq, + enum spa_direction direction, uint32_t port_id, + uint32_t id, uint32_t start, uint32_t num, + const struct spa_pod *filter) +{ + + struct impl *this = object; + struct port *port; + struct spa_pod *param; + struct spa_pod_builder b = { 0 }; + uint8_t buffer1024; + struct spa_result_node_params result; + uint32_t count = 0; + int res; + + spa_return_val_if_fail(this != NULL, -EINVAL); + spa_return_val_if_fail(num != 0, -EINVAL); + + spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL); + port = &this->port; + + result.id = id; + result.next = start; + next: + result.index = result.next++; + + spa_pod_builder_init(&b, buffer, sizeof(buffer)); + + switch (id) { + case SPA_PARAM_EnumFormat: + if (this->codec == NULL) + return -EIO; + if (this->transport == NULL) + return -EIO; + + if ((res = this->codec->enum_config(this->codec, + this->is_duplex ? MEDIA_CODEC_FLAG_SINK : 0, + this->transport->configuration, + this->transport->configuration_len, + id, result.index, &b, ¶m)) != 1) + return res; + break; + + case SPA_PARAM_Format: + if (!port->have_format) + return -EIO; + if (result.index > 0) + return 0; + + param = spa_format_audio_raw_build(&b, id, &port->current_format.info.raw); + break; + + case SPA_PARAM_Buffers: + if (!port->have_format) + return -EIO; + if (result.index > 0) + return 0; + + param = spa_pod_builder_add_object(&b, + SPA_TYPE_OBJECT_ParamBuffers, id, + SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(2, 2, MAX_BUFFERS), + SPA_PARAM_BUFFERS_blocks, SPA_POD_Int(1), + SPA_PARAM_BUFFERS_size, SPA_POD_CHOICE_RANGE_Int( + this->props.min_latency * port->frame_size, + this->props.min_latency * port->frame_size, + INT32_MAX), + SPA_PARAM_BUFFERS_stride, SPA_POD_Int(port->frame_size)); + break; + + case SPA_PARAM_Meta: + switch (result.index) { + case 0: + param = spa_pod_builder_add_object(&b, + SPA_TYPE_OBJECT_ParamMeta, id, + SPA_PARAM_META_type, SPA_POD_Id(SPA_META_Header), + SPA_PARAM_META_size, SPA_POD_Int(sizeof(struct spa_meta_header))); + 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_Buffers), + SPA_PARAM_IO_size, SPA_POD_Int(sizeof(struct spa_io_buffers))); + break; + default: + return 0; + } + break; + + case SPA_PARAM_Latency: + switch (result.index) { + case 0: + param = spa_latency_build(&b, id, &port->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; +} + +static int clear_buffers(struct impl *this, struct port *port) +{ + do_stop(this); + if (port->n_buffers > 0) { + spa_list_init(&port->ready); + port->n_buffers = 0; + } + return 0; +} + +static int port_set_format(struct impl *this, struct port *port, + uint32_t flags, + const struct spa_pod *format) +{ + int err; + + if (format == NULL) { + spa_log_debug(this->log, "clear format"); + clear_buffers(this, port); + port->have_format = false; + } else { + struct spa_audio_info info = { 0 }; + + if ((err = spa_format_parse(format, &info.media_type, &info.media_subtype)) < 0) + return err; + + if (info.media_type != SPA_MEDIA_TYPE_audio || + info.media_subtype != SPA_MEDIA_SUBTYPE_raw) + return -EINVAL; + + if (spa_format_audio_raw_parse(format, &info.info.raw) < 0) + return -EINVAL; + + port->frame_size = info.info.raw.channels; + switch (info.info.raw.format) { + case SPA_AUDIO_FORMAT_S16: + port->frame_size *= 2; + break; + case SPA_AUDIO_FORMAT_S24: + port->frame_size *= 3; + break; + case SPA_AUDIO_FORMAT_S24_32: + case SPA_AUDIO_FORMAT_S32: + case SPA_AUDIO_FORMAT_F32: + port->frame_size *= 4; + break; + default: + return -EINVAL; + } + + port->current_format = info; + port->have_format = true; + } + + port->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS; + if (port->have_format) { + port->info.change_mask |= SPA_PORT_CHANGE_MASK_FLAGS; + port->info.flags = SPA_PORT_FLAG_LIVE; + port->info.change_mask |= SPA_PORT_CHANGE_MASK_RATE; + port->info.rate = SPA_FRACTION(1, port->current_format.info.raw.rate); + port->paramsIDX_Format = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_READWRITE); + port->paramsIDX_Buffers = SPA_PARAM_INFO(SPA_PARAM_Buffers, SPA_PARAM_INFO_READ); + port->paramsIDX_Latency.flags ^= SPA_PARAM_INFO_SERIAL; + } else { + port->paramsIDX_Format = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE); + port->paramsIDX_Buffers = SPA_PARAM_INFO(SPA_PARAM_Buffers, 0); + } + emit_port_info(this, port, false); + + return 0; +} + +static int +impl_node_port_set_param(void *object, + enum spa_direction direction, uint32_t port_id, + uint32_t id, uint32_t flags, + const struct spa_pod *param) +{ + struct impl *this = object; + struct port *port; + int res; + + spa_return_val_if_fail(this != NULL, -EINVAL); + spa_return_val_if_fail(CHECK_PORT(node, direction, port_id), -EINVAL); + port = &this->port; + + switch (id) { + case SPA_PARAM_Format: + res = port_set_format(this, port, flags, param); + break; + case SPA_PARAM_Latency: + res = 0; + break; + default: + res = -ENOENT; + break; + } + return res; +} + +static int +impl_node_port_use_buffers(void *object, + enum spa_direction direction, uint32_t port_id, + uint32_t flags, + struct spa_buffer **buffers, uint32_t n_buffers) +{ + struct impl *this = object; + struct port *port; + uint32_t i; + + spa_return_val_if_fail(this != NULL, -EINVAL); + spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL); + port = &this->port; + + spa_log_debug(this->log, "use buffers %d", n_buffers); + + if (!port->have_format) + return -EIO; + + clear_buffers(this, port); + + for (i = 0; i < n_buffers; i++) { + struct buffer *b = &port->buffersi; + + b->buf = buffersi; + b->id = i; + SPA_FLAG_SET(b->flags, BUFFER_FLAG_OUT); + + b->h = spa_buffer_find_meta_data(buffersi, SPA_META_Header, sizeof(*b->h)); + + if (buffersi->datas0.data == NULL) { + spa_log_error(this->log, "%p: need mapped memory", this); + return -EINVAL; + } + } + port->n_buffers = n_buffers; + + return 0; +} + +static int +impl_node_port_set_io(void *object, + enum spa_direction direction, + uint32_t port_id, + uint32_t id, + void *data, size_t size) +{ + struct impl *this = object; + struct port *port; + + spa_return_val_if_fail(this != NULL, -EINVAL); + + spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL); + port = &this->port; + + switch (id) { + case SPA_IO_Buffers: + port->io = data; + break; + default: + return -ENOENT; + } + return 0; +} + +static int impl_node_port_reuse_buffer(void *object, uint32_t port_id, uint32_t buffer_id) +{ + return -ENOTSUP; +} + +static int impl_node_process(void *object) +{ + struct impl *this = object; + struct port *port; + struct spa_io_buffers *io; + + spa_return_val_if_fail(this != NULL, -EINVAL); + + port = &this->port; + if ((io = port->io) == NULL) + return -EIO; + + if (this->position && this->position->clock.flags & SPA_IO_CLOCK_FLAG_FREEWHEEL) { + io->status = SPA_STATUS_NEED_DATA; + return SPA_STATUS_HAVE_DATA; + } + + if (io->status == SPA_STATUS_HAVE_DATA && io->buffer_id < port->n_buffers) { + struct buffer *b = &port->buffersio->buffer_id; + + if (!SPA_FLAG_IS_SET(b->flags, BUFFER_FLAG_OUT)) { + spa_log_warn(this->log, "%p: buffer %u in use", this, io->buffer_id); + io->status = -EINVAL; + return -EINVAL; + } + + spa_log_trace(this->log, "%p: queue buffer %u", this, io->buffer_id); + + spa_list_append(&port->ready, &b->link); + SPA_FLAG_CLEAR(b->flags, BUFFER_FLAG_OUT); + + io->buffer_id = SPA_ID_INVALID; + io->status = SPA_STATUS_OK; + } + if (!spa_list_is_empty(&port->ready)) { + if (this->following) { + if (this->position) { + this->current_time = this->position->clock.nsec; + } else { + struct timespec now; + spa_system_clock_gettime(this->data_system, CLOCK_MONOTONIC, &now); + this->current_time = SPA_TIMESPEC_TO_NSEC(&now); + } + } + if (this->need_flush) + reset_buffer(this); + flush_data(this, this->current_time); + } + + return SPA_STATUS_HAVE_DATA; +} + +static const struct spa_node_methods impl_node = { + SPA_VERSION_NODE_METHODS, + .add_listener = impl_node_add_listener, + .set_callbacks = impl_node_set_callbacks, + .sync = impl_node_sync, + .enum_params = impl_node_enum_params, + .set_param = impl_node_set_param, + .set_io = impl_node_set_io, + .send_command = impl_node_send_command, + .add_port = impl_node_add_port, + .remove_port = impl_node_remove_port, + .port_enum_params = impl_node_port_enum_params, + .port_set_param = impl_node_port_set_param, + .port_use_buffers = impl_node_port_use_buffers, + .port_set_io = impl_node_port_set_io, + .port_reuse_buffer = impl_node_port_reuse_buffer, + .process = impl_node_process, +}; + +static void transport_delay_changed(void *data) +{ + struct impl *this = data; + spa_log_debug(this->log, "transport %p delay changed", this->transport); + set_latency(this, true); +} + +static int do_transport_destroy(struct spa_loop *loop, + bool async, + uint32_t seq, + const void *data, + size_t size, + void *user_data) +{ + struct impl *this = user_data; + this->transport = NULL; + return 0; +} + +static void transport_destroy(void *data) +{ + struct impl *this = data; + spa_log_debug(this->log, "transport %p destroy", this->transport); + spa_loop_invoke(this->data_loop, do_transport_destroy, 0, NULL, 0, true, this); +} + +static void transport_state_changed(void *data, + enum spa_bt_transport_state old, + enum spa_bt_transport_state state) +{ + struct impl *this = data; + + spa_log_debug(this->log, "%p: transport %p state %d->%d", this, this->transport, old, state); + + if (state < SPA_BT_TRANSPORT_STATE_ACTIVE && old == SPA_BT_TRANSPORT_STATE_ACTIVE && + this->started) { + uint8_t buffer1024; + struct spa_pod_builder b = { 0 }; + + spa_log_debug(this->log, "%p: transport %p becomes inactive: stop and indicate error", + this, this->transport); + + /* + * If establishing connection fails due to remote end not activating + * the transport, we won't get a write error, but instead see a transport + * state change. + * + * Stop and emit a node error, to let upper levels handle it. + */ + + do_stop(this); + + spa_pod_builder_init(&b, buffer, sizeof(buffer)); + spa_node_emit_event(&this->hooks, + spa_pod_builder_add_object(&b, + SPA_TYPE_EVENT_Node, SPA_NODE_EVENT_Error)); + } +} + +static const struct spa_bt_transport_events transport_events = { + SPA_VERSION_BT_TRANSPORT_EVENTS, + .delay_changed = transport_delay_changed, + .state_changed = transport_state_changed, + .destroy = transport_destroy, +}; + +static int impl_get_interface(struct spa_handle *handle, const char *type, void **interface) +{ + struct impl *this; + + spa_return_val_if_fail(handle != NULL, -EINVAL); + spa_return_val_if_fail(interface != NULL, -EINVAL); + + this = (struct impl *) handle; + + if (spa_streq(type, SPA_TYPE_INTERFACE_Node)) + *interface = &this->node; + else + return -ENOENT; + + return 0; +} + +static int impl_clear(struct spa_handle *handle) +{ + struct impl *this = (struct impl *) handle; + + if (this->codec_data) + this->codec->deinit(this->codec_data); + if (this->codec_props && this->codec->clear_props) + this->codec->clear_props(this->codec_props); + if (this->transport) + spa_hook_remove(&this->transport_listener); + spa_system_close(this->data_system, this->timerfd); + spa_system_close(this->data_system, this->flush_timerfd); + return 0; +} + +static size_t +impl_get_size(const struct spa_handle_factory *factory, + const struct spa_dict *params) +{ + return sizeof(struct impl); +} + +static int +impl_init(const struct spa_handle_factory *factory, + struct spa_handle *handle, + const struct spa_dict *info, + const struct spa_support *support, + uint32_t n_support) +{ + struct impl *this; + struct port *port; + const char *str; + + spa_return_val_if_fail(factory != NULL, -EINVAL); + spa_return_val_if_fail(handle != NULL, -EINVAL); + + handle->get_interface = impl_get_interface; + handle->clear = impl_clear; + + this = (struct impl *) handle; + + this->log = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log); + this->data_loop = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_DataLoop); + this->data_system = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_DataSystem); + + spa_log_topic_init(this->log, &log_topic); + + if (this->data_loop == NULL) { + spa_log_error(this->log, "a data loop is needed"); + return -EINVAL; + } + if (this->data_system == NULL) { + spa_log_error(this->log, "a data system is needed"); + return -EINVAL; + } + + this->node.iface = SPA_INTERFACE_INIT( + SPA_TYPE_INTERFACE_Node, + SPA_VERSION_NODE, + &impl_node, this); + spa_hook_list_init(&this->hooks); + + this->info_all = SPA_NODE_CHANGE_MASK_FLAGS | + SPA_NODE_CHANGE_MASK_PARAMS | + SPA_NODE_CHANGE_MASK_PROPS; + this->info = SPA_NODE_INFO_INIT(); + this->info.max_input_ports = 1; + this->info.max_output_ports = 0; + this->info.flags = SPA_NODE_FLAG_RT; + this->paramsIDX_PropInfo = SPA_PARAM_INFO(SPA_PARAM_PropInfo, SPA_PARAM_INFO_READ); + this->paramsIDX_Props = SPA_PARAM_INFO(SPA_PARAM_Props, SPA_PARAM_INFO_READWRITE); + this->info.params = this->params; + this->info.n_params = N_NODE_PARAMS; + + port = &this->port; + port->info_all = SPA_PORT_CHANGE_MASK_FLAGS | + SPA_PORT_CHANGE_MASK_PARAMS; + port->info = SPA_PORT_INFO_INIT(); + port->info.flags = 0; + port->paramsIDX_EnumFormat = SPA_PARAM_INFO(SPA_PARAM_EnumFormat, SPA_PARAM_INFO_READ); + port->paramsIDX_Meta = SPA_PARAM_INFO(SPA_PARAM_Meta, SPA_PARAM_INFO_READ); + port->paramsIDX_IO = SPA_PARAM_INFO(SPA_PARAM_IO, SPA_PARAM_INFO_READ); + port->paramsIDX_Format = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE); + port->paramsIDX_Buffers = SPA_PARAM_INFO(SPA_PARAM_Buffers, 0); + port->paramsIDX_Latency = SPA_PARAM_INFO(SPA_PARAM_Latency, SPA_PARAM_INFO_READWRITE); + port->info.params = port->params; + port->info.n_params = N_PORT_PARAMS; + + port->latency = SPA_LATENCY_INFO(SPA_DIRECTION_INPUT); + port->latency.min_quantum = 1.0f; + port->latency.max_quantum = 1.0f; + set_latency(this, false); + + spa_list_init(&port->ready); + + if (info && (str = spa_dict_lookup(info, "api.bluez5.a2dp-duplex")) != NULL) + this->is_duplex = spa_atob(str); + + if (info && (str = spa_dict_lookup(info, SPA_KEY_API_BLUEZ5_TRANSPORT))) + sscanf(str, "pointer:%p", &this->transport); + + if (this->transport == NULL) { + spa_log_error(this->log, "a transport is needed"); + return -EINVAL; + } + if (this->transport->media_codec == NULL) { + spa_log_error(this->log, "a transport codec is needed"); + return -EINVAL; + } + + this->codec = this->transport->media_codec; + + if (this->is_duplex) { + if (!this->codec->duplex_codec) { + spa_log_error(this->log, "transport codec doesn't support duplex"); + return -EINVAL; + } + this->codec = this->codec->duplex_codec; + } + + if (this->codec->init_props != NULL) + this->codec_props = this->codec->init_props(this->codec, + this->is_duplex ? MEDIA_CODEC_FLAG_SINK : 0, + this->transport->device->settings); + + if (this->codec->bap) + this->is_output = this->transport->bap_initiator; + else + this->is_output = true; + + reset_props(this, &this->props); + + spa_bt_transport_add_listener(this->transport, + &this->transport_listener, &transport_events, this); + + this->timerfd = spa_system_timerfd_create(this->data_system, + CLOCK_MONOTONIC, SPA_FD_CLOEXEC | SPA_FD_NONBLOCK); + + this->flush_timerfd = spa_system_timerfd_create(this->data_system, + CLOCK_MONOTONIC, SPA_FD_CLOEXEC | SPA_FD_NONBLOCK); + + return 0; +} + +static const struct spa_interface_info impl_interfaces = { + {SPA_TYPE_INTERFACE_Node,}, +}; + +static int +impl_enum_interface_info(const struct spa_handle_factory *factory, + const struct spa_interface_info **info, uint32_t *index) +{ + spa_return_val_if_fail(factory != NULL, -EINVAL); + spa_return_val_if_fail(info != NULL, -EINVAL); + spa_return_val_if_fail(index != NULL, -EINVAL); + + switch (*index) { + case 0: + *info = &impl_interfaces*index; + break; + default: + return 0; + } + (*index)++; + return 1; +} + +static const struct spa_dict_item info_items = { + { SPA_KEY_FACTORY_AUTHOR, "Wim Taymans <wim.taymans@gmail.com>" }, + { SPA_KEY_FACTORY_DESCRIPTION, "Play audio with the media" }, + { SPA_KEY_FACTORY_USAGE, SPA_KEY_API_BLUEZ5_TRANSPORT"=<transport>" }, +}; + +static const struct spa_dict info = SPA_DICT_INIT_ARRAY(info_items); + +const struct spa_handle_factory spa_media_sink_factory = { + SPA_VERSION_HANDLE_FACTORY, + SPA_NAME_API_BLUEZ5_MEDIA_SINK, + &info, + impl_get_size, + impl_init, + impl_enum_interface_info, +}; + +/* Retained for backward compatibility: */ +const struct spa_handle_factory spa_a2dp_sink_factory = { + SPA_VERSION_HANDLE_FACTORY, + SPA_NAME_API_BLUEZ5_A2DP_SINK, + &info, + impl_get_size, + impl_init, + impl_enum_interface_info, +};
View file
pipewire-0.3.59.tar.gz/spa/plugins/bluez5/media-source.c
Added
@@ -0,0 +1,1663 @@ +/* Spa Media Source + * + * Copyright © 2018 Wim Taymans + * Copyright © 2019 Collabora Ltd. + * + * 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 <stddef.h> +#include <stdio.h> +#include <time.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/socket.h> + +#include <spa/support/plugin.h> +#include <spa/support/loop.h> +#include <spa/support/log.h> +#include <spa/support/system.h> +#include <spa/utils/list.h> +#include <spa/utils/keys.h> +#include <spa/utils/names.h> +#include <spa/utils/result.h> +#include <spa/utils/string.h> +#include <spa/monitor/device.h> + +#include <spa/node/node.h> +#include <spa/node/utils.h> +#include <spa/node/io.h> +#include <spa/node/keys.h> +#include <spa/param/param.h> +#include <spa/param/latency-utils.h> +#include <spa/param/audio/format.h> +#include <spa/param/audio/format-utils.h> +#include <spa/pod/filter.h> + +#include "defs.h" +#include "rtp.h" +#include "media-codecs.h" + +static struct spa_log_topic log_topic = SPA_LOG_TOPIC(0, "spa.bluez5.source.media"); +#undef SPA_LOG_TOPIC_DEFAULT +#define SPA_LOG_TOPIC_DEFAULT &log_topic + +#include "decode-buffer.h" + +#define DEFAULT_CLOCK_NAME "clock.system.monotonic" + +struct props { + char clock_name64; +}; + +#define FILL_FRAMES 2 +#define MAX_BUFFERS 32 + +struct buffer { + uint32_t id; + unsigned int outstanding:1; + struct spa_buffer *buf; + struct spa_meta_header *h; + struct spa_list link; +}; + +struct port { + struct spa_audio_info current_format; + uint32_t frame_size; + unsigned int have_format:1; + + uint64_t info_all; + struct spa_port_info info; + struct spa_io_buffers *io; + struct spa_io_rate_match *rate_match; + struct spa_latency_info latency; +#define IDX_EnumFormat 0 +#define IDX_Meta 1 +#define IDX_IO 2 +#define IDX_Format 3 +#define IDX_Buffers 4 +#define IDX_Latency 5 +#define N_PORT_PARAMS 6 + struct spa_param_info paramsN_PORT_PARAMS; + + struct buffer buffersMAX_BUFFERS; + uint32_t n_buffers; + + struct spa_list free; + struct spa_list ready; + + struct spa_bt_decode_buffer buffer; +}; + +struct impl { + struct spa_handle handle; + struct spa_node node; + + struct spa_log *log; + struct spa_loop *data_loop; + struct spa_system *data_system; + + struct spa_hook_list hooks; + struct spa_callbacks callbacks; + + uint32_t quantum_limit; + + uint64_t info_all; + struct spa_node_info info; +#define IDX_PropInfo 0 +#define IDX_Props 1 +#define IDX_NODE_IO 2 +#define N_NODE_PARAMS 3 + struct spa_param_info paramsN_NODE_PARAMS; + struct props props; + + struct spa_bt_transport *transport; + struct spa_hook transport_listener; + + struct port port; + + unsigned int started:1; + unsigned int transport_acquired:1; + unsigned int following:1; + unsigned int matching:1; + unsigned int resampling:1; + + unsigned int is_input:1; + unsigned int is_duplex:1; + unsigned int use_duplex_source:1; + + int fd; + struct spa_source source; + + struct spa_source timer_source; + int timerfd; + + struct spa_io_clock *clock; + struct spa_io_position *position; + + uint64_t current_time; + uint64_t next_time; + + const struct media_codec *codec; + bool codec_props_changed; + void *codec_props; + void *codec_data; + struct spa_audio_info codec_format; + + uint8_t buffer_read4096; + struct timespec now; + uint64_t sample_count; + + int duplex_timerfd; + uint64_t duplex_timeout; +}; + +#define CHECK_PORT(this,d,p) ((d) == SPA_DIRECTION_OUTPUT && (p) == 0) + +static void reset_props(struct props *props) +{ + strncpy(props->clock_name, DEFAULT_CLOCK_NAME, sizeof(props->clock_name)); +} + +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 impl *this = object; + struct spa_pod *param; + struct spa_pod_builder b = { 0 }; + uint8_t buffer1024; + struct spa_result_node_params result; + uint32_t count = 0, index_offset = 0; + bool enum_codec = false; + + 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: + enum_codec = true; + index_offset = 0; + } + break; + } + case SPA_PARAM_Props: + { + switch (result.index) { + default: + enum_codec = true; + index_offset = 0; + } + break; + } + default: + return -ENOENT; + } + + if (enum_codec) { + int res; + if (this->codec->enum_props == NULL || this->codec_props == NULL || + this->transport == NULL) + return 0; + else if ((res = this->codec->enum_props(this->codec_props, + this->transport->device->settings, + id, result.index - index_offset, + &b, ¶m)) != 1) + return res; + } + + 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; +} + +static int set_timeout(struct impl *this, uint64_t time) +{ + struct itimerspec ts; + ts.it_value.tv_sec = time / SPA_NSEC_PER_SEC; + ts.it_value.tv_nsec = time % SPA_NSEC_PER_SEC; + ts.it_interval.tv_sec = 0; + ts.it_interval.tv_nsec = 0; + return spa_system_timerfd_settime(this->data_system, + this->timerfd, SPA_FD_TIMER_ABSTIME, &ts, NULL); +} + +static int set_timers(struct impl *this) +{ + struct timespec now; + + spa_system_clock_gettime(this->data_system, CLOCK_MONOTONIC, &now); + this->next_time = SPA_TIMESPEC_TO_NSEC(&now); + + return set_timeout(this, this->following ? 0 : this->next_time); +} + +static int do_reassign_follower(struct spa_loop *loop, + bool async, + uint32_t seq, + const void *data, + size_t size, + void *user_data) +{ + struct impl *this = user_data; + struct port *port = &this->port; + + spa_bt_decode_buffer_recover(&port->buffer); + return 0; +} + +static inline bool is_following(struct impl *this) +{ + return this->position && this->clock && this->position->clock.id != this->clock->id; +} + +static int impl_node_set_io(void *object, uint32_t id, void *data, size_t size) +{ + struct impl *this = object; + bool following; + + spa_return_val_if_fail(this != NULL, -EINVAL); + + switch (id) { + case SPA_IO_Clock: + this->clock = data; + if (this->clock != NULL) { + spa_scnprintf(this->clock->name, + sizeof(this->clock->name), + "%s", this->props.clock_name); + } + break; + case SPA_IO_Position: + this->position = data; + break; + default: + return -ENOENT; + } + + following = is_following(this); + if (this->started && following != this->following) { + spa_log_debug(this->log, "%p: reassign follower %d->%d", this, this->following, following); + this->following = following; + spa_loop_invoke(this->data_loop, do_reassign_follower, 0, NULL, 0, true, this); + } + return 0; +} + +static void emit_node_info(struct impl *this, bool full); + +static int apply_props(struct impl *this, const struct spa_pod *param) +{ + struct props new_props = this->props; + int changed = 0; + + if (param == NULL) { + reset_props(&new_props); + } else { + /* noop */ + } + + changed = (memcmp(&new_props, &this->props, sizeof(struct props)) != 0); + this->props = new_props; + return changed; +} + +static int impl_node_set_param(void *object, uint32_t id, uint32_t flags, + const struct spa_pod *param) +{ + struct impl *this = object; + + spa_return_val_if_fail(this != NULL, -EINVAL); + + switch (id) { + case SPA_PARAM_Props: + { + int res, codec_res = 0; + res = apply_props(this, param); + if (this->codec_props && this->codec->set_props) { + codec_res = this->codec->set_props(this->codec_props, param); + if (codec_res > 0) + this->codec_props_changed = true; + } + if (res > 0 || codec_res > 0) { + this->info.change_mask |= SPA_NODE_CHANGE_MASK_PARAMS; + this->paramsIDX_Props.flags ^= SPA_PARAM_INFO_SERIAL; + emit_node_info(this, false); + } + break; + } + default: + return -ENOENT; + } + + return 0; +} + +static void reset_buffers(struct port *port) +{ + uint32_t i; + + spa_list_init(&port->free); + spa_list_init(&port->ready); + + for (i = 0; i < port->n_buffers; i++) { + struct buffer *b = &port->buffersi; + spa_list_append(&port->free, &b->link); + b->outstanding = false; + } +} + +static void recycle_buffer(struct impl *this, struct port *port, uint32_t buffer_id) +{ + struct buffer *b = &port->buffersbuffer_id; + + if (b->outstanding) { + spa_log_trace(this->log, "%p: recycle buffer %u", this, buffer_id); + spa_list_append(&port->free, &b->link); + b->outstanding = false; + } +} + +static int32_t read_data(struct impl *this) { + const ssize_t b_size = sizeof(this->buffer_read); + int32_t size_read = 0; + +again: + /* read data from socket */ + size_read = recv(this->fd, this->buffer_read, b_size, MSG_DONTWAIT); + + if (size_read == 0) + return 0; + else if (size_read < 0) { + /* retry if interrupted */ + if (errno == EINTR) + goto again; + + /* return socket has no data */ + if (errno == EAGAIN || errno == EWOULDBLOCK) + return 0; + + /* go to 'stop' if socket has an error */ + spa_log_error(this->log, "read error: %s", strerror(errno)); + return -errno; + } + + return size_read; +} + +static int32_t decode_data(struct impl *this, uint8_t *src, uint32_t src_size, + uint8_t *dst, uint32_t dst_size) +{ + ssize_t processed; + size_t written, avail; + + if ((processed = this->codec->start_decode(this->codec_data, + src, src_size, NULL, NULL)) < 0) + return processed; + + src += processed; + src_size -= processed; + + /* decode */ + avail = dst_size; + while (src_size > 0) { + if ((processed = this->codec->decode(this->codec_data, + src, src_size, dst, avail, &written)) <= 0) + return processed; + + /* update source and dest pointers */ + spa_return_val_if_fail (avail > written, -ENOSPC); + src_size -= processed; + src += processed; + avail -= written; + dst += written; + } + return dst_size - avail; +} + +static void media_on_ready_read(struct spa_source *source) +{ + struct impl *this = source->data; + struct port *port = &this->port; + struct timespec now; + void *buf; + int32_t size_read, decoded; + uint32_t avail; + uint64_t dt; + + /* make sure the source is an input */ + if ((source->rmask & SPA_IO_IN) == 0) { + spa_log_error(this->log, "source is not an input, rmask=%d", source->rmask); + goto stop; + } + if (this->transport == NULL) { + spa_log_debug(this->log, "no transport, stop reading"); + goto stop; + } + + spa_log_trace(this->log, "socket poll"); + + /* read */ + size_read = read_data (this); + if (size_read == 0) + return; + if (size_read < 0) { + spa_log_error(this->log, "failed to read data: %s", spa_strerror(size_read)); + goto stop; + } + + /* update the current pts */ + spa_system_clock_gettime(this->data_system, CLOCK_MONOTONIC, &now); + + if (this->codec_props_changed && this->codec_props + && this->codec->update_props) { + this->codec->update_props(this->codec_data, this->codec_props); + this->codec_props_changed = false; + } + + /* decode to buffer */ + buf = spa_bt_decode_buffer_get_write(&port->buffer, &avail); + spa_log_trace(this->log, "read socket data size:%d, avail:%d", size_read, avail); + decoded = decode_data(this, this->buffer_read, size_read, buf, avail); + if (decoded < 0) { + spa_log_debug(this->log, "failed to decode data: %d", decoded); + return; + } + if (decoded == 0) { + spa_log_trace(this->log, "no decoded socket data"); + return; + } + + /* discard when not started */ + if (!this->started) + return; + + spa_bt_decode_buffer_write_packet(&port->buffer, decoded); + + dt = SPA_TIMESPEC_TO_NSEC(&this->now); + this->now = now; + dt = SPA_TIMESPEC_TO_NSEC(&this->now) - dt; + + spa_log_trace(this->log, "decoded socket data size:%d frames:%d dt:%d dms", + (int)decoded, (int)decoded/port->frame_size, + (int)(dt / 100000)); + + return; + +stop: + if (this->source.loop) + spa_loop_remove_source(this->data_loop, &this->source); +} + +static int set_duplex_timeout(struct impl *this, uint64_t timeout) +{ + struct itimerspec ts; + ts.it_value.tv_sec = timeout / SPA_NSEC_PER_SEC; + ts.it_value.tv_nsec = timeout % SPA_NSEC_PER_SEC; + ts.it_interval.tv_sec = 0; + ts.it_interval.tv_nsec = 0; + return spa_system_timerfd_settime(this->data_system, + this->duplex_timerfd, 0, &ts, NULL); +} + +static void media_on_duplex_timeout(struct spa_source *source) +{ + struct impl *this = source->data; + uint64_t exp; + + if (spa_system_timerfd_read(this->data_system, this->duplex_timerfd, &exp) < 0) + spa_log_warn(this->log, "error reading timerfd: %s", strerror(errno)); + + set_duplex_timeout(this, this->duplex_timeout); + + media_on_ready_read(source); +} + +static int setup_matching(struct impl *this) +{ + struct port *port = &this->port; + + if (this->position && port->rate_match) { + port->rate_match->rate = 1 / port->buffer.corr; + + this->matching = this->following; + this->resampling = this->matching || + (port->current_format.info.raw.rate != this->position->clock.rate.denom); + } else { + this->matching = false; + this->resampling = false; + } + + if (port->rate_match) + SPA_FLAG_UPDATE(port->rate_match->flags, SPA_IO_RATE_MATCH_FLAG_ACTIVE, this->matching); + + return 0; +} + +static void media_on_timeout(struct spa_source *source) +{ + struct impl *this = source->data; + struct port *port = &this->port; + uint64_t exp, duration; + uint32_t rate; + struct spa_io_buffers *io = port->io; + uint64_t prev_time, now_time; + + if (this->transport == NULL) + return; + + if (this->started && spa_system_timerfd_read(this->data_system, this->timerfd, &exp) < 0) + spa_log_warn(this->log, "error reading timerfd: %s", strerror(errno)); + + prev_time = this->current_time; + now_time = this->current_time = this->next_time; + + spa_log_trace(this->log, "%p: timer %"PRIu64" %"PRIu64"", this, + now_time, now_time - prev_time); + + if (SPA_LIKELY(this->position)) { + duration = this->position->clock.duration; + rate = this->position->clock.rate.denom; + } else { + duration = 1024; + rate = 48000; + } + + setup_matching(this); + + this->next_time = now_time + duration * SPA_NSEC_PER_SEC / port->buffer.corr / rate; + + if (SPA_LIKELY(this->clock)) { + this->clock->nsec = now_time; + this->clock->position += duration; + this->clock->duration = duration; + this->clock->rate_diff = port->buffer.corr; + this->clock->next_nsec = this->next_time; + } + + spa_log_trace(this->log, "%p: %d", this, io->status); + io->status = SPA_STATUS_HAVE_DATA; + spa_node_call_ready(&this->callbacks, SPA_STATUS_HAVE_DATA); + + set_timeout(this, this->next_time); +} + +static int transport_start(struct impl *this) +{ + int res, val; + struct port *port = &this->port; + uint32_t flags; + + if (this->transport_acquired) + return 0; + + spa_log_debug(this->log, "%p: transport %p acquire", this, + this->transport); + if ((res = spa_bt_transport_acquire(this->transport, false)) < 0) + return res; + + this->transport_acquired = true; + + if (this->codec->bap) + flags = 0; + else + flags = this->is_duplex ? 0 : MEDIA_CODEC_FLAG_SINK; + + this->codec_data = this->codec->init(this->codec, + flags, + this->transport->configuration, + this->transport->configuration_len, + &port->current_format, + this->codec_props, + this->transport->read_mtu); + if (this->codec_data == NULL) + return -EIO; + + spa_log_info(this->log, "%p: using %s codec %s", this, + this->codec->bap ? "BAP" : "A2DP", this->codec->description); + + val = fcntl(this->transport->fd, F_GETFL); + if (fcntl(this->transport->fd, F_SETFL, val | O_NONBLOCK) < 0) + spa_log_warn(this->log, "%p: fcntl %u %m", this, val | O_NONBLOCK); + + val = FILL_FRAMES * this->transport->write_mtu; + if (setsockopt(this->transport->fd, SOL_SOCKET, SO_SNDBUF, &val, sizeof(val)) < 0) + spa_log_warn(this->log, "%p: SO_SNDBUF %m", this); + + val = FILL_FRAMES * this->transport->read_mtu; + if (setsockopt(this->transport->fd, SOL_SOCKET, SO_RCVBUF, &val, sizeof(val)) < 0) + spa_log_warn(this->log, "%p: SO_RCVBUF %m", this); + + val = 6; + if (setsockopt(this->transport->fd, SOL_SOCKET, SO_PRIORITY, &val, sizeof(val)) < 0) + spa_log_warn(this->log, "SO_PRIORITY failed: %m"); + + reset_buffers(port); + + spa_bt_decode_buffer_clear(&port->buffer); + if ((res = spa_bt_decode_buffer_init(&port->buffer, this->log, + port->frame_size, port->current_format.info.raw.rate, + this->quantum_limit, this->quantum_limit)) < 0) + return res; + + this->fd = this->transport->fd; + + this->source.data = this; + + if (!this->use_duplex_source) { + this->source.fd = this->transport->fd; + this->source.func = media_on_ready_read; + this->source.mask = SPA_IO_IN; + this->source.rmask = 0; + spa_loop_add_source(this->data_loop, &this->source); + } else { + /* + * XXX: For an unknown reason (on Linux 5.13.10), the socket when working with + * XXX: "duplex" stream sometimes stops waking up from the poll, even though + * XXX: you can recv() from the socket with no problem. + * XXX: + * XXX: The reason for this should be found and fixed. + * XXX: To work around this, for now we just do the stupid thing and poll + * XXX: on a timer, chosen so that it's fast enough for the aptX-LL codec + * XXX: we currently support (which sends mSBC data), and also for Opus + * XXX: forward stream. + */ + this->source.fd = this->duplex_timerfd; + this->source.func = media_on_duplex_timeout; + this->source.mask = SPA_IO_IN; + this->source.rmask = 0; + spa_loop_add_source(this->data_loop, &this->source); + + this->duplex_timeout = SPA_NSEC_PER_MSEC * 25/10; + set_duplex_timeout(this, this->duplex_timeout); + } + + this->timer_source.data = this; + this->timer_source.fd = this->timerfd; + this->timer_source.func = media_on_timeout; + this->timer_source.mask = SPA_IO_IN; + this->timer_source.rmask = 0; + spa_loop_add_source(this->data_loop, &this->timer_source); + + this->sample_count = 0; + + setup_matching(this); + + set_timers(this); + + return 0; +} + +static int do_start(struct impl *this) +{ + int res = 0; + + if (this->started) + return 0; + + spa_return_val_if_fail(this->transport != NULL, -EIO); + + this->following = is_following(this); + + spa_log_debug(this->log, "%p: start state:%d following:%d", + this, this->transport->state, this->following); + + if (this->transport->state >= SPA_BT_TRANSPORT_STATE_PENDING || + this->is_duplex || this->codec->bap) + res = transport_start(this); + + this->started = true; + + return res; +} + +static int do_remove_source(struct spa_loop *loop, + bool async, + uint32_t seq, + const void *data, + size_t size, + void *user_data) +{ + struct impl *this = user_data; + struct itimerspec ts; + + spa_log_debug(this->log, "%p: remove source", this); + + set_duplex_timeout(this, 0); + + if (this->source.loop) + spa_loop_remove_source(this->data_loop, &this->source); + + if (this->timer_source.loop) + spa_loop_remove_source(this->data_loop, &this->timer_source); + ts.it_value.tv_sec = 0; + ts.it_value.tv_nsec = 0; + ts.it_interval.tv_sec = 0; + ts.it_interval.tv_nsec = 0; + spa_system_timerfd_settime(this->data_system, this->timerfd, 0, &ts, NULL); + + return 0; +} + +static int transport_stop(struct impl *this) +{ + struct port *port = &this->port; + int res; + + spa_log_debug(this->log, "%p: transport stop", this); + + spa_loop_invoke(this->data_loop, do_remove_source, 0, NULL, 0, true, this); + + if (this->transport && this->transport_acquired) + res = spa_bt_transport_release(this->transport); + else + res = 0; + + this->transport_acquired = false; + + if (this->codec_data) + this->codec->deinit(this->codec_data); + this->codec_data = NULL; + + spa_bt_decode_buffer_clear(&port->buffer); + + return res; +} + +static int do_stop(struct impl *this) +{ + int res; + + if (!this->started) + return 0; + + spa_log_debug(this->log, "%p: stop", this); + + res = transport_stop(this); + + this->started = false; + + return res; +} + +static int impl_node_send_command(void *object, const struct spa_command *command) +{ + struct impl *this = object; + struct port *port; + int res; + + spa_return_val_if_fail(this != NULL, -EINVAL); + spa_return_val_if_fail(command != NULL, -EINVAL); + + port = &this->port; + + switch (SPA_NODE_COMMAND_ID(command)) { + case SPA_NODE_COMMAND_Start: + if (!port->have_format) + return -EIO; + if (port->n_buffers == 0) + return -EIO; + + if ((res = do_start(this)) < 0) + return res; + break; + case SPA_NODE_COMMAND_Suspend: + case SPA_NODE_COMMAND_Pause: + if ((res = do_stop(this)) < 0) + return res; + break; + default: + return -ENOTSUP; + } + return 0; +} + +static void emit_node_info(struct impl *this, bool full) +{ + uint64_t old = full ? this->info.change_mask : 0; + + struct spa_dict_item node_info_items = { + { SPA_KEY_DEVICE_API, "bluez5" }, + { SPA_KEY_MEDIA_CLASS, this->is_input ? "Audio/Source" : "Stream/Output/Audio" }, + { SPA_KEY_NODE_LATENCY, this->is_input ? "" : "512/48000" }, + { "media.name", ((this->transport && this->transport->device->name) ? + this->transport->device->name : this->codec->bap ? "BAP" : "A2DP") }, + { SPA_KEY_NODE_DRIVER, this->is_input ? "true" : "false" }, + }; + + if (full) + this->info.change_mask = this->info_all; + if (this->info.change_mask) { + this->info.props = &SPA_DICT_INIT_ARRAY(node_info_items); + spa_node_emit_info(&this->hooks, &this->info); + this->info.change_mask = old; + } +} + +static void emit_port_info(struct impl *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) { + spa_node_emit_port_info(&this->hooks, + SPA_DIRECTION_OUTPUT, 0, &port->info); + port->info.change_mask = old; + } +} + +static int +impl_node_add_listener(void *object, + struct spa_hook *listener, + const struct spa_node_events *events, + void *data) +{ + struct impl *this = object; + struct spa_hook_list save; + + spa_return_val_if_fail(this != NULL, -EINVAL); + + spa_hook_list_isolate(&this->hooks, &save, listener, events, data); + + emit_node_info(this, true); + emit_port_info(this, &this->port, true); + + spa_hook_list_join(&this->hooks, &save); + + return 0; +} + +static int +impl_node_set_callbacks(void *object, + const struct spa_node_callbacks *callbacks, + void *data) +{ + struct impl *this = object; + + spa_return_val_if_fail(this != NULL, -EINVAL); + + this->callbacks = SPA_CALLBACKS_INIT(callbacks, data); + + return 0; +} + +static int impl_node_sync(void *object, int seq) +{ + struct impl *this = object; + + spa_return_val_if_fail(this != NULL, -EINVAL); + + spa_node_emit_result(&this->hooks, seq, 0, 0, NULL); + + return 0; +} + +static int impl_node_add_port(void *object, enum spa_direction direction, uint32_t port_id, + const struct spa_dict *props) +{ + return -ENOTSUP; +} + +static int impl_node_remove_port(void *object, enum spa_direction direction, uint32_t port_id) +{ + return -ENOTSUP; +} + +static int +impl_node_port_enum_params(void *object, int seq, + enum spa_direction direction, uint32_t port_id, + uint32_t id, uint32_t start, uint32_t num, + const struct spa_pod *filter) +{ + + struct impl *this = object; + struct port *port; + struct spa_pod *param; + struct spa_pod_builder b = { 0 }; + uint8_t buffer1024; + struct spa_result_node_params result; + uint32_t count = 0; + int res; + + spa_return_val_if_fail(this != NULL, -EINVAL); + spa_return_val_if_fail(num != 0, -EINVAL); + + spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL); + port = &this->port; + + result.id = id; + result.next = start; + next: + result.index = result.next++; + + spa_pod_builder_init(&b, buffer, sizeof(buffer)); + + switch (id) { + case SPA_PARAM_EnumFormat: + if (result.index > 0) + return 0; + if (this->codec == NULL) + return -EIO; + if (this->transport == NULL) + return -EIO; + + if ((res = this->codec->enum_config(this->codec, + this->is_duplex ? 0 : MEDIA_CODEC_FLAG_SINK, + this->transport->configuration, + this->transport->configuration_len, + id, result.index, &b, ¶m)) != 1) + return res; + break; + + case SPA_PARAM_Format: + if (!port->have_format) + return -EIO; + if (result.index > 0) + return 0; + + param = spa_format_audio_raw_build(&b, id, &port->current_format.info.raw); + break; + + case SPA_PARAM_Buffers: + if (!port->have_format) + return -EIO; + if (result.index > 0) + return 0; + + param = spa_pod_builder_add_object(&b, + SPA_TYPE_OBJECT_ParamBuffers, id, + SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(2, 1, MAX_BUFFERS), + SPA_PARAM_BUFFERS_blocks, SPA_POD_Int(1), + SPA_PARAM_BUFFERS_size, SPA_POD_CHOICE_RANGE_Int( + this->quantum_limit * port->frame_size, + 16 * port->frame_size, + INT32_MAX), + SPA_PARAM_BUFFERS_stride, SPA_POD_Int(port->frame_size)); + break; + + case SPA_PARAM_Meta: + switch (result.index) { + case 0: + param = spa_pod_builder_add_object(&b, + SPA_TYPE_OBJECT_ParamMeta, id, + SPA_PARAM_META_type, SPA_POD_Id(SPA_META_Header), + SPA_PARAM_META_size, SPA_POD_Int(sizeof(struct spa_meta_header))); + 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_Buffers), + SPA_PARAM_IO_size, SPA_POD_Int(sizeof(struct spa_io_buffers))); + break; + case 1: + param = spa_pod_builder_add_object(&b, + SPA_TYPE_OBJECT_ParamIO, id, + SPA_PARAM_IO_id, SPA_POD_Id(SPA_IO_RateMatch), + SPA_PARAM_IO_size, SPA_POD_Int(sizeof(struct spa_io_rate_match))); + break; + default: + return 0; + } + break; + + case SPA_PARAM_Latency: + switch (result.index) { + case 0: + param = spa_latency_build(&b, id, &port->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; +} + +static int clear_buffers(struct impl *this, struct port *port) +{ + do_stop(this); + if (port->n_buffers > 0) { + spa_list_init(&port->free); + spa_list_init(&port->ready); + port->n_buffers = 0; + } + return 0; +} + +static int port_set_format(struct impl *this, struct port *port, + uint32_t flags, + const struct spa_pod *format) +{ + int err; + + if (format == NULL) { + spa_log_debug(this->log, "clear format"); + clear_buffers(this, port); + port->have_format = false; + } else { + struct spa_audio_info info = { 0 }; + + if ((err = spa_format_parse(format, &info.media_type, &info.media_subtype)) < 0) + return err; + + if (info.media_type != SPA_MEDIA_TYPE_audio || + info.media_subtype != SPA_MEDIA_SUBTYPE_raw) + return -EINVAL; + + if (spa_format_audio_raw_parse(format, &info.info.raw) < 0) + return -EINVAL; + + port->frame_size = info.info.raw.channels; + + switch (info.info.raw.format) { + case SPA_AUDIO_FORMAT_S16: + port->frame_size *= 2; + break; + case SPA_AUDIO_FORMAT_S24: + port->frame_size *= 3; + break; + case SPA_AUDIO_FORMAT_S24_32: + case SPA_AUDIO_FORMAT_S32: + case SPA_AUDIO_FORMAT_F32: + port->frame_size *= 4; + break; + default: + return -EINVAL; + } + + port->current_format = info; + port->have_format = true; + } + + port->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS; + if (port->have_format) { + port->info.change_mask |= SPA_PORT_CHANGE_MASK_FLAGS; + port->info.flags = SPA_PORT_FLAG_LIVE; + port->info.change_mask |= SPA_PORT_CHANGE_MASK_RATE; + port->info.rate = SPA_FRACTION(1, port->current_format.info.raw.rate); + port->paramsIDX_Format = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_READWRITE); + port->paramsIDX_Buffers = SPA_PARAM_INFO(SPA_PARAM_Buffers, SPA_PARAM_INFO_READ); + port->paramsIDX_Latency.flags ^= SPA_PARAM_INFO_SERIAL; + } else { + port->paramsIDX_Format = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE); + port->paramsIDX_Buffers = SPA_PARAM_INFO(SPA_PARAM_Buffers, 0); + } + emit_port_info(this, port, false); + + return 0; +} + +static int +impl_node_port_set_param(void *object, + enum spa_direction direction, uint32_t port_id, + uint32_t id, uint32_t flags, + const struct spa_pod *param) +{ + struct impl *this = object; + struct port *port; + int res; + + spa_return_val_if_fail(this != NULL, -EINVAL); + + spa_return_val_if_fail(CHECK_PORT(node, direction, port_id), -EINVAL); + port = &this->port; + + switch (id) { + case SPA_PARAM_Format: + res = port_set_format(this, port, flags, param); + break; + case SPA_PARAM_Latency: + res = 0; + break; + default: + res = -ENOENT; + break; + } + return res; +} + +static int +impl_node_port_use_buffers(void *object, + enum spa_direction direction, uint32_t port_id, + uint32_t flags, + struct spa_buffer **buffers, uint32_t n_buffers) +{ + struct impl *this = object; + struct port *port; + uint32_t i; + + spa_return_val_if_fail(this != NULL, -EINVAL); + + spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL); + port = &this->port; + + spa_log_debug(this->log, "use buffers %d", n_buffers); + + if (!port->have_format) + return -EIO; + + clear_buffers(this, port); + + for (i = 0; i < n_buffers; i++) { + struct buffer *b = &port->buffersi; + struct spa_data *d = buffersi->datas; + + b->buf = buffersi; + b->id = i; + + b->h = spa_buffer_find_meta_data(buffersi, SPA_META_Header, sizeof(*b->h)); + + if (d0.data == NULL) { + spa_log_error(this->log, "%p: need mapped memory", this); + return -EINVAL; + } + spa_list_append(&port->free, &b->link); + b->outstanding = false; + } + port->n_buffers = n_buffers; + + return 0; +} + +static int +impl_node_port_set_io(void *object, + enum spa_direction direction, + uint32_t port_id, + uint32_t id, + void *data, size_t size) +{ + struct impl *this = object; + struct port *port; + + spa_return_val_if_fail(this != NULL, -EINVAL); + + spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL); + port = &this->port; + + switch (id) { + case SPA_IO_Buffers: + port->io = data; + break; + case SPA_IO_RateMatch: + port->rate_match = data; + break; + default: + return -ENOENT; + } + return 0; +} + +static int impl_node_port_reuse_buffer(void *object, uint32_t port_id, uint32_t buffer_id) +{ + struct impl *this = object; + struct port *port; + + spa_return_val_if_fail(this != NULL, -EINVAL); + + spa_return_val_if_fail(port_id == 0, -EINVAL); + port = &this->port; + + if (port->n_buffers == 0) + return -EIO; + + if (buffer_id >= port->n_buffers) + return -EINVAL; + + recycle_buffer(this, port, buffer_id); + + return 0; +} + +static uint32_t get_samples(struct impl *this, uint32_t *duration) +{ + struct port *port = &this->port; + uint32_t samples; + + if (SPA_LIKELY(port->rate_match) && this->resampling) { + samples = port->rate_match->size; + } else { + if (SPA_LIKELY(this->position)) + samples = this->position->clock.duration * port->current_format.info.raw.rate + / this->position->clock.rate.denom; + else + samples = 1024; + } + + if (SPA_LIKELY(this->position)) + *duration = this->position->clock.duration * port->current_format.info.raw.rate + / this->position->clock.rate.denom; + else if (SPA_LIKELY(this->clock)) + *duration = this->clock->duration * port->current_format.info.raw.rate + / this->clock->rate.denom; + else + *duration = 1024 * port->current_format.info.raw.rate / 48000; + + return samples; +} + +static void process_buffering(struct impl *this) +{ + struct port *port = &this->port; + uint32_t duration; + const uint32_t samples = get_samples(this, &duration); + uint32_t avail; + void *buf; + + spa_bt_decode_buffer_process(&port->buffer, samples, duration); + + setup_matching(this); + + buf = spa_bt_decode_buffer_get_read(&port->buffer, &avail); + + /* copy data to buffers */ + if (!spa_list_is_empty(&port->free) && avail > 0) { + struct buffer *buffer; + struct spa_data *datas; + uint32_t data_size; + + data_size = samples * port->frame_size; + + avail = SPA_MIN(avail, data_size); + + spa_bt_decode_buffer_read(&port->buffer, avail); + + buffer = spa_list_first(&port->free, struct buffer, link); + spa_list_remove(&buffer->link); + + spa_log_trace(this->log, "dequeue %d", buffer->id); + + if (buffer->h) { + buffer->h->seq = this->sample_count; + buffer->h->pts = SPA_TIMESPEC_TO_NSEC(&this->now); + buffer->h->dts_offset = 0; + } + + datas = buffer->buf->datas; + + spa_assert(datas0.maxsize >= data_size); + + datas0.chunk->offset = 0; + datas0.chunk->size = avail; + datas0.chunk->stride = port->frame_size; + + memcpy(datas0.data, buf, avail); + + this->sample_count += avail / port->frame_size; + + /* ready buffer if full */ + spa_log_trace(this->log, "queue %d frames:%d", buffer->id, (int)avail / port->frame_size); + spa_list_append(&port->ready, &buffer->link); + } +} + +static int impl_node_process(void *object) +{ + struct impl *this = object; + struct port *port; + struct spa_io_buffers *io; + struct buffer *buffer; + + spa_return_val_if_fail(this != NULL, -EINVAL); + + port = &this->port; + if ((io = port->io) == NULL) + return -EIO; + + spa_log_trace(this->log, "%p status:%d", this, io->status); + + /* Return if we already have a buffer */ + if (io->status == SPA_STATUS_HAVE_DATA) + return SPA_STATUS_HAVE_DATA; + + /* Recycle */ + if (io->buffer_id < port->n_buffers) { + recycle_buffer(this, port, io->buffer_id); + io->buffer_id = SPA_ID_INVALID; + } + + /* Handle buffering delay */ + process_buffering(this); + + /* Return if there are no buffers ready to be processed */ + if (spa_list_is_empty(&port->ready)) + return SPA_STATUS_OK; + + /* Get the new buffer from the ready list */ + buffer = spa_list_first(&port->ready, struct buffer, link); + spa_list_remove(&buffer->link); + buffer->outstanding = true; + + /* Set the new buffer in IO */ + io->buffer_id = buffer->id; + io->status = SPA_STATUS_HAVE_DATA; + + /* Notify we have a buffer ready to be processed */ + return SPA_STATUS_HAVE_DATA; +} + +static const struct spa_node_methods impl_node = { + SPA_VERSION_NODE_METHODS, + .add_listener = impl_node_add_listener, + .set_callbacks = impl_node_set_callbacks, + .sync = impl_node_sync, + .enum_params = impl_node_enum_params, + .set_param = impl_node_set_param, + .set_io = impl_node_set_io, + .send_command = impl_node_send_command, + .add_port = impl_node_add_port, + .remove_port = impl_node_remove_port, + .port_enum_params = impl_node_port_enum_params, + .port_set_param = impl_node_port_set_param, + .port_use_buffers = impl_node_port_use_buffers, + .port_set_io = impl_node_port_set_io, + .port_reuse_buffer = impl_node_port_reuse_buffer, + .process = impl_node_process, +}; + +static int do_transport_destroy(struct spa_loop *loop, + bool async, + uint32_t seq, + const void *data, + size_t size, + void *user_data) +{ + struct impl *this = user_data; + this->transport = NULL; + this->transport_acquired = false; + return 0; +} + +static void transport_destroy(void *data) +{ + struct impl *this = data; + spa_log_debug(this->log, "transport %p destroy", this->transport); + spa_loop_invoke(this->data_loop, do_transport_destroy, 0, NULL, 0, true, this); +} + +static const struct spa_bt_transport_events transport_events = { + SPA_VERSION_BT_TRANSPORT_EVENTS, + .destroy = transport_destroy, +}; + +static int impl_get_interface(struct spa_handle *handle, const char *type, void **interface) +{ + struct impl *this; + + spa_return_val_if_fail(handle != NULL, -EINVAL); + spa_return_val_if_fail(interface != NULL, -EINVAL); + + this = (struct impl *) handle; + + if (spa_streq(type, SPA_TYPE_INTERFACE_Node)) + *interface = &this->node; + else + return -ENOENT; + + return 0; +} + +static int impl_clear(struct spa_handle *handle) +{ + struct impl *this = (struct impl *) handle; + struct port *port = &this->port; + if (this->codec_data) + this->codec->deinit(this->codec_data); + if (this->codec_props && this->codec->clear_props) + this->codec->clear_props(this->codec_props); + if (this->transport) + spa_hook_remove(&this->transport_listener); + spa_system_close(this->data_system, this->timerfd); + if (this->duplex_timerfd >= 0) { + spa_system_close(this->data_system, this->duplex_timerfd); + this->duplex_timerfd = -1; + } + spa_bt_decode_buffer_clear(&port->buffer); + return 0; +} + +static size_t +impl_get_size(const struct spa_handle_factory *factory, + const struct spa_dict *params) +{ + return sizeof(struct impl); +} + +static int +impl_init(const struct spa_handle_factory *factory, + struct spa_handle *handle, + const struct spa_dict *info, + const struct spa_support *support, + uint32_t n_support) +{ + struct impl *this; + struct port *port; + const char *str; + + spa_return_val_if_fail(factory != NULL, -EINVAL); + spa_return_val_if_fail(handle != NULL, -EINVAL); + + handle->get_interface = impl_get_interface; + handle->clear = impl_clear; + + this = (struct impl *) handle; + + this->log = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log); + this->data_loop = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_DataLoop); + this->data_system = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_DataSystem); + + spa_log_topic_init(this->log, &log_topic); + + if (this->data_loop == NULL) { + spa_log_error(this->log, "a data loop is needed"); + return -EINVAL; + } + if (this->data_system == NULL) { + spa_log_error(this->log, "a data system is needed"); + return -EINVAL; + } + + this->node.iface = SPA_INTERFACE_INIT( + SPA_TYPE_INTERFACE_Node, + SPA_VERSION_NODE, + &impl_node, this); + spa_hook_list_init(&this->hooks); + + reset_props(&this->props); + + /* set the node info */ + this->info_all = SPA_NODE_CHANGE_MASK_FLAGS | + SPA_NODE_CHANGE_MASK_PROPS | + SPA_NODE_CHANGE_MASK_PARAMS; + this->info = SPA_NODE_INFO_INIT(); + this->info.max_input_ports = 0; + this->info.max_output_ports = 1; + this->info.flags = SPA_NODE_FLAG_RT; + this->paramsIDX_PropInfo = SPA_PARAM_INFO(SPA_PARAM_PropInfo, SPA_PARAM_INFO_READ); + this->paramsIDX_Props = SPA_PARAM_INFO(SPA_PARAM_Props, SPA_PARAM_INFO_READWRITE); + this->paramsIDX_NODE_IO = SPA_PARAM_INFO(SPA_PARAM_IO, SPA_PARAM_INFO_READ); + this->info.params = this->params; + this->info.n_params = N_NODE_PARAMS; + + /* set the port info */ + port = &this->port; + port->info_all = SPA_PORT_CHANGE_MASK_FLAGS | + SPA_PORT_CHANGE_MASK_PARAMS; + port->info = SPA_PORT_INFO_INIT(); + port->info.change_mask = SPA_PORT_CHANGE_MASK_FLAGS; + port->info.flags = SPA_PORT_FLAG_LIVE | + SPA_PORT_FLAG_TERMINAL; + port->paramsIDX_EnumFormat = SPA_PARAM_INFO(SPA_PARAM_EnumFormat, SPA_PARAM_INFO_READ); + port->paramsIDX_Meta = SPA_PARAM_INFO(SPA_PARAM_Meta, SPA_PARAM_INFO_READ); + port->paramsIDX_IO = SPA_PARAM_INFO(SPA_PARAM_IO, SPA_PARAM_INFO_READ); + port->paramsIDX_Format = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE); + port->paramsIDX_Buffers = SPA_PARAM_INFO(SPA_PARAM_Buffers, 0); + port->paramsIDX_Latency = SPA_PARAM_INFO(SPA_PARAM_Latency, SPA_PARAM_INFO_READWRITE); + port->info.params = port->params; + port->info.n_params = N_PORT_PARAMS; + + port->latency = SPA_LATENCY_INFO(SPA_DIRECTION_OUTPUT); + port->latency.min_quantum = 1.0f; + port->latency.max_quantum = 1.0f; + + /* Init the buffer lists */ + spa_list_init(&port->ready); + spa_list_init(&port->free); + + this->quantum_limit = 8192; + + if (info != NULL) { + if (info && (str = spa_dict_lookup(info, "clock.quantum-limit"))) + spa_atou32(str, &this->quantum_limit, 0); + if ((str = spa_dict_lookup(info, SPA_KEY_API_BLUEZ5_TRANSPORT)) != NULL) + sscanf(str, "pointer:%p", &this->transport); + if ((str = spa_dict_lookup(info, "bluez5.media-source-role")) != NULL) + this->is_input = spa_streq(str, "input"); + if ((str = spa_dict_lookup(info, "api.bluez5.a2dp-duplex")) != NULL) + this->is_duplex = spa_atob(str); + } + + if (this->transport == NULL) { + spa_log_error(this->log, "a transport is needed"); + return -EINVAL; + } + if (this->transport->media_codec == NULL) { + spa_log_error(this->log, "a transport codec is needed"); + return -EINVAL; + } + this->codec = this->transport->media_codec; + + if (this->is_duplex) { + if (!this->codec->duplex_codec) { + spa_log_error(this->log, "transport codec doesn't support duplex"); + return -EINVAL; + } + this->codec = this->codec->duplex_codec; + this->is_input = true; + } + this->use_duplex_source = this->is_duplex || (this->codec->duplex_codec != NULL); + + if (this->codec->bap) + this->is_input = this->transport->bap_initiator; + + if (this->codec->init_props != NULL) + this->codec_props = this->codec->init_props(this->codec, + this->is_duplex ? 0 : MEDIA_CODEC_FLAG_SINK, + this->transport->device->settings); + + spa_bt_transport_add_listener(this->transport, + &this->transport_listener, &transport_events, this); + + this->timerfd = spa_system_timerfd_create(this->data_system, + CLOCK_MONOTONIC, SPA_FD_CLOEXEC | SPA_FD_NONBLOCK); + + if (this->use_duplex_source) { + this->duplex_timerfd = spa_system_timerfd_create(this->data_system, + CLOCK_MONOTONIC, SPA_FD_CLOEXEC | SPA_FD_NONBLOCK); + } else { + this->duplex_timerfd = -1; + } + + return 0; +} + +static const struct spa_interface_info impl_interfaces = { + {SPA_TYPE_INTERFACE_Node,}, +}; + +static int +impl_enum_interface_info(const struct spa_handle_factory *factory, + const struct spa_interface_info **info, uint32_t *index) +{ + spa_return_val_if_fail(factory != NULL, -EINVAL); + spa_return_val_if_fail(info != NULL, -EINVAL); + spa_return_val_if_fail(index != NULL, -EINVAL); + + switch (*index) { + case 0: + *info = &impl_interfaces*index; + break; + default: + return 0; + } + (*index)++; + return 1; +} + +static const struct spa_dict_item info_items = { + { SPA_KEY_FACTORY_AUTHOR, "Collabora Ltd. <contact@collabora.com>" }, + { SPA_KEY_FACTORY_DESCRIPTION, "Capture bluetooth audio with media" }, + { SPA_KEY_FACTORY_USAGE, SPA_KEY_API_BLUEZ5_TRANSPORT"=<transport>" }, +}; + +static const struct spa_dict info = SPA_DICT_INIT_ARRAY(info_items); + +const struct spa_handle_factory spa_media_source_factory = { + SPA_VERSION_HANDLE_FACTORY, + SPA_NAME_API_BLUEZ5_MEDIA_SOURCE, + &info, + impl_get_size, + impl_init, + impl_enum_interface_info, +}; + +/* Retained for backward compatibility */ +const struct spa_handle_factory spa_a2dp_source_factory = { + SPA_VERSION_HANDLE_FACTORY, + SPA_NAME_API_BLUEZ5_A2DP_SOURCE, + &info, + impl_get_size, + impl_init, + impl_enum_interface_info, +};
View file
pipewire-0.3.58.tar.gz/spa/plugins/bluez5/meson.build -> pipewire-0.3.59.tar.gz/spa/plugins/bluez5/meson.build
Changed
@@ -17,9 +17,9 @@ bluez5_sources = 'plugin.c', 'codec-loader.c', - 'a2dp-codecs.c', - 'a2dp-sink.c', - 'a2dp-source.c', + 'media-codecs.c', + 'media-sink.c', + 'media-source.c', 'sco-sink.c', 'sco-source.c', 'sco-io.c', @@ -59,7 +59,7 @@ codec_args = '-DCODEC_PLUGIN' bluez_codec_sbc = shared_library('spa-codec-bluez5-sbc', - 'a2dp-codec-sbc.c', 'a2dp-codecs.c' , + 'a2dp-codec-sbc.c', 'media-codecs.c' , include_directories : configinc , c_args : codec_args, dependencies : spa_dep, sbc_dep , @@ -67,7 +67,7 @@ install_dir : spa_plugindir / 'bluez5') bluez_codec_faststream = shared_library('spa-codec-bluez5-faststream', - 'a2dp-codec-faststream.c', 'a2dp-codecs.c' , + 'a2dp-codec-faststream.c', 'media-codecs.c' , include_directories : configinc , c_args : codec_args, dependencies : spa_dep, sbc_dep , @@ -76,7 +76,7 @@ if fdk_aac_dep.found() bluez_codec_aac = shared_library('spa-codec-bluez5-aac', - 'a2dp-codec-aac.c', 'a2dp-codecs.c' , + 'a2dp-codec-aac.c', 'media-codecs.c' , include_directories : configinc , c_args : codec_args, dependencies : spa_dep, fdk_aac_dep , @@ -86,7 +86,7 @@ if aptx_dep.found() bluez_codec_aptx = shared_library('spa-codec-bluez5-aptx', - 'a2dp-codec-aptx.c', 'a2dp-codecs.c' , + 'a2dp-codec-aptx.c', 'media-codecs.c' , include_directories : configinc , c_args : codec_args, dependencies : spa_dep, aptx_dep, sbc_dep , @@ -102,7 +102,7 @@ ldac_dep += ldac_abr_dep endif bluez_codec_ldac = shared_library('spa-codec-bluez5-ldac', - 'a2dp-codec-ldac.c', 'a2dp-codecs.c' , + 'a2dp-codec-ldac.c', 'media-codecs.c' , include_directories : configinc , c_args : ldac_args, dependencies : spa_dep, ldac_dep , @@ -112,7 +112,7 @@ if get_option('bluez5-codec-lc3plus').allowed() and lc3plus_dep.found() bluez_codec_lc3plus = shared_library('spa-codec-bluez5-lc3plus', - 'a2dp-codec-lc3plus.c', 'a2dp-codecs.c' , + 'a2dp-codec-lc3plus.c', 'media-codecs.c' , include_directories : configinc , c_args : codec_args, dependencies : spa_dep, lc3plus_dep, mathlib , @@ -124,10 +124,20 @@ opus_args = codec_args opus_dep = opus_dep bluez_codec_opus = shared_library('spa-codec-bluez5-opus', - 'a2dp-codec-opus.c', 'a2dp-codecs.c' , + 'a2dp-codec-opus.c', 'media-codecs.c' , include_directories : configinc , c_args : opus_args, dependencies : spa_dep, opus_dep, mathlib , install : true, install_dir : spa_plugindir / 'bluez5') endif + +if get_option('bluez5-codec-lc3').allowed() and lc3_dep.found() + bluez_codec_lc3 = shared_library('spa-codec-bluez5-lc3', + 'bap-codec-lc3.c', 'media-codecs.c' , + include_directories : configinc , + c_args : codec_args, + dependencies : spa_dep, lc3_dep, mathlib , + install : true, + install_dir : spa_plugindir / 'bluez5') +endif
View file
pipewire-0.3.58.tar.gz/spa/plugins/bluez5/plugin.c -> pipewire-0.3.59.tar.gz/spa/plugins/bluez5/plugin.c
Changed
@@ -29,10 +29,12 @@ extern const struct spa_handle_factory spa_bluez5_dbus_factory; extern const struct spa_handle_factory spa_bluez5_device_factory; -extern const struct spa_handle_factory spa_a2dp_sink_factory; -extern const struct spa_handle_factory spa_a2dp_source_factory; +extern const struct spa_handle_factory spa_media_sink_factory; +extern const struct spa_handle_factory spa_media_source_factory; extern const struct spa_handle_factory spa_sco_sink_factory; extern const struct spa_handle_factory spa_sco_source_factory; +extern const struct spa_handle_factory spa_a2dp_sink_factory; +extern const struct spa_handle_factory spa_a2dp_source_factory; SPA_EXPORT int spa_handle_factory_enum(const struct spa_handle_factory **factory, uint32_t *index) @@ -48,10 +50,10 @@ *factory = &spa_bluez5_device_factory; break; case 2: - *factory = &spa_a2dp_sink_factory; + *factory = &spa_media_sink_factory; break; case 3: - *factory = &spa_a2dp_source_factory; + *factory = &spa_media_source_factory; break; case 4: *factory = &spa_sco_sink_factory; @@ -59,6 +61,12 @@ case 5: *factory = &spa_sco_source_factory; break; + case 6: + *factory = &spa_a2dp_sink_factory; + break; + case 7: + *factory = &spa_a2dp_source_factory; + break; default: return 0; }
View file
pipewire-0.3.58.tar.gz/spa/plugins/bluez5/quirks.c -> pipewire-0.3.59.tar.gz/spa/plugins/bluez5/quirks.c
Changed
@@ -56,7 +56,6 @@ #include <spa/utils/json.h> #include <spa/utils/string.h> -#include "a2dp-codecs.h" #include "defs.h" static struct spa_log_topic log_topic = SPA_LOG_TOPIC(0, "spa.bluez5.quirks");
View file
pipewire-0.3.58.tar.gz/spa/plugins/libcamera/libcamera-device.cpp -> pipewire-0.3.59.tar.gz/spa/plugins/libcamera/libcamera-device.cpp
Changed
@@ -54,30 +54,29 @@ using namespace libcamera; -struct props { - char device128; - char device_name128; -}; - -static void reset_props(struct props *props) -{ - spa_zero(*props); -} +namespace { struct impl { struct spa_handle handle; - struct spa_device device; + struct spa_device device = {}; struct spa_log *log; - struct props props; + std::string device_id; struct spa_hook_list hooks; - CameraManager *manager; + std::shared_ptr<CameraManager> manager; std::shared_ptr<Camera> camera; + + impl(spa_log *log, + std::shared_ptr<CameraManager> manager, + std::shared_ptr<Camera> camera, + std::string device_id); }; +} + static std::string cameraModel(const Camera *camera) { const ControlList &props = camera->properties(); @@ -120,11 +119,11 @@ info.change_mask = SPA_DEVICE_CHANGE_MASK_PROPS; #define ADD_ITEM(key, value) itemsn_items++ = SPA_DICT_ITEM_INIT(key, value) - snprintf(path, sizeof(path), "libcamera:%s", impl->props.device); + snprintf(path, sizeof(path), "libcamera:%s", impl->device_id.c_str()); ADD_ITEM(SPA_KEY_OBJECT_PATH, path); ADD_ITEM(SPA_KEY_DEVICE_API, "libcamera"); ADD_ITEM(SPA_KEY_MEDIA_CLASS, "Video/Device"); - ADD_ITEM(SPA_KEY_API_LIBCAMERA_PATH, impl->props.device); + ADD_ITEM(SPA_KEY_API_LIBCAMERA_PATH, impl->device_id.c_str()); if (auto location = cameraLoc(impl->camera.get())) ADD_ITEM(SPA_KEY_API_LIBCAMERA_LOCATION, location); @@ -132,7 +131,7 @@ snprintf(model, sizeof(model), "%s", cameraModel(impl->camera.get()).c_str()); ADD_ITEM(SPA_KEY_DEVICE_PRODUCT_NAME, model); ADD_ITEM(SPA_KEY_DEVICE_DESCRIPTION, model); - snprintf(name, sizeof(name), "libcamera_device.%s", impl->props.device); + snprintf(name, sizeof(name), "libcamera_device.%s", impl->device_id.c_str()); ADD_ITEM(SPA_KEY_DEVICE_NAME, name); #undef ADD_ITEM @@ -235,13 +234,30 @@ static int impl_clear(struct spa_handle *handle) { - struct impl *impl = (struct impl *) handle; - if (impl->manager) - libcamera_manager_release(impl->manager); - impl->manager = NULL; + std::destroy_at(reinterpret_cast<impl *>(handle)); return 0; } +impl::impl(spa_log *log, + std::shared_ptr<CameraManager> manager, + std::shared_ptr<Camera> camera, + std::string device_id) + : handle({ SPA_VERSION_HANDLE, impl_get_interface, impl_clear }), + log(log), + device_id(std::move(device_id)), + manager(std::move(manager)), + camera(std::move(camera)) +{ + libcamera_log_topic_init(log); + + spa_hook_list_init(&hooks); + + device.iface = SPA_INTERFACE_INIT( + SPA_TYPE_INTERFACE_Device, + SPA_VERSION_DEVICE, + &impl_device, this); +} + static size_t impl_get_size(const struct spa_handle_factory *factory, const struct spa_dict *params) @@ -256,44 +272,32 @@ const struct spa_support *support, uint32_t n_support) { - struct impl *impl; const char *str; int res; spa_return_val_if_fail(factory != NULL, -EINVAL); spa_return_val_if_fail(handle != NULL, -EINVAL); - handle->get_interface = impl_get_interface; - handle->clear = impl_clear, impl = (struct impl *) handle; - - impl->log = (struct spa_log*) spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log); - libcamera_log_topic_init(impl->log); - - spa_hook_list_init(&impl->hooks); - - impl->device.iface = SPA_INTERFACE_INIT( - SPA_TYPE_INTERFACE_Device, - SPA_VERSION_DEVICE, - &impl_device, impl); + auto log = static_cast<spa_log *>(spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log)); - reset_props(&impl->props); + auto manager = libcamera_manager_acquire(res); + if (!manager) { + spa_log_error(log, "can't start camera manager: %s", spa_strerror(res)); + return res; + } + std::string device_id; if (info && (str = spa_dict_lookup(info, SPA_KEY_API_LIBCAMERA_PATH))) - strncpy(impl->props.device, str, sizeof(impl->props.device)); - - impl->manager = libcamera_manager_acquire(); - if (impl->manager == NULL) { - res = -errno; - spa_log_error(impl->log, "can't start camera manager: %s", spa_strerror(res)); - return res; - } + device_id = str; - impl->camera = impl->manager->get(impl->props.device); - if (impl->camera == NULL) { - spa_log_error(impl->log, "unknown camera id %s", impl->props.device); - libcamera_manager_release(impl->manager); + auto camera = manager->get(device_id); + if (!camera) { + spa_log_error(log, "unknown camera id %s", device_id.c_str()); return -ENOENT; } + + new (handle) impl(log, std::move(manager), std::move(camera), std::move(device_id)); + return 0; }
View file
pipewire-0.3.58.tar.gz/spa/plugins/libcamera/libcamera-manager.cpp -> pipewire-0.3.59.tar.gz/spa/plugins/libcamera/libcamera-manager.cpp
Changed
@@ -29,7 +29,10 @@ #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> - +#include <utility> +#include <mutex> +#include <optional> +#include <queue> #include <libcamera/camera.h> #include <libcamera/camera_manager.h> @@ -54,67 +57,65 @@ #define MAX_DEVICES 64 -struct global { - int ref; - CameraManager *manager; -}; - -static struct global global; +namespace { struct device { uint32_t id; std::shared_ptr<Camera> camera; }; -typedef struct impl { +struct impl { struct spa_handle handle; - struct spa_device device; + struct spa_device device = {}; struct spa_log *log; - struct spa_loop *main_loop; + struct spa_loop_utils *loop_utils; struct spa_hook_list hooks; - uint64_t info_all; - struct spa_device_info info; + static constexpr uint64_t info_all = SPA_DEVICE_CHANGE_MASK_FLAGS | SPA_DEVICE_CHANGE_MASK_PROPS; + struct spa_device_info info = SPA_DEVICE_INFO_INIT(); - CameraManager *manager; + std::shared_ptr<CameraManager> manager; void addCamera(std::shared_ptr<libcamera::Camera> camera); - void removeCamera(std::shared_ptr<libcamera::Camera> camera); + void removeCamera(std::shared_ptr<libcamera::Camera> camera); struct device devicesMAX_DEVICES; - uint32_t n_devices; -} Impl; + uint32_t n_devices = 0; -int libcamera_manager_release(CameraManager *manager) -{ - if (global.manager != manager) - return -EINVAL; + struct hotplug_event { + enum class type { add, remove } type; + std::shared_ptr<Camera> camera; + }; - if (--global.ref == 0) { - global.manager->stop(); - delete global.manager; - global.manager = NULL; + std::mutex hotplug_events_lock; + std::queue<hotplug_event> hotplug_events; + struct spa_source *hotplug_event_source; + + impl(spa_log *log, spa_loop_utils *loop_utils, spa_source *hotplug_event_source); + + ~impl() + { + spa_loop_utils_destroy_source(loop_utils, hotplug_event_source); } - return 0; +}; + } -CameraManager *libcamera_manager_acquire(void) +static std::weak_ptr<CameraManager> global_manager; + +std::shared_ptr<CameraManager> libcamera_manager_acquire(int& res) { - int res; + if (auto manager = global_manager.lock()) + return manager; - if (global.ref++ == 0) { - global.manager = new CameraManager(); - if (global.manager == NULL) - return NULL; + auto manager = std::make_shared<CameraManager>(); + if ((res = manager->start()) < 0) + return {}; - if ((res = global.manager->start()) < 0) { - libcamera_manager_release(global.manager); - errno = -res; - return NULL; - } - } - return global.manager; + global_manager = manager; + + return manager; } static struct device *add_device(struct impl *impl, std::shared_ptr<Camera> camera) @@ -127,15 +128,15 @@ id = impl->n_devices++; device = &impl->devicesid; device->id = id; - device->camera = camera; + device->camera = std::move(camera); return device; } -static struct device *find_device(struct impl *impl, std::shared_ptr<Camera> camera) +static struct device *find_device(struct impl *impl, const Camera *camera) { uint32_t i; for (i = 0; i < impl->n_devices; i++) { - if (impl->devicesi.camera == camera) + if (impl->devicesi.camera.get() == camera) return &impl->devicesi; } return NULL; @@ -143,12 +144,14 @@ static void remove_device(struct impl *impl, struct device *device) { - *device = impl->devices--impl->n_devices; + device->camera.reset(); + *device = std::move(impl->devices--impl->n_devices); } static void clear_devices(struct impl *impl) { - impl->n_devices = 0; + while (impl->n_devices > 0) + impl->devices--impl->n_devices.camera.reset(); } static int emit_object_info(struct impl *impl, struct device *device) @@ -176,66 +179,119 @@ ADD_ITEM(SPA_KEY_API_LIBCAMERA_PATH, path); #undef ADD_ITEM - dict = SPA_DICT_INIT(items, n_items); - info.props = &dict; - spa_device_emit_object_info(&impl->hooks, id, &info); + dict = SPA_DICT_INIT(items, n_items); + info.props = &dict; + spa_device_emit_object_info(&impl->hooks, id, &info); return 1; } -void Impl::addCamera(std::shared_ptr<Camera> camera) +static void try_add_camera(struct impl *impl, std::shared_ptr<Camera> camera) { - struct impl *impl = this; struct device *device; - spa_log_info(impl->log, "new camera"); - - if ((device = find_device(impl, camera)) != NULL) + if ((device = find_device(impl, camera.get())) != NULL) return; - if ((device = add_device(impl, camera)) == NULL) + if ((device = add_device(impl, std::move(camera))) == NULL) return; + spa_log_info(impl->log, "camera added: %s", device->camera->id().c_str()); emit_object_info(impl, device); } -void Impl::removeCamera(std::shared_ptr<Camera> camera) +static void try_remove_camera(struct impl *impl, const Camera *camera) { - struct impl *impl = this; struct device *device; - spa_log_info(impl->log, "camera removed"); if ((device = find_device(impl, camera)) == NULL) return; + spa_log_info(impl->log, "camera removed: %s", device->camera->id().c_str()); remove_device(impl, device); } -static int start_monitor(struct impl *impl) +static void consume_hotplug_event(struct impl *impl, impl::hotplug_event& event) { - impl->manager->cameraAdded.connect(impl, &Impl::addCamera); - impl->manager->cameraRemoved.connect(impl, &Impl::removeCamera); - return 0; + auto& type, camera = event; + + switch (type) { + case impl::hotplug_event::type::add: + spa_log_info(impl->log, "camera appeared: %s", camera->id().c_str()); + try_add_camera(impl, std::move(camera)); + break; + case impl::hotplug_event::type::remove: + spa_log_info(impl->log, "camera disappeared: %s", camera->id().c_str()); + try_remove_camera(impl, camera.get()); + break; + } +} + +static void on_hotplug_event(void *data, std::uint64_t) +{ + auto impl = static_cast<struct impl *>(data); + + for (;;) { + std::optional<impl::hotplug_event> event; + + { + std::unique_lock guard(impl->hotplug_events_lock); + + if (!impl->hotplug_events.empty()) { + event = std::move(impl->hotplug_events.front()); + impl->hotplug_events.pop(); + } + } + + if (!event) + break; + + consume_hotplug_event(impl, *event); + } +} + +void impl::addCamera(std::shared_ptr<Camera> camera) +{ + { + std::unique_lock guard(hotplug_events_lock); + hotplug_events.push({ hotplug_event::type::add, std::move(camera) }); + } + + spa_loop_utils_signal_event(loop_utils, hotplug_event_source); +} + +void impl::removeCamera(std::shared_ptr<Camera> camera) +{ + { + std::unique_lock guard(hotplug_events_lock); + hotplug_events.push({ hotplug_event::type::remove, std::move(camera) }); + } + + spa_loop_utils_signal_event(loop_utils, hotplug_event_source); +} + +static void start_monitor(struct impl *impl) +{ + impl->manager->cameraAdded.connect(impl, &impl::addCamera); + impl->manager->cameraRemoved.connect(impl, &impl::removeCamera); } static int stop_monitor(struct impl *impl) { - if (impl->manager != NULL) { - impl->manager->cameraAdded.disconnect(impl, &Impl::addCamera); - impl->manager->cameraRemoved.disconnect(impl, &Impl::removeCamera); + if (impl->manager) { + impl->manager->cameraAdded.disconnect(impl, &impl::addCamera); + impl->manager->cameraRemoved.disconnect(impl, &impl::removeCamera); } clear_devices (impl); return 0; } -static int enum_devices(struct impl *impl) +static void collect_existing_devices(struct impl *impl) { auto cameras = impl->manager->cameras(); - for (const std::shared_ptr<Camera> &cam : cameras) { - impl->addCamera(cam); - } - return 0; + for (std::shared_ptr<Camera>& camera : cameras) + try_add_camera(impl, std::move(camera)); } static const struct spa_dict_item device_info_items = { @@ -262,9 +318,7 @@ struct impl *impl = (struct impl*)hook->priv; if (spa_hook_list_is_empty(&impl->hooks)) { stop_monitor(impl); - if (impl->manager) - libcamera_manager_release(impl->manager); - impl->manager = NULL; + impl->manager.reset(); } } @@ -274,26 +328,29 @@ { int res; struct impl *impl = (struct impl*) object; - struct spa_hook_list save; + struct spa_hook_list save; + bool had_manager = !!impl->manager; spa_return_val_if_fail(impl != NULL, -EINVAL); spa_return_val_if_fail(events != NULL, -EINVAL); - impl->manager = libcamera_manager_acquire(); - if (impl->manager == NULL) - return -errno; + if (!impl->manager && !(impl->manager = libcamera_manager_acquire(res))) + return res; - spa_hook_list_isolate(&impl->hooks, &save, listener, events, data); + spa_hook_list_isolate(&impl->hooks, &save, listener, events, data); emit_device_info(impl, true); - if ((res = enum_devices(impl)) < 0) - return res; - - if ((res = start_monitor(impl)) < 0) - return res; + if (had_manager) { + for (std::size_t i = 0; i < impl->n_devices; i++) + emit_object_info(impl, &impl->devicesi); + } + else { + collect_existing_devices(impl); + start_monitor(impl); + } - spa_hook_list_join(&impl->hooks, &save); + spa_hook_list_join(&impl->hooks, &save); listener->removed = impl_hook_removed; listener->priv = impl; @@ -325,14 +382,30 @@ static int impl_clear(struct spa_handle *handle) { - struct impl *impl = (struct impl *) handle; + auto impl = reinterpret_cast<struct impl *>(handle); + stop_monitor(impl); - if (impl->manager) - libcamera_manager_release(impl->manager); - impl->manager = NULL; + std::destroy_at(impl); + return 0; } +impl::impl(spa_log *log, spa_loop_utils *loop_utils, spa_source *hotplug_event_source) + : handle({ SPA_VERSION_HANDLE, impl_get_interface, impl_clear }), + log(log), + loop_utils(loop_utils), + hotplug_event_source(hotplug_event_source) +{ + libcamera_log_topic_init(log); + + spa_hook_list_init(&hooks); + + device.iface = SPA_INTERFACE_INIT( + SPA_TYPE_INTERFACE_Device, + SPA_VERSION_DEVICE, + &impl_device, this); +} + static size_t impl_get_size(const struct spa_handle_factory *factory, const struct spa_dict *params) @@ -347,35 +420,25 @@ const struct spa_support *support, uint32_t n_support) { - struct impl *impl; - spa_return_val_if_fail(factory != NULL, -EINVAL); spa_return_val_if_fail(handle != NULL, -EINVAL); - handle->get_interface = impl_get_interface; - handle->clear = impl_clear; - - impl = (struct impl *) handle; - - impl->log = (struct spa_log*)spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log); - libcamera_log_topic_init(impl->log); + auto log = static_cast<spa_log *>(spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log)); - impl->main_loop = (struct spa_loop*)spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Loop); - if (impl->main_loop == NULL) { - spa_log_error(impl->log, "a main-loop is needed"); + auto loop_utils = static_cast<spa_loop_utils *>(spa_support_find(support, n_support, SPA_TYPE_INTERFACE_LoopUtils)); + if (!loop_utils) { + spa_log_error(log, "a " SPA_TYPE_INTERFACE_LoopUtils " is needed"); return -EINVAL; } - spa_hook_list_init(&impl->hooks); - impl->device.iface = SPA_INTERFACE_INIT( - SPA_TYPE_INTERFACE_Device, - SPA_VERSION_DEVICE, - &impl_device, impl); + auto hotplug_event_source = spa_loop_utils_add_event(loop_utils, on_hotplug_event, handle); + if (!hotplug_event_source) { + int res = -errno; + spa_log_error(log, "failed to create hotplug event: %m"); + return res; + } - impl->info = SPA_DEVICE_INFO_INIT(); - impl->info_all = SPA_DEVICE_CHANGE_MASK_FLAGS | - SPA_DEVICE_CHANGE_MASK_PROPS; - impl->info.flags = 0; + new (handle) impl(log, loop_utils, hotplug_event_source); return 0; }
View file
pipewire-0.3.58.tar.gz/spa/plugins/libcamera/libcamera-manager.hpp -> pipewire-0.3.59.tar.gz/spa/plugins/libcamera/libcamera-manager.hpp
Changed
@@ -22,11 +22,8 @@ * DEALINGS IN THE SOFTWARE. */ -#include <libcamera/camera_manager.h> - -#include <linux/media.h> +#include <memory> -using namespace libcamera; +#include <libcamera/camera_manager.h> -CameraManager *libcamera_manager_acquire(void); -int libcamera_manager_release(CameraManager *manager); +std::shared_ptr<libcamera::CameraManager> libcamera_manager_acquire(int& res);
View file
pipewire-0.3.58.tar.gz/spa/plugins/libcamera/libcamera-source.cpp -> pipewire-0.3.59.tar.gz/spa/plugins/libcamera/libcamera-source.cpp
Changed
@@ -31,6 +31,7 @@ #include <sys/stat.h> #include <fcntl.h> #include <deque> +#include <optional> #include <spa/support/plugin.h> #include <spa/support/log.h> @@ -60,15 +61,9 @@ #include "libcamera.h" #include "libcamera-manager.hpp" -struct props { - char device128; - char device_name128; -}; +using namespace libcamera; -static void reset_props(struct props *props) -{ - spa_zero(*props); -} +namespace { #define MAX_BUFFERS 32 #define MASK_BUFFERS 31 @@ -97,75 +92,97 @@ struct port { struct impl *impl; - bool have_format; - struct spa_video_info current_format; - struct spa_fraction rate; + std::optional<spa_video_info> current_format; + + struct spa_fraction rate = {}; StreamConfiguration streamConfig; - uint32_t memtype; + uint32_t memtype = 0; struct control controlsMAX_CONTROLS; - uint32_t n_controls; + uint32_t n_controls = 0; struct buffer buffersMAX_BUFFERS; - uint32_t n_buffers; + uint32_t n_buffers = 0; struct spa_list queue; - struct spa_ringbuffer ring; + struct spa_ringbuffer ring = SPA_RINGBUFFER_INIT(); uint32_t ring_idsMAX_BUFFERS; - uint64_t info_all; - struct spa_port_info info; - struct spa_io_buffers *io; - struct spa_io_sequence *control; + static constexpr uint64_t info_all = SPA_PORT_CHANGE_MASK_FLAGS | SPA_PORT_CHANGE_MASK_PARAMS; + struct spa_port_info info = SPA_PORT_INFO_INIT(); + struct spa_io_buffers *io = nullptr; + struct spa_io_sequence *control = nullptr; struct spa_param_info params8; - uint32_t fmt_index; - bool next_fmt; + uint32_t fmt_index = 0; PixelFormat enum_fmt; - uint32_t size_index; - bool next_size; + uint32_t size_index = 0; + + port(struct impl *impl) + : impl(impl) + { + spa_list_init(&queue); + + params0 = SPA_PARAM_INFO(SPA_PARAM_PropInfo, SPA_PARAM_INFO_READ); + params1 = SPA_PARAM_INFO(SPA_PARAM_EnumFormat, SPA_PARAM_INFO_READ); + params2 = SPA_PARAM_INFO(SPA_PARAM_Meta, SPA_PARAM_INFO_READ); + params3 = SPA_PARAM_INFO(SPA_PARAM_IO, SPA_PARAM_INFO_READ); + params4 = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE); + params5 = SPA_PARAM_INFO(SPA_PARAM_Buffers, 0); + + info.flags = SPA_PORT_FLAG_LIVE | SPA_PORT_FLAG_PHYSICAL | SPA_PORT_FLAG_TERMINAL; + info.params = params; + info.n_params = 6; + } }; struct impl { struct spa_handle handle; - struct spa_node node; + struct spa_node node = {}; struct spa_log *log; struct spa_loop *data_loop; struct spa_system *system; - uint64_t info_all; - struct spa_node_info info; + static constexpr uint64_t info_all = + SPA_NODE_CHANGE_MASK_FLAGS | + SPA_NODE_CHANGE_MASK_PROPS | + SPA_NODE_CHANGE_MASK_PARAMS; + struct spa_node_info info = SPA_NODE_INFO_INIT(); struct spa_param_info params8; - struct props props; + + std::string device_id; + std::string device_name; struct spa_hook_list hooks; - struct spa_callbacks callbacks; + struct spa_callbacks callbacks = {}; - struct port out_ports1; + std::array<port, 1> out_ports; - struct spa_io_position *position; - struct spa_io_clock *clock; + struct spa_io_position *position = nullptr; + struct spa_io_clock *clock = nullptr; - CameraManager *manager; + std::shared_ptr<CameraManager> manager; std::shared_ptr<Camera> camera; - FrameBufferAllocator *allocator; + FrameBufferAllocator *allocator = nullptr; std::vector<std::unique_ptr<libcamera::Request>> requestPool; std::deque<libcamera::Request *> pendingRequests; void requestComplete(libcamera::Request *request); - unsigned int have_config; std::unique_ptr<CameraConfiguration> config; - struct spa_source source; + struct spa_source source = {}; - unsigned int active:1; - unsigned int acquired:1; + bool active = false; + bool acquired = false; + + impl(spa_log *log, spa_loop *data_loop, spa_system *system, + std::shared_ptr<CameraManager> manager, std::shared_ptr<Camera> camera, std::string device_id); }; -typedef struct impl Impl; +} #define CHECK_PORT(impl,direction,port_id) ((direction) == SPA_DIRECTION_OUTPUT && (port_id) == 0) @@ -198,22 +215,20 @@ switch (id) { case SPA_PARAM_PropInfo: { - struct props *p = &impl->props; - switch (result.index) { case 0: param = (struct spa_pod*)spa_pod_builder_add_object(&b, SPA_TYPE_OBJECT_PropInfo, id, SPA_PROP_INFO_id, SPA_POD_Id(SPA_PROP_device), SPA_PROP_INFO_description, SPA_POD_String("The libcamera device"), - SPA_PROP_INFO_type, SPA_POD_String(p->device)); + SPA_PROP_INFO_type, SPA_POD_String(impl->device_id.c_str())); break; case 1: param = (struct spa_pod*)spa_pod_builder_add_object(&b, SPA_TYPE_OBJECT_PropInfo, id, SPA_PROP_INFO_id, SPA_POD_Id(SPA_PROP_deviceName), SPA_PROP_INFO_description, SPA_POD_String("The libcamera device name"), - SPA_PROP_INFO_type, SPA_POD_String(p->device_name)); + SPA_PROP_INFO_type, SPA_POD_String(impl->device_name.c_str())); break; default: return 0; @@ -222,14 +237,12 @@ } case SPA_PARAM_Props: { - struct props *p = &impl->props; - switch (result.index) { case 0: param = (struct spa_pod*)spa_pod_builder_add_object(&b, SPA_TYPE_OBJECT_Props, id, - SPA_PROP_device, SPA_POD_String(p->device), - SPA_PROP_deviceName, SPA_POD_String(p->device_name)); + SPA_PROP_device, SPA_POD_String(impl->device_id.c_str()), + SPA_PROP_deviceName, SPA_POD_String(impl->device_name.c_str())); break; default: return 0; @@ -262,15 +275,22 @@ switch (id) { case SPA_PARAM_Props: { - struct props *p = &impl->props; - if (param == NULL) { - reset_props(p); + impl->device_id.clear(); + impl->device_name.clear(); return 0; } - spa_pod_parse_object(param, + + char device128; + int res = spa_pod_parse_object(param, SPA_TYPE_OBJECT_Props, NULL, - SPA_PROP_device, SPA_POD_OPT_Stringn(p->device, sizeof(p->device))); + SPA_PROP_device, SPA_POD_OPT_Stringn(device, sizeof(device))); + + if (res < 0) + return res; + + impl->device_id = device; + break; } default: @@ -311,7 +331,7 @@ { struct port *port = GET_OUT_PORT(impl, 0); - if (!port->have_format) + if (!port->current_format) return -EIO; if (port->n_buffers == 0) return -EIO; @@ -432,36 +452,36 @@ { struct spa_pod_frame f; - if (!port->have_format) + if (!port->current_format) return -EIO; if (index > 0) return 0; spa_pod_builder_push_object(builder, &f, SPA_TYPE_OBJECT_Format, SPA_PARAM_Format); spa_pod_builder_add(builder, - SPA_FORMAT_mediaType, SPA_POD_Id(port->current_format.media_type), - SPA_FORMAT_mediaSubtype, SPA_POD_Id(port->current_format.media_subtype), + SPA_FORMAT_mediaType, SPA_POD_Id(port->current_format->media_type), + SPA_FORMAT_mediaSubtype, SPA_POD_Id(port->current_format->media_subtype), 0); - switch (port->current_format.media_subtype) { + switch (port->current_format->media_subtype) { case SPA_MEDIA_SUBTYPE_raw: spa_pod_builder_add(builder, - SPA_FORMAT_VIDEO_format, SPA_POD_Id(port->current_format.info.raw.format), - SPA_FORMAT_VIDEO_size, SPA_POD_Rectangle(&port->current_format.info.raw.size), - SPA_FORMAT_VIDEO_framerate, SPA_POD_Fraction(&port->current_format.info.raw.framerate), + SPA_FORMAT_VIDEO_format, SPA_POD_Id(port->current_format->info.raw.format), + SPA_FORMAT_VIDEO_size, SPA_POD_Rectangle(&port->current_format->info.raw.size), + SPA_FORMAT_VIDEO_framerate, SPA_POD_Fraction(&port->current_format->info.raw.framerate), 0); break; case SPA_MEDIA_SUBTYPE_mjpg: case SPA_MEDIA_SUBTYPE_jpeg: spa_pod_builder_add(builder, - SPA_FORMAT_VIDEO_size, SPA_POD_Rectangle(&port->current_format.info.mjpg.size), - SPA_FORMAT_VIDEO_framerate, SPA_POD_Fraction(&port->current_format.info.mjpg.framerate), + SPA_FORMAT_VIDEO_size, SPA_POD_Rectangle(&port->current_format->info.mjpg.size), + SPA_FORMAT_VIDEO_framerate, SPA_POD_Fraction(&port->current_format->info.mjpg.framerate), 0); break; case SPA_MEDIA_SUBTYPE_h264: spa_pod_builder_add(builder, - SPA_FORMAT_VIDEO_size, SPA_POD_Rectangle(&port->current_format.info.h264.size), - SPA_FORMAT_VIDEO_framerate, SPA_POD_Fraction(&port->current_format.info.h264.framerate), + SPA_FORMAT_VIDEO_size, SPA_POD_Rectangle(&port->current_format->info.h264.size), + SPA_FORMAT_VIDEO_framerate, SPA_POD_Fraction(&port->current_format->info.h264.framerate), 0); break; default: @@ -515,7 +535,7 @@ break; case SPA_PARAM_Buffers: { - if (!port->have_format) + if (!port->current_format) return -EIO; if (result.index > 0) return 0; @@ -591,12 +611,12 @@ int res; if (format == NULL) { - if (!port->have_format) + if (!port->current_format) return 0; spa_libcamera_stream_off(impl); spa_libcamera_clear_buffers(impl, port); - port->have_format = false; + port->current_format.reset(); spa_libcamera_close(impl); goto done; @@ -616,31 +636,31 @@ return -EINVAL; } - if (port->have_format && info.media_type == port->current_format.media_type && - info.media_subtype == port->current_format.media_subtype && - info.info.raw.format == port->current_format.info.raw.format && - info.info.raw.size.width == port->current_format.info.raw.size.width && - info.info.raw.size.height == port->current_format.info.raw.size.height) + if (port->current_format && info.media_type == port->current_format->media_type && + info.media_subtype == port->current_format->media_subtype && + info.info.raw.format == port->current_format->info.raw.format && + info.info.raw.size.width == port->current_format->info.raw.size.width && + info.info.raw.size.height == port->current_format->info.raw.size.height) return 0; break; case SPA_MEDIA_SUBTYPE_mjpg: if (spa_format_video_mjpg_parse(format, &info.info.mjpg) < 0) return -EINVAL; - if (port->have_format && info.media_type == port->current_format.media_type && - info.media_subtype == port->current_format.media_subtype && - info.info.mjpg.size.width == port->current_format.info.mjpg.size.width && - info.info.mjpg.size.height == port->current_format.info.mjpg.size.height) + if (port->current_format && info.media_type == port->current_format->media_type && + info.media_subtype == port->current_format->media_subtype && + info.info.mjpg.size.width == port->current_format->info.mjpg.size.width && + info.info.mjpg.size.height == port->current_format->info.mjpg.size.height) return 0; break; case SPA_MEDIA_SUBTYPE_h264: if (spa_format_video_h264_parse(format, &info.info.h264) < 0) return -EINVAL; - if (port->have_format && info.media_type == port->current_format.media_type && - info.media_subtype == port->current_format.media_subtype && - info.info.h264.size.width == port->current_format.info.h264.size.width && - info.info.h264.size.height == port->current_format.info.h264.size.height) + if (port->current_format && info.media_type == port->current_format->media_type && + info.media_subtype == port->current_format->media_subtype && + info.info.h264.size.width == port->current_format->info.h264.size.width && + info.info.h264.size.height == port->current_format->info.h264.size.height) return 0; break; default: @@ -648,9 +668,9 @@ } } - if (port->have_format && !(flags & SPA_NODE_PARAM_FLAG_TEST_ONLY)) { + if (port->current_format && !(flags & SPA_NODE_PARAM_FLAG_TEST_ONLY)) { spa_libcamera_use_buffers(impl, port, NULL, 0); - port->have_format = false; + port->current_format.reset(); } if (spa_libcamera_set_format(impl, port, &info, flags & SPA_NODE_PARAM_FLAG_TEST_ONLY) < 0) @@ -658,12 +678,11 @@ if (!(flags & SPA_NODE_PARAM_FLAG_TEST_ONLY)) { port->current_format = info; - port->have_format = true; } done: port->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS; - if (port->have_format) { + if (port->current_format) { port->params4 = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_READWRITE); port->params5 = SPA_PARAM_INFO(SPA_PARAM_Buffers, SPA_PARAM_INFO_READ); } else { @@ -715,7 +734,7 @@ port = GET_PORT(impl, direction, port_id); - if (!port->have_format) + if (!port->current_format) return -EIO; if (port->n_buffers) { @@ -896,13 +915,39 @@ static int impl_clear(struct spa_handle *handle) { - struct impl *impl; - - impl = (struct impl *) handle; - impl->~Impl(); + std::destroy_at(reinterpret_cast<impl *>(handle)); return 0; } +impl::impl(spa_log *log, spa_loop *data_loop, spa_system *system, + std::shared_ptr<CameraManager> manager, std::shared_ptr<Camera> camera, std::string device_id) + : handle({ SPA_VERSION_HANDLE, impl_get_interface, impl_clear }), + log(log), + data_loop(data_loop), + system(system), + device_id(std::move(device_id)), + out_ports{{this}}, + manager(std::move(manager)), + camera(std::move(camera)) +{ + libcamera_log_topic_init(log); + + spa_hook_list_init(&hooks); + + node.iface = SPA_INTERFACE_INIT( + SPA_TYPE_INTERFACE_Node, + SPA_VERSION_NODE, + &impl_node, this); + + params0 = SPA_PARAM_INFO(SPA_PARAM_PropInfo, SPA_PARAM_INFO_READ); + params1 = SPA_PARAM_INFO(SPA_PARAM_Props, SPA_PARAM_INFO_READWRITE); + + info.max_output_ports = 1; + info.flags = SPA_NODE_FLAG_RT; + info.params = params; + info.n_params = 2; +} + static size_t impl_get_size(const struct spa_handle_factory *factory, const struct spa_dict *params) @@ -917,87 +962,45 @@ const struct spa_support *support, uint32_t n_support) { - struct impl *impl; const char *str; - struct port *port; int res; spa_return_val_if_fail(factory != NULL, -EINVAL); spa_return_val_if_fail(handle != NULL, -EINVAL); - impl = new(handle) Impl(); - - handle->get_interface = impl_get_interface; - handle->clear = impl_clear; + auto log = static_cast<spa_log *>(spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log)); + auto data_loop = static_cast<spa_loop *>(spa_support_find(support, n_support, SPA_TYPE_INTERFACE_DataLoop)); + auto system = static_cast<spa_system *>(spa_support_find(support, n_support, SPA_TYPE_INTERFACE_System)); - impl->log = (struct spa_log*)spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log); - libcamera_log_topic_init(impl->log); - - impl->data_loop = (struct spa_loop*)spa_support_find(support, n_support, SPA_TYPE_INTERFACE_DataLoop); - impl->system = (struct spa_system*)spa_support_find(support, n_support, SPA_TYPE_INTERFACE_System); - - if (impl->data_loop == NULL) { - spa_log_error(impl->log, "a data_loop is needed"); + if (!data_loop) { + spa_log_error(log, "a data_loop is needed"); return -EINVAL; } - if (impl->system == NULL) { - spa_log_error(impl->log, "a system is needed"); + if (!system) { + spa_log_error(log, "a system is needed"); return -EINVAL; } - impl->node.iface = SPA_INTERFACE_INIT( - SPA_TYPE_INTERFACE_Node, - SPA_VERSION_NODE, - &impl_node, impl); - spa_hook_list_init(&impl->hooks); - - impl->info_all = SPA_NODE_CHANGE_MASK_FLAGS | - SPA_NODE_CHANGE_MASK_PROPS | - SPA_NODE_CHANGE_MASK_PARAMS; - impl->info = SPA_NODE_INFO_INIT(); - impl->info.max_output_ports = 1; - impl->info.flags = SPA_NODE_FLAG_RT; - impl->params0 = SPA_PARAM_INFO(SPA_PARAM_PropInfo, SPA_PARAM_INFO_READ); - impl->params1 = SPA_PARAM_INFO(SPA_PARAM_Props, SPA_PARAM_INFO_READWRITE); - impl->info.params = impl->params; - impl->info.n_params = 2; - reset_props(&impl->props); - - port = GET_OUT_PORT(impl, 0); - port->impl = impl; - spa_list_init(&port->queue); - port->info_all = SPA_PORT_CHANGE_MASK_FLAGS | - SPA_PORT_CHANGE_MASK_PARAMS; - port->info = SPA_PORT_INFO_INIT(); - port->info.flags = SPA_PORT_FLAG_LIVE | - SPA_PORT_FLAG_PHYSICAL | - SPA_PORT_FLAG_TERMINAL; - port->params0 = SPA_PARAM_INFO(SPA_PARAM_PropInfo, SPA_PARAM_INFO_READ); - port->params1 = SPA_PARAM_INFO(SPA_PARAM_EnumFormat, SPA_PARAM_INFO_READ); - port->params2 = SPA_PARAM_INFO(SPA_PARAM_Meta, SPA_PARAM_INFO_READ); - port->params3 = SPA_PARAM_INFO(SPA_PARAM_IO, SPA_PARAM_INFO_READ); - port->params4 = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE); - port->params5 = SPA_PARAM_INFO(SPA_PARAM_Buffers, 0); - port->info.params = port->params; - port->info.n_params = 6; + auto manager = libcamera_manager_acquire(res); + if (!manager) { + spa_log_error(log, "can't start camera manager: %s", spa_strerror(res)); + return res; + } + std::string device_id; if (info && (str = spa_dict_lookup(info, SPA_KEY_API_LIBCAMERA_PATH))) - strncpy(impl->props.device, str, sizeof(impl->props.device)); - - impl->manager = libcamera_manager_acquire(); - if (impl->manager == NULL) { - res = -errno; - spa_log_error(impl->log, "can't start camera manager: %s", spa_strerror(res)); - return res; - } + device_id = str; - impl->camera = impl->manager->get(impl->props.device); - if (impl->camera == NULL) { - spa_log_error(impl->log, "unknown camera id %s", impl->props.device); - libcamera_manager_release(impl->manager); + auto camera = manager->get(device_id); + if (!camera) { + spa_log_error(log, "unknown camera id %s", device_id.c_str()); return -ENOENT; } + + new (handle) impl(log, data_loop, system, + std::move(manager), std::move(camera), std::move(device_id)); + return 0; }
View file
pipewire-0.3.58.tar.gz/spa/plugins/libcamera/libcamera-utils.cpp -> pipewire-0.3.59.tar.gz/spa/plugins/libcamera/libcamera-utils.cpp
Changed
@@ -40,7 +40,7 @@ if (impl->acquired) return 0; - spa_log_info(impl->log, "open camera %s", impl->props.device); + spa_log_info(impl->log, "open camera %s", impl->device_id.c_str()); impl->camera->acquire(); impl->allocator = new FrameBufferAllocator(impl->camera); @@ -54,10 +54,10 @@ struct port *port = &impl->out_ports0; if (!impl->acquired) return 0; - if (impl->active || port->have_format) + if (impl->active || port->current_format) return 0; - spa_log_info(impl->log, "close camera %s", impl->props.device); + spa_log_info(impl->log, "close camera %s", impl->device_id.c_str()); delete impl->allocator; impl->allocator = nullptr; @@ -69,13 +69,12 @@ static void spa_libcamera_get_config(struct impl *impl) { - if (impl->have_config) + if (impl->config) return; StreamRoles roles; roles.push_back(VideoRecording); impl->config = impl->camera->generateConfiguration(roles); - impl->have_config = true; } static int spa_libcamera_buffer_recycle(struct impl *impl, struct port *port, uint32_t buffer_id) @@ -91,10 +90,10 @@ if (buffer_id >= impl->requestPool.size()) { spa_log_warn(impl->log, "invalid buffer_id %u >= %zu", buffer_id, impl->requestPool.size()); - return -EINVAL; - } + return -EINVAL; + } Request *request = impl->requestPoolbuffer_id.get(); - Stream *stream = port->streamConfig.stream(); + Stream *stream = port->streamConfig.stream(); FrameBuffer *buffer = impl->allocator->buffers(stream)buffer_id.get(); if ((res = request->addBuffer(stream, buffer)) < 0) { spa_log_warn(impl->log, "can't add buffer %u for request: %s", @@ -104,7 +103,7 @@ if (!impl->active) { impl->pendingRequests.push_back(request); return 0; - } else { + } else { if ((res = impl->camera->queueRequest(request)) < 0) { spa_log_warn(impl->log, "can't queue buffer %u: %s", buffer_id, spa_strerror(res)); @@ -119,7 +118,7 @@ int res; if ((res = impl->allocator->allocate(port->streamConfig.stream())) < 0) - return res; + return res; for (unsigned int i = 0; i < count; i++) { std::unique_ptr<Request> request = impl->camera->createRequest(i); @@ -129,7 +128,7 @@ } impl->requestPool.push_back(std::move(request)); } - return res; + return res; } static void freeBuffers(struct impl *impl, struct port *port) @@ -419,8 +418,6 @@ if ((res = allocBuffers(impl, port, port->streamConfig.bufferCount)) < 0) goto error; - port->have_format = true; - port->info.change_mask |= SPA_PORT_CHANGE_MASK_FLAGS | SPA_PORT_CHANGE_MASK_RATE; port->info.flags = SPA_PORT_FLAG_CAN_ALLOC_BUFFERS | SPA_PORT_FLAG_LIVE | @@ -607,7 +604,7 @@ } -void Impl::requestComplete(libcamera::Request *request) +void impl::requestComplete(libcamera::Request *request) { struct impl *impl = this; struct port *port = &impl->out_ports0; @@ -618,12 +615,12 @@ spa_log_debug(impl->log, "request complete"); if ((request->status() == Request::RequestCancelled)) { - spa_log_debug(impl->log, "Request was cancelled"); - return; - } + spa_log_debug(impl->log, "Request was cancelled"); + return; + } FrameBuffer *buffer = request->findBuffer(stream); if (buffer == nullptr) { - spa_log_warn(impl->log, "unknown buffer"); + spa_log_warn(impl->log, "unknown buffer"); return; } const FrameMetadata &fmd = buffer->metadata(); @@ -664,7 +661,7 @@ struct port *port = &impl->out_ports0; int res; - if (!port->have_format) { + if (!port->current_format) { spa_log_error(impl->log, "Exting %s with -EIO", __FUNCTION__); return -EIO; } @@ -674,15 +671,15 @@ impl->camera->requestCompleted.connect(impl, &impl::requestComplete); - spa_log_info(impl->log, "starting camera %s", impl->props.device); + spa_log_info(impl->log, "starting camera %s", impl->device_id.c_str()); if ((res = impl->camera->start()) < 0) return res == -EACCES ? -EBUSY : res; for (Request *req : impl->pendingRequests) { - if ((res = impl->camera->queueRequest(req)) < 0) + if ((res = impl->camera->queueRequest(req)) < 0) return res == -EACCES ? -EBUSY : res; - } - impl->pendingRequests.clear(); + } + impl->pendingRequests.clear(); impl->source.func = libcamera_on_fd_events; impl->source.data = impl; @@ -724,7 +721,7 @@ return 0; } - spa_log_info(impl->log, "stopping camera %s", impl->props.device); + spa_log_info(impl->log, "stopping camera %s", impl->device_id.c_str()); impl->pendingRequests.clear(); if ((res = impl->camera->stop()) < 0)
View file
pipewire-0.3.58.tar.gz/spa/plugins/support/logger.c -> pipewire-0.3.59.tar.gz/spa/plugins/support/logger.c
Changed
@@ -357,7 +357,7 @@ if ((str = spa_dict_lookup(info, SPA_KEY_LOG_LEVEL)) != NULL) this->log.level = atoi(str); if ((str = spa_dict_lookup(info, SPA_KEY_LOG_FILE)) != NULL) { - this->file = fopen(str, "w"); + this->file = fopen(str, "we"); if (this->file == NULL) fprintf(stderr, "Warning: failed to open file %s: (%m)", str); else
View file
pipewire-0.3.58.tar.gz/spa/plugins/videoconvert/videoadapter.c -> pipewire-0.3.59.tar.gz/spa/plugins/videoconvert/videoadapter.c
Changed
@@ -823,12 +823,12 @@ case SPA_NODE_COMMAND_Suspend: configure_format(this, 0, NULL); SPA_FALLTHROUGH - case SPA_NODE_COMMAND_Flush: - this->io_buffers.status = SPA_STATUS_OK; - SPA_FALLTHROUGH case SPA_NODE_COMMAND_Pause: this->started = false; break; + case SPA_NODE_COMMAND_Flush: + this->io_buffers.status = SPA_STATUS_OK; + break; default: break; }
View file
pipewire-0.3.59.tar.gz/src/examples/audio-capture.c
Added
@@ -0,0 +1,206 @@ +/* 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. + */ + +/* + title + Audio capture using \ref pw_stream "pw_stream". + title + */ + +#include <stdio.h> +#include <errno.h> +#include <math.h> +#include <signal.h> + +#include <spa/param/audio/format-utils.h> + +#include <pipewire/pipewire.h> + +struct data { + struct pw_main_loop *loop; + struct pw_stream *stream; + + struct spa_audio_info format; +}; + +/* our data processing function is in general: + * + * struct pw_buffer *b; + * b = pw_stream_dequeue_buffer(stream); + * + * .. consume stuff in the buffer ... + * + * pw_stream_queue_buffer(stream, b); + */ +static void on_process(void *userdata) +{ + struct data *data = userdata; + struct pw_buffer *b; + struct spa_buffer *buf; + float *samples, max; + uint32_t c, n, n_channels, n_samples, peak; + + if ((b = pw_stream_dequeue_buffer(data->stream)) == NULL) { + pw_log_warn("out of buffers: %m"); + return; + } + + buf = b->buffer; + if ((samples = buf->datas0.data) == NULL) + return; + + n_channels = data->format.info.raw.channels; + n_samples = buf->datas0.chunk->size / sizeof(float); + + fprintf(stdout, "captured %d samples\n", n_samples / n_channels); + for (c = 0; c < data->format.info.raw.channels; c++) { + max = 0.0f; + for (n = c; n < n_samples; n += n_channels) + max = fmaxf(max, fabsf(samplesn)); + + peak = SPA_CLAMP(max * 30, 0, 39); + + fprintf(stdout, "channel %d: |%*s%*s| peak:%f\n", + c, peak+1, "*", 40 - peak, "", max); + } + /* move cursor up */ + fprintf(stdout, "%c%dA", 0x1b, n_channels + 1); + fflush(stdout); + + pw_stream_queue_buffer(data->stream, b); +} + +/* Be notified when the stream param changes. We're only looking at the + * format changes. + */ +static void +on_stream_param_changed(void *_data, uint32_t id, const struct spa_pod *param) +{ + struct data *data = _data; + + /* NULL means to clear the format */ + if (param == NULL || id != SPA_PARAM_Format) + return; + + if (spa_format_parse(param, &data->format.media_type, &data->format.media_subtype) < 0) + return; + + /* only accept raw audio */ + if (data->format.media_type != SPA_MEDIA_TYPE_audio || + data->format.media_subtype != SPA_MEDIA_SUBTYPE_raw) + return; + + /* call a helper function to parse the format for us. */ + spa_format_audio_raw_parse(param, &data->format.info.raw); + + fprintf(stdout, "capturing rate:%d channels:%d\n", + data->format.info.raw.rate, data->format.info.raw.channels); + +} + +static const struct pw_stream_events stream_events = { + PW_VERSION_STREAM_EVENTS, + .param_changed = on_stream_param_changed, + .process = on_process, +}; + +static void do_quit(void *userdata, int signal_number) +{ + struct data *data = userdata; + pw_main_loop_quit(data->loop); +} + +int main(int argc, char *argv) +{ + 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); + + /* make a main loop. If you already have another main loop, you can add + * the fd of this pipewire mainloop to it. */ + data.loop = pw_main_loop_new(NULL); + + pw_loop_add_signal(pw_main_loop_get_loop(data.loop), SIGINT, do_quit, &data); + pw_loop_add_signal(pw_main_loop_get_loop(data.loop), SIGTERM, do_quit, &data); + + /* Create a simple stream, the simple stream manages the core and remote + * objects for you if you don't need to deal with them. + * + * If you plan to autoconnect your stream, you need to provide at least + * media, category and role properties. + * + * Pass your events and a user_data pointer as the last arguments. This + * will inform you about the stream state. The most important event + * 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, "Capture", + 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); + + /* uncomment if you want to capture from the sink monitor ports */ + /* pw_properties_set(props, PW_KEY_STREAM_CAPTURE_SINK, "true"); */ + + data.stream = pw_stream_new_simple( + pw_main_loop_get_loop(data.loop), + "audio-capture", + props, + &stream_events, + &data); + + /* Make one parameter with the supported formats. The SPA_PARAM_EnumFormat + * id means that this is a format enumeration (of 1 value). + * We leave the channels and rate empty to accept the native graph + * rate and channels. */ + params0 = spa_format_audio_raw_build(&b, SPA_PARAM_EnumFormat, + &SPA_AUDIO_INFO_RAW_INIT( + .format = SPA_AUDIO_FORMAT_F32)); + + /* Now connect this stream. We ask that our process function is + * called in a realtime thread. */ + pw_stream_connect(data.stream, + PW_DIRECTION_INPUT, + PW_ID_ANY, + PW_STREAM_FLAG_AUTOCONNECT | + PW_STREAM_FLAG_MAP_BUFFERS | + PW_STREAM_FLAG_RT_PROCESS, + params, 1); + + /* and wait while we let things run */ + pw_main_loop_run(data.loop); + + pw_stream_destroy(data.stream); + pw_main_loop_destroy(data.loop); + pw_deinit(); + + return 0; +}
View file
pipewire-0.3.58.tar.gz/src/examples/meson.build -> pipewire-0.3.59.tar.gz/src/examples/meson.build
Changed
@@ -3,6 +3,7 @@ 'audio-src', 'audio-dsp-src', 'audio-dsp-filter', + 'audio-capture', 'video-play', 'video-src', 'video-dsp-play',
View file
pipewire-0.3.58.tar.gz/src/modules/module-avb/stream.c -> pipewire-0.3.59.tar.gz/src/modules/module-avb/stream.c
Changed
@@ -136,7 +136,7 @@ p->timestamp = ptime; p->dbc = dbc; - n = sendmsg(stream->source->fd, &stream->msg, 0); + n = sendmsg(stream->source->fd, &stream->msg, MSG_NOSIGNAL); if (n < 0 || n != (ssize_t)stream->pdu_size) { pw_log_error("sendmsg() failed %zd != %zd: %m", n, stream->pdu_size);
View file
pipewire-0.3.58.tar.gz/src/modules/module-client-node/v0/ext-client-node.h -> pipewire-0.3.59.tar.gz/src/modules/module-client-node/v0/ext-client-node.h
Changed
@@ -140,9 +140,9 @@ #define PW_CLIENT_NODE0_MESSAGE_TYPE(message) (((struct pw_client_node0_message*)(message))->body.type.value) -#define PW_CLIENT_NODE0_MESSAGE_INIT(message) (struct pw_client_node0_message) \ +#define PW_CLIENT_NODE0_MESSAGE_INIT(message) ((struct pw_client_node0_message) \ { { { sizeof(struct pw_client_node0_message_body), SPA_TYPE_Struct } }, \ - { SPA_POD_INIT_Int(message) } } + { SPA_POD_INIT_Int(message) } }) #define PW_CLIENT_NODE0_MESSAGE_INIT_FULL(type,size,message,...) (type) \ { { { size, SPA_TYPE_Struct } }, \
View file
pipewire-0.3.58.tar.gz/src/modules/module-filter-chain.c -> pipewire-0.3.59.tar.gz/src/modules/module-filter-chain.c
Changed
@@ -519,13 +519,13 @@ struct graph_port { const struct fc_descriptor *desc; - void *hndl; + void **hndl; uint32_t port; }; struct graph_hndl { const struct fc_descriptor *desc; - void *hndl; + void **hndl; }; struct graph { @@ -577,6 +577,10 @@ struct graph graph; }; +static int graph_instantiate(struct graph *graph); +static void graph_cleanup(struct graph *graph); + + static void capture_destroy(void *d) { struct impl *impl = d; @@ -587,18 +591,24 @@ static void capture_process(void *d) { struct impl *impl = d; + pw_stream_trigger_process(impl->playback); +} + +static void playback_process(void *d) +{ + struct impl *impl = d; struct pw_buffer *in, *out; struct graph *graph = &impl->graph; - uint32_t i, outsize = 0, n_hndl = graph->n_hndl; + uint32_t i, insize = 0, 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"); + pw_log_debug("%p: out of capture buffers: %m", impl); if ((out = pw_stream_dequeue_buffer(impl->playback)) == NULL) - pw_log_debug("out of playback buffers: %m"); + pw_log_debug("%p: out of playback buffers: %m", impl); if (in == NULL || out == NULL) goto done; @@ -614,12 +624,14 @@ port = i < graph->n_input ? &graph->inputi : NULL; if (port && port->desc) - port->desc->connect_port(port->hndl, port->port, + port->desc->connect_port(*port->hndl, port->port, SPA_PTROFF(bd->data, offs, void)); - outsize = i == 0 ? size : SPA_MIN(outsize, size); + insize = i == 0 ? size : SPA_MIN(insize, size); stride = SPA_MAX(stride, bd->chunk->stride); } + outsize = insize; + for (i = 0; i < out->buffer->n_datas; i++) { bd = &out->buffer->datasi; @@ -628,7 +640,7 @@ port = i < graph->n_output ? &graph->outputi : NULL; if (port && port->desc) - port->desc->connect_port(port->hndl, port->port, bd->data); + port->desc->connect_port(*port->hndl, port->port, bd->data); else memset(bd->data, 0, outsize); @@ -636,9 +648,13 @@ bd->chunk->size = outsize; bd->chunk->stride = stride; } + + pw_log_trace_fp("%p: stride:%d in:%d out:%d requested:%"PRIu64" (%"PRIu64")", impl, + stride, insize, outsize, out->requested, out->requested * stride); + for (i = 0; i < n_hndl; i++) { struct graph_hndl *hndl = &graph->hndli; - hndl->desc->run(hndl->hndl, outsize / sizeof(float)); + hndl->desc->run(*hndl->hndl, outsize / sizeof(float)); } done: @@ -646,8 +662,6 @@ pw_stream_queue_buffer(impl->capture, in); if (out != NULL) pw_stream_queue_buffer(impl->playback, out); - - pw_stream_trigger_process(impl->playback); } static float get_default(struct impl *impl, struct descriptor *desc, uint32_t p) @@ -746,11 +760,12 @@ struct fc_port *p = &d->portsport->p; float def, min, max; char name512; + uint32_t rate = impl->rate ? impl->rate : 48000; if (p->hint & FC_HINT_SAMPLE_RATE) { - def = p->def * impl->rate; - min = p->min * impl->rate; - max = p->max * impl->rate; + def = p->def * rate; + min = p->min * rate; + max = p->max * rate; } else { def = p->def; min = p->min; @@ -908,13 +923,14 @@ for (i = 0; i < graph->n_hndl; i++) { struct graph_hndl *hndl = &graph->hndli; const struct fc_descriptor *d = hndl->desc; + if (hndl->hndl == NULL || *hndl->hndl == NULL) + continue; if (d->deactivate) - d->deactivate(hndl->hndl); + d->deactivate(*hndl->hndl); if (d->activate) - d->activate(hndl->hndl); + d->activate(*hndl->hndl); } } - static void param_props_changed(struct impl *impl, const struct spa_pod *param) { struct spa_pod_object *obj = (struct spa_pod_object *) param; @@ -989,8 +1005,15 @@ switch (id) { case SPA_PARAM_Format: - if (param == NULL) - graph_reset(graph); + if (param == NULL) { + graph_cleanup(graph); + } else { + struct spa_audio_info_raw info; + spa_zero(info); + spa_format_audio_raw_parse(param, &info); + impl->rate = info.rate; + graph_instantiate(graph); + } break; case SPA_PARAM_Props: if (param != NULL) @@ -1020,6 +1043,7 @@ static const struct pw_stream_events out_stream_events = { PW_VERSION_STREAM_EVENTS, .destroy = playback_destroy, + .process = playback_process, .state_changed = state_changed, .param_changed = param_changed }; @@ -1477,6 +1501,7 @@ bool have_config = false; uint32_t i; int res; + float *data; while (spa_json_get_string(json, key, sizeof(key)) > 0) { if (spa_streq("type", key)) { @@ -1551,6 +1576,14 @@ port->idx = i; port->external = SPA_ID_INVALID; port->p = desc->outputi; + if ((data = port->audio_datai) == NULL) { + data = calloc(1, MAX_SAMPLES * sizeof(float)); + if (data == NULL) { + pw_log_error("cannot create port data: %m"); + return -errno; + } + } + port->audio_datai = data; spa_list_init(&port->link_list); } for (i = 0; i < desc->n_control; i++) { @@ -1581,21 +1614,31 @@ return 0; } -static void node_free(struct node *node) +static void node_cleanup(struct node *node) { - uint32_t i, j; const struct fc_descriptor *d = node->desc->desc; + uint32_t i; - spa_list_remove(&node->link); for (i = 0; i < node->n_hndl; i++) { - for (j = 0; j < node->desc->n_output; j++) - free(node->output_portj.audio_datai); if (node->hndli == NULL) continue; if (d->deactivate) d->deactivate(node->hndli); d->cleanup(node->hndli); + node->hndli = NULL; + } +} + +static void node_free(struct node *node) +{ + uint32_t i, j; + + spa_list_remove(&node->link); + for (i = 0; i < node->n_hndl; i++) { + for (j = 0; j < node->desc->n_output; j++) + free(node->output_portj.audio_datai); } + node_cleanup(node); descriptor_unref(node->desc); free(node->input_port); free(node->output_port); @@ -1604,61 +1647,88 @@ free(node); } -static struct node *find_next_node(struct graph *graph) +static void graph_cleanup(struct graph *graph) { struct node *node; - spa_list_for_each(node, &graph->node_list, link) { - if (node->n_deps == 0 && !node->visited) { - node->visited = true; - return node; - } - } - return NULL; + spa_list_for_each(node, &graph->node_list, link) + node_cleanup(node); } -static int setup_input_port(struct graph *graph, struct port *port) +static int graph_instantiate(struct graph *graph) { - struct descriptor *desc = port->node->desc; - const struct fc_descriptor *d = desc->desc; + struct impl *impl = graph->impl; + struct node *node; + struct port *port; struct link *link; - uint32_t i, n_hndl = port->node->n_hndl; + struct descriptor *desc; + const struct fc_descriptor *d; + uint32_t i, j; + int res; - spa_list_for_each(link, &port->link_list, input_link) { - struct port *peer = link->output; - for (i = 0; i < n_hndl; i++) { - pw_log_info("connect input port %s%d:%s %p", - port->node->name, i, d->portsport->p.name, - peer->audio_datai); - d->connect_port(port->node->hndli, port->p, peer->audio_datai); + spa_list_for_each(node, &graph->node_list, link) { + float *sd = silence_data, *dd = discard_data; + + node_cleanup(node); + + desc = node->desc; + d = desc->desc; + if (d->flags & FC_DESCRIPTOR_SUPPORTS_NULL_DATA) + sd = dd = NULL; + + for (i = 0; i < node->n_hndl; i++) { + pw_log_info("instantiate %s %d rate:%lu", d->name, i, impl->rate); + if ((node->hndli = d->instantiate(d, impl->rate, i, node->config)) == NULL) { + pw_log_error("cannot create plugin instance: %m"); + res = -errno; + goto error; + } + for (j = 0; j < desc->n_input; j++) { + port = &node->input_portj; + d->connect_port(node->hndli, port->p, sd); + + spa_list_for_each(link, &port->link_list, input_link) { + struct port *peer = link->output; + pw_log_info("connect input port %s%d:%s %p", + node->name, i, d->portsport->p.name, + peer->audio_datai); + d->connect_port(node->hndli, port->p, peer->audio_datai); + } + } + for (j = 0; j < desc->n_output; j++) { + port = &node->output_portj; + pw_log_info("connect output port %s%d:%s %p", + node->name, i, d->portsport->p.name, + port->audio_datai); + d->connect_port(node->hndli, port->p, port->audio_datai); + } + for (j = 0; j < desc->n_control; j++) { + port = &node->control_portj; + d->connect_port(node->hndli, port->p, &port->control_data); + } + for (j = 0; j < desc->n_notify; j++) { + port = &node->notify_portj; + d->connect_port(node->hndli, port->p, &port->control_data); + } + if (d->activate) + d->activate(node->hndli); } } return 0; +error: + graph_cleanup(graph); + return res; } -static int setup_output_port(struct graph *graph, struct port *port) +static struct node *find_next_node(struct graph *graph) { - struct descriptor *desc = port->node->desc; - const struct fc_descriptor *d = desc->desc; - struct link *link; - uint32_t i, n_hndl = port->node->n_hndl; - - spa_list_for_each(link, &port->link_list, output_link) { - for (i = 0; i < n_hndl; i++) { - float *data; - if ((data = port->audio_datai) == NULL) { - data = calloc(1, MAX_SAMPLES * sizeof(float)); - if (data == NULL) - return -errno; - } - port->audio_datai = data; - pw_log_info("connect output port %s%d:%s %p", - port->node->name, i, d->portsport->p.name, - port->audio_datai); - d->connect_port(port->node->hndli, port->p, data); + struct node *node; + spa_list_for_each(node, &graph->node_list, link) { + if (node->n_deps == 0 && !node->visited) { + node->visited = true; + return node; } - link->input->node->n_deps--; } - return 0; + return NULL; } static int setup_graph(struct graph *graph, struct spa_json *inputs, struct spa_json *outputs) @@ -1666,11 +1736,11 @@ struct impl *impl = graph->impl; struct node *node, *first, *last; struct port *port; + struct link *link; struct graph_port *gp; struct graph_hndl *gh; uint32_t i, j, n_nodes, n_input, n_output, n_control, n_hndl = 0; int res; - unsigned long p; struct descriptor *desc; const struct fc_descriptor *d; char v256; @@ -1741,52 +1811,11 @@ n_control = 0; n_nodes = 0; spa_list_for_each(node, &graph->node_list, link) { - float *sd = silence_data, *dd = discard_data; - + node->n_hndl = n_hndl; desc = node->desc; - d = desc->desc; - if (d->flags & FC_DESCRIPTOR_SUPPORTS_NULL_DATA) - sd = dd = NULL; - - for (i = 0; i < n_hndl; i++) { - pw_log_info("instantiate %s %d", d->name, i); - if ((node->hndli = d->instantiate(d, &impl->rate, i, node->config)) == NULL) { - pw_log_error("cannot create plugin instance: %m"); - res = -errno; - goto error; - } - node->n_hndl = i + 1; - - for (j = 0; j < desc->n_input; j++) { - p = desc->inputj; - d->connect_port(node->hndli, p, sd); - } - for (j = 0; j < desc->n_output; j++) { - p = desc->outputj; - d->connect_port(node->hndli, p, dd); - } - for (j = 0; j < desc->n_control; j++) { - port = &node->control_portj; - d->connect_port(node->hndli, port->p, &port->control_data); - } - for (j = 0; j < desc->n_notify; j++) { - port = &node->notify_portj; - d->connect_port(node->hndli, port->p, &port->control_data); - } - if (d->activate) - d->activate(node->hndli); - } n_control += desc->n_control; n_nodes++; } - pw_log_info("suggested rate:%lu capture:%d playback:%d", impl->rate, - impl->capture_info.rate, impl->playback_info.rate); - - if (impl->capture_info.rate == 0) - impl->capture_info.rate = impl->rate; - if (impl->playback_info.rate == 0) - impl->playback_info.rate = impl->rate; - graph->n_input = 0; graph->input = calloc(n_input * n_hndl, sizeof(struct graph_port)); graph->n_output = 0; @@ -1802,7 +1831,7 @@ pw_log_info("input port %s%d:%s", first->name, i, d->portsdesc->inputj.name); gp->desc = d; - gp->hndl = first->hndli; + gp->hndl = &first->hndli; gp->port = desc->inputj; } } else { @@ -1836,7 +1865,7 @@ port->node->name, i, d->portsport->p.name); port->external = graph->n_input; gp->desc = d; - gp->hndl = port->node->hndli; + gp->hndl = &port->node->hndli; gp->port = port->p; } graph->n_input++; @@ -1850,7 +1879,7 @@ pw_log_info("output port %s%d:%s", last->name, i, d->portsdesc->outputj.name); gp->desc = d; - gp->hndl = last->hndli; + gp->hndl = &last->hndli; gp->port = desc->outputj; } } else { @@ -1884,7 +1913,7 @@ port->node->name, i, d->portsport->p.name); port->external = graph->n_output; gp->desc = d; - gp->hndl = port->node->hndli; + gp->hndl = &port->node->hndli; gp->port = port->p; } graph->n_output++; @@ -1904,17 +1933,14 @@ desc = node->desc; d = desc->desc; - for (i = 0; i < desc->n_input; i++) - setup_input_port(graph, &node->input_porti); - for (i = 0; i < n_hndl; i++) { gh = &graph->hndlgraph->n_hndl++; - gh->hndl = node->hndli; + gh->hndl = &node->hndli; gh->desc = d; - } - for (i = 0; i < desc->n_output; i++) - setup_output_port(graph, &node->output_porti); + spa_list_for_each(link, &node->output_porti.link_list, output_link) + link->input->node->n_deps--; + } /* collect all control ports on the graph */ for (i = 0; i < desc->n_control; i++) { @@ -1922,17 +1948,8 @@ graph->n_control++; } } - return 0; - + res = 0; error: - spa_list_for_each(node, &graph->node_list, link) { - for (i = 0; i < node->n_hndl; i++) { - if (node->hndli != NULL) - node->desc->desc->cleanup(node->hndli); - node->hndli = NULL; - } - node->n_hndl = 0; - } return res; } @@ -2063,12 +2080,20 @@ static void impl_destroy(struct impl *impl) { + /* disconnect both streams before destroying any of them */ + if (impl->capture) + pw_stream_disconnect(impl->capture); + if (impl->playback) + pw_stream_disconnect(impl->playback); + if (impl->capture) pw_stream_destroy(impl->capture); if (impl->playback) pw_stream_destroy(impl->playback); + if (impl->core && impl->do_disconnect) pw_core_disconnect(impl->core); + pw_properties_free(impl->capture_props); pw_properties_free(impl->playback_props); graph_free(&impl->graph); @@ -2178,7 +2203,6 @@ impl->module = module; impl->context = context; - impl->rate = 48000; impl->graph.impl = impl; spa_list_init(&impl->plugin_list); @@ -2188,6 +2212,8 @@ pw_properties_setf(props, PW_KEY_NODE_LINK_GROUP, "filter-chain-%u-%u", pid, id); if (pw_properties_get(props, PW_KEY_NODE_VIRTUAL) == NULL) pw_properties_set(props, PW_KEY_NODE_VIRTUAL, "true"); + if (pw_properties_get(props, "resample.prefill") == NULL) + pw_properties_set(props, "resample.prefill", "true"); if (pw_properties_get(props, PW_KEY_NODE_DESCRIPTION) == NULL) pw_properties_setf(props, PW_KEY_NODE_DESCRIPTION, "filter-chain-%u-%u", pid, id); @@ -2205,6 +2231,7 @@ copy_props(impl, props, PW_KEY_NODE_LATENCY); copy_props(impl, props, PW_KEY_NODE_VIRTUAL); copy_props(impl, props, PW_KEY_MEDIA_NAME); + copy_props(impl, props, "resample.prefill"); parse_audio_info(impl->capture_props, &impl->capture_info); parse_audio_info(impl->playback_props, &impl->playback_info);
View file
pipewire-0.3.58.tar.gz/src/modules/module-filter-chain/builtin_plugin.c -> pipewire-0.3.59.tar.gz/src/modules/module-filter-chain/builtin_plugin.c
Changed
@@ -52,7 +52,7 @@ }; static void *builtin_instantiate(const struct fc_descriptor * Descriptor, - unsigned long *SampleRate, int index, const char *config) + unsigned long SampleRate, int index, const char *config) { struct builtin *impl; @@ -60,7 +60,7 @@ if (impl == NULL) return NULL; - impl->rate = *SampleRate; + impl->rate = SampleRate; return impl; } @@ -576,7 +576,7 @@ } static void * convolver_instantiate(const struct fc_descriptor * Descriptor, - unsigned long *SampleRate, int index, const char *config) + unsigned long SampleRate, int index, const char *config) { struct convolver_impl *impl; float *samples; @@ -588,6 +588,7 @@ int blocksize = 0, tailsize = 0; int delay = 0; float gain = 1.0f; + unsigned long rate; if (config == NULL) return NULL; @@ -647,8 +648,13 @@ samples = create_dirac(filename, gain, delay, offset, length, &n_samples); } else { + rate = SampleRate; samples = read_samples(filename, gain, delay, offset, - length, channel, SampleRate, &n_samples); + length, channel, &rate, &n_samples); + if (rate != SampleRate) { + pw_log_warn("Convolver samplerate %lu doesn't match filter rate %lu. " + "Consider forcing a filter rate.", rate, SampleRate); + } } if (samples == NULL) return NULL; @@ -664,7 +670,7 @@ if (impl == NULL) goto error; - impl->rate = *SampleRate; + impl->rate = SampleRate; impl->conv = convolver_new(blocksize, tailsize, samples, n_samples); if (impl->conv == NULL) @@ -750,7 +756,7 @@ } static void *delay_instantiate(const struct fc_descriptor * Descriptor, - unsigned long *SampleRate, int index, const char *config) + unsigned long SampleRate, int index, const char *config) { struct delay_impl *impl; struct spa_json it2; @@ -782,7 +788,7 @@ if (impl == NULL) return NULL; - impl->rate = *SampleRate; + impl->rate = SampleRate; impl->buffer_samples = max_delay * impl->rate; pw_log_info("%lu %d", impl->rate, impl->buffer_samples);
View file
pipewire-0.3.58.tar.gz/src/modules/module-filter-chain/ladspa_plugin.c -> pipewire-0.3.59.tar.gz/src/modules/module-filter-chain/ladspa_plugin.c
Changed
@@ -47,10 +47,10 @@ }; static void *ladspa_instantiate(const struct fc_descriptor *desc, - unsigned long *SampleRate, int index, const char *config) + unsigned long SampleRate, int index, const char *config) { struct descriptor *d = (struct descriptor *)desc; - return d->d->instantiate(d->d, *SampleRate); + return d->d->instantiate(d->d, SampleRate); } static const LADSPA_Descriptor *find_desc(LADSPA_Descriptor_Function desc_func, const char *name)
View file
pipewire-0.3.58.tar.gz/src/modules/module-filter-chain/lv2_plugin.c -> pipewire-0.3.59.tar.gz/src/modules/module-filter-chain/lv2_plugin.c
Changed
@@ -298,7 +298,7 @@ } static void *lv2_instantiate(const struct fc_descriptor *desc, - unsigned long *SampleRate, int index, const char *config) + unsigned long SampleRate, int index, const char *config) { struct descriptor *d = (struct descriptor*)desc; struct plugin *p = d->p; @@ -308,7 +308,7 @@ static const int32_t min_block_length = 1; static const int32_t max_block_length = 8192; static const int32_t seq_size = 32768; - float fsample_rate = *SampleRate; + float fsample_rate = SampleRate; i = calloc(1, sizeof(*i)); if (i == NULL) @@ -350,7 +350,7 @@ i->options_feature.data = i->options; i->featuresn_features++ = &i->options_feature; - i->instance = lilv_plugin_instantiate(p->p, *SampleRate, i->features); + i->instance = lilv_plugin_instantiate(p->p, SampleRate, i->features); if (i->instance == NULL) { free(i); return NULL;
View file
pipewire-0.3.58.tar.gz/src/modules/module-filter-chain/plugin.h -> pipewire-0.3.59.tar.gz/src/modules/module-filter-chain/plugin.h
Changed
@@ -72,7 +72,7 @@ struct fc_port *ports; void *(*instantiate) (const struct fc_descriptor *desc, - unsigned long *SampleRate, int index, const char *config); + unsigned long SampleRate, int index, const char *config); void (*cleanup) (void *instance);
View file
pipewire-0.3.58.tar.gz/src/modules/module-loopback.c -> pipewire-0.3.59.tar.gz/src/modules/module-loopback.c
Changed
@@ -179,6 +179,12 @@ static void capture_process(void *d) { struct impl *impl = d; + pw_stream_trigger_process(impl->playback); +} + +static void playback_process(void *d) +{ + struct impl *impl = d; struct pw_buffer *in, *out; uint32_t i; @@ -225,8 +231,6 @@ pw_stream_queue_buffer(impl->capture, in); if (out != NULL) pw_stream_queue_buffer(impl->playback, out); - - pw_stream_trigger_process(impl->playback); } static void param_latency_changed(struct impl *impl, const struct spa_pod *param, @@ -305,6 +309,7 @@ static const struct pw_stream_events out_stream_events = { PW_VERSION_STREAM_EVENTS, .destroy = playback_destroy, + .process = playback_process, .state_changed = stream_state_changed, .param_changed = playback_param_changed, }; @@ -399,12 +404,20 @@ static void impl_destroy(struct impl *impl) { + /* disconnect both streams before destroying any of them */ + if (impl->capture) + pw_stream_disconnect(impl->capture); + if (impl->playback) + pw_stream_disconnect(impl->playback); + if (impl->capture) pw_stream_destroy(impl->capture); if (impl->playback) pw_stream_destroy(impl->playback); + if (impl->core && impl->do_disconnect) pw_core_disconnect(impl->core); + pw_properties_free(impl->capture_props); pw_properties_free(impl->playback_props); free(impl); @@ -518,6 +531,8 @@ pw_properties_setf(props, PW_KEY_NODE_LINK_GROUP, "loopback-%u-%u", pid, id); if (pw_properties_get(props, PW_KEY_NODE_VIRTUAL) == NULL) pw_properties_set(props, PW_KEY_NODE_VIRTUAL, "true"); + if (pw_properties_get(props, "resample.prefill") == NULL) + pw_properties_set(props, "resample.prefill", "true"); if ((str = pw_properties_get(props, "capture.props")) != NULL) pw_properties_update_string(impl->capture_props, str, strlen(str)); @@ -533,6 +548,7 @@ copy_props(impl, props, PW_KEY_NODE_LATENCY); copy_props(impl, props, PW_KEY_NODE_VIRTUAL); copy_props(impl, props, PW_KEY_MEDIA_NAME); + copy_props(impl, props, "resample.prefill"); if ((str = pw_properties_get(props, PW_KEY_NODE_NAME)) == NULL) { pw_properties_setf(props, PW_KEY_NODE_NAME,
View file
pipewire-0.3.58.tar.gz/src/modules/module-protocol-native/connection.c -> pipewire-0.3.59.tar.gz/src/modules/module-protocol-native/connection.c
Changed
@@ -221,7 +221,10 @@ struct cmsghdr *cmsg = NULL; struct msghdr msg = { 0 }; struct iovec iov1; - char cmsgbufCMSG_SPACE(MAX_FDS_MSG * sizeof(int)); + union { + char cmsgbufCMSG_SPACE(MAX_FDS_MSG * sizeof(int)); + struct cmsghdr align; + } cmsgbuf; int n_fds = 0; size_t avail; @@ -231,7 +234,7 @@ iov0.iov_len = avail; msg.msg_iov = iov; msg.msg_iovlen = 1; - msg.msg_control = cmsgbuf; + msg.msg_control = &cmsgbuf; msg.msg_controllen = sizeof(cmsgbuf); msg.msg_flags = MSG_CMSG_CLOEXEC | MSG_DONTWAIT; @@ -755,7 +758,10 @@ struct msghdr msg = { 0 }; struct iovec iov1; struct cmsghdr *cmsg; - char cmsgbufCMSG_SPACE(MAX_FDS_MSG * sizeof(int)); + union { + char cmsgbufCMSG_SPACE(MAX_FDS_MSG * sizeof(int)); + struct cmsghdr align; + } cmsgbuf; int res = 0, *fds; uint32_t fds_len, to_close, n_fds, outfds, i; struct buffer *buf; @@ -786,7 +792,7 @@ msg.msg_iovlen = 1; if (outfds > 0) { - msg.msg_control = cmsgbuf; + msg.msg_control = &cmsgbuf; msg.msg_controllen = CMSG_SPACE(fds_len); cmsg = CMSG_FIRSTHDR(&msg); cmsg->cmsg_level = SOL_SOCKET;
View file
pipewire-0.3.58.tar.gz/src/modules/module-protocol-native/defs.h -> pipewire-0.3.59.tar.gz/src/modules/module-protocol-native/defs.h
Changed
@@ -31,13 +31,19 @@ void (*done_callback) (void *data, int res), void *data); -static inline void *get_first_pod_from_data(void *data, size_t maxsize, off_t offset) +static inline void *get_first_pod_from_data(void *data, uint32_t maxsize, uint64_t offset) { void *pod; - if (offset + sizeof(struct spa_pod) > maxsize) + if (maxsize <= offset) return NULL; + + /* spa_pod_parser_advance() rounds up, so round down here to compensate */ + maxsize = SPA_ROUND_DOWN_N(maxsize - offset, 8); + if (maxsize < sizeof(struct spa_pod)) + return NULL; + pod = SPA_PTROFF(data, offset, void); - if (offset + SPA_POD_SIZE(pod) > maxsize) + if (SPA_POD_BODY_SIZE(pod) > maxsize - sizeof(struct spa_pod)) return NULL; return pod; }
View file
pipewire-0.3.58.tar.gz/src/modules/module-protocol-native/protocol-footer.c -> pipewire-0.3.59.tar.gz/src/modules/module-protocol-native/protocol-footer.c
Changed
@@ -45,7 +45,7 @@ unsigned int started:1; }; -#define FOOTER_BUILDER_INIT(builder) (struct footer_builder) { builder } +#define FOOTER_BUILDER_INIT(builder) ((struct footer_builder) { (builder) }) static void start_footer_entry(struct footer_builder *fb, uint32_t opcode) {
View file
pipewire-0.3.58.tar.gz/src/modules/module-protocol-native/protocol-native.c -> pipewire-0.3.59.tar.gz/src/modules/module-protocol-native/protocol-native.c
Changed
@@ -219,13 +219,13 @@ spa_pod_parser_get(prs, \ SPA_POD_Int(&(n_params)), NULL) < 0) \ return -EINVAL; \ - params = NULL; \ - if (n_params > 0) { \ + (params) = NULL; \ + if ((n_params) > 0) { \ uint32_t i; \ - if (n_params > MAX_PARAM_INFO) \ + if ((n_params) > MAX_PARAM_INFO) \ return -ENOSPC; \ - params = alloca(n_params * sizeof(struct spa_param_info)); \ - for (i = 0; i < n_params; i++) { \ + (params) = alloca((n_params) * sizeof(struct spa_param_info)); \ + for (i = 0; i < (n_params); i++) { \ if (spa_pod_parser_get(prs, \ SPA_POD_Id(&(params)i.id), \ SPA_POD_Int(&(params)i.flags), NULL) < 0) \ @@ -240,18 +240,18 @@ do { \ if (spa_pod_parser_push_struct(prs, f) < 0 || \ spa_pod_parser_get(prs, \ - SPA_POD_Int(&n_permissions), NULL) < 0) \ + SPA_POD_Int(&(n_permissions)), NULL) < 0) \ return -EINVAL; \ - permissions = NULL; \ - if (n_permissions > 0) { \ + (permissions) = NULL; \ + if ((n_permissions) > 0) { \ uint32_t i; \ - if (n_permissions > MAX_PERMISSIONS) \ + if ((n_permissions) > MAX_PERMISSIONS) \ return -ENOSPC; \ - permissions = alloca(n_permissions * sizeof(struct pw_permission)); \ - for (i = 0; i < n_permissions; i++) { \ + (permissions) = alloca((n_permissions) * sizeof(struct pw_permission)); \ + for (i = 0; i < (n_permissions); i++) { \ if (spa_pod_parser_get(prs, \ - SPA_POD_Int(&permissionsi.id), \ - SPA_POD_Int(&permissionsi.permissions), NULL) < 0) \ + SPA_POD_Int(&(permissions)i.id), \ + SPA_POD_Int(&(permissions)i.permissions), NULL) < 0) \ return -EINVAL; \ } \ } \
View file
pipewire-0.3.58.tar.gz/src/modules/module-protocol-native/v0/protocol-native.c -> pipewire-0.3.59.tar.gz/src/modules/module-protocol-native/v0/protocol-native.c
Changed
@@ -415,7 +415,7 @@ (iter) <= SPA_PTROFF((body), (_size)-(body)->value.size, __typeof__(*iter)); \ (iter) = SPA_PTROFF((iter), (body)->value.size, __typeof__(*iter))) -#define SPA0_POD_PROP_N_VALUES(b,size) ((size - sizeof(struct spa_pod_prop_body0)) / (b)->value.size) +#define SPA0_POD_PROP_N_VALUES(b,size) (((size) - sizeof(struct spa_pod_prop_body0)) / (b)->value.size) static int remap_from_v2(uint32_t type, void *body, uint32_t size, struct pw_impl_client *client, struct spa_pod_builder *builder)
View file
pipewire-0.3.58.tar.gz/src/modules/module-protocol-pulse/manager.c -> pipewire-0.3.59.tar.gz/src/modules/module-protocol-pulse/manager.c
Changed
@@ -35,13 +35,13 @@ #define MAX_PARAMS 32 -#define manager_emit_sync(m) spa_hook_list_call(&m->hooks, struct pw_manager_events, sync, 0) -#define manager_emit_added(m,o) spa_hook_list_call(&m->hooks, struct pw_manager_events, added, 0, o) -#define manager_emit_updated(m,o) spa_hook_list_call(&m->hooks, struct pw_manager_events, updated, 0, o) -#define manager_emit_removed(m,o) spa_hook_list_call(&m->hooks, struct pw_manager_events, removed, 0, o) -#define manager_emit_metadata(m,o,s,k,t,v) spa_hook_list_call(&m->hooks, struct pw_manager_events, metadata,0,o,s,k,t,v) -#define manager_emit_disconnect(m) spa_hook_list_call(&m->hooks, struct pw_manager_events, disconnect, 0) -#define manager_emit_object_data_timeout(m,o,k) spa_hook_list_call(&m->hooks, struct pw_manager_events, object_data_timeout,0,o,k) +#define manager_emit_sync(m) spa_hook_list_call(&(m)->hooks, struct pw_manager_events, sync, 0) +#define manager_emit_added(m,o) spa_hook_list_call(&(m)->hooks, struct pw_manager_events, added, 0, o) +#define manager_emit_updated(m,o) spa_hook_list_call(&(m)->hooks, struct pw_manager_events, updated, 0, o) +#define manager_emit_removed(m,o) spa_hook_list_call(&(m)->hooks, struct pw_manager_events, removed, 0, o) +#define manager_emit_metadata(m,o,s,k,t,v) spa_hook_list_call(&(m)->hooks, struct pw_manager_events, metadata,0,o,s,k,t,v) +#define manager_emit_disconnect(m) spa_hook_list_call(&(m)->hooks, struct pw_manager_events, disconnect, 0) +#define manager_emit_object_data_timeout(m,o,k) spa_hook_list_call(&(m)->hooks, struct pw_manager_events, object_data_timeout,0,o,k) struct object;
View file
pipewire-0.3.58.tar.gz/src/modules/module-protocol-pulse/pulse-server.c -> pipewire-0.3.59.tar.gz/src/modules/module-protocol-pulse/pulse-server.c
Changed
@@ -89,7 +89,7 @@ #define MAX_FORMATS 32 /* The max amount of data we send in one block when capturing. In PulseAudio this * size is derived from the mempool PA_MEMPOOL_SLOT_SIZE */ -#define MAX_FRAGSIZE (64*1024) +#define MAX_BLOCK (64*1024) #define TEMPORARY_MOVE_TIMEOUT (SPA_NSEC_PER_SEC) @@ -455,7 +455,7 @@ static uint64_t fix_playback_buffer_attr(struct stream *s, struct buffer_attr *attr, uint32_t rate, struct spa_fraction *lat) { - uint32_t frame_size, max_prebuf, minreq, latency, max_latency; + uint32_t frame_size, max_prebuf, minreq, latency, max_latency, maxlength; struct defs *defs = &s->impl->defs; if ((frame_size = s->frame_size) == 0) @@ -463,24 +463,26 @@ if (frame_size == 0) frame_size = 4; - pw_log_info("%s maxlength:%u tlength:%u minreq:%u prebuf:%u", + maxlength = SPA_ROUND_DOWN(MAXLENGTH, frame_size); + + pw_log_info("%s maxlength:%u tlength:%u minreq:%u prebuf:%u max:%u", s->client->name, attr->maxlength, attr->tlength, - attr->minreq, attr->prebuf); + attr->minreq, attr->prebuf, maxlength); minreq = frac_to_bytes_round_up(s->min_req, &s->ss); max_latency = defs->quantum_limit * frame_size; - if (attr->maxlength == (uint32_t) -1 || attr->maxlength > MAXLENGTH) - attr->maxlength = MAXLENGTH; - attr->maxlength = SPA_ROUND_UP(attr->maxlength, frame_size); + if (attr->maxlength == (uint32_t) -1 || attr->maxlength > maxlength) + attr->maxlength = maxlength; + else + attr->maxlength = SPA_ROUND_DOWN(attr->maxlength, frame_size); minreq = SPA_MIN(minreq, attr->maxlength); if (attr->tlength == (uint32_t) -1) attr->tlength = frac_to_bytes_round_up(s->default_tlength, &s->ss); - attr->tlength = SPA_MIN(attr->tlength, attr->maxlength); + attr->tlength = SPA_CLAMP(attr->tlength, minreq, attr->maxlength); attr->tlength = SPA_ROUND_UP(attr->tlength, frame_size); - attr->tlength = SPA_MAX(attr->tlength, minreq); if (attr->minreq == (uint32_t) -1) { uint32_t process = frac_to_bytes_round_up(s->default_req, &s->ss); @@ -655,39 +657,46 @@ static uint64_t fix_record_buffer_attr(struct stream *s, struct buffer_attr *attr, uint32_t rate, struct spa_fraction *lat) { - uint32_t frame_size, minfrag, latency; + uint32_t frame_size, minfrag, latency, maxlength; if ((frame_size = s->frame_size) == 0) frame_size = sample_spec_frame_size(&s->ss); if (frame_size == 0) frame_size = 4; + maxlength = SPA_ROUND_DOWN(MAXLENGTH, frame_size); + pw_log_info("%s maxlength:%u fragsize:%u framesize:%u", s->client->name, attr->maxlength, attr->fragsize, frame_size); - if (attr->maxlength == (uint32_t) -1 || attr->maxlength > MAXLENGTH) - attr->maxlength = MAXLENGTH; - attr->maxlength -= attr->maxlength % frame_size; + if (attr->maxlength == (uint32_t) -1 || attr->maxlength > maxlength) + attr->maxlength = maxlength; + else + attr->maxlength = SPA_ROUND_DOWN(attr->maxlength, frame_size); attr->maxlength = SPA_MAX(attr->maxlength, frame_size); minfrag = frac_to_bytes_round_up(s->min_frag, &s->ss); if (attr->fragsize == (uint32_t) -1 || attr->fragsize == 0) attr->fragsize = frac_to_bytes_round_up(s->default_frag, &s->ss); - attr->fragsize = SPA_MIN(attr->fragsize, attr->maxlength); + attr->fragsize = SPA_CLAMP(attr->fragsize, minfrag, attr->maxlength); attr->fragsize = SPA_ROUND_UP(attr->fragsize, frame_size); - attr->fragsize = SPA_MAX(attr->fragsize, minfrag); attr->tlength = attr->minreq = attr->prebuf = 0; - /* make sure can queue at least to fragsize without overruns */ - if (attr->maxlength < attr->fragsize * 4) + /* make sure we can queue at least to fragsize without overruns */ + if (attr->maxlength < attr->fragsize * 4) { attr->maxlength = attr->fragsize * 4; + if (attr->maxlength > maxlength) { + attr->maxlength = maxlength; + attr->fragsize = SPA_ROUND_DOWN(maxlength / 4, frame_size); + } + } - latency = attr->fragsize / frame_size; + latency = attr->fragsize; - lat->num = latency; + lat->num = latency / frame_size; lat->denom = rate; clamp_latency(s, lat); @@ -1341,7 +1350,8 @@ pw_log_trace("avail:%d index:%u", avail, index); while ((uint32_t)avail >= stream->attr.fragsize) { - towrite = SPA_MIN(avail, MAX_FRAGSIZE); + towrite = SPA_MIN(avail, MAX_BLOCK); + towrite = SPA_MIN(towrite, stream->attr.fragsize); towrite = SPA_ROUND_DOWN(towrite, stream->frame_size); msg = message_alloc(impl, stream->channel, towrite); @@ -1421,7 +1431,7 @@ spa_ringbuffer_read_data(&stream->ring, stream->buffer, MAXLENGTH, index % MAXLENGTH, - p, avail); + p, SPA_MIN((uint32_t)avail, size)); index += avail; } pd.playing_for = size;
View file
pipewire-0.3.58.tar.gz/src/modules/module-protocol-pulse/utils.c -> pipewire-0.3.59.tar.gz/src/modules/module-protocol-pulse/utils.c
Changed
@@ -195,7 +195,7 @@ strcat(pid_file, "/pid"); - if ((f = fopen(pid_file, "w")) == NULL) { + if ((f = fopen(pid_file, "we")) == NULL) { res = -errno; pw_log_error("failed to open pid file: %m"); return res;
View file
pipewire-0.3.58.tar.gz/src/modules/module-raop-discover.c -> pipewire-0.3.59.tar.gz/src/modules/module-raop-discover.c
Changed
@@ -113,7 +113,7 @@ const char *domain; }; -#define TUNNEL_INFO(...) (struct tunnel_info){ __VA_ARGS__ } +#define TUNNEL_INFO(...) ((struct tunnel_info){ __VA_ARGS__ }) struct tunnel { struct spa_list link;
View file
pipewire-0.3.58.tar.gz/src/modules/module-raop-sink.c -> pipewire-0.3.59.tar.gz/src/modules/module-raop-sink.c
Changed
@@ -421,6 +421,7 @@ switch (impl->codec) { case CODEC_PCM: + case CODEC_ALAC: len = write_codec_pcm(dst, impl->buffer, n_frames); break; default: @@ -463,6 +464,7 @@ switch (impl->codec) { case CODEC_PCM: + case CODEC_ALAC: len = write_codec_pcm(dst, impl->buffer, n_frames); break; default: @@ -1756,6 +1758,8 @@ str = "PCM"; if (spa_streq(str, "PCM")) impl->codec = CODEC_PCM; + else if (spa_streq(str, "ALAC")) + impl->codec = CODEC_ALAC; else { pw_log_error( "can't handle codec type %s", str); res = -EINVAL;
View file
pipewire-0.3.58.tar.gz/src/modules/module-raop/rtsp-client.c -> pipewire-0.3.59.tar.gz/src/modules/module-raop/rtsp-client.c
Changed
@@ -314,8 +314,8 @@ return -EPROTO; *value++ = '\0'; - while (*value == ' ') - value++; + + value = pw_strip(value, " "); pw_properties_set(client->headers, key, value); }
View file
pipewire-0.3.58.tar.gz/src/modules/module-session-manager/client-session/endpoint-link.h -> pipewire-0.3.59.tar.gz/src/modules/module-session-manager/client-session/endpoint-link.h
Changed
@@ -25,6 +25,7 @@ #ifndef MODULE_SESSION_MANAGER_ENDPOINT_LINK_H #define MODULE_SESSION_MANAGER_ENDPOINT_LINK_H +#include <stdint.h> #ifdef __cplusplus extern "C" {
View file
pipewire-0.3.58.tar.gz/src/modules/module-session-manager/client-session/session.h -> pipewire-0.3.59.tar.gz/src/modules/module-session-manager/client-session/session.h
Changed
@@ -25,6 +25,7 @@ #ifndef MODULE_SESSION_MANAGER_SESSION_H #define MODULE_SESSION_MANAGER_SESSION_H +#include <stdint.h> #ifdef __cplusplus extern "C" {
View file
pipewire-0.3.58.tar.gz/src/modules/module-zeroconf-discover.c -> pipewire-0.3.59.tar.gz/src/modules/module-zeroconf-discover.c
Changed
@@ -109,7 +109,7 @@ const char *domain; }; -#define TUNNEL_INFO(...) (struct tunnel_info){ __VA_ARGS__ } +#define TUNNEL_INFO(...) ((struct tunnel_info){ __VA_ARGS__ }) struct tunnel { struct spa_list link;
View file
pipewire-0.3.58.tar.gz/src/pipewire/array.h -> pipewire-0.3.59.tar.gz/src/pipewire/array.h
Changed
@@ -52,7 +52,7 @@ size_t extend; /**< number of bytes to extend with */ }; -#define PW_ARRAY_INIT(extend) (struct pw_array) { NULL, 0, 0, extend } +#define PW_ARRAY_INIT(extend) ((struct pw_array) { NULL, 0, 0, (extend) }) #define pw_array_get_len_s(a,s) ((a)->size / (s)) #define pw_array_get_unchecked_s(a,idx,s,t) SPA_PTROFF((a)->data,(idx)*(s),t) @@ -67,17 +67,17 @@ #define pw_array_first(a) ((a)->data) #define pw_array_end(a) SPA_PTROFF((a)->data, (a)->size, void) -#define pw_array_check(a,p) (SPA_PTROFF(p,sizeof(*p),void) <= pw_array_end(a)) +#define pw_array_check(a,p) (SPA_PTROFF(p,sizeof(*(p)),void) <= pw_array_end(a)) #define pw_array_for_each(pos, array) \ - for (pos = (__typeof__(pos)) pw_array_first(array); \ + for ((pos) = (__typeof__(pos)) pw_array_first(array); \ pw_array_check(array, pos); \ (pos)++) #define pw_array_consume(pos, array) \ - for (pos = (__typeof__(pos)) pw_array_first(array); \ + for ((pos) = (__typeof__(pos)) pw_array_first(array); \ pw_array_check(array, pos); \ - pos = (__typeof__(pos)) pw_array_first(array)) + (pos) = (__typeof__(pos)) pw_array_first(array)) #define pw_array_remove(a,p) \ ({ \
View file
pipewire-0.3.58.tar.gz/src/pipewire/context.c -> pipewire-0.3.59.tar.gz/src/pipewire/context.c
Changed
@@ -1205,7 +1205,7 @@ if (settings->clock_rate_update_mode == CLOCK_RATE_UPDATE_MODE_HARD) suspend_driver(context, n); } else { - if (n->info.state >= PW_NODE_STATE_IDLE) + if (n->info.state >= PW_NODE_STATE_SUSPENDED) suspend_driver(context, n); } /* we're setting the pending rate. This will become the new @@ -1263,7 +1263,7 @@ n->current_pending = false; } - pw_log_debug("%p: driving %p running:%d passive:%d quantum:%u '%s'", + pw_log_debug("%p: driver %p running:%d passive:%d quantum:%u '%s'", context, n, running, n->passive, quantum, n->name); /* first change the node states of the followers to the new target */
View file
pipewire-0.3.58.tar.gz/src/pipewire/filter.c -> pipewire-0.3.59.tar.gz/src/pipewire/filter.c
Changed
@@ -1179,7 +1179,7 @@ struct pw_filter *filter; int count; }; -#define MATCH_INIT(f) (struct match){ .filter = f } +#define MATCH_INIT(f) ((struct match){ .filter = (f) }) static int execute_match(void *data, const char *location, const char *action, const char *val, size_t len)
View file
pipewire-0.3.58.tar.gz/src/pipewire/impl-node.c -> pipewire-0.3.59.tar.gz/src/pipewire/impl-node.c
Changed
@@ -188,12 +188,12 @@ pw_loop_invoke(this->data_loop, do_node_remove, 1, NULL, 0, true, this); } -static int pause_node(struct pw_impl_node *this) +static int idle_node(struct pw_impl_node *this) { struct impl *impl = SPA_CONTAINER_OF(this, struct impl, this); int res = 0; - pw_log_debug("%p: pause node state:%s pending:%s pause-on-idle:%d", this, + pw_log_debug("%p: idle node state:%s pending:%s pause-on-idle:%d", this, pw_node_state_as_string(this->info.state), pw_node_state_as_string(impl->pending_state), impl->pause_on_idle); @@ -201,6 +201,9 @@ if (impl->pending_state <= PW_NODE_STATE_IDLE) return 0; + if (!impl->pause_on_idle) + return 0; + node_deactivate(this); res = spa_node_send_command(this->node, @@ -247,7 +250,8 @@ if (impl->pending_state >= PW_NODE_STATE_RUNNING) return 0; - pw_log_debug("%p: start node", this); + pw_log_debug("%p: start node driving:%d driver:%d added:%d", this, + this->driving, this->driver, this->added); if (!(this->driving && this->driver)) { impl->pending_play = true; @@ -357,6 +361,9 @@ switch (state) { case PW_NODE_STATE_RUNNING: + pw_log_debug("%p: start node driving:%d driver:%d added:%d", node, + node->driving, node->driver, node->added); + if (node->driving && node->driver) { res = spa_node_send_command(node->node, &SPA_NODE_COMMAND_INIT(SPA_NODE_COMMAND_Start)); @@ -438,6 +445,9 @@ p->state = PW_IMPL_PORT_STATE_CONFIGURE; } + pw_log_debug("%p: suspend node driving:%d driver:%d added:%d", this, + this->driving, this->driver, this->added); + res = spa_node_send_command(this->node, &SPA_NODE_COMMAND_INIT(SPA_NODE_COMMAND_Suspend)); if (res == -ENOTSUP) @@ -2185,8 +2195,7 @@ break; case PW_NODE_STATE_IDLE: - if (impl->pause_on_idle) - res = pause_node(node); + res = idle_node(node); break; case PW_NODE_STATE_RUNNING: @@ -2215,8 +2224,7 @@ state < PW_NODE_STATE_RUNNING && impl->pending_play) { impl->pending_play = false; - spa_node_send_command(node->node, - &SPA_NODE_COMMAND_INIT(SPA_NODE_COMMAND_Pause)); + idle_node(node); } pw_work_queue_cancel(impl->work, node, impl->pending_id); node->info.state = impl->pending_state;
View file
pipewire-0.3.58.tar.gz/src/pipewire/log.h -> pipewire-0.3.59.tar.gz/src/pipewire/log.h
Changed
@@ -116,7 +116,7 @@ */ #define PW_LOG_TOPIC_STATIC(var, topic) \ static struct spa_log_topic var##__LINE__ = SPA_LOG_TOPIC(0, topic); \ - static struct spa_log_topic *var = &(var##__LINE__) + static struct spa_log_topic *(var) = &(var##__LINE__) /** * Declare a static log topic named \a var. @@ -131,7 +131,7 @@ */ #define PW_LOG_TOPIC(var, topic) \ struct spa_log_topic var##__LINE__ = SPA_LOG_TOPIC(0, topic); \ - struct spa_log_topic *var = &(var##__LINE__) + struct spa_log_topic *(var) = &(var##__LINE__) #define PW_LOG_TOPIC_INIT(var) \ spa_log_topic_init(pw_log_get(), var);
View file
pipewire-0.3.58.tar.gz/src/pipewire/map.h -> pipewire-0.3.59.tar.gz/src/pipewire/map.h
Changed
@@ -85,7 +85,7 @@ }; /** \param extend the amount of bytes to grow the map with when needed */ -#define PW_MAP_INIT(extend) (struct pw_map) { PW_ARRAY_INIT(extend), SPA_ID_INVALID } +#define PW_MAP_INIT(extend) ((struct pw_map) { PW_ARRAY_INIT(extend), SPA_ID_INVALID }) /** * Get the number of currently allocated elements in the map.
View file
pipewire-0.3.58.tar.gz/src/pipewire/permission.h -> pipewire-0.3.59.tar.gz/src/pipewire/permission.h
Changed
@@ -66,7 +66,7 @@ uint32_t permissions; /**< bitmask of above permissions */ }; -#define PW_PERMISSION_INIT(id,p) (struct pw_permission){ (id), (p) } +#define PW_PERMISSION_INIT(id,p) ((struct pw_permission){ (id), (p) }) #define PW_PERMISSION_FORMAT "%c%c%c%c" #define PW_PERMISSION_ARGS(permission) \
View file
pipewire-0.3.58.tar.gz/src/pipewire/private.h -> pipewire-0.3.59.tar.gz/src/pipewire/private.h
Changed
@@ -177,7 +177,7 @@ return NULL; } -#define pw_protocol_emit_destroy(p) spa_hook_list_call(&p->listener_list, struct pw_protocol_events, destroy, 0) +#define pw_protocol_emit_destroy(p) spa_hook_list_call(&(p)->listener_list, struct pw_protocol_events, destroy, 0) struct pw_protocol { struct spa_list link; /**< link in context protocol_list */ @@ -805,7 +805,7 @@ #define pw_impl_port_emit_param_changed(p,i) pw_impl_port_emit(p, param_changed, 1, i) #define pw_impl_port_emit_latency_changed(p) pw_impl_port_emit(p, latency_changed, 2) -#define PW_IMPL_PORT_IS_CONTROL(port) SPA_FLAG_MASK(port->flags, \ +#define PW_IMPL_PORT_IS_CONTROL(port) SPA_FLAG_MASK((port)->flags, \ PW_IMPL_PORT_FLAG_BUFFERS|PW_IMPL_PORT_FLAG_CONTROL,\ PW_IMPL_PORT_FLAG_CONTROL) struct pw_impl_port {
View file
pipewire-0.3.58.tar.gz/src/pipewire/stream.c -> pipewire-0.3.59.tar.gz/src/pipewire/stream.c
Changed
@@ -400,6 +400,28 @@ return NULL; } +static inline uint32_t update_requested(struct stream *impl) +{ + uint32_t index, id, res = 0; + struct buffer *buffer; + struct spa_io_rate_match *r = impl->rate_match; + + if (spa_ringbuffer_get_read_index(&impl->dequeued.ring, &index) < 1) + return 0; + + id = impl->dequeued.idsindex & MASK_BUFFERS; + buffer = &impl->buffersid; + if (r) { + buffer->this.requested = r->size; + res = r->size > 0 ? 1 : 0; + } else { + buffer->this.requested = impl->quantum; + res = 1; + } + pw_log_trace_fp("%p: update buffer:%u size:%"PRIu64, impl, id, buffer->this.requested); + return res; +} + static int do_call_process(struct spa_loop *loop, bool async, uint32_t seq, const void *data, size_t size, void *user_data) @@ -411,9 +433,11 @@ return 0; } -static void call_process(struct stream *impl) +static inline void call_process(struct stream *impl) { pw_log_trace_fp("%p: call process rt:%u", impl, impl->process_rt); + if (impl->direction == SPA_DIRECTION_OUTPUT && update_requested(impl) <= 0) + return; if (impl->process_rt) spa_callbacks_call(&impl->rt_callbacks, struct pw_stream_events, process, 0); else @@ -563,28 +587,6 @@ return 0; } -static inline uint32_t update_requested(struct stream *impl) -{ - uint32_t index, id, res = 0; - struct buffer *buffer; - struct spa_io_rate_match *r = impl->rate_match; - - if (spa_ringbuffer_get_read_index(&impl->dequeued.ring, &index) < 1) - return 0; - - id = impl->dequeued.idsindex & MASK_BUFFERS; - buffer = &impl->buffersid; - if (r) { - buffer->this.requested = r->size; - res = r->size > 0 ? 1 : 0; - } else { - buffer->this.requested = impl->quantum; - res = 1; - } - pw_log_trace_fp("%p: update buffer:%u size:%"PRIu64, impl, id, buffer->this.requested); - return res; -} - static int impl_send_command(void *object, const struct spa_command *command) { struct stream *impl = object; @@ -612,10 +614,8 @@ if (impl->direction == SPA_DIRECTION_INPUT) impl->io->status = SPA_STATUS_NEED_DATA; - else if (!impl->process_rt && !impl->driving) { - if (update_requested(impl) > 0) - call_process(impl); - } + else if (!impl->process_rt && !impl->driving) + call_process(impl); stream_set_state(stream, PW_STREAM_STATE_STREAMING, NULL); } @@ -1081,8 +1081,7 @@ /* we're not draining, not a driver check if we need to get * more buffers */ if (ask_more) { - if (update_requested(impl) > 0) - call_process(impl); + call_process(impl); /* 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 && @@ -1403,7 +1402,7 @@ struct pw_stream *stream; int count; }; -#define MATCH_INIT(s) (struct match){ .stream = s } +#define MATCH_INIT(s) ((struct match){ .stream = (s) }) static int execute_match(void *data, const char *location, const char *action, const char *val, size_t len) @@ -2352,7 +2351,7 @@ int res; if (impl->direction == SPA_DIRECTION_OUTPUT) { if (impl->process_rt) - spa_callbacks_call(&impl->rt_callbacks, struct pw_stream_events, process, 0); + call_process(impl); res = impl->node_methods.process(impl); } else { res = SPA_STATUS_NEED_DATA; @@ -2386,11 +2385,9 @@ if (!impl->driving && !impl->trigger) { res = trigger_request_process(impl); } else { - if (impl->direction == SPA_DIRECTION_OUTPUT && - !impl->process_rt) { - pw_loop_invoke(impl->context->main_loop, - do_call_process, 1, NULL, 0, false, impl); - } + if (!impl->process_rt) + call_process(impl); + res = pw_loop_invoke(impl->context->data_loop, do_trigger_process, 1, NULL, 0, false, impl); }
View file
pipewire-0.3.58.tar.gz/src/pipewire/thread-loop.c -> pipewire-0.3.59.tar.gz/src/pipewire/thread-loop.c
Changed
@@ -91,7 +91,7 @@ #define CHECK(expression,label) \ do { \ - if ((errno = expression) != 0) { \ + if ((errno = (expression)) != 0) { \ res = -errno; \ pw_log_error(#expression ": %s", strerror(errno)); \ goto label; \
View file
pipewire-0.3.58.tar.gz/src/pipewire/thread.c -> pipewire-0.3.59.tar.gz/src/pipewire/thread.c
Changed
@@ -37,7 +37,7 @@ #define CHECK(expression,label) \ do { \ - if ((errno = expression) != 0) { \ + if ((errno = (expression)) != 0) { \ res = -errno; \ pw_log_error(#expression ": %s", strerror(errno)); \ goto label; \
View file
pipewire-0.3.58.tar.gz/src/tools/pw-cli.c -> pipewire-0.3.59.tar.gz/src/tools/pw-cli.c
Changed
@@ -2374,8 +2374,8 @@ fprintf(stderr, "Error: \"%s\"\n", error); free(error); } + data.current->prompt_pending = pw_core_sync(data.current->core, 0, 0); while (!data.quit && data.current) { - data.current->prompt_pending = pw_core_sync(data.current->core, 0, 0); pw_main_loop_run(data.loop); if (!monitor) break;
View file
pipewire-0.3.58.tar.gz/src/tools/pw-dot.c -> pipewire-0.3.59.tar.gz/src/tools/pw-dot.c
Changed
@@ -514,7 +514,7 @@ fputs(d->dot_str, stdout); } else { /* open the file */ - fp = fopen(path, "w"); + fp = fopen(path, "we"); if (fp == NULL) { printf("open error: could not open %s for writing\n", path); return -1;
View file
pipewire-0.3.58.tar.gz/src/tools/pw-profiler.c -> pipewire-0.3.59.tar.gz/src/tools/pw-profiler.c
Changed
@@ -262,7 +262,7 @@ printf("\ndumping scripts for %d followers\n", d->n_followers); - out = fopen("Timing1.plot", "w"); + out = fopen("Timing1.plot", "we"); if (out == NULL) { pw_log_error("Can't open Timing1.plot: %m"); } else { @@ -282,7 +282,7 @@ fclose(out); } - out = fopen("Timing2.plot", "w"); + out = fopen("Timing2.plot", "we"); if (out == NULL) { pw_log_error("Can't open Timing2.plot: %m"); } else { @@ -298,7 +298,7 @@ fclose(out); } - out = fopen("Timing3.plot", "w"); + out = fopen("Timing3.plot", "we"); if (out == NULL) { pw_log_error("Can't open Timing3.plot: %m"); } else { @@ -328,7 +328,7 @@ fclose(out); } - out = fopen("Timing4.plot", "w"); + out = fopen("Timing4.plot", "we"); if (out == NULL) { pw_log_error("Can't open Timing4.plot: %m"); } else { @@ -355,7 +355,7 @@ fclose(out); } - out = fopen("Timing5.plot", "w"); + out = fopen("Timing5.plot", "we"); if (out == NULL) { pw_log_error("Can't open Timing5.plot: %m"); } else { @@ -381,7 +381,7 @@ "unset output\n"); fclose(out); } - out = fopen("Timings.html", "w"); + out = fopen("Timings.html", "we"); if (out == NULL) { pw_log_error("Can't open Timings.html: %m"); } else { @@ -409,7 +409,7 @@ fclose(out); } - out = fopen("generate_timings.sh", "w"); + out = fopen("generate_timings.sh", "we"); if (out == NULL) { pw_log_error("Can't open generate_timings.sh: %m"); } else { @@ -624,7 +624,7 @@ data.filename = opt_output; - data.output = fopen(data.filename, "w"); + data.output = fopen(data.filename, "we"); if (data.output == NULL) { fprintf(stderr, "Can't open file %s: %m\n", data.filename); return -1;
View file
pipewire-0.3.58.tar.gz/test/pwtest.c -> pipewire-0.3.59.tar.gz/test/pwtest.c
Changed
@@ -835,7 +835,7 @@ #ifdef __linux__ { FILE *f; - f = fopen("/proc/sys/fs/pipe-max-size", "r"); + f = fopen("/proc/sys/fs/pipe-max-size", "re"); if (f) { if (fscanf(f, "%d", &r) == 1) pipe_max_size = SPA_MIN(r, pipe_max_size); @@ -1244,7 +1244,7 @@ /* Marker file to avoid removing a random directory during cleanup */ r = spa_scnprintf(path, sizeof(path), "%s/pwtest.dir", dir); spa_assert_se((size_t)r == strlen(dir) + 11); - fp = fopen(path, "w"); + fp = fopen(path, "we"); spa_assert_se(fp); fprintf(fp, "pwtest\n"); fclose(fp);
View file
pipewire-0.3.58.tar.gz/test/test-config.c -> pipewire-0.3.59.tar.gz/test/test-config.c
Changed
@@ -35,7 +35,7 @@ char *basename; pwtest_mkstemp(path); - fp = fopen(path, "w"); + fp = fopen(path, "we"); fputs("data = x", fp); fclose(fp);
View file
pipewire-0.3.58.tar.gz/test/test-logger.c -> pipewire-0.3.59.tar.gz/test/test-logger.c
Changed
@@ -63,7 +63,7 @@ /* Print a line expected to be truncated */ spa_log_error(iface, "MARK: %1100s", "foo"); - fp = fopen(fname, "r"); + fp = fopen(fname, "re"); while (fgets(buffer, sizeof(buffer), fp) != NULL) { if (strstr(buffer, "MARK:")) { const char *suffix = ".. (truncated)\n"; @@ -110,7 +110,7 @@ * tty so expect none despite colors being enabled */ spa_log_error(iface, "MARK\n"); - fp = fopen(fname, "r"); + fp = fopen(fname, "re"); while (fgets(buffer, sizeof(buffer), fp) != NULL) { if (strstr(buffer, "MARK")) { mark_line_found = true; @@ -157,7 +157,7 @@ if (level < SPA_LOG_LEVEL_TRACE) pw_log(level + 1, "ABOVE"); - fp = fopen(fname, "r"); + fp = fopen(fname, "re"); while (fgets(buffer, sizeof(buffer), fp) != NULL) { if (strstr(buffer, "CURRENT")) current_level_found = true; @@ -427,7 +427,7 @@ spa_logt_info(iface, &topic, "MARK\n"); - fp = fopen(fname, "r"); + fp = fopen(fname, "re"); while (fgets(buffer, sizeof(buffer), fp) != NULL) { if (strstr(buffer, "MARK")) { mark_line_found = true; @@ -602,7 +602,7 @@ /* Now check that the line is in the chained file logger too */ spa_memzero(buffer, sizeof(buffer)); mark_line_found = false; - fp = fopen(fname, "r"); + fp = fopen(fname, "re"); while (fgets(buffer, sizeof(buffer), fp) != NULL) { if (strstr(buffer, token)) { mark_line_found = true;
View file
pipewire-0.3.58.tar.gz/test/test-properties.c -> pipewire-0.3.59.tar.gz/test/test-properties.c
Changed
@@ -484,7 +484,7 @@ dict = SPA_DICT_INIT(items, 2); pwtest_mkstemp(tmpfile); - fp = fopen(tmpfile, "w"); + fp = fopen(tmpfile, "we"); pwtest_ptr_notnull(fp); r = pw_properties_serialize_dict(fp, &dict, 0); pwtest_int_eq(r, 1);
View file
pipewire-0.3.58.tar.gz/test/test-spa-pod.c -> pipewire-0.3.59.tar.gz/test/test-spa-pod.c
Changed
@@ -1639,6 +1639,54 @@ return PWTEST_PASS; } +static int handle_overflow(void *data, uint32_t size) +{ + uint32_t *d = data; + (*d)++; + return -ENOSPC; +} + +static struct spa_pod_builder_callbacks overflow_cb = { + SPA_VERSION_POD_BUILDER_CALLBACKS, + .overflow = handle_overflow +}; + +PWTEST(pod_overflow2) +{ + uint8_t buffer1024; + struct spa_pod_builder b = { 0 }; + struct spa_pod_builder_state state; + struct spa_pod_frame f2; + uint32_t idx, overflow_count = 0; + struct spa_pod *pod; + + spa_pod_builder_init(&b, buffer, sizeof(buffer)); + spa_pod_builder_set_callbacks(&b, &overflow_cb, &overflow_count); + + spa_pod_builder_push_object(&b, &f0, SPA_TYPE_OBJECT_PropInfo, SPA_PARAM_PropInfo); + spa_pod_builder_add(&b, + SPA_PROP_INFO_id, SPA_POD_Id(32567359), + SPA_PROP_INFO_type, SPA_POD_CHOICE_ENUM_Int(1, 0), + SPA_PROP_INFO_description, SPA_POD_String("DV Timings"), + 0); + + spa_pod_builder_get_state(&b, &state), + + spa_pod_builder_prop(&b, SPA_PROP_INFO_labels, 0); + spa_pod_builder_push_struct(&b, &f1); + + for (idx = 0; idx < 512; idx++) { + spa_pod_builder_int(&b, idx); + spa_pod_builder_string(&b, "foo"); + } + spa_assert_se(b.state.offset > sizeof(buffer)); + pod = spa_pod_builder_pop(&b, &f1); + spa_assert_se(pod == NULL); + spa_assert_se(overflow_count == 1); + + return PWTEST_PASS; +} + PWTEST_SUITE(spa_pod) { pwtest_add(pod_abi_sizes, PWTEST_NOARG); @@ -1652,6 +1700,7 @@ pwtest_add(pod_parser2, PWTEST_NOARG); pwtest_add(pod_static, PWTEST_NOARG); pwtest_add(pod_overflow, PWTEST_NOARG); + pwtest_add(pod_overflow2, 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
.